简介:DH2URDF 是一份面向机器人建模、运动学教学与 URDF 入门学习者的轻量转换工具包,重点解决依据 Denavit-Hartenberg 参数表手工搭建连杆坐标系和关节描述时步骤繁琐、容易出错的问题。压缩包共 9 个文件,约 138KB,以 4 个 JavaScript 脚本承担核心转换与网页交互,并配合 HTML 演示页面、空白 URDF 模板、Markdown 说明文档及 litcoffee 源码等,体量小巧但结构完整,适合在浏览器中直接运行和对照查看。转换结果会将各轴放置到 DH 参考系上,并以 URDF 圆柱体形式呈现,读者可在此基础上继续补全机器人模型,再结合关节值观察姿态变化。已有 1049 人学习,说明其在相关学习中具有一定参考价值;对于高校机器人课程设计、ROS/URDF 建模练习以及需要把 DH 表快速落到模型文件中的开发者,这份材料既可作为教学演示,也便于理解参数与模型元素之间的映射关系并做二次开发。
1. 手上有一张 DH 表,但整条工具链只认 URDF
做机械臂控制的人大多经历过这个场面:运动学已经在 MATLAB 或一份 Excel 里跑通了,DH 表就四列,正解反解都对得上,末端位姿和实测一毫米不差。可一旦要把这台臂放进 RViz 看姿态、进 MoveIt 做规划、或者丢进某个基于刚体动力学的仿真器里跑,第一步就会卡住——这些工具要的是 URDF,而 URDF 里根本没有a、alpha、d、theta这四个字段。DH2URDF 这条链路做的事情很具体:把一张 Denavit-Hartenberg 参数表,翻译成 URDF 的 link/joint 树,让关节轴位置、连杆偏置、末端法兰位姿在转换后依然一致。它解决的问题不复杂但极其消磨时间——手工在 URDF 里试origin的 xyz 和 rpy,改一个数看一眼 RViz,十几次之后就不知道自己错在哪了。适合两类人:手上有现成 DH 表想把模型搬进 URDF 生态的,以及反过来要从 URDF 反推 DH 做标定的。
2. DH 表和 URDF 的四个参数对不上三个字段:先搞清楚谁吃掉了谁
2.1 标准 DH 与改进 DH 的连乘顺序,决定了你该读哪一列
同样叫「DH 参数」,实际存在两套写法,而且两套写法在数学上不等价。标准 DH( distal 形式,很多国产教材和早期文献默认这套)把单行参数写成四个基本变换的右乘:
T(i-1 -> i) = Rot_z(theta_i) · Trans_z(d_i) · Trans_x(a_i) · Rot_x(alpha_i)改进 DH(Craig 的 proximal 形式,多数机器人厂商的 datasheet 和 ROS 生态里的资料更偏这套)把固定偏置挪到了前面:
T(i-1 -> i) = Rot_x(alpha_{i-1}) · Trans_x(a_{i-1}) · Rot_z(theta_i) · Trans_z(d_i)差别看着只是换序,落到具体数值上是灾难性的。标准 DH 里a_i、alpha_i描述的是「沿本连杆走到下一个关节」的那段几何;改进 DH 里a_{i-1}、alpha_{i-1}描述的是「从上一个关节进到本连杆坐标系」的那段偏置。同一台六轴臂,两套写法填出来的表,第一行的a一个是 0、一个不是 0,alpha的下标整体错一位。我见过最典型的翻车是把厂商给的改进 DH 表直接丢进按标准 DH 写的转换脚本里,末端位置差了三十多厘米,但因为关节数多、每节误差被摊得很均匀,肉眼看 RViz 竟不觉得有明显异常。
判别办法有三个,按可靠性排序。最可靠的是看alpha的定义:alpha_i是z_{i-1}到z_i的夹角就是标准 DH,是z_i到z_{i+1}的夹角就是改进 DH。次可靠的是看第一行:标准 DH 的第一行a_1必然是 0(因为 base 到第一个关节没有 common normal 可言),改进 DH 的第一行alpha_0和a_0通常为 0。最不可靠的是问人——口头传过来的「我们是标准 DH」有相当比例是错的,一定要拿数据验。
现成的验法很土但管用:把这组表分别按两套公式各算一遍正运动学,取几组随机关节角,和厂商给的末端位姿、或者实物示教器上读到的位姿对一遍。哪套对上用哪套,别纠结命名。这一步是后面所有工作的地基,地基歪了,URDF 写得再漂亮也是废的。
2.2 URDF 的 joint origin、axis、limit 分别吃掉 DH 的哪一部分
URDF 描述运动学的核心是<joint>元素,它只有五个真正影响几何的字段组合:
| URDF 字段 | 含义 | 对应 DH 里的部分 |
|---|---|---|
origin的xyz | 父 link 坐标系下,关节坐标系原点的平移 | a、d的组合 |
origin的rpy | 父 link 坐标系下,关节坐标系的固定姿态 | alpha(以及 theta 的零位偏置) |
axis的xyz | 关节运动轴,在关节坐标系里表达 | 恒为0 0 1(DH 的定义使然) |
type | revolute / continuous / prismatic / fixed | theta是变量还是常量 |
limit | 位置上下限、速度、力矩 | DH 表里通常没有,得另外补 |
关键点在axis。DH 约定里,关节变量theta_i永远是绕z_{i-1}转的,这个约定是整套 DH 体系能成立的前提。而 URDF 里,关节的旋转发生在关节坐标系(也就是 child link 坐标系)自身,绕axis指定的那根轴。两者要对上,唯一的做法是让关节坐标系的z轴和 DH 的z_{i-1}重合,然后把axis写成0 0 1。这一步想通了,剩下的全是代数。
limit是个容易被忽略的坑。URDF 对revolute类型强制要求lower和upper,而且两者不能相等;continuous反而不允许写limit(写了有些解析器会警告)。从 DH 表转过来的模型,DH 表里通常只有几何,没有行程限制,这块必须从电机侧或者机械限位另外填。填 0 到 0 会直接让后面所有基于该模型的规划器报错。
2.3 连杆坐标系和关节坐标系不是一回事,惯量和网格不能照抄
这一点在纯运动学验证阶段不会暴露,一旦开始加载 STL 网格或者做动力学仿真就会集中爆发。
DH 的 link frame 定义在连杆的远端——第 i 个坐标系的原点落在关节 i+1 的轴线上,x_i沿 common normal 指过去。而 URDF 的 child link frame 严格等于 joint frame,也就是关节 i 的轴线上、theta_i转过之后的位置。两者之间差着一个固定的刚体变换。
后果很直接:如果你从 CAD 里量出某个连杆的质心相对「DH 坐标系 i」的偏移是(x, y, z),然后原样写进<inertial><origin xyz="x y z"/>,这个质心就贴错位置了,而且错得不多不少,正好差那一个固定变换。轻则动力学仿真出来的关节力矩偏一点,重则整个模型在重力下自己往下塌。
我的处理习惯是:转换脚本只负责运动学主干,<inertial>和<visual>统一留成占位,等 URDF 的运动学用正解对拍通过之后,再单独一趟把惯量和网格补进去。两件事混在一起做,出问题时你分不清是 DH 读错了还是网格偏移写错了。另外要提前认清一个边界:URDF 是树,不支持闭链。并联机构、四连杆、带钢丝绳耦合的欠驱动手,这些都没法用纯 URDF 表达,只能靠<mimic>近似或者换 SDF、MJCF 这类支持闭链的格式。DH2URDF 这条路只对串联开链成立,别硬套。
3. 把一张 DH 表算成 URDF:矩阵怎么拆成 xyz 和 rpy
3.1 拆法只有一个:把连乘写成「固定 origin + 纯旋转」
URDF 的关节在数学上就是一个约定:从父 link 到 child link 的变换,等于一个固定的origin变换,右乘一个绕axis的转角变换。写成矩阵:
T(i-1 -> i) = O_i · Rot_z(q_i)其中O_i是 URDF 里origin对应的齐次矩阵,q_i是关节变量。而标准 DH 的单行变换是:
T(i-1 -> i) = Rot_z(theta_i) · Trans_z(d_i) · Trans_x(a_i) · Rot_x(alpha_i)两边要相等,就得把 DH 的连乘重新分组。做法是把它当成一个长串连乘,然后把每个Rot_z(theta_i)往后挪一位:
Rot_z(θ1)·A1·Rot_z(θ2)·A2·…·Rot_z(θn)·An其中A_i = Trans_z(d_i)·Trans_x(a_i)·Rot_x(alpha_i)。挪完之后每一项变成A_{i-1}·Rot_z(θ_i),正好对上 URDF 的O_i · Rot_z(q_i)形式,于是:
O_i = A_{i-1} = Trans_z(d_{i-1}) · Trans_x(a_{i-1}) · Rot_x(alpha_{i-1})把A_{i-1}展开成平移加旋转:平移部分是(a_{i-1}, 0, d_{i-1})(Trans_z和Trans_x互不干扰,直接相加),旋转部分只有Rot_x(alpha_{i-1})。写进 URDF 就是xyz="a_{i-1} 0 d_{i-1}"、rpy="alpha_{i-1} 0 0"。
有个尾巴要处理:A_n在整个连乘里没被任何O吃掉,因为它后面没有Rot_z了。所以标准 DH 转出来的 URDF 需要额外加一个fixed类型的关节,origin填(a_n, 0, d_n)和(alpha_n, 0, 0),作为末端法兰或工具坐标系。漏掉这一步,末端会莫名其妙短一截。
改进 DH 的分组更省事。Rot_z(theta_i)和Trans_z(d_i)都绕同一根轴,可以交换,于是单行变换改写成[Rot_x(alpha_{i-1})·Trans_x(a_{i-1})·Trans_z(d_i)] · Rot_z(theta_i)。方括号里就是O_i,一口气吃到第 n 行,末端不需要任何补偿关节。
3.2 两类 DH 的 origin 公式与那个必然踩的索引坑
把上面两步整理成能直接抄的表:
| 约定 | 关节 i 的origin xyz | 关节 i 的origin rpy | 末端补偿关节 |
|---|---|---|---|
| 标准 DH | (a_{i-1}, 0, d_{i-1}) | (alpha_{i-1}, 0, 0) | 需要,(a_n, 0, d_n)/(alpha_n, 0, 0) |
| 改进 DH | `(a_{i-1}, -d_i·sin(alpha_{ |
本文还有配套的精品资源,点击获取