URDFLoader简介

0 阅读4分钟

课程链接:www.bilibili.com/cheese/play…

课程目标

  • 认识urdf
  • 使用urdf-loader 加载urdf 模型
  • 了解urdf-loader 无法实现的功能

1-URDF 简介

URDF(Unified Robot Description Format,统一机器人描述格式)是一种基于 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>

效果如下:

image-20260129201804689

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

image-20260325202333153

  • 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 下载下来看看。

git clone https://github.com/gkjohnson/urdf-loaders.git

2.在urdf-loaders中运行以下命令。

#进入项目
cd javascript

#安装依赖
npm i

#启动项目
npm start

3.查看项目案例。

注:在实际开发中,我们一般是用npm 安装urdf-loader的。

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 部分。

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 的加载监听。

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 模型。

<link name="Body">
    ...
    <visual>
      ...
      <geometry>
          <mesh filename="../meshes/Body.STL" />
      </geometry>
      ...
    </visual>
</link>

因此,URDFLoader 需要LoadingManager 监听所有文件是否都加载成功。

manager.onLoad() 就是所有文件都加载成功后触发的方法。

const loader = new URDFLoader(manager);
manager.onLoad = () => {
    ...
}

在URDFLoader的源码中,所有子文件的加载都是受LoadingManager 管理的。

const loader = new THREE.TextureLoader(manager);
const loader = new STLLoader(manager);

3.适配模型坐标系。

在urdf 的坐标系中,一般z轴是朝上的。

而在three.js 的坐标系中,y 轴是朝上的。

因此,在上面的代码中,将robot 绕x轴旋转了90度。

robot.rotation.x = Math.PI / 2;

4.通过robot.traverse() 方法调整robot中的模型。

robot.traverse(c => {
    c.castShadow = true;
});

在此方法里,我们可以设置模型的各种属性,比如材质。

5.robot.joints[HP${ i }].setJointValue() 演示了根据关节名设置关节的旋转弧度的方法。

6.将模型最底部的位置对齐到地面,也就是高度为0 的位置。

const bb = new Box3();
bb.setFromObject(robot);
robot.position.y -= bb.min.y;

4-URDF 模型的拖拽控制

在完整版示例里,我们可以使用鼠标拖拽URDF模型的link,使其绕父关节旋转。

在javascript/src/urdf-manipulator-element.js 中可以看到拖拽相关的代码。

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项目封装好的拖拽控制对象。

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 文件中不同类型的资源路径。
<geometry>
    <mesh filename="package://example-robot-data/robots/panda_description/meshes/visual/finger.stl" />
</geometry>
  • 鼠标划上模型时,提示link 信息、离link 最近的父级或祖父级的可活动的joint 信息。

image-20250801154559053

  • 在拖拽机器人关节的时候,显示关节的变换轨迹。

image-20250801154816588

  • 显示机器人的坐标系、碰撞体、质心和惯性矩。

image-20250801154231967

  • 机器人实时动画缓冲功能。

动画

总结

这一课我们认识了URDF 文件的基本结构,知道了通过urdf-loader加载和渲染URDF 模型的方法,以及如何拖拽旋转URDF关节。

与此同时,我还说了urdf-loader 无法实现的一些功能,这些功能就是我们接下来的课程重点。