用Python和NumPy一步步推导6轴机械臂的正逆解(附完整代码)

机械臂控制的核心在于运动学计算,而正逆解则是运动学的两大基石。对于Gluon-6L3这类6轴机械臂,理解其运动学原理并实现代码化,是机器人开发者的必备技能。本文将用Python和NumPy库,从零开始推导正逆解算法,并提供可直接运行的代码实现。

1. 准备工作与环境搭建

在开始之前,我们需要明确几个关键概念:

  • 正运动学(Forward Kinematics) :已知各关节角度,计算机械臂末端执行器的位置和姿态
  • 逆运动学(Inverse Kinematics) :已知末端执行器的目标位置和姿态,反求各关节角度

首先设置Python环境,推荐使用Jupyter Notebook进行交互式开发:

import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
%matplotlib inline

定义机械臂的DH参数表(以Gluon-6L3为例):

关节 θ (rad) α (rad) a (mm) d (mm)
1 θ1 π/2 0 89.2
2 θ2 0 425 0
3 θ3 0 392 0
4 θ4 π/2 0 109.3
5 θ5 -π/2 0 94.75
6 θ6 0 0 82.5

提示:DH参数是机械臂运动学的基础,不同型号机械臂的参数各不相同,需要从技术文档中获取准确值。

2. 正运动学推导与实现

正运动学的核心是构建从基座到末端执行器的变换矩阵。每个关节的变换矩阵可以表示为:

def dh_transform_matrix(theta, alpha, a, d):
    """计算单个关节的DH变换矩阵"""
    ct = np.cos(theta)
    st = np.sin(theta)
    ca = np.cos(alpha)
    sa = np.sin(alpha)
    
    return np.array([
        [ct, -st*ca, st*sa, a*ct],
        [st, ct*ca, -ct*sa, a*st],
        [0, sa, ca, d],
        [0, 0, 0, 1]
    ])

完整的正运动学计算流程:

  1. 为每个关节创建变换矩阵
  2. 将所有变换矩阵按顺序相乘
  3. 提取末端执行器的位置和姿态
def forward_kinematics(thetas, dh_params):
    """计算正运动学"""
    T = np.eye(4)
    for i in range(6):
        theta = thetas[i] + dh_params[i][0]  # 考虑关节偏移
        alpha, a, d = dh_params[i][1], dh_params[i][2], dh_params[i][3]
        Ti = dh_transform_matrix(theta, alpha, a, d)
        T = np.dot(T, Ti)
    
    position = T[:3, 3]
    orientation = T[:3, :3]
    return position, orientation

验证正运动学计算:

dh_params = [
    [0, np.pi/2, 0, 89.2],
    [0, 0, 425, 0],
    [0, 0, 392, 0],
    [0, np.pi/2, 0, 109.3],
    [0, -np.pi/2, 0, 94.75],
    [0, 0, 0, 82.5]
]

# 测试所有关节角度为0时的位置
thetas = [0, 0, 0, 0, 0, 0]
position, _ = forward_kinematics(thetas, dh_params)
print(f"末端位置: {position}")

3. 逆运动学推导与实现

逆运动学求解更为复杂,通常有几何法、代数法和数值法三种方法。我们采用代数法进行推导,主要步骤如下:

3.1 求解θ1

θ1可以通过末端执行器的位置在XY平面上的投影来确定:

def solve_theta1(T, d4):
    """求解第一个关节角度"""
    px = T[0, 3]
    py = T[1, 3]
    
    # 计算两种可能的解
    phi = np.arctan2(py, px)
    temp = (d4**2) / (px**2 + py**2)
    if temp > 1:
        return None  # 无解
    
    delta = np.arctan2(d4, np.sqrt(px**2 + py**2 - d4**2))
    theta1_a = phi + delta
    theta1_b = phi - delta
    
    return [theta1_a, theta1_b]

3.2 求解θ5

θ5可以通过末端执行器的Z轴方向向量确定:

def solve_theta5(T, theta1):
    """求解第五个关节角度"""
    ax = T[0, 2]
    ay = T[1, 2]
    
    c5 = np.sin(theta1)*ax - np.cos(theta1)*ay
    s5 = np.sqrt(1 - c5**2)
    
    theta5_a = np.arctan2(s5, c5)
    theta5_b = np.arctan2(-s5, c5)
    
    return [theta5_a, theta5_b]

3.3 求解θ6

θ6可以通过末端执行器的X轴方向向量确定:

def solve_theta6(T, theta1, theta5):
    """求解第六个关节角度"""
    nx = T[0, 0]
    ny = T[1, 0]
    ox = T[0, 1]
    oy = T[1, 1]
    
    s5 = np.sin(theta5)
    if abs(s5) < 1e-6:  # 避免除以0
        return None
    
    s6 = (-np.sin(theta1)*ox + np.cos(theta1)*oy) / s5
    c6 = (np.sin(theta1)*nx - np.cos(theta1)*ny) / s5
    
    return np.arctan2(s6, c6)

3.4 求解θ2, θ3, θ4

这三个角度需要联立方程求解,过程较为复杂:

def solve_theta234(T, theta1, theta5, dh_params):
    """求解第二、三、四关节角度"""
    a2, a3 = dh_params[1][2], dh_params[2][2]
    d1, d5 = dh_params[0][3], dh_params[4][3]
    
    # 计算中间变量
    c1 = np.cos(theta1)
    s1 = np.sin(theta1)
    px = T[0, 3]
    py = T[1, 3]
    pz = T[2, 3]
    az = T[2, 2]
    
    s234 = -az / np.sin(theta5)
    c234 = (c1*T[0,2] + s1*T[1,2]) / np.sin(theta5)
    theta234 = np.arctan2(s234, c234)
    
    # 计算k1和k2
    k1 = c1*px + s1*py - d5*s234
    k2 = pz - d1 + d5*c234
    
    # 求解θ3
    denominator = 2*a2*a3
    numerator = k1**2 + k2**2 - a2**2 - a3**2
    c3 = numerator / denominator
    
    if abs(c3) > 1:
        return None  # 无解
    
    s3 = np.sqrt(1 - c3**2)
    theta3_a = np.arctan2(s3, c3)
    theta3_b = np.arctan2(-s3, c3)
    
    solutions = []
    for theta3 in [theta3_a, theta3_b]:
        # 求解θ2
        denom = a2**2 + a3**2 + 2*a2*a3*np.cos(theta3)
        s2 = (k2*(a2 + a3*np.cos(theta3)) - k1*a3*np.sin(theta3)) / denom
        c2 = (k1*(a2 + a3*np.cos(theta3)) + k2*a3*np.sin(theta3)) / denom
        theta2 = np.arctan2(s2, c2)
        
        # 求解θ4
        theta4 = theta234 - theta2 - theta3
        solutions.append((theta2, theta3, theta4))
    
    return solutions

4. 完整逆运动学实现

将上述各步骤组合起来,形成完整的逆运动学求解函数:

def inverse_kinematics(T, dh_params):
    """完整的逆运动学求解"""
    d4 = dh_params[3][3]
    
    # 第一步:求解θ1
    theta1_solutions = solve_theta1(T, d4)
    if theta1_solutions is None:
        return []
    
    all_solutions = []
    
    for theta1 in theta1_solutions:
        # 第二步:求解θ5
        theta5_solutions = solve_theta5(T, theta1)
        if theta5_solutions is None:
            continue
            
        for theta5 in theta5_solutions:
            # 第三步:求解θ6
            theta6 = solve_theta6(T, theta1, theta5)
            if theta6 is None:
                continue
                
            # 第四步:求解θ2, θ3, θ4
            theta234_solutions = solve_theta234(T, theta1, theta5, dh_params)
            if theta234_solutions is None:
                continue
                
            for theta2, theta3, theta4 in theta234_solutions:
                solution = [theta1, theta2, theta3, theta4, theta5, theta6]
                all_solutions.append(solution)
    
    return all_solutions

5. 验证与可视化

为了验证我们的算法是否正确,我们可以进行闭环测试:

# 随机生成一组关节角度
test_angles = np.random.uniform(-np.pi, np.pi, 6)

# 计算正运动学得到末端位姿
position, orientation = forward_kinematics(test_angles, dh_params)
T = np.eye(4)
T[:3, :3] = orientation
T[:3, 3] = position

# 用逆运动学反解关节角度
solutions = inverse_kinematics(T, dh_params)

# 比较原始角度与求解结果
if len(solutions) > 0:
    print("原始角��:", np.degrees(test_angles))
    print("求解结果:", np.degrees(solutions[0]))
    
    # 验证误差
    position2, _ = forward_kinematics(solutions[0], dh_params)
    error = np.linalg.norm(position - position2)
    print(f"位置误差: {error:.6f} mm")

可视化机械臂姿态:

def plot_robot(ax, thetas, dh_params):
    """绘制机械臂姿态"""
    points = [np.zeros(3)]
    T = np.eye(4)
    
    for i in range(6):
        theta = thetas[i] + dh_params[i][0]
        alpha, a, d = dh_params[i][1], dh_params[i][2], dh_params[i][3]
        Ti = dh_transform_matrix(theta, alpha, a, d)
        T = np.dot(T, Ti)
        points.append(T[:3, 3])
    
    points = np.array(points)
    ax.plot(points[:,0], points[:,1], points[:,2], 'o-', linewidth=2)
    ax.set_xlabel('X (mm)')
    ax.set_ylabel('Y (mm)')
    ax.set_zlabel('Z (mm)')
    ax.set_title('机械臂姿态可视化')

fig = plt.figure(figsize=(10, 8))
ax = fig.add_subplot(111, projection='3d')
plot_robot(ax, test_angles, dh_params)

6. 实际应用中的注意事项

在实际项目中应用这些算法时,需要考虑以下几个关键点:

  1. 奇异位形处理 :当机械臂处于奇异位形时,逆运动学可能无解或有无穷多解

    • 检测奇异位形:当sin(θ5)接近0时
    • 解决方案:微小调整目标位姿或采用数值方法
  2. 多解选择策略 :逆运动学通常有多个解

    • 选择最接近当前姿态的解
    • 选择使机械臂运动最小的解
    • 避开关节限位
  3. 数值稳定性

    • 使用 atan2 代替 atan 避免象限错误
    • 添加微小值防止除以零
    • 合理设置阈值判断相等
  4. 实时性优化

    • 预计算常见位姿的解
    • 使用查表法加速计算
    • 考虑使用Cython或Numba加速Python代码
def choose_best_solution(solutions, current_angles):
    """选择最优的逆解"""
    if not solutions:
        return None
    
    # 计算每个解与当前角度的距离
    distances = [np.sum((np.array(sol) - np.array(current_angles))**2) 
                for sol in solutions]
    
    # 选择距离最小的解
    return solutions[np.argmin(distances)]

7. 性能优化与扩展

对于需要更高性能的场景,可以考虑以下优化方法:

  1. 使用符号计算预生成公式

    import sympy as sp
     
    # 定义符号变量
    theta1, theta2, theta3, theta4, theta5, theta6 = sp.symbols('theta1:7')
    alpha1, alpha2, alpha3, alpha4, alpha5, alpha6 = sp.symbols('alpha1:7')
    a1, a2, a3, a4, a5, a6 = sp.symbols('a1:7')
    d1, d2, d3, d4, d5, d6 = sp.symbols('d1:7')
     
    # 符号化DH矩阵
    def sym_dh_matrix(theta, alpha, a, d):
        return sp.Matrix([
            [sp.cos(theta), -sp.sin(theta)*sp.cos(alpha), sp.sin(theta)*sp.sin(alpha), a*sp.cos(theta)],
            [sp.sin(theta), sp.cos(theta)*sp.cos(alpha), -sp.cos(theta)*sp.sin(alpha), a*sp.sin(theta)],
            [0, sp.sin(alpha), sp.cos(alpha), d],
            [0, 0, 0, 1]
        ])
     
    # 预计算变换矩阵
    T_total = sp.eye(4)
    for i in range(6):
        T_total = T_total * sym_dh_matrix(eval(f'theta{i+1}'), eval(f'alpha{i+1}'), 
                                         eval(f'a{i+1}'), eval(f'd{i+1}'))
    
  2. 使用Cython加速核心计算

    # cython: boundscheck=False, wraparound=False
    import numpy as np
    cimport numpy as np
    from libc.math cimport sin, cos, atan2, sqrt
     
    def cython_forward_kinematics(np.ndarray[np.float64_t, ndim=1] thetas, 
                                np.ndarray[np.float64_t, ndim=2] dh_params):
        cdef np.ndarray[np.float64_t, ndim=2] T = np.eye(4)
        cdef np.ndarray[np.float64_t, ndim=2] Ti
        cdef double theta, alpha, a, d
        cdef double ct, st, ca, sa
        cdef int i
         
        for i in range(6):
            theta = thetas[i] + dh_params[i,0]
            alpha, a, d = dh_params[i,1], dh_params[i,2], dh_params[i,3]
            ct, st = cos(theta), sin(theta)
            ca, sa = cos(alpha), sin(alpha)
             
            Ti = np.array([
                [ct, -st*ca, st*sa, a*ct],
                [st, ct*ca, -ct*sa, a*st],
                [0, sa, ca, d],
                [0, 0, 0, 1]
            ])
            T = np.dot(T, Ti)
         
        return T[:3,3], T[:3,:3]
    
  3. 支持其他机械臂型号

    class RobotArm:
        def __init__(self, dh_params):
            self.dh_params = np.array(dh_params)
            self.num_joints = len(dh_params)
         
        def forward_kinematics(self, thetas):
            """实例化的正运动学计算"""
            return forward_kinematics(thetas, self.dh_params)
         
        def inverse_kinematics(self, T):
            """实例化的逆运动学计算"""
            return inverse_kinematics(T, self.dh_params)
         
        def plot(self, thetas):
            """实例化的可视化方法"""
            fig = plt.figure(figsize=(10, 8))
            ax = fig.add_subplot(111, projection='3d')
            plot_robot(ax, thetas, self.dh_params)
            return fig, ax
     
    # 创建Gluon-6L3实例
    gluon_6l3 = RobotArm(dh_params)
    

8. 常见问题与调试技巧

在实现机械臂运动学过程中,可能会遇到以下典型问题:

  1. 逆解不存在

    • 检查目标位姿是否在工作空间内
    • 验证DH参数是否正确
    • 尝试微调目标位姿
  2. 解不准确

    • 检查角度计算是否使用了正确的象限(始终使用atan2)
    • 验证正运动学计算是否正确
    • 检查数值计算的精度问题
  3. 多解选择不当

    • 实现碰撞检测
    • 考虑关节限位
    • 记录历史解以保证运动连续性
  4. 奇异位形问题

    • 检测接近奇异位形的情况
    • 实现特殊处理逻辑
    • 考虑使用阻尼最小二乘法

调试时可以使用的工具方法:

def check_singularity(T, theta5):
    """检查是否处于奇异位形"""
    return abs(np.sin(theta5)) < 1e-6

def validate_dh_params(dh_params):
    """验证DH参数的有效性"""
    required_keys = ['theta', 'alpha', 'a', 'd']
    if len(dh_params) != 6:
        raise ValueError("6轴机械臂需要6组DH参数")
    for param in dh_params:
        if len(param) != 4:
            raise ValueError("每组DH参数需要4个值")
    return True

def normalize_angles(angles):
    """将角度归一化到[-π, π]区间"""
    return (angles + np.pi) % (2*np.pi) - np.pi

在完成这些基础工作后,可以进一步扩展功能,如:

  • 实现轨迹规划算法
  • 添加碰撞检测功能
  • 开发可视化调试工具
  • 集成到ROS等机器人框架中

机械臂运动学的实现是机器人控制的基础,理解这些核心算法将为你打开更高级的机器人开发大门。在实际项目中,建议从简单案例开始,逐步验证每个步骤的正确性,再扩展到完整系统。

更多推荐