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