AMR机械臂协同作业系统 - 第4节

4. 机械臂URDF建模

4. 机械臂URDF建模:从零构建一个6轴机械臂的URDF模型

好,咱们进入正题。URDF,全称是Unified Robot Description Format,说白了就是ROS世界里描述机器人长什么样的标准语言。你想想看,要让一个机械臂动起来,你得先告诉计算机它有几根骨头、几个关节、每个关节能怎么转,对吧?URDF就是干这个的。

我个人习惯,建模之前先画个草图。别急着写代码,先在脑子里或者纸上把结构理清楚。6轴机械臂,说白了就是6个旋转关节,从底座开始,依次是:基座、腰部、大臂、小臂、腕部1、腕部2、末端执行器。每个关节之间用一个连杆连接。

核心要点: URDF的核心三要素是关节(joint)、连杆(link)和传动(transmission)。关节定义运动方式,连杆定义外观和碰撞属性,传动则把电机和关节绑定起来。

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,结果仿真里电机直接带不动大臂,一启动就报错。

避坑指南: 我曾经在调试时发现,明明URDF语法没问题,但ros2_control就是加载不了传动。查了半天,原来是hardwareInterface的名字写错了。记住,ROS2 Humble里用的是hardware_interface/PositionJointInterface,不是PositionJointInterface。少一个斜杠都不行。

4.3 完整6轴机械臂的结构图

说了这么多,咱们用一张图把整个结构串起来。下面是我用SVG画的6轴机械臂URDF结构图,展示了从基座到末端执行器的关节-连杆层级关系:

6轴机械臂URDF结构层级图 base_link (基座) 固定关节 (fixed) link_waist (腰部) 关节: joint_waist (revolute, Z轴) link_arm1 (大臂) 关节: joint_arm1 (revolute, Y轴) link_arm2 (小臂) 关节: joint_arm2 (revolute, Y轴) link_wrist1 (腕部1) 关节: joint_wrist1 (revolute, Z轴) link_wrist2 (腕部2) 关节: joint_wrist2 (revolute, Y轴) 传动: trans_waist (减速比100:1) 传动: trans_arm1 (减速比150:1) 传动: trans_arm2 (减速比120:1)

这张图里,每个方框代表一个连杆,方框之间的文字代表关节类型和旋转轴。右侧的黄色框是传动配置。你想想看,从底座到末端,一共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图,直观地看看父子关系对不对。

常见错误: 我曾经犯过一个低级错误——两个连杆用了同一个名字。URDF里name属性必须唯一,重复了会直接报错。还有,origin的rpy顺序是roll, pitch, yaw,别搞反了。

好了,到这一步,你的6轴机械臂URDF模型已经搭好了。从基座到末端,6个关节、6个连杆、6个传动,一个不少。接下来,你就可以把它加载到RViz里看看长什么样,或者配合ros2_control做仿真控制了。