ARTICLE DETAIL

资讯详情

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

阿克曼小车TEB局部规划器参数调优实战:从原理到配置

阿克曼小车TEB局部规划器参数调优实战:从原理到配置 1. 写在前面阿克曼小车调TEB难在哪如果你玩过差速底盘再用阿克曼底盘跑ROS导航第一感觉大概率是“怎么突然不会走了”。差速车可以原地转圈、横向微调路径稍微歪一点也能硬掰回来阿克曼车不行前轮只能转有限角度倒车和前进的轨迹是完全不同的圆弧车子不能横移也不能原地掉头。这意味着底层规划器必须真正理解“最小转弯半径”和“轴距”这两个硬约束而不能简单地把小车当成一个点或者一个全向移动体来处理。TEB Local PlannerTimed Elastic Band时间弹性带是目前ROS里对阿克曼底盘支持最完整的局部规划器之一也是我在这类小车上用得最多、踩坑最多的规划器。它的核心思路是把全局路径“拉成”一条带时间信息的弹性带通过非线性优化同时考虑避障、时间最优、运动学约束输出符合底盘能力的速度指令。听起来很美好但实际跑起来参数稍微不合适小车就会表现出一系列让人头皮发麻的问题走S形、转弯转不过去、离障碍物太近、原地抽搐、急刹急加速……每个问题背后都对应着一组参数的关系没理顺。这篇文章我不会给你罗列一大堆默认参数而是从阿克曼底盘的实际需求出发把TEB里和底盘约束、轨迹优化、障碍物避碰相关的关键参数逐项拆开讲清楚说明“为什么这么调”最后附上一份我实测能稳定跑起来的完整配置文件。内容面向正在用阿克曼小车做自主导航、被局部规划器折腾到怀疑人生的朋友也适合刚把ROS环境搭好、准备开始接触导航的初学者参考。你需要对ROS基本概念节点、话题、tf有大概了解但不要求你提前精通TEB。2. TEB到底是怎么工作的为什么它对阿克曼这么重要2.1 从“时间弹性带”这个名字说起TEB的思路可以这样理解全局规划器比如NavFn、A*给出了一条从起点到终点的粗略路径这条路径可能很曲折、有棱角而且没有任何速度信息。TEB要做的事就是把这条路径拆成一串带时间戳的位姿序列然后把“位姿、时间间隔”当成优化变量去求解一个带约束的非线性优化问题。优化目标通常包含几个部分总时间尽量短也就是车尽量快、路径尽量平顺、尽量远离障碍物、满足速度/加速度限制、满足机器人的运动学模型。最后通过g2o或ceres求解得到一串新的、平滑的、带速度信息的轨迹再取其中一个靠前的位姿转换成线速度和角速度发给底盘。这个设计对阿克曼底盘非常友好因为运动学约束可以直接写进优化的约束条件里。比如“转弯半径不能小于某个值”“前轮转角不能超过某个范围”这些约束会在优化时被当作硬性限制去满足。相比之下DWA这类基于采样的规划器虽然也能跑阿克曼但对底盘模型的建模要粗放一些调起来更依赖经验和试探。2.2 阿克曼的运动学约束到底约束了什么阿克曼底盘在平面上运动时车辆会被限制在一族圆弧上。车辆转弯半径R与前轮转角δ、轴距L之间的关系近似为R ≈ L / tan(δ)举个例子轴距0.25米的小车前轮最大转角30度那么最小转弯半径大约是0.25 / tan(30°) ≈ 0.43米。也就是说这辆车在任何时刻都只能沿着半径不小于0.43米的圆弧运动不能更急地转弯。这就是阿克曼和差速的本质区别差速车转弯半径理论上可以为零原地旋转阿克曼车不行。如果规划器不理会这个约束仍然给出一段需要原地掉头的轨迹那底盘要么执行不了要么靠“边前进边急转弯”硬凑结果就是轨迹偏差大、车身姿态歪、甚至撞上障碍物。TEB里和这个直接相关的参数有两个wheelbase轴距和min_turning_radius最小转弯半径。前者告诉优化器“这台车的轴距是多少”后者告诉它“允许的最小转弯半径是多少”。如果这两个值设置得不准确TEB算出来的轨迹就会和真实底盘不匹配表现出的症状包括转弯时切内弯、过弯后姿态偏移、连续弯道走不好等。注意min_turning_radius不是随便估算的最好实际测量。把前轮打到最大角度让车以极低速度转一圈用卷尺测出外轮或车辆质心的转弯半径再留出10%~20%的余量填进去。这个参数直接决定TEB是否能规划出可执行的轨迹精度越准后面调其他权重就越省心。3. 调参前的准备环境、底盘模型、代价地图3.1 环境搭建推荐用鱼香ROS一键安装TEB调试本身不涉及环境安装但你总得先把ROS环境跑起来。如果你还在为ROS安装发愁我强烈建议试试搜索“鱼香ROS一键安装”这个工具它在国内环境下能把ROS安装流程简化很多尤其是网络不通畅时能省下大量折腾时间。我自己在Ubuntu 20.04和22.04上都用过支持ROS 1 Noetic和ROS 2 Humble装完基本可以直接用。装好ROS之后需要安装TEB相关的包。ROS 1 Noetic下通常是sudo apt install ros-noetic-teb-local-planner ros-noetic-costmap-converterROS 2 Humble下则是sudo apt install ros-humble-teb-local-planner ros-humble-costmap-converter当然如果你习惯源码编译也可以从GitHub拉取teb_local_planner仓库到工作空间里编译这种方式的好处是能随时改源码调试但对大多数人来说没必要。3.2 先把“车到底长什么样”告诉TEB这是我在调车时踩过的最深的一个坑模型参数对不上调半天权重都是白费。你需要确认三件事第一wheelbase轴距。用尺子量前轮中心到后轮中心之间的距离单位是米。第二min_turning_radius最小转弯半径按上面说的方法实测。第三footprint_model机器人轮廓也就是在俯视图上车体的外轮廓用于障碍物距离计算。如果车体是长方形建议用多边形描述不要偷懒用圆形否则通过窄门时明明两侧还有空间TEB却认为过不去。另外要确认底盘的里程计和速度指令模式。阿克曼底盘大多接受线速度 前轮转角指令或者线速度 角速度指令。TEB通过cmd_angle_instead_rotvel这个参数来区分如果你的底盘控制器接收的是前轮转角就设为true并在wheelbase中给出轴距TEB会自动把角速度换算成前轮转角。如果是接收角速度指令就保持false同时仍然建议正确设置轴距用于运动学约束计算。3.3 局部代价地图也得配合着看TEB和代价地图是密不可分的。TEB避障用的障碍物信息一部分来自局部代价地图一部分来自传感器直接投影。当你觉得“TEB离障碍物太近”或者“明明有空间但规划不出来”时不要只盯着TEB的参数先检查局部代价地图的膨胀半径和分辨率。局部代价地图里有两个关键参数resolution分辨率和inflation_radius膨胀半径。分辨率一般设成0.05米也就是每个栅格5厘米。膨胀半径建议比机器人的外接圆半径稍微大一点比如机器人半宽0.15米膨胀半径可以设0.2米左右。这里要注意TEB在优化时不仅考虑障碍物栅格本身还考虑代价值所以膨胀半径设得太大会让TEB误以为空间很窄路径会往后退缩设得太小又可能让车贴着障碍物走一旦里程计或传感器有误差就容易蹭到东西。4. 核心参数逐项拆解从一组能跑的配置开始4.1 机器人模型与输出控制方式TEB里和底盘模型直接相关的参数集中在这几项cmd_angle_instead_rotvel这个参数我建议阿克曼用户直接设为true。它的作用是让TEB输出前轮转角而不是角速度。很多阿克曼底盘驱动板直接接受前轮转角指令省去了在底层做换算的麻烦。如果你的底盘控制器只接受角速度那就保持false但一定要确认你的角速度到前轮转角的换算关系中轴距是正确的。wheelbase轴距单位米。前面反复强调过这里再说一次这个值必须实测。TEB内部计算转弯半径和角速度/转角换算时都依赖它差1厘米都会导致过弯姿态偏移。min_turning_radius最小转弯半径。这个参数对阿克曼来说几乎是“安全底线”。我见过有人把它设成0.1米结果小车在窄走廊里被规划出一条理论上不可能走出来的轨迹底盘拼命打方向也跟不上。建议设为实测最小转弯半径的1.1~1.2倍留出余量。footprint_model机器人轮廓描述。推荐用多边形polygon把车体四个角的坐标写上去。注意坐标是相对于机器人中心点的x向前、y向左。4.2 速度与加速度限制怎么给阿克曼小车的速度和加速度设置比差速车更需要“留一手”。max_vel_x最大前进速度。对于室内小车0.3~0.5米/秒是比较实用的区间。不要觉得设得越大越好速度越大TEB需要的规划前瞻距离就越长反应时间越短一旦出现动态障碍物刹车距离不够就很容易撞上。max_vel_x_backwards最大倒车速度。阿克曼车倒车时方向稳定性比前进差很多一般设成前进速度的一半左右比如0.15米/秒。太高会让优化器倾向于频繁倒车调整姿态看起来就像车在“抽搐”。max_vel_theta最大角速度。阿克曼车的角速度受最小转弯半径约束不是随便给的。你可以用这个公式估算最大角速度 ≈ 最大前进速度 / 最小转弯半径。如果最大前进速度0.4米/秒最小转弯半径0.4米那么最大角速度大约1.0弧度/秒。设得比这个值大TEB会规划出底盘执行不了的快速转弯。acc_lim_x和acc_lim_theta线加速度和角加速度。这两个值影响加减速的平滑度。常见误区是设得过大导致起步和刹车很“冲”设得过小TEB会认为加速太慢而不愿意提速导致轨迹整体变慢。经验值线加速度0.3~0.5米/秒²角加速度0.5~1.0弧度/秒²可以根据实际底盘的响应能力再微调。4.3 权重参数的平衡艺术TEB优化问题里每个目标项前面都有一个权重系数。权重越大优化器越优先满足这一项。理解这个逻辑调参就有方向了。weight_optimaltime时间最优权重。这个值越大TEB会越激进地压缩轨迹时间也就是让车跑得更快、转弯更急。阿克曼车上不建议设太大我一般从0.5~1.0起步。如果发现小车转弯时速度过快、姿态不稳就把这个权重降下来。weight_obstacle障碍物避碰权重。这个值要明显高于其他权重因为安全永远是第一位的。一般从50~100起步。如果发现小车在障碍物附近犹豫、路径明显往后退缩可以适当降低如果发现它离障碍物太近就增大。weight_kinematics_forward_drive前进方向权重。它惩罚“倒车”行为。阿克曼车正常行驶时应该尽量不倒车这个权重默认可以设4.0左右。如果车在走廊尽头或目标点附近频繁倒车调整可以再把这项调高一点让优化器更倾向于“往前走一点再转弯”而不是原地来回挪。weight_kinematics_turning_radius最小转弯半径约束权重。这个权重惩罚轨迹中那些小于min_turning_radius的转弯。阿克曼车上这个值必须够大建议5.0以上。如果这个权重设小了TEB会在优化时偷偷“忽略”转弯半径限制轨迹虽然看起来更顺滑但底盘根本执行不了。weight_shortest_path最短路径权重。这个值在阿克曼车上我建议直接设0。为什么因为它惩罚的是“轨迹总长度”而阿克曼车的轨迹长度和转弯半径约束往往是矛盾的。一条在欧几里得距离上更短的路径可能需要更急的转弯而一条稍微绕一点的路径对阿克曼车来说反而更平顺、更快。最大化最短路径权重会把阿克曼车又带回“差速思维”里去。4.4 障碍物距离与惩罚阈值TEB里有一组参数专门控制障碍物距离计算min_obstacle_dist轨迹与障碍物的最小距离。这个值要结合车体尺寸设。如果车体半宽0.15米这个值建议0.2~0.3米相当于给车留出5~15厘米的安全余量。太大会导致窄道通过性变差太小则容易蹭到障碍物。penalty_epsilon惩罚阈值。TEB计算障碍物代价时只有当轨迹上的点到障碍物的距离小于min_obstacle_dist penalty_epsilon时才开始惩罚。这个值可以理解为“警戒距离”。设0.1米左右比较合适。太大意味着TEB在离障碍物还很远时就开始“紧张”轨迹会明显向内收缩太小则会让车“贴脸”之后才反应容易来不及避让。obstacle_cost障碍物代价。这是障碍物项的惩罚系数与weight_obstacle配合使用。实际调的时候两者可以看作一个整体。增大obstacle_cost而weight_obstacle保持不变等价于增强避障优先级。include_costmap_obstacles是否将局部代价地图中的障碍物纳入优化。建议设为true。如果你发现TEB对传感器直接探测到的障碍物有反应但对地图里的静态障碍物无动于衷多半是这个参数被设成了false。4.5 时间分辨率和采样点数量TEB把轨迹离散成一系列带时间戳的位姿相邻位姿之间的时间间隔由dt_ref控制采样点数量则由min_samples和max_samples控制。dt_ref参考时间间隔单位秒。它决定相邻两个位姿之间的时间间隔。取值太小轨迹点会很密优化更精细但计算量大取值太大轨迹点稀疏优化粗糙可能出现轨迹“跳跃”。我一般从0.3秒起步。对于阿克曼车由于运动学约束比较复杂建议不要把dt_ref设得太小否则优化器需要更多迭代才能收敛。dt_hysteresis时间间隔滞回。TEB会在dt_ref和dt_hysteresis之间动态调整位姿间距避免频繁改变采样密度。一般设0.1秒即可。teb_autosize是否自动调整采样点数量。阿克曼车建议设true。当TEB检测到位姿数量过多或过少时会自动增减采样点让轨迹长度保持在一个合理范围内。设成false后你得手动维护采样点数量对新手不友好。max_global_lookahead_dist全局路径前瞻距离。这个值控制TEB从全局路径上取多长一段作为优化目标。阿克曼车因为转弯半径大需要更长的前瞻距离才能提前规划转弯一般设1.5~3.0米。设得太小车到了弯道口才开始转弯很容易转不过去设得太大计算负担增加而且容易跟着全局路径的远端波动走。goal_tolerance到达目标点的容差。阿克曼车没法精确“怼”到点通常设0.1~0.2米。如果设太小车会在目标点附近反复调整姿态看起来很傻。5. 完整配置文件参考阿克曼专用5.1 teb_local_planner_params.yaml下面这份配置是我在一台轴距0.25米、最小转弯半径约0.45米、车体尺寸0.5米×0.3米的阿克曼小车上实测过的。不同底盘需要根据第4节的说明调整具体数值但整体结构可以直接套用TebLocalPlannerROS: # 话题与坐标系 odom_topic: odom map_frame: map # 机器人模型 cmd_angle_instead_rotvel: true # 阿克曼专用输出前轮转角而不是角速度 wheelbase: 0.25 # 轴距务必实测 min_turning_radius: 0.45 # 最小转弯半径实测后留余量 footprint_model: type: polygon # 多边形描述车体轮廓 vertices: [[-0.25, -0.15], [-0.25, 0.15], [0.25, 0.15], [0.25, -0.15]] # 运动学约束 max_vel_x: 0.4 # 最大前进速度 m/s max_vel_x_backwards: 0.15 # 最大倒车速度 m/s max_vel_theta: 1.0 # 最大角速度 rad/s约等于0.4/0.4 acc_lim_x: 0.35 # 线加速度 m/s^2 acc_lim_theta: 0.6 # 角加速度 rad/s^2 # 优化目标权重 weight_optimaltime: 1.0 # 时间最优权重车太冲就调小 weight_obstacle: 100.0 # 障碍物避碰权重安全第一 weight_inflation: 0.2 # 膨胀区域权重影响在障碍物附近的犹豫程度 weight_kinematics_forward_drive: 4.0 # 前进方向权重防止频繁倒车 weight_kinematics_turning_radius: 5.0 # 转弯半径约束权重必须够大 weight_kinematics_optimize_turning_radius: 0.0 weight_shortest_path: 0.0 # 阿克曼车不用最短路径权重 weight_angle_diff: 1.0 # 目标朝向权重 # 障碍物参数 min_obstacle_dist: 0.22 # 最小障碍物距离 penalty_epsilon: 0.1 # 警戒距离 obstacle_cost: 300.0 # 障碍物代价 inflate_obstacle_cost: 50.0 # 膨胀区域代价 include_costmap_obstacles: true # 使用局部代价地图的障碍物 costmap_obstacles_behind_robot_dist: 1.0 # 车后方障碍物纳入优化的距离 obstacle_poses_affected: 20 # 障碍物影响的位姿数量 inflation_dist: 0.3 # 膨胀距离 # 轨迹时间分辨率 teb_autosize: true dt_ref: 0.3 # 位姿间参考时间间隔 dt_hysteresis: 0.1 min_samples: 10 max_samples: 500 # 全局路径与目标 max_global_lookahead_dist: 2.5 # 前瞻距离阿克曼建议大一点 force_reinit_new_goal_dist: 1.0 goal_tolerance: 0.15 # 目标容差 # 优化器 no_inner_iterations: 5 no_outer_iterations: 4 oscillation_reduction: true # 振荡抑制防止S形摆动 penalty_epsilon: 0.1 # 频率 controller_frequency: 10.05.2 局部代价地图的关键配置TEB再强也依赖代价地图提供障碍物信息。下面这份局部代价地图配置可以配合上面的TEB参数使用local_costmap: global_frame: odom robot_base_frame: base_link update_frequency: 10.0 publish_frequency: 10.0 transform_tolerance: 0.5 static_map: false rolling_window: true width: 6.0 height: 6.0 resolution: 0.05 inflation_radius: 0.25 plugins: - {name: obstacle_layer, type: costmap_2d::ObstacleLayer} - {name: inflation_layer, type: costmap_2d::InflationLayer} obstacle_layer: observation_sources: laser_scan laser_scan: data_type: LaserScan topic: scan marking: true clearing: true obstacle_range: 3.0 raytrace_range: 4.0 inflation_layer: inflation_radius: 0.25 cost_scaling_factor: 3.0inflation_radius设成0.25米cost_scaling_factor设成3.0是为了让膨胀区域有个平缓的过渡带。TEB在优化时会结合这个代价值来判断“离障碍物多近算是危险”如果膨胀太突兀轨迹容易出现急转。6. 实测问题与调整实录6.1 小车走S形振荡抑制没生效症状直线行驶时小车沿路径左右摆动像喝醉了一样。速度越快越明显。排查思路先确认oscillation_reduction是否设为了true。如果已经是true再看max_vel_theta角速度上限过大会让优化器在横向偏差面前“反应过度”。把max_vel_theta从1.0降到0.6同时把weight_kinematics_forward_drive从4.0提到5.0S形会明显缓解。另外一个容易忽略的地方dt_ref如果设得太小比如0.2以下优化器会过度拟合全局路径的微小波动也容易诱发摆动。适当增大dt_ref到0.3~0.4轨迹会更“钝”一些自然就不摆了。6.2 转向过早、转弯半径不够症状小车在进弯前就开始大幅度转向或者在弯道中明显跟不上全局路径的弧度冲出路径。排查思路这个问题首先要检查min_turning_radius和weight_kinematics_turning_radius。如果最小转弯半径填得比实际值小TEB会认为车能转更急的弯结果就是转弯时切内弯、甚至压到路边。同时确认wheelbase的数值是否准确。然后看max_global_lookahead_dist。阿克曼车是需要“提前量”的如果你设的是1.0米那车在接近弯道时才开始规划转弯留给优化器的响应空间太小。我在实测中把max_global_lookahead_dist从1.5提到2.5之后过弯平顺度提升了非常多。6.3 过窄道时来回摆动、不敢走症状小车面对一扇只比车体宽20厘米的门明明空间够却在门口反复进退始终不进去。排查思路大概率是min_obstacle_dist或者膨胀半径设得太大。如果min_obstacle_dist是0.3米车体半宽0.15米那就等于要求门两侧各留0.45米门宽至少0.9米才肯过。我的做法是先把min_obstacle_dist降到0.18~0.2米同时把inflation_radius从0.3降到0.25然后再试。如果调完还是犹豫可以看下weight_obstacle是不是太高了。优化器在障碍物代价极高的情况下会宁愿多绕路或者原地等待也不愿意从窄缝里挤过去。把weight_obstacle从100调到80左右往往能松一口气。6.4 其他常见问题速查表症状最可能的参数原因建议调整起步/刹车很冲acc_lim_x过大降到0.3以下到达目标点后原地“抽搐”goal_tolerance过小增大到0.15~0.2米倒车太频繁、姿态很难看weight_kinematics_forward_drive过小提高到5.0以上动态避障太迟缓max_global_lookahead_dist过小增大到2.5米以上轨迹在障碍物附近大幅内缩weight_obstacle过高或inflation_radius过大降低权重或减小膨胀规划器偶尔输出剧烈抖动dt_ref过小或max_vel_theta过大增大dt_ref到0.3降max_vel_theta7. 最后再分享一个小技巧我在调阿克曼小车的时候有一个习惯调完任何一组参数都会先让车在空旷区域跑一个简单的直线U形弯测试确认基本运动学约束没被突破再进障碍物密集区。很多朋友一上来就直奔“复杂工况”结果几个问题叠加在一起根本分不清是哪个参数导致的。先易后难、每次只动一个参数这是我踩了无数坑之后最想强调的。另外调试时一定要把/teb_local_planner/teb_posesTEB生成的轨迹点和/teb_local_planner/teb_marker障碍物约束标记可视化出来。光看车的运动轨迹很难准确判断问题但把规划器内部生成的轨迹拉出来看一眼就能看出是“障碍物约束太强导致轨迹缩成一团”还是“时间最优权重太高导致轨迹追求极限速度”。直观、省时比盲调参数高效得多。阿克曼小车调TEB没有一劳永逸的“万能参数表”每一台车的轴距、转弯半径、底盘响应速度都不一样。但只要理解了每个参数背后对应的物理意义把这篇文章里的配置作为起点一步一步按实际表现去微调你的小车一定能跑得又稳又顺畅。
返回列表