工业6轴机械臂运动学逆解(解析解)

工业6轴机械臂运动学逆解(解析解)

  通常工业机械臂采用6旋转轴串连的形式,保证了灵活性,但为其运动学逆解(即已知机械臂末端的位姿 P P P,求机械臂各个旋转轴的旋转角)带来了较大的困难,通常没有解析解。为了提高实时性,经过前辈们不懈的研究,当6轴机械臂满足pieper准则时,可以得出其运动学逆解的解析解。pieper准则如下:

  1. 三个相邻关节轴线交于一点,如fanuc m10系列;
  2. 三个相邻关节轴线相互平行,如ur5系列;
    以下将通过一个简单实例,介绍机械臂在pieper第一准则的情况的运动学逆解。

机械臂运动学模型

  机械臂的简化模型如图1所示。


图1

  采用DH矩阵的形式对机械臂进行建模,DH矩阵如下:

关节连杆夹角连杆长度连杆偏距初始关节角
1000.10
20.5 π \pi π000
300.500.5 π \pi π
40.5 π \pi π00.50
5-0.5 π \pi π000
60.5 π \pi π000

  从图1中可以看出,在初始状态下,机械臂的第2、3关节轴与基座坐标系的Y轴相互平行,机械臂的第4、5、6关节轴线相交于一点 P P P,满足pieper第一准则,且点 P P P与基座坐标系的XOZ平面重合。在这些条件下,虽然限制了机械臂的设计和构型,但极大地简化了逆解的过程。

运动学逆解

  由于机械臂的结构比较简单,故采用几何法的方式求解机械臂的运动学逆解。
  机械臂末端初始位姿 P 0 P_0 P0,运动学逆解末端的位姿 P ( x , y , z ) P(x,y,z) P(x,y,z)

求解第1关节轴的关节角

以基坐标系为参考,机械臂末端位置示意图如图2所示。


图2

  在图1中,点 P 0 P_0 P0为机械臂末端的初始状态位置(当前位置),点 P P P为机械臂末端的期望位置,由于初始状态时,机械臂末端的初始位置与 X 0 Y 0 X_0Y_0 X0Y0平面重合,故关节轴1需要旋转 θ 1 \theta_1 θ1 θ 1 + π \theta_1 + \pi θ1+π才能使期望位置 P P P X 0 Y 0 X_0Y_0 X0Y0平面重合,故,关节轴1存在两个解

θ 1 = { a t a n 2 ( y , x ) a t a n 2 ( y , x ) + π (1) \theta_1 = \begin{cases} atan2(y, x)\\ atan2(y, x) + \pi\\ \end{cases} \tag1 θ1={atan2(y,x)atan2(y,x)+π(1)

求解第2、3关节轴的关节角

  关节轴1经过旋转 θ 1 = a t a n 2 ( y , x ) \theta_1=atan2(y, x) θ1=atan2(y,x)角度,以第一关节轴的坐标系为参考,得示意图如图3所示。 P 1 P_1 P1为机械臂末端的当前位置, P P P为末端期望位置, O A 0 OA_0 OA0 O A 1 OA_1 OA1为机械臂的第2连杆, A 0 P A_0P A0P A 1 P A_1P A1P为机械臂的第3连杆。


图3

  设 A 0 ( x 0 , 0 , z 0 ) A_0(x_0, 0, z_0) A0(x0,0,z0),可得方程:

x 0 2 + z 0 2 = D H [ 2 , 1 ] 2 ( x − x 0 ) 2 + ( z − D H [ 0 , 2 ] − z 0 ) 2 = D H [ 3 , 2 ] 2 (2) x_0^2 + z_0^2 = DH[2, 1]^2\\ (x - x_0)^2 + (z - DH[0, 2] - z_0)^2 = DH[3, 2]^2 \tag2 x02+z02=DH[2,1]2(xx0)2+(zDH[0,2]z0)2=DH[3,2]2(2)

  对2元2次方程组(2)进行求解,得 ( x 00 , z 00 ) (x_{00}, z_{00}) (x00,z00) ( x 01 , z 01 ) (x_{01}, z_{01}) (x01,z01) ( x 02 , z 02 ) (x_{02}, z_{02}) (x02,z02) ( x 03 , z 03 ) (x_{03}, z_{03}) (x03,z03)四组解,去除其中的非实数解。由此可得到关节轴2、3的旋转角。

θ 2 = a t a n 2 ( z 0 , x 0 ) θ 3 = a t a n 2 ( z − D H [ 0 , 2 ] − z 0 , x − x 0 ) − a t a n 2 ( z 0 , x 0 ) (2) \theta_2 = atan2(z_0, x_0)\\ \theta_3 = atan2(z - DH[0, 2] - z_0, x - x_0) - atan2(z_0, x_0) \tag2 θ2=atan2(z0,x0)θ3=atan2(zDH[0,2]z0,xx0)atan2(z0,x0)(2)
  如图2所示,解方程(2),可得到关节角 θ 2 \theta_2 θ2 θ 3 \theta_3 θ3的两组解。
  同理,当取 θ 1 = a t a n 2 ( y , x ) + π \theta_1=atan2(y, x) + \pi θ1=atan2(y,x)+π时,亦可得到关节角 θ 2 \theta_2 θ2 θ 3 \theta_3 θ3的两组解。

  至此, θ 1 \theta_1 θ1 θ 2 \theta_2 θ2 θ 3 \theta_3 θ3存在8组解,可去除其中相同的解。

求解第4、5、6关节轴的关节角

经过对机械臂前3根轴的旋转,已经机械臂的末端位置与期望的末端位置相重合,由于关节轴4、5、6相交与末端位置,对此3轴的旋转不会改变末端的位置,故,单独对此3轴进行姿态解算即可得到关节角。
R 1 ( θ 1 ) R_1(\theta_1) R1(θ1) R 2 ( θ 2 ) R_2(\theta_2) R2(θ2) R 3 ( θ 3 ) R_3(\theta_3) R3(θ3) R 4 ( θ 4 ) R_4(\theta_4) R4(θ4) R 5 ( θ 5 ) R_5(\theta_5) R5(θ5) R 6 ( θ 6 ) R_6(\theta_6) R6(θ6)表示各轴的变换矩阵。将对关节轴4、5、6的旋转看成是动欧拉角ZYZ的旋转模式,其旋转矩阵为 R ( θ 4 , θ 5 , θ 6 ) R(\theta_4,\theta_5,\theta_6) R(θ4,θ5,θ6)

R ( θ 4 , θ 5 , θ 6 ) = [ c o s θ 4 − s i n θ 4 0 s i n θ 4 c o s θ 4 0 0 0 1 ] [ c o s θ 5 0 s i n θ 5 0 1 0 − s i n θ 5 0 c o s θ 5 ] [ c o s θ 6 − s i n θ 6 0 s i n θ 6 c o s θ 6 0 0 0 1 ] (3) R(\theta_4,\theta_5,\theta_6) = \begin{bmatrix} cos\theta_4&-sin\theta_4&0\\ sin\theta_4&cos\theta_4&0\\ 0&0&1\\ \end{bmatrix} \begin{bmatrix} cos\theta_5&0&sin\theta_5\\ 0&1&0\\ -sin\theta_5&0&cos\theta_5\\ \end{bmatrix} \begin{bmatrix} cos\theta_6&-sin\theta_6&0\\ sin\theta_6&cos\theta_6&0\\ 0&0&1\\ \end{bmatrix} \tag3 R(θ4,θ5,θ6)= cosθ4sinθ40sinθ4cosθ40001 cosθ50sinθ5010sinθ50cosθ5 cosθ6sinθ60sinθ6cosθ60001 (3)
具体计算得:
R ( θ 4 , θ 5 , θ 6 ) = [ c o s θ 4 c o s θ 5 c o s θ 6 − s i n θ 4 s i n θ 6 − c o s θ 4 c o s θ 5 s i n θ 6 − s i n θ 4 c o s c o s θ 6 θ 4 s i n θ 5 s i n θ 4 c o s θ 5 c o s θ 6 + c o s θ 4 s i n θ 6 − s i n θ 4 c o s θ 5 s i n θ 6 + c o s θ 4 c o s θ 6 s i n θ 4 s i n θ 5 − s i n θ 5 c o s θ 6 s i n θ 5 s i n θ 6 c o s θ 5 ] (4) R(\theta_4,\theta_5,\theta_6) = \begin{bmatrix} cos\theta_4cos\theta_5cos\theta_6-sin\theta_4sin\theta_6&-cos\theta_4cos\theta_5sin\theta_6-sin\theta_4cos&cos\theta_6\theta_4sin\theta_5\\ sin\theta_4cos\theta_5cos\theta_6+cos\theta_4sin\theta_6&-sin\theta_4cos\theta_5sin\theta_6+cos\theta_4cos\theta_6&sin\theta_4sin\theta_5\\ -sin\theta_5cos\theta_6&sin\theta_5sin\theta_6&cos\theta_5\\ \end{bmatrix} \tag4 R(θ4,θ5,θ6)= cosθ4cosθ5cosθ6sinθ4sinθ6sinθ4cosθ5cosθ6+cosθ4sinθ6sinθ5cosθ6cosθ4cosθ5sinθ6sinθ4cossinθ4cosθ5sinθ6+cosθ4cosθ6sinθ5sinθ6cosθ6θ4sinθ5sinθ4sinθ5cosθ5 (4)

  由机械臂的正运动学可得:
R ( θ 1 ) R ( θ 2 ) R ( θ 3 ) R ( θ 4 = 0 ) R ( θ 4 , θ 5 , θ 6 ) = R P (5) R(\theta_1)R(\theta_2)R(\theta_3)R(\theta_4=0)R(\theta_4, \theta_5, \theta_6)=R_P \tag5 R(θ1)R(θ2)R(θ3)R(θ4=0)R(θ4,θ5,θ6)=RP(5)

  在公式(5)中, R P R_P RP为机械臂末端点 P P P的姿态。对公式(5)进行移项得:

R ( θ 4 , θ 5 , θ 6 ) = [ R ( θ 1 ) R ( θ 2 ) R ( θ 3 ) R ( θ 4 = 0 ) ] − 1 R P = [ r 11 r 12 r 13 r 21 r 22 r 23 r 31 r 32 r 33 ] (6) R(\theta_4, \theta_5, \theta_6)=[R(\theta_1)R(\theta_2)R(\theta_3)R(\theta_4=0)]^{-1}R_P= \begin{bmatrix} r_{11}&r_{12}&r_{13}\\ r_{21}&r_{22}&r_{23}\\ r_{31}&r_{32}&r_{33}\\ \end{bmatrix} \tag6 R(θ4,θ5,θ6)=[R(θ1)R(θ2)R(θ3)R(θ4=0)]1RP= r11r21r31r12r22r32r13r23r33 (6)

  联立公式(4)(6)可得两组解:

{ θ 4 = a t a n 2 ( r 23 , r 13 ) θ 5 = a t a n 2 ( s q r t ( r 31 2 + r 32 2 ) , r 33 ) θ 6 = a t a n 2 ( r 32 , − r 31 ) (7) \begin{cases} \theta_4=atan2(r_{23}, r_{13})\\ \theta_5=atan2(sqrt(r_{31}^2+r_{32}^2), r_{33})\\ \theta_6=atan2(r_{32}, -r_{31}) \end{cases} \tag7 θ4=atan2(r23,r13)θ5=atan2(sqrt(r312+r322),r33)θ6=atan2(r32,r31)(7)

{ θ 4 = a t a n 2 ( r 23 , r 13 ) + π θ 5 = − a t a n 2 ( s q r t ( r 31 2 + r 32 2 ) , r 33 ) θ 6 = a t a n 2 ( r 32 , − r 31 ) + π (8) \begin{cases} \theta_4=atan2(r_{23}, r_{13}) + \pi\\ \theta_5=-atan2(sqrt(r_{31}^2+r_{32}^2), r_{33})\\ \theta_6=atan2(r_{32}, -r_{31}) + \pi \end{cases} \tag8 θ4=atan2(r23,r13)+πθ5=atan2(sqrt(r312+r322),r33)θ6=atan2(r32,r31)+π(8)

  综上,完成机械臂的运动学逆解的解析解求解过程,可能存在8个以上的解,可根据一些约束调节对求得的解进行删选,如关节限位、碰撞检测等。

示例程序

import numpy as np
import math
from pyquaternion import Quaternion

np.set_printoptions(suppress=True)

# DH矩阵每列的含义:连杆夹角、连杆长度、连杆偏距、初始关节角
DH = np.mat([[             0,   0, 0.1,             0], 
             [ 0.5 * math.pi,   0,   0,             0], 
             [             0, 0.5,   0, 0.5 * math.pi], 
             [ 0.5 * math.pi,   0, 0.5,             0], 
             [-0.5 * math.pi,   0,   0,             0], 
             [ 0.5 * math.pi,   0,   0,             0]])

def transformToMatrix(alpha, a, d, theta):
    T0 = np.eye(4)
    T1 = np.mat([[1,               0,                0, 0], 
                 [0, math.cos(alpha), -math.sin(alpha), 0], 
                 [0, math.sin(alpha),  math.cos(alpha), 0], 
                 [0,               0,                0, 1]])
    T2 = np.mat([[1, 0, 0, a], 
                 [0, 1, 0, 0], 
                 [0, 0, 1, d], 
                 [0, 0, 0, 1]])
    T3 = np.mat([[math.cos(theta), -math.sin(theta), 0, 0], 
                 [math.sin(theta),  math.cos(theta), 0, 0], 
                 [              0,                0, 1, 0], 
                 [              0,                0, 0, 1]])
    return T1 * T2 * T3

def forwardKinematic(DH, j0, j1, j2, j3, j4, j5):
    T0 = transformToMatrix(DH[0, 0], DH[0, 1], DH[0, 2], DH[0, 3] + j0)
    T1 = transformToMatrix(DH[1, 0], DH[1, 1], DH[1, 2], DH[1, 3] + j1)
    T2 = transformToMatrix(DH[2, 0], DH[2, 1], DH[2, 2], DH[2, 3] + j2)
    T3 = transformToMatrix(DH[3, 0], DH[3, 1], DH[3, 2], DH[3, 3] + j3)
    T4 = transformToMatrix(DH[4, 0], DH[4, 1], DH[4, 2], DH[4, 3] + j4)
    T5 = transformToMatrix(DH[5, 0], DH[5, 1], DH[5, 2], DH[5, 3] + j5)
    #print(T0 * T1 * T2 * T3 * T4 * T5)
    return T0 * T1 * T2 * T3 * T4 * T5

def calcu3ForwardJointAngle(DH, j0, x0, y0, x, y, b, js):
    if abs((x0 - x) * (x0 - x) + (y0 - y) * (y0 - y) - b*b) < 0.0001:
        js.append([j0])
        js[-1].append(math.atan2(y0, x0))
        js[-1].append(math.atan2(y - y0, x - x0) - math.atan2(y0, x0) + 0.5 * math.pi - DH[2, 3])

        js.append([j0 + math.pi])
        js[-1].append(math.pi - math.atan2(y0, x0))
        js[-1].append(math.atan2(y0, x0) - math.atan2(y - y0, x - x0) + 0.5 * math.pi - DH[2, 3])
    if abs((x0 - x) * (x0 - x) + (-y0 - y) * (-y0 - y) - b*b) < 0.0001:
        js.append([j0])
        js[-1].append(math.atan2(-y0, x0))
        js[-1].append(math.atan2(y + y0, x - x0) - math.atan2(-y0, x0) + 0.5 * math.pi - DH[2, 3])

        js.append([j0 + math.pi])
        js[-1].append(math.pi - math.atan2(-y0, x0))
        js[-1].append(math.atan2(-y0, x0) - math.atan2(y + y0, x - x0) + 0.5 * math.pi - DH[2, 3])

def quaternionToRotationMatrix(x, y, z, w):
    # a = math.sqrt(x*x + y*y + z*z)
    # if a == 0:
    #     return np.eye(3)
    # v1x = 0
    # v1y = -z
    # v1z = y
    # b = math.sqrt(v1x*v1x + v1y*v1y + v1z*v1z)
    # if b == 0:
    #     v1y = 1.0
    #     v1z = 0.0
    #     b = 1.0
    # v2 = np.cross(np.array([x, y, z]), np.array([v1x, v1y, v1z]))
    # #print(np.array([x, y, z]), np.array([v1x, v1y, v1z]), v2)
    # c = math.sqrt(v2[0]*v2[0] + v2[1]*v2[1] + v2[2]*v2[2])
    # R01 = np.mat([[x / a, v1x / b, v2[0] / c],
    #               [y / a, v1y / b, v2[1] / c],
    #               [z / a, v1z / b, v2[2] / c]])
    
    # theta = 2 * math.acos(w)
    # #print(R01)
    # R12 = np.mat([[1,               0,                0],
    #               [0, math.cos(theta), -math.sin(theta)],
    #               [0, math.sin(theta),  math.cos(theta)]])
    # return R01 * R12 * np.linalg.inv(R01)

    a = math.sqrt(x*x + y*y + z*z + w*w)
    if a == 0:
        print('quaternion is error')
        return np.eye(3)
    x = x / a
    y = y / a
    z = z / a
    w = w / a
    return np.mat([[1 - 2*y*y - 2*z*z,     2*x*y - 2*z*w,     2*x*z + 2*y*w],
                   [    2*x*y + 2*z*w, 1 - 2*x*x - 2*z*z,     2*y*z - 2*x*w],
                   [    2*x*z - 2*y*w,     2*y*z + 2*x*w, 1 - 2*x*x - 2*y*y]])

def rotateMatrixToQuaternion(R):
    R1 = [[R[0, 0], R[0, 1], R[0, 2]],
          [R[1, 0], R[1, 1], R[1, 2]],
          [R[2, 0], R[2, 1], R[2, 2]]]
    q = Quaternion(matrix=np.array(R1))

    return q.x, q.y, q.z, q.w

def inverseKinematic(DH, x, y, z, ox, oy, oz, ow):
    q_length = math.sqrt(ox*ox + oy*oy + oz*oz + ow*ow)
    if q_length == 0:
        print('quaternion is error')
        return
    else:
        ox = ox / q_length
        oy = oy / q_length
        oz = oz / q_length
        ow = ow / q_length

    js = []
    j00 = math.atan2(y, x)
    a = DH[2, 1]
    b = DH[3, 2]
    c = math.sqrt(x*x + y*y + (z - DH[0, 2])*(z - DH[0, 2]))
    a0 = 4*(x*x+y*y) + 4*(z - DH[0, 2])*(z - DH[0, 2])
    a1 = -4*(a*a - b*b + c*c)*math.sqrt(x*x + y*y)
    a2 = (a*a - b*b + c*c) * (a*a - b*b + c*c) - 4 * (z - DH[0, 2]) * (z - DH[0, 2]) * a * a
    if a1*a1 - 4*a0*a2 > 0:
        x0 = (-a1 + math.sqrt(a1*a1 - 4*a0*a2)) / (2 * a0)
        y0 = math.sqrt(a*a - x0*x0)
        #print('x0: ', x0, 'y0: ', y0)
        calcu3ForwardJointAngle(DH, j00, x0, y0, math.sqrt(x*x+y*y), z - DH[0, 2], b, js)

        x0 = (-a1 - math.sqrt(a1*a1 - 4*a0*a2)) / (2 * a0)
        y0 = math.sqrt(a*a - x0*x0)
        #print('x1: ', x0, 'y1: ', y0)
        calcu3ForwardJointAngle(DH, j00, x0, y0, math.sqrt(x*x+y*y), z - DH[0, 2], b, js)
    elif a1*a1 - 4*a0*a2 == 0:
        x0 = (-a1) / (2 * a0)
        y0 = math.sqrt(a*a - x0*x0)
        #print('x0: ', x0, 'y0: ', y0)
        calcu3ForwardJointAngle(DH, j00, x0, y0, math.sqrt(x*x+y*y), z - DH[0, 2], b, js)
    else:
        print('no solve')
        js = [[]]

    new_js = []
    for j in js:
        R = quaternionToRotationMatrix(ox, oy, oz, ow)
        T01 = transformToMatrix(DH[0, 0], DH[0, 1], DH[0, 2], DH[0, 3] + j[0])
        T12 = transformToMatrix(DH[1, 0], DH[1, 1], DH[1, 2], DH[1, 3] + j[1])
        T23 = transformToMatrix(DH[2, 0], DH[2, 1], DH[2, 2], DH[2, 3] + j[2])
        T34 = transformToMatrix(DH[3, 0], DH[3, 1], DH[3, 2], DH[3, 3])
        R01 = np.mat([[T01[0, 0], T01[0, 1], T01[0, 2]],
                      [T01[1, 0], T01[1, 1], T01[1, 2]],
                      [T01[2, 0], T01[2, 1], T01[2, 2]]])
        R12 = np.mat([[T12[0, 0], T12[0, 1], T12[0, 2]],
                      [T12[1, 0], T12[1, 1], T12[1, 2]],
                      [T12[2, 0], T12[2, 1], T12[2, 2]]])
        R23 = np.mat([[T23[0, 0], T23[0, 1], T23[0, 2]],
                      [T23[1, 0], T23[1, 1], T23[1, 2]],
                      [T23[2, 0], T23[2, 1], T23[2, 2]]])
        R34 = np.mat([[T34[0, 0], T34[0, 1], T34[0, 2]],
                      [T34[1, 0], T34[1, 1], T34[1, 2]],
                      [T34[2, 0], T34[2, 1], T34[2, 2]]])
        R = (R34.T) * (R23.T) * (R12.T) * (R01.T) * R
        #print('RRRRRRRRR: \n', R)
        alpha = math.atan2(R[1, 2], R[0, 2])
        betla = math.atan2(math.sqrt(R[2, 0]*R[2, 0] + R[2, 1]*R[2, 1]), R[2, 2])
        gamal = math.atan2(R[2, 1], -R[2, 0])

        new_js.append([])
        new_js[-1].append(j[0])
        new_js[-1].append(j[1])
        new_js[-1].append(j[2])
        new_js[-1].append(alpha)
        new_js[-1].append(betla)
        new_js[-1].append(gamal)
        #print(new_js[-1])

        new_js.append([])
        new_js[-1].append(j[0])
        new_js[-1].append(j[1])
        new_js[-1].append(j[2])
        new_js[-1].append(alpha + math.pi)
        new_js[-1].append(-betla)
        new_js[-1].append(gamal + math.pi)
        #print(new_js[-1])

    return new_js

if __name__ == '__main__':
    print("hello world")
    #print(DH)
    print(forwardKinematic(DH, 1, 0, 0, 0, 0, 0))
    js = inverseKinematic(DH, 0.5, 0.11, 0.26, 0.0, 0.0, 0.8, 0.6)
    print('##########################')
    for j in js:
        print('joint angle: ', j)
        T = forwardKinematic(DH, j[0], j[1], j[2], j[3], j[4], j[5])
        #print('P: ', T[0, 3], T[1, 3], T[2, 3])
        #print('Q: ', rotateMatrixToQuaternion(T))
    print('##########################')

注意实现

  1. 此运动学逆解求解器不具备通用性,只适用于满足以上DH矩阵格式的6轴串联机器人;
  2. 示例程序中,没有对所求的解进行筛选,存在超出限位、碰撞的情况;
  3. 在求解2元2次方程组时,注意其中的非实数解;
  • 20
    点赞
  • 54
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值