坐标系与运动学基础:笛卡尔坐标系、关节坐标系、正运动学与逆运动学
各位同学,欢迎来到第二章。这一章的内容,是整个运动控制课程的基石。说白了,你后面要写的所有轨迹规划、插补算法,都离不开今天讲的这几个概念。
我记得刚入行那会儿,带我的老工程师丢给我一本机器人学教材,第一句话就是:“先把坐标系和运动学搞明白,不然你写的代码就是废的。”当时我不太理解,直到自己调试一台六轴机器人时,因为正逆解算错了,差点把末端执行器撞到工件上……嗯,从那以后,我再也不敢轻视这些“基础”了。
2.1 两种坐标系:笛卡尔坐标系 vs 关节坐标系
做运动控制,你脑子里必须同时装着两套坐标系。一套是笛卡尔坐标系,也叫世界坐标系、直角坐标系;另一套是关节坐标系,也叫轴坐标系。
核心区别一句话概括:
- 笛卡尔坐标系:描述工具末端在空间中的位置 (X, Y, Z) 和姿态 (Rx, Ry, Rz)。你看到的是“点”和“方向”。
- 关节坐标系:描述每个运动轴的角度或位置 (J1, J2, ..., Jn)。你看到的是“电机转了多少圈”。
举个例子。你让一个SCARA机器人去抓一个螺丝。在笛卡尔坐标系下,你关心的是“吸嘴移动到螺丝上方10mm处”;在关节坐标系下,你关心的是“大臂转30度,小臂转-45度,Z轴下降50mm”。
我个人习惯,在调试阶段先用关节坐标系点动,确认每个轴的运动范围;在轨迹规划阶段再用笛卡尔坐标系,因为更直观。
| 特性 | 笛卡尔坐标系 | 关节坐标系 |
|---|---|---|
| 描述对象 | 末端执行器的位姿 | 每个关节的变量 |
| 直观性 | 高,符合人类空间直觉 | 低,需要理解机械结构 |
| 控制难度 | 需要运动学解算 | 直接控制电机 |
| 典型应用 | 焊接、涂胶、搬运 | 回零、单轴调试、避障 |
避坑指南:我曾经在调试一个六轴机器人时,直接在笛卡尔坐标系下发了一个直线运动指令,结果机器人走到一半突然报警“奇异点”。后来才发现,在笛卡尔空间看起来是一条直线,但在关节空间可能经过某个轴无法到达的位置。所以,笛卡尔空间的路径,必须经过逆运动学验证,确认每个关节都在允许范围内。
2.2 正运动学:从关节到笛卡尔
正运动学,就是已知每个关节的角度(或位置),求末端执行器在笛卡尔空间中的位姿。说白了,是“正向推导”。
你想想看,机器人的每个关节都是一个旋转或平移副。从基座开始,经过一系列连杆和关节的变换,最终到达末端。这个过程,在数学上就是齐次变换矩阵的连乘。
对于串联机器人,正运动学公式可以写成:
T_0n = T_01 * T_12 * T_23 * ... * T_(n-1)n
其中,T_0n 是从基座坐标系到末端坐标系的变换矩阵,包含了位置和姿态信息。每个 T_(i-1)i 是根据 Denavit-Hartenberg (D-H) 参数建立的。
我个人习惯,在写代码前先画一个D-H参数表。比如一个简单的两连杆平面机械臂:
| 连杆 i | ai-1 (连杆长度) | αi-1 (连杆扭角) | di (连杆偏距) | θi (关节角) |
|---|---|---|---|---|
| 1 | L1 | 0 | 0 | θ1 |
| 2 | L2 | 0 | 0 | θ2 |
对应的正运动学代码实现(Python示例):
import numpy as np
def forward_kinematics_2link(theta1, theta2, L1, L2):
"""
两连杆平面机械臂正运动学
:param theta1: 关节1角度 (rad)
:param theta2: 关节2角度 (rad)
:param L1: 连杆1长度
:param L2: 连杆2长度
:return: 末端位置 (x, y)
"""
x = L1 * np.cos(theta1) + L2 * np.cos(theta1 + theta2)
y = L1 * np.sin(theta1) + L2 * np.sin(theta1 + theta2)
return x, y
# 示例:关节角30度和45度,连杆长度均为1
x, y = forward_kinematics_2link(np.radians(30), np.radians(45), 1.0, 1.0)
print(f"末端位置: ({x:.3f}, {y:.3f})")
# 输出: 末端位置: (1.573, 1.207)
小技巧:正运动学是唯一确定的。给定一组关节角,末端位姿是唯一的。所以调试时,你可以先用正运动学算出一个已知位姿,然后手动转动关节到对应角度,看末端是否到达预期位置。这是验证D-H参数是否正确的最快方法。
2.3 逆运动学:从笛卡尔到关节
逆运动学就反过来了:已知末端在笛卡尔空间的位姿,求各个关节的角度。这是运动控制中最头疼的部分,没有之一。
为什么头疼?因为逆运动学通常有多解,甚至无解。你想想看,一个六轴机器人,末端停在空间某一点,手臂可以“弯着”过来,也可以“伸着”过来,肘关节可以朝上也可以朝下。这就是多解。
对于六轴机器人,逆运动学没有通用的解析解,通常需要数值迭代(比如牛顿-拉夫森法)。但对于一些特定构型(如PUMA 560、SCARA),可以推导出解析解。
以刚才的两连杆机械臂为例,逆运动学有解析解:
import numpy as np
def inverse_kinematics_2link(x, y, L1, L2):
"""
两连杆平面机械臂逆运动学
:param x, y: 末端位置
:param L1, L2: 连杆长度
:return: 两组可能的关节角 (theta1, theta2)
"""
# 计算cos(theta2)
cos_theta2 = (x**2 + y**2 - L1**2 - L2**2) / (2 * L1 * L2)
# 检查可达性
if abs(cos_theta2) > 1:
raise ValueError("目标点不可达")
# 肘部向上和向下两种解
theta2_up = np.arccos(cos_theta2) # 肘部向下
theta2_down = -np.arccos(cos_theta2) # 肘部向上
# 计算theta1
def calc_theta1(theta2):
k1 = L1 + L2 * np.cos(theta2)
k2 = L2 * np.sin(theta2)
return np.arctan2(y, x) - np.arctan2(k2, k1)
theta1_up = calc_theta1(theta2_up)
theta1_down = calc_theta1(theta2_down)
return (theta1_up, theta2_up), (theta1_down, theta2_down)
# 示例:末端在(1.573, 1.207)
solutions = inverse_kinematics_2link(1.573, 1.207, 1.0, 1.0)
for i, (t1, t2) in enumerate(solutions):
print(f"解{i+1}: θ1={np.degrees(t1):.1f}°, θ2={np.degrees(t2):.1f}°")
# 输出: 解1: θ1=30.0°, θ2=45.0°
# 输出: 解2: θ1=75.0°, θ2=-45.0°
避坑指南:我曾经在写一个六轴机器人的逆运动学数值解时,迭代不收敛,查了半天发现是初始值给得太随意。记住,数值逆运动学对初始值非常敏感。我的建议是:先用上一时刻的关节角作为当前时刻的初始值,这样迭代快且稳定。另外,一定要做可达性检查,别让机器人去够它够不着的地方。
2.4 知识体系总览
下面这张图,是我自己总结的坐标系与运动学知识体系。你可以把它当作本章的“地图”。
2.5 工程中的选择策略
逆运动学有多解,那到底选哪个?我个人的经验是,遵循以下优先级:
- 关节限位优先:首先排除超出软限位或硬限位的解。
- 运动连续性:选择与当前关节角最接近的解,避免大范围跳动。
- 避障需求:如果有障碍物,选择能让机器人远离障碍物的构型。
- 能耗最优:在满足以上条件后,选择关节运动量最小的解。
小技巧:在实际代码中,我会把逆运动学的所有解都算出来,然后写一个select_best_solution()函数,传入当前关节角、关节限位、障碍物信息等,返回最优解。这样逻辑清晰,也方便后期维护。
好了,这一章的内容就到这里。坐标系和运动学是运动控制的“内功”,练好了,后面写轨迹规划、插补算法时就会顺手很多。记住,正运动学是基础,逆运动学是核心,两者缺一不可。