news 2026/9/4 6:46:13

树莓派ROS2串口控制STM32小车:从/cmd_vel到电机驱动

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
树莓派ROS2串口控制STM32小车:从/cmd_vel到电机驱动

搞过轮式小车项目的朋友应该都有体会:真正费时间的往往不是电机驱动,也不是某个传感器,而是“树莓派里的 ROS2 话题数据,怎么可靠地送到 STM32 去控制电机”。ROS2 侧/cmd_vel用起来很方便,但串口另一头的单片机就像一个黑盒,帧格式、校验、粘包半包、电机方向、超时停车,每个环节都可能埋坑。

这篇文章围绕“树莓派运行 ROS2、通过串口控制 STM32 小车”这套完整链路展开,从系统架构讲到通信协议,再到两端代码和联调排错。内容偏向一套可以直接照做的闭环教程,适合 ROS2 初学者、STM32 玩家,以及正在做竞赛小车或课程设计轮式机器人的人。

1. 项目背景与整体架构

1.1 为什么是树莓派 + ROS2 + STM32

很多入门小车方案是“STM32 直接控制电机”,代码简单但缺少上层调度能力;也见过“树莓派直接接电机驱动板”,接线省事但受 Linux 实时性和 GPIO 数量的限制。把两者组合起来,是比较主流且适合做扩展的架构:

  • 树莓派负责“大脑”工作:运行 ROS2、接收手柄/键盘指令、跑导航算法、处理摄像头、做路径规划。
  • STM32 负责“小脑”工作:接收串口指令、产生 PWM、控制电机方向、读取编码器、做底层速度闭环。
  • 两者通过串口(UART)通信。ROS2 话题里的速度消息经过协议打包后发送给 STM32,STM32 解析后驱动电机。

这个组合的优点是分工明确。Linux 生态丰富,开发效率高;STM32 实时性好,适合直接操作定时器、GPIO 和中断。后续你想加激光雷达、IMU、摄像头,都只需要在树莓派这端扩展 ROS2 功能包,底盘端不必反复改。

1.2 系统模块与数据流

整个小车系统的数据流可以概括为:

键盘/手柄/Rviz2 ↓ /cmd_vel 话题 (geometry_msgs/Twist) ↓ 树莓派 ROS2 串口桥接节点 serial_bridge ↓ 串口协议帧 USB 转 TTL / 板载 UART ↓ STM32 串口接收解析 ↓ 运动学解算:v、w 换算左右轮速度 ↓ TIM 定时器 PWM 输出 + GPIO 方向控制 ↓ 电机驱动模块(TB6612 / L298N) ↓ 直流减速电机带动轮子

从 ROS2 的角度看,最核心的是如何将/cmd_vel话题中的Twist消息,转换成一段适合串口传输的二进制协议。STM32 端不需要关注 ROS2 是什么,只需要能正确解析协议帧并执行控制。

1.3 本文覆盖的内容

本文不会把 ROS2 和 STM32 的所有细节从头讲一遍,而是把两端的衔接点讲透。读完你可以掌握:

  • 树莓派上 ROS2 环境与工作空间的搭建思路。
  • /cmd_vel话题的含义和使用方式。
  • 一套适合 STM32 解析的串口帧协议设计。
  • 树莓派端桥接节点的 Python 实现。
  • STM32 端串口接收状态机、PWM 输出的核心逻辑。
  • 两端联调时需要使用的命令和排查手段。

2. 环境准备与版本规划

2.1 树莓派系统与 ROS2 版本

树莓派 4B 和树莓派 5 都可以运行 ROS2。本文以 Ubuntu 22.04 64 位系统加 ROS2 Humble 为例,这也是目前资料最丰富、遇到问题最容易搜到解决方案的组合。

如果你手里是树莓派 5,同样可以安装 Ubuntu 22.04 或 Ubuntu 24.04。需要留意的是:ROS2 版本和 Ubuntu 版本有对应关系,不能随便混用。

Ubuntu 版本对应 ROS2 LTS 版本
Ubuntu 22.04ROS2 Humble
Ubuntu 24.04ROS2 Jazzy

版本差异会影响 apt 安装包名和部分 API,但本文的核心思路是通用的。无论你装的是 Humble 还是 Jazzy,cmd_vel、串口协议、STM32 控制逻辑基本一致。

ROS2 的安装建议直接参考官方二进制安装流程。安装完成后,在终端中执行:

apt-cache search ros-humble | head -n 5

如果能搜索到大量 ROS2 功能包,说明源已经配置好了。然后安装桌面版:

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

对于纯命令行调试的树莓派,也可以安装ros-humble-ros-base,之后按需装rviz2等可视化工具。

安装完成后,将 ROS2 环境写入 shell 配置:

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

如果下载速度不理想,可以先把 Ubuntu 的 apt 源更换为速度较好的镜像源,再去安装 ROS2。务必注意:镜像源要与系统版本严格匹配,不要混用不同版本的源。

2.2 STM32 开发环境

STM32 端推荐使用 STM32CubeMX 生成初始化工程,再配合 Keil MDK 或 STM32CubeIDE 编写业务代码。无论你用哪一套 IDE,核心思路都相同:

  • 串口 USART1:用于接收树莓派指令。
  • 定时器 TIM2 / TIM3:输出 PWM,分别控制左右电机。
  • GPIO:控制电机驱动模块的方向引脚。
  • 可选编码器接口:后续做速度闭环时使用。

MCU 型号以 STM32F103 系列为例。如果你用的是 STM32F407、STM32G4 等型号,底层库函数会有差异,但串口解析状态机和 PWM 控制思路完全一致。

2.3 通信硬件准备

树莓派与 STM32 之间最常见、最稳定的连接方式是 USB 转 TTL 串口模块:

树莓派 USB 口 ↓ USB 转 TTL 模块 ↓ TXD RXD GND STM32 的 USART1

注意交叉连接:USB 转 TTL 的 TXD 接 STM32 的 RX,RXD 接 STM32 的 TX,GND 必须共地。如果使用 STM32 板载的 USB 转串口芯片,树莓派端看到的设备名通常是/dev/ttyACM0;如果使用独立 CH340/CP2102 模块,则通常是/dev/ttyUSB0

也可以直接连接树莓派的 GPIO UART 引脚,但需要先关闭系统串口控制台功能,还要注意电平匹配问题。新手阶段建议优先使用 USB 转 TTL,既省事又便于排查。

3. ROS2 话题与通信协议设计

3.1 /cmd_vel 话题说明

在 ROS2 机器人系统中,速度控制指令一般通过/cmd_vel话题发布,消息类型是geometry_msgs/msg/Twist。它包含两部分:

  • linear:线速度,单位 m/s。轮式小车通常只使用linear.x
  • angular:角速度,单位 rad/s。差速小车通常只使用angular.z

下面命令可以手动发布一个“直行 0.2 m/s”的指令:

ros2 topic pub -r 10 /cmd_vel geometry_msgs/msg/Twist \ "{linear: {x: 0.2, y: 0.0, z: 0.0}, angular: {x: 0.0, y: 0.0, z: 0.0}}"

按下Ctrl + C停止发布后,小车并不会自动刹车。因此实际控制中必须做“超时停车”处理,否则树莓派端程序异常退出后,STM32 还在执行最后一条速度指令,这是非常危险的行为。

3.2 串口通信帧格式设计

ROS2 的Twist消息是 float 类型,而单片机解析浮点并不方便,而且不同平台浮点存储格式也存在隐患。因此上位机先将速度转换成整数,再通过二进制帧发送。

这里给出一种简单可靠的帧格式:

| 0xAA | 0x55 | cmd | data_len | data ... | checksum |

字段说明:

字段长度说明
0xAA1 字节帧头 1
0x551 字节帧头 2
cmd1 字节命令字,例如 0x01 表示运动控制
data_len1 字节数据段长度 N
dataN 字节数据内容
checksum1 字节校验和,等于前面所有字节累加后取低 8 位

以运动控制命令为例:

0xAA 0x55 0x01 0x04 [VX_L] [VX_H] [WZ_L] [WZ_H] CHECKSUM

其中线速度VX的单位是 mm/s,角速度WZ的单位是 0.01 rad/s,都使用 int16 小端字节序。为什么要放大?因为 ROS2 中linear.x可能是 0.23 这样的浮点数,串口传输整数更高效、更容易调试,所以在树莓派端放大 1000 或 100,STM32 端再缩小还原。

校验和计算示例:

假设发送linear.x = 0.2 m/sangular.z = 0 rad/s

  • VX = 200 mm/s,十六进制为0xC8 0x00
  • WZ = 0,十六进制为0x00 0x00

此时 CHECKSUM =(0xAA + 0x55 + 0x01 + 0x04 + 0xC8 + 0x00 + 0x00 + 0x00) & 0xFF

这套协议的好处是结构简单,可以在命令字上继续扩展,比如增加舵机控制命令、编码器回传命令、蜂鸣器命令等。

3.3 从 Twist 到左右轮速度

差速小车底盘有两个主动轮,ROS2 下发的是车体中心线速度 v 和角速度 w,需要先换算成左右轮速度:

v_left = v - w * wheel_base / 2 v_right = v + w * wheel_base / 2

其中wheel_base是两个驱动轮之间的轮距。做这一步换算可以在 STM32 端,也可以放在树莓派端。放在 STM32 端的优势是,STM32 直接控制电机,后续加入编码器闭环时不依赖上层。本文示例采用在 STM32 端做运动学换算的方案。

3.4 稳定通信的三个原则

第一,控制周期稳定。ROS2 端不需要每次收到话题都立刻发串口帧,而是用一个固定周期定时器发送最新速度值,例如 50ms 发送一次,防止话题发布频率忽高忽低。

第二,两端都要做超时保护。STM32 如果超过 500ms 没有收到新指令,就立即停车。这是最后一道安全防线。

第三,程序调试阶段要分级。先把 PC 串口助手跑通,再对接树莓派;先把固定速度指令跑通,再接键盘遥控。

4. 树莓派 ROS2 节点实现

4.1 创建 ROS2 工作空间与功能包

先创建 ROS2 工作空间:

mkdir -p ~/ros2_ws/src cd ~/ros2_ws/src ros2 pkg create car_bringup --build-type ament_python

不过ros2 pkg create自动生成的文件不够完整,需要手动补充。建议结构如下:

~/ros2_ws/src/car_bringup/ ├── package.xml ├── setup.py ├── setup.cfg ├── launch/ │ └── car_launch.py ├── resource/ │ └── car_bringup └── car_bringup/ ├── __init__.py └── serial_bridge.py

setup.py中声明节点入口:

entry_points={ 'console_scripts': [ 'serial_bridge = car_bringup.serial_bridge:main', ], },

package.xml中添加运行依赖:

<exec_depend>rclpy</exec_depend> <exec_depend>geometry_msgs</exec_depend> <exec_depend>python3-serial</exec_depend>

还需要安装 PySerial 库:

sudo apt install python3-serial -y

如果使用虚拟环境,也可以使用pip install pyserial

4.2 串口桥接节点完整代码

文件路径:~/ros2_ws/src/car_bringup/car_bringup/serial_bridge.py

#!/usr/bin/env python3 import rclpy from rclpy.node import Node from geometry_msgs.msg import Twist from rclpy.qos import QoSProfile, ReliabilityPolicy import serial import struct import math class SerialBridgeNode(Node): def __init__(self): super().__init__('serial_bridge') # 参数可以通过 launch 文件或命令行覆盖 self.declare_parameter('port', '/dev/ttyUSB0') self.declare_parameter('baudrate', 115200) self.declare_parameter('send_hz', 20.0) self.declare_parameter('stop_timeout', 0.5) port = self.get_parameter('port').value baudrate = self.get_parameter('baudrate').value send_hz = self.get_parameter('send_hz').value self.stop_timeout = self.get_parameter('stop_timeout').value # 打开串口 try: self.ser = serial.Serial(port, baudrate, timeout=0.05) self.get_logger().info( f'串口已打开: {port} @ {baudrate}' ) except Exception as e: self.get_logger().fatal(f'无法打开串口 {port}: {e}') raise SystemExit(1) # 订阅/cmd_vel qos = QoSProfile( depth=10, reliability=ReliabilityPolicy.BEST_EFFORT ) self.subscription = self.create_subscription( Twist, '/cmd_vel', self.cmd_vel_callback, qos ) # 保存最新速度指令 self.current_vx = 0.0 self.current_wz = 0.0 self.last_cmd_time = self.get_clock().now() # 固定周期发送 self.send_period =
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/4 6:45:42

JSP课程设计实战复盘:从JavaBean到MVC的Web开发演进

简介&#xff1a;这是一份面向高校Java Web初学者的JSP课程设计实践资源&#xff0c;聚焦基础动态网页开发能力训练&#xff0c;适用于JSPJavaBeanAccess技术栈入门学习与课程作业参考。资源以简易留言本为核心功能&#xff0c;完整实现用户留言、UBB代码解析&#xff08;含正则…

作者头像 李华
网站建设 2026/9/4 6:44:43

工业级强化学习算法骨架:PyTorch+Gym生产就绪实现

简介&#xff1a;本资源是一套面向人工智能与深度学习初学者及进阶实践者的PyTorch强化学习代码库&#xff0c;聚焦Gym环境下的主流算法工程实现&#xff0c;解决理论理解与代码落地脱节的问题。压缩包共28个文件&#xff0c;含23个核心Python源码&#xff08;如PPO、DQN、SAC等…

作者头像 李华
网站建设 2026/9/4 6:44:14

ZBrush数字雕刻软件:从核心概念到实战安装与入门指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/4 6:43:57

CMake进阶实战与工程化

本篇是 CMake 系列的收尾篇&#xff0c;在前两篇基础上深入语法细节、拆解真实工业级项目写法、提供可落地的工程规范与问题解决方案&#xff0c;最终形成从入门到企业级应用的完整知识体系。1&#xff1a;CMake核心语法前两篇用到了基础语法&#xff0c;本节系统梳理所有高频语…

作者头像 李华
网站建设 2026/9/4 6:43:32

Python驱动Django漏洞挖掘:从黑盒探测到白盒审计的实战指南

简介&#xff1a;本资源是一套基于Python与Django框架实现的Web漏洞挖掘扫描系统&#xff0c;面向信息安全初学者、毕业设计学生及Web安全实践者&#xff0c;聚焦于自动化识别SQL注入等常见Web漏洞&#xff0c;并支持高中低风险分级可视化报告输出。压缩包共461个文件&#xff…

作者头像 李华
网站建设 2026/9/4 6:43:20

Moreau–Yosida正则化与Langevin采样:稀疏后验的活跃轨迹复杂度分析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华