ROS教程15:robot_localization——从轮式里程计与 IMU 到机器人状态估计
ROS教程15:robot_localization——从轮式里程计与 IMU 到机器人状态估计 摘要:承接差速底盘、TF 与 URDF/Xacro,使用 robot_localization 将原始轮式里程计和 IMU 接入 EKF,理解状态、协方差、时间、frame 与 TF ownership。 @[toc]第 12~14 章已经分别建立了三层基础: 12345678第 12 章:Driver 数据契约/cmd_vel -> wheel -> /odom、/imu/data_raw、/joint_states第 13 章:TF / tf2map -> odom -> base_link -> sensor frames第 14 章:URDF / Xacrobase_link -> wheel / laser / imu / camera 到这里,一个新的问题出现了: 12Driver 已经能发布 /odomIMU 也已经能发布 /imu/data_raw 为什么还需要 robot_localization? 因为 /o...
ROS教程14:URDF Xacro robot_state_publisher joint_states从机器人模型到自动生成整机 TF Tree
ROS教程14:URDF / Xacro / robot_state_publisher / joint_states——从机器人模型到自动生成整机 TF Tree 摘要:承接第 13 章 TF,以 AGV 为例读懂 URDF,并用 Xacro 完成参数化、宏复用与条件展开,再串联 robot_description、joint_states 和 robot_state_publisher 生成整机 TF。 @[toc]第 13 章已经把 TF 的核心机制跑通: 1234map -> odom -> base_link -> sensor frames 当时为了把注意力集中在 tf2 本身,base_link -> laser_link、base_link -> imu_link 使用了手写 static_transform_publisher: 12base_link -> laser_linkbase_link -> imu_link 这种方法适合验证一两条边,但真实机器人通常不止两个 fra...
ROS教程13:TF tf2 与移动机器人坐标系——从坐标变换原理到 BufferCore、时间缓存与 map→odom→base_link→sensor
ROS教程13:TF / tf2 与移动机器人坐标系——从坐标变换原理到 BufferCore、时间缓存与 map→odom→base_link→sensor 摘要:从第 12 章的 Odometry 与 Imu 出发,解释 odom 含义、刚体变换数学、tf2 内部缓存与查询算法,并用实验建立移动机器人 TF tree。 @[toc]第 12 章已经把差速底盘的数据整理成 ROS 能理解的标准消息: 123456789/cmd_vel -> Twist -> 左右轮目标速度底层 ChassisData -> JointState -> Imu -> Odometry -> DiagnosticArray 其中已经出现过几个很重要的字符串: 123odombase_linkimu_link 例如 /odom 中写了: 12header.frame_id = odomchild_frame_id = base_link /imu/data_raw 中写了: 1header.frame_...
ROS教程12:ROS消息与驱动数据契约——用差速底盘理解 Twist、JointState、Imu 与 Odometry
ROS教程12:ROS消息与驱动数据契约——用差速底盘理解 Twist、JointState、Imu 与 Odometry 摘要:以差速底盘为例,从 Twist 的 v/w、左右轮运动学和真实误差来源,讲到 Odometry 与 6×6 covariance 的含义、测量和工程使用。 @[toc]阶段 A 已经完成 Topic、Service、Action、CallbackQueue、Spinner、测试和整机启动链。本章开始进入驱动数据,但刻意不展开 TCP、CAN、串口、自定义设备协议,也不讨论如何从 Linux Socket 收字节。 本章只做两件事: 123456789101112131415方向 1:设备数据 -> ROS已经取得的底盘数据 -> JointState -> Imu -> Odometry -> DiagnosticArray方向 2:ROS -> 底盘/cmd_vel -> geometry_msgs/Twist -> 差速底盘逆运动学 ->...
ROS教程11
ROS教程11.5:roscore源码阅读——从启动脚本到 Master 注册表与控制面 摘要:沿 Noetic 源码解释 roscore、roslaunch、rosmaster、Master 注册表与 Python/C++ 边界,并用 bringup、READY Service 和 Docker Compose 落地整机启动。 @[toc]阶段 06~11 已经从 roscpp、Topic、TCPROS、CallbackQueue、Service 与 Action 的客户端/服务端路径建立了 ROS1 通信模型。本章作为阶段 A 的收束篇,换到 Master 服务端视角,把前面多次出现但尚未完整拆开的 roscore、roslaunch、rosmaster 和 Master 注册表串起来。 本章不重复第 07 章已经完成的 roscpp registerPublisher() / registerSubscriber() 客户端调用,也不重复第 08 章 TCPROS socket 数据面,更不重新展开第 11 章 actionlib 状态...
ROS教程11:从 Service 到 Action——同步请求响应、长任务、Feedback、Cancel 与 actionlib 状态机
ROS教程11:从 Service 到 Action——同步请求响应、长任务、Feedback、Cancel 与 actionlib 状态机 摘要:沿 roscpp Service 与 actionlib 源码主线,解释请求响应、CallbackQueue、Action 五个 Topic、Feedback、Result、Cancel 与 Preempt 的真实执行边界。 @[toc]第 10 章已经把 Topic Node 的运行机制转成了可重复验证证据:纯 C++ 逻辑使用 gtest,ROS Node 组合行为使用 rostest,rqt 用于观察,rosbag 用于固定输入。到这里,Topic 的控制面、TCPROS 数据面、CallbackQueue、Spinner 和测试闭环已经串起来。 阶段 11 不进入 CAN/UART Driver,而是先补齐 ROS1 中另外两种常用通信语义:Service 与 Action。 123456789101112Topic -> 持续数据流 -> Publisher 不等待某个 Subscri...
ROS教程10:从 gtest 到 rostest——Unit Test、Node 集成测试、rosbag 与 rqt 验证闭环
ROS教程10:从 gtest 到 rostest——Unit Test、Node 集成测试、rosbag 与 rqt 验证闭环 摘要:把阶段 06~09 的运行机制转成可重复验证证据:拆分纯 C++ 逻辑与 ROS 包装层,用 gtest、rostest、rqt 和 rosbag 建立最小回归闭环。 @[toc] 第 09 章已经把 callback 执行链追到了用户函数:消息进入 SubscriptionQueue、再进入 CallbackQueue,最终由 Spinner worker 调用用户 callback。到这里,我们已经能解释“程序为什么这样运行”,但还没有回答另一个工程问题: 当代码修改以后,怎样证明原有行为没有被破坏;当 Node 看起来能运行时,又怎样把“我看到了正常日志”升级成可重复、可自动执行的测试证据? 第 10 章不把 gtest、rostest、rqt、rosbag 分别写成四份工具说明,而是围绕一条测试主线展开: 123456789101112131415纯 C++ 逻辑 ↓gtest ↓ROS Node 包装层 ↓...
ROS教程09:从 CallbackQueue 追到 Spinner——Callback 执行、阻塞与 roscpp 线程模型
ROS教程09:从 CallbackQueue 追到 Spinner——Callback 执行、阻塞与 roscpp 线程模型 摘要:沿 CallbackQueue 与 Spinner 源码主线,结合真实阻塞、并发与自定义队列日志,解释 callback 的执行线程、串行边界、适用场景与驱动线程模型。 @[toc]第 08 章已经把一条 TCPROS 消息追到 Subscriber 侧:网络线程完成收包和组帧以后,消息进入 Subscription::handleMessage(),随后由 SubscriptionQueue 和 CallbackQueue 接管。到那里为止,还没有回答一个对驱动开发非常关键的问题: 消息已经到达当前进程以后,用户 callback 为什么是在现在执行、由哪个线程执行;一个 callback 阻塞以后,又会拖住谁? 第 09 章只围绕这个问题展开。主线从第 08 章的终点继续,不再重复 Master、XML-RPC、TCPROS Header、序列化和 Socket 收发。 本章使用新的实验 package: 12345678910ro...
ROS教程08:从 TransportTCP
ROS教程08:从 TransportTCP::connect() 追到 TCPROS Connection Header、序列化与 Socket 数据传输 摘要:沿 ROS1 Noetic roscpp 的 TCPROS 数据面源码,追踪连接握手、Header 校验、消息序列化、发送队列、Socket 收发与接收侧组帧。 @[toc]第 07 章已经完成 ROS1 Topic 的控制面与发现流程:Publisher/Subscriber 向 Master 注册,Subscriber 通过 registerSubscriber 返回值或 publisherUpdate 得到 Publisher Node API URI,再通过 requestTopic 与 Publisher 协商 TCPROS,最终进入: 123Subscription::pendingConnectionDone() ↓TransportTCP::connect(pub_host, pub_port) 第 08 章从这里继续。为了避免 pendingConnectionDone() ...
ROS教程07:从 ros-start 顺着源码读懂 Master、XML-RPC 与 Topic 注册发现
ROS教程07:从 ros::start() 顺着源码读懂 Master、XML-RPC 与 Topic 注册发现 摘要:沿 ros::start() 的真实调用顺序逐层阅读 Poll、XML-RPC、Topic、Service 与 callback 源码,再回看 Publisher/Subscriber 的注册发现全过程。 @[toc]本章继续使用现有工程: 123ros_ws/src/ros1_hello/src/hello_node.cppros_ws/src/ros1_hello/src/hello_listener.cppros_debug_ws/src/ros_comm 第 06 章已经确认第一条启动链: 1234567ros::init() ↓第一个 ros::NodeHandle ↓NodeHandle::construct() ↓ros::start() 第 07 章直接从 ros::start() 的源码入口继续向下阅读。阅读顺序遵循真实调用关系:先认识 roscpp 在启动阶段创建或注册的 Manager、线程、监听端...








