Skip to content

设置 SDF 模型

本指南将为一个简单的差速驱动机器人创建 SDF(Simulation Description Format,仿真描述格式)文件,帮助读者积累为 Gazebo 仿真构建 SDF 文件的实践经验。

注意:本教程的完整源代码可在 navigation2_tutorials 仓库的 sam_bot_description 包中找到。该仓库中的代码是完成本系列全部教程后的最终版本。

SDF 是一种用于仿真器(如 Gazebo)的文件格式,可描述仿真环境、模型(包括连杆、关节及物理仿真元数据)以及相关插件。本指南创建的 SDF 适用于现代 Gazebo,但也可用于其他仿真器,如 Open3D Engine 或 Isaac Sim。

借助 sdformat-urdf 包,可以将 SDF 与 robot_state_publisher 配合使用,后续教程会详细介绍。

首先,加载 ROS 2 安装目录的环境变量:

Terminal window
source /opt/ros/<ros2-distro>/setup.bash

然后安装 sdformat-urdf 包:

Terminal window
sudo apt install ros-$ROS_DISTRO-sdformat-urdf

该包提供一个 C++ 库和 urdf_parser_plugin,能将 SDFormat XML 转换为 URDF C++ 结构。安装后,即可用 SDFormat XML 替代 URDF XML 作为机器人描述。

注意:如需了解更多关于 SDF 和 sdformat_urdf 的信息,可查阅官方 SDFormat 网站 和 sdformat_urdf GitHub 仓库。

目前的 SDF 基本上是 URDF 代码的 SDFormat 版本。两种描述的差异主要体现在后续添加的插件和传感器上。此外,SDF 还加入了仿真器所需的物理属性,如惯性(inertia)、质量(mass)和材质(material)。

以下是 URDF 代码的 SDF 版本:

<?xml version="1.0" ?>
<sdf version="1.8" xmlns:xacro="http://ros.org/wiki/xacro">
<model name='sam_bot' canonical_link='base_link'>
<!-- Define robot constants -->
<xacro:property name="base_width" value="0.31"/>
<xacro:property name="base_length" value="0.42"/>
<xacro:property name="base_height" value="0.18"/>
<xacro:property name="wheel_radius" value="0.10"/>
<xacro:property name="wheel_width" value="0.04"/>
<xacro:property name="wheel_ygap" value="0.025"/>
<xacro:property name="wheel_zoff" value="0.05"/>
<xacro:property name="wheel_xoff" value="0.12"/>
<xacro:property name="caster_xoff" value="0.14"/>
<!-- Define some commonly used inertial properties -->
<xacro:macro name="box_inertia" params="m w h d">
<inertial>
<pose>0 0 0 ${pi/2} 0 ${pi/2}</pose>
<mass>${m}</mass>
<inertia>
<ixx>${(m/12) * (h*h + d*d)}</ixx>
<ixy>0.0</ixy>
<ixz>0.0</ixz>
<iyy>${(m/12) * (w*w + d*d)}</iyy>
<iyz>0.0</iyz>
<izz>${(m/12) * (w*w + h*h)}</izz>
</inertia>
</inertial>
</xacro:macro>
<xacro:macro name="cylinder_inertia" params="m r h">
<inertial>
<pose>0 0 0 ${pi/2} 0 0</pose>
<mass>${m}</mass>
<inertia>
<ixx>${(m/12) * (3*r*r + h*h)}</ixx>
<ixy>0.0</ixy>
<ixz>0.0</ixz>
<iyy>${(m/12) * (3*r*r + h*h)}</iyy>
<iyz>0.0</iyz>
<izz>${(m/2) * (r*r)}</izz>
</inertia>
</inertial>
</xacro:macro>
<xacro:macro name="sphere_inertia" params="m r">
<inertial>
<mass>${m}</mass>
<inertia>
<ixx>${(2/5) * m * (r*r)}</ixx>
<ixy>0.0</ixy>
<ixz>0.0</ixz>
<iyy>${(2/5) * m * (r*r)}</iyy>
<iyz>0.0</iyz>
<izz>${(2/5) * m * (r*r)}</izz>
</inertia>
</inertial>
</xacro:macro>
<!-- Robot Base -->
<link name='base_link'>
<must_be_base_link>true</must_be_base_link>
<visual name="base_link_visual">
<geometry>
<box><size>
${base_length} ${base_width} ${base_height}
</size></box>
</geometry>
<material>
<ambient>0 1 1 1</ambient>
<diffuse>0 1 1 1</diffuse>
</material>
</visual>
<collision name="base_link_collision">
<geometry>
<box><size>
${base_length} ${base_width} ${base_height}
</size></box>
</geometry>
</collision>
</link>
<!-- Robot Footprint -->
<link name='base_footprint'>
<pose relative_to="base_joint"/>
<xacro:box_inertia m="15" w="${base_width}" d="${base_length}" h="${base_height}"/>
</link>
<joint name='base_joint' type='fixed'>
<parent>base_link</parent>
<child>base_footprint</child>
<pose relative_to="base_link">0.0 0.0 ${-(wheel_radius+wheel_zoff)} 0 0 0</pose>
</joint>
<!-- Wheels -->
<xacro:macro name="wheel" params="prefix x_reflect y_reflect">
<link name="${prefix}_link">
<pose relative_to="${prefix}_joint"/>
<visual name="${prefix}_link_visual">
<pose relative_to="${prefix}_link">0 0 0 ${pi/2} 0 0</pose>
<geometry>
<cylinder>
<radius>${wheel_radius}</radius>
<length>${wheel_width}</length>
</cylinder>
</geometry>
<material>
<ambient>0.3 0.3 0.3 1.0</ambient>
<diffuse>0.7 0.7 0.7 1.0</diffuse>
</material>
</visual>
<collision name="${prefix}_link_collision">
<pose relative_to="${prefix}_link">0 0 0 ${pi/2} 0 0</pose>
<geometry>
<cylinder>
<radius>${wheel_radius}</radius>
<length>${wheel_width}</length>
</cylinder>
</geometry>
</collision>
<xacro:cylinder_inertia m="0.5" r="${wheel_radius}" h="${wheel_width}"/>
</link>
<joint name="${prefix}_joint" type="revolute">
<parent>base_link</parent>
<child>${prefix}_link</child>
<pose relative_to="base_link">${x_reflect*wheel_xoff} ${y_reflect*(base_width/2+wheel_ygap)} ${-wheel_zoff} 0 0 0</pose>
<axis>
<xyz>0 1 0</xyz>
<limit>
<lower>-inf</lower>
<upper>inf</upper>
</limit>
</axis>
</joint>
</xacro:macro>
<xacro:wheel prefix="drivewhl_l" x_reflect="-1" y_reflect="1" />
<xacro:wheel prefix="drivewhl_r" x_reflect="-1" y_reflect="-1" />
<link name="front_caster">
<pose relative_to="caster_joint"/>
<visual name="front_caster_visual">
<geometry>
<sphere>
<radius>${(wheel_radius+wheel_zoff-(base_height/2))}</radius>
</sphere>
</geometry>
<material>
<ambient>0 1 1 1</ambient>
<diffuse>0 1 1 1</diffuse>
</material>
</visual>
<collision name="front_caster_collision">
<geometry>
<sphere>
<radius>${(wheel_radius+wheel_zoff-(base_height/2))}</radius>
</sphere>
</geometry>
</collision>
<xacro:sphere_inertia m="0.5" r="${(wheel_radius+wheel_zoff-(base_height/2))}"/>
</link>
<joint name="caster_joint" type="fixed">
<parent>base_link</parent>
<child>front_caster</child>
<pose relative_to="base_link">${caster_xoff} 0.0 ${-(base_height/2)} 0 0 0</pose>
</joint>
</model>
</sdf>

将 display.launch.py 中的 default_model_path 改为引用 SDF 描述而不是 URDF 描述。

default_model_path = os.path.join(pkg_share, 'src', 'description', 'sam_bot_description.sdf')

构建并加载包,然后启动 display.launch.py:

Terminal window
colcon build --symlink-install
source install/setup.bash
ros2 launch sam_bot_description display.launch.py

注意:可能会出现类似 [kdl_parser]: The root link base_link has an inertia specified in the URDF, but KDL does not support a root link with an inertia. As a workaround, you can add an extra dummy link to your URDF. 的警告。该警告可以安全忽略,这只是 SDF 到 URDF 转换过程中的一个已知现象。

base-bot_3

至此,本教程全部完成:为简单的差速驱动机器人创建了 SDF 文件,搭建了 ROS 2 项目,启动了使用该 SDF 发布机器人坐标变换的 publisher 节点,并通过 RViz 可视化验证了 SDF 的正确性。