首页/目录/全部文章

全部文章

八个专题的源码、算法与协议笔记都在这里。

笔记列表

ros_tutorials 源码详细分析

ros_tutorials 源码详细分析

工作区路径:/home/cp/work2/ros2Learn/ros2_humble/src/ros/ros_tutorials
仓库:github.com/ros/ros_tutorials

ros_tutorials 是 ROS 官方入门教程集合,包含经典 turtlesim 仿真器及 ROS 1 时代的 C++/Python 教程。在 ROS 2 Humble 工作区中,只有 turtlesim 是 ament 包、会被实际编译roscpp_tutorialsrospy_tutorials 仍为 catkin/ROS 1 遗留代码,供参考,不参与 ROS 2 构建。


1. 仓库结构总览

1
2
3
4
5
ros_tutorials/
├── ros_tutorials/ # 元包(catkin metapackage,ROS 1)
├── turtlesim/ # ★ ROS 2 核心:Qt 仿真 + 自定义 msg/srv/action
├── roscpp_tutorials/ # ROS 1 C++ 教程(catkin,未移植)
└── rospy_tutorials/ # ROS 1 Python 教程(catkin,未移植)
构建系统 ROS 2 可编译 版本 作用
turtlesim ament_cmake 1.4.3 交互式小乌龟仿真,ROS 2 官方教程标配
roscpp_tutorials catkin 0.9.1 roscpp API 分步示例
rospy_tutorials catkin 0.9.1 rospy API 分步示例
ros_tutorials catkin metapackage 0.9.1 聚合上述三包

2. 在 ROS 2 生态中的角色

flowchart LR
  subgraph tutorials [ros_tutorials]
    TS[turtlesim_node\nQt GUI]
    TELEOP[turtle_teleop_key]
    DRAW[draw_square]
    MIMIC[mimic]
  end

  subgraph concepts [演示的 ROS 2 概念]
    TOPIC[Topic 发布/订阅]
    SRV[Service 请求/响应]
    ACT[Action 长时间任务]
    PARAM[Parameter 动态配置]
  end

  subgraph users [典型使用者]
    DOC[官方文档教程]
    LEARN[初学者实验]
    TEST[多节点/多命名空间测试]
  end

  TS --> TOPIC & SRV & ACT & PARAM
  TELEOP --> TOPIC & ACT
  DRAW --> TOPIC & SRV
  MIMIC --> TOPIC
  tutorials --> users

turtlesim 是 ROS 2 “Hello World” 级演示平台:比 demo_nodes_cpp 更直观,覆盖 topic、service、action、parameter 四类通信原语。


3. turtlesim — 架构设计

3.1 类层次

1
2
3
4
5
6
TurtleApp (QApplication + rclcpp::init)
└── TurtleFrame (QFrame, 500×500 画布)
├── QTimer 16ms → onUpdate() → spin_some + updateTurtles
├── 全局服务: spawn / kill / clear / reset
└── map<string, TurtlePtr> turtles_
└── Turtle (单只乌龟的 ROS 接口 + 运动学)
文件 职责
TurtleApp turtlesim.cpp 初始化 rclcpp + Qt 事件循环
TurtleFrame turtle_frame.cpp/h 画布、定时器、乌龟生命周期管理
Turtle turtle.cpp/h 单龟 ROS 接口、运动积分、绘制

3.2 Qt + rclcpp 融合模式

核心在 TurtleFrame::onUpdate()

1
2
3
4
5
6
7
8
9
10
11
12
void TurtleFrame::onUpdate()
{
if (!rclcpp::ok())
{
close();
return;
}

rclcpp::spin_some(nh_);

updateTurtles();
}
  • QTimer 16ms(约 60Hz)驱动 GUI 刷新
  • 每帧调用 rclcpp::spin_some 处理回调,避免阻塞 Qt 主线程
  • 这是 GUI 节点中 rclcpp 与 Qt 集成的经典模式

3.3 坐标系

  • 内部仿真:pos_ 以米为单位,Y 轴向下(屏幕坐标)
  • 对外 Pose.msg:Y 轴向上(y = canvas_height - pos_.y()
  • 角度 theta:弧度,逆时针为正

4. ROS 2 接口一览

4.1 节点级服务(/turtlesim 节点)

服务 类型 功能
/spawn turtlesim/srv/Spawn 创建新乌龟
/kill turtlesim/srv/Kill 删除指定乌龟
/clear std_srvs/Empty 清除轨迹
/reset std_srvs/Empty 重置为单龟初始状态

4.2 每只乌龟的接口(以 turtle1 为例)

类型 名称 消息/服务类型
订阅 /turtle1/cmd_vel geometry_msgs/Twist
发布 /turtle1/pose turtlesim/Pose
发布 /turtle1/color_sensor turtlesim/Color
服务 /turtle1/set_pen turtlesim/SetPen
服务 /turtle1/teleport_relative turtlesim/TeleportRelative
服务 /turtle1/teleport_absolute turtlesim/TeleportAbsolute
Action /turtle1/rotate_absolute turtlesim/RotateAbsolute

4.3 自定义消息/服务/Action

Pose.msg — 位姿 + 速度反馈:

1
2
float32 x, y, theta
float32 linear_velocity, angular_velocity

RotateAbsolute.action — ROS 2 Action 教程示例:

1
2
3
4
5
6
7
8
# Goal
float32 theta # 目标朝向(弧度)
---
# Result
float32 delta # 相对起始角位移
---
# Feedback
float32 remaining # 剩余转角

Spawn.srv — 可选名称,空则自动生成 turtle2turtle3


5. Turtle 类核心逻辑

5.1 构造:注册 ROS 接口

1
2
3
4
5
6
7
8
9
10
Turtle::Turtle(...)
{
rclcpp::QoS qos(rclcpp::KeepLast(7));
velocity_sub_ = nh_->create_subscription<geometry_msgs::msg::Twist>(
real_name + "/cmd_vel", qos, ...);
pose_pub_ = nh_->create_publisher<turtlesim::msg::Pose>(real_name + "/pose", qos);
...
rotate_absolute_action_server_ = rclcpp_action::create_server<...>(
nh, real_name + "/rotate_absolute", ...);
}

5.2 运动学更新(update

每帧 dt ≈ 0.016s 执行:

  1. Teleport 请求(相对/绝对瞬移,可画线)
  2. Action 旋转:向目标角 theta 以 ±1.0 rad/s 旋转,发布 feedback/result
  3. 速度超时:超过 1 秒无 cmd_vel 则速度归零
  4. 积分运动
1
2
3
4
5
6
orient_ = orient_ + ang_vel_ * dt;
orient_ = normalizeAngle(orient_);
pos_.rx() += std::cos(orient_) * lin_vel_x_ * dt
- std::sin(orient_) * lin_vel_y_ * dt;
pos_.ry() -= std::cos(orient_) * lin_vel_y_ * dt
+ std::sin(orient_) * lin_vel_x_ * dt;
  1. 边界 clamp — 撞墙警告并限制在画布内
  2. 发布 pose 和 color_sensor(读取轨迹图像像素 RGB)

5.3 Action 与 cmd_vel 互斥

收到 cmd_vel 时会 abort 正在执行的 rotate_absolute goal;收到新 rotation goal 也会 abort 前一个。体现 Action 与连续控制的分工。


6. TurtleFrame 管理逻辑

6.1 启动

  • 声明参数 background_r/g/b(0–255,默认蓝紫色 #4556ff
  • share/turtlesim/images/ 加载 9 种乌龟皮肤(ardent, foxy, humble 等发行版命名)
  • 默认 spawn 一只 turtle1 在画布中心
  • 订阅 /parameter_events 以响应背景色变更

6.2 乌龟命名

1
2
3
4
5
6
7
if (real_name.empty())
{
do {
ss << "turtle" << ++id_counter_;
real_name = ss.str();
} while (hasTurtle(real_name));
}

空名称自动生成;重名 spawn 返回失败。


7. 教程可执行文件

CMakeLists 构建 4 个目标:

可执行文件 源码 演示内容
turtlesim_node src/*.cpp 主仿真器
turtle_teleop_key teleop_turtle_key.cpp 键盘控制 + Action 客户端
draw_square draw_square.cpp 状态机画正方形
mimic mimic.cpp 订阅 pose 转发 cmd_vel

7.1 turtle_teleop_key

  • 原始终端模式读键盘(Unix termios / Windows ReadConsoleInput
  • 方向键 → 发布 Twistturtle1/cmd_vel
  • G/B/V/C/D/E/R/T → 发送 RotateAbsolute action goal
  • 独立线程 rclcpp::spin 处理 action 回调

7.2 draw_square

有限状态机:FORWARD → STOP_FORWARD → TURN → STOP_TURN → ...

  • 订阅 turtle1/pose 获取反馈
  • 发布 turtle1/cmd_vel 控制运动
  • 启动时调用 /reset 服务
  • 典型 闭环控制 入门示例

7.3 mimic

1
2
3
4
5
6
twist_pub_ = this->create_publisher<geometry_msgs::msg::Twist>("output/cmd_vel", 1);
pose_sub_ = this->create_subscription<turtlesim::msg::Pose>(
"input/pose", 1, ...);
// 将 input 龟的速度复制给 output 龟
twist.angular.z = pose->angular_velocity;
twist.linear.x = pose->linear_velocity;

配合 remapping 可实现「一只龟模仿另一只」:

1
2
3
ros2 run turtlesim mimic --ros-args \
-r input/pose:=/turtle1/pose \
-r output/cmd_vel:=/turtle2/cmd_vel

8. 多实例与 Launch

1
2
3
4
5
6
return LaunchDescription([
launch_ros.actions.Node(
namespace= "turtlesim1", package='turtlesim', executable='turtlesim_node', ...),
launch_ros.actions.Node(
namespace= "turtlesim2", package='turtlesim', executable='turtlesim_node', ...),
])

命名空间隔离后,话题变为 /turtlesim1/turtle1/cmd_vel/turtlesim2/turtle1/cmd_vel,演示 namespace 机制


9. 依赖关系

1
2
3
4
5
6
turtlesim
├── rclcpp / rclcpp_action
├── geometry_msgs / std_msgs / std_srvs
├── rosidl_default_generators # 自定义接口
├── Qt5 Widgets # GUI
└── ament_index_cpp # 查找 share/images

10. roscpp_tutorials(ROS 1 遗留)

构建系统:catkin + roscpp + rosconsole

按子目录组织的 C++ 教程,覆盖 ROS 1 API:

目录 主题
talker/ / listener/ 最基本 pub/sub
add_two_ints_* Service 客户端/服务端
parameters/ 参数服务器
timers/ Timer 回调
listener_*_spin 多种 spin 模式
notify_connect/ 连接/订阅通知
node_handle_namespaces/ 命名空间
time_api/sleep/ 时间 API

代码使用 #include "ros/ros.h",带 %Tag(...)% 标记供文档系统自动抽取。ROS 2 等价教程在 ros2/examples 和官方文档中,不在此包。


11. rospy_tutorials(ROS 1 遗留)

构建系统:catkin + rospy

按编号组织的 Python 教程:

目录 主题
001_talker_listener/ pub/sub 基础
002_headers/ 消息 header
003_listener_with_user_data/ 回调 userdata
004_listener_subscribe_notify/ 订阅通知
005_add_two_ints/ Service
006_parameters/ 参数
007_connection_header/ 连接头
008_on_shutdown/ 关闭钩子
009_advanced_publish/ 高级发布

含与 roscpp_tutorials 交叉测试的 launch 文件(C++ ↔ Python 互通)。同样未移植到 rclpy。


12. 典型使用流程

1
2
3
4
5
6
7
8
9
10
11
12
13
# 终端 1:启动仿真
ros2 run turtlesim turtlesim_node

# 终端 2:键盘控制
ros2 run turtlesim turtle_teleop_key

# 或:自动画正方形
ros2 run turtlesim draw_square

# 查看接口
ros2 topic list
ros2 service list
ros2 action list

常用 CLI 实验:

1
2
3
4
ros2 service call /spawn turtlesim/srv/Spawn "{x: 2.0, y: 2.0, theta: 0.0, name: ''}"
ros2 topic pub /turtle1/cmd_vel geometry_msgs/msg/Twist \
"{linear: {x: 1.0}, angular: {z: 0.5}}"
ros2 param set /turtlesim background_r 200

13. 设计特点与局限

特点 说明
教学优先 API 覆盖全面,代码可读性高
Qt 集成 spin_some + QTimer 经典 GUI 模式
多龟支持 spawn/kill 动态管理
ROS 2 Action RotateAbsolute 是官方 action 教程载体
发行版彩蛋 乌龟皮肤命名对应 ROS 发行版
局限 说明
无物理仿真 简单运动学积分,非 Gazebo
2D 平面 仅 x/y/theta
ROS 1 教程未移植 roscpp/rospy tutorials 在 Humble 中不编译
单线程 spin_some 不适合高并发重计算
无 TF 不发布坐标变换(与真实机器人栈不同)

14. 推荐阅读顺序

  1. 快速体验:运行 turtlesim_node + turtle_teleop_key
  2. 主程序turtlesim.cppturtle_frame.cppturtle.cpp
  3. 接口定义msg/srv/action/
  4. 教程节点draw_square.cpp(闭环)→ mimic.cpp(remapping)→ teleop_turtle_key.cpp(action)
  5. 对比 ROS 1 教程roscpp_tutorials/talker/talker.cpp 与 ROS 2 demo_nodes_cpp 的差异
  6. Launchmultisim.launch.py 理解 namespace

15. 小结

ros_tutorials 仓库是 ROS 官方教程的历史集合:在 ROS 2 Humble 中,实际可用的是 turtlesim——一个 Qt + rclcpp 的小乌龟仿真器,完整演示 Topic、Service、Action、Parameter 四大通信机制,并附带键盘遥控、画正方形、mimic 等示例节点。roscpp_tutorialsrospy_tutorials 保留 ROS 1 catkin 代码,供 API 对比参考,但不参与 ROS 2 构建。

如果你希望,我可以把本文写入 ros2doc/ros/ros_tutorials 源码详细分析.md,或继续分析 draw_square 状态机与 pose 闭环控制的数学细节

urdfdom 源码详细分析

urdfdom 源码详细分析

工作区路径:/home/cp/work2/ros2Learn/ros2_humble/src/ros/urdfdom
版本:3.0.2,许可证 BSD

urdfdom 是 URDF(Unified Robot Description Format)的 XML 解析与序列化库:用 TinyXML 读取 URDF XML,填充 urdfdom_headers 中定义的 C++ 数据结构(LinkJointMaterial 等)。它本身不做运动学计算,也不依赖 rclcpp;ROS 2 中 urdf 包、kdl_parserrobot_state_publisher 等都在此之上工作。


1. 与 urdfdom_headers 的分工

urdfdom 与同级包 urdfdom_headers 是配套关系:

职责 内容
urdfdom_headers 数据模型(头文件) ModelInterfaceLinkJointPoseInertial
urdfdom XML ↔ 数据模型 parseURDF()parseLink()exportURDF()
1
2
3
4
5
URDF XML 文件
↓ TinyXML 解析 (urdfdom)
urdf::ModelInterface (urdfdom_headers)

urdf::Model (ros2/urdf) / kdl_parser / Gazebo / MoveIt ...

2. 仓库结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
urdfdom/
├── CMakeLists.txt # 顶层构建、依赖、安装
├── package.xml
├── cmake/ # FindTinyXML、pkg-config、config.cmake
├── xsd/
│ ├── urdf.xsd # URDF XML Schema
│ └── autogenerated.urdf
└── urdf_parser/
├── include/urdf_parser/
│ ├── urdf_parser.h # 公共 API
│ └── exportdecl.h # DLL 导出宏
├── src/
│ ├── model.cpp # ★ parseURDF 主流程
│ ├── link.cpp # link/visual/collision/inertial
│ ├── joint.cpp # joint 全类型 + mimic/limit/dynamics
│ ├── pose.cpp # origin xyz/rpy
│ ├── world.cpp # world 解析(桩)
│ ├── urdf_sensor.cpp # camera/ray 传感器
│ ├── urdf_model_state.cpp # 模型状态
│ ├── twist.cpp
│ ├── check_urdf.cpp # CLI 校验工具
│ └── urdf_to_graphviz.cpp # 导出 Graphviz DOT
└── test/ # gtest + 内嵌 gtest 源码

3. 四个共享库 + 一个 INTERFACE 目标

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
add_urdfdom_library(LIBNAME urdfdom_world
SOURCES src/pose.cpp src/model.cpp src/link.cpp src/joint.cpp src/world.cpp)

add_urdfdom_library(LIBNAME urdfdom_model
SOURCES src/pose.cpp src/model.cpp src/link.cpp src/joint.cpp)

add_urdfdom_library(LIBNAME urdfdom_sensor
SOURCES src/urdf_sensor.cpp
LINK urdfdom_model)

add_urdfdom_library(LIBNAME urdfdom_model_state
SOURCES src/urdf_model_state.cpp src/twist.cpp)

add_library(urdf_parser INTERFACE)
target_link_libraries(urdf_parser INTERFACE urdfdom_model urdfdom_sensor urdfdom_world)
内容 ROS 2 常用程度
liburdfdom_model.so robot/link/joint 核心解析 最常用
liburdfdom_world.so 含 world 桩代码 很少
liburdfdom_sensor.so <sensor> 扩展 较少
liburdfdom_model_state.so 模型状态快照 较少
urdf_parser (INTERFACE) 聚合上述库 CMake 依赖用

注意model.cpplink.cppjoint.cpppose.cppurdfdom_modelurdfdom_world各编译一份(代码重复链接到两个 .so)。


4. 公共 API

1
2
3
4
5
6
7
8
namespace urdf{
URDFDOM_DLLAPI ModelInterfaceSharedPtr parseURDF(const std::string &xml_string);
URDFDOM_DLLAPI ModelInterfaceSharedPtr parseURDFFile(const std::string &path);
URDFDOM_DLLAPI TiXmlDocument* exportURDF(ModelInterfaceSharedPtr &model);
URDFDOM_DLLAPI TiXmlDocument* exportURDF(const ModelInterface &model);
URDFDOM_DLLAPI bool parsePose(Pose&, TiXmlElement*);
...
}
函数 作用
parseURDF(xml) 从 XML 字符串解析,成功返回 ModelInterfaceSharedPtr,失败返回 null
parseURDFFile(path) 读文件后调用 parseURDF
exportURDF(model) 将内存模型序列化为 TiXmlDocument
URDFVersion 解析 <robot version="x.y">,当前仅支持 1.0

5. parseURDF 主流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
ModelInterfaceSharedPtr parseURDF(const std::string &xml_string)
{
ModelInterfaceSharedPtr model(new ModelInterface);
TiXmlDocument xml_doc;
xml_doc.Parse(xml_string.c_str());
...
TiXmlElement *robot_xml = xml_doc.FirstChildElement("robot");
model->name_ = robot_xml->Attribute("name");
// 版本必须为 1.0
URDFVersion version(robot_xml->Attribute("version"));

// 1. 解析所有 <material>
// 2. 解析所有 <link>,关联 visual 材质
// 3. 解析所有 <joint>
// 4. model->initTree(parent_link_tree) 建立父子关系
// 5. model->initRoot(parent_link_tree) 确定唯一根 link
return model;
}

解析顺序严格为:material → link → joint → 建树

失败策略:任一步出错即 model.reset() 返回 null,错误通过 console_bridge 打日志。


6. 建树逻辑(urdfdom_headers)

initTreeinitRoot 定义在 urdfdom_headers/include/urdf_model/model.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void initTree(std::map<std::string, std::string> &parent_link_tree)
{
for (each joint) {
// child_link->setParent(parent_link)
// child_link->parent_joint = joint
// parent_link->child_links.push_back(child_link)
parent_link_tree[child_name] = parent_name;
}
}

void initRoot(const std::map<std::string, std::string> &parent_link_tree)
{
// 找 parent_link_tree 中不出现的 link → 根
// 必须恰好一个根,否则 ParseError
}

约束:

  • 每个 joint 必须有 parent/child link,且 link 必须已定义
  • 整棵 URDF 必须是单根树(一个 root link)
  • 多根或无根都会抛 ParseError

parseLink 支持:

XML 元素 C++ 结构 说明
<inertial> Inertial mass、6 惯性张量分量、origin
<visual> × N visual_array[] 几何 + 材质引用
<collision> × N collision_array[] 碰撞几何
几何类型 Geometry sphere / box / cylinder / mesh

几何解析示例(mesh):

1
2
3
4
5
6
7
8
bool parseMesh(Mesh &m, TiXmlElement *c)
{
m.filename = c->Attribute("filename");
if (c->Attribute("scale"))
m.scale.init(c->Attribute("scale"));
else
m.scale = (1,1,1);
}

材质处理两阶段:

  1. 顶层 <material name="..."> 进入 model->materials_
  2. visual 中 <material name="Grey"/> 通过 assignMaterial() 解析引用或内联定义

8. Joint 解析(joint.cpp

支持的关节类型:

XML type Joint::Type
fixed FIXED
revolute REVOLUTE(必须<limit>
continuous CONTINUOUS
prismatic PRISMATIC(必须<limit>
floating FLOATING
planar PLANAR

可选子元素:

元素 结构 要点
<origin> parent_to_joint_origin_transform 缺省为单位变换
<axis xyz="..."> Vector3 缺省 (1,0,0)
<limit> JointLimits effort/velocity 必填
<mimic joint="..." multiplier="" offset=""> JointMimic robot_state_publisher 会用到
<dynamics> damping/friction
<safety_controller> soft limits + k gains
<calibration> rising/falling

9. Pose 解析(pose.cpp

1
2
3
4
5
6
7
bool parsePose(Pose &pose, TiXmlElement* xml)
{
if (xml->Attribute("xyz"))
pose.position.init(xyz_str);
if (xml->Attribute("rpy"))
pose.rotation.init(rpy_str); // roll-pitch-yaw
}

<origin xyz="..." rpy="..."/> 在 joint、visual、collision、inertial 中广泛使用。数值解析与异常类型(ParseError)在 urdfdom_headersutils.h 中实现。


10. 扩展模块状态

模块 文件 状态
World world.cpp parseWorld() 为桩,”to be implemented”
Sensor urdf_sensor.cpp 实现 camera/ray 等 <sensor> 解析
ModelState urdf_model_state.cpp 机器人状态快照格式

大多数 ROS 2 机器人描述只需 urdfdom_model


11. 命令行工具

工具 作用
check_urdf 解析 URDF,打印 robot 名与 link 树
urdf_to_graphviz 输出 Graphviz DOT(urdf_to_graphiz 为拼写错误的 deprecated 别名)
urdf_mem_test 内存/解析压力测试

check_urdf 用法:

1
2
check_urdf robot.urdf
# 输出 root link 及递归 child 树

12. 依赖关系

1
2
3
4
5
urdfdom
├── urdfdom_headers # C++ 数据模型(header-only 安装)
├── TinyXML # XML 解析(v1,tinyxml.h)
├── tinyxml_vendor # ROS 提供的 TinyXML 封装
└── console_bridge # CONSOLE_BRIDGE_logError/Debug

构建类型为 纯 cmake(非 ament),ROS 2 通过 package.xml<build_type>cmake</build_type> 集成进 colcon。


13. 在 ROS 2 中的调用链

ROS 2 应用通常不直接调用 urdfdom,而是走 urdf 包:

URDF XML stringurdf::Model::initStringpluginlib\nurdf_xml_parser/URDFXMLParserurdf::parseURDF\n(urdfdom)urdf::Model\nextends ModelInterfacekdl_parser\nrobot_state_publisher\nrviz ...

urdf::URDFXMLParser 内部直接调用:

1
2
3
4
urdf::ModelInterfaceSharedPtr URDFXMLParser::parse(const std::string & xml_string)
{
return urdf::parseURDF(xml_string);
}

robot_state_publisher 中的典型用法:

1
2
3
urdf::Model model;
model.initString(urdf_xml);
kdl_parser::treeFromUrdfModel(model, tree);

14. 测试

测试 内容
urdf_unit_test Rotation RPY/四元数往返、URDF 解析边界
urdf_version_test version 属性校验
urdf_double_convert 浮点转换
memtest 大规模解析内存

测试内嵌了完整 gtest 源码(较老的做法)。


15. XSD 与规范

xsd/urdf.xsd 提供 URDF XML Schema;autogenerated.urdf 为生成示例。运行时解析不强制 XSD 校验,靠手写 TinyXML 逻辑 + 运行时检查。

当前硬编码限制:

1
2
3
4
if (!version.equal(1, 0))
{
throw std::runtime_error("Invalid 'version' specified; only version 1.0 is currently supported");
}

16. 设计特点与局限

特点 说明
DOM 风格 解析后完整对象树在内存,可随机访问
双向转换 parse + export(round-trip 基本支持)
树结构校验 initTree/initRoot 保证单根 kinematic tree
console_bridge 日志 错误/调试信息统一输出
多库拆分 按功能域分 so,可按需链接
局限 说明
TinyXML v1 较老 API,非 TinyXML2
仅 URDF 1.0 version 属性其他值拒绝
World 未实现 parseWorld 为空
无 xacro xacro 由 launch/外部工具预处理
无 schema 运行时验证 依赖代码内检查
代码重复 model/link/joint 源文件编入两个 .so

17. 推荐阅读顺序

  1. 数据模型urdfdom_headers/include/urdf_model/model.hlink.hjoint.h
  2. 入口model.cppparseURDF()
  3. 细节link.cpp(几何/惯性)→ joint.cpp(关节类型/mimic)→ pose.cpp
  4. 集成ros2/urdf/urdf/src/urdf_plugin.cppmodel.cpp(pluginlib 选择)
  5. 下游kdl_parser / robot_state_publisher 如何把 ModelInterface 转为 KDL/TF
  6. 工具check_urdf.cpp 理解解析结果树形结构

18. 小结

urdfdom 是 ROS 机器人描述的 XML 解析层:TinyXML 读入 URDF,填充 urdfdom_headersModelInterface,并通过 initTree/initRoot 建立 link-joint 树。ROS 2 中 urdf::Model::initString() 经 pluginlib 默认调用 urdf::parseURDF(),再被 kdl_parserrobot_state_publisher、rviz 等广泛使用。理解 urdfdom 是理解整条 URDF → 运动学 → TF/可视化 链路的起点。

如果你希望,我可以把本文写入 ros2doc/ros/urdfdom 源码详细分析.md,或继续分析 urdfdom_headers 中 Link/Joint 数据结构的完整字段定义

Ubuntu 22.04 下 ROS 2 Humble:环境搭建、编译与文档生成

Ubuntu 22.04 下 ROS 2 Humble:环境搭建、编译与文档生成

说明:LTS 版本为 22.04(非 22.4)。以下命令默认使用 jammy 与 ROS 2 Humble,与当前工作区 ros2_humble 一致。


1. 系统与基础依赖

1
2
3
4
sudo apt update && sudo apt install -y \
locales curl gnupg lsb-release software-properties-common \
build-essential cmake git python3-pip python3-venv \
python3-colcon-common-extensions python3-vcstool

设置 UTF-8 区域(ROS 2 官方建议):

1
2
3
sudo locale-gen en_US en_US.UTF-8
sudo update-locale LC_ALL=en_US.UTF-8 LANG=en_US.UTF-8
export LANG=en_US.UTF-8

2. 安装 ROS 2 Humble(二进制)

官方文档 添加源并安装。典型最小开发环境可用 Desktop(含 rviz 等)或 ROS-Base(更轻):

1
2
3
4
5
6
7
sudo curl -sSL https://raw.githubusercontent.com/ros/rosdistro/master/ros.key \
-o /usr/share/keyrings/ros-archive-keyring.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/ros-archive-keyring.gpg] http://packages.ros.org/ros2/ubuntu $(. /etc/os-release && echo $UBUNTU_CODENAME) main" \
| sudo tee /etc/apt/sources.list.d/ros2.list > /dev/null

sudo apt update
sudo apt install -y ros-humble-desktop ros-dev-tools

每次新开终端加载环境:

1
source /opt/ros/humble/setup.bash

(可写入 ~/.bashrc。)


3. 编译本工作区 ros2_humble

在已 source /opt/ros/humble/setup.bash 的前提下:

1
2
3
cd /path/to/ros2Learn/ros2_humble
# 若尚未有 src,需按你方流程用 vcstool 拉取;已有 src 则直接编译
colcon build --symlink-install --cmake-args -DCMAKE_BUILD_TYPE=RelWithDebInfo

编译完成后:

1
source install/setup.bash

常见注意点:

  • 全量源码树体量大,可先只编关心的包:
    colcon build --packages-up-to rclcpp
  • 内存不足时可限制并行:
    colcon build --parallel-workers 2

4. 「生成文档」的几种含义与做法

4.1 本仓库 doc/ 下的 Markdown(含 Mermaid)

这些是静态说明,无需编译;若需要 HTML/PDF 或统一站点:

方式 用途
VS Code / Cursor 预览 直接打开 .md,安装 Mermaid 插件可看图
MkDocs + mkdocs-material + mermaid 插件 生成静态站点
mdBook 适合书式文档,可集成 mermaid

示例(MkDocs,在项目根或 doc/ 下自建 mkdocs.yml 后):

1
2
3
pip install mkdocs mkdocs-material pymdown-extensions
mkdocs serve # 本地预览
mkdocs build # 输出到 site/

(当前仓库若未配置 mkdocs.yml,需自行添加;不属于 ROS 核心工作区的一部分。)

4.2 从 C/C++ 源码生成 API 文档(Doxygen)

适用于阅读 rcl / rmw / rclcpp 等 API:

1
sudo apt install -y doxygen graphviz

单个包里若存在 Doxyfile 或 CMake 里启用了 doxygen_add_docs,则在该包 build 目录执行对应 target;很多上游包默认打开 Doxygen,可自行在包根目录生成配置:

1
2
3
4
cd /path/to/some_package
doxygen -g Doxyfile
# 编辑 Doxyfile:INPUT、RECURSIVE、OUTPUT_DIRECTORY 等
doxygen Doxyfile

生成结果一般在 html/index.html,用浏览器打开即可。

4.3 官方 ROS 2 手册(Sphinx,独立仓库)

若目标是生成与 docs.ros.org 同风格的 Humble 手册(非你本地 ros2_humble/src 的 API 站):

1
2
3
4
5
6
7
sudo apt install -y python3-pip
pip install --user sphinx sphinx-rtd-theme
git clone https://github.com/ros2/ros2_documentation.git
cd ros2_documentation
pip install --user -r requirements.txt
make html
# 输出在 build/html,打开 build/html/index.html

分支需选 humble(克隆后 git checkout humble)。

4.4 包内自带的文档目标(若存在)

部分包会定义 docdocs 的 CMake target。可在编译后尝试:

1
2
3
4
cd ros2_humble
colcon build --packages-select SOME_PACKAGE
# 再进入 build/SOME_PACKAGE 查看是否有 doc 相关 target
cmake --build build/SOME_PACKAGE --target help 2>/dev/null | grep -i doc

有则例如:cmake --build build/SOME_PACKAGE --target doc


5. 建议小结

目标 推荐做法
能编译、运行本工作区 Ubuntu 22.04 + ros-humble-desktop + colcon build + source install/setup.bash
维护架构说明 Markdown 继续放在 doc/,编辑器预览;需要站点时用 MkDocs/mdBook
啃 C API apt install doxygen graphviz,对目标包生成或自建 Doxyfile
啃用户级教程与概念 克隆 ros2_documentationhumble 分支,make html

若你后续把「文档」限定为某一种(例如只要 MkDocs 站点或只要 rcl 的 Doxygen),可以在 doc/ 里再拆一篇专用步骤并配上仓库内的配置文件。

HCI 设备注册与数据通路

HCI 设备注册与数据通路

1. 注册

典型 transport:

1
2
3
4
5
6
probe
-> hci_alloc_dev
-> set bus/driver callbacks
-> SET_HCIDEV_DEV
-> set quirks
-> hci_register_dev

注册完成后出现 hciN,HCI Core异步执行 open/setup/config。

2. hci_dev状态

关键状态包括 registered、up/running、init、setup、config、suspend、rfkill。Transport必须保证 open/close和异步completion之间的生命周期。

3. Open

Open通常:

-启动RX endpoints/serial;
-提交URB或使能IRQ;
-初始化queue/parser;
-打开controller电源;
-允许TX。

HCI Core随后发送 Reset、Read Local Version、Read Supported Commands/Features等。

4. TX skb

hci_skb_pkt_type(skb)决定帧类型。Transport在成功接管skb后负责最终释放;错误路径必须遵循对应callback约定,避免double free/leak。

5. RX

Driver必须提交完整、带packet type的 frame:

1
2
3
4
event: header 2 bytes + plen
ACL : header 4 bytes + dlen
SCO : header 3 bytes + dlen
ISO : header 4 bytes + dlen

UART parser用 h4_recv_buf()按descriptor表重组;USB由endpoint天然区分部分类型。

6. Setup

setup可在核心初始化命令前/期间进行厂商firmware下载。某些设备下载后USB re-enumerate,初次probe仅负责loader阶段。

7. Unregister

1
2
3
4
disconnect/remove
-> hci_unregister_dev
-> kill work/URBs/IRQ
-> hci_free_dev

先阻止新I/O,再同步取消完成回调。

8. 用户接口

  • mgmt socket供bluetoothd;
  • raw HCI socket供低层工具;
  • sysfs /sys/class/bluetooth/hciN
  • debugfs视配置;
  • rfkill控制radio policy。

Raw HCI并不绕过controller firmware风险,应限制权限。

BTUSB 探测、固件与电源管理

BTUSB 探测、固件与电源管理

1. Probe

btusb_probe()匹配USB ID/interface class和driver_info quirks:

-找到interrupt、bulk、isoc endpoints;
-分配 btusb_datahci_dev
-设置 open/close/flush/send/notify;
-按Intel/Realtek/Broadcom/MediaTek等覆盖setup回调;
-claim相关interface;
-hci_register_dev()

2. RX URBs

  • interrupt IN:HCI events;
  • bulk IN:ACL;
  • isochronous IN:SCO;
    -部分设备用bulk传ISO。

Completion校验status/length,提交HCI frame并在运行状态下resubmit。

3. TX

  • command走control URB;
  • ACL走bulk OUT;
  • SCO走isoc OUT;
    -ISO依设备能力。

Anchor跟踪in-flight URBs,close/disconnect时统一kill。

4. SCO

Voice连接数变化触发work,选择alternate setting和isoc packet size。USB bandwidth或altsetting错误表现为A2DP正常但HFP语音失败。

5. 厂商初始化

USB ID/版本决定:

-Intel firmware和bootloader mode;
-Realtek patch/config;
-Broadcom patchram;
-MediaTek firmware;
-QCA/Atheros下载或quirk。

不能只按USB VID推断firmware文件名。

6. Suspend

btusb_suspend()

-阻止新TX;
-检查/等待busy计数;
-停止URBs;
-保存wake相关状态。

btusb_resume()重提RX URBs、恢复isoc和queued deferred TX。USB autosuspend由 enable_autosuspend及Kconfig默认控制。

7. Remote wake

控制器、USB host和平台电源域均需支持。若combo module电源被Rockchip rfkill切断,USB remote wake无法弥补。

8. Disconnect

必须处理主interface和isoc/diag interface先后disconnect,避免二次unregister。

9. 调试

1
2
3
4
lsusb -t
usb-devices
dmesg | grep -Ei 'Bluetooth|btusb|firmware'
cat /sys/bus/usb/devices/*/power/control

频繁 -71/-110通常先检查USB信号、电源和autosuspend,而不是HCI协议。

HCI UART:Line Discipline 与 Serdev

HCI UART:Line Discipline 与 Serdev

1. 两种绑定

TTY line discipline

用户态打开 /dev/ttySx,设置波特率并附加 N_HCI,再通过ioctl选择protocol。传统 hciattach使用此路径。

Serdev

Bluetooth作为UART controller的DT/ACPI child,kernel serdev driver直接probe,管理电源、GPIO、clock和baud,不需要用户态attach。

2. Common object

struct hci_uart包含:

  • tty或serdev;
  • hci_dev;
  • protocol ops/private data;
  • tx queue/state bits;
  • init work;
  • current/oper speed;
  • flow control。

3. Protocol registry

hci_uart_register_proto()把 H4/H5/BCSP/BCM/QCA等挂入固定ID数组。选择后protocol提供:

  • open/close;
  • flush;
  • setup;
  • recv;
  • enqueue/dequeue;
  • wakeup;
  • suspend/resume。

4. TX

1
2
3
4
5
hci_uart_send_frame
-> proto enqueue
-> hci_uart_tx_wakeup
-> tty write 或 serdev_device_write_buf
-> write_wakeup循环dequeue

HCI_UART_SENDINGTX_WAKEUP位避免并发发送者丢唤醒。

5. RX

TTY receive_buf或serdev receive_buf把字节交给protocol parser。Parser可跨调用保存partial frame。

6. Speed

常见流程:

1.初始低速;
2.发送厂商baud命令;
3.等待controller确认;
4.切host UART speed;
5.启用flow control。

次序或时延错误会在下载固件后失联。

7. Serdev DT

标准写法是UART child:

1
2
3
4
5
bluetooth {
compatible = "brcm,bcm43438-bt";
shutdown-gpios = <...>;
max-speed = <3000000>;
};

但本树多数RK3588板型使用独立 bluetooth-platdata节点和Rockchip rfkill,不能混为同一驱动模型。

8. RK3588

模块常连接UART7或其它高速UART,需RTS/CTS、DMA/PIO和pinmux一致。平台rfkill可在suspend把RTS切为GPIO。

H4、H5、BCSP 与低功耗 UART 协议

H4、H5、BCSP 与低功耗 UART 协议

1. H4

最简单framing:

1
1-byte packet type + HCI header + payload

无校验、重传或序号,依赖可靠UART和通常的RTS/CTS。h4_recv_buf()按packet descriptor组帧。

2. H5 / Three-wire

H5提供:

-SLIP式定界/转义;
-seq/ack;
-可靠包重传;
-可选CRC;
-link establishment;
-无需硬件flow control的能力。

状态机和retrans timer比H4复杂。错误波特率、peer reset或丢字节会触发重新同步。

3. BCSP

CSR BlueCore协议,同样有reliable/unreliable channels、ack、CRC和SLIP framing,主要服务旧硬件。

4. HCILL

TI Low Level协议在H4数据外加入sleep/wakeup控制,协调主机和controller UART clock/power。

5. QCA IBS

Qualcomm In-Band Sleep使用wake/sleep字节和timer管理TX/RX休眠,避免常开UART。hci_qca.c还管理regulator、clock、speed和firmware。

6. Broadcom

Broadcom仍以H4传包,但serdev driver管理shutdown GPIO、device wake/host wake、clock和runtime PM。

7. Protocol选择

选择依据controller,不是主机SoC:

  • H4:广泛;
  • H5:Realtek及部分3-wire芯片;
  • LL:TI;
  • QCA:Qualcomm;
  • BCM:Broadcom;
  • ATH3K:Atheros;
  • MRVL:Marvell。

8. 本defconfig

显式启用 BT_HCIUART_ATH3K,它select H4。没有显式启用H5/BCM/QCA等,除非其它Kconfig选择或实际构建配置追加,否则对应对象不会进入 hci_uart.o

9. 性能

UART吞吐受波特率、flow control、调度延迟和small packet开销限制。ACL吞吐高时必须使用稳定RTS/CTS;低功耗握手参数过激会增加首包时延。

Broadcom、Realtek、Intel、QCA 与 MediaTek 支持

Broadcom、Realtek、Intel、QCA 与 MediaTek 支持

1. Helper层

btbcmbtrtlbtintelbtqcabtmtk封装厂商命令、版本解析、firmware下载和quirks,由USB/UART/SDIO transport调用。

2. Broadcom

-读取local name/version;
-设置地址;
-patchram firmware;
-UART clock/baud;
-shutdown/device-wake/host-wake;
-32.768kHz clock。

BT_HCIBTUSB_BCM默认随BTUSB启用;UART BCM需单独Kconfig。

3. Realtek

-根据LMP subversion、HCI revision、ROM version和USB product匹配chip info;
-加载firmware和config;
-解析project ID;
-下载fragment;
-设置quirks。

USB RTL默认选中;UART RTL依赖serdev/TTY port和H5。

4. Intel

-识别legacy/new generation;
-读取secure boot参数;
-加载.sfi/.ddc
-firmware download event状态机;
-boot到operational firmware;
-telemetry/coredump。

BTUSB无条件select BT_INTEL,但不表示机器存在Intel设备。

5. QCA

-Rome/Cherokee/WCN等版本;
-加载 rampatch和NVM;
-UART IBS;
-regulator/clock;
-baud切换;
-soc type quirks。

USB QCA也可能由btusb路径处理。

6. MediaTek

btmtk共同helper服务 btusb(需相应选项)、btmtksdiobtmtkuart,处理WMT命令、firmware和reset。

7. Address

设备地址可能来自controller OTP、firmware config、NVMEM或用户态。缺失时HCI Core可能标记 invalid address;不要在多个产品烧录同一静态地址。

8. Firmware命名

文件名常由运行时版本构造。应从 request_firmware错误日志确认准确路径,不根据营销芯片名猜测。

9. RK3588板卡

wifi_chip_type = "ap6275p"等平台字符串提示某些板型采用Broadcom/Infineon combo,但实际蓝牙transport和初始化还取决于UART/rfkill及用户态attach配置;不能仅凭该Wi-Fi字符串断言具体BT芯片revision。

SDIO、Marvell、MediaTek 与其它 Transport

SDIO、Marvell、MediaTek 与其它 Transport

1. Generic SDIO

btsdio.c匹配Bluetooth SDIO class:

-enable function;
-claim IRQ;
-读写packet registers;
-创建hci_dev;
-host sleep/wake;
-异常时reset/recovery。

2. Marvell

btmrvl_main.c提供共同HCI逻辑、command和event处理;btmrvl_sdio.c实现:

-firmware helper/request;
-SDIO card ID;
-host interrupt;
-TX aggregation/queue;
-power-save;
-device dump。

WANT_DEV_COREDUMP用于故障现场。

3. MediaTek SDIO

btmtksdio.c管理SDIO IRQ、ring/queue、WMT初始化、firmware和reset。必须与同module的Wi-Fi共存/电源时序匹配。

4. MediaTek UART

btmtkuart.c是独立serdev driver,使用 btmtk helper,不属于通用 hci_uart.o protocol列表。

5. Qualcomm SMD

btqcomsmd.c通过RPMSG/SMD channel与WCNSS通信,不走外部UART/USB,主要用于Qualcomm平台,不适用于RK3588常规板载模块。

6. RSI

btrsi.c把HCI流量交给RSI 91x coexistence模块调度。

7. Virtio

virtio_bt.c向guest暴露虚拟HCI device,virtqueue承载TX/RX。它与VFIO直通物理Bluetooth不是同一机制。

8. VHCI

hci_vhci.c创建 /dev/vhci,用户态可注入/接收HCI包,用于仿真和测试。Defconfig将其内建。

9. Legacy

PCMCIA、BlueFRITZ、BCM203x等文件为旧硬件支持;源码存在不代表RK3588产品使用。

10. RK3588

本 defconfig启用Marvell SDIO,但多数板载combo的Bluetooth仍可能走UART,Wi-Fi才走SDIO/PCIe。总线必须以原理图、DTS和运行枚举为准。

固件下载、初始化与错误恢复

固件下载、初始化与错误恢复

1. 阶段

1
2
3
4
5
6
7
8
transport probe
-> HCI open
-> identify boot ROM/version
-> request_firmware
-> download patch/config
-> controller reset/boot
-> read capabilities
-> mark HCI up

不同驱动可能在 setupconfigure或独立loader device完成。

2. Firmware API

request_firmware()/lib/firmware或配置的loader路径取blob。错误需区分:

  • -ENOENT文件缺失;
    -格式/版本不匹配;
    -传输timeout;
    -controller拒绝fragment;
    -下载后未boot;
    -USB重新枚举。

3. 命令同步

Helper常用同步HCI command等待Command Complete/Status或vendor event。不能从持有会被RX completion获取的锁时等待。

4. UART下载

固件可能要求初始baud、下载baud和operational baud。Host/controller切速边界需严格同步。

5. USB loader

ATH3K等可能先以firmware loader PID出现,下载后disconnect并以普通HCI PID重新枚举。这不是异常掉线。

6. Recovery

恢复层级:

1.清queue/parser;
2.HCI Reset;
3.transport close/open;
4.vendor reset;
5.GPIO/USB function power cycle;
6.combo shared power cycle。

最后一级可能同时重置Wi-Fi,应由平台层协调。

7. Coredump

Intel/Marvell等支持devcoredump/vendor dump。Dump可能包含地址、连接元数据和firmware状态,访问需受控。

8. 固件安全

并非所有controller都验证签名。量产应:

-固件来自受控只读镜像;
-校验包版本/hash;
-限制更新接口;
-跟踪芯片revision;
-不要把未知vendor blob作为普通可写配置;
-升级保留回滚方案。

9. 版本诊断

1
2
3
btmgmt info
hciconfig -a
dmesg | grep -Ei 'Bluetooth|firmware|patch|ROM|version'

日志中的requested filename和revision比模块商品名更可靠。