移动机器人自主导航,说到底就三件事:我在哪、要去哪、怎么安全地过去。而标题里这一串东西——EKF状态估计、AMCL蒙特卡洛定位、模糊控制避障、激光雷达、ROS+Gazebo仿真——恰好就是一套完整回答这三个问题的方案。我最初觉得这组合有点杂,但实际搭下来会发现,每个模块都在干自己最擅长的那摊活:EKF管传感器融合,AMCL管全局定位,模糊控制管局部避障,Gazebo负责把机器人和环境模拟出来。整套系统跑起来之后,机器人能在仿真环境里从一个点导航到另一个点,遇到障碍物还能绕着走。这个项目适合刚接触机器人导航的开发者,也适合想把分散技术点串成一个完整系统的同学参考。下面按项目结构拆开讲,包括原理、代码思路和调试经验。
1. 整体拆解:导航、定位、避障三件事怎么串成一条流水线
1.1 三个核心模块的分工
先说清楚这套系统里每个模块到底负责什么。EKF状态估计是“传感器融合层”,它把轮式里程计、IMU甚至部分激光雷达信息融合起来,输出一个相对平滑、不易漂移的局部位姿估计。AMCL是“全局定位层”,它基于已知地图和激光雷达扫描,估算机器人在地图中的全局位置,解决的是“我到底在地图哪个位置”。模糊控制是“局部避障层”,它接收激光雷达测距信息,通过模糊推理输出速度和转角指令,解决的是“眼前有障碍物怎么躲”。
这三个模块不是替代关系,而是互补关系。EKF就像人的内耳前庭,能感知身体移动但不知道自己在房间哪个角落;AMCL就像眼睛看着户型图确认“我在客厅还是卧室”;模糊控制就像手碰到热锅会本能缩回,不经过大脑深思熟虑。三者配合才有完整的自主导航能力。
1.2 为什么选这个组合而不是一锅端
很多初学者喜欢问,直接用move_base里的DWA或者TEB局部规划器不就行了吗,为什么还要单独上一套模糊控制避障?
我的理解是,这个组合方案看重的是“容错”。ROS的move_base确实自带局部规划器,但它的避障行为依赖代价地图的分层和梯度计算,在Gazebo里如果传感器噪声一大、地图膨胀半径设得不合理,容易出现明明看到障碍物却依旧撞上去的情况。模糊控制的好处是规则直白、计算量小,不依赖精确的环境模型,就像老司机的肌肉记忆,看到前面近了就减速打方向。所以在这个项目里,它更像是给整个导航系统加了一道保险,在全局规划和传统局部规划失效的边缘时刻兜底。
另外,EKF和AMCL这两个定位算法组合也有讲究。EKF负责维护“连续性”,它把轮速计和IMU融合出一个高频的位姿变化轨迹;AMCL负责维护“绝对性”,它用激光雷达扫描和地图匹配定期修正累计误差。如果只靠EKF,几分钟内漂移就能让机器人定位偏出半米;如果只靠AMCL,在特征稀疏的走廊里,粒子滤波也常常会陷入多峰分布而无法确定唯一位置。两个方法配合,一个管短时高频,一个管长时准确,是室内机器人定位的经典方案。
1.3 系统数据流:从激光雷达扫描到轮子转动
整个系统的数据流大概是这样的:激光雷达持续输出/scan话题,一帧数据同时被多个模块使用。AMCL收到/scan后结合地图做粒子权重更新,输出机器人在地图中的位姿(即map到odom的坐标变换)。EKF节点接收轮式里程计和IMU数据,输出平滑的odom到base_link变换。导航目标给定后,move_base的全局规划器在地图上搜索一条路径,局部规划器沿路径行驶;与此同时,模糊避障节点也在监听/scan,一旦检测到障碍距离低于阈值,就产出一个紧急转向指令。
这些模块最终都要汇聚到速度指令上,我习惯用一个轻量级仲裁节点来处理:正常情况下采纳move_base的cmd_vel,模糊避障发出的紧急指令仅在距离阈值超限时接管。这样既保留常规导航的规划能力,又具备突发障碍的快速反应。仲裁逻辑很简单,但非常实用,能做掉大量“局部规划器卡住”的尴尬场景。
2. EKF状态估计:融合里程计与IMU,先解决“我动了多少”
2.1 为什么不能只信轮式里程计
轮式里程计的原理是“航位推算”:通过左右轮编码器计算位移和转角,再从初始位姿累加出当前位置。它的短时精度其实不错,帧间变化很平滑,问题是“积分漂移”——轮子打滑、地面不平、轮胎气压不一致,都会让累计误差越来越大,走个几十米就明显偏离真实轨迹。
IMU的陀螺仪对角速度的测量短时很准,加速度计对重力的参考也有用,但加速度积分成位移会二次漂移,时间一长完全不能看。单独信谁都不靠谱,但互补之后就不一样了:轮速计能修正IMU的角速度漂移,IMU能平滑轮速计的转弯噪声。EKF干的就是这活,用状态估计把多源信息按置信度加权融合。
2.2 EKF核心计算过程
扩展卡尔曼滤波解决的是非线性系统下的状态估计问题。这里的状态向量可以取为:
x = [x, y, θ, v, ω]^T运动模型是非线性的,比如位姿变化满足:
x_k = x_{k-1} + v·dt·cos(θ_{k-1}) y_k = y_{k-1} + v·dt·sin(θ_{k-1}) θ_k = θ_{k-1} + ω·dt为了套用卡尔曼滤波框架,需要对这个非线性模型求雅可比矩阵,也就是对状态量求偏导。常见的做法是把速度和角速度也放进状态里,这样预测矩阵可以简单很多。观测模型则是把IMU的角速度、轮速计的位姿增量映射到状态空间。
EKF的预测步和更新步可以简化为五条公式:预测状态均值、预测协方差、计算卡尔曼增益、更新状态均值、更新协方差。卡尔曼增益本质上是一个加权系数,它根据预测的不确定度和观测的不确定度决定“更相信模型”还是“更相信传感器”。如果观测噪声R设得小,算法就更信任传感器,更新后的状态会追着观测跑;如果过程噪声Q设得小,算法就更信任运动模型,轨迹会更平滑但可能对真实偏差反应迟钝。
2.3 在ROS里搭EKF融合节点
ROS里最常见的现成方案是robot_pose_ekf包,虽然它只融合轮式里程计、IMU和视觉里程计,不直接吃激光雷达,但对本项目够用了。我这边更推荐自己用tf2和message_filters写一个轻量EKF节点,原因是可以完全控制协方差参数,还能把输出直接变换成odom_combined发给AMCL使用。
关键话题和参数大概是:订阅/odom(轮式里程计)、/imu/data,用时间同步器对齐时间戳,然后按传感器各自的名义噪声填写R矩阵。注意IMU的数据频率通常比轮速计高不少,时间同步时要做缓存队列,避免消息等待时间过长导致延迟累积。
我一般会用如下思路组织代码:
# 伪代码结构,用于说明流程 class EkfNode: def __init__(self): self.ekf = ExtendedKalmanFilter(dim_x=5, dim_z=3) self.ekf.Q = np.diag([0.02, 0.02, 0.02, 0.2, 0.2]) self.ekf.R_odom = np.diag([0.03, 0.03, 0.02]) self.ekf.R_imu = np.diag([0.01]) def odom_callback(self, msg): z = extract_odom_measurement(msg) self.ekf.update(z, H_odom) publish_estimate() def imu_callback(self, msg): z = extract_imu_measurement(msg) self.ekf.update(z, H_imu) publish_estimate() def predict_step(self): self.ekf.predict(F_from_kinematics, dt)在Gazebo仿真里,轮速计和IMU都是由gazebo插件模拟的,噪声水平可以人为设定,这其实是个好事——你可以先给IMU加很小的噪声,确认EKF能跟上,再逐步加大噪声,检验滤波的鲁棒性。
2.4 用Matlab离线调参的价值
标题里那部分Matlab源码,在工程上干的就是离线验证的活。把Gazebo里录制的一段rosbag数据导出来,在Matlab里跑一遍EKF和纯里程计对比,你可以直观看到累积轨迹的差异,也能画出3σ协方差椭圆。协方差椭圆是判断滤波器健康度的重要指标:椭圆逐渐变大说明观测修正不足或Q设置过小,椭圆忽大忽小剧烈抖动说明R矩阵和实际传感器噪声不匹配。
调协方差矩阵的经验是:先调对角元素,非对角先设0;Q平移项控制在0.01到0.05量级,角度项控制在0.01到0.05弧度量级;R根据传感器手册或实测方差填写。在Gazebo里更简单的做法是跑一段直行和原地旋转,用统计工具算出里程计和IMU实际的方差,再回填到R里,效果往往比手调半天好得多。
3. AMCL蒙特卡洛定位:从“大致知道”到“精确知道”
3.1 粒子滤波到底在干什么
AMCL的全称是自适应蒙特卡洛定位,本质是粒子滤波在移动机器人2D定位上的标准实现。它的核心思想是用一堆带权重的粒子来表示机器人位姿的概率分布。每个粒子都是一个假设:也许机器人在这里,也许在那里,权重越高代表这个假设和激光雷达实际扫描越匹配。
具体流程是三步循环。预测:根据运动模型把每个粒子按照里程计增量移动,同时加上一定的高斯噪声,让粒子散布开来。更新:每个粒子把当前激光雷达扫描投影到地图上,计算扫描匹配的似然概率,作为该粒子的权重,得分高的粒子存活,得分低的权重变小。重采样:根据权重重新抽取粒子,权重高的粒子可能被复制多份,权重低的粒子逐渐消失。如此循环,粒子群会从初始的分散状态逐渐收敛到机器人真实位姿附近。
3.2 自适应机制:KLD采样与粒子数量
AMCL的自适应之处在于它不会永远用固定数量的粒子。通过KLD采样方法,它会在定位不确定时自动增加粒子数,在分布收敛后减少粒子数。因为KLD采样衡量的是当前粒子分布和目标分布之间的KL散度,当粒子分布很集中时,用较少粒子就能维持采样多样性;当分布还很散时,必须增加粒子数量,否则无法覆盖整个状态空间。
在Gazebo里,我见过不少人把max_particles设成几千甚至上万,结果跑起来CPU占用率飙升,而定位精度并没有明显改善。合理的做法是初始阶段给足粒子用于收敛,稳定后让系统自动降下来。例如min_particles=500、max_particles=3000,在特征丰富的房间内,稳定运行后粒子数常常自动维持在800到1000上下,效果已经很好。
3.3 AMCL参数配置与调参心得
AMCL参数多,但真正影响定位质量的主要是这几类。
激光雷达模型参数中,z_hit、z_short、z_max、z_rand四个权重之和应为1,分别对应击中、短测、最大距离、随机读数四种观测模型的信任程度。在室内环境里我一般取z_hit=0.75、z_short=0.1、z_max=0.05、z_rand=0.1。sigma_hit控制激光点匹配的高斯宽度,取值通常在0.2米左右,太小会让权重变化过于敏感,稍微一点噪声就打飘。
运动模型参数alpha1到alpha5和里程计噪声有关。Gazebo里轮速计噪声可控,我一般给alpha1=0.1、alpha2=0.1、alpha3=0.1、alpha4=0.1、alpha5=0.2,再根据实际漂移微调。update_min_d和update_min_a控制机器人移动多少距离或旋转多少角度才触发一次激光更新,设得太小会导致频繁匹配、计算量大,设得太大则定位反应迟钝。我常用update_min_d=0.25米、update_min_a=0.2弧度。
初始位姿对AMCL收敛影响极大。在Gazebo中启动后,如果机器人不动,粒子很难收敛到正确位置,因为激光扫描一点变化都没有,权重更新无从谈起。所以启动后先让机器人转个圈,或者小幅移动一段,让雷达“看到”不同角度的环境,粒子才会快速收敛。
3.4 定位丢失与绑架问题
AMCL有一个经典困境叫“绑架机器人问题”。把已经收敛的机器人突然搬到另一个位置,理论上粒子都集中在老位置,新位置的粒子几乎没有权重,系统会误以为定位依然正确。在Gazebo里做验证时,很多人用rosrun teleop控制机器人绕场走一圈,定位表现很好,但一旦手动把机器人模型拖到地图另一侧,定位就彻底崩了。
解决绑架问题的思路是监控粒子分布的统计量,比如平均权重或者有效粒子数。当平均权重低于某个阈值时,说明当前粒子分布和观察数据严重不匹配,应该重新随机撒粒子或者把粒子散布范围扩大。AMCL本身提供global_localization模式,开启后会在整个地图上均匀撒粒子,只是收敛速度慢,适合作为恢复手段。实际项目中,我会在导航节点里加一个监控线程,一旦检测到定位置信度过低就自动切换成全局定位模式,让粒子重新覆盖整个地图。
4. 模糊控制避障:不建精确模型,也能机灵绕过障碍
4.1 为什么用模糊控制做局部避障
模糊控制的最大优点是不需要被控对象的精确数学模型,它模仿的是人类驾驶员的经验推理:距离近了就减速,左边障碍多就右转。这个特性特别适合避障场景,因为障碍物分布千奇百怪,很难用一个统一的几何函数去描述“该往哪躲”。
相比之下,经典PID控制需要明确的误差模型,势场法容易在狭窄通道里震荡甚至陷入局部极小,VFH直方图法效果不错但计算复杂度高。模糊控制在极短的距离范围内处理“有没有障碍、在哪个方向、有多近”这类定性信息,天然适配激光雷达输出的距离数据。
4.2 激光雷达距离信息如何模糊化
先把激光雷达的扫描角度划分为前、左、右三个区域。例如一帧Velodyne或者单线雷达有720个点,角度范围从-90度到90度,那就把-90到-30度作为左侧区域,-30到30度作为前方区域,30到90度作为右侧区域。每个区域取最小距离作为代表值,得到三组输入。
这三个距离值还不能直接用于推理,需要先映射到模糊集合上。常见做法是定义三个隶属度函数:近、中、远。距离0到0.5米属于“近”,隶属度线性从1降到0;0.5到1.5米属于“中”,中间峰值设为1;大于1.5米属于“远”,隶属度逐渐上升到1。具体函数可以用三角形或梯形,调试时调整阈值和函数的斜率就行。
这段Python伪代码展示了模糊化的核心逻辑:
def fuzzify_distance(dist): if dist < 0.3: return {"近": 1.0, "中": 0.0, "远": 0.0} elif dist < 0.5: return {"近": (0.5 - dist) / 0.2, "中": (dist - 0.3) / 0.2, "远": 0.0} elif dist < 1.2: return {"近": 0.0, "中": 1.0, "远": 0.0} elif dist < 1.8: return {"近": 0.0, "中": (1.8 - dist) / 0.6, "远": (dist - 1.2) / 0.6} else: return {"近": 0.0, "中": 0.0, "远": 1.0}4.3 模糊规则表的构建与调整
模糊规则是这套避障策略的核心资产。常用的规则形式是“如果前方近且左侧远且右侧中,那么低速左转”。你需要把“近、中、远”三个模糊集合和“左、前、右”三个输入组合出来的多种情况全部列出来,逐条给输出。
我整理过一张常用规则表,基本逻辑是:前方障碍远时保持直行;前方障碍近时根据左右两侧的障碍分布决定转向方向和速度;左右两侧都有障碍且前方很拥挤时减速甚至停车。规则表在Gazebo里调参时是逐条验证的,一次只改一条,跑完一圈看效果再改下一条。
模糊推理的输出是模糊集合,不能直接驱动电机,需要用去模糊化方法把模糊量转换成精确的速度值。最常见的是重心法,也就是把输出隶属度函数的重心横坐标作为最终输出值。重心法计算量不大,输出曲线平滑,是我在项目里用得最多的方案。
4.4 模糊避障与全局路径规划的协同
模糊避障最大的问题是它只看局部,没有全局观念,可能为了躲一个障碍绕到一条死胡同里。所以在我搭建的这个系统里,模糊避障只作为紧急兜底,平时由move_base的规划路径主导。
具体实现时,仲裁节点的逻辑是:当最近障碍物距离大于0.6米时,模糊控制器输出只是跟随move_base的规划路径;当距离小于0.6米时,模糊控制器直接覆盖速度指令,输出紧急转向。在Gazebo里做验证时,我在路径中段放了一个临时障碍物,全局路径会重新规划或者DWA会尝试绕行,模糊避障则负责最后0.6米的微操,三者的配合让机器人几乎不会撞上任何墙和柱子。
5. 代价地图、move_base与整套导航框架的协同
5.1 激光雷达数据预处理
激光雷达原始数据不能直接丢给算法,需要先做清洗。Gazebo仿真里的雷达噪声虽然比真机小,但偶尔也会出现少量NaN或者inf值,这些异常点必须先滤掉,否则代价地图和模糊控制都会算错。其次是限幅,把雷达的量程限制在有效范围内,比如最小0.1米、最大10米,小于0.1米的点大多是底盘自身反射,要剔除。
还有一个经验是降采样。雷达一帧720个点全部塞进代价地图没问题,但如果之后还要跑粒子滤波等重计算,可以把连续角度上的点抽稀,例如每3度取一个点,大大减少计算量却不会降低避障精度。
5.2 全局与局部代价地图的分工
move_base使用的代价地图分为全局和局部两层。全局代价地图基于静态地图和已知障碍物,用于全局路径规划,膨胀半径通常设得较大,比如0.3米,目的是让路径提前避让墙角,避免贴着墙走。局部代价地图实际上是将激光雷达实时扫描加入代价地图,它只覆盖机器人周围几米范围,分辨率更高,更新更快,DWA就在这张图上采样速度窗口并评价轨迹安全性。
在调参时我通常先调局部代价地图,因为它的更新快,参数直观。obstacle_range控制传感器有效范围,raytrace_range控制自由空间标记范围。这两个参数一个管“哪里是障碍”,一个管“哪里是空地”,如果设定不当,会出现机器人把很远的障碍也算作高风险而原地打转,或者明明有障碍却因为标记范围太小而撞上去。
5.3 全局规划器与局部规划器的配合
全局规划器常用NavFn实现A*算法,在静态地图上搜索一条从当前位置到目标点的无碰撞路径。局部规划器则在全局路径引导下,实时考虑局部代价地图,输出平滑的速度指令。DWA仿真的是一段时间内不同速度组合(线速度和角速度的组合)产生的轨迹,并从中选择一条既接近全局路径又避开障碍物、速度又合理的轨迹。
在Gazebo调试中经常遇到的情况是:全局路径规划得很好,但局部规划器在狭窄门洞里反复卡住,因为代价地图上的可行空间太窄。解决办法通常是调小inflation_radius,或者把机器人的footprint设置得更精确,让规划器有更多可通行的空间。另一个技巧是提高vx_samples和vth_samples,增加速度采样数量可以让DWA找到更多可行轨迹,但代价是计算耗时增加,Gazebo下尤其明显,我一般取16或24。
5.4 模糊避障作为局部行为仲裁的补充
在Gazebo里跑通整套导航后,会发现在动态障碍物场景下,DWA有时会突然急停或者绕远路。因为代价地图的更新有延迟,等到规划器反应过来,障碍物已经到脸上了。这时候模糊避障的值就体现出来了,它不依赖代价地图,直接从雷达数据算结果,反应时间几乎是实时的。仲裁逻辑我已经说过,距离阈值我设为0.5米,转向角速度直接给0.6到0.8弧度每秒,线速度降到0.1米每秒。效果是机器人像受惊的小动物一样先躲开,再恢复正常导航,不会出现原地呆立或者撞上动态行人。
6. 仿真环境搭建与复现:从零跑通整个系统
6.1 环境准备与版本选型
搭建这套仿真环境我推荐使用Ubuntu 20.04搭配ROS Noetic,Gazebo默认版本是11,兼容性很稳定。如果使用Ubuntu 18.04,对应ROS Melodic也没问题,但Gazebo 9和后续版本的插件配置略有差异,需要手动适配。
安装步骤不多说,重点是验证环境完整性。启动Gazebo后先检查一下空世界能否正常加载,然后用gazebo_ros插件测试雷达话题是否发布,rostopic echo /scan能看到数据,说明传感器链路已经通了。这一步是整套系统复现的地基,地基层次出了问题后面全白搭。
6.2 机器人模型与传感器配置
在Gazebo中,机器人模型用URDF描述,底盘、轮子、雷达,最关键的是要添加仿真插件,比如差分驱动插件和雷达插件。差分驱动插件接收cmd_vel,将速度换算成左右轮转速,再通过里程计插件反馈轮速和位姿给ROS。雷达插件则基于GPU或者CPU射线模型模拟激光雷达的扫描结果。
经常被人忽略的是TF树。机器人模型的每个link之间需要定义明确的joint坐标系关系,base_link到laser_link的静态变换必须有,否则后续所有算法都无法将雷达数据转换到机器人坐标系中。启动robot_state_publisher之后,可以在rviz里看到完整的TF树,确认map -> odom -> base_link -> laser_link链路都正常。
6.3 launch文件组织与运行流程
我会把整个系统拆成几个launch文件:一个启动Gazebo世界,一个启动机器人模型,一个启动定位模块(包含AMCL和EKF),一个启动move_base和导航配置。配置参数统一放在yaml文件里,方便复用和调整。
launch文件里除了启动各节点外,还要包含地图服务的启动。我用的是静态地图,先通过map_server加载一张栅格地图,AMCL会订阅这张地图做粒子权重计算。值得一提的是,Gazebo里仿真的地图可以手动绘制,最简单的办法是用画图工具画一张黑白位图,黑色代表障碍物,白色代表空地,再配合栅格地图配置文件使用。
6.4 运行效果验证与调优顺序
系统启动后先在rviz里观察三个关键指标:TF树是否完整、AMCL粒子是否收敛到真实位置、全局路径是否能规划出来。用2D Nav Goal图标在rviz里点击一个目标点,机器人开始导航后,注意观察局部规划器的轨迹是否平滑。
调优建议按“从内到外”的顺序来:先确认EKF输出的/odom平滑稳定,再调整AMCL让粒子收敛,接着调代价地图参数让路径规划合理,最后才介入模糊避障的参数。如果一上来就调模糊避障,问题可能来自底层定位,完全没法定位到根因。我见过的不少案例都是AMCL粒子不收敛导致整个导航乱走,表面看起来像是导航算法的问题,实际是定位层崩了。
7. 常见问题与排查技巧实录
7.1 AMCL粒子发散、定位丢失
现象是rviz中粒子团迅速散开,机器人模型和真实位置偏差越来越大。排查顺序先看里程计,检查轮式里程计是否有跳变,特别是Gazebo中急转弯时打滑严重的话,需要在EKF节点里调小过程噪声Q,让滤波器更信任IMU。再看激光雷达数据,如果/scan中有大量最大距离值(表示前方空旷),粒子匹配权重区分度低,也会发散。最后检查初始位姿,很多时候只是启动时初始位姿给得太偏,多转几圈就能收敛。
7.2 EKF状态突变
有时候机器人明明静止,EKF输出的位姿却突然跳一下。这通常是因为传感器时间戳没对齐,或者某一路观测数据出现了瞬间的大噪声。解决方法是给每个传感器消息设置合理的时间同步容差,同时在EKF更新时做异常值检测——新息马氏距离超过阈值的观测就不采纳。在Gazebo里,IMU数据偶尔会有一个离群点,加了异常值拒绝之后基本能稳。
7.3 模糊避障引起的抖动
模糊避障最常见的调试问题是机器人在障碍前反复左右摆动,看起来像“犹豫不决”。这多半是模糊规则表里存在逻辑冲突,比如前方近时既有左转也有右转规则触发,且权重相近;也可能是去模糊化重心法在多峰输出下产生了不稳定的中间值。我在调的时候会把规则表逐条打印出来,配合rqt曲线看实时输入输出,能很直观地定位是哪条规则捣乱。另一个技巧是给输出加一个滞回比较器,输出变化超过一定阈值才实际执行,避免微观抖动。
7.4 Gazebo卡顿严重
粒子数多、雷达点云频率高、gpu占用大都会让Gazebo跑得很吃力。我一般先把雷达频率调到10Hz,粒子上限降到2000以下,再关掉不必要的可视化插件。如果还是卡,就把地图分辨率从0.05米每像素降到0.1米每像素,通常能换来明显流畅度提升。仿真里卡顿会导致传感器数据时间戳不规律,进一步引发定位抖动,所以优先保流畅度比保精度重要。
7.5 常见问题速查表
| 问题现象 | 可能原因 | 排查与解决办法 |
|---|---|---|
| AMCL粒子不收敛 | 初始位姿偏差大、激光噪声大 | 启动后让机器人转圈,增大max_particles,检查/scan数据质量 |
| 导航路径贴墙太近 | 代价地图膨胀半径太小 | 调大inflation_radius,检查footprint是否过大 |
| 机器人原地抖动 | DWA采样空间过小、模糊规则冲突 | 增加采样数量,逐条删除冲突模糊规则 |
| 定位突然跳变 | 传感器时间戳错乱 | 检查时间同步器,增加异常值拒绝 |
| Gazebo卡顿 | 粒子数、点云频率过高 | 降粒子数上限,降雷达频率,降低地图分辨率 |
| 避障反应过慢 | 局部代价地图更新延迟 | 缩短update_frequency,或启用模糊避障紧急接管 |
这套项目我拆过许多次,最大的体会是Gazebo里跑通不难,难的是把每个模块的参数调到互相匹配。EKF不信IMU、AMCL不信雷达、模糊避障阈值过高,任何一个环节失调都会引发连锁反应。调试时最忌讳乱改一气,一定要从定位链路开始逐层验证。真机上的情况只会比Gazebo更复杂,传感器噪声更大,时间同步更严格,但在仿真里把这些思路理顺了,真机移植的很多坑都已经提前踩过一遍。如果你也在搭类似的导航系统,建议按这个顺序来:先把EKF调稳,再把AMCL调准,然后让move_base跑顺,最后加模糊避障兜底,一套下来会省掉很多弯路。