从0制作ROS2导航小车2——urdf与xacro

一、urdf与xacro

通过两段简单代码介绍urdf与xacro,可供学习了解,不过如今ai已成熟,大可不必太详细学习代码如何编写,要利用ai代码,重点是理解代码框架。

urdf:

<!-- 简单的小车模型 - URDF版本 -->
<robot name="my_robot">
  <!-- 基座连杆(Base Link) -->
  <link name="base_link">
    <!-- 视觉属性(颜色和形状) -->
    <visual>
      <geometry>
        <box size="0.4 0.2 0.1"/> <!-- 长宽高 -->
      </geometry>
      <material name="blue">
        <color rgba="0 0 1 1"/> <!-- RGBA颜色(蓝色) -->
      </material>
    </visual>
    <!-- 碰撞属性(可选,但仿真时必须) -->
    <collision>
      <geometry>
        <box size="0.4 0.2 0.1"/>
      </geometry>
    </collision>
  </link>

  <!-- 左轮 -->
  <link name="left_wheel">
    <visual>
      <geometry>
        <cylinder radius="0.05" length="0.03"/> <!-- 圆柱体轮子 -->
      </geometry>
      <material name="black">
        <color rgba="0 0 0 1"/> <!-- 黑色 -->
      </material>
    </visual>
  </link>
  <joint name="left_wheel_joint" type="continuous"> <!-- 连续旋转关节 -->
    <parent link="base_link"/>
    <child link="left_wheel"/>
    <origin xyz="-0.2 0.15 0" rpy="0 1.57 0"/> <!-- 位置和旋转(绕x轴转90度) -->
  </joint>

urdf是一种基于XML的格式,用于描述机器人的物理结构,包括连杆(links)、关节(joints)、传感器、外观(几何形状、材质)等。具体代码可以看例子中的注释。

Xacro:

<!-- 同一小车模型 - Xacro版本 -->
<robot name="my_robot" xmlns:xacro="http://www.ros.org/wiki/xacro">
  <!-- 定义全局变量(参数化设计) -->
  <xacro:property name="body_length" value="0.4" />   <!-- 基座长度 -->
  <xacro:property name="wheel_radius" value="0.05" /> <!-- 轮子半径 -->

  <!-- 颜色宏(避免重复定义颜色) -->
  <xacro:macro name="material_blue" params="name">
    <material name="${name}">
      <color rgba="0 0 1 1"/> <!-- 蓝色 -->
    </material>
  </xacro:macro>

  <!-- 轮子宏(复用轮子的几何和关节) -->
  <xacro:macro name="add_wheel" params="wheel_name x_pos">
    <!-- 轮子连杆 -->
    <link name="${wheel_name}">
      <visual>
        <geometry>
          <cylinder radius="${wheel_radius}" length="0.03"/>
        </geometry>
        <material name="black"/>
      </visual>
    </link>
    <!-- 轮子关节 -->
    <joint name="${wheel_name}_joint" type="continuous">
      <parent link="base_link"/>
      <child link="${wheel_name}"/>
      <origin xyz="${x_pos} 0.15 0" rpy="0 1.57 0"/> <!-- x_pos控制左右位置 -->
    </joint>
  </xacro:macro>

  <!-- 基座连杆 -->
  <link name="base_link">
    <visual>
      <geometry>
        <box size="${body_length} 0.2 0.1"/>
      </geometry>
      <xacro:material_blue name="blue"/> <!-- 调用颜色宏 -->
    </visual>
    <collision>
      <geometry>
        <box size="${body_length} 0.2 0.1"/>
      </geometry>
    </collision>
  </link>

  <!-- 添加左右轮子(只需调用宏) -->
  <xacro:add_wheel wheel_name="left_wheel" x_pos="-0.2"/>
  <xacro:add_wheel wheel_name="right_wheel" x_pos="0.2"/>
</robot>

Xacro是URDF的扩展,通过宏(macros)、变量和编程逻辑(如条件语句、数学计算)简化URDF的编写。可以看到代码中多了全局变量,方便了后续修改,可读性也高。

二、urdf与xacro比较

特性URDFXacro
代码复用无宏,需重复编写相同结构支持宏和变量,减少重复代码
参数化硬编码,修改需逐个替换支持变量和数学表达式,便于全局调整
可维护性低(复杂模型难以维护)高(模块化设计)
最终形式直接使用需预处理为URDF

其实二者没有可比性,可以说xacro是写urdf的工具,xacro最终会被预处理成标准的URDF供ROS使用。使用xacro是为了提高代码可读性和可移植性,也不需要专门写urdf和xacro两种代码,只需要写好xacro依靠以下命令进行xacro转化urdf。

# 使用ROS工具生成URDF
rosrun xacro xacro my_robot.xacro > my_robot_generated.urdf

转化完成后还需要检验urdf文件是否正确,用以下代码输出

# 检查URDF文件
check_urdf your_robot.urdf

如未安装以来,以下命令

# 安装依赖
sudo apt-get install liburdfdom-tools

三、REP-105标准 (ai代码)

REP-105(ROS Enhancement Proposal 105)是ROS(机器人操作系统)社区定义的一项标准,全称为​“Coordinate Frames for Mobile Platforms”​​(移动平台的坐标系框架)。它规定了在移动机器人系统中坐标框架(TF frames)的命名和层次结构,旨在解决不同ROS组件之间的坐标系兼容性问题,确保导航、感知、控制等模块能够正确协同工作。

1. ​标准坐标框架层次

REP-105定义了以下关键坐标系及其层级关系(从全局到局部):

  • ​**map**
    • 作用:全局静态坐标系,表示机器人所处的环境地图(如SLAM生成的地图)。
    • 特性:长期固定,不随机器人运动改变,通常由地图服务器(如map_server)发布。
  • ​**odom**
    • 作用:基于里程计的局部坐标系,表示机器人相对于起始点的运动轨迹。
    • 特性:短期准确但长期会有漂移(累计误差),由里程计数据(如轮式编码器、IMU)驱动。
  • ​**base_link**
    • 作用:机器人本体的中心坐标系,通常固定在机器人质心或几何中心。
    • 特性:随机器人运动实时变化,所有传感器数据应转换到此坐标系。
  • ​**base_footprint**​(可选)
    • 作用base_link在地面上的垂直投影,用于导航规划(如路径避障)。
    • 特性:其Z轴高度为0(接触地面),便于描述机器人在地面的位置。

层次关系

map (全局) → odom (局部无漂移) → base_link (机器人本体) → [其他传感器/部件]

所以我们在建模的时候要严格按照标准,但这个标准在ai代码时极为重要,要要求ai按REP-105编写xacro。

 四、小车xacro与urdf代码

预计制作麦轮小车或者四轮差速小车

完全使用ai生成,稍微修改,

xacro:

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

  <!-- ================= 全局参数和宏定义 ================= -->
  <xacro:property name="PI" value="3.14159"/>
  
  <!-- 材料定义 -->
  <material name="orange">
    <color rgba="1 0.5 0 1"/>  <!-- 橙色 -->
  </material>
  <material name="black">
    <color rgba="0 0 0 1"/>     <!-- 黑色 -->
  </material>
  <material name="blue">
    <color rgba="0 0 0.8 1"/>   <!-- 蓝色 -->
  </material>

  <!-- 轮子宏定义 -->
  <xacro:macro name="drive_wheel" params="prefix x y">
    <link name="${prefix}_wheel">
      <visual>
        <!-- 圆柱体绕 X 轴旋转 90 度,轴线对齐 Y 轴 -->
        <origin xyz="0 0 0" rpy="${PI/2} 0 0"/>
        <geometry>
          <cylinder radius="0.05" length="0.03"/>
        </geometry>
        <material name="black"/>
      </visual>
      <collision>
        <origin xyz="0 0 0" rpy="${PI/2} 0 0"/>
        <geometry>
          <cylinder radius="0.05" length="0.03"/>
        </geometry>
      </collision>
      <inertial>
        <mass value="0.5"/>
        <inertia ixx="0.001" ixy="0" ixz="0" iyy="0.001" iyz="0" izz="0.001"/>
      </inertial>
    </link>
    
    <joint name="${prefix}_wheel_joint" type="continuous">
      <parent link="base_link"/>
      <child link="${prefix}_wheel"/>
      <!-- 移除关节坐标系旋转,仅调整几何体方向 -->
      <origin xyz="${x} ${y} -0.075" rpy="0 0 0"/>
      <!-- 指定绕 Y 轴旋转 -->
      <axis xyz="0 1 0"/>
    </joint>
  </xacro:macro>

  <!-- ================= 主机器人结构 ================= -->
  <!-- 虚拟根链接 -->
  <link name="base_footprint"/>

  <!-- 立方体底盘 -->
  <link name="base_link">
    <visual>
      <geometry>
        <box size="0.3 0.3 0.15"/>  <!-- 长(x) 宽(y) 高(z) -->
      </geometry>
      <material name="orange"/>
    </visual>
    <collision>
      <geometry>
        <box size="0.3 0.3 0.15"/>
      </geometry>
    </collision>
    <inertial>
      <mass value="5"/>
      <inertia ixx="0.1" ixy="0" ixz="0" iyy="0.1" iyz="0" izz="0.1"/>
    </inertial>
  </link>
  
  <joint name="base_footprint_joint" type="fixed">
    <parent link="base_footprint"/>
    <child link="base_link"/>
    <origin xyz="0 0 0.075"/>
  </joint>

  <!-- 激光雷达 -->
  <link name="lidar_link">
    <visual>
      <geometry>
        <cylinder radius="0.05" length="0.15"/>  <!-- RPLIDAR A1近似尺寸 -->
      </geometry>
      <material name="blue"/>
    </visual>
  </link>
  
  <joint name="lidar_joint" type="fixed">
    <parent link="base_link"/>
    <child link="lidar_link"/>
    <origin xyz="0 0 0.075"/>  <!-- 安装在顶部中心 -->
  </joint>

  <!-- IMU -->
  <link name="imu_link">
    <inertial>
      <mass value="0.01"/>
      <inertia ixx="1e-5" ixy="0" ixz="0" iyy="1e-5" iyz="0" izz="1e-5"/>
    </inertial>
  </link>
  
  <joint name="imu_joint" type="fixed">
    <parent link="base_link"/>
    <child link="imu_link"/>
    <origin xyz="0 0 0"/>  <!-- 安装在底盘中心 -->
  </joint>

  <!-- 四轮差速驱动 -->
  <xacro:drive_wheel prefix="front_left" x="0.12" y="0.12"/>
  <xacro:drive_wheel prefix="front_right" x="0.12" y="-0.12"/>
  <xacro:drive_wheel prefix="rear_left" x="-0.12" y="0.12"/>
  <xacro:drive_wheel prefix="rear_right" x="-0.12" y="-0.12"/>

</robot>

urdf:

<?xml version="1.0"?>
<robot name="dayang_robot">
  <!-- ================= 全局参数和宏定义 ================= -->
  <material name="orange">
    <color rgba="1 0.5 0 1"/>
  </material>
  <material name="black">
    <color rgba="0 0 0 1"/>
  </material>
  <material name="blue">
    <color rgba="0 0 0.8 1"/>
  </material>

  <!-- ================= 主机器人结构 ================= -->
  <!-- 虚拟根链接 -->
  <link name="base_footprint"/>

  <!-- 立方体底盘 -->
  <link name="base_link">
    <visual>
      <geometry>
        <box size="0.3 0.3 0.15"/>
      </geometry>
      <material name="orange"/>
    </visual>
    <collision>
      <geometry>
        <box size="0.3 0.3 0.15"/>
      </geometry>
    </collision>
    <inertial>
      <mass value="5"/>
      <inertia ixx="0.1" ixy="0" ixz="0" iyy="0.1" iyz="0" izz="0.1"/>
    </inertial>
  </link>
  

  <joint name="base_footprint_joint" type="fixed">
  <parent link="base_footprint"/>
  <child link="base_link"/>
  <origin xyz="0 0 0.075"/>  <!-- 原0改为0.075 -->
</joint>

  <!-- 激光雷达 -->
  <link name="laser_link">
    <visual>
      <geometry>
        <cylinder radius="0.05" length="0.15"/>
      </geometry>
      <material name="blue"/>
    </visual>
  </link>
  
  <joint name="laser_joint" type="fixed">
    <parent link="base_link"/>
    <child link="laser_link"/>
    <origin xyz="0 0 0.075"/>
  </joint>

  <!-- IMU -->
  <link name="imu_link">
    <inertial>
      <mass value="0.01"/>
      <inertia ixx="1e-5" ixy="0" ixz="0" iyy="1e-5" iyz="0" izz="1e-5"/>
    </inertial>
  </link>
  
  <joint name="imu_joint" type="fixed">
    <parent link="base_link"/>
    <child link="imu_link"/>
    <origin xyz="0 0 0"/>
  </joint>

  <!-- 四轮差速驱动 -->
  <!-- front_left_wheel -->
  <link name="front_left_wheel">
    <visual>
      <origin xyz="0 0 0" rpy="1.570795 0 0"/>
      <geometry>
        <cylinder radius="0.05" length="0.03"/>
      </geometry>
      <material name="black"/>
    </visual>
    <collision>
      <origin xyz="0 0 0" rpy="1.570795 0 0"/>
      <geometry>
        <cylinder radius="0.05" length="0.03"/>
      </geometry>
    </collision>
    <inertial>
      <mass value="0.5"/>
      <inertia ixx="0.001" ixy="0" ixz="0" iyy="0.001" iyz="0" izz="0.001"/>
    </inertial>
  </link>
  
  <joint name="front_left_wheel_joint" type="continuous">
    <parent link="base_link"/>
    <child link="front_left_wheel"/>
    <origin xyz="0.12 0.12 -0.075" rpy="0 0 0"/>
    <axis xyz="0 1 0"/>
  </joint>

  <!-- front_right_wheel -->
  <link name="front_right_wheel">
    <visual>
      <origin xyz="0 0 0" rpy="1.570795 0 0"/>
      <geometry>
        <cylinder radius="0.05" length="0.03"/>
      </geometry>
      <material name="black"/>
    </visual>
    <collision>
      <origin xyz="0 0 0" rpy="1.570795 0 0"/>
      <geometry>
        <cylinder radius="0.05" length="0.03"/>
      </geometry>
    </collision>
    <inertial>
      <mass value="0.5"/>
      <inertia ixx="0.001" ixy="0" ixz="0" iyy="0.001" iyz="0" izz="0.001"/>
    </inertial>
  </link>
  
  <joint name="front_right_wheel_joint" type="continuous">
    <parent link="base_link"/>
    <child link="front_right_wheel"/>
    <origin xyz="0.12 -0.12 -0.075" rpy="0 0 0"/>
    <axis xyz="0 1 0"/>
  </joint>

  <!-- rear_left_wheel -->
  <link name="rear_left_wheel">
    <visual>
      <origin xyz="0 0 0" rpy="1.570795 0 0"/>
      <geometry>
        <cylinder radius="0.05" length="0.03"/>
      </geometry>
      <material name="black"/>
    </visual>
    <collision>
      <origin xyz="0 0 0" rpy="1.570795 0 0"/>
      <geometry>
        <cylinder radius="0.05" length="0.03"/>
      </geometry>
    </collision>
    <inertial>
      <mass value="0.5"/>
      <inertia ixx="0.001" ixy="0" ixz="0" iyy="0.001" iyz="0" izz="0.001"/>
    </inertial>
  </link>
  
  <joint name="rear_left_wheel_joint" type="continuous">
    <parent link="base_link"/>
    <child link="rear_left_wheel"/>
    <origin xyz="-0.12 0.12 -0.075" rpy="0 0 0"/>
    <axis xyz="0 1 0"/>
  </joint>

  <!-- rear_right_wheel -->
  <link name="rear_right_wheel">
    <visual>
      <origin xyz="0 0 0" rpy="1.570795 0 0"/>
      <geometry>
        <cylinder radius="0.05" length="0.03"/>
      </geometry>
      <material name="black"/>
    </visual>
    <collision>
      <origin xyz="0 0 0" rpy="1.570795 0 0"/>
      <geometry>
        <cylinder radius="0.05" length="0.03"/>
      </geometry>
    </collision>
    <inertial>
      <mass value="0.5"/>
      <inertia ixx="0.001" ixy="0" ixz="0" iyy="0.001" iyz="0" izz="0.001"/>
    </inertial>
  </link>
  
  <joint name="rear_right_wheel_joint" type="continuous">
    <parent link="base_link"/>
    <child link="rear_right_wheel"/>
    <origin xyz="-0.12 -0.12 -0.075" rpy="0 0 0"/>
    <axis xyz="0 1 0"/>
  </joint>
</robot>

launch:

from launch import LaunchDescription
from launch.actions import DeclareLaunchArgument
from launch.substitutions import LaunchConfiguration
from launch_ros.actions import Node
from ament_index_python.packages import get_package_share_directory
import os

def generate_launch_description():
    package_dir = get_package_share_directory('dayang_robot_description')
    urdf_file = os.path.join(package_dir, 'urdf', 'dayang_robot.urdf')
    rviz_config = os.path.join(package_dir, 'rviz', 'dayang_robot.rviz')

    # 读取 URDF 文件内容
    with open(urdf_file, 'r') as file:
        robot_desc = file.read()

    # 定义节点
    robot_state_publisher = Node(
        package='robot_state_publisher',
        executable='robot_state_publisher',
        parameters=[{'robot_description': robot_desc}]
    )

    joint_state_publisher_gui = Node(
        package='joint_state_publisher_gui',
        executable='joint_state_publisher_gui'
    )

    rviz_node = Node(
        package='rviz2',
        executable='rviz2',
        arguments=['-d', rviz_config]
    )

    return LaunchDescription([
        robot_state_publisher,
        joint_state_publisher_gui,
        rviz_node
    ])

五、效果展示

### 设置和运行Realsense相机的仿真环境 为了在ROS 2中设置和运行Realsense相机的仿真环境,需安装必要的软件包并配置文件以支持Realsense相机的功能。这包括但不限于安装`realsense2_camera` ROS 2包以及确保其能够Gazebo协同工作。 #### 安装依赖项 首先,在目标计算机上安装适用于ROS 2版本的`realsense2-camera`包。可以通过官方仓库或源码编译的方式完成此操作。对于大多数用户而言,推荐使用apt-realsense2-camera ``` 其中 `<ros2-distro>` 应替换为实际使用的ROS 2发行版名称,比如 `foxy`, `galactic` 或者更新版本。 #### 配置URDF/Xacro模型 为了让Realsense相机能够在仿真的机器人平台上正常运作,需要修改机器人的描述文件(通常是`.urdf.xacro`格式),加入代表Realsense设备的部分。这部分通常涉及定义传感器链路及其相对于其他部件的姿态参数。具体实现方式取决于所选用的具体型号及应用场景需求[^1]。 例如,可以向XACRO文件添加如下片段来引入D435型号的Realsense摄像头组件: ```xml <xacro:include filename="$(find realsense2_description)/urdf/d435.urdf.xacro"/> <xacro:d435 name="camera" parent="base_link"> <origin xyz="0.1 0 0.2" rpy="0 0 0"/> </xacro:d435> ``` 上述代码段表示将名为“camera”的D435 Realsense装置连接至基座坐标系下的指定位置,并设定初始姿态。 #### 启动仿真会话 准备好硬件描述之后,下一步就是启动包含该摄像机在内的整个系统的模拟实例。一般情况下,这是通过编写专门用于加载所有必要资源和服务的launch脚本来达成目的。下面给出了一种可能的方法来构建这样的launch文件[^4]: ```python from launch import LaunchDescription from launch_ros.actions import Node import os from ament_index_python.packages import get_package_share_directory def generate_launch_description(): pkg_realsense = get_package_share_directory('realsense2_camera') rviz_config_file = os.path.join(pkg_realsense, 'rviz', 'rs_view.rviz') return LaunchDescription([ # 加载带有Realsense相机的小车模型到Gazebo中 Node(package='gazebo_ros', executable='spawn_entity.py', arguments=['-topic', '/robot_description', '-entity', 'my_robot']), # 运行Realsense节点以发布图像数据流和其他相关信息 Node(package='realsense2_camera', namespace='/camera', executable='realsense2_node', output='screen'), # 可选:打开RVIZ查看器以便实时观察来自Realsense的数据可视化效果 Node(package='rviz2', executable='rviz2', arguments=['-d', rviz_config_file]) ]) ``` 这段Python风格的Launch Description负责依次执行三个主要动作——把含有Realsense相机的小车子实体部署入Gazebo世界;激活Realsense SDK对应的ROS接口程序从而广播各类传感信号;最后可选择开启一个图形界面工具(RVIZ),让用户更直观地监控实验进展状况。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值