☰
ROS2 Humble激光雷达避障实战:从LaserScan数据到Python避障节点5分钟跑通
2026/10/7 6:33:35 网站建设 项目流程

说句实话,网上凡是带“保姆级”三个字的教程,十个有八个打开之后是一堆环境变量的排列组合,看完反而更懵。所以这篇我换个写法:先明确告诉你“5分钟”这个说法是怎么来的,再带你从零把一个避障节点写出来、跑起来、调明白。前提是你已经装好了Ubuntu 22.04和ROS2 Humble,激光雷达插上电能出/scan数据,底盘能收/cmd_vel指令——这三样如果没搞定,5分钟确实不够,但你把这篇文章看完也就不用到处翻资料了。

适合读这篇文章的人很明确:手里有ROS2小车或自己攒了一套底盘+激光雷达,想让小车动起来之后不撞墙,但不知道代码该从哪下手。你不需要会SLAM,不需要懂路径规划,Python基础语法会写if就行。我会把避障从原理到代码、从串口排查到参数微调全部讲透,最后再聊聊怎么从“能避障”升级到“走得更聪明”。项目里的核心就是ROS2 Humble、激光雷达、Python这三个关键词,放心,一个都不会少。

1. 开始之前:这套避障方案对硬件和系统的真实要求

1.1 为什么是ROS2 Humble:版本选择的现实逻辑

先解决一个很多人上来就会问的问题:为什么偏偏是ROS2 Humble?

最直接的理由是它有长期支持(LTS),支持周期到2027年。这对做实物机器人的人来说太重要了——你不可能每半年重装一次系统,更不可能在比赛前夜发现官方把某个核心包停更了。ROS2目前可以装的主线版本里,Foxy已经过时,Iron不是LTS,Rolling是滚动版天天变,真正能安心放在实验室或者比赛车上用的就是Humble。

如果你是刚从ROS1 Noetic转过来的,感受会更明显。Noetic的生命周期已经逼近终点,而且ROS1的架构决定了它处理多机通信、断线重连这些问题时非常吃力。ROS2用DDS做底层通信,节点之间是真正的分布式架构,激光雷达驱动、底盘驱动、避障节点哪怕拆到三台机器上跑,通信也不会有问题。对一篇避障教程来说,这些底层优势你可能暂时感知不到,但你迟早会用上。

版本差异方面我给个小表格,方便你判断自己该不该换:

发行版对应Ubuntu支持周期适合场景
ROS1 Noetic20.04已进入维护末期老项目迁移,不建议新开坑
ROS2 Foxy20.04已停止维护别用了,包都装不全
ROS2 Humble22.04到2027年入门、比赛、产品原型都合适
ROS2 Rolling不定滚动更新尝鲜可以,别拿来做项目

一句话总结:如果你现在还没上车,直接选Humble;如果已经在别的版本上写了一半,除非有非换不可的理由,否则也别折腾,先把功能跑通更重要。

1.2 激光雷达和底盘:入门配置与接口要求

避障这件事,激光雷达就是小车的“眼睛”。入门阶段没必要追求什么64线机械式雷达,一个单线激光雷达就完全够用。市面上最常见的几款我列一下:

雷达型号测距原理测距范围扫描频率ROS2驱动情况
YDLidar X2/X4三角测距0.12m~8m/10m5-12Hz有官方ros2驱动
RPLidar A1三角测距0.12m~12m5.5Hz有官方ros2驱动
RPLidar A2三角测距0.15m~12m10Hz有官方ros2驱动
YDLidar TG系列TOF0.05m~25m10Hz有官方ros2驱动

选雷达时有三个参数我最看重:测距范围、扫描频率、视场角(FOV)。入门前两个不用说,第三个容易被忽略。这里的视场角指的是雷达能“看到”多大角度范围,有的雷达是360度全向扫描,有的只有240度甚至更小。避障算法其实只关心小车前进方向前后那一圈,360度最好,240度也够用,但要提前知道你的雷达视野边界在哪,不然避障逻辑容易在盲区方向判断失误。

底盘这块更简单。你只需要确认一件事:底盘驱动能监听/cmd_vel话题,话题的消息类型是geometry_msgs/msg/Twist。不管你的底盘是Arduino、STM32还是树莓派直连电机驱动板,只要能做到“收到Twist消息就转成电机PWM”,后面的事全部交给ROS2上层。很多成品小车底盘出厂就提供ROS2驱动包,到手ros2 run就能跑;自己攒的底盘就得看你单片机里的串口协议怎么写了。

1.3 “5分钟”是怎么算出来的:环境就绪后的核心链路

现在能解释标题里“5分钟”的底气了。假设环境全部就绪,你实际要做的操作是:创建Python包(1分钟)→ 写一个避障节点文件(2分钟,代码我会给你)→ 配置入口点并编译(1分钟)→ 运行雷达驱动和避障节点(1分钟)。满打满算5分钟,一点不虚。

但“环境全部就绪”这六个字背后是有隐藏成本的。我帮你拆开看看这些成本都在哪:

  • ROS2 Humble本身装好(约15分钟,取决于网络)
  • 激光雷达驱动装好,拔插USB后/scan话题有数据(约10分钟,串口权限是个大坑)
  • 底盘驱动能响应/cmd_vel指令(约10分钟,看你底盘的文档质量)

这三件事做完,你才拿到进入“5分钟”赛道的入场券。所以这篇保姆级教程把第一部分给了环境检查,后面才讲避障代码。千万别觉得前面麻烦,实测下来,十个人做避障,七个都卡在雷达驱动上,真正写避障节点的时间少得可怜。

2. 避障不玄乎:一个反应式决策就够,关键是读懂LaserScan

2.1 /scan话题里装了啥:LaserScan消息的关键字段

雷达驱动起来之后,你会看到一个话题叫做/scan,消息类型是sensor_msgs/msg/LaserScan。避障节点的所有输入,全部来自这条消息。所以看懂它等于拿到了小车的“视力报告”。

LaserScan消息的核心字段就几个:

字段含义示例值
angle_min扫描起始角度(弧度)-3.14159(-180°)
angle_max扫描结束角度(弧度)3.14159(180°)
angle_increment相邻两个扫描点之间的角度间隔(弧度)0.01745(约1°)
range_min有效测距最小值(米)0.06
range_max有效测距最大值(米)10.0
ranges所有扫描点的距离数组(米)长度等于点数
intensities反射强度数组,辅助信号,避障可以忽略——

ranges这个数组是核心中的核心。数组的每一个元素,代表雷达在这个角度上测到的障碍物距离。角度和数组下标的换算关系是死规矩:

angle = angle_min + index * angle_increment

举个例子:如果angle_min是-180°(约-3.14弧度),angle_increment是1°(约0.01745弧度),那么ranges[0]就是正后方180°方向的障碍距离,ranges[180]大约是正前方的距离。你可以把ranges想象成一把以雷达为中心向外辐射的米尺,每一度方向上都有一把。

有一个坑必须在看数据前就提醒你:ranges里的值不全是有效数字。常见的是inf(超出测距范围,没探测到东西)和nan(当前角度信号无效)。写代码时如果不把这些滤掉,min()一算就直接出错。后面代码里我会专门处理这一层。

2.2 左右比较的贪心策略:让小车像人一样“哪里空往哪走”

算法层面,这篇不搞SLAM,不搞栅格地图,就用一个最朴素的反应式策略。核心思路一句话:把前方激光数据分成左、中、右三个区域,如果正前方距离太近,就朝“左侧更空”或“右侧更空”的那边转弯。

这个逻辑你在超市推购物车时天天用——过道里遇到个货架堵路,你会先停一步,往左右各瞄一眼,哪边宽敞就往哪边拐,拐过去之后重新看路继续走。小车不需要知道自己在哪,也不需要知道目的地,它只需要保证“此刻不撞上面前的东西”。

三个区域的具体划分方式我建议这样定(角度均以雷达正前方为0°):

  • 正前方区域:-30°到+30°,用于决定是否需要避障
  • 左侧区域:0°到+60°,用于判断左侧可用空间
  • 右侧区域:-60°到0°,用于判断右侧可用空间

每个区域取该角度范围内所有有效距离的最小值。判断逻辑很简单:

  1. 如果前方最小距离大于安全阈值——直行,不要犹豫
  2. 如果前方最小距离小于安全阈值——刹车,然后比较左右区域的距离,哪边大往哪边转
  3. 转向过程中线速度设为0,先原地转,转到前方安全了再继续走

这里有个细节值得说一下:为什么前方区域只取±30°,而不是整个前方90°都用?因为激光雷达扫描频率有限,底盘也有刹车惯性,我们把“避障触发”的条件卡得更紧一点,留给小车更多的反应时间。30°这个值不是拍脑袋定的,你可以用三角函数算一下——当障碍物在侧前方45°方向时,小车转个20°就能绕开,如果等到障碍物进入正前方30°再反应,转弯半径会明显变大。实测下来±30°是一个兼顾灵敏度和稳定性的值,你先照抄,后面再按你底盘的轴距调。

2.3 为什么不直接上DWA和A*:先跑通再升级的正确姿势

你可能会想:现在路径规划算法那么多,DWA、VFH、A*,直接上不好吗?我的建议是:先别。

DWA(动态窗口法)确实是更平滑的局部避障方案,它会在速度空间里搜索一组可行速度组合,同时考虑障碍物距离、目标方向和机器人运动学约束。但代价是要配置的权重参数一大堆,而且它通常需要里程计信息来维持坐标系更新。如果你的底盘没有精度好的里程计,DWA很容易出现“目标点已到达”或“原地转圈”的抽风行为。VFH(向量场直方图)算法处理窄缝的能力更强,但同样需要对雷达数据做直方图滤波,代码量直接翻倍。

而我这篇用的贪心策略,是整个避障算法家族里最朴素、最好调、错误最直观的一个。它不考虑目标点,不考虑全局路径,只做最本能的“避障反射”。好处有三点:

  • 代码量小,几十行就能跑通,逻辑一眼就能看懂
  • 不依赖里程计、IMU、TF树,底盘哪怕数据不准也能跑
  • 出了问题好排查,动不了就是雷达数据问题,乱转就是参数问题

等你用这篇的方法把整个链路跑通了,再升级DWA、VFH甚至Navigation2都有基础。到那时候你会发现,不管换成什么高级算法,第一步仍然是从/scan里读数据、过滤无效值、计算区域距离——这些基本功是一样的。

3. Python避障节点实操:从建包、写代码到跑起来

3.1 创建工作空间和Python包

打开终端,先建工作空间和包。假设你用户名是ros,工作空间放在~/ros2_ws:

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

这条命令会生成一个名叫avoid_demo的Python包,目录结构大概是:

avoid_demo/ ├── package.xml ├── resource/ ├── setup.cfg ├── setup.py └── avoid_demo/ ├── __init__.py └── main.py(这个文件是我们创建的)

注意最内层的avoid_demo目录才是代码目录,我们等会儿把避障节点写在里面的main.py里。外层avoid_demo目录下的setup.py是打包配置,决定了ROS2怎么找到并启动你的节点。

3.2 逐段拆解避障节点代码

先看完整的代码,可以直接复制保存为~/ros2_ws/src/avoid_demo/avoid_demo/main.py:

#!/usr/bin/env python3 import math import rclpy from rclpy.node import Node from sensor_msgs.msg import LaserScan from geometry_msgs.msg import Twist class AvoidanceNode(Node): def __init__(self): super().__init__('avoidance_node') # 发布速度指令到底盘 self.cmd_pub = self.create_publisher(Twist, '/cmd_vel', 10) # 订阅激光雷达数据 self.scan_sub = self.create_subscription( LaserScan, '/scan', self.scan_callback, 10) # 避障参数 self.safe_distance = 0.4 # 正前方触发避障的阈值,单位:米 self.forward_speed = 0.12 # 直行线速度,单位:m/s self.turn_speed = 0.4 # 原地转弯角速度,单位:rad/s self.front_angle = 30.0 # 正前方区域半角,单位:度 self.side_angle = 60.0 # 左右区域扫描角度,单位:度 self.get_logger().info('避障节点已启动,等待激光数据...') def min_dist_in_range(self, ranges, angle_min, angle_increment, start_deg, end_deg): """计算某个角度范围内的最小有效距离""" start_idx = max( 0, int((math.radians(start_deg) - angle_min) / angle_increment)) end_idx = min( len(ranges), int((math.radians(end_deg) - angle_min) / angle_increment)) min_dist = float('inf') for i in range(start_idx, end_idx): d = ranges[i] # 过滤无效值,同时剔除距离过近的噪点 if math.isfinite(d) and d > 0.05: min_dist = min(min_dist, d) return min_dist def scan_callback(self, msg: LaserScan): angle_min = msg.angle_min angle_increment = msg.angle_increment ranges = msg.ranges # 计算三个关键区域的最小距离 front_min = self.min_dist_in_range( ranges, angle_min, angle_increment, -self.front_angle, self.front_angle) left_min = self.min_dist_in_range( ranges, angle_min, angle_increment, 0.0, self.side_angle) right_min = self.min_dist_in_range( ranges, angle_min, angle_increment, -self.side_angle, 0.0) twist = Twist() if front_min < self.safe_distance: # 前方有障碍,停车并转向更空旷的一侧 twist.linear.x = 0.0 if left_min >= right_min: twist.angular.z = self.turn_speed self.get_logger().info( f'前方 {front_min:.2f}m 有障碍,左转 (左:{left_min:.2f}m 右:{right_min:.2f}m)') else: twist.angular.z = -self.turn_speed self.get_logger().info( f'前方 {front_min:.2f}m 有障碍,右转 (左:{left_min:.2f}m 右:{right_min:.2f}m)') else: # 前方安全,直行 twist.linear.x = self.forward_speed twist.angular.z = 0.0 self.cmd_pub.publish(twist) def destroy_node(self): # 节点销毁时发布零速度,避免小车突然加速 zero_twist = Twist() zero_twist.linear.x = 0.0 zero_twist.angular.z = 0.0 self.cmd_pub.publish(zero_twist) super().destroy_node() def main(args=None): rclpy.init(args=args) node = AvoidanceNode() try: rclpy.spin(node) except KeyboardInterrupt: pass finally: node.destroy_node() rclpy.shutdown() if __name__ == '__main__': main()

这段代码的骨架是ROS2节点最常见的模式:构造函数里创建订阅者和发布者,然后回调函数处理数据。我挑几个地方重点讲一下。

min_dist_in_range这个函数是整个节点的核心工具。它接收角度范围参数,先通过angle_min和angle_increment把角度换算成数组下标,然后遍历这段下标找最小有效距离。换算公式就是前面讲过的angle = angle_min + index * angle_increment,只不过这里做了反向操作。处理无效值时用math.isfinite()一次筛掉inf和nan,再用d > 0.05筛掉那些由于雷达自身外壳反射产生的近距离噪点,这两行过滤缺一不可。

转向逻辑里有个容易被忽略的细节:left_min >= right_min时我选择左转,两边的值相等时默认左转。这是人为设定的优先级,本身没有对错,但如果你发现小车特别偏好某个方向,可以改这个判断条件来调整。

还有destroy_node里发布零速度这个操作。看起来不起眼,实际非常重要——ROS2节点在Ctrl+C退出时,如果不显式发一条零速度,底盘可能会保持最后一刻的速度指令继续往前冲。这在调试小车时很吓人,我见过有人因为没处理这个细节,小车在测试结束瞬间直接加速撞墙的。加这个保险之后,无论你是正常退出还是按Ctrl+C,小车都会先停下来再关闭节点。

3.3 配置entry point并编译运行

代码写完了,还得让ROS2知道怎么启动这个节点。打开setup.py,把entry_points部分改成下面这样:

entry_points={ 'console_scripts': [ 'avoidance_node = avoid_demo.main:main', ], },

这行配置的意思是:执行ros2 run avoid_demo avoidance_node时,ROS2会去avoid_demo包里找到main.py文件,然后调用里面的main()函数。包名、文件名、函数名三者必须对得上,少一个都会在运行时报“找不到入口点”的错误。

接下来编译并运行:

cd ~/ros2_ws colcon build --packages-select avoid_demo source install/setup.bash ros2 run avoid_demo avoidance_node

终端里如果出现避障节点已启动,等待激光数据...的日志,并且在雷达驱动运行的情况下开始不断打印前方距离和转向信息,恭喜你,避障代码已经跑起来了。

4. 实车联调:为什么小车还是撞了墙?排查链路与调参方法

4.1 串口权限和雷达驱动:没有/scan后面全白搭

最常见的“不动”问题,十有八九出在雷达数据上。先检查/scan话题有没有数据:

ros2 topic list ros2 topic echo /scan --once

第二条命令如果一直卡着不输出,说明雷达驱动压根没发布数据。先看雷达的USB设备是否存在:

ls -l /dev/ttyUSB0

如果提示No such file or directory,先拔插一次USB线再试。如果设备存在但提示Permission denied,是串口权限问题。临时解决办法是:

sudo chmod 777 /dev/ttyUSB0

这个命令重启后失效,但调试时最省事。一劳永逸的办法是把自己的用户加入dialout组:

sudo usermod -aG dialout $USER

注意执行完这条要重新登录一次才生效。权限搞定之后重新启动雷达驱动,ros2 topic echo /scan --once能刷出数据,才算进入下一步。

还有一个很隐蔽的坑:有些激光雷达需要先启动驱动节点才能识别串口,有些则相反,必须先上电再启动驱动。如果你的雷达总是“有时有数据有时没数据”,检查一下你的上电顺序是不是稳定的。

4.2 先手动验证cmd_vel,再交给避障节点

雷达数据有了,避障节点也在跑,但小车不动。这时候很多人会直接怀疑代码写得有问题,其实多半是/cmd_vel根本没通。我排查的顺序很固定,先手动发一条速度指令验证底盘:

ros2 topic pub --once /cmd_vel geometry_msgs/msg/Twist "{linear: {x: 0.1, y: 0.0, z: 0.0}, angular: {x: 0.0, y: 0.0, z: 0.5}}"

这条命令会发布一个“前进0.1m/s且左转0.5rad/s”的速度指令。如果小车动了,说明底盘驱动正常;如果没动,问题在底盘驱动,不在避障代码。这时候去检查底盘驱动节点的日志,看看有没有订阅/cmd_vel话题、有没有权限写入串口、电机的使能引脚是不是没拉高。

验证完底盘,再用RViz2确认雷达视角。这条命令启动可视化工具:

ros2 run rviz2 rviz2

在RViz2里添加一个LaserScan显示组件,Topic选/scan,Fixed Frame设成你雷达的frame_id(一般是laser或laser_frame)。如果画面里能看到一圈离散的点云,说明雷达数据在空间上也是正确的。这一步非常推荐做,因为雷达数据虽然“有数值”,但不代表“空间位置正确”——比如frame_id配错会导致点在错误的位置显示出来,避障逻辑虽然还在跑,但拿到的数据已经是错的。

4.3 实测中一定会遇到的三个“撞墙”原因

跑起来之后,几乎每个人都会遇到小车撞墙的问题。我总结一下最常碰到的三种情况,以及对应排查链路。

第一种:小车冲向障碍物但不减速,直到撞上才转向。这种多半是安全距离阈值太小。safe_distance = 0.4这个值是基于一般室内小车速度(0.12m/s)算出来的:以这个速度,0.4米可以提供大约3秒的反应时间,足够了。但如果你把forward_speed调大,安全距离必须同步调大。一个粗略的经验公式是:安全距离 ≈ 最大线速度 × 1.5 + 0.2。比如速度提到0.3m/s,安全距离就得调到0.65左右。

第二种:小车原地疯狂转圈,但四周明明没有障碍物。这种常见于雷达安装位置不居中或底盘轴距不对称,导致左右区域的测量结果始终一边偏大一边偏小。排查方法是把小车放在开阔场地中央,用RViz2看两边的距离读数是否基本一致。如果不一致,先检查雷达是否装在了小车的几何中心;实在没法挪的话,可以在代码里给某一侧的距离加一个固定补偿值。

第三种:小车灵巧地绕过了大箱子,却在小桌腿这类细杆状障碍物上撞上去。这是激光雷达的“漏检”问题——细杆对雷达波束的反射面积太小,加上每次扫描有角度间隔,杆子可能正好落在两个扫描点之间。缓解办法是把雷达扫描频率调高一点,同时把front_angle从30°扩大到45°,让细杆提前进入触发区域。

排查这类“撞墙”问题时,千万记住一条:先看/scan数据,再看避障节点的决策日志,最后才怀疑底盘响应。数据是源头,你的算法再完美,雷达看漏了就是会撞;底盘响应再快,决策错了方向也不对。按这个链路一层层查,问题不会跑出这三样。

5. 从“能避障”到“不撞墙还走得顺”:速度平滑、视野盲区与进阶路线

5.1 让转向更顺滑:速度平滑与安全距离联动

基础版本跑通之后,你可能会发现一个问题:小车动不动就急停急转,像个喝了酒的人走路。原因是当前逻辑里“转向”和“直行”是二值切换,没有过渡。解决办法是给角速度加一个低通滤波,让方向变化变得平滑:

# 在 __init__ 里加一个平滑变量 self.smooth_angular_z = 0.0 # 在 scan_callback 里发布之前加平滑 target_angular_z = 0.0 if front_min < self.safe_distance: target_angular_z = self.turn_speed if left_min >= right_min else -self.turn_speed self.smooth_angular_z = 0.8 * self.smooth_angular_z + 0.2 * target_angular_z twist.angular.z = self.smooth_angular_z

这个平滑公式的原理是:新输出的角速度由80%的旧值和20%的新目标值组成。每一次回调都往目标值“挪”一点,而不是直接跳过去。0.8和0.2这两个系数决定了平滑速度,系数越小响应越快但越突兀,系数越大响应越慢但越平滑。你可以在0.7/0.3到0.9/0.1之间多试几组,找到适合自己手感的值。

安全距离也可以做成动态的:线速度越大,刹车距离越长,安全距离就应该越大。可以在代码里让safe_distance跟着forward_speed联动:

self.safe_distance = max(0.35, self.forward_speed * 1.5 + 0.2)

这样你只调一个forward_speed,安全距离会自动跟上,省得两个参数来回调半天。

5.2 激光雷达的盲区与安装位置问题

很多人忽略一件事:单线激光雷达只能扫描一个平面。如果你把雷达装在小车底盘上,它只能看到和你雷达同高度的障碍物。一张矮板凳、一截台阶、一块石头,如果高度低于雷达扫描平面,雷达根本看不见。反过来,如果雷达装得太高,低于雷达平面的障碍物也会被漏掉。

处理办法是安装高度取一个折中:雷达离地高度控制在10~15厘米,这样能兼顾常见的桌腿、纸箱和部分地面杂物。还有一个小技巧——把雷达略微向前倾斜几度安装,可以让扫描平面稍微覆盖到前方地面的近距离区域,减少低矮障碍漏检。但倾斜角度别太大,不然远处的点云会明显变形,影响避障判断。

另外,雷达的360度FOV并不等于小车周围的360度都能被检测到。你把雷达装在小车正中央,雷达正下方那一圈会顶在小车机身上,形成一段没有数据的“阴影区”。代码里我加的那个d > 0.05过滤,就是为了把这段阴影区的近距离读数忽略掉,防止把车自身当成障碍物。如果你的小车尺寸比较大,可能需要把这个阈值适当再调大一点。

5.3 进阶方向:最空旷方向法、Navigation2与cartographer建图的衔接

基础功能稳定之后,想往更高级的方向走,我建议按这个顺序来。

第一步,把“左右比较”换成“最空旷方向”。思路是:在前方180°范围内遍历所有角度,找一个距离最远的方向,然后让小车朝那个方向转。这个策略比左右比较更灵活,能处理“左侧很近但左前方很远”这类场景。实现起来只需要修改min_dist_in_range的返回逻辑,改成返回“最大距离对应的角度”,然后让angular.z和目标角度做差,乘上一个比例系数输出。改动量不大,但避障效果会明显上一个台阶。

第二步,引入Navigation2。Navigation2是ROS2官方的导航框架,它把地图、代价地图、全局路径规划器、局部路径规划器全包了。你可以在局部代价地图里配置DWA插件,它会自动完成速度采样和轨迹评分。但注意,Navigation2通常需要一个静态地图,这就要用到SLAM了。

第三步,接cartographer建图。搜索热词里有“ros2+cartographer+激光雷达建图并保存”,说明这是很多人的痛点。用cartographer建图的基本链路是:雷达数据/scan加上底盘里程计/odom和TF树,共同输入给cartographer节点,输出/map。很多人“建图飘”的根本原因不是cartographer本身,而是TF树没配好——map→odom→base_link→laser这条链路少一个frame都飘。如果你决定走这条路,建议先去把URDF里每个frame的关系理清楚,再碰cartographer。

如果你用的是ESP32这类MCU做底盘控制,还可以看看micro-ROS的方案。通过micro-ROS Agent把ESP32挂进ROS2网络,让单片机直接以micro-ROS节点的身份发布里程计、订阅/cmd_vel,整个系统就完全一体化了。但这属于另一个话题,等避障跑通了再说也不迟。

调了这么多天,我个人最深的体会是:避障这个功能,难点从来不在算法,而在“你有没有把数据的来龙去脉搞清楚”。/scan每个值代表什么、/cmd_vel发出去了底盘到底收没收、TF树缺没缺节点——这些基本功扎实了,避障代码反而是一小时就能写完的事。希望这篇能帮你少走我当初走过的弯路,把车先跑起来,剩下的路,咱们一步步走。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询