简介:DH2URDF 是一份将 Denavit-Hartenberg 参数表转换为统一机器人描述格式(URDF)的 JavaScript 工具资源,面向机器人建模、运动学仿真与 ROS 学习方向的开发者及学生。它解决的是手工编写 URDF 繁琐、易错的问题:输入 DH 表即可生成对应 URDF,并把转换后的坐标轴以圆柱体形式放置在 DH 参考系上,便于进一步搭建完整机器人模型。压缩包共 9 个文件,约 138KB,包含 4 个 js 脚本、1 个 html 演示页、1 个 urdf 模板、1 个 litcoffee 源码及 license、md 说明等,核心转换逻辑与网页演示相互配合,另附数学与模板辅助脚本。已有 1049 人学习下载。读者可借此理解 DH 参数与 URDF 关节、连杆的映射关系,掌握从参数表到可视化模型的完整流程,并可在配套网站查看 URDF 与关节值,适合作为机器人建模入门与工具脚本参考。
1. DH2URDF:把一张DH参数表变成机器人能跑的URDF文件
如果你手头有一台串联机械臂,运动学参数通常以Denavit-Hartenberg表的形式躺在论文或规格书里,四个参数一行,看着清爽。但仿真环境不认这张表,它要的是URDF——一种用XML描述连杆、关节、坐标系关系的统一格式。手动把DH表翻译成URDF,是件极其折磨人的事:每个关节的origin要算rpy,每个连杆的视觉几何要单独配,改一个alpha角,后面全得跟着动。DH2URDF这个项目就是冲着这个痛点来的,用JavaScript写的一套转换工具,输入DH参数,输出可直接加载的URDF。它适合正在做机械臂仿真、运动学验证、或者需要批量生成机器人模型的开发者,尤其是那些不想在坐标变换上反复翻车的同学。
2. 核心转换逻辑:从四参数到XML树的映射规则
2.1 DH参数与URDF关节坐标系的对应关系
标准DH参数描述的是相邻两个连杆坐标系之间的变换,四个参数分别是:连杆长度a、连杆扭角alpha、关节偏距d、关节转角theta。在URDF里,每个关节对应一个 元素,关节的origin需要表达父连杆坐标系到子连杆坐标系的变换。这里有个容易混淆的点:DH表里的变换是“从连杆i-1到连杆i”,而URDF的关节origin是“从父link到子link”,两者在串联结构里恰好对应,但旋转顺序必须严格按DH约定来。
常见做法是,把DH变换拆成四步:绕Z轴旋转theta、沿Z轴平移d、沿X轴平移a、绕X轴旋转alpha。这四步的乘积就是关节origin的完整变换矩阵。DH2URDF内部就是按这个顺序构造齐次变换矩阵,然后从矩阵反解出rpy和xyz。
// 根据DH参数构造4x4齐次变换矩阵 function dhToMatrix(a, alpha, d, theta) { const ct = Math.cos(theta), st = Math.sin(theta); const ca = Math.cos(alpha), sa = Math.sin(alpha); // 标准DH变换矩阵:RotZ(theta) * TransZ(d) * TransX(a) * RotX(alpha) return [ [ct, -st * ca, st * sa, a * ct], [st, ct * ca, -ct * sa, a * st], [0, sa, ca, d ], [0, 0, 0, 1 ] ]; }这段代码是整个转换的基石。参数a和d是长度量,单位要和URDF保持一致,通常用米;alpha和theta是弧度制,如果原始表给的是角度,得先转弧度。矩阵构造完之后,平移部分直接取第四列的前三个元素作为origin的xyz,旋转部分需要转成RPY欧拉角。
2.2 从旋转矩阵提取RPY并生成joint节点
URDF的 里, 的rpy属性用的是固定轴XYZ欧拉角,也就是先绕X转roll,再绕Y转pitch,最后绕Z转yaw。而从DH矩阵提取RPY,需要处理万向锁和象限判断,这是最容易出错的地方。DH2URDF里用了一套标准的提取逻辑,核心是判断pitch角是否接近±90度,如果是,就退化处理。
// 从旋转矩阵提取RPY(固定轴XYZ) function matrixToRPY(R) { let roll, pitch, yaw; const sy = Math.sqrt(R[0][0] * R[0][0] + R[1][0] * R[1][0]); if (sy > 1e-6) { roll = Math.atan2(R[2][1], R[2][2]); pitch = Math.atan2(-R[2][0], sy); yaw = Math.atan2(R[1][0], R[0][0]); } else { // 万向锁:pitch接近±90度 roll = Math.atan2(-R[1][2], R[1][1]); pitch = Math.atan2(-R[2][0], sy); yaw = 0; } return { roll, pitch, yaw }; }拿到rpy和xyz之后,就可以拼出 节点。每个关节还需要指定类型:旋转关节用revolute,移动关节用prismatic。DH表里如果theta是变量,就是revolute;如果d是变量,就是prismatic。DH2URDF会根据哪一列是符号变量来自动判断,这个逻辑在批量处理时特别省事。
2.3 连杆几何与惯性参数的填充策略
URDF不只是运动学,仿真还需要视觉和碰撞几何。DH表本身不包含这些信息,DH2URDF的做法是给每个连杆生成一个默认的圆柱体,半径和长度根据相邻关节的a和d估算。常见做法是:连杆长度取两个关节原点之间的距离,半径取a和d中较小值的一半。惯性矩阵按实心圆柱公式算,质量默认给1千克,这些都可以在配置里覆盖。
// 生成默认连杆几何与惯性 function createLink(name, length, radius, mass = 1.0) { const ixx = (1/12) * mass * (3 * radius * radius + length * length); const izz = 0.5 * mass * radius * radius; return { name, visual: { geometry: { cylinder: { radius, length } } }, collision: { geometry: { cylinder: { radius, length } } }, inertial: { mass, inertia: { ixx, iyy: ixx, izz, ixy: 0, ixz: 0, iyz: 0 } } }; }这段代码生成的连杆虽然粗糙,但足够让URDF在RViz或Gazebo里加载起来。实际项目里,你可以在生成的URDF基础上替换visual的mesh,或者调整inertial参数。DH2URDF把运动学部分做对,几何和惯性留出接口,这个取舍很务实。
3. 项目结构与运行方式:怎么把DH表喂进去
3.1 目录结构与模块划分
DH2URDF的代码结构很直白,没有过度设计。根目录下通常有src、examples、dist三个文件夹。src里放核心逻辑,按功能拆成dh-parser、matrix-utils、urdf-builder、cli四个模块。examples里放几组DH参数示例,包括常见的六轴机械臂和SCARA构型。dist是打包后的产物,可以直接在浏览器里跑,也可以Node.js调用。
| 模块 | 职责 | 关键函数 |
|---|---|---|
| dh-parser | 解析DH表,支持CSV和JSON | parseDHTable |
| matrix-utils | 矩阵运算与RPY提取 | dhToMatrix, matrixToRPY |
| urdf-builder | 拼装URDF XML | buildURDF, createLink |
| cli | 命令行入口 | main |
这种拆分的好处是,如果你只想在网页里做个转换工具,直接引urdf-builder就行;如果要批量处理,用cli更顺手。
3.2 安装与命令行运行
项目用npm管理依赖,Node.js版本建议14以上。安装步骤很标准:
git clone <仓库地址> cd dh2urdf npm install npm run build构建完成后,可以用命令行转换DH表。假设你有一个dh.csv,每行是a, alpha, d, theta,theta列用q1、q2这样的符号表示变量:
node dist/cli.js --input examples/dh.csv --output robot.urdf --name my_robot参数说明:--input指定DH表文件,--output指定输出URDF路径,--name是机器人名称,会作为 标签的name属性。如果DH表里theta是具体数值而不是符号,生成的关节会被固定,适合做静态模型。
3.3 在浏览器里直接转换
如果不想装Node环境,dist目录下有个index.html,用浏览器打开就能用。页面里有个文本框,粘贴DH参数,点转换,右侧实时生成URDF。这个页面用的是同一套核心逻辑,只是包了一层UI。对于快速验证一组参数,这种方式最省事。
提示:浏览器版本默认不处理文件上传,如果需要批量转换,还是走命令行。
3.4 验证生成的URDF
生成的URDF可以用check_urdf工具验证语法,或者在RViz里加载看模型。常见做法是先用check_urdf过一遍,确认没有XML结构错误,再丢进RViz看关节坐标系对不对。如果RViz里模型散架了,多半是某个关节的origin算错了,回头检查DH表的alpha和a是否对应。
check_urdf robot.urdf这条命令会输出关节树结构,如果某个关节的parent或child链接不上,会直接报错。这是最快速的排查手段。
4. 避坑与常见问题:DH表转URDF时容易翻车的地方
4.1 现象:RViz里模型关节位置全错,连杆飘在空中
原因:DH参数的角度单位是度,但代码按弧度处理。很多规格书里alpha和theta给的是角度值,直接喂进去就会导致旋转量放大57倍。解决:在解析阶段加一个单位判断,或者手动把角度转弧度。DH2URDF的cli里有个--angle-unit参数,默认是rad,如果表里是度,改成deg。
4.2 现象:生成的URDF加载报错,提示joint origin的rpy不是有效数字
原因:旋转矩阵提取RPY时遇到万向锁,分母接近零,算出了NaN。解决:在matrixToRPY里加阈值判断,当sy小于1e-6时走退化分支。这个坑在alpha接近±90度时特别常见,比如某些直角坐标机器人。
4.3 现象:关节类型判断错误,旋转关节被当成固定关节
原因:DH表里theta列如果是符号变量,但解析时被当成字符串常量,就会生成fixed类型的joint。解决:在dh-parser里明确区分数值和符号,符号变量统一用q开头命名,解析时检查是否匹配/^q\d+$/。如果匹配,关节类型设为revolute,否则设为fixed。
4.4 现象:连杆惯性矩阵导致Gazebo里机器人抖动或飞走
原因:默认质量1千克,但惯性张量按实心圆柱算,如果连杆长度和半径比例失调,惯性矩阵可能不满足正定性。解决:在createLink里加一个检查,确保ixx + iyy >= izz,不满足就调整半径。或者直接在Gazebo里把惯性参数替换成从CAD导出的值。
4.5 现象:批量转换时,多个URDF的link名称冲突
原因:DH2URDF默认用link_0、link_1命名,如果同时加载多个机器人到同一个仿真场景,名称会撞车。解决:在cli里加--prefix参数,给所有link和joint名称加前缀。这个习惯在多人协作的仿真项目里特别重要。
5. 二次开发与改造:让DH2URDF适配你的机器人
5.1 替换默认几何为真实mesh
DH2URDF生成的圆柱体只是占位符,实际项目里你需要把visual和collision替换成CAD导出的STL或DAE。改造点在urdf-builder的createLink函数里,把cylinder替换成mesh,并指定filename和scale。常见做法是维护一个link名称到mesh路径的映射表,在生成URDF时查表替换。
// 改造createLink,支持mesh映射 function createLinkWithMesh(name, meshMap) { const meshPath = meshMap[name]; if (meshPath) { return { name, visual: { geometry: { mesh: { filename: meshPath, scale: '1 1 1' } } }, collision: { geometry: { mesh: { filename: meshPath, scale: '1 1 1' } } } }; } // 回退到默认圆柱 return createLink(name, 0.1, 0.05); }这个改造不影响运动学部分,只替换几何显示。meshMap可以从JSON文件加载,方便不同机器人切换。
5.2 增加关节限位和动力学参数
URDF的 里还可以配 和 ,DH表里没有这些信息,但实际仿真需要。改造思路是在DH表里增加两列:关节限位和阻尼。解析时如果检测到这两列,就填充到joint节点里。如果没有,就给默认值,比如限位±180度,阻尼0.1。
<joint name="joint_1" type="revolute"> <parent link="link_0"/> <child link="link_1"/> <origin xyz="0 0 0.1" rpy="0 0 0"/> <axis xyz="0 0 1"/> <limit lower="-3.14" upper="3.14" effort="100" velocity="1.0"/> <dynamics damping="0.1" friction="0.0"/> </joint>这段XML是改造后的目标格式。limit的effort和velocity根据你的电机参数填,damping和friction影响仿真稳定性。我一般会把这些参数单独放一个配置文件,生成URDF时合并进去。
5.3 验证改造结果:用正向运动学对比
改造完之后,怎么确认URDF和原始DH表描述的是同一个机器人?最可靠的方法是用正向运动学算一组关节角对应的末端位姿,然后和DH表直接算的结果对比。DH2URDF的examples里有个verify脚本,输入关节角,分别用DH矩阵和URDF解析算末端位置,误差在1e-6以内就算通过。
// 正向运动学验证:DH vs URDF function verifyFK(dhParams, jointAngles) { const T_dh = dhParams.reduce((acc, p, i) => multiply(acc, dhToMatrix(p.a, p.alpha, p.d, jointAngles[i])), identity()); const T_urdf = computeFKFromURDF('robot.urdf', jointAngles); const posError = distance(T_dh.slice(0,3,3), T_urdf.slice(0,3,3)); console.log('位置误差:', posError); return posError < 1e-6; }这个验证步骤我每次改完转换逻辑都会跑一遍。有一次偷懒没跑,结果alpha角的符号搞反了,RViz里看着正常,但实际运动方向完全相反,后来在实物上调试才发现,血泪教训。从那以后,不管改多小的参数,我都强制走一遍正向运动学对比,确认末端位姿一致再往下做。希望这个习惯也能帮到你。
本文还有配套的精品资源,点击获取