详细解析ROS 2 中 XacroSDF 与 ** URDF** 的关系,以及它们在实际项目中的协作流程。


核心关系总览

┌─────────────────────────────────────────────────────────────┐
│                        建模与仿真栈                          │
├─────────────────────────────────────────────────────────────┤
│  抽象层 │  Xacro(宏/参数/模板)                              │
│         │     ↓ 编译/展开                                      │
│  标准层 │  URDF(ROS 标准机器人描述)                         │
│         │     ↓ 转换/加载                                      │
│  仿真层 │  SDF(Gazebo 仿真描述)                             │
│         │     ↓ 实例化                                         │
│  运行时 │  Gazebo / RViz / MoveIt / ros2_control             │
└─────────────────────────────────────────────────────────────┘

1. URDF 在 ROS 2 中的角色

1.1 ROS 2 中的变化

特性 ROS 1 ROS 2
解析库 urdfdom urdfdom(保持兼容)
加载方式 robot_state_publisher robot_state_publisher(ROS 2 版本)
构建工具 catkin colcon / ament
包格式 package.xml format 2 package.xml format 3
多机器人 复杂 通过命名空间简化

1.2 ROS 2 URDF 加载流程

# ROS 2 典型 launch 文件(Python)
from launch import LaunchDescription
from launch_ros.actions import Node
from launch.substitutions import Command, FindExecutable, PathJoinSubstitution
from launch_ros.substitutions import FindPackageShare

def generate_launch_description():
    # 1. 使用 xacro 编译 URDF
    robot_description = Command([
        PathJoinSubstitution([FindExecutable(name="xacro")]),
        " ",
        PathJoinSubstitution([
            FindPackageShare("my_robot"),
            "urdf",
            "robot.urdf.xacro"
        ]),
        " ",  # 传递参数
        "use_gazebo:=true",
        " ",
        "sim_mode:=true"
    ])
    
    # 2. 发布 robot_state
    robot_state_pub = Node(
        package="robot_state_publisher",
        executable="robot_state_publisher",
        parameters=[{"robot_description": robot_description}]
    )
    
    # 3. 关节状态发布(真实硬件或仿真)
    joint_state_pub = Node(
        package="joint_state_publisher",
        executable="joint_state_publisher"
    )
    
    return LaunchDescription([robot_state_pub, joint_state_pub])

2. Xacro → URDF → SDF 的完整转换链

2.1 阶段一:Xacro 编译

# ROS 2 命令
ros2 run xacro xacro robot.xacro use_gazebo:=true > robot.urdf

# 或查看展开结果
ros2 run xacro xacro robot.xacro

Xacro 在 ROS 2 中的增强:

  • 支持 $(find-pkg-share ...) 替代 $(find ...)
  • 更好的 YAML 参数集成
  • 条件编译支持
<!-- ROS 2 风格的 xacro -->
<?xml version="1.0"?>
<robot xmlns:xacro="http://www.ros.org/wiki/xacro" name="my_robot">
    
    <!-- 使用 ROS 2 包路径 -->
    <xacro:include filename="$(find-pkg-share my_robot)/urdf/materials.xacro"/>
    
    <!-- 从 YAML 加载参数(ROS 2 推荐方式) -->
    <xacro:property name="config" value="${xacro.load_yaml('$(find-pkg-share my_robot)/config/robot_params.yaml')}"/>
    <xacro:property name="wheel_radius" value="${config['wheel']['radius']}"/>
    
    <!-- 条件参数 -->
    <xacro:arg name="use_gazebo" default="false"/>
    <xacro:if value="$(arg use_gazebo)">
        <xacro:include filename="$(find-pkg-share my_robot)/urdf/gazebo.xacro"/>
    </xacro:if>
    
</robot>

2.2 阶段二:URDF 解析

# ROS 2 中解析 URDF(Python)
from ament_index_python.packages import get_package_share_directory
import xacro
import xml.etree.ElementTree as ET

# 处理 xacro
xacro_file = get_package_share_directory('my_robot') + '/urdf/robot.xacro'
doc = xacro.process_file(xacro_file, mappings={'use_gazebo': 'true'})
robot_desc = doc.toxml()

# 解析为 DOM
root = ET.fromstring(robot_desc)
for link in root.findall('link'):
    print(f"Link: {link.get('name')}")

2.3 阶段三:URDF → SDF 转换

# 使用 gz 命令(Gazebo Sim / Ignition)
gz sdf -p robot.urdf > robot.sdf

# 或从 ROS 包直接转换
gz sdf -p $(ros2 pkg prefix my_robot)/share/my_robot/urdf/robot.urdf > robot.sdf

转换细节:

URDF 元素 SDF 对应 说明
<link> <link> 直接映射,SDF 支持更多表面属性
<joint> <joint> SDF 类型更丰富(ball, universal, screw)
<transmission> 移除 转为 <joint><physics> 或插件
<gazebo> 直接合并 保留所有属性
<sensor> <sensor> SDF 传感器配置更详细
<plugin> <plugin> 需确认 Gazebo 版本兼容性

3. ROS 2 中的实际协作架构

3.1 典型项目文件结构

my_robot/
├── CMakeLists.txt
├── package.xml
├── config/
│   ├── robot_params.yaml          # Xacro 参数
│   ├── controllers.yaml           # ros2_control 配置
│   └── gazebo_params.yaml         # Gazebo 特定参数
├── launch/
│   ├── robot_description.launch.py    # 仅加载描述
│   ├── simulation.launch.py           # Gazebo 仿真
│   └── real_hardware.launch.py        # 真实硬件
├── urdf/
│   ├── robot.urdf.xacro           # 主入口
│   ├── robot_core.xacro           # 核心结构
│   ├── materials.xacro            # 材质定义
│   ├── wheels.xacro               # 轮子宏
│   ├── sensors/
│   │   ├── lidar.xacro
│   │   ├── camera.xacro
│   │   └── imu.xacro
│   └── gazebo.xacro               # Gazebo 特定(条件包含)
├── sdf/
│   └── my_robot/                  # 手动调优的 SDF(可选)
│       ├── model.sdf
│       └── model.config
└── worlds/
    └── my_world.sdf               # Gazebo 世界文件

3.2 条件化构建:同一 Xacro 多目标输出

<!-- robot.urdf.xacro -->
<?xml version="1.0"?>
<robot xmlns:xacro="http://www.ros.org/wiki/xacro" name="my_robot">

    <!-- 参数定义 -->
    <xacro:arg name="use_gazebo" default="false"/>
    <xacro:arg name="use_ros2_control" default="true"/>
    <xacro:arg name="sim_mode" default="false"/>
    
    <!-- 核心结构(始终包含) -->
    <xacro:include filename="robot_core.xacro"/>
    
    <!-- Gazebo 特定(仿真时) -->
    <xacro:if value="$(arg use_gazebo)">
        <xacro:include filename="gazebo.xacro"/>
        
        <!-- Gazebo 插件 -->
        <gazebo>
            <plugin filename="gz_ros2_control-system" name="gz_ros2_control::GazeboSimROS2ControlPlugin">
                <parameters>$(find my_robot)/config/controllers.yaml</parameters>
            </plugin>
        </gazebo>
    </xacro:if>
    
    <!-- ROS 2 Control(硬件或仿真) -->
    <xacro:if value="$(arg use_ros2_control)">
        <xacro:include filename="ros2_control.xacro"/>
    </xacro:if>
    
    <!-- 真实硬件特定 -->
    <xacro:unless value="$(arg sim_mode)">
        <xacro:include filename="real_hardware.xacro"/>
    </xacro:unless>

</robot>

4. ROS 2 中的关键集成点

4.1 ros2_control 与 URDF/SDF

<!-- ros2_control.xacro -->
<ros2_control name="RobotSystem" type="system">
    
    <!-- 硬件接口(真实硬件) -->
    <hardware>
        <plugin>my_robot_hardware/RobotHardware</plugin>
        <param name="serial_port">/dev/ttyUSB0</param>
    </hardware>
    
    <!-- 或 Gazebo 仿真接口 -->
    <!-- 
    <hardware>
        <plugin>gz_ros2_control/GazeboSimSystem</plugin>
    </hardware>
    -->
    
    <!-- 关节接口定义 -->
    <joint name="left_wheel_joint">
        <command_interface name="velocity"/>
        <state_interface name="position"/>
        <state_interface name="velocity"/>
    </joint>
    
    <joint name="right_wheel_joint">
        <command_interface name="velocity"/>
        <state_interface name="position"/>
        <state_interface name="velocity"/>
    </joint>
    
</ros2_control>

4.2 Gazebo Sim(Ignition)与 ROS 2

<!-- 在 Xacro 中嵌入 Gazebo Sim 配置 -->
<gazebo reference="lidar_link">
    <sensor name="lidar" type="gpu_lidar">
        <topic>scan</topic>
        <update_rate>10</update_rate>
        <ray>
            <scan>
                <horizontal>
                    <samples>640</samples>
                    <min_angle>-1.396263</min_angle>
                    <max_angle>1.396263</max_angle>
                </horizontal>
            </scan>
            <range>
                <min>0.08</min>
                <max>10.0</max>
            </range>
        </ray>
        <!-- ROS 2 桥接配置 -->
        <plugin filename="gz-sim-sensors-system" name="gz::sim::systems::Sensors">
            <render_engine>ogre2</render_engine>
        </plugin>
    </sensor>
</gazebo>

ROS 2 桥接(自动):

  • Gazebo Sim 自动发布到 ROS 2 话题(通过 gz-transportros_gz_bridge
  • 无需额外配置即可在 RViz2 中显示

5. 三种格式的选择策略

5.1 决策流程图

开始建模
    │
    ▼
是否需要复杂参数化/宏? ──是──► 使用 Xacro
    │否                              │
    ▼                                ▼
直接在 URDF 中编写 ◄────────── 编译为 URDF
    │                                │
    ▼                                ▼
是否需要 Gazebo 仿真? ──是──► 转换为 SDF(自动或手动)
    │否                              │
    ▼                                ▼
用于 ROS 2 控制/可视化 ◄────── 在 Gazebo 中仿真
(RViz2, MoveIt 2, 
 ros2_control)

5.2 使用场景对照

场景 推荐格式 说明
ROS 2 导航/控制开发 Xacro → URDF 标准流程,支持 ros2_control
MoveIt 2 运动规划 Xacro → URDF MoveIt Setup Assistant 需要 URDF
Gazebo 经典仿真 Xacro → URDF → SDF gazebo_ros_pkgs 自动处理
Gazebo Sim (Ignition) Xacro → URDF → SDF 推荐方式,或使用原生 SDF
多机器人仿真 SDF World 直接编写 SDF 世界文件
闭链机构(并联机器人) 手动 SDF URDF 不支持,必须直接写 SDF
复杂环境/传感器配置 手动 SDF 比 URDF 转换更灵活
Web 可视化(webots/three.js) URDF 或 Xacro 多数 Web 工具支持 URDF

6. ROS 2 中的高级集成模式

6.1 混合模式:URDF + SDF 覆盖

# launch/simulation.launch.py
from launch import LaunchDescription
from launch.actions import IncludeLaunchDescription, DeclareLaunchArgument
from launch.launch_description_sources import PythonLaunchDescriptionSource
from launch.substitutions import LaunchConfiguration, PathJoinSubstitution
from launch_ros.substitutions import FindPackageShare
from launch_ros.actions import Node

def generate_launch_description():
    # 参数
    use_rviz = LaunchConfiguration('use_rviz')
    
    # 1. 生成 URDF(用于 ROS 2 生态)
    robot_description = Command([
        'xacro ',
        PathJoinSubstitution([
            FindPackageShare('my_robot'),
            'urdf',
            'robot.urdf.xacro'
        ]),
        ' use_gazebo:=true'
    ])
    
    # 2. 生成 SDF(用于 Gazebo,可选手动优化)
    # 或直接使用 URDF,让 Gazebo 自动转换
    
    # 3. 启动 Gazebo Sim
    gazebo = IncludeLaunchDescription(
        PythonLaunchDescriptionSource([
            PathJoinSubstitution([
                FindPackageShare('ros_gz_sim'),
                'launch',
                'gz_sim.launch.py'
            ])
        ]),
        launch_arguments={
            'gz_args': PathJoinSubstitution([
                FindPackageShare('my_robot'),
                'worlds',
                'my_world.sdf'  # SDF 世界,包含机器人
            ]) + ' -r -v 4'
        }.items()
    )
    
    # 4. 发布机器人状态(TF)
    robot_state_pub = Node(
        package='robot_state_publisher',
        executable='robot_state_publisher',
        parameters=[{
            'robot_description': robot_description,
            'publish_frequency': 50.0
        }]
    )
    
    # 5. 桥接 Gazebo ↔ ROS 2
    bridge = Node(
        package='ros_gz_bridge',
        executable='parameter_bridge',
        arguments=[
            '/clock@rosgraph_msgs/msg/Clock[gz.msgs.Clock',
            '/cmd_vel@geometry_msgs/msg/Twist]gz.msgs.Twist',
            '/odom@nav_msgs/msg/Odometry[gz.msgs.Odometry',
            '/scan@sensor_msgs/msg/LaserScan[gz.msgs.LaserScan',
            '/camera/image@sensor_msgs/msg/Image[gz.msgs.Image',
            '/camera/camera_info@sensor_msgs/msg/CameraInfo[gz.msgs.CameraInfo',
        ],
        output='screen'
    )
    
    # 6. RViz2
    rviz = Node(
        package='rviz2',
        executable='rviz2',
        arguments=['-d', PathJoinSubstitution([
            FindPackageShare('my_robot'),
            'config',
            'view_robot.rviz'
        ])],
        condition=IfCondition(use_rviz)
    )
    
    return LaunchDescription([
        DeclareLaunchArgument('use_rviz', default_value='true'),
        gazebo,
        robot_state_pub,
        bridge,
        rviz
    ])

6.2 运行时关系

┌──────────────────────────────────────────────────────────────┐
│                         ROS 2 运行时                          │
│  ┌─────────────────┐      ┌──────────────────────────────┐   │
│  │   RViz2         │◄────►│   robot_state_publisher      │   │
│  │   (可视化)       │      │   (TF 树 + 机器人描述)        │   │
│  └─────────────────┘      └──────────────┬───────────────┘   │
│         ▲                                │                    │
│         │                                │ URDF (string)      │
│         │                         ┌──────▼──────┐             │
│         │                         │  xacro 编译  │             │
│         │                         └──────┬──────┘             │
│         │                                │                    │
│  ┌──────┴──────────┐      ┌─────────────▼──────────────┐     │
│  │   MoveIt 2      │◄────►│   ros2_control             │     │
│  │   (运动规划)     │      │   (控制器管理)              │     │
│  └─────────────────┘      └─────────────┬──────────────┘     │
│                                         │                     │
│                              ┌──────────┴──────────┐         │
│                              │   硬件接口或仿真接口   │         │
│                              │   (Gazebo/Robot)    │         │
│                              └──────────┬──────────┘         │
└─────────────────────────────────────────┼────────────────────┘
                                          │
┌─────────────────────────────────────────┼────────────────────┐
│           Gazebo Sim (Ignition)         │                    │
│  ┌──────────────────────────────────────┴──────────────┐     │
│  │  SDF World (自动从 URDF 转换或手动编写)              │     │
│  │  ┌─────────────────────────────────────────────┐    │     │
│  │  │  SDF Model (机器人实体)                      │    │     │
│  │  │  ├─ Links, Joints, Sensors                  │    │     │
│  │  │  ├─ Plugins (gz_ros2_control, sensors)      │    │     │
│  │  │  └─ Physics properties                      │    │     │
│  │  └─────────────────────────────────────────────┘    │     │
│  └─────────────────────────────────────────────────────┘     │
└──────────────────────────────────────────────────────────────┘

7. 关键注意事项

7.1 版本兼容性

组件 ROS 2 版本 Gazebo 版本 SDF 版本
Foxy Gazebo 11 1.7
Galactic Gazebo 11 / Ignition Edifice 1.8
Humble Gazebo 11 / Ignition Fortress 1.9
Iron Gazebo 11 / Harmonic 1.10
Jazzy Harmonic 1.11

7.2 常见陷阱

  1. 坐标系差异

    • URDF:右手坐标系,Z 向上
    • SDF:相同,但某些旧版 Gazebo 插件可能有差异
  2. 插件命名空间

    <!-- ROS 1 (Gazebo Classic) -->
    <plugin name="diff_drive" filename="libgazebo_ros_diff_drive.so"/>
    
    <!-- ROS 2 (Gazebo Sim) -->
    <plugin filename="gz-sim-diff-drive-system" name="gz::sim::systems::DiffDrive"/>
    
  3. 话题映射

    • ROS 2 使用 DDS,话题名称可能不同
    • 使用 ros_gz_bridge 进行显式映射
  4. 资源路径

    # 确保模型路径在 GAZEBO_MODEL_PATH 或 SDF_PATH 中
    export GZ_SIM_RESOURCE_PATH=$GZ_SIM_RESOURCE_PATH:~/ros2_ws/install/my_robot/share
    

总结

问题 答案
Xacro 是什么? URDF 的预处理器,用于参数化建模
URDF 在 ROS 2 中变了吗? 格式不变,但加载工具和生态集成更新
SDF 必须手动写吗? 可以从 URDF 自动转换,复杂场景建议手动优化
三者如何协作? Xacro 生成 URDF,URDF 用于 ROS 2 生态,转换为 SDF 用于 Gazebo
ROS 2 推荐流程? Xacro → URDF(同时用于 RViz/ros2_control)→ 自动/手动转 SDF(Gazebo)

理解这三者的关系,是构建完整 ROS 2 机器人应用的基础。

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐