
URDF的建模思路(2)
第二部分主要分析link和Joint,然后介绍草图到urdf的全过程,并且给出一个实践 Link(连杆) 基本Link定义 几何形状类型 1. 盒子(Box) 2. 圆柱(Cylinder) 3. 球体(Sphere) 4. 网格模型(Mesh) 坐标系说明 xyz : 位置偏移(x, y, z)单位:米 rpy : 旋转角度(roll, pitch, yaw)单位:弧度 roll: 绕X轴旋转 pitch: 绕Y轴旋转 yaw: 绕Z…
FIELD_GUIDE
FIELD GUIDE
Use the guide rail to jump between sections.
第二部分主要分析link和Joint,然后介绍草图到urdf的全过程,并且给出一个实践
Link(连杆)
基本Link定义
<link name="base_link">
<!-- 视觉属性 -->
<visual>
<origin xyz="0 0 0" rpy="0 0 0"/>
<geometry>
<box size="1 1 1"/>
</geometry>
<material name="blue">
<color rgba="0 0 1 1"/>
</material>
</visual>
<!-- 碰撞属性 -->
<collision>
<origin xyz="0 0 0" rpy="0 0 0"/>
<geometry>
<box size="1 1 1"/>
</geometry>
</collision>
<!-- 惯性属性 -->
<inertial>
<origin xyz="0 0 0" rpy="0 0 0"/>
<mass value="1.0"/>
<inertia ixx="1.0" ixy="0.0" ixz="0.0"
iyy="1.0" iyz="0.0"
izz="1.0"/>
</inertial>
</link>
几何形状类型
1. 盒子(Box)
<geometry>
<box size="长 宽 高"/>
</geometry>
2. 圆柱(Cylinder)
<geometry>
<cylinder radius="半径" length="长度"/>
</geometry>
3. 球体(Sphere)
<geometry>
<sphere radius="半径"/>
</geometry>
4. 网格模型(Mesh)
<geometry>
<mesh filename="package://package_name/meshes/model.stl" scale="1 1 1"/>
</geometry>
坐标系说明
xyz: 位置偏移(x, y, z)单位:米rpy: 旋转角度(roll, pitch, yaw)单位:弧度- roll: 绕X轴旋转
- pitch: 绕Y轴旋转
- yaw: 绕Z轴旋转
Joint(关节)
关节类型
- fixed(固定关节): 不允许运动
- revolute(旋转关节): 绕轴旋转,有角度限制
- continuous(连续旋转关节): 绕轴旋转,无角度限制
- prismatic(滑动关节): 沿轴线性移动
- floating(浮动关节): 6自由度运动
- planar(平面关节): 平面内运动
关节定义示例
Fixed Joint(固定关节)
<joint name="base_to_link1" type="fixed">
<parent link="base_link"/>
<child link="link1"/>
<origin xyz="0 0 0.5" rpy="0 0 0"/>
</joint>
Revolute Joint(旋转关节)
<joint name="joint1" type="revolute">
<parent link="link1"/>
<child link="link2"/>
<origin xyz="0 0 0.5" rpy="0 0 0"/>
<axis xyz="0 0 1"/>
<limit lower="-1.57" upper="1.57" effort="10" velocity="1.0"/>
<dynamics damping="0.7" friction="0.0"/>
</joint>
Continuous Joint(连续旋转关节)
<joint name="wheel_joint" type="continuous">
<parent link="base_link"/>
<child link="wheel"/>
<origin xyz="0 0 0" rpy="0 0 0"/>
<axis xyz="0 1 0"/>
<limit effort="10" velocity="1.0"/>
</joint>
Prismatic Joint(滑动关节)
<joint name="slider_joint" type="prismatic">
<parent link="base_link"/>
<child link="slider"/>
<origin xyz="0 0 0" rpy="0 0 0"/>
<axis xyz="0 0 1"/>
<limit lower="0" upper="1.0" effort="10" velocity="1.0"/>
</joint>
关节参数说明
parent: 父连杆名称child: 子连杆名称origin: 关节相对于父连杆的位置和姿态axis: 运动轴方向(xyz向量)limit: 运动限制lower: 下限(弧度或米)upper: 上限(弧度或米)effort: 最大力/力矩velocity: 最大速度
dynamics: 动力学参数damping: 阻尼系数friction: 摩擦系数
从草图到URDF的完整流程
让我们通过一个实际案例,展示从概念到URDF的完整过程。
案例:设计一个简单的巡检机器人
需求分析:
- 功能:室内巡检,携带相机和激光雷达
- 环境:平坦地面,需要通过门框
- 尺寸:宽度<0.6m,高度<1.2m
- 负载:传感器总重约2kg
- 速度:最大0.5m/s
第一步:概念设计
草图设计:
顶视图:
+------------------+
| [激光雷达] |
| |
| +---------+ |
| | 控制器 | |
| +---------+ |
| |
+------------------+
● ●
左轮 右轮
侧视图:
[激光雷达]
|
+-------+
| | ← 0.3m高
| 主体 |
+-------+
|
● ← 轮子半径0.1m
第二步:参数确定
| 部件 | 尺寸(m) | 质量(kg) | 备注 |
|---|---|---|---|
| 基座 | 0.5×0.4×0.3 | 8 | 包含电池和控制器 |
| 左轮 | R=0.1, W=0.05 | 1 | 驱动轮 |
| 右轮 | R=0.1, W=0.05 | 1 | 驱动轮 |
| 支撑轮 | R=0.05 | 0.5 | 万向轮 |
| 激光雷达 | R=0.05, H=0.04 | 0.3 | 安装在顶部 |
第三步:坐标系设计
base_link坐标系:
- 原点:基座几何中心
- X轴:向前
- Y轴:向左
- Z轴:向上
左轮位置:
- X: 0 (与基座中心对齐)
- Y: 0.225 (基座宽度的一半 + 轮宽的一半)
- Z: 0 (与基座底部平齐)
右轮位置:
- X: 0
- Y: -0.225
- Z: 0
激光雷达位置:
- X: 0
- Y: 0
- Z: 0.15 (基座高度的一半)
第四步:惯性矩阵计算
基座(盒子):
ixx = (1/12) × 8 × (0.4² + 0.3²) = 0.167iyy = (1/12) × 8 × (0.5² + 0.3²) = 0.227izz = (1/12) × 8 × (0.5² + 0.4²) = 0.273
轮子(圆柱):
ixx = (1/12) × 1 × (3×0.1² + 0.05²) = 0.0027iyy = (1/12) × 1 × (3×0.1² + 0.05²) = 0.0027izz = (1/2) × 1 × 0.1² = 0.005
第五步:编写URDF
现在我们有了所有参数,可以开始编写URDF了(见下面的完整示例)。
第六步:验证测试
# 1. 语法检查
check_urdf patrol_robot.urdf
# 2. 可视化
roslaunch urdf_tutorial display.launch model:=patrol_robot.urdf
# 3. 在RViz中检查:
# - 坐标系方向是否正确
# - 轮子位置是否对称
# - 激光雷达是否在正确位置
# - 使用joint_state_publisher测试轮子转动
# 4. Gazebo仿真测试
roslaunch gazebo_ros empty_world.launch
rosrun gazebo_ros spawn_model -file patrol_robot.urdf -urdf -model patrol_robot
第七步:问题排查与优化
常见问题:
-
机器人在Gazebo中倒下
- 检查质心位置
- 增加基座质量
- 降低重心高度
-
轮子转动方向错误
- 检查axis参数
- 调整rpy角度
-
碰撞检测异常
- 简化碰撞几何
- 检查碰撞体是否重叠
-
性能问题
- 使用简单几何作为碰撞体
- 减少mesh的面数
设计检查清单
在完成URDF后,使用此清单进行检查:
结构检查:
- 有且仅有一个base_link作为根节点
- 所有link都通过joint连接到树中
- 没有循环依赖
- 命名规范一致
坐标系检查:
- 遵循ROS坐标系约定(X前Y左Z上)
- 关节轴方向正确
- 对称部件位置对称
物理参数检查:
- 所有link都有质量(非零)
- 惯性矩阵正定(对角元素为正)
- 质心位置合理
- 关节限位合理
几何检查:
- 视觉几何与实际尺寸匹配
- 碰撞几何简化合理
- 没有明显的几何穿透
功能检查:
- 关节运动范围满足需求
- 没有自碰撞
- 传感器位置合理
- 执行器参数合理
调试与优化
常见问题诊断
问题1:机器人在RViz中显示不正确
症状:
- 部件位置错误
- 部件方向错误
- 部件缺失
诊断步骤:
# 1. 检查URDF语法
check_urdf your_robot.urdf
# 2. 查看TF树
rosrun tf view_frames
# 会生成frames.pdf,检查坐标系关系
# 3. 在RViz中显示TF
# 添加TF display,检查每个坐标系的位置和方向
# 4. 使用joint_state_publisher
# 手动调整关节角度,观察运动是否正确
解决方法:
- 检查origin的xyz和rpy参数
- 检查parent和child是否正确
- 检查关节类型是否合适
问题2:机器人在Gazebo中行为异常
症状:
- 机器人倒下或飞走
- 关节运动不正常
- 碰撞检测错误
诊断步骤:
# 1. 检查惯性参数
# 在Gazebo中查看质心位置
gz topic -e /gazebo/default/physics/contacts
# 2. 检查碰撞几何
# 在Gazebo中显示碰撞体
# View -> Collisions
# 3. 检查关节动力学
gz joint -m your_robot -j joint_name
解决方法:
- 确保所有link都有非零质量
- 检查惯性矩阵是否正定
- 简化碰撞几何
- 添加适当的阻尼和摩擦
问题3:关节运动方向错误
症状:
- 关节转动方向与预期相反
- 关节轴方向不对
诊断方法:
<!-- 在RViz中测试 -->
<!-- 1. 使用joint_state_publisher -->
<!-- 2. 调整关节角度为正值 -->
<!-- 3. 观察实际运动方向 -->
解决方法:
- 调整axis参数(改变符号)
- 调整origin的rpy参数
- 重新设计坐标系
问题4:性能问题
症状:
- Gazebo运行缓慢
- RViz显示卡顿
解决方法:
<!-- 1. 简化碰撞几何 -->
<collision>
<geometry>
<!-- 使用box/cylinder/sphere代替mesh -->
<box size="1 1 1"/>
</geometry>
</collision>
<!-- 2. 降低mesh精度 -->
<visual>
<geometry>
<!-- 使用低面数的mesh -->
<mesh filename="low_poly_model.stl"/>
</geometry>
</visual>
<!-- 3. 减少传感器更新频率 -->
<update_rate>10</update_rate> <!-- 降低频率 -->
优化技巧
1. 质量分布优化
目标:提高稳定性和运动性能
<!-- 降低重心 -->
<inertial>
<!-- 将质心向下偏移 -->
<origin xyz="0 0 -0.05" rpy="0 0 0"/>
<mass value="10"/>
<inertia .../>
</inertial>
<!-- 增加基座质量 -->
<!-- 使移动平台更稳定 -->
2. 关节限位优化
<!-- 避免自碰撞 -->
<limit lower="-1.57" upper="1.57" effort="10" velocity="1.0"/>
<!-- 添加安全余量 -->
<!-- 实际物理限位:-90° to +90° -->
<!-- URDF中设置:-85° to +85° -->
<limit lower="-1.48" upper="1.48" effort="10" velocity="1.0"/>
3. 动力学参数优化
<!-- 添加阻尼,使运动更平滑 -->
<dynamics damping="0.7" friction="0.1"/>
<!-- 不同关节使用不同参数 -->
<!-- 大关节:高阻尼 -->
<dynamics damping="1.0" friction="0.2"/>
<!-- 小关节:低阻尼 -->
<dynamics damping="0.3" friction="0.05"/>
4. 碰撞几何优化
<!-- 原则:碰撞几何尽量简单 -->
<!-- 不好的做法 -->
<collision>
<geometry>
<mesh filename="complex_model.stl"/> <!-- 10000个面 -->
</geometry>
</collision>
<!-- 好的做法 -->
<collision>
<geometry>
<cylinder radius="0.1" length="0.5"/> <!-- 简单几何 -->
</geometry>
</collision>
<!-- 或使用简化的mesh -->
<collision>
<geometry>
<mesh filename="simplified_model.stl"/> <!-- 100个面 -->
</geometry>
</collision>
5. 使用Xacro进行参数化
<?xml version="1.0"?>
<robot xmlns:xacro="http://www.ros.org/wiki/xacro" name="my_robot">
<!-- 定义参数,便于调整 -->
<xacro:property name="base_length" value="0.6"/>
<xacro:property name="base_width" value="0.4"/>
<xacro:property name="base_height" value="0.2"/>
<xacro:property name="wheel_radius" value="0.1"/>
<xacro:property name="wheel_width" value="0.05"/>
<xacro:property name="wheel_base" value="${base_width + wheel_width}"/>
<!-- 使用参数 -->
<link name="base_link">
<visual>
<geometry>
<box size="${base_length} ${base_width} ${base_height}"/>
</geometry>
</visual>
</link>
<!-- 定义宏,避免重复代码 -->
<xacro:macro name="wheel" params="prefix reflect">
<link name="${prefix}_wheel">
<visual>
<geometry>
<cylinder radius="${wheel_radius}" length="${wheel_width}"/>
</geometry>
</visual>
<collision>
<geometry>
<cylinder radius="${wheel_radius}" length="${wheel_width}"/>
</geometry>
</collision>
<inertial>
<mass value="1.0"/>
<inertia ixx="0.0027" ixy="0" ixz="0"
iyy="0.0027" iyz="0"
izz="0.005"/>
</inertial>
</link>
<joint name="${prefix}_wheel_joint" type="continuous">
<parent link="base_link"/>
<child link="${prefix}_wheel"/>
<origin xyz="0 ${reflect * wheel_base/2} 0" rpy="-1.5708 0 0"/>
<axis xyz="0 0 1"/>
</joint>
</xacro:macro>
<!-- 使用宏创建左右轮 -->
<xacro:wheel prefix="left" reflect="1"/>
<xacro:wheel prefix="right" reflect="-1"/>
</robot>
测试流程
阶段1:静态测试
# 1. 语法检查
check_urdf robot.urdf
# 2. 可视化检查
roslaunch urdf_tutorial display.launch model:=robot.urdf
# 3. 检查项:
# - 所有部件都显示了吗?
# - 位置和方向正确吗?
# - 颜色和材质正确吗?
阶段2:运动学测试
# 1. 启动joint_state_publisher
roslaunch urdf_tutorial display.launch model:=robot.urdf gui:=true
# 2. 测试每个关节:
# - 移动滑块,观察关节运动
# - 检查运动方向是否正确
# - 检查运动范围是否合理
# - 检查是否有自碰撞
阶段3:动力学测试
# 1. 在Gazebo中加载
roslaunch gazebo_ros empty_world.launch
rosrun gazebo_ros spawn_model -file robot.urdf -urdf -model my_robot
# 2. 测试项:
# - 机器人是否稳定?
# - 重力影响是否正确?
# - 关节运动是否平滑?
# - 碰撞检测是否正确?
# 3. 施加力测试
gz model -m my_robot -f 10 0 0 # 施加10N的力
阶段4:集成测试
# 1. 加载控制器
roslaunch my_robot_control control.launch
# 2. 发送控制命令
rostopic pub /cmd_vel geometry_msgs/Twist "..."
# 3. 测试传感器
rostopic echo /scan # 激光雷达
rostopic echo /camera/image_raw # 相机
# 4. 记录数据
rosbag record -a
完整示例
简单两轮机器人
<?xml version="1.0"?>
<robot name="simple_robot">
<!-- 基座 -->
<link name="base_link">
<visual>
<geometry>
<box size="0.6 0.4 0.2"/>
</geometry>
<material name="blue">
<color rgba="0 0 0.8 1"/>
</material>
</visual>
<collision>
<geometry>
<box size="0.6 0.4 0.2"/>
</geometry>
</collision>
<inertial>
<mass value="10"/>
<inertia ixx="0.4" ixy="0" ixz="0"
iyy="0.4" iyz="0"
izz="0.2"/>
</inertial>
</link>
<!-- 左轮 -->
<link name="left_wheel">
<visual>
<geometry>
<cylinder radius="0.1" length="0.05"/>
</geometry>
<material name="black">
<color rgba="0 0 0 1"/>
</material>
</visual>
<collision>
<geometry>
<cylinder radius="0.1" length="0.05"/>
</geometry>
</collision>
<inertial>
<mass value="1"/>
<inertia ixx="0.01" ixy="0" ixz="0"
iyy="0.01" iyz="0"
izz="0.01"/>
</inertial>
</link>
<!-- 右轮 -->
<link name="right_wheel">
<visual>
<geometry>
<cylinder radius="0.1" length="0.05"/>
</geometry>
<material name="black">
<color rgba="0 0 0 1"/>
</material>
</visual>
<collision>
<geometry>
<cylinder radius="0.1" length="0.05"/>
</geometry>
</collision>
<inertial>
<mass value="1"/>
<inertia ixx="0.01" ixy="0" ixz="0"
iyy="0.01" iyz="0"
izz="0.01"/>
</inertial>
</link>
<!-- 左轮关节 -->
<joint name="left_wheel_joint" type="continuous">
<parent link="base_link"/>
<child link="left_wheel"/>
<origin xyz="0 0.225 0" rpy="-1.5708 0 0"/>
<axis xyz="0 0 1"/>
</joint>
<!-- 右轮关节 -->
<joint name="right_wheel_joint" type="continuous">
<parent link="base_link"/>
<child link="right_wheel"/>
<origin xyz="0 -0.225 0" rpy="-1.5708 0 0"/>
<axis xyz="0 0 1"/>
</joint>
</robot>
简单机械臂
<?xml version="1.0"?>
<robot name="simple_arm">
<!-- 基座 -->
<link name="base_link">
<visual>
<geometry>
<cylinder radius="0.1" length="0.05"/>
</geometry>
<material name="grey">
<color rgba="0.5 0.5 0.5 1"/>
</material>
</visual>
<collision>
<geometry>
<cylinder radius="0.1" length="0.05"/>
</geometry>
</collision>
<inertial>
<mass value="2"/>
<inertia ixx="0.01" ixy="0" ixz="0"
iyy="0.01" iyz="0"
izz="0.01"/>
</inertial>
</link>
<!-- 第一节臂 -->
<link name="link1">
<visual>
<origin xyz="0 0 0.25" rpy="0 0 0"/>
<geometry>
<cylinder radius="0.05" length="0.5"/>
</geometry>
<material name="red">
<color rgba="0.8 0 0 1"/>
</material>
</visual>
<collision>
<origin xyz="0 0 0.25" rpy="0 0 0"/>
<geometry>
<cylinder radius="0.05" length="0.5"/>
</geometry>
</collision>
<inertial>
<origin xyz="0 0 0.25" rpy="0 0 0"/>
<mass value="1"/>
<inertia ixx="0.02" ixy="0" ixz="0"
iyy="0.02" iyz="0"
izz="0.001"/>
</inertial>
</link>
<!-- 第二节臂 -->
<link name="link2">
<visual>
<origin xyz="0 0 0.2" rpy="0 0 0"/>
<geometry>
<cylinder radius="0.04" length="0.4"/>
</geometry>
<material name="green">
<color rgba="0 0.8 0 1"/>
</material>
</visual>
<collision>
<origin xyz="0 0 0.2" rpy="0 0 0"/>
<geometry>
<cylinder radius="0.04" length="0.4"/>
</geometry>
</collision>
<inertial>
<origin xyz="0 0 0.2" rpy="0 0 0"/>
<mass value="0.5"/>
<inertia ixx="0.01" ixy="0" ixz="0"
iyy="0.01" iyz="0"
izz="0.001"/>
</inertial>
</link>
<!-- 关节1:基座到第一节臂 -->
<joint name="joint1" type="revolute">
<parent link="base_link"/>
<child link="link1"/>
<origin xyz="0 0 0.025" rpy="0 0 0"/>
<axis xyz="0 0 1"/>
<limit lower="-3.14" upper="3.14" effort="10" velocity="1.0"/>
<dynamics damping="0.7"/>
</joint>
<!-- 关节2:第一节臂到第二节臂 -->
<joint name="joint2" type="revolute">
<parent link="link1"/>
<child link="link2"/>
<origin xyz="0 0 0.5" rpy="0 0 0"/>
<axis xyz="0 1 0"/>
<limit lower="-1.57" upper="1.57" effort="10" velocity="1.0"/>
<dynamics damping="0.7"/>
</joint>
</robot>
最佳实践
1. 命名规范
- 使用描述性名称:
base_link,left_wheel,arm_joint1 - 保持一致的命名风格
- 避免使用特殊字符和空格
2. 坐标系设置
- 遵循ROS坐标系约定(X前,Y左,Z上)
- 关节轴方向要明确
- 注意父子连杆的相对位置
3. 物理参数
- 质量单位:千克(kg)
- 长度单位:米(m)
- 角度单位:弧度(rad)
- 惯性矩阵必须正定
4. 惯性矩阵计算
常见几何体的惯性矩阵:
盒子(Box):
圆柱(Cylinder):
球体(Sphere):
5. 验证URDF
使用ROS工具验证URDF文件:
# 检查URDF语法
check_urdf my_robot.urdf
# 生成PDF可视化
urdf_to_graphiz my_robot.urdf
# 在RViz中查看
roslaunch urdf_tutorial display.launch model:=my_robot.urdf
6. 常见错误
- 忘记定义base_link:每个机器人必须有一个根连杆
- 关节轴方向错误:导致运动方向不符合预期
- 惯性参数为零:在Gazebo仿真中会出错
- 碰撞几何过于复杂:影响仿真性能
- 父子关系循环:形成闭环会导致错误
7. 优化建议
- 碰撞几何使用简化形状(box, cylinder, sphere)
- 视觉几何可以使用详细的mesh模型
- 合理设置关节限位,避免自碰撞
- 为每个运动关节添加阻尼参数