4. 机械臂URDF建模:从零构建一个6轴机械臂的URDF模型
好,咱们进入正题。URDF,全称是Unified Robot Description Format,说白了就是ROS世界里描述机器人长什么样的标准语言。你想想看,要让一个机械臂动起来,你得先告诉计算机它有几根骨头、几个关节、每个关节能怎么转,对吧?URDF就是干这个的。
我个人习惯,建模之前先画个草图。别急着写代码,先在脑子里或者纸上把结构理清楚。6轴机械臂,说白了就是6个旋转关节,从底座开始,依次是:基座、腰部、大臂、小臂、腕部1、腕部2、末端执行器。每个关节之间用一个连杆连接。
4.1 先搭骨架:定义连杆与关节
咱们从底座开始。底座是固定的,所以它的关节类型是fixed。但注意,即使是固定关节,也得有个parent和child。我一般把world作为虚拟父级,底座作为子级。
来看一段最基础的连杆定义:
<link name="base_link">
<visual>
<geometry>
<cylinder length="0.1" radius="0.15"/>
</geometry>
<origin xyz="0 0 0" rpy="0 0 0"/>
<material name="blue">
<color rgba="0 0 1 1"/>
</material>
</visual>
<collision>
<geometry>
<cylinder length="0.1" radius="0.15"/>
</geometry>
</collision>
</link>
嗯,这里要注意:visual是给你看的,collision是给物理引擎算碰撞用的。我在项目中遇到过,有人把visual做得特别精细,结果collision忘了写,一运行机械臂直接穿模到地底下去了。所以,碰撞模型一定要有,而且尽量用简单的几何体——圆柱、立方体、球体,别用网格。
接下来是关节。第一个活动关节是腰部旋转:
<joint name="joint_waist" type="revolute">
<parent link="base_link"/>
<child link="link_waist"/>
<origin xyz="0 0 0.1" rpy="0 0 0"/>
<axis xyz="0 0 1"/>
<limit lower="-3.14" upper="3.14" effort="100" velocity="1.0"/>
</joint>
为什么origin的z是0.1?因为底座高度是0.1米,关节正好在底座顶面。这个坐标系偏移,说白了就是「关节相对于父连杆的位置」。axis定义了旋转轴,0 0 1就是绕Z轴转,也就是水平旋转。
limit一定要设置合理。我刚开始做的时候,把upper和lower设成了正负π,结果仿真时机械臂像风扇一样转个不停。后来我学乖了,先查一下真实机械臂的关节限位,再填进去。
4.2 传动:让电机和关节「对上话」
URDF本身不包含电机信息。但如果你要用ros2_control或者Gazebo做仿真,就必须加transmission标签。它的作用,说白了就是告诉系统:「这个关节是由哪个电机驱动的,减速比是多少」。
<transmission name="trans_waist">
<type>transmission_interface/SimpleTransmission</type>
<joint name="joint_waist">
<hardwareInterface>hardware_interface/PositionJointInterface</hardwareInterface>
</joint>
<actuator name="motor_waist">
<mechanicalReduction>100</mechanicalReduction>
</actuator>
</transmission>
你看,这里mechanicalReduction是100,意味着电机转100圈,关节才转1圈。为什么?因为实际电机转速高、扭矩小,必须通过减速器放大扭矩。我在项目中遇到过,有人把减速比设成1,结果仿真里电机直接带不动大臂,一启动就报错。
ros2_control就是加载不了传动。查了半天,原来是hardwareInterface的名字写错了。记住,ROS2 Humble里用的是hardware_interface/PositionJointInterface,不是PositionJointInterface。少一个斜杠都不行。
4.3 完整6轴机械臂的结构图
说了这么多,咱们用一张图把整个结构串起来。下面是我用SVG画的6轴机械臂URDF结构图,展示了从基座到末端执行器的关节-连杆层级关系:
这张图里,每个方框代表一个连杆,方框之间的文字代表关节类型和旋转轴。右侧的黄色框是传动配置。你想想看,从底座到末端,一共6个旋转关节,每个关节都配了一个传动。这就是一个完整的6轴机械臂URDF骨架。
4.4 完整代码示例
下面是一个简化但完整的6轴机械臂URDF片段,包含了两个关节和传动。实际项目中,你需要把6个关节全部写出来,但结构是完全一样的:
<?xml version="1.0"?>
<robot name="my_6axis_arm" xmlns:xacro="http://www.ros.org/wiki/xacro">
<!-- 基座 -->
<link name="base_link">
<visual>
<geometry><cylinder length="0.1" radius="0.15"/></geometry>
<material name="blue"/>
</visual>
</link>
<!-- 腰部关节 -->
<joint name="joint_waist" type="revolute">
<parent link="base_link"/>
<child link="link_waist"/>
<origin xyz="0 0 0.1" rpy="0 0 0"/>
<axis xyz="0 0 1"/>
<limit lower="-3.14" upper="3.14" effort="100" velocity="1.0"/>
</joint>
<link name="link_waist">
<visual>
<geometry><box size="0.1 0.1 0.3"/></geometry>
<origin xyz="0 0 0.15" rpy="0 0 0"/>
</visual>
</link>
<!-- 大臂关节 -->
<joint name="joint_arm1" type="revolute">
<parent link="link_waist"/>
<child link="link_arm1"/>
<origin xyz="0 0 0.3" rpy="0 0 0"/>
<axis xyz="0 1 0"/>
<limit lower="-2.0" upper="2.0" effort="150" velocity="0.8"/>
</joint>
<link name="link_arm1">
<visual>
<geometry><box size="0.08 0.08 0.4"/></geometry>
<origin xyz="0 0 0.2" rpy="0 0 0"/>
</visual>
</link>
<!-- 传动配置 -->
<transmission name="trans_waist">
<type>transmission_interface/SimpleTransmission</type>
<joint name="joint_waist">
<hardwareInterface>hardware_interface/PositionJointInterface</hardwareInterface>
</joint>
<actuator name="motor_waist">
<mechanicalReduction>100</mechanicalReduction>
</actuator>
</transmission>
<transmission name="trans_arm1">
<type>transmission_interface/SimpleTransmission</type>
<joint name="joint_arm1">
<hardwareInterface>hardware_interface/PositionJointInterface</hardwareInterface>
</joint>
<actuator name="motor_arm1">
<mechanicalReduction>150</mechanicalReduction>
</actuator>
</transmission>
</robot>
xacro来写URDF。为什么?因为6个关节的结构几乎一样,只是参数不同。用xacro的宏定义,你可以把关节和连杆封装成一个模板,然后传参调用。这样代码量能减少70%,而且改参数特别方便。
4.5 验证你的URDF
写完之后怎么验证?ROS2提供了check_urdf工具。在终端里运行:
check_urdf my_arm.urdf
它会输出每个关节和连杆的层级关系。如果语法有错,它会直接告诉你哪一行有问题。我一般还会用urdf_to_graphiz生成一张PDF图,直观地看看父子关系对不对。
name属性必须唯一,重复了会直接报错。还有,origin的rpy顺序是roll, pitch, yaw,别搞反了。
好了,到这一步,你的6轴机械臂URDF模型已经搭好了。从基座到末端,6个关节、6个连杆、6个传动,一个不少。接下来,你就可以把它加载到RViz里看看长什么样,或者配合ros2_control做仿真控制了。