从零开始实现TEB局部轨迹规划算法(优化器基于ceres-solver)

TEB学习总结

TEB introduction

时间弹性:TEB算法考虑了机器人在路径规划过程中的运动时间,通过对路径特征进行建模,可以根据不同的速度限制和运动约束生成时间弹性的轨迹。这意味着算法可以根据需要自动调整运动速度和规划时间,以适应不同的应用场景和运动要求。

局部优化:TEB算法是一种局部路径规划算法,主要用于解决机器人在具体环境中的实时路径规划问题。它将机器人当前状态和目标状态作为输入,考虑机器人的动力学约束、障碍物避障和平滑性等因素,生成包含时间信息的平滑路径。

多目标支持:TEB算法能够有效处理机器人面临的多个目标点或轨迹约束。通过灵活的轨迹生成和优化策略,可以在考虑多个目标的情况下生成最优的运动轨迹。这对于需要在复杂环境中进行导航或任务执行的机器人特别有用。

高效性:TEB算法通过合理的优化策略和启发式方法,在保证路径质量的同时,能够实现较高的计算效率。这使得算法适合于实时机器人路径规划应用,并能够处理较复杂的环境和运动要求。

TEB parameters

X i = [ x i , y i , θ i , δ T i ] \mathbf{X_i} = [ x_i, y_i , \theta_i , \delta T_i ] Xi=[xi,yi,θi,δTi]

TEB constraints

safety constraint

f s a f e t y = { ( ∥ x i − o i ∥ − d m i n ) , if  ∥ x i − o i ∥ < d m i n 0 , else (1) f_{safety} = \begin{cases} (\mathbf{\|x_i - o_i\|}- d_{min}), & \text{if }\mathbf{\|x_i - o_i\|} < d_{min} \\ 0, & \text{else} \end{cases}\tag{1} fsafety={(xioidmin),0,if xioi<dminelse(1)
公式中 x i = [ x i , y i ] \mathbf{x_i} = [x_i,y_i] xi=[xi,yi]路径点, o i = [ o x , o y ] \mathbf{o_i} = [o_x, o_y] oi=[ox,oy] 是距离当前点最近的一个障碍物点, d m i n d_{min} dmin 是允许的障碍物和机器人的最小距离, 所以这个代价函数的目的就是计算每一个点和障碍物的距离,如果距离小于 d m i n d_{min} dmin 这个代价函数就会对整体代价引入新误差,这样计算梯度的时候就会把当前点往远离障碍物的方向踢,这里会有一个问题,就是当路径点被障碍物包围的时候, 优化就会很大概率会失败,因为障碍物附近的梯度如下图, 中间的白色区域是距离障碍物最远的,那么优化的就会把在障碍物里面的点往中间推, 把外面的关键点往远离障碍物的方向推, 优化的结果可能会变成右图(左图是优化前), 为了解决这个问题可以参考一下ego planner的论文

障碍物梯度

via-point constraint

f v i a = ∣ ∣ x i − p ∣ ∣ (2) f_{via} = ||\mathbf{x_i - p}|| \tag{2} fvia=∣∣xip∣∣(2)
公式中的 p \mathbf{p} p 是希望轨迹穿过的点,比如有三个点 p 1 , p 2 , p 3 \mathbf{p_1,p_2, p_3} p1,p2,p3,希望机器人在运动的时候经过,优化开始前首先需要找到距离这三个点最近的轨迹点,分别是 x 1 , x 2 , x 3 \mathbf{x_1,x_2,x_3} x1,x2,x3, 优化过程中代价函数只对这三个点起作用 x 1 , x 2 , x 3 \mathbf{x_1,x_2,x_3} x1,x2,x3,这种需要经过的点,如果是距离初始轨迹比较远的话,往往单次优化的效果都会很不好甚至可能收敛的结果很不好,因此最好是分多次优化,每次优化之前都需要根据需要调整轨迹点

velocity constraint

v i = ∣ ∣ x i + 1 − x i ∣ ∣ δ T i \begin{equation} v_i = \frac{\mathbf{||x_{i+1} - x_i||}}{\delta T_i} \tag{3} \end{equation} vi=δTi∣∣xi+1xi∣∣(3)
w i = N o r m a l i z e ( θ i + 1 − θ i ) δ T i \begin{equation} w_i = \frac{Normalize(\theta_{i+1} - \theta_i)}{\delta T_i} \tag{4} \end{equation} wi=δTiNormalize(θi+1θi)(4)
函数 N o r m a l i z e ( ) Normalize() Normalize()是把角度插值限定在[-pi,pi]之间,不然角速度就计算出错了, 线速度的计算不考虑正负的话直接求两个点的距离除时间就完事了,后续求加速度的时候才需要考虑正负线速度, 速度计算公式有了,对应的代价函数就好处理了
f l i n e a r _ v e l = { v i − v m a x if  v i > v m a x 0 , else (5) f_{linear\_vel} = \begin{cases} v_i - v_{max} & \text{if }v_i > v_{max} \\ 0, & \text{else} \end{cases}\tag{5} flinear_vel={vivmax0,if vi>vmaxelse(5)

f a n g u l a r _ v e l = { ∣ w i ∣ − w m a x if  ∣ w i ∣ > w m a x 0 , else (6) f_{angular\_vel} = \begin{cases} |w_i| - w_{max} & \text{if }|w_i| > w_{max} \\ 0, & \text{else} \end{cases}\tag{6} fangular_vel={wiwmax0,if wi>wmaxelse(6)

accleration constraint

v a c c i = 2 ( v i + 1 − v i ) δ T i + 1 + δ T i \begin{equation} vacc_i = \frac{2(v_{i+1} - v_i)}{\delta T_{i+1} + \delta T_i} \tag{7} \end{equation} vacci=δTi+1+δTi2(vi+1vi)(7)
这里求线性加速度的时候就需要考虑线速度的正负了, 这里可以考虑使用点乘的方法来确定线速度的方向, 点乘 a ⋅ b = ∥ a ∥ ∥ b ∥ c o s ( θ ) a\cdot b = \|a\|\|b\|cos(\theta) ab=a∥∥bcos(θ), 几何含义是向量a在b上的投影乘b的模, 根据点乘的结果的正负来判断速度的方向, 点乘为正,则速度为正,反之为负数, 其中 θ \theta θ x i x_i xi上的航向
速度的横向纵向分解

w a c c i = 2 ( w i + 1 − w i ) δ T i + 1 + δ T i \begin{equation} wacc_i = \frac{2(w_{i+1} - w_i)}{\delta T_{i+1} + \delta T_i} \tag{8} \end{equation} wacci=δTi+1+δTi2(wi+1wi)(8)
同样加速度公式有了,代价函数也就可以写了
f l i n e a r _ a c c = { ∣ v a c c i ∣ − v a c c m a x if  ∣ v a c c i ∣ > v a c c m a x 0 , else (9) f_{linear\_acc} = \begin{cases} |vacc_i| - vacc_{max} & \text{if }|vacc_i| > vacc_{max} \\ 0, & \text{else} \end{cases}\tag{9} flinear_acc={vaccivaccmax0,if vacci>vaccmaxelse(9)

f a n g u l a r _ a c c = { ∣ w a c c i ∣ − w a c c m a x if  ∣ w a c c i ∣ > w a c c m a x 0 , else (10) f_{angular\_acc} = \begin{cases} |wacc_i| - wacc_{max} & \text{if }|wacc_i| > wacc_{max} \\ 0, & \text{else} \end{cases}\tag{10} fangular_acc={wacciwaccmax0,if wacci>waccmaxelse(10)

non-holonomic kinematics

f k i n e m a t i c = ∣ ( cos ⁡ ( θ i + 1 ) + cos ⁡ ( θ i ) ) ⋅ d x [ 1 ] − ( sin ⁡ ( θ i + 1 ) + sin ⁡ ( θ i ) ) ⋅ d x [ 0 ] ∣ \begin{equation} f_{kinematic} = \left| (\cos(\theta_{i+1}) + \cos(\theta_i)) \cdot \mathbf{dx}[1] - (\sin(\theta_{i+1}) + \sin(\theta_i)) \cdot \mathbf{dx}[0] \right|\tag{11} \end{equation} fkinematic=(cos(θi+1)+cos(θi))dx[1](sin(θi+1)+sin(θi))dx[0](11)
公式中的 d x = x i + 1 − x i \mathbf{dx} = \mathbf{x_{i+1} - x_i} dx=xi+1xi, 这个代价函数的功能就是让参数 θ \theta θ要符合运动学约束,也就是把机器人的角度和位置给耦合在一起了,如果没有这个约束, θ , x i \theta, \mathbf{x_i} θ,xi这两个参数就不会有联系

fastest constraint

这个约束的目的就是让优化出来的轨迹在符合运动学约束的情况下, 轨迹的速度要尽可能大, 也就是 δ T i \delta T_i δTi尽可能小

f f a s t e s t = δ T i \begin{equation} f_{fastest} = \delta T_i\tag{12} \end{equation} ffastest=δTi(12)

shortest constraint

仅仅是有速度最快是不够的, 速度快不代表路径短,轨迹符合运动学的前提下,我们还希望轨迹越短越好

f f a s t e s t = ∣ ∣ x i − x i + 1 ∣ ∣ (13) \begin{equation} f_{fastest} = ||\mathbf{x_i} - \mathbf{x_{i+1}}|| \end{equation}\tag{13} ffastest=∣∣xixi+1∣∣(13)

jerk constraint

teb论文中是没有对机器人的jerk做约束的,但是实际使用上有的场合可能希望机器人运动的要平稳一点,速度不要突变, 所以我觉得jerk也是挺重要的

f j e r k = 3 ( a c c i + 1 − a c c i ) δ T i + δ T i + 1 + δ T i + 2 (14) \begin{equation} f_{jerk} = \frac{3(acc_{i+1} - acc_i)}{\delta T_i + \delta T_{i+1} + \delta T_{i+2}} \end{equation}\tag{14} fjerk=δTi+δTi+1+δTi+23(acci+1acci)(14)

least square problem

所有代价函数都写好啦,现在剩下的就是如何把这些代价函数给建模成最小二乘问题,下面公式15就是我自己建模后的结果,其中n是路径点的个数, m是需要经过的点的个数, 公式中的 x \mathbf{x} x需要根据代价函数来确定, 比如安全约束中 x = x i \mathbf{x = x_i} x=xi,线速度约束中 x = [ x i , x i + 1 ] \mathbf{x = [x_i, x_{i+1}]} x=[xi,xi+1], 运动学约束中 x = [ x i , x i + 1 , θ i , θ i + 1 ] \mathbf{x = [x_i, x_{i+1}, \theta_i, \theta_{i+1}]} x=[xi,xi+1,θi,θi+1]
min ⁡ x ∑ i = 0 n ∥ f i ( x ) ∣ 2 2 = ∑ i = 0 n f s a f e t y i ( x ) 2 + ∑ i = 0 m f v i a i ( x ) 2 + ∑ i = 0 n − 1 f l i n e a r _ v e l i ( x ) 2 + ∑ i = 0 n − 1 f a n g u l a r _ v e l i ( x ) 2 + ∑ i = 0 n − 2 f l i n e a r _ a c c i ( x ) 2 + ∑ i = 0 n − 2 f a n g u l a r _ a c c i ( x ) 2 + ∑ i = 0 n − 3 f j e r k i ( x ) 2 + ∑ i = 0 n − 1 f k i n e m a t i c i ( x ) 2 + ∑ i = 0 n − 1 f f a s t e s t i ( x ) 2 + ∑ i = 0 n − 1 f s h o r t e s t i ( x ) 2 + (15) \begin{aligned} \min_{\mathbf{x}} \sum_{i=0}^n \|f_i(\mathbf{x})|_2^2 = &\sum_{i=0}^{n} f^{i}_{safety}(\mathbf{x})^2 + \sum_{i=0}^{m} f^{i}_{via}(\mathbf{x})^2 + \sum_{i=0}^{n-1} f^{i}_{linear\_vel}(\mathbf{x})^2 + \sum_{i=0}^{n-1} f^{i}_{angular\_vel}(\mathbf{x})^2 +\\ &\sum_{i=0}^{n-2} f^{i}_{linear\_acc}(\mathbf{x})^2 + \sum_{i=0}^{n-2} f^{i}_{angular\_acc}(\mathbf{x})^2 + \sum_{i=0}^{n-3} f^{i}_{jerk}(\mathbf{x})^2 + \sum_{i=0}^{n-1} f^{i}_{kinematic}(\mathbf{x})^2 + \\ &\sum_{i=0}^{n-1} f^{i}_{fastest}(\mathbf{x})^2 + \sum_{i=0}^{n-1} f^{i}_{shortest}(\mathbf{x})^2 + \end{aligned}\tag{15} xmini=0nfi(x)22=i=0nfsafetyi(x)2+i=0mfviai(x)2+i=0n1flinear_veli(x)2+i=0n1fangular_veli(x)2+i=0n2flinear_acci(x)2+i=0n2fangular_acci(x)2+i=0n3fjerki(x)2+i=0n1fkinematici(x)2+i=0n1ffastesti(x)2+i=0n1fshortesti(x)2+(15)
teb开源算法使用的优化器是g2o,我觉得使用g2o太麻烦了,这里我采用的是ceres-solver的最小二乘求解器

experiment

本测试参数,最大角速度为0.35, 最大线速度为0.45, 最大角加速度和线加速度都是0.6
为了测试优化效果,我把优化时间,路径长度,线速度,角速度,加速度,加加速度的数据都画出来了

with jerk

在这里插入图片描述

在这里插入图片描述在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

从上述图片很容易看得出来, 哪一个是轨迹是比较平滑的,我跑过teb的源码, 从rviz上看他的结果,路径还是挺光滑的,但是没有速度加速度方面的数据,也不知道速度会不会存在突变的情况,我自己实现的如果代价函数不包括jerk,轨迹速度变化会比较剧烈,不知道是否是参数没有调好, 加了jerk后参数轨迹就很平滑了, teb这个算法还是挺简单的,主要是建模部分,优化部分也不需要自己动手,调包就完事儿了。这个算法的速度对硬件要求比较高,并且优化时长随着路径点数量的增多(dt越小),优化需要的时间会相应增加。如果路径总长度大约在3m左右,优化的平均时间可以维持在50ms,这是在X86笔记本上,如果在一些比较垃圾的板子上,时间估计就的100ms+了

为什么叫弹性带算法呢? 请看视频

vokoscreen-2023-11-03_15-52-32

reference

C. Rösmann, W. Feiten, T. Wösch, F. Hoffmann and T. Bertram: Trajectory modification considering dynamic constraints of autonomous robots. Proc. 7th German Conference on Robotics, Germany, Munich, May 2012, pp 74–79.

C. Rösmann, W. Feiten, T. Wösch, F. Hoffmann and T. Bertram: Efficient trajectory optimization using a sparse model. Proc. IEEE European Conference on Mobile Robots, Spain, Barcelona, Sept. 2013, pp. 138–143.

http://ceres-solver.org/tutorial.html

### Teb 算法的技术实现 #### 轨迹弹性带 (TEB) 方法概述 轨迹弹性带 (Time-Elastic Band, TEB) 是一种用于移动机器人局部路径规划的方法,其核心思想是在时间维度上引入弹性约束来优化机器人的运动轨迹。这种方法不仅考虑几何上的最优路径,还兼顾动态特性以及障碍物规避的需求[^1]。 #### 定义与表示形式 在 TEB 的框架下,每一段轨迹可以被建模为一组状态变量 \( \mathbf{X}_i = [x_i, y_i, \theta_i, \delta T_i] \),其中: - \( x_i, y_i \): 表示第 \( i \) 时刻的二维位置; - \( \theta_i \): 表示航向角; - \( \delta T_i \): 时间增量,代表从当前节点到下一节点的时间间隔。 这些状态通过一系列约束条件连接起来,形成完整的轨迹表达式[^3]。 #### 数据结构初始化 为了支持高效的计算过程,在程序设计阶段需先清理旧有的数据结构并重新构建新的规划环境。例如,调用 `clearPlanner()` 函数能够清空内部存储的地图信息和已生成的部分路径: ```cpp virtual void clearPlanner() { clearGraph(); teb_.clearTimedElasticBand(); } ``` 上述代码片段展示了如何释放内存资源以便于后续操作[^2]。 #### 障碍物规避机制 针对复杂场景中的碰撞检测问题,TEB 提供了一种基于足迹模型的安全保障措施。具体而言,它会评估潜在冲突区域并对相应部分施加排斥力以调整最终路线方向[^4]。 #### 使用 Ceres Solver 进行优化求解 由于整个系统的非线性和多目标性质,采用通用数值方法难以获得满意的结果;因此推荐利用 Google 开源库 **Ceres Solver** 来完成高精度迭代运算流程。此工具擅长处理大规模稀疏矩阵方程组,并具备良好的收敛性能特点适配于此类应用场合需求之中。 以下是简化版伪代码展示如何设置目标函数及其参数配置方式: ```cpp // 构造最小化问题实例对象 Problem problem; // 添加误差项至待解决列表当中去 for(auto& point : points){ CostFunction* cost_function = new AutoDiffCostFunction<MyErrorModel>(...); problem.AddResidualBlock(cost_function,...); } Solver::Options options; options.linear_solver_type = ceres::SPARSE_SCHUR; // 设置线性代数求解器类型 ... Solve(options,&problem,nullptr); // 执行最优化过程直至满足终止准则为止 ``` 以上内容涵盖了从零开始搭建一个基本功能完备版本所需的关键知识点和技术要点说明文档编写完毕之后还需要经过充分测试验证才能投入实际部署运行环境中使用前务必仔细阅读官方手册获取更多高级选项指导建议参考资料链接如下所示
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值