程序项目代做,有需求私信(小程序、网站、爬虫、电路板设计、驱动、应用程序开发、毕设疑难问题处理等)

ROS2之URDF建模

ROS是机器人操作系统,当然要给机器人使用啦,不过在使用之前,还得让ROS认识下我们使用的机器人,如何把一个机器人介绍给ROS呢?

为此,ROS专门提供了一种机器人建模方法——URDF,用来描述机器人外观、性能等各方面属性。

一、URDF语法

1.1 机器人的组成

建模描述机器人的过程中,我们自己需要先熟悉机器人的组成和参数,比如机器人一般是由硬件结构、驱动系统、传感器系统、控制系统四大部分组成,市面上一些常见的机器人,无论是移动机器人还是机械臂,我们都可以按照这四大组成部分进行分解。

其中:

  • 硬件结构就是底盘、外壳、电机等实打实可以看到的设备;
  • 驱动系统就是可以驱使这些设备正常使用的装置,比如电机的驱动器,电源管理系统等;
  • 传感系统包括电机上的编码器、板载的IMU、安装的摄像头、雷达等等,便于机器人感知自己的状态和外部的环境;
  • 控制系统就是我们开发过程的主要载体了,一般是树莓派、电脑等计算平台,以及里边的操作系统和应用软件。

机器人建模的过程,其实就是按照类似的思路,通过建模语言,把机器人每一个部分都描述清楚,再组合起来的过程。

1.2 什么是URDF

URDFUnified Robot Description Format,统一机器人描述格式)是一种XML格式的文件,用于从物理和逻辑上描述一个机器人模型。

你可以把它理解为机器人的数字说明书或三维装配图,它告诉计算机:

  • 机器人由哪些零件(连杆、关节)组成;
  • 这些零件长什么样(形状、尺寸、颜色);
  • 零件之间如何连接(位置、旋转、运动类型);
  • 零件的物理属性(质量、惯性);

1.3 URDF组成

URDF主要描述两大核心元素:

  • 连杆(Link):代表机器人的刚性部件,如机械臂的每一节臂杆、底盘、轮子、传感器支架等,包括:
    • 视觉属性:形状(立方体、圆柱体、网格模型)、尺寸、颜色、纹理;
    • 碰撞属性:用于物理仿真的简化几何形状;
    • 惯性属性:质量、转动惯量(对仿真至关重要)。
  • 关节(Joint):定义连杆之间的连接方式和运动关系,主要类型:
    • 固定关节(fixed):完全固定连接;
    • 旋转关节(revolute):绕单轴旋转,有角度限制(如机械臂关节);
    • 连续关节(continuous):无限旋转(如轮子);
    • 平移关节(prismatic):线性滑动;
    • 平面关节(planar):在平面内运动;
    • 浮动关节(floating):完全自由(6自由度)。

机器人描述由一组连杆元素和一组将连杆连接起来的关节元素组成;

因此,典型的机器人描述大致如下所示:

<?xml version="1.0"?>
<?xml-model href="https://raw.githubusercontent.com/ros/urdfdom/master/xsd/urdf.xsd" ?>
<robot name="pr2" xmlns="http://www.ros.org">
  <link> ... </link>
  <link> ... </link>
  <link> ... </link>

  <joint>  ....  </joint>
  <joint>  ....  </joint>
  <joint>  ....  </joint>
</robot>

可以看到,URDF格式的根元素是一个 <robot> 元素。

1.3.1 link元素

link元素描述了一个具有惯性、视觉特征和碰撞属性的刚体。以下是一个link元素的示例:

<link name="my_link">
  <inertial>
    <origin xyz="0 0 0.5" rpy="0 0 0"/>
    <mass value="1"/>
    <inertia ixx="100"  ixy="0"  ixz="0" iyy="100" iyz="0" izz="100" />
  </inertial>

  <visual>
    <origin xyz="0 0 0" rpy="0 0 0" />
    <geometry>
      <box size="1 1 1" />
    </geometry>
    <material name="Cyan">
      <color rgba="0 1.0 1.0 1.0"/>
    </material>
  </visual>

  <collision>
    <origin xyz="0 0 0" rpy="0 0 0"/>
    <geometry>
      <cylinder radius="1" length="0.5"/>
    </geometry>
  </collision>
</link>

link元素有一个属性,即name(必选):用来描述连杆本身的名称.。此外,link包含若干部元素,接下来我们一一介绍。

1.3.1.1 惯性属性inertial

inertial(可选):用于描述连杆的质量、其质心的位置以及其中心惯性属性。

其下可以包含如下元素:

  • origin(可选):此位姿(平移、旋转)描述了连杆质心坐标系C相对于连杆坐标系L的位置和方向。
    • xyz (可选):表示从Lo(连杆坐标系原点)到Co(连杆质心)的位置向量,形式为 x L̂x + y L̂y + z L̂z,其中 L̂xL̂yL̂z 是连杆坐标系L的正交单位向量;
    • rpy(可选):表示C的单位向量ĈxĈyĈz相对于连杆坐标系L的方向,以欧拉旋转(横滚roll、俯仰pitch、偏航yaw)序列表示,单位为弧度。注意:ĈxĈyĈz不需要与连杆的惯性主轴对齐;
  • mass:连杆的质量由该元素的value属性表示;
  • inertia:此连杆关于Co(连杆质心)的惯性矩ixxiyyizz和惯性积 ixyixziyz,这些值对应于固定在质心坐标系C中的单位向量ĈxĈyĈz。注意:ĈxĈyĈz相对于L̂xL̂yL̂z 的方向由 <origin> 标签中的rpy值指定。
1.3.1.2 视觉属性visual

visual(可选):连杆的视觉属性,此元素指定了用于可视化目的的对象形状(长方体、圆柱体等)。

注意:同一个连杆可以存在多个 <visual> 标签实例。它们定义的几何体的并集形成了该连杆的视觉表示。

其下可以包含如下元素:

  • origin (可选):视觉元素相对于连杆坐标系的参考坐标系;
    • xyz (可选):分别是xyz方向上的平移;
    • py (可选):表示固定轴的横滚、俯仰和偏航角度,单位为弧度;
  • geometry(必选):表示几何形状,可以是以下之一:
    • <box>
      • size属性包含长方体的三个边长,长方体的原点位于其中心;
    • <cylinder>
      • 指定半径和长度。圆柱体的原点位于其中心;
    • <sphere>
      • 指定半径。球体的原点位于其中心;
    • <mesh>
      • 由文件名指定的三角网格元素,以及一个可选的缩放比例,用于缩放网格的轴对齐边界框。任何几何格式都可以接受,但具体应用程序的兼容性取决于实现。对于最佳纹理和颜色支持,推荐的格式是 Collada .dae文件。引用同一模型的机器之间不会传输网格文件。它必须是本地文件。在文件名前加上 package://<packagename>/<path> 可以使网格文件的路径相对于包 <packagename>
  • material(可选):视觉元素的材质,其name属性可以用于指定材质的名称;
    • <color> (可选)
      • rgba由一组四个数字(代表红/绿/蓝/透明度alpha)指定的材质颜色,每个数字的范围为 [0,1];
    • <texture> (可选)
      • 材质的纹理由文件名指定。
1.3.1.3 碰撞属性collision

collision用于描述碰撞参数,里边的内容似乎和<visual>一样,也有<geometry><origin>,看似相同,其实区别还是比较大的;

  • origin (可选):碰撞元素相对于连杆坐标系的参考坐标系;
    • xyz (可选):分别是xyz方向上的平移;
    • py (可选):表示固定轴的横滚、俯仰和偏航角度,单位为弧度;
  • geometry:请参见上述视觉元素中的几何描述。
1.3.2 joint元素

joint元素描述了关节的运动学和动力学特性,并指定了关节的安全限制。

以下是一个 joint元素的示例:

<joint name="my_joint" type="floating">
   <origin xyz="0 0 1" rpy="0 0 3.1416"/>
   <parent link="link1"/>
   <child link="link2"/>

   <calibration rising="0.0"/>
   <dynamics damping="0.0" friction="0.0"/>
   <limit effort="30" velocity="1.0" lower="-2.2" upper="0.7" />
   <safety_controller k_velocity="10" k_position="15" soft_lower_limit="-2.0" soft_upper_limit="0.5" />
</joint>

下面图中,蓝色关节表示出一个运动的轴,蓝色关节是可以围绕蓝色的轴旋转的,也就是child这个link是可以围绕joint上下旋转。

joint连接两个link,需要分一个主次关系,主关节是parent link,子关节是child link, 在xml形式的描述中这个两个link是必须存在的。

joint元素有两个属性:

  • name(必须):指定关节的唯一名称;

  • type(必须):指定关节的类型,类型可以是以下之一;

    • revolute :旋转关节,和continuous类型的区别在于不能无限旋转,而是带有角度限制,比如机械臂的两个连杆,就属于这种运动;

    • continuous:旋转关节,可以围绕单轴无限旋转;比如小车的轮子,就属于这种类型;

    • prismatic:滑动关节,可以沿某一个轴平移,也带有位置的极限,一般直线电机就是这种运动方式;

    • fixed:固定关节,是唯一一种不允许运动的关节,不过使用还是比较频繁的,比如相机这个连杆,安装在机器人上,相对位置是不会变化的,此时使用的连接方式就是fixed

    • floating:浮动关节,允许进行平移、旋转运动;

    • planar :平面关节,允许在平面正交方向上平移或者旋转;

此外,joint包含若干部元素,接下来我们挑选部分介绍介绍。

1.3.2.1 origin

origin(可选):表示从父连杆到子连杆的变换,元素属性有:

  • xyz (可选):表示xyz 偏移量,所有位置均以米为单位指定;
  • rpy (可选):表示绕固定轴的旋转:先绕x轴横滚(roll),然后绕y轴俯仰(pitch),最后绕z轴偏航(yaw),所有角度均以弧度为单位指定。
1.3.2.2 parent

parent描述父连杆名称,元素属性有:

  • link:在机器人树结构中作为此连杆父连杆的连杆名称。
1.3.2.3 child

child描述子连杆名称,元素属性有:

  • link:作为子连杆的连杆名称。
1.3.2.4 calibration

关节的参考位置,用来校准关节的绝对位置。

1.3.2.5 dynamics

描述关节的物理属性,例如阻尼值、物理静摩擦力等,经常在动力学仿真中用到。

1.3.2.6 limit

描述运动的一些极限值,包括关节运动的上下限位置、速度限制、力矩限制等。

1.3.2.7 mimic

描述该关节与已有关节的关系。

1.3.2.8 safety_controller

描述安全控制器参数。保护机器人关节的运动。

二、URDF案例

创建my_learning_urdfPython版本的功能包;

pi@NanoPC-T6:~/dev_ws$ cd src
pi@NanoPC-T6:~/dev_ws/src$ ros2 pkg create --build-type ament_python my_learning_urdf

在包中创建如下文件夹:

  • urdf:存放机器人模型的URDFxacro文件;
  • meshes:放置URDF中引用的模型渲染文件;
  • launch:保存相关启动文件;
  • rviz:保存rviz的配置文件。

我们需要修改setup.py文件,添加配置文件:

import os
from glob import glob

    ...

    data_files=[
        ('share/ament_index/resource_index/packages',
            ['resource/' + package_name]),
        ('share/' + package_name, ['package.xml']),
        (os.path.join('share', package_name, 'launch'), glob(os.path.join('launch', '*.launch.py'))),
        (os.path.join('share', package_name, 'urdf'), glob(os.path.join('urdf', '*.*'))),
        (os.path.join('share', package_name, 'urdf/sensors'), glob(os.path.join('urdf/sensors', '*.*'))),
        (os.path.join('share', package_name, 'meshes'), glob(os.path.join('meshes', '*.*'))),
        (os.path.join('share', package_name, 'rviz'), glob(os.path.join('rviz', '*.rviz'))),
    ],

    ...

2.1 模型文件

urdf下新建文件mbot_base.urdf

<?xml version="1.0" ?>
<robot name="mbot">

    <link name="base_link">
        <visual>
            <origin xyz=" 0 0 0" rpy="0 0 0" /> <!-- 机器人中心,坐标系原点 -->
            <geometry>
                <cylinder length="0.16" radius="0.20"/> <!-- 直径0.4m,高0.16m的圆柱 -->
            </geometry>
            <material name="yellow">
                <color rgba="1 0.4 0 1"/> <!-- 橙黄色 -->
            </material>
        </visual>
    </link>

    <joint name="left_wheel_joint" type="continuous">
        <origin xyz="0 0.19 -0.05" rpy="0 0 0"/> <!-- 位置:Y轴+0.19,Z轴-0.05 -->
        <parent link="base_link"/>
        <child link="left_wheel_link"/>
        <axis xyz="0 1 0"/>  <!-- 绕Y轴旋转 -->
    </joint>

    <link name="left_wheel_link">
        <visual>
            <origin xyz="0 0 0" rpy="1.5707 0 0" /> <!-- 旋转90度(π/2=1.5707) -->
            <geometry>
                <cylinder radius="0.06" length = "0.025"/> <!-- 半径0.06m,厚0.025m -->
            </geometry>
            <material name="white">
                <color rgba="1 1 1 0.9"/>
            </material>
        </visual>
    </link>

    <joint name="right_wheel_joint" type="continuous">
        <origin xyz="0 -0.19 -0.05" rpy="0 0 0"/>
        <parent link="base_link"/>
        <child link="right_wheel_link"/>
        <axis xyz="0 1 0"/>
    </joint>

    <link name="right_wheel_link">
        <visual>
            <origin xyz="0 0 0" rpy="1.5707 0 0" />
            <geometry>
                <cylinder radius="0.06" length = "0.025"/>
            </geometry>
            <material name="white">
                <color rgba="1 1 1 0.9"/>
            </material>
        </visual>
    </link>

    <joint name="front_caster_joint" type="continuous">
        <origin xyz="0.18 0 -0.095" rpy="0 0 0"/>
        <parent link="base_link"/>
        <child link="front_caster_link"/>
        <axis xyz="0 1 0"/>
    </joint>

    <link name="front_caster_link">
        <visual>
            <origin xyz="0 0 0" rpy="0 0 0"/>
            <geometry>
                <sphere radius="0.015" />
            </geometry>
            <material name="black">
                <color rgba="0 0 0 0.95"/>
            </material>
        </visual>
    </link>

    <joint name="back_caster_joint" type="continuous">
        <origin xyz="-0.18 0 -0.095" rpy="0 0 0"/>
        <parent link="base_link"/>
        <child link="back_caster_link"/>
        <axis xyz="0 1 0"/>
    </joint>

    <link name="back_caster_link">
        <visual>
            <origin xyz="0 0 0" rpy="0 0 0"/>
            <geometry>
                <sphere radius="0.015" />
            </geometry>
            <material name="black">
                <color rgba="0 0 0 0.95"/>
            </material>
        </visual>
    </link>

</robot>

这将会创建一个两轮差分驱动机器人的URDF模型,包含若干个部分。

圆柱体(直径0.4米,高0.16米),橙黄色,机器人中心,坐标系原点。

2.1.2 2个驱动轮(左右轮)

分别为left_wheel_linkright_wheel_link

  • left_wheel_link
    • 圆柱体(半径0.06米,厚度0.025米);
    • 在基座坐标系中 (0, 0.19, -0.05)
    • 关节类型:continuous(连续旋转关节,无限旋转);
    • 旋转轴:绕Y(0, 1, 0)
    • 视觉旋转:rpy="1.5707 0 0" 将圆柱旋转90度,使其直立(假设原始圆柱是平放的);
  • right_wheel_link:与左轮对称布置,Y坐标为负;
2.1.3 2个万向轮(前后脚轮,用于支撑)

分别为front_caster_linkback_caster_link

  • front_caster_link
    • 形状:小球体(半径0.015米),模拟万向轮;
    • 在基座坐标系中前方 (0.18, 0, -0.095)
    • 颜色:黑色;
  • back_caster_link:与前万向轮对称布置,X坐标为负(-0.18)。

2.2 launch文件

launch文件夹下创建display.launch.py文件;

from ament_index_python.packages import get_package_share_path

from launch import LaunchDescription
from launch.actions import DeclareLaunchArgument
from launch.conditions import IfCondition, UnlessCondition
from launch.substitutions import Command, LaunchConfiguration

from launch_ros.actions import Node
from launch_ros.parameter_descriptions import ParameterValue

def generate_launch_description():
    urdf_tutorial_path = get_package_share_path('my_learning_urdf')
    # 设置默认的URDF文件和RViz配置文件路径
    default_model_path = urdf_tutorial_path / 'urdf/mbot_base.urdf'
    default_rviz_config_path = urdf_tutorial_path / 'rviz/urdf.rviz'

    # 命令行参数:--gui true/false,控制是否使用GUI界面发布关节状态
    gui_arg = DeclareLaunchArgument(name='gui', default_value='false', choices=['true', 'false'],
                                    description='Flag to enable joint_state_publisher_gui')
    
    # 命令行参数:--model <路径>,指定URDF文件路径
    model_arg = DeclareLaunchArgument(name='model', default_value=str(default_model_path),
                                      description='Absolute path to robot urdf file')
    
    # 命令行参数:--rvizconfig <路径>,指定RViz配置文件
    rviz_arg = DeclareLaunchArgument(name='rvizconfig', default_value=str(default_rviz_config_path),
                                     description='Absolute path to rviz config file')

    # 关键:使用xacro命令解析URDF文件(支持参数化、宏等高级特性)
    robot_description = ParameterValue(Command(['xacro ', LaunchConfiguration('model')]),
                                       value_type=str)
	
    # robot_state_publisher 节点
    robot_state_publisher_node = Node(
        package='robot_state_publisher',
        executable='robot_state_publisher',
        parameters=[{'robot_description': robot_description}]
    )

    # 关节状态发布器(二选一)
    # 文本版本(无GUI)
    joint_state_publisher_node = Node(
        package='joint_state_publisher',
        executable='joint_state_publisher',
        condition=UnlessCondition(LaunchConfiguration('gui'))
    )

    # GUI版本(带滑动条控制)
    joint_state_publisher_gui_node = Node(
        package='joint_state_publisher_gui',
        executable='joint_state_publisher_gui',
        condition=IfCondition(LaunchConfiguration('gui'))
    )

    # rviz2 可视化节点
    rviz_node = Node(
        package='rviz2',
        executable='rviz2',
        name='rviz2',
        output='screen',
        arguments=['-d', LaunchConfiguration('rvizconfig')],
    )

    return LaunchDescription([
        gui_arg,
        model_arg,
        rviz_arg,
        joint_state_publisher_node,
        joint_state_publisher_gui_node,
        robot_state_publisher_node,
        rviz_node
    ])

这个Launch文件主要做三件事:

  • 加载机器人URDF模型(支持xacro格式);
  • 发布机器人的状态变换(TF);
  • rviz2中可视化机器人。
2.2.1 节点

脚本运行会创建以下几个节点:

  • joint_state_publisher:发布每个joint(除fixed类型)的状态,一个无界面的、基础版的关节状态发布器;
  • joint_state_publisher_gui:发布每个joint(除fixed类型)的状态,可以通过UI界面对joint进行控制;
  • robot_state_publisher:将机器人各个linksjoints之间的关系,通过TF的形式,整理成三维姿态信息发布。
  • rviz2:在rviz2中可视化机器人;

joint_state_publisher这是一个官方ROS2包,主要功能:

  • 输入:

    • 读取URDF中的关节定义;

    • 接收用户或程序指定的关节角度;

  • 输出:

    • 发布 /joint_states 话题,消息类型为 sensor_msgs/msg/JointState

    • 包含所有关节的名称、位置、速度、力等信息。

2.2.2 数据流与节点关系

数据流与节点关系:

用户通过滑动条/GUI或程序 → joint_state_publisher(_gui)
                                     ↓ 发布/joint_states话题
                        robot_state_publisher
                                     ↓ 计算并发布TF变换
                              rviz2 和其他节点
                                     ↓ 接收TF并可视化

2.3 urdf.rviz

rviz目录下新建urdf.rviz文件;

Panels:
  - Class: rviz_common/Displays
    Name: Displays
  - Class: rviz_common/Views
    Name: Views
Visualization Manager:
  Class: ""
  Displays:
    - Class: rviz_default_plugins/Grid
      Name: Grid
      Value: true
    - Alpha: 0.8
      Class: rviz_default_plugins/RobotModel
      Description Source: Topic
      Description Topic:
        Value: /robot_description
      Enabled: true
      Name: RobotModel
      Value: true
    - Class: rviz_default_plugins/TF
      Name: TF
      Value: true
  Global Options:
    Fixed Frame: base_link
    Frame Rate: 30
  Name: root
  Tools:
    - Class: rviz_default_plugins/MoveCamera
  Value: true
  Views:
    Current:
      Class: rviz_default_plugins/Orbit
      Distance: 1.7
      Name: Current View
      Pitch: 0.33
      Value: Orbit (rviz)
      Yaw: 5.5
Window Geometry:
  Height: 800
  Width: 1200

2.4 编译运行

编译程序:

pi@NanoPC-T6:~/dev_ws$ colcon build --paths src/my_learning_urdf
pi@NanoPC-T6:~/dev_ws$ source install/setup.sh
2.4.1 可视化

启动终端,运行如下命令;

pi@NanoPC-T6:~/dev_ws$ ros2 launch my_learning_urdf display.launch.py

很快就可以看到rviz中显示的机器人模型啦,大家可以使用鼠标拖拽观察;

从可视化的效果来看,这个机器人由5link和4个joint组成。

2.4.2 查看URDF模型结构

我们分析的对不对呢,可以在模型文件的路径下,使用urdf_to_graphviz这个小工具来分析下;

pi@NanoPC-T6:~/dev_ws/src/my_learning_urdf/urdf$ urdf_to_graphviz mbot_base.urdf
WARNING: OUTPUT not given. This type of usage is deprecated!Usage: urdf_to_graphviz input.xml [OUTPUT]  Will create either $ROBOT_NAME.gv & $ROBOT_NAME.pdf in CWD  or OUTPUT.gv & OUTPUT.pdf.
Created file mbot.gv
Created file mbot.pdf

pi@NanoPC-T6:~/dev_ws/src/my_learning_urdf/urdf$ ls
mbot_base.urdf  mbot.gv  mbot.pdf

运行成功后会产生一个pdf文件,打开之后就可以看到URDF模型分析的结果啦,是不是和我们的猜测完全相同呢!

参考文章

[1] 古月居ROS2入门教程学习笔记

[2] ROS中阶笔记(二):机器人系统设计—URDF机器人建模

[3] XML Robot Description Format (URDF)

posted @ 2025-12-22 22:31  大奥特曼打小怪兽  阅读(12)  评论(0)    收藏  举报
如果有任何技术小问题,欢迎大家交流沟通,共同进步