课程链接:www.bilibili.com/cheese/play...
课程目标
- 认识urdf
- 使用urdf-loader 加载urdf 模型
- 了解urdf-loader 无法实现的功能
1-URDF 简介
URDF(Unified Robot Description Format,统一机器人描述格式)是一种基于 XML 的文件格式,用于描述机器人的结构、关节、连杆、传感器等物理和运动学属性。
示例
xml
<?xml version="1.0"?>
<robot name="multipleshapes">
<link name="base_link">
<visual>
<geometry>
<cylinder length="0.6" radius="0.2"/>
</geometry>
</visual>
</link>
<joint name="base_to_right_leg" type="fixed">
<parent link="base_link"/>
<child link="right_leg"/>
</joint>
<link name="right_leg">
<visual>
<geometry>
<box size="0.6 0.1 0.2"/>
</geometry>
</visual>
</link>
</robot>
效果如下:

URDF文件可以通过父子关系架构出一棵图形树,从而实现父关节带动子关节的运动。

- link 译作连杆,是机器人的刚性部件,它是有实际模型的。
- joint 译作关节,可暂且将其理解为一个坐标系的概念,它不是一个实物,它连接着2个link。
- URDF图形树不会闭环。
- link 的子节点只能是joint,joint的子节点只能是link。
- 1个link可以有多个joint子级,但只能有1个joint父级。
- 1个joint只能有1个link子级,1个link父级。
大家可以在ros2 的官方文档看到更多关于URDF的介绍。
2-urdf-loader 的下载
urdf-loader 是由Garrett Johnson 开发的一个用于加载urdf 文件的工具。
urdf-loader 的js 版本是基于three.js 开发的。
接下来咱们说一下其具体的用法。
1.我们可以直接把urdf-loader 下载下来看看。
bash
git clone https://github.com/gkjohnson/urdf-loaders.git
2.在urdf-loaders中运行以下命令。
bash
#进入项目
cd javascript
#安装依赖
npm i
#启动项目
npm start
3.查看项目案例。
- 完整版:http://localhost:5173/javascript/example//index.html
- 基础版:http://localhost:5173/javascript/example/simple.html
注:在实际开发中,我们一般是用npm 安装urdf-loader的。
css
npm i urdf-loader
urdf-loader 在github 中的案例适合我们去学习其用法。
3-urdf-loader 的基本用法
我们可以去 javascript/example/src/simple.js 里看一下urdf-loader 基础版的用法。
urdf-loader 是基于three.js 开发的,three.js 场景的搭建我就不在多说了,咱们直接看urdf-loader 部分。
ini
let robot;
const manager = new LoadingManager();
const loader = new URDFLoader(manager);
loader.load('../../../urdf/T12/urdf/T12_flipped.URDF', result => {
robot = result;
});
// wait until all the geometry has loaded to add the model to the scene
manager.onLoad = () => {
robot.rotation.x = Math.PI / 2;
robot.traverse(c => {
c.castShadow = true;
});
for (let i = 1; i <= 6; i++) {
robot.joints[`HP${ i }`].setJointValue(MathUtils.degToRad(30));
robot.joints[`KP${ i }`].setJointValue(MathUtils.degToRad(120));
robot.joints[`AP${ i }`].setJointValue(MathUtils.degToRad(-60));
}
robot.updateMatrixWorld(true);
const bb = new Box3();
bb.setFromObject(robot);
robot.position.y -= bb.min.y;
scene.add(robot);
};
解释代码
1.URDFLoader 就是urdf 加载工具。
2.urdf 的加载监听。
ini
const loader = new URDFLoader(manager);
loader.load('../../../urdf/T12/urdf/T12_flipped.URDF', result => {
robot = result;
});
URDFLoader的load 可以监听urdf 文件的加载,在其回调函数中的robot 就是将urdf 解析后的three.js 图形对象。
但是此时的robot图形对象里还没有真正的模型,真正的模型需要等模型文件加载完成之后,才能被添加到robot。
3.使用LoadingManager 监听所有文件的加载。
在urdf 文件中,一般会关联着许多的子文件,如模型文件、贴图文件等。
如下面的 中,就关联着一个Body.STL 模型。
xml
<link name="Body">
...
<visual>
...
<geometry>
<mesh filename="../meshes/Body.STL" />
</geometry>
...
</visual>
</link>
因此,URDFLoader 需要LoadingManager 监听所有文件是否都加载成功。
manager.onLoad() 就是所有文件都加载成功后触发的方法。
ini
const loader = new URDFLoader(manager);
manager.onLoad = () => {
...
}
在URDFLoader的源码中,所有子文件的加载都是受LoadingManager 管理的。
ini
const loader = new THREE.TextureLoader(manager);
const loader = new STLLoader(manager);
3.适配模型坐标系。
在urdf 的坐标系中,一般z轴是朝上的。
而在three.js 的坐标系中,y 轴是朝上的。
因此,在上面的代码中,将robot 绕x轴旋转了90度。
ini
robot.rotation.x = Math.PI / 2;
4.通过robot.traverse() 方法调整robot中的模型。
ini
robot.traverse(c => {
c.castShadow = true;
});
在此方法里,我们可以设置模型的各种属性,比如材质。
5.robot.joints[HP${ i }].setJointValue() 演示了根据关节名设置关节的旋转弧度的方法。
6.将模型最底部的位置对齐到地面,也就是高度为0 的位置。
ini
const bb = new Box3();
bb.setFromObject(robot);
robot.position.y -= bb.min.y;
4-URDF 模型的拖拽控制
在完整版示例里,我们可以使用鼠标拖拽URDF模型的link,使其绕父关节旋转。
在javascript/src/urdf-manipulator-element.js 中可以看到拖拽相关的代码。
kotlin
const dragControls = new PointerURDFDragControls(this.scene, this.camera, el);
dragControls.onDragStart = joint => {
this.dispatchEvent(new CustomEvent('manipulate-start', { bubbles: true, cancelable: true, detail: joint.name }));
this.controls.enabled = false;
this.redraw();
};
dragControls.onDragEnd = joint => {
this.dispatchEvent(new CustomEvent('manipulate-end', { bubbles: true, cancelable: true, detail: joint.name }));
this.controls.enabled = true;
this.redraw();
};
dragControls.updateJoint = (joint, angle) => {
this.setJointValue(joint.name, angle);
};
dragControls.onHover = joint => {
highlightLinkGeometry(joint, false);
this.dispatchEvent(new CustomEvent('joint-mouseover', { bubbles: true, cancelable: true, detail: joint.name }));
this.redraw();
};
dragControls.onUnhover = joint => {
highlightLinkGeometry(joint, true);
this.dispatchEvent(new CustomEvent('joint-mouseout', { bubbles: true, cancelable: true, detail: joint.name }));
this.redraw();
};
解释代码
PointerURDFDragControls 是urdf-loader项目封装好的拖拽控制对象。
kotlin
const dragControls = new PointerURDFDragControls(this.scene, this.camera, el);
在拖拽过程中,PointerURDFDragControls 会触发以下事件:
- onDragStart 拖拽开始
- onDragEnd 拖拽结束
- updateJoint 关节的弧度发生改变
- onHover 鼠标划入
- onUnhover 鼠标划出
当前案例在onDragStart 和onDragEnd 中会控制了OrbitControls 的有效性,避免相机轨道的拖拽和关节拖拽的冲突;
在updateJoint 中实现了关节旋转和dom表单的同步更新;
在onHover 和onUnhover 中实现了鼠标划上的物体的高亮。
5-urdf-loader 无法实现的功能
在实际的工作中,urdf-loader 并不能满足所有的开发需求,而能否实现机器人可视化中灵活多变的需求,会决定你在相关领域的专业程度。
接下来给大家分享几个我在实际工作中常见的需求:
- 适配urdf 文件中不同类型的资源路径。
xml
<geometry>
<mesh filename="package://example-robot-data/robots/panda_description/meshes/visual/finger.stl" />
</geometry>
-
鼠标划上模型时,提示link 信息、离link 最近的父级或祖父级的可活动的joint 信息。
-
在拖拽机器人关节的时候,显示关节的变换轨迹。
-
显示机器人的坐标系、碰撞体、质心和惯性矩。
-
机器人实时动画缓冲功能。

总结
这一课我们认识了URDF 文件的基本结构,知道了通过urdf-loader加载和渲染URDF 模型的方法,以及如何拖拽旋转URDF关节。
与此同时,我还说了urdf-loader 无法实现的一些功能,这些功能就是我们接下来的课程重点。