3. 机械臂运动学基础:正运动学与逆运动学、DH参数、常用坐标系

机械臂运动学是机器人控制的理论基石。它回答两个核心问题:“关节转了多少,末端在哪?”(正运动学)和“末端要去哪,关节该转多少?”(逆运动学)。在ROS2中,无论是MoveIt2的路径规划、Rviz2中的模型显示,还是真实机械臂的伺服控制,底层都依赖运动学求解结果。本章将围绕DH参数建模、坐标系变换与正逆解逻辑展开,为后续编写ROS2控制节点打下数学基础。

3.1 常用坐标系:从世界到末端

在ROS2中,机械臂涉及多个坐标系,它们通过TF树(坐标变换树)关联。理解这些坐标系是理解运动学的第一步。

坐标系名称 ROS2中常用TF名称 物理含义
世界/基座坐标系 world / base_link 机械臂安装的固定参考点,通常位于基座中心地面
关节坐标系 joint1_link … joint6_link 每个关节轴心处的坐标系,随关节转动而运动
工具坐标系 tool0 / gripper_link 末端执行器(夹爪、吸盘)的参考点,用于控制末端位姿
用户/工件坐标系 object_frame 用户自定义,用于描述目标物体位置,便于编程

在Rviz2中,你可以通过“Fixed Frame”设置固定坐标系(通常为base_link),并显示TF树来观察各坐标系间的相对位姿。ROS2的tf2库负责维护这些坐标系的实时变换关系,而运动学计算正是提供这些变换的数学依据。

实操提示:在URDF(统一机器人描述格式)中,每个对应一个坐标系,每个定义了父子坐标系间的变换。URDF中的标签包含origin(相对父坐标系的平移与旋转)和axis(旋转轴),这些参数与DH参数存在对应关系,但URDF更直观,DH更适合数学推导。

3.2 DH参数:描述相邻关节的“四要素”

Denavit-Hartenberg(DH)参数是一种系统化描述相邻连杆间几何关系的方法。它用四个参数唯一确定两个相邻关节坐标系间的变换矩阵。对于标准DH(SDH)和修正DH(MDH),参数定义略有差异,ROS2中URDF默认使用类似MDH的视觉习惯,但运动学库(如KDL)内部仍按标准DH逻辑处理。这里以标准DH为例说明。

四个参数分别为:

  • ai-1(连杆长度):沿Xi-1轴,从Zi-1移动到Zi的距离。
  • αi-1(连杆扭转角):绕Xi-1轴,从Zi-1旋转到Zi的角度。
  • di(连杆偏距):沿Zi轴,从Xi-1移动到Xi的距离。
  • θi(关节角):绕Zi轴,从Xi-1旋转到Xi的角度(对于旋转关节,这是变量)。

以常见的六轴串联机械臂(如UR5e或自研教学臂)为例,其DH参数表通常如下(数值仅为示意):

关节i ai-1 (mm) αi-1 (°) di (mm) θi (初始°)
100162.50
2-425-900-90
3-392000
40-90133.30
509099.70
60-9099.60

有了DH表,相邻关节的齐次变换矩阵Tii-1可由下式得到:

T_i^(i-1) = Rot(z, θ_i) * Trans(z, d_i) * Trans(x, a_i) * Rot(x, α_i)

在C++中,你可以使用Eigen库手动实现该矩阵,或直接调用ROS2的kdl_parser和KDL::ChainFkSolverPos_recursive完成正解计算。

3.3 正运动学:从关节空间到笛卡尔空间

正运动学(Forward Kinematics, FK)是已知各关节角度,求解末端执行器在基座坐标系下的位姿(位置+姿态)。其本质是依次右乘各关节的齐次变换矩阵:

T_06 = T_01 * T_12 * T_23 * T_34 * T_45 * T_56

其中T_06左上3×3子矩阵为姿态(旋转矩阵R),右上3×1为位置向量P。在ROS2中,你可以通过tf2_ros的TransformBroadcaster发布该变换,或使用moveit_msgs::msg::RobotState直接设置关节位置并查询末端位姿。

下面是一个使用moveit_py或C++接口计算正解的伪代码示例:

// 假设使用MoveIt2的RobotState
robot_state::RobotStatePtr current_state = move_group->getCurrentState();
const robot_state::JointModelGroup* joint_model_group =
    current_state->getJointModelGroup("arm_group");

std::vector<double> joint_values = {0.0, -1.57, 0.0, -1.57, 0.0, 0.0};
current_state->setJointGroupPositions(joint_model_group, joint_values);

Eigen::Isometry3d end_effector_pose =
    current_state->getGlobalLinkTransform("tool0");
std::cout << "末端位置: " << end_effector_pose.translation().transpose() << std::endl;
std::cout << "末端姿态(四元数): "
          << Eigen::Quaterniond(end_effector_pose.linear()).coeffs().transpose() << std::endl;

正运动学计算简单、唯一且无奇异问题,常用于仿真验证、可视化以及逆解结果的校验。

3.4 逆运动学:从笛卡尔空间回到关节空间

逆运动学(Inverse Kinematics, IK)是给定末端目标位姿,求解满足条件的关节角度。这是运动规划中最具挑战性的部分,因为:

  • 多解性:同一末端位姿可能对应多组关节角(如肘部向上/向下)。
  • 可能无解:目标点超出机械臂工作空间。
  • 奇异位形:在特定姿态下,关节速度与末端速度的映射关系丢失自由度,导致求解不稳定。

IK求解方法主要分为两类:

  1. 解析法(封闭解):适用于满足Pieper准则的机械臂(后三个关节轴交于一点)。求解快、精度高,但需要针对具体构型推导公式。
  2. 数值法(迭代法):如雅可比伪逆法、牛顿-拉夫逊法。通用性强,但需要提供初始猜测,可能陷入局部最优,且计算量较大。

在ROS2中,MoveIt2默认使用KDL(Kinematics and Dynamics Library)的数值IK求解器,也支持配置TRAC-IK(一种更鲁棒的数值求解器)。实际使用中,TRAC-IK在奇异点附近表现更好,建议在kinematics.yaml中切换。

# 在move_group的config/kinematics.yaml中
arm_group:
  kinematics_solver: kdl_kinematics_plugin/KDLKinematicsPlugin
  # 或使用 trac_ik_kinematics_plugin/TRAC_IKKinematicsPlugin
  kinematics_solver_search_resolution: 0.005
  kinematics_solver_timeout: 0.05
  kinematics_solver_attempts: 3

下面的SVG图展示了正逆运动学在ROS2控制流水线中的位置与关系,以及IK多解与奇异点的概念。

正运动学 (FK) 关节角 θ₁…θ₆ T₀₆ = Π Tᵢ₋₁ᵢ 末端位姿 (x,y,z,R) 逆运动学 (IK) 目标位姿 (x,y,z,R) IK求解器 (KDL/TRAC-IK) 关节角 θ₁…θ₆ (多解) 校验 误差反馈 IK多解性 同一末端点,肘部上/下两种构型 奇异位形 关节轴共线,末端失去一个自由度 ▲ 求解难点

3.5 案例:在MoveIt2中完成一次正逆解验证

假设你已在RViz中加载了一个六轴机械臂(如panda或ur5e),并配置好MoveIt2。下面通过Python脚本演示如何调用正逆解接口:

import rclpy
from moveit_py.core import RobotModel, RobotState
from moveit_py.planning import MoveGroupPy

rclpy.init()
node = rclpy.create_node('fk_ik_demo')

# 加载机器人模型
robot_model = RobotModel(urdf='path/to/robot.urdf', srdf='path/to/robot.srdf')
robot_state = RobotState(robot_model)
joint_group = robot_model.get_joint_model_group('arm_group')

# --- 正运动学 ---
joint_values = [0.0, -0.785, 0.0, -2.356, 0.0, 1.571, 0.785]  # 7自由度示例
robot_state.set_joint_group_positions(joint_group, joint_values)
fk_pose = robot_state.get_global_link_transform('tool0')
print(f"FK 位置: {fk_pose.translation()}")

# --- 逆运动学 ---
target_pose = fk_pose  # 用正解结果作为目标
success = robot_state.set_from_ik(joint_group, target_pose, 'tool0', timeout=0.5)
if success:
    ik_joints = robot_state.get_joint_group_positions(joint_group)
    print(f"IK 关节角: {ik_joints}")
else:
    print("IK 求解失败")

node.destroy_node()
rclpy.shutdown()

运行该脚本,你会发现IK求得的关节角与初始值不同(多解),但末端位姿一致。这就是正逆解闭环验证的基本方法。

3.6 常见误区与复盘

误区一:混淆URDF中的坐标轴方向与DH参数。
URDF中<joint>的axis指定旋转轴,但DH参数中的Z轴方向必须与关节旋转轴对齐。若URDF建模时轴方向定义不一致,直接套用标准DH表会导致计算错误。建议使用kdl_parser从URDF生成KDL树,避免手动推导。
误区二:忽略IK求解时间与奇异点。
在实时控制中,IK求解可能耗时超过控制周期(如1kHz控制要求1ms内解出)。数值IK在奇异点附近可能迭代不收敛或产生极大关节速度。工程上常采用闭式解优先 + 数值解兜底的策略,并在规划路径时主动避开奇异区域。
复盘建议:当MoveIt2规划失败时,首先检查目标位姿是否在工作空间内(可用Rviz2的InteractiveMarker拖动末端测试);其次查看kinematics.yaml中的求解器与超时设置;最后确认TF树中base_link到tool0的变换是否连续无跳变。

运动学是连接“关节角”与“笛卡尔空间”的桥梁。正运动学提供确定性映射,逆运动学则引入多解与奇异问题。在ROS2中,MoveIt2封装了大部分求解细节,但理解DH参数与坐标系变换的本质,能帮助你在遇到规划失败、奇异规避或自定义机械臂时快速定位问题。下一阶段,我们将在此基础上引入速度级运动学(雅可比矩阵),为力控与轨迹跟踪做准备。