【ROS2学习笔记】URDF 机器人建模

前言

本系列博文是本人的学习笔记,自用为主,不是教程,学习请移步其他大佬的相关教程。前几篇学习资源来自鱼香ROS大佬的详细教程,适合深入学习,但对本人这样的初学者不算友好,后续笔记将以@古月居的ROS2入门21讲为主,侵权即删。

一、学习目标

  1. 理解 URDF 的核心作用 —— 让 ROS “认识” 机器人的形状、结构和运动方式
  2. 掌握 URDF 的两大核心元素:link(连杆)和joint(关节)的配置规则
  3. 能看懂并编写简单的 URDF 文件(描述移动机器人 / 机械臂基础结构)
  4. 学会用工具可视化 URDF 模型(RViz 预览、urdf_to_graphviz看结构)
  5. 明确建模中的关键细节(单位、坐标系、外观与碰撞的区别)

二、铺垫:机器人的组成(建模的基础)

在学 URDF 之前,先搞懂机器人的实际结构 —— 建模是 “数字孪生”,必须对应真实机器人的组成。机器人通常分 4 部分,URDF 主要描述硬件结构(其他部分通过 ROS 节点实现):

组成部分作用(小白理解)对应 URDF 描述的内容
硬件结构看得见的刚体(底盘、轮子、机械臂连杆、相机)link(描述每个刚体的形状、大小)
驱动系统让刚体动起来的装置(电机、舵机)joint(描述电机驱动的运动方式)
传感系统感知环境的部件(雷达、相机、编码器)部分传感器用link+fixed关节固定
控制系统计算和发指令的平台(树莓派、电脑)不通过 URDF 描述(通过 ROS 节点实现)

核心逻辑:URDF 的本质是 “用 XML 语言把机器人的硬件结构拆成link(刚体)和joint(连接 / 运动),再组合成完整模型”。

三、URDF 是什么?(基础概念)

3.1 一句话定义:ROS 的 “机器人说明书”

URDF 全称是 Unified Robot Description Format(统一机器人描述格式),是 ROS 官方规定的机器人建模语言,特点:

  • 基于XML 格式(类似网页的 HTML 标签,有<标签名>和属性);
  • 能描述机器人的:
    1. 外观(形状、颜色、大小);
    2. 结构(哪个刚体连哪个刚体);
    3. 运动(刚体能怎么动、动多大范围);
    4. 物理属性(质量、惯性、碰撞检测)。

3.2 URDF 的基本结构(固定模板)

所有 URDF 文件都以<robot>为根标签,内部包含多个<link><joint>,结构如下:

<?xml version="1.0" ?> <!-- XML版本声明(固定) --> <robot name="机器人名字"> <!-- 根标签,name是机器人的唯一标识 --> <!-- 1. 定义连杆(link):比如底盘、轮子 --> <link name="连杆1名字"> <!-- 外观、碰撞、惯性等配置 --> </link> <!-- 2. 定义关节(joint):连接两个连杆 --> <joint name="关节1名字" type="关节类型"> <!-- 父连杆、子连杆、运动轴等配置 --> </joint> <!-- 更多link和joint... --> </robot> 

四、URDF 核心元素 1:连杆(link)—— 描述机器人的 “刚体”

link对应机器人的一个独立刚体(比如底盘、轮子、相机),主要配置 3 类属性:外观(visual)碰撞(collision)惯性(inertial)(初学者先掌握前两类)。

4.1 link的核心子标签(小白必懂)

子标签作用关键子元素小白解读与例子
<visual>描述机器人的 “外观”(仅显示用)<origin><geometry><material>比如底盘外观是黄色圆柱体,轮子是白色圆柱体
<collision>描述机器人的 “碰撞检测”(运算用)<origin><geometry>为了减少计算,碰撞模型通常简化(比如外观是复杂模型,碰撞是简单长方体)
<inertial>描述机器人的 “物理惯性”(动力学用)<mass>(质量)、<inertia>(惯性矩阵)比如重 1kg 的底盘比 0.5kg 的更难加速(初学者可先忽略,动力学仿真时再用)

4.2 逐标签详解(结合例子)

以 “移动机器人底盘(base_link)” 为例,解析每个标签的含义:

<link name="base_link"> <!-- link名字:base_link(底盘,约定俗成的名字) --> <!-- 1. 外观配置(visual):用户看到的样子 --> <visual> <!-- origin:外观相对于link坐标系的偏移(xyz平移,rpy旋转) --> <!-- xyz:(0,0,0)表示外观和link原点重合;rpy:(0,0,0)表示无旋转(弧度制!) --> <origin xyz="0 0 0" rpy="0 0 0" /> <!-- geometry:外观的几何形状(常用3种) --> <geometry> <!-- 圆柱体:length(长度)、radius(半径),单位:米 --> <cylinder length="0.16" radius="0.20"/> <!-- 其他形状示例: <sphere radius="0.1"/> <!-- 球体:半径0.1米 --> <box size="0.2 0.3 0.1"/> <!-- 长方体:x/y/z边长 --> <mesh filename="package://learning_urdf/meshes/base.stl"/> <!-- 外部3D模型(STL文件) --> --> </geometry> <!-- material:外观颜色(rgba:红、绿、蓝、透明度,范围0-1) --> <material name="yellow"> <!-- name:颜色名字(可复用) --> <color rgba="1 0.4 0 1"/> <!-- 黄色(红=1,绿=0.4,蓝=0,透明度=1) --> </material> </visual> <!-- 2. 碰撞配置(collision):简化的几何形状,减少碰撞检测计算量 --> <collision> <!-- 偏移和外观一致(也可单独设置,比如重心偏移) --> <origin xyz="0 0 0" rpy="0 0 0"/> <geometry> <!-- 碰撞用和外观相同的圆柱体(简单形状,计算快) --> <cylinder length="0.16" radius="0.20"/> </geometry> </collision> <!-- 3. 惯性配置(inertial):动力学仿真用(初学者可选) --> <inertial> <mass value="1.0"/> <!-- 质量:1千克 --> <inertia ixx="0.01" ixy="0.0" ixz="0.0" iyy="0.01" iyz="0.0" izz="0.02"/> <!-- 惯性矩阵(需计算,可工具生成) --> </inertial> </link> 
关键注意点:
  • 单位:长度用米(m),角度用弧度(rad)(比如 90 度 = 1.5707 弧度);
  • visual vs collision:外观可以复杂(好看),碰撞必须简化(比如用长方体代替复杂外壳),否则 ROS 计算碰撞时会卡顿;
  • origin的坐标系:每个link都有自己的坐标系(默认原点在刚体中心),origin描述子元素(外观 / 碰撞)相对于这个原点的偏移。

五、URDF 核心元素 2:关节(joint)—— 描述机器人的 “运动”

joint连接两个link(父连杆parent和子连杆child),定义它们之间的运动方式(比如轮子绕轴转、机械臂关节上下转)。

5.1 六种关节类型(重点!结合实际场景)

ROS 的 URDF 支持 6 种关节类型,初学者只需掌握前 4 种,后 2 种(floating、planar)很少用:

关节类型运动方式关键特点实际应用例子
continuous绕单一轴无限旋转无角度限制移动机器人的驱动轮(可以一直转)
revolute绕单一轴有限旋转有最小 / 最大角度限制(limit机械臂的肘关节(只能弯 90 度)
prismatic沿单一轴有限平移有最小 / 最大位置限制(limit直线电机(比如升降平台上下移)
fixed无运动(固定连接)最常用,父 / 子连杆相对位置不变相机与底盘的连接(相机不动)
floating无固定轴,可平移 + 旋转(6 自由度)无限制,少用无人机(但无人机通常用其他建模方式)
planar沿某平面平移 + 绕平面法向旋转(3 自由度)限制在平面内运动,少用桌面上的滑块(只能在桌面移动 + 旋转)

5.2 joint的核心子标签(逐标签解析)

以 “移动机器人左车轮关节(left_wheel_joint)” 为例,解析每个标签:

<joint name="left_wheel_joint" type="continuous"> <!-- 关节名+类型(无限旋转) --> <!-- 1. origin:子连杆(轮子)相对于父连杆(底盘)的偏移 --> <!-- xyz:轮子在底盘的x=0,y=0.19(底盘半径0.2,轮子在外侧),z=-0.05(轮子中心比底盘低0.05米,接触地面) --> <!-- rpy:(0,0,0)无旋转 --> <origin xyz="0 0.19 -0.05" rpy="0 0 0"/> <!-- 2. parent & child:连接的两个连杆 --> <parent link="base_link"/> <!-- 父连杆:底盘(固定不动的参考) --> <child link="left_wheel_link"/> <!-- 子连杆:左车轮(相对于父连杆运动) --> <!-- 3. axis:关节的运动轴(单位向量,xyz分别对应x/y/z轴) --> <!-- (0,1,0):绕y轴旋转(轮子绕y轴转才能前进,符合实际) --> <axis xyz="0 1 0"/> <!-- 4. limit:关节的运动限制(continuous类型无角度限制,可省略;revolute/prismatic必须加) --> <!-- 示例(revolute关节的limit): <limit lower="-1.57" upper="1.57" effort="10" velocity="1.0"/> lower/upper:最小/最大角度(弧度,-1.57~1.57=±90度) effort:最大力矩(牛顿·米,电机能提供的最大力) velocity:最大角速度(弧度/秒,电机最大转速) --> </joint> 
关键注意点:
  • 父 / 子连杆顺序parent是固定参考(如底盘),child是运动的连杆(如轮子),不能反;
  • axis的方向:决定关节绕哪个轴运动,比如轮子必须绕 y 轴转(0,1,0)才能前进,绕 x 轴转(1,0,0)就会侧翻;
  • limit的单位:角度用弧度,平移用米,速度用弧度 / 秒(角速度)或米 / 秒(线速度)。

六、实战:移动机器人 URDF 完整解析(从文件到可视化)

以用户提供的 “四轮移动机器人(2 驱动轮 + 2 万向轮)” 为例,带小白完整看懂 URDF,并学会可视化。

6.1 功能包结构(存放 URDF 相关文件)

建模前先建规范的功能包,方便管理文件,结构如下(learning_urdf是功能包名):

learning_urdf/ ├── urdf/ # 存放URDF文件(核心) │ └── mbot_base.urdf # 机器人底盘URDF文件 ├── meshes/ # 存放外部3D模型(STL/DAE文件,本例用基础几何形状,暂空) ├── launch/ # 存放启动文件(一键启动RViz可视化) │ └── display.launch.py# 可视化启动文件 └── rviz/ # 存放RViz配置文件(保存可视化视角、显示项) └── urdf_rviz.rviz # RViz配置(显示URDF模型) 

6.2 完整 URDF 文件解析(mbot_base.urdf

逐段注释,小白能看懂每个linkjoint的作用:

<?xml version="1.0" ?> <!-- 根标签:机器人名字叫mbot --> <robot name="mbot"> <!-- 1. 底盘连杆(base_link):机器人的基础,所有其他连杆都间接连到这里 --> <link name="base_link"> <visual> <origin xyz="0 0 0" rpy="0 0 0"/> <geometry> <!-- 底盘是圆柱体:长度0.16m(前后方向),半径0.20m(左右方向) --> <cylinder length="0.16" radius="0.20"/> </geometry> <material name="yellow"> <color rgba="1 0.4 0 1"/> <!-- 黄色外观 --> </material> </visual> </link> <!-- 2. 左驱动轮关节(连接base_link和left_wheel_link) --> <joint name="left_wheel_joint" type="continuous"> <!-- 轮子位置:x=0(前后居中),y=0.19(比底盘半径小0.01m,避免超出),z=-0.05(接触地面) --> <origin xyz="0 0.19 -0.05" rpy="0 0 0"/> <parent link="base_link"/> <!-- 父连杆:底盘 --> <child link="left_wheel_link"/> <!-- 子连杆:左车轮 --> <axis xyz="0 1 0"/> <!-- 绕y轴旋转(前进方向) --> </joint> <!-- 3. 左驱动轮连杆(left_wheel_link) --> <link name="left_wheel_link"> <visual> <!-- rpy="1.5707 0 0":绕x轴转90度(圆柱体立起来,变成轮子形状) --> <origin xyz="0 0 0" rpy="1.5707 0 0" /> <geometry> <!-- 轮子:半径0.06m(直径12cm),长度0.025m(轮子宽度) --> <cylinder radius="0.06" length="0.025"/> </geometry> <material name="white"> <color rgba="1 1 1 0.9"/> <!-- 白色外观 --> </material> </visual> </link> <!-- 4. 右驱动轮关节(和左轮对称,y=-0.19) --> <joint name="right_wheel_joint" type="continuous"> <origin xyz="0 -0.19 -0.05" rpy="0 0 0"/> <parent link="base_link"/> <child link="right_wheel_link"/> <axis xyz="0 1 0"/> <!-- 同样绕y轴旋转 --> </joint> <!-- 5. 右驱动轮连杆(和左轮相同) --> <link name="right_wheel_link"> <visual> <origin xyz="0 0 0" rpy="1.5707 0 0" /> <geometry> <cylinder radius="0.06" length="0.025"/> </geometry> <material name="white"> <color rgba="1 1 1 0.9"/> </material> </visual> </link> <!-- 6. 前万向轮关节(front_caster_joint,continuous类型,自由旋转) --> <joint name="front_caster_joint" type="continuous"> <!-- 位置:x=0.18(底盘前端),y=0(左右居中),z=-0.095(比驱动轮低,接触地面) --> <origin xyz="0.18 0 -0.095" rpy="0 0 0"/> <parent link="base_link"/> <child link="front_caster_link"/> <axis xyz="0 1 0"/> <!-- 绕y轴旋转(万向轮自由转向) --> </joint> <!-- 7. 前万向轮连杆(球体形状,半径0.015m) --> <link name="front_caster_link"> <visual> <origin xyz="0 0 0" rpy="0 0 0"/> <geometry> <sphere radius="0.015" /> <!-- 万向轮用球体表示 --> </geometry> <material name="black"> <color rgba="0 0 0 0.95"/> <!-- 黑色外观 --> </material> </visual> </link> <!-- 8. 后万向轮关节(和前轮对称,x=-0.18) --> <joint name="back_caster_joint" type="continuous"> <origin xyz="-0.18 0 -0.095" rpy="0 0 0"/> <parent link="base_link"/> <child link="back_caster_link"/> <axis xyz="0 1 0"/> </joint> <!-- 9. 后万向轮连杆(和前轮相同) --> <link name="back_caster_link"> <visual> <origin xyz="0 0 0" rpy="0 0 0"/> <geometry> <sphere radius="0.015" /> </geometry> <material name="black"> <color rgba="0 0 0 0.95"/> </material> </visual> </link> </robot> 

6.3 模型可视化(让小白看到实际效果)

光有 URDF 文件还不够,需要用 RViz 显示模型,步骤如下:

步骤 1:编写 Launch 文件(launch/display.launch.py

Launch 文件用于一键启动 RViz,并加载 URDF 模型,避免手动输入复杂命令:

from launch import LaunchDescription from launch_ros.actions import Node from ament_index_python.packages import get_package_share_directory import os def generate_launch_description(): # 1. 获取功能包路径(自动找到learning_urdf的位置) pkg_path = get_package_share_directory('learning_urdf') # 2. URDF文件路径 urdf_path = os.path.join(pkg_path, 'urdf', 'mbot_base.urdf') # 3. RViz配置文件路径 rviz_path = os.path.join(pkg_path, 'rviz', 'urdf_rviz.rviz') # 4. 启动RViz节点,加载URDF和RViz配置 rviz_node = Node( package='rviz2', # RViz功能包 executable='rviz2', # RViz可执行文件 arguments=[ '-d', rviz_path, # 加载预配置的RViz文件 '--ros-args', '-p', f'robot_description:={open(urdf_path).read()}' # 传递URDF内容给ROS参数 ] ) # 5. 启动关节状态发布节点(显示关节运动,本例固定,可选) joint_state_pub_node = Node( package='joint_state_publisher_gui', executable='joint_state_publisher_gui' # 带GUI的关节控制(可拖动关节运动) ) # 6. 启动机器人状态发布节点(发布TF树,让RViz显示连杆关系) robot_state_pub_node = Node( package='robot_state_publisher', executable='robot_state_publisher', arguments=[urdf_path] # 传入URDF文件 ) return LaunchDescription([ joint_state_pub_node, robot_state_pub_node, rviz_node ]) 
步骤 2:运行可视化
  1. 效果:RViz 窗口显示黄色底盘、白色驱动轮、黑色万向轮,可通过joint_state_publisher_gui的滑块拖动轮子旋转。

启动 Launch 文件

ros2 launch learning_urdf display.launch.py 

编译功能包

cd dev_ws # 进入ROS工作空间 colcon build --packages-select learning_urdf # 编译URDF功能包 source install/setup.bash # 加载环境变量 

6.4 查看 URDF 结构(urdf_to_graphviz工具)

用工具生成 TF 树,直观看到linkjoint的连接关系:

  1. 查看效果:当前目录生成mbot_base.gvmbot_base.gv.pdf,打开 PDF 可看到:base_link是根,连接 4 个关节(左 / 右轮、前 / 后万向轮),每个关节对应一个子link—— 和我们设计的完全一致!

生成结构文件

cd dev_ws/src/learning_urdf/urdf # 进入URDF文件目录 urdf_to_graphviz mbot_base.urdf # 生成结构文件 

安装工具(如果没有):

sudo apt install ros-humble-urdf-tools # Humble版本,其他版本替换为对应名称 

七、复习要点总结(小白必背)

  1. URDF 核心作用:用 XML 描述机器人的刚体(link)和运动(joint),让 ROS “认识” 机器人;
  2. link三要素
    • visual:外观(形状、颜色、偏移),给人看;
    • collision:碰撞(简化几何),给 ROS 计算用;
    • inertial:惯性(质量、惯性矩阵),给动力学仿真用;
  3. joint关键参数
    • 类型:continuous(轮子)、revolute(机械臂关节)、fixed(固定);
    • 连接:parent(固定参考)和child(运动连杆);
    • 运动:axis(运动轴)、limit(运动限制);
  4. 单位陷阱:长度用米,角度用弧度(90 度 = 1.5707),速度用 rad/s;
  5. 可视化工具
    • Launch 文件 + RViz:看模型外观;
    • urdf_to_graphviz:看linkjoint的连接结构。

掌握这些,小白就能看懂大部分 URDF 文件,也能编写简单的移动机器人 / 机械臂模型,为后续的运动控制、仿真打下基础

Read more

Flutter 三方库 l10n_countries 的鸿蒙化适配指南 - 实现全球 250+ 国家与地区的本地化信息映射、支持端侧多语言地理名称展示与旗帜图标索引实战

Flutter 三方库 l10n_countries 的鸿蒙化适配指南 - 实现全球 250+ 国家与地区的本地化信息映射、支持端侧多语言地理名称展示与旗帜图标索引实战

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net Flutter 三方库 l10n_countries 的鸿蒙化适配指南 - 实现全球 250+ 国家与地区的本地化信息映射、支持端侧多语言地理名称展示与旗帜图标索引实战 前言 在进行 Flutter for OpenHarmony 的全球化(i18n)电商、旅游或社交应用开发时,如何根据用户的语言设置,准确展示全球各国的名称、二位/三位代码(ISO 3166-1)及货币信息?手动维护上百个国家的翻译表显然是不现实的。l10n_countries 是一款功能完备的本地化地理信息库。本文将介绍如何在鸿蒙端构建极致、精准的全球地理背景感知能力。 一、原直观解析 / 概念介绍 1.1 基础原理 该库内置了一套基于 CLDR(通用当地文字库)的大型映射表。通过将 ISO 国家代码作为唯一索引,

By Ne0inhk
Flutter 组件 dartframe 的适配 鸿蒙Harmony 实战 - 极简主义后端框架集成、多端逻辑复用与业务解耦重构方案

Flutter 组件 dartframe 的适配 鸿蒙Harmony 实战 - 极简主义后端框架集成、多端逻辑复用与业务解耦重构方案

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net Flutter 组件 dartframe 的适配 鸿蒙Harmony 实战 - 极简主义后端框架集成、多端逻辑复用与业务解耦重构方案 前言 在 Flutter 生态不断向桌面和服务器端扩展的今天,寻找一个轻量、灵活且对 Dart 原生特性挖掘深化的框架,已成为全栈开发者的追求。dartframe 正是这样一款倡导“极简、快速、模块化”的通用型 Dart 开发框架,它不依赖于繁重的第三方库,力求给开发者最直观、最清爽的编码体验。 当我们站在鸿蒙系统(OpenHarmony)适配的门槛上,审视这类框架时,其最大的魅力在于:它能让我们在鸿蒙端复用那一套被验证过的、纯粹的 Dart 业务逻辑块,同时轻松剥离那些高度依赖平台、环境的副作用代码。 本文将带你深度剖析如何通过 dartframe 在鸿蒙应用中实现高效的“多端同构”开发模式,

By Ne0inhk
Linux匿名管道通信:原理深挖+代码实现,一篇吃透进程间数据流转

Linux匿名管道通信:原理深挖+代码实现,一篇吃透进程间数据流转

🔥个人主页:Cx330🌸 ❄️个人专栏:《C语言》《LeetCode刷题集》《数据结构-初阶》《C++知识分享》 《优选算法指南-必刷经典100题》《Linux操作系统》:从入门到入魔 《Git深度解析》:版本管理实战全解 🌟心向往之行必能至 🎥Cx330🌸的简介: 目录 前言: 一. 进程间通信介绍 1.1 进程间通信目的 1.2 进程间通信的发展与分类 二、先搞懂:什么是管道?匿名管道有何特殊性? 2.1 管道的本质 2.2 管道的核心特性 三、匿名管道的创建 3.1 匿名管道的创建流程 3.2 匿名管道的使用示例 四. 核心深挖:匿名管道的底层原理 4.1 fork

By Ne0inhk
Flutter 组件 cleany 适配鸿蒙 HarmonyOS 实战:自动化清理矩阵,构建复杂应用的状态闭环与资源防腐架构

Flutter 组件 cleany 适配鸿蒙 HarmonyOS 实战:自动化清理矩阵,构建复杂应用的状态闭环与资源防腐架构

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net Flutter 组件 cleany 适配鸿蒙 HarmonyOS 实战:自动化清理矩阵,构建复杂应用的状态闭环与资源防腐架构 前言 在鸿蒙(OpenHarmony)生态迈向多任务并行、长周期驻留及高频账户流转的全场景办公与生活背景下,如何确保应用在退出登录、环境切换或异常恢复时能够“不留痕迹”地销毁脏数据,已成为衡量应用健壮性的核心指标。在鸿蒙设备这类强调分布式沙箱隔离与严苛内存占用(Resident Set Size)管控的环境下,如果应用缺乏统一的资源清理机制,由于由于散落在各处的 Stream 监听、本地缓存及内存单例,极易由于由于状态残留导致不同用户间的数据越权或 UI 状态的逻辑死锁。 我们需要一种能够集中注册清理任务、支持并发异步销毁且具备原子性执行保障的状态复位框架。 cleany 为 Flutter 开发者引入了极其暴力且高效的“全域清算”范式。它通过中心化的管理器(Manager),允许各个业务模块在初始化时注册其对应的资源回收钩子。在适

By Ne0inhk