坐标系与运动学基础:笛卡尔坐标系、关节坐标系、正运动学与逆运动学

各位同学,欢迎来到第二章。这一章的内容,是整个运动控制课程的基石。说白了,你后面要写的所有轨迹规划、插补算法,都离不开今天讲的这几个概念。

我记得刚入行那会儿,带我的老工程师丢给我一本机器人学教材,第一句话就是:“先把坐标系和运动学搞明白,不然你写的代码就是废的。”当时我不太理解,直到自己调试一台六轴机器人时,因为正逆解算错了,差点把末端执行器撞到工件上……嗯,从那以后,我再也不敢轻视这些“基础”了。

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 知识体系总览

下面这张图,是我自己总结的坐标系与运动学知识体系。你可以把它当作本章的“地图”。

坐标系与运动学知识体系 笛卡尔坐标系 关节坐标系 正运动学 关节 → 笛卡尔 逆运动学 笛卡尔 → 关节 核心方法 D-H参数法 · 齐次变换矩阵 解析解 · 数值迭代 工程挑战 多解选择 · 奇异点规避 可达性检查 · 初始值敏感 正运动学唯一确定,逆运动学通常多解 实际工程中需根据约束条件选择最优解

2.5 工程中的选择策略

逆运动学有多解,那到底选哪个?我个人的经验是,遵循以下优先级:

  1. 关节限位优先:首先排除超出软限位或硬限位的解。
  2. 运动连续性:选择与当前关节角最接近的解,避免大范围跳动。
  3. 避障需求:如果有障碍物,选择能让机器人远离障碍物的构型。
  4. 能耗最优:在满足以上条件后,选择关节运动量最小的解。

小技巧:在实际代码中,我会把逆运动学的所有解都算出来,然后写一个select_best_solution()函数,传入当前关节角、关节限位、障碍物信息等,返回最优解。这样逻辑清晰,也方便后期维护。

好了,这一章的内容就到这里。坐标系和运动学是运动控制的“内功”,练好了,后面写轨迹规划、插补算法时就会顺手很多。记住,正运动学是基础,逆运动学是核心,两者缺一不可。