ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

开源扫地机器人全栈拆解:ROS2与STM32的感知-决策-执行架构实战

开源扫地机器人全栈拆解:ROS2与STM32的感知-决策-执行架构实战 1. 一台扫地机为什么值得全栈拆解扫地机器人这个品类很多人第一反应是不就是个会跑的吸尘器。但如果你真正拆过一台会发现它几乎是消费电子里集成度最变态的产品之一移动底盘、传感器融合、路径规划、电机控制、电池管理、无线通信、人机交互一个都不少。更关键的是这些东西不是各自独立工作的它们要在有限的算力和功耗预算下实时协同。这就意味着一台扫地机天然就是一套完整的机器人工程教学平台。我接触过不少做机器人方向的朋友普遍有个困惑学ROS2的时候跑仿真很顺一到真机就抓瞎学STM32的时候点灯、串口、PWM都能搞定但不知道怎么把这些东西串成一个能自主行动的系统。开源扫地机器人恰好填上了这个断层——它把ROS2的上层决策和STM32的底层执行用一条清晰的链路连起来让你能看到从激光雷达出一帧点云到轮子实际转动之间到底发生了什么。这篇文章面向三类人一是想找一个完整机器人项目练手的在校学生二是做嵌入式但想往机器人方向转的工程师三是已经会ROS2但没碰过真机底盘的开发者。我会按照整体架构设计→核心模块拆解→实操落地→问题排查的顺序把一台开源扫地机器人从里到外讲透。涉及ROS2、STM32、传感器、电机驱动、通信协议这些关键词的地方我都会给出具体的参数和操作思路而不是泛泛而谈。先说结论扫地机器人的技术栈可以拆成感知-决策-执行三层ROS2负责感知和决策STM32负责执行和实时反馈两者通过串口或CAN通信。理解了这条主线后面所有细节都是挂在上面的。2. 整体架构设计与方案选型思路2.1 三层架构的划分逻辑一台扫地机器人的软件系统本质上是一个典型的分层控制架构。我把它分成三层来看感知层激光雷达LDS、IMU、碰撞传感器、悬崖传感器、里程计。这一层负责把物理世界的信息转成数字信号。决策层跑在Linux主控比如树莓派、Jetson Nano或RK3588上的ROS2节点负责SLAM建图、路径规划、任务调度。执行层STM32单片机负责电机PID控制、传感器轮询、电池监测、与主控通信。为什么这么分核心原因是实时性要求不同。路径规划可以容忍几十毫秒的延迟但电机控制如果延迟超过几毫秒轮子就会抖动甚至失控。Linux系统因为调度机制的问题做不到硬实时所以必须把实时任务下沉到STM32。这是机器人系统设计里最基本的一条原则该实时的地方用RTOS或裸机该复杂计算的地方用Linux。2.2 主控与MCU的选型考量主控这边常见的选择有几种。树莓派4B性价比高ROS2 Humble跑起来没问题但GPIO和实时性一般Jetson Nano/NX算力强适合跑视觉SLAM但功耗和散热是个问题RK3588是这两年的新宠NPU算力足够接口也丰富。如果只是做基础扫地机功能树莓派4B加一个激光雷达就够用了。STM32这边我建议至少用STM32F103C8T6起步如果要做更复杂的电机控制比如FOC可以上STM32F407或G431。F103的资源对于扫地机来说其实偏紧但胜在便宜、资料多、社区成熟。我实测下来用F103做三轮全向底盘的PID控制配合超声波和红外传感器CPU占用大概在60%左右还有余量。通信方式上串口是最简单可靠的方案。ROS2这边通过ros2_serial或者自己写一个serial_nodeSTM32那边用UART中断收发。如果对实时性要求更高可以用CAN总线但调试复杂度会上升。我建议新手先用串口跑通再考虑升级。2.3 为什么选ROS2而不是ROS1这个问题被问过很多次。ROS1在2025年已经停止维护了ROS2是唯一的选择。但更重要的是ROS2的几个特性对扫地机这种产品特别友好DDS通信去中心化节点之间直接通信不需要roscore。扫地机的主控和MCU之间如果都用ROS2通信会更灵活。实时性支持ROS2支持实时内核和QoS配置可以给电机控制话题设置更高的优先级。生命周期管理节点可以有序启动和关闭这对扫地机的开机自检流程很重要。ROS2的版本选择上Humble是目前的LTS版本支持到2027年社区资源最丰富。Jazzy是最新版但有些包还没跟上。我建议新手直接上Humble等生态成熟了再迁移。3. 核心模块拆解与关键技术点3.1 激光雷达与SLAM建图扫地机最核心的传感器就是激光雷达。常见的低成本方案是RPLIDAR A1或镭神N10价格在几百块扫描半径12米左右精度够用。雷达通过串口或USB转串口接到主控ROS2这边用sllidar_ros2或rplidar_ros2驱动包。SLAM算法上Cartographer和slam_toolbox是两个主流选择。Cartographer建图质量高但配置复杂对算力要求也高slam_toolbox轻量配置简单适合树莓派。我实测在树莓派4B上跑slam_toolbox建一个80平米的房间CPU占用大概40%内存占用500MB左右可以接受。建图过程中有个坑雷达的安装位置会影响建图效果。如果雷达装得太低会被家具腿遮挡装得太高又可能扫到天花板。一般建议雷达安装在离地10-15厘米的位置视野尽量开阔。另外雷达的扫描频率要和机器人的移动速度匹配移动太快会导致点云稀疏建图出现空洞。3.2 STM32底层电机控制电机控制是STM32的核心任务。扫地机一般用直流有刷电机或无刷电机配合编码器做闭环控制。有刷电机驱动简单用DRV8833或TB6612就行无刷电机效率高但需要FOC驱动比如DRV8323。PID控制是绕不开的。我一般用位置式PID做速度环参数整定的顺序是先调P让轮子能响应但不过冲再加I消除稳态误差最后加D抑制振荡。具体参数上P一般在0.5-2.0之间I在0.01-0.1之间D在0-0.5之间。这些值不是固定的要根据电机特性和负载调整。编码器读取上STM32的定时器编码器模式是最方便的方案。把编码器的A、B相接到定时器的CH1和CH2配置成编码器模式硬件自动计数CPU只需要定期读取计数值就行。注意编码器的线数和轮子直径要匹配否则里程计会不准。计算公式是每米脉冲数 编码器线数 × 减速比 / (轮子直径 × π)。3.3 传感器融合与里程计扫地机的里程计一般来自编码器但编码器有累积误差跑久了会漂。所以需要和IMU融合。IMU用MPU6050或ICM20602通过I2C接到STM32读取加速度和角速度做姿态解算。融合算法上互补滤波是最简单的方案角度 α × (角度 角速度 × dt) (1-α) × 加速度计角度。α一般取0.98。如果要更精确可以用卡尔曼滤波或Mahony算法。我建议先用互补滤波跑通再考虑升级。ROS2这边里程计通过odom话题发布格式是nav_msgs/Odometry。STM32把编码器计数和IMU数据打包成自定义协议通过串口发给主控主控解析后发布odom话题。这里要注意时间戳同步STM32的时间戳和主控的时间戳要对齐否则TF变换会出错。3.4 通信协议设计STM32和主控之间的通信协议我建议自定义一个简单的帧格式帧头(2字节) 长度(1字节) 命令(1字节) 数据(N字节) 校验(1字节) 帧尾(2字节)帧头用0xAA 0x55帧尾用0x0D 0x0A校验用异或。命令字段区分是里程计数据、传感器数据还是控制指令。数据字段用小端序浮点数用IEEE754格式。为什么要自定义协议而不是用现成的因为扫地机的数据量不大但实时性要求高自定义协议可以做到最小开销。另外调试的时候可以用串口助手直接看原始数据比解析ROS2消息方便。4. 实操过程与核心环节实现4.1 环境搭建ROS2 Humble安装与配置先说ROS2的安装。Ubuntu 22.04对应Humble安装步骤如下# 设置locale sudo apt update sudo apt install locales sudo locale-gen en_US en_US.UTF-8 sudo update-locale LC_ALLen_US.UTF-8 LANGen_US.UTF-8 # 添加ROS2源 sudo apt install software-properties-common sudo add-apt-repository universe sudo apt update sudo apt install curl -y 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 # 安装ROS2 sudo apt update sudo apt install ros-humble-desktop安装完成后记得source /opt/ros/humble/setup.bash并加到.bashrc里。如果遇到packages.ros.org连接错误检查网络和DNS设置或者换用国内镜像源。STM32开发环境这边我推荐VSCode PlatformIO或者STM32CubeIDE。PlatformIO配置简单跨平台适合快速开发CubeIDE是官方工具调试功能强。如果要用J-Link下载需要在VSCode里配置launch.json和tasks.json指定J-Link的路径和芯片型号。4.2 STM32端电机控制与传感器轮询STM32的主循环我一般这么设计while (1) { // 1. 读取编码器 encoder_left TIM_GetCounter(TIM2); encoder_right TIM_GetCounter(TIM3); // 2. 读取IMU MPU6050_Read_Accel(accel); MPU6050_Read_Gyro(gyro); // 3. 读取超声波和红外 ultrasonic_distance Ultrasonic_Measure(); cliff_status Read_Cliff_Sensors(); // 4. PID计算 pid_left PID_Compute(pid_left_ctx, target_left, encoder_left); pid_right PID_Compute(pid_right_ctx, target_right, encoder_right); // 5. 设置PWM Set_PWM(pid_left, pid_right); // 6. 发送数据到主控 Send_Data_To_Host(); // 7. 接收主控指令 Receive_Command_From_Host(); HAL_Delay(10); // 100Hz循环 }这个循环频率是100Hz对于扫地机来说足够了。如果要做更精细的控制可以提高到200Hz或500Hz但要注意CPU占用。超声波测距用定时器输入捕获模式测量回响信号的高电平时间然后换算成距离。公式是距离 高电平时间 × 声速 / 2。声速取340m/s注意温度补偿。我实测HC-SR04在室内环境下误差在1-2厘米左右够用。4.3 ROS2端节点设计与话题通信ROS2这边的节点设计我建议分成几个独立的节点serial_node负责和STM32通信发布odom和sensor_data话题订阅cmd_vel话题。slam_node跑slam_toolbox订阅scan话题发布map话题。navigation_node跑Nav2订阅map和odom发布cmd_vel。robot_state_publisher发布TF变换。serial_node的核心逻辑是import rclpy from rclpy.node import Node from nav_msgs.msg import Odometry from geometry_msgs.msg import Twist import serial class SerialNode(Node): def __init__(self): super().__init__(serial_node) self.ser serial.Serial(/dev/ttyUSB0, 115200, timeout0.1) self.odom_pub self.create_publisher(Odometry, odom, 10) self.cmd_sub self.create_subscription(Twist, cmd_vel, self.cmd_callback, 10) self.timer self.create_timer(0.01, self.read_serial) def read_serial(self): if self.ser.in_waiting 0: data self.ser.read(self.ser.in_waiting) # 解析数据发布odom odom Odometry() # ... 填充odom self.odom_pub.publish(odom) def cmd_callback(self, msg): # 打包cmd_vel发送到STM32 packet self.pack_cmd(msg.linear.x, msg.angular.z) self.ser.write(packet)这里有个细节串口的读写要放在不同的线程或定时器里否则会阻塞。我一般用create_timer做读用回调做写。4.4 建图与导航的完整流程建图流程是这样的启动雷达驱动ros2 launch sllidar_ros2 sllidar_launch.py启动serial_noderos2 run my_robot serial_node启动slam_toolboxros2 launch slam_toolbox online_async_launch.py启动RViz2rviz2添加LaserScan和Map显示手动控制机器人走一圈ros2 run teleop_twist_keyboard teleop_twist_keyboard保存地图ros2 run nav2_map_server map_saver_cli -f my_map导航流程加载地图ros2 run nav2_map_server map_server --ros-args -p yaml_filename:my_map.yaml启动Nav2ros2 launch nav2_bringup navigation_launch.py在RViz2里设置初始位置和目标点机器人自动规划路径并移动这里有个坑Nav2的代价地图参数需要根据机器人尺寸调整。robot_radius要设成机器人的实际半径inflation_radius要设成半径的1.5倍左右否则机器人会贴着墙走或者卡住。5. 常见问题与排查技巧实录5.1 通信类问题问题1串口数据乱码这是最常见的问题。原因一般是波特率不匹配、数据位/停止位配置错误、或者地线没接好。排查步骤先用串口助手看STM32发的原始数据确认波特率正确再检查ROS2这边的串口配置最后检查硬件连线特别是GND。问题2CAN通信突然连不上CAN总线的问题一般是终端电阻没接、波特率不匹配、或者总线负载过高。排查步骤用万用表测CAN_H和CAN_L之间的电阻应该是60欧姆左右检查所有节点的波特率是否一致用CAN分析仪看总线负载如果超过70%就要优化。问题3ROS2话题收不到数据先ros2 topic list看话题是否存在再ros2 topic echo /odom看有没有数据。如果没有数据检查发布者是否启动如果有数据但频率不对检查定时器配置。另外QoS配置不匹配也会导致收不到数据发布者和订阅者的QoS要一致。5.2 电机控制类问题问题4电机抖动或异响一般是PID参数不合适。先降低P如果抖动减轻说明P太大如果响应变慢说明P太小。I太大会导致积分饱和表现为电机突然加速D太大会导致高频振荡。我一般用Ziegler-Nichols方法做初步整定再手动微调。问题5里程计漂移严重编码器分辨率不够、轮子打滑、或者地面不平都会导致漂移。解决办法提高编码器线数在轮子上加防滑材料用IMU做融合。如果漂移是系统性的可以标定轮子直径和轮距修正里程计模型。问题6五线四相步进电机不转五线四相步进电机的接线是红线接VCC其余四根接驱动器的输出。如果不动检查驱动器是否使能、脉冲信号是否正常、相序是否正确。用万用表测每相的电阻应该差不多。如果相序错了电机会抖动但不转。5.3 建图与导航类问题问题7建图出现重影或错位一般是雷达数据和时间戳不同步。检查雷达驱动的时间戳是否用了系统时间检查TF变换是否正确检查机器人移动速度是否太快。另外雷达的安装角度也要标定如果雷达装歪了建图会整体旋转。问题8导航时机器人卡住先看代价地图如果障碍物膨胀太大机器人会认为通道太窄。调整inflation_radius和cost_scaling_factor。如果机器人原地打转检查cmd_vel的角速度限制如果机器人不动检查cmd_vel是否被其他节点覆盖。问题9八叉树地图导航内存占用高八叉树地图OctoMap的分辨率越高内存占用越大。如果树莓派内存不够可以降低分辨率比如从0.05米降到0.1米或者限制地图范围。另外定期清理旧数据也能释放内存。5.4 开发环境类问题问题10VSCode配置STM32开发环境失败一般是路径配置错误。检查c_cpp_properties.json里的includePath是否包含了STM32的头文件目录检查launch.json里的svdFile是否指向了正确的SVD文件检查J-Link的驱动是否安装。如果还是不行用STM32CubeIDE先跑通再迁移到VSCode。问题11STM32芯片包安装失败在Keil或CubeIDE里安装芯片包时如果网络不好可以手动下载.pack文件然后离线安装。另外芯片包的版本要和芯片型号匹配比如STM32F1系列要用F1的包不能用F4的。问题12ROS2安装教程里的命令报错最常见的是apt update报错一般是源的问题。检查/etc/apt/sources.list.d/ros2.list的内容是否正确检查网络是否能访问packages.ros.org如果用了代理要配置apt的代理。另外Ubuntu版本和ROS2版本要对应22.04对应Humble24.04对应Jazzy。6. 从扫地机延伸到更广的机器人开发一台开源扫地机器人跑通之后你会发现很多技能是可以迁移的。比如ROS2的话题-服务-动作机制在机械臂、无人机、自动驾驶上都是一样的STM32的PID和传感器融合在平衡车、云台、四足机器人上也是通用的。如果你想继续深入我建议几个方向一是视觉SLAM用RGB-D相机替代激光雷达跑ORB-SLAM3或RTAB-Map二是多机协同用ROS2的DDS发现机制让多台扫地机协同建图三是强化学习用Gazebo仿真环境训练路径规划策略再迁移到真机。最后分享一个我在实际项目中踩过的坑不要一开始就追求全功能。我见过太多人一上来就想做激光雷达视觉AI导航结果卡在串口通信上。正确的做法是先把最小闭环跑通——STM32控制轮子转ROS2能发指令收里程计——然后再逐步加传感器和算法。这个顺序看起来慢实际上是最快的。另外文档和注释比代码本身更重要。扫地机的代码量不大但涉及硬件、通信、算法多个层面过一个月再看没有注释根本记不住当时为什么这么写。我现在的习惯是每个模块开头写清楚输入输出、依赖关系、已知问题调试的时候能省很多时间。
返回列表