从 MCU 开发过渡到 ROS2:嵌入式开发者入门教程

面向已经有 MCU / STM32 / ESP32 / 电机控制 / 串口通信 / CAN 通信基础的同学。
本文目标不是让你放弃 MCU,而是帮助你理解:MCU 负责底层实时控制,ROS2 负责机器人系统级调度、通信、感知、建图和导航。


一、为什么 MCU 开发者要学习 ROS2?

如果你已经做过单片机项目,一般会接触这些内容:

  • GPIO 控制
  • PWM 输出
  • ADC 采样
  • 串口通信
  • CAN 通信
  • 编码器测速
  • PID 控制
  • 电机驱动
  • IMU、超声波、雷达等传感器采集

这些能力非常适合做底层控制。

但是当项目变成机器人、小车、机械臂、无人车、巡检车时,只靠 MCU 就会很吃力。

比如一个智能小车项目可能需要:

  • 电机闭环控制
  • 编码器数据读取
  • IMU 姿态数据读取
  • 激光雷达建图
  • 摄像头识别
  • 路径规划
  • 自动导航
  • 多传感器融合
  • 上位机可视化
  • 多模块通信

这时候 MCU 更适合作为“底层控制器”,而 ROS2 更适合作为“机器人系统的大脑”。

可以简单理解为:

MCU:负责实时控制、传感器采集、电机驱动、安全保护
ROS2:负责系统通信、建图导航、路径规划、可视化、模块管理

二、MCU 和 ROS2 的定位区别

1. MCU 更像“神经末梢”

MCU 擅长做实时、稳定、确定性的控制任务。

例如:

  • 电机 PWM 输出
  • 编码器计数
  • PID 控制
  • 传感器原始数据读取
  • 急停逻辑
  • 低层安全保护
  • CAN / UART 通信

MCU 代码通常长这样:

while (1)
{
    read_encoder();
    read_imu();
    pid_control();
    set_motor_pwm();
    delay_ms(10);
}

这种写法非常适合底层实时控制。

2. ROS2 更像“机器人系统框架”

ROS2 不是传统意义上的操作系统,它更像一个机器人软件开发框架。

它擅长做:

  • 多模块通信
  • 传感器数据分发
  • 雷达建图
  • 视觉识别
  • 路径规划
  • 自动导航
  • 机器人状态可视化
  • 多进程协作
  • 多机器通信

ROS2 中的程序通常不是一个大 while 循环,而是拆成多个节点。

例如:

/camera_node        摄像头节点
/lidar_node         雷达节点
/imu_node           IMU 节点
/motor_node         电机控制节点
/nav_node           导航节点
/map_node           建图节点

每个节点只做一件事,然后通过 ROS2 的通信机制互相配合。


三、从 MCU 思维切换到 ROS2 思维

1. 从“一个 main 函数”切换到“多个 Node 节点”

MCU 项目通常只有一个主程序:

main.c

而 ROS2 项目通常由多个节点组成:

robot_ws/
├── src/
│   ├── motor_driver/
│   ├── imu_driver/
│   ├── lidar_driver/
│   ├── navigation/
│   └── robot_bringup/

在 ROS2 中,Node 是最基本的功能单元。

例如:

motor_node      负责电机控制
imu_node        负责 IMU 数据
lidar_node      负责雷达数据
camera_node     负责摄像头数据
nav_node        负责导航

2. 从“函数调用”切换到“消息通信”

MCU 中经常这样写:

speed = get_encoder_speed();
pid_output = pid_calculate(speed, target_speed);
set_motor_pwm(pid_output);

这是直接函数调用。

而 ROS2 中更常见的是消息通信:

编码器节点发布速度数据
        ↓
控制节点订阅速度数据
        ↓
控制节点计算速度命令
        ↓
电机节点接收速度命令
        ↓
MCU 执行电机控制

对应 ROS2 通信关系:

/encoder_node  --->  /wheel_speed
/control_node  --->  /cmd_vel
/motor_node    <---  /cmd_vel

3. 从“串口协议”切换到“Topic 话题”

以前做 MCU,你可能会设计串口协议:

帧头  命令字  数据长度  数据  校验
0xAA  0x01   0x04     speed  checksum

在 ROS2 里,可以把数据封装成消息,然后发布到话题。

常见话题有:

/cmd_vel
/imu/data
/odom
/scan
/camera/image_raw

可以简单理解:

Topic = 数据频道
Message = 频道里传输的数据格式
Node = 发送或接收数据的程序模块

四、MCU 概念和 ROS2 概念对照表

MCU / 嵌入式概念ROS2 对应概念说明
main 函数Node 节点一个 ROS2 程序模块
while 循环spin / timer callbackROS2 的事件循环
UART / CAN 协议Topic / Service / ActionROS2 通信方式
struct 结构体msg 消息类型定义数据格式
函数调用Service 服务请求一次,返回一次
长时间任务Action 动作适合导航、机械臂运动等耗时任务
全局变量配置Parameter 参数运行时配置节点参数
printf 调试ros2 topic echo / rqt / rviz2ROS2 调试和可视化
FreeRTOS TaskNode / Executor多任务调度思想类似
上位机串口助手ROS2 CLI 工具查看节点、话题、消息

五、ROS2 核心概念

1. Node:节点

节点就是 ROS2 里的一个功能模块。

例如:

motor_node      电机节点
imu_node        IMU 节点
camera_node     摄像头节点
lidar_node      雷达节点
control_node    控制节点

一个节点最好只负责一个明确的功能。

比如不要把电机控制、IMU 读取、雷达建图、导航算法全部写在一个节点里。这样会导致代码越来越乱,不利于后期调试和维护。

2. Topic:话题

Topic 是 ROS2 中最常用的通信方式。

特点:

  • 发布者 Publisher 负责发送数据
  • 订阅者 Subscriber 负责接收数据
  • 一个话题可以有多个发布者
  • 一个话题也可以有多个订阅者
  • 适合持续发送的数据

例如:

IMU 节点  --->  /imu/data  --->  控制节点
雷达节点 --->  /scan      --->  建图节点
控制节点 --->  /cmd_vel   --->  底盘节点

常见 Topic:

话题名作用
/cmd_vel机器人速度控制命令
/odom里程计数据
/imu/dataIMU 数据
/scan激光雷达数据
/camera/image_raw摄像头原始图像

3. Service:服务

Service 是“一问一答”的通信方式。

类似你在 MCU 中写一个函数:

reset_encoder();

在 ROS2 中可以设计成服务:

/reset_encoder

当你调用这个服务时,底盘节点执行编码器清零,然后返回结果。

Service 适合:

  • 清零编码器
  • 重启某个模块
  • 查询当前状态
  • 设置一次性参数
  • 触发一次动作

4. Action:动作

Action 适合耗时任务。

例如:

  • 导航到某个目标点
  • 机械臂移动到某个位置
  • 自动充电
  • 执行巡航任务

Action 和 Service 的区别:

类型特点适用场景
Service请求一次,返回一次清零、查询、设置
Action执行中可以反馈进度,也可以取消导航、机械臂运动、复杂任务

5. Parameter:参数

参数类似 MCU 里的配置项。

例如:

wheel_radius: 0.05
max_speed: 1.2
pid_kp: 1.0
pid_ki: 0.1
pid_kd: 0.01
serial_port: /dev/ttyUSB0
baudrate: 115200

以前你可能会把 PID 参数写死在代码里:

float kp = 1.0;
float ki = 0.1;
float kd = 0.01;

在 ROS2 里,建议把参数放到配置文件中。

例如:

motor_node:
  ros__parameters:
    kp: 1.0
    ki: 0.1
    kd: 0.01
    max_speed: 1.2

这样修改参数时,不一定要重新编译代码。


六、推荐学习环境

建议使用以下环境入门:

项目推荐
操作系统Ubuntu 22.04 / Ubuntu 24.04
ROS2 版本Humble / Jazzy
开发语言C++ / Python
构建工具colcon
编辑器VS Code
可视化工具RViz2 / rqt
仿真工具Gazebo
上位机平台树莓派 / Jetson / 工控机 / 香橙派 / x86 小主机
MCUSTM32 / ESP32 / GD32 / Arduino / Teensy

如果你是刚入门,建议:

Ubuntu 22.04 + ROS2 Humble
或者
Ubuntu 24.04 + ROS2 Jazzy

不要一开始就使用太冷门的系统和硬件平台,否则环境问题会浪费很多时间。


七、ROS2 安装后的第一个体验

安装完成后,先执行环境变量:

source /opt/ros/humble/setup.bash

如果你安装的是 ROS2 Jazzy,则执行:

source /opt/ros/jazzy/setup.bash

可以把它写入 .bashrc:

echo "source /opt/ros/humble/setup.bash" >> ~/.bashrc
source ~/.bashrc

如果是 Jazzy:

echo "source /opt/ros/jazzy/setup.bash" >> ~/.bashrc
source ~/.bashrc

测试 ROS2 是否安装成功:

ros2 --help

运行官方发布者示例:

ros2 run demo_nodes_cpp talker

再打开一个终端,运行订阅者:

ros2 run demo_nodes_cpp listener

如果能看到类似下面的信息,说明 ROS2 通信正常:

I heard: [Hello World: 1]
I heard: [Hello World: 2]
I heard: [Hello World: 3]

这个例子就类似 MCU 里的串口发送和接收,只不过这里使用的是 ROS2 的 Topic 通信机制。


八、常用 ROS2 命令

1. 查看当前节点

ros2 node list

输出示例:

/talker
/listener

2. 查看当前话题

ros2 topic list

输出示例:

/chatter
/parameter_events
/rosout

3. 查看话题数据

ros2 topic echo /chatter

4. 查看话题频率

ros2 topic hz /chatter

这个命令可以查看某个话题的数据发布频率。

比如:

ros2 topic hz /imu/data
ros2 topic hz /scan
ros2 topic hz /odom

5. 查看话题信息

ros2 topic info /chatter

6. 查看消息类型

ros2 interface show std_msgs/msg/String

7. 查看服务列表

ros2 service list

8. 查看参数列表

ros2 param list

九、创建自己的 ROS2 工作空间

ROS2 项目一般使用工作空间管理。

创建工作空间:

mkdir -p ~/ros2_ws/src
cd ~/ros2_ws

编译工作空间:

colcon build

加载当前工作空间环境:

source install/setup.bash

建议每次新开终端后都执行:

source /opt/ros/humble/setup.bash
source ~/ros2_ws/install/setup.bash

如果是 Jazzy:

source /opt/ros/jazzy/setup.bash
source ~/ros2_ws/install/setup.bash

十、创建第一个 C++ 节点

对于 MCU 开发者,建议优先学习 C++ 版本,因为你已经有 C / 嵌入式基础。

创建功能包:

cd ~/ros2_ws/src
ros2 pkg create my_robot_cpp --build-type ament_cmake --dependencies rclcpp std_msgs

目录结构大致如下:

my_robot_cpp/
├── CMakeLists.txt
├── package.xml
├── include/
├── src/
└── ...

十一、编写一个发布者节点

创建文件:

cd ~/ros2_ws/src/my_robot_cpp/src
touch simple_publisher.cpp

写入代码:

#include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/string.hpp"

class SimplePublisher : public rclcpp::Node
{
public:
    SimplePublisher() : Node("simple_publisher")
    {
        publisher_ = this->create_publisher<std_msgs::msg::String>("mcu_data", 10);

        timer_ = this->create_wall_timer(
            std::chrono::milliseconds(500),
            std::bind(&SimplePublisher::timer_callback, this));
    }

private:
    void timer_callback()
    {
        auto msg = std_msgs::msg::String();
        msg.data = "Hello from MCU developer";

        publisher_->publish(msg);

        RCLCPP_INFO(this->get_logger(), "Publish: %s", msg.data.c_str());
    }

    rclcpp::Publisher<std_msgs::msg::String>::SharedPtr publisher_;
    rclcpp::TimerBase::SharedPtr timer_;
};

int main(int argc, char *argv[])
{
    rclcpp::init(argc, argv);
    rclcpp::spin(std::make_shared<SimplePublisher>());
    rclcpp::shutdown();
    return 0;
}

这个程序做的事情很简单:

每 500ms 发布一次字符串消息
话题名称为 /mcu_data
消息类型为 std_msgs/msg/String

你可以把它理解为:

MCU 每 500ms 通过串口发送一次数据

只不过在 ROS2 中,数据不是发到串口,而是发布到 Topic。


十二、修改 CMakeLists.txt

打开 CMakeLists.txt,加入:

add_executable(simple_publisher src/simple_publisher.cpp)
ament_target_dependencies(simple_publisher rclcpp std_msgs)

install(TARGETS
  simple_publisher
  DESTINATION lib/${PROJECT_NAME}
)

然后编译:

cd ~/ros2_ws
colcon build
source install/setup.bash

运行节点:

ros2 run my_robot_cpp simple_publisher

另开一个终端查看话题:

ros2 topic echo /mcu_data

十三、编写一个订阅者节点

创建文件:

cd ~/ros2_ws/src/my_robot_cpp/src
touch simple_subscriber.cpp

写入代码:

#include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/string.hpp"

class SimpleSubscriber : public rclcpp::Node
{
public:
    SimpleSubscriber() : Node("simple_subscriber")
    {
        subscriber_ = this->create_subscription<std_msgs::msg::String>(
            "mcu_data",
            10,
            std::bind(&SimpleSubscriber::topic_callback, this, std::placeholders::_1));
    }

private:
    void topic_callback(const std_msgs::msg::String::SharedPtr msg)
    {
        RCLCPP_INFO(this->get_logger(), "Receive: %s", msg->data.c_str());
    }

    rclcpp::Subscription<std_msgs::msg::String>::SharedPtr subscriber_;
};

int main(int argc, char *argv[])
{
    rclcpp::init(argc, argv);
    rclcpp::spin(std::make_shared<SimpleSubscriber>());
    rclcpp::shutdown();
    return 0;
}

继续修改 CMakeLists.txt:

add_executable(simple_subscriber src/simple_subscriber.cpp)
ament_target_dependencies(simple_subscriber rclcpp std_msgs)

install(TARGETS
  simple_publisher
  simple_subscriber
  DESTINATION lib/${PROJECT_NAME}
)

重新编译:

cd ~/ros2_ws
colcon build
source install/setup.bash

运行发布者:

ros2 run my_robot_cpp simple_publisher

另开终端运行订阅者:

ros2 run my_robot_cpp simple_subscriber

这就是 ROS2 中最基础的发布订阅模型。


十四、用 ROS2 理解一个真实机器人系统

假设你做一台 ROS2 智能小车。

1. MCU 负责底层

MCU 负责:

1. 读取编码器
2. 读取 IMU
3. 运行电机 PID
4. 输出 PWM
5. 通过串口或 CAN 和上位机通信
6. 处理急停和保护逻辑

MCU 内部仍然可以这样写:

while (1)
{
    read_encoder();
    read_imu();
    parse_cmd_vel();
    pid_control();
    set_motor_pwm();
    upload_status();
}

2. ROS2 上位机负责系统功能

ROS2 负责:

1. 接收雷达数据
2. 接收摄像头数据
3. 运行 SLAM 建图
4. 运行导航算法
5. 发布速度命令 /cmd_vel
6. 接收 MCU 上传的里程计 /odom
7. 在 RViz2 中显示机器人状态

3. 整体架构

                  ┌──────────────────────┐
                  │      ROS2 上位机       │
                  │ Jetson / 树莓派 / 工控机 │
                  └──────────┬───────────┘
                             │
              ┌──────────────┼──────────────┐
              │              │              │
        /scan 雷达       /camera 图像      /cmd_vel 速度命令
              │              │              │
              ▼              ▼              ▼
        SLAM 建图节点    视觉识别节点     底盘控制节点
                                             │
                                             │ UART / CAN
                                             ▼
                                  ┌─────────────────┐
                                  │       MCU        │
                                  │ STM32 / ESP32    │
                                  └────────┬────────┘
                                           │
                              ┌────────────┼────────────┐
                              ▼            ▼            ▼
                            电机         编码器        IMU

十五、ROS2 和 MCU 通信方式

1. 串口通信

串口是最简单、最常用的方式。

适合:

  • 小车底盘
  • 简单机械臂
  • 传感器模块
  • 低成本项目

ROS2 上位机通过串口发送:

线速度 vx
角速度 wz

MCU 返回:

左轮速度
右轮速度
编码器值
IMU 数据
电池电压

2. CAN 通信

如果你的项目是:

  • 卡丁车
  • 无人车
  • 多电机控制
  • 工业控制
  • 底盘控制器
  • 多节点实时通信

那么 CAN 会比普通串口更可靠。

典型结构:

ROS2 上位机
    │
USB-CAN / CAN-HAT
    │
CAN 总线
    │
MCU 控制板
    │
电机驱动器

3. micro-ROS

如果你希望 MCU 直接成为 ROS2 网络中的一部分,可以学习 micro-ROS。

普通模式:

ROS2 上位机  <--串口协议-->  MCU

micro-ROS 模式:

ROS2 上位机  <--micro-ROS Agent-->  MCU micro-ROS Client

micro-ROS 可以让 MCU 使用类似 ROS2 的发布订阅机制。

不过初学者不建议一开始就上 micro-ROS,因为它对环境、内存、RTOS、移植能力有一定要求。

推荐学习路线:

先学 ROS2 基础
再学 ROS2 串口通信
再学底盘控制
最后再学 micro-ROS

十六、从 MCU 过渡到 ROS2 的推荐学习路线

阶段一:补 Linux 基础

ROS2 大多数开发环境运行在 Linux 上,所以要先熟悉 Ubuntu。

需要掌握:

cd
ls
mkdir
rm
cp
mv
sudo
apt
chmod
tar
grep
ps
kill

还要理解:

  • 文件路径
  • 环境变量
  • 终端操作
  • 软件包安装
  • 串口权限
  • USB 设备识别

常用命令:

ls /dev/ttyUSB*
ls /dev/ttyACM*
dmesg | grep tty
sudo usermod -aG dialout $USER

阶段二:学习 ROS2 基础命令

必须掌握:

ros2 node list
ros2 topic list
ros2 topic echo
ros2 topic hz
ros2 topic info
ros2 interface show
ros2 service list
ros2 param list
ros2 run
ros2 launch

这些命令就像 MCU 开发里的串口助手、示波器、逻辑分析仪。

阶段三:学习 ROS2 C++ 节点开发

重点学习:

  • 创建 package
  • 编写 Node
  • Publisher 发布话题
  • Subscriber 订阅话题
  • Timer 定时器
  • Parameter 参数
  • Service 服务
  • Launch 启动文件

阶段四:学习串口通信

你要实现:

ROS2 节点打开 /dev/ttyUSB0
发送速度命令给 MCU
接收 MCU 返回的编码器和 IMU 数据
发布成 ROS2 话题

推荐话题设计:

/cmd_vel        ROS2 发给底盘的速度命令
/odom           MCU 返回的里程计数据
/imu/data       MCU 返回的 IMU 数据
/battery        MCU 返回的电池电压

阶段五:学习小车底盘模型

移动机器人常用差速模型:

左轮速度 = v - w * L / 2
右轮速度 = v + w * L / 2

其中:

v = 线速度
w = 角速度
L = 轮距

这部分你作为 MCU 开发者会更容易理解,因为它和电机控制、编码器、PID 强相关。

阶段六:学习 TF 坐标变换

机器人系统里有很多坐标系:

map
odom
base_link
base_footprint
laser
camera_link
imu_link

常见关系:

map  --->  odom  --->  base_link  --->  laser

TF 用来描述这些坐标系之间的位置关系。

如果你要做导航、建图、雷达显示,TF 是必须学的。

阶段七:学习 RViz2 可视化

RViz2 可以显示:

  • 机器人模型
  • 雷达点云
  • 摄像头图像
  • 地图
  • 路径
  • 坐标系
  • 里程计
  • IMU 数据

对 MCU 开发者来说,RViz2 就像一个高级版的“机器人调试上位机”。

阶段八:学习 SLAM 和 Navigation2

当你掌握基础通信后,可以继续学习:

  • slam_toolbox
  • cartographer
  • Nav2
  • AMCL
  • costmap
  • planner
  • controller
  • behavior tree

入门顺序建议:

先让小车能动
再让雷达能显示
再让里程计正常
再配置 TF
再运行 SLAM
最后运行 Nav2 导航

不要一上来就直接搞自动导航,否则很容易被 TF、odom、map、雷达坐标、参数配置搞崩。


十七、一个典型 ROS2 小车项目结构

my_robot_ws/
├── src/
│   ├── my_robot_bringup/
│   │   ├── launch/
│   │   │   └── robot.launch.py
│   │   └── config/
│   │       └── robot.yaml
│   │
│   ├── my_robot_description/
│   │   ├── urdf/
│   │   │   └── robot.urdf.xacro
│   │   └── meshes/
│   │
│   ├── my_robot_base/
│   │   ├── src/
│   │   │   └── base_driver.cpp
│   │   └── include/
│   │
│   ├── my_robot_msgs/
│   │   ├── msg/
│   │   └── srv/
│   │
│   └── my_robot_navigation/
│       ├── config/
│       └── launch/

各包作用:

包名作用
my_robot_bringup总启动入口
my_robot_description机器人模型
my_robot_base底盘驱动
my_robot_msgs自定义消息
my_robot_navigation导航配置

十八、ROS2 底盘驱动节点设计

底盘节点通常需要完成以下功能。

1. 订阅 /cmd_vel

ROS2 导航系统发布速度命令
底盘节点订阅 /cmd_vel
底盘节点把速度转换成左右轮目标速度
通过串口或 CAN 发给 MCU

2. 发布 /odom

MCU 返回编码器数据
底盘节点计算机器人位移
发布 /odom

3. 发布 TF

odom -> base_link

4. 发布 /imu/data

MCU 返回 IMU 数据
底盘节点发布成 sensor_msgs/msg/Imu

5. 参数化配置

例如:

base_driver:
  ros__parameters:
    serial_port: "/dev/ttyUSB0"
    baudrate: 115200
    wheel_radius: 0.05
    wheel_base: 0.30
    publish_tf: true

十九、从 STM32 小车迁移到 ROS2 的示例方案

原始 MCU 小车结构

STM32
├── 电机 PWM
├── 编码器
├── MPU6050
├── OLED
├── 超声波
└── 蓝牙串口

控制方式:

手机蓝牙发送前进、后退、左转、右转
STM32 接收指令后控制电机

升级成 ROS2 小车

升级后结构:

ROS2 上位机
├── 雷达节点
├── 摄像头节点
├── SLAM 节点
├── 导航节点
├── 底盘驱动节点
└── RViz2 可视化

STM32
├── 电机 PID
├── 编码器读取
├── IMU 数据读取
└── 串口通信

通信协议:

ROS2 -> STM32:
目标线速度 vx
目标角速度 wz

STM32 -> ROS2:
左轮速度
右轮速度
IMU 数据
电池电压

二十、MCU 代码和 ROS2 代码如何分工?

1. MCU 不建议做的事情

不建议 MCU 做:

  • 图像识别
  • 雷达建图
  • 全局路径规划
  • 大地图存储
  • 复杂导航
  • 深度学习推理
  • 多传感器大规模融合

这些更适合 ROS2 上位机。

2. MCU 应该做的事情

MCU 应该做:

  • 电机闭环
  • 编码器读取
  • 低层安全保护
  • 实时控制
  • 急停处理
  • 传感器底层驱动
  • 与上位机通信

3. ROS2 应该做的事情

ROS2 应该做:

  • 系统模块组织
  • 上层算法
  • 建图导航
  • 视觉感知
  • 数据可视化
  • 多模块通信
  • 参数管理
  • 日志记录

二十一、推荐实战项目路线

项目 1:ROS2 串口助手节点

目标:

写一个 ROS2 节点
打开串口
接收 MCU 数据
发布到 /mcu_data

你能学到:

  • ROS2 节点
  • 串口通信
  • Topic 发布
  • Linux 串口权限

项目 2:ROS2 控制 LED

目标:

ROS2 发布 /led_cmd
MCU 接收命令
控制 LED 开关

通信示例:

ROS2 -> MCU: LED_ON
ROS2 -> MCU: LED_OFF

这个项目很简单,但非常适合理解 ROS2 和 MCU 的连接方式。

项目 3:ROS2 控制电机

目标:

ROS2 发布速度命令
MCU 控制电机转速
编码器反馈速度
ROS2 显示速度曲线

你能学到:

  • /cmd_vel
  • 电机控制
  • 编码器反馈
  • PID 调参
  • rqt_plot 画速度曲线

项目 4:ROS2 差速小车

目标:

键盘控制小车前进、后退、转向

需要掌握:

  • geometry_msgs/msg/Twist
  • /cmd_vel
  • 差速运动学
  • 左右轮速度换算
  • MCU 电机闭环

项目 5:ROS2 雷达建图

目标:

接入 2D 激光雷达
显示 /scan
运行 SLAM
生成地图

需要掌握:

  • 雷达驱动
  • /scan
  • TF
  • RViz2
  • slam_toolbox

项目 6:ROS2 自动导航

目标:

在 RViz2 中点击目标点
机器人自动规划路径并移动过去

需要掌握:

  • Nav2
  • map
  • odom
  • base_link
  • costmap
  • planner
  • controller
  • recovery behavior

二十二、学习 ROS2 最容易踩的坑

1. 环境变量没有 source

问题:

ros2 run 找不到包

解决:

source /opt/ros/humble/setup.bash
source ~/ros2_ws/install/setup.bash

如果是 Jazzy:

source /opt/ros/jazzy/setup.bash
source ~/ros2_ws/install/setup.bash

2. 编译后没有重新 source

每次 colcon build 后最好执行:

source install/setup.bash

3. package.xml 依赖没写

如果你用了:

std_msgs
geometry_msgs
sensor_msgs
nav_msgs
tf2_ros

就要在 package.xml 和 CMakeLists.txt 中添加依赖。

4. 串口权限不足

问题:

Permission denied: /dev/ttyUSB0

解决:

sudo usermod -aG dialout $USER

然后重启电脑或重新登录。

5. 话题名不一致

例如一个节点发布:

/cmd_vel

另一个节点订阅:

/cmd_vel_test

那它们永远通信不上。

检查命令:

ros2 topic list
ros2 topic info /cmd_vel

6. TF 坐标系错误

常见错误:

No transform from base_link to laser
No transform from odom to base_link

解决思路:

ros2 run tf2_tools view_frames

检查坐标树是否完整。

7. 时间戳错误

很多传感器消息都需要时间戳。

例如:

msg.header.stamp = this->now();

如果时间戳不对,RViz2、SLAM、Nav2 都可能异常。

8. 坐标系命名混乱

建议统一使用:

map
odom
base_link
laser
camera_link
imu_link

不要一会儿写 base,一会儿写 base_link,否则后期非常难排错。


二十三、MCU 开发者学习 ROS2 的优势

你从 MCU 转 ROS2,其实有很大优势。

因为你已经懂:

  • 电机控制
  • 编码器
  • PWM
  • PID
  • 传感器
  • 串口
  • CAN
  • 实时控制
  • 硬件调试
  • 电源和信号问题

很多纯软件出身的人学 ROS2 时,反而不理解机器人底层为什么会抖、为什么会丢数据、为什么轮速不准、为什么电机响应慢。

所以 MCU 开发者学习 ROS2 的核心不是“重新开始”,而是把你的底层能力接到更大的机器人系统里。


二十四、推荐学习顺序总结

第 1 步:熟悉 Ubuntu 基础命令
第 2 步:安装 ROS2 Humble / Jazzy
第 3 步:运行 talker / listener 示例
第 4 步:学习 node、topic、service、action、parameter
第 5 步:用 C++ 写 publisher 和 subscriber
第 6 步:写 ROS2 串口节点
第 7 步:让 ROS2 控制 MCU 的 LED
第 8 步:让 ROS2 控制 MCU 的电机
第 9 步:实现 /cmd_vel 到左右轮速度转换
第 10 步:发布 /odom 和 /imu/data
第 11 步:学习 TF 和 RViz2
第 12 步:接入雷达
第 13 步:学习 SLAM
第 14 步:学习 Nav2 自动导航
第 15 步:进阶学习 micro-ROS

二十五、最终理解

MCU 和 ROS2 不是替代关系,而是配合关系。

可以这样理解:

MCU:负责底层、实时、可靠、安全
ROS2:负责上层、智能、通信、感知、导航

更形象一点:

MCU 是机器人的肌肉和反射神经
ROS2 是机器人的大脑和神经网络

真正的机器人项目通常是:

ROS2 上位机负责决策
MCU 下位机负责执行

当你能把 MCU 的电机控制、传感器读取、串口通信能力和 ROS2 的话题、节点、导航、建图结合起来,你就已经从普通单片机开发进入机器人系统开发了。


结语

如果你已经会 MCU,不要害怕 ROS2。

ROS2 看起来概念很多,但本质上就是:

把机器人系统拆成很多节点
节点之间通过话题、服务、动作通信
底层控制交给 MCU
上层智能交给 ROS2

学习 ROS2 的关键不是一开始就追求高大上的自动驾驶、SLAM、AI 识别,而是先把最基础的通信链路打通:

ROS2 发布命令
MCU 接收命令
MCU 执行控制
MCU 返回状态
ROS2 可视化显示

只要这条链路跑通,后面的雷达、导航、建图、视觉识别都可以一步步加上去。

Logo

聚焦RDK与具身智能,汇集机器人开发教程、开源案例和技术交流

更多推荐