用Python和NumPy一步步推导6轴机械臂的正逆解(附完整代码)
用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]
])
完整的正运动学计算流程:
- 为每个关节创建变换矩阵
- 将所有变换矩阵按顺序相乘
- 提取末端执行器的位置和姿态
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. 实际应用中的注意事项
在实际项目中应用这些算法时,需要考虑以下几个关键点:
-
奇异位形处理 :当机械臂处于奇异位形时,逆运动学可能无解或有无穷多解
- 检测奇异位形:当sin(θ5)接近0时
- 解决方案:微小调整目标位姿或采用数值方法
-
多解选择策略 :逆运动学通常有多个解
- 选择最接近当前姿态的解
- 选择使机械臂运动最小的解
- 避开关节限位
-
数值稳定性 :
- 使用
atan2代替atan避免象限错误 - 添加微小值防止除以零
- 合理设置阈值判断相等
- 使用
-
实时性优化 :
- 预计算常见位姿的解
- 使用查表法加速计算
- 考虑使用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. 性能优化与扩展
对于需要更高性能的场景,可以考虑以下优化方法:
-
使用符号计算预生成公式 :
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}')) -
使用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] -
支持其他机械臂型号 :
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. 常见问题与调试技巧
在实现机械臂运动学过程中,可能会遇到以下典型问题:
-
逆解不存在 :
- 检查目标位姿是否在工作空间内
- 验证DH参数是否正确
- 尝试微调目标位姿
-
解不准确 :
- 检查角度计算是否使用了正确的象限(始终使用atan2)
- 验证正运动学计算是否正确
- 检查数值计算的精度问题
-
多解选择不当 :
- 实现碰撞检测
- 考虑关节限位
- 记录历史解以保证运动连续性
-
奇异位形问题 :
- 检测接近奇异位形的情况
- 实现特殊处理逻辑
- 考虑使用阻尼最小二乘法
调试时可以使用的工具方法:
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等机器人框架中
机械臂运动学的实现是机器人控制的基础,理解这些核心算法将为你打开更高级的机器人开发大门。在实际项目中,建议从简单案例开始,逐步验证每个步骤的正确性,再扩展到完整系统。
更多推荐
所有评论(0)