ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

机械臂正运动学解析:从DH参数到末端位姿的完整指南

机械臂正运动学解析:从DH参数到末端位姿的完整指南 做机械臂这几年经常有人拿着一张DH参数表来问我这串数字到底怎么变成末端的xyz坐标今天这篇“机械臂速成小指南”第九期就把正运动学分析这件事彻底讲清楚。不管你是从零复刻一台3D打印机械臂还是用总线舵机搭了个六轴玩具又或者是刚把UR10的URDF模型加载进ROS准备做仿真正运动学都是你绕不开的第一道门槛。我会从坐标系、DH参数、矩阵推导一直讲到Python代码实现和实地调试避坑尽量让没有学过机器人学的人也能跟上节奏。1. 正运动学分析到底在算什么1.1 一句话理解正运动学正运动学英文叫Forward Kinematics缩写FK。它的任务很简单已知机械臂每一个关节的转角或者移动关节的位移求末端执行器在空间里的位置和姿态。打个比方你抬手臂去够桌上的杯子。你的大脑知道肩膀转了多少度、肘关节弯曲了多少度然后就能大致判断手指头落在了哪里。机械臂也一样每个关节都装了电机和编码器我们能直接读到关节角度但控制系统真正关心的是末端法兰盘、吸盘、夹爪在世界坐标里的位姿。正运动学就是那座从关节空间到笛卡尔空间的桥。很多刚开始做机械臂的朋友上来就急着调PID、写轨迹规划结果发现夹爪永远抓不到目标点。原因往往是连最基本的正解都没搞清楚——你给机械臂发了个“关节动到某个角度”的指令但不知道这个角度对应的末端在哪里后面所有工作都是空中楼阁。1.2 为什么动手做机械臂第一件事是学它我见过不少人在B站和GitHub上复刻“稚晖君同款机械臂”3D打印件打了一堆总线舵机也转起来了但一到抓取环节就傻眼视觉识别到物块坐标机械臂却不知道该怎么把末端送过去。问题的根源就是缺少正运动学这个基础。视觉给出的是末端目标位置关节空间里各轴角度是执行机构的原始输入两者之间的换算必须靠正运动学来打通。更进一步轨迹规划、碰撞检测、雅可比矩阵、动力学分析全都要在正运动学正确之后才有意义。哪怕你用的是现成的MoveIt!它内部的 kinematics 插件也是先把URDF里的运动学模型读出来再通过求解器算正解和逆解。说白了正运动学就是机械臂的“出厂设定”。设定错了后面一切都是错的。反过来如果正运动学算得准调试视觉抓取、轨迹插补时你会省掉大量“蒙着眼睛调参”的痛苦。1.3 正运动学和逆运动学的边界逆运动学Inverse KinematicsIK是相反的过程给定末端目标位姿求关节角。大家经常把两者搞混其实它们的关系很像“地图导航”和“反查路线”。正运动学是唯一的给一组关节角末端位姿一定是唯一确定的。逆运动学则不一定唯一同一个末端位置可能对应好几组关节角比如六轴机械臂常见的肘上、肘下姿态所以IK求解器经常要加“最优解”“避奇异”之类的约束。我的建议是先写正解再碰逆解。正解代码量小、逻辑直观而且逆解算完的结果最终都要用正解来验证对不对。很多ROS开发者在MoveIt!里做逆解规划轨迹出问题后第一件事就是回到正解打印末端坐标检查路径点是否连续。所以本篇只钻正运动学逆运动学留到下一期再展开。2. 动手建模前先把坐标系这事搞明白2.1 坐标系怎么摆才不会乱正运动学本质上就是坐标变换而坐标变换的前提是坐标系定义清楚。机械臂上常见的几个坐标系包括基座坐标系Base Frame、关节坐标系Joint Frames、工具坐标系Tool Frame、以及世界坐标系World Frame。基座坐标系一般固定在机械臂底座工具坐标系固定在末端执行器上。建立坐标系时有个核心原则每个关节的z轴必须沿着该关节的旋转轴线移动关节则沿移动方向。至于x轴一般由相邻两z轴的公垂线方向确定。这个过程听起来抽象但实际做下来有章可循。我最开始自己建坐标系时吃过亏z轴方向反了后续所有角度都差180度末端位置能对姿态却全错。后来才总结出经验——先在纸上把每个关节旋转的正方向画出来用右手定则确定z轴再挨个建立坐标系宁可多花半小时画图也别在代码里瞎猜。2.2 DH参数两套主流约定别选错从关节1到关节N之间机械臂由连杆和关节构成。为了用数字描述这些结构1955年Denavit和Hartenberg提出了DH参数法。这套方法的精髓是用四个参数描述相邻两个坐标系之间的变换关节角theta、连杆偏距d、连杆长度a、连杆转角alpha。但这里有一个大坑DH参数分为标准型Standard DHSDH和改进型Modified DHMDH。两套约定对alpha、a、d、theta的定义顺序不一样变换矩阵相乘的先后顺序也不一样。很多初学者在网上看了两种资料各取一半结果算出来天差地别。简单对比一下约定类型变换顺序四个参数的意义标准DHSDHRot(z, theta) - Trans(z, d) - Trans(x, a) - Rot(x, alpha)d是沿上一关节z轴的距离a是沿x轴的距离改进DHMDHRot(x, alpha) - Trans(x, a) - Rot(z, theta) - Trans(z, d)d是沿当前关节z轴的距离a是沿x轴的距离实际工程里URUniversal Robots、Panda等很多六轴机械臂用MDH而早期教材和KUKA某些型号习惯用SDH。如果你面对的是UR10这类通过ROS控制的机械臂URDF里通常已经写好了关节位置和坐标变换但如果你想自己推导一遍一定要确定你用的DH表是哪套约定。我的建议是选一套用到底代码里注释写明“MDH”这样即使出了问题也方便回头排查。2.3 经典六轴机械臂DH表长什么样以常见的UR10机械臂为例它的MDH参数表大致长这样注意不同来源可能有微小差异实际以厂家手册为准关节ialpha(i-1)a(i-1)d(i)theta(i)1000.1625q12pi/200q230-0.4250q340-0.392250.1333q45pi/200.0997q56-pi/200.0996q6这里alpha表示连杆转角a表示连杆长度d表示连杆偏距theta就是我们要输入的关节角。看到这些数字你可能觉得有点干但真正用起来时它就是正运动学的“原料”。要提醒一句别把DH表里的参数和你机械臂实际打印的尺寸搞混。我自己做过一台3D打印机械臂图纸上大臂长度是120mm但打印、装配、拧紧螺丝之后实际两关节旋转轴之间的距离变成了120.8mm。0.8毫米的误差在正运动学里看着不大末端一放大比如大臂加小臂累计下来差出好几毫米视觉抓取照样偏。所以有条件的话DH参数最好实测校准而不是直接抄3D图纸。3. 从关节角度到末端位姿正运动学推导全过程3.1 一个连杆变换矩阵是怎么来的DH参数最终都要转化为4×4齐次变换矩阵。这个矩阵左上角3×3是旋转矩阵右上角3×1是平移向量左下角是0 0 0 1。一个矩阵就能同时表示“从上一坐标系转到下一坐标系”的姿态变化和原点位移。以SDH为例相邻两关节坐标系间的变换矩阵是[ T_i^{i-1} Rot(z, \theta_i) \cdot Trans(z, d_i) \cdot Trans(x, a_i) \cdot Rot(x, \alpha_i) ]展开成矩阵就是[ T_i^{i-1} \begin{bmatrix} \cos\theta_i -\sin\theta_i \cos\alpha_i \sin\theta_i \sin\alpha_i a_i \cos\theta_i \ \sin\theta_i \cos\theta_i \cos\alpha_i -\cos\theta_i \sin\alpha_i a_i \sin\theta_i \ 0 \sin\alpha_i \cos\alpha_i d_i \ 0 0 0 1 \end{bmatrix} ]如果用的是MDH矩阵形式略有不同顺序变成先绕x轴转alpha再沿x轴平移a然后绕z轴转theta沿z轴平移d。所以公式可以现查但约定不能混。建议在网上找一两个机械臂DH参考实现对照自己的机械臂把矩阵写一遍印象会深很多。3.2 两连杆平面臂手算示例别一上来就推六轴先拿最简单的两连杆平面机械臂练手。假设它有两个旋转关节都在同一个平面内运动。连杆长度分别是L1、L2关节角度分别是theta1、theta2。按照DH法你可以建立两个坐标系但平面问题不一定要硬套4×4矩阵。直接画三角形末端在基座坐标系下的位置就是[ x L_1\cos\theta_1 L_2\cos(\theta_1\theta_2) ] [ y L_1\sin\theta_1 L_2\sin(\theta_1\theta_2) ]末端姿态角则是theta1theta2。这个例子虽然简单但能帮你建立直觉末端位置不是简简单单“L1L2”而是由关节角的三角函数组合出来的。很多调参的新手在纸上写了一个期望末端坐标再逆推角度时发现不匹配就是因为忘了第二根杆的绝对角度是theta1theta2而不是theta2。3.3 六自由度正解把4×4矩阵连乘起来到了六轴思路和两连杆是一样的只是从2个坐标系扩展到7个坐标系基座坐标系加6个关节坐标系末端坐标系在关节6之后。具体步骤如下确定你用的DH约定填好6行DH表。把每一行DH参数代入相邻坐标系的变换矩阵公式得到T01、T12、T23、T34、T45、T56。把6个矩阵按顺序连乘得到末端相对于基座的齐次变换矩阵T06。从T06里取出位置向量和旋转矩阵就得到末端的位姿。注意连乘顺序不能反。矩阵乘法不满足交换律哪个关节在前就先乘哪个。我见过有人总是把顺序写反结果末端位置偶尔对、偶尔不对后来发现是T34和T45的顺序搞反了。建议在代码里直接写成T06 T01 T12 T23 T34 T45 T56一眼就能看出顺序。4. 用代码把正运动学跑起来4.1 Python实现几十行搞定FK写代码验证正运动学是最快的方式。我习惯用Python加NumPy代码短、能做矩阵乘法也能直接拿结果和ROS/仿真对比。下面是一个基于MDH的正运动学示例伪代码级别的可用实现import numpy as np def dh_transform(alpha, a, d, theta): 改进DHMDH变换矩阵 alpha: 连杆转角 (rad) a: 连杆长度 d: 连杆偏距 theta: 关节角 (rad) ct np.cos(theta) st np.sin(theta) ca np.cos(alpha) sa np.sin(alpha) return np.array([ [ct, -st, 0, a], [st * ca, ct * ca, -sa, -sa * d], [st * sa, ct * sa, ca, ca * d], [0, 0, 0, 1] ]) # UR10示例DH表MDH单位米角度用弧度 dh_params [ (0, 0, 0.1625, 0), # 关节1 (np.pi/2, 0, 0, 0), # 关节2 (0, -0.425, 0, 0), # 关节3 (0, -0.39225, 0.1333, 0), # 关节4 (np.pi/2, 0, 0.0997, 0), # 关节5 (-np.pi/2, 0, 0.0996, 0) # 关节6 ] def forward_kinematics(joint_angles): T np.eye(4) for (alpha, a, d, theta_offset), q in zip(dh_params, joint_angles): theta theta_offset q T T dh_transform(alpha, a, d, theta) return T # 示例全部关节归零 angles np.zeros(6) T06 forward_kinematics(angles) print(T06)上面代码里的dh_params我故意没有填theta偏移实际机械臂的DH表里每个关节还有一个初始零位偏移。如果你拿到的DH表里theta那一列写的是“theta_i q_i”那代码里就要加上这个偏移量。很多“差90度”的诡异问题都是偏移量没加对。跑完代码你会得到一个4×4矩阵最后一列的前三个数就是末端在基座坐标系下的xyz坐标。左上角的3×3矩阵是旋转矩阵可以用它算出欧拉角或四元数方便和ROS里的tf消息对起来。4.2 ROS/URDF/MoveIt 交叉验证写完Python正解怎么确认算得对不对最靠谱的办法是和ROS里的URDF模型做交叉验证。UR10、Panda这类机械臂官方或社区提供了完整的URDF文件。你用robot_state_publisher发布关节状态RViz里就会显示末端的坐标系位置。这时你打开一个终端发布一组关节角度再用tf2_ros读取tool0末端相对于base_link的坐标和你Python算出来的结果对比。如果两者一致就说明你的DH表、代码、矩阵公式全都没毛病。实际操作中我经常用ros2 run tf2_ros tf2_echo base_link tool0这类命令来查看位姿。如果你在Gazebo里做仿真也可以直接读取模型关节状态然后在外部脚本里用正运动学算一遍两边对照。这样做的价值在于URDF模型就是厂家或社区根据真实机械臂参数建的它基本可以当作“标准答案”。你不需要真的给机械臂通电就知道自己的运动学代码准不准。顺便回答一个常见问题UR10机械臂可以通过ROS控制吗当然可以而且UR官方有对应的驱动包。但不管用ROS控制还是用厂家示教器控制底层都要做正运动学计算。你把UR10的URDF在ROS里加载出来通过MoveIt!下发关节角度RViz里看到末端位置那个位置本质上就是由URDF里的关节坐标和连杆变换算出来的。理解了正运动学你再看ROS里这一套流程会非常有底。4.3 用数值方法自检结果如果你没有ROS环境或者不想搭仿真也可以用数值法给自己的正解做“体检”。思路很简单利用正运动学的微分近似。给某个关节加一个极小的角度扰动比如0.0001弧度其他关节不动重新算末端位置看末端位移的方向是否和该关节的旋转方向吻合。理论上末端位移速度应该等于雅可比矩阵乘关节角速度。你可以顺手算一下雅可比矩阵的列向量和数值差分得到的结果对比。如果差太多就说明正运动学代码里有bug。这个方法虽然听起来有点绕但它能帮你排查出很多“看起来对但姿态不对”的隐患。我自己写机械臂代码时习惯维护一个自检脚本随机生成100组关节角用正解算末端再用ROS里读出来的结果对比误差超过1毫米就报警。这一招在后续做视觉抓取时特别有用因为机械臂的微小偏差在相机坐标系下会被进一步放大。5. 正运动学实战中的坑我从调试现场整理的经验5.1 常见问题速查表我把自己和身边朋友踩过的坑整理成了一张表按出现频率排序方便你直接对照排查。问题现象可能原因排查方法末端位置完全不对差得很离谱DH参数表选错了约定SDH/MDH混用统一用一种约定按选定约定重新推导矩阵公式位置大致对但姿态偏了90度某个关节的z轴方向定义反了检查关节旋转正方向重新建立坐标系所有关节归零时末端位置不在预期位置机械零位没有对齐或DH表里的theta偏移没加把机械臂手动调到零位和DH表里的初始状态对照单个关节转90度末端运动轨迹是弧线但方向反了关节角符号和DH表里的正方向不一致手动旋转关节对比代码输出和实际运动方向代码里算对了但ROS里末端坐标对不上缺少工具坐标系TF变换或者URDF里base_link定义不一致确认TF树里base_link到tool0的完整链路偶尔对、偶尔不对角度单位混用度/弧度全局统一使用弧度制在接口处单独转换这张表后面三条是我特别想强调的。单位问题尤其阴险Python的math.sin默认按弧度但你在UI里输入的可能是角度很多人忘了转成弧度结果关节越多错得越离谱。5.2 三个容易忽略的细节细节一机械零位不是“关节角读数零”。你以为关节角度为0时机械臂看起来是竖直的但编码器零点可能偏了几度。这就是为什么DH表里的theta偏移那么重要。我在3D打印机械臂上装舵机时舵机中立位置和机械臂理想姿态经常差十几度必须测量并补偿到DH表或代码里。细节二相邻坐标系z轴的公垂线方向定义了x轴。很多人在纸上画坐标时喜欢随便定x轴方向导致a的正负号反了。最典型的例子就是UR10第二、三根连杆的a是负值如果记成了正数末端位置就会“镜像”到另一边。遇到这种问题不妨在纸上重新画一遍把x轴方向标清楚。细节三旋转矩阵的提取不是简单的“取前三行三列”就完事。如果你用欧拉角表示末端姿态必须明确用的是哪一套欧拉角约定ZYX还是ZYZ固定轴还是旋转轴。同一个旋转矩阵用不同约定算出来的欧拉角完全不同。所以和ROS接口做对照时最好先用四元数对齐再转欧拉角避免被约定坑死。5.3 调试效率提升小技巧正运动学调试最怕的是“全错”。所以我的习惯是分两步走先只让第一个关节动其余关节锁零看末端是不是绕基座z轴转圈再让第二个关节动看末端运动平面和预期是否一致。一个关节一个关节地验能很快把问题定位到具体某一行的DH参数上。另外一个很有用的做法是做一张“关节角—末端坐标对照表”。比如固定五个关节只动第六个关节每转30度记录一次末端位置手动用尺子量或者用激光测距来验证。这个方法听着土但能有效发现DH表误差。很多做总线舵机机械臂的朋友最后都靠这种笨办法把零点校准给做准了。结尾正运动学值得花一周时间吃透我在实际项目中最大的体会是正运动学看起来属于“理论课”但它其实是机械臂从仿真走向实物的关键一步。你可以在没有机械臂的时候用Python和URDF模型先把正解跑通等机械臂到手再花一个下午把机械零位、DH参数实测校准完整个系统就会变得特别“听话”。相反如果你跳过正运动学直接搞逆解或轨迹规划后面出了问题会非常难排查因为你根本不知道末端真实在哪。最后再分享一个小技巧无论你用ROS还是裸机代码都要把正运动学写成一个独立模块输入关节角输出位姿不需要依赖任何GUI。这样后续不管做视觉抓取、轨迹规划还是写一个漂亮的调试界面你都能直接复用这套“小腿”。机械臂这东西动起来靠电机但能不能精准到位拼的往往是正运动学这种基础功。希望这篇小指南能让你少走些弯路。
返回列表