Apollo决策规划 - EM planner
旨在对b站老王所讲的百度Apollo - EM planner算法做浓缩版总结
0 决策规划背景
基于图搜索
优点:
可以得到全局层面最优解,适用于比较低维数的规划问题
缺点:
规划问题维数较高时,面临指数爆炸问题
基于采样
优点:
计算量低,曲率连续,行驶平顺性好
缺点:
曲线通常是四阶或者更高,系数的计算难以使车辆到达确定的运动状态
基于插值的曲率规划
优点:
算法总是可以收敛到满足条件的解,适用于全局规划和局部规划
缺点:
产生的轨迹不连续而且不平稳,路径的最优性受时间影响
基于函数优化
优点:
影响因素考虑多,包含道路限制条件、自车限制,便于处理约束,可以满足不同优化目标,求解速度快
缺点:
最优解一般比较贴近约束,转化为优化问题本身较为困难
自动驾驶总共分成了六个等级:L0 - L5
等级 | 详述 |
---|---|
L0 | 没有任何自动驾驶功能 |
L1 | 有横向和纵向的自动驾驶功能,但是横纵向无法联合作用 |
L2 | 横纵向可以联合作用,但是驾驶员必须对一切情况负责 |
L3 | 功能和L2基本相同,最大的区别在于责任,对于部分场景,驾驶员不必负责 |
L4 | 大部分道路皆可自动驾驶,大部分场景都不需要驾驶员负责 |
L5 | 完全自动驾驶 |
EM Planner是Apollo的经典决策规划算法,擅长处理复杂环境下的决策规划问题。
2 规划需要的背景知识
2.1 五次多项式
- 位置
x ( t ) = a 0 + a 1 t + a 2 t 2 + a 3 t 3 + a 4 t 4 + a 5 t 5 y ( t ) = b 0 + b 1 t + b 2 t 2 + b 3 t 3 + b 4 t 4 + b 5 t 5 x(t) = a_{0}+a_{1}t+a_{2}t^2+a_{3}t^3+a_{4}t^4+a_{5}t^5\\ y(t) = b_{0}+b_{1}t+b_{2}t^2+b_{3}t^3+b_{4}t^4+b_{5}t^5 x(t)=a0+a1t+a2t2+a3t3+a4t4+a5t5y(t)=b0+b1t+b2t2+b3t3+b4t4+b5t5 - 速度
x ˙ ( t ) = a 1 + 2 a 2 t + 3 a 3 t 2 + 4 a 4 t 3 + 5 a 5 t 4 y ˙ ( t ) = b 1 + 2 b 2 t + 3 b 3 t 2 + 4 b 4 t 3 + 5 b 5 t 4 \dot{x} (t)=a_{1}+2a_{2}t+3a_{3}t^2+4a_{4}t^3+5a_{5}t^4\\ \dot{y} (t)=b_{1}+2b_{2}t+3b_{3}t^2+4b_{4}t^3+5b_{5}t^4 x˙(t)=a1+2a2t+3a3t2+4a4t3+5a5t4y˙(t)=b1+2b2t+3b3t2+4b4t3+5b5t4 - 加速度
x ¨ ( t ) = 2 a 2 + 6 a 3 t + 12 a 4 t 2 + 20 a 5 t 3 y ¨ ( t ) = 2 b 2 + 6 b 3 t + 12 b 4 t 2 + 20 b 5 t 3 \ddot{x} (t)=2a_{2}+6a_{3}t+12a_{4}t^2+20a_{5}t^3\\ \ddot{y} (t)=2b_{2}+6b_{3}t+12b_{4}t^2+20b_{5}t^3 x¨(t)=2a2+6a3t+12a4t2+20a5t3y¨(t)=2b2+6b3t+12b4t2+20b5t3
化为矩阵形式。
X = [ x ( 0 ) x ( 0 ) ˙ x ( 0 ) ¨ x ( T ) x ( T ) ˙ x ( T ) ¨ ] = [ t 0 5 t 0 4 t 0 3 t 0 2 t 0 1 5 t 0 4 4 t 0 3 3 t 0 2 2 t 0 1 0 20 t 0 3 12 t 0 2 6 t 0 2 0 0 t 1 5 t 1 4 t 1 3 t 1 2 t 1 1 5 t 1 4 4 t 1 3 3 t 1 2 2 t 1 1 0 20 t 1 3 12 t 1 2 6 t 1 2 0 0 ] [ a 5 a 4 a 3 a 2 a 1 a 0 ] = T X A X=\begin{bmatrix}x(0) \\\dot{x(0)} \\\ddot{x(0)} \\x(T) \\\dot{x(T)} \\\ddot{x(T)} \end{bmatrix}=\begin{bmatrix} t_{0}^5& t_{0}^4 & t_{0}^3 & t_{0}^2 & t_{0} &1 \\ 5t_{0}^4 & 4t_{0}^3 & 3t_{0}^2 & 2t_{0} & 1 & 0\\ 20t_{0}^3 & 12t_{0}^2 & 6t_{0} & 2 & 0 & 0\\ t_{1}^5& t_{1}^4 & t_{1}^3 & t_{1}^2 & t_{1}& 1\\ 5t_{1}^4 & 4t_{1}^3 & 3t_{1}^2 & 2t_{1} & 1 & 0\\ 20t_{1}^3 & 12t_{1}^2 & 6t_{1} & 2 & 0 & 0 \end{bmatrix}\begin{bmatrix}a_{5} \\a_{4} \\a_{3} \\a_{2} \\a_{1} \\a_{0} \end{bmatrix}=TXA X= x(0)x(0)˙x(0)¨x(T)x(T)˙x(T)¨ = t055t0420t03t155t1420t13t044t0312t02t144t1312t12t033t026t0t133t126t1t022t02t122t12t010t110100100 a5a4a3a2a1a0 =TXA
Y = [ y ( 0 ) y ( 0 ) ˙ y ( 0 ) ¨ y ( T ) y ( T ) ˙ y ( T ) ¨ ] = [ t 0 5 t 0 4 t 0 3 t 0 2 t 0 1 5 t 0 4 4 t 0 3 3 t 0 2 2 t 0 1 0 20 t 0 3 12 t 0 2 6 t 0 2 0 0 t 1 5 t 1 4 t 1 3 t 1 2 t 1 1 5 t 1 4 4 t 1 3 3 t 1 2 2 t 1 1 0 20 t 1 3 12 t 1 2 6 t 1 2 0 0 ] [ a 5 b 4 b 3 b 2 b 1 b 0 ] = T X B Y=\begin{bmatrix}y(0) \\\dot{y(0)} \\\ddot{y(0)} \\y(T) \\\dot{y(T)} \\\ddot{y(T)} \end{bmatrix}=\begin{bmatrix} t_{0}^5& t_{0}^4 & t_{0}^3 & t_{0}^2 & t_{0} &1 \\ 5t_{0}^4 & 4t_{0}^3 & 3t_{0}^2 & 2t_{0} & 1 & 0\\ 20t_{0}^3 & 12t_{0}^2 & 6t_{0} & 2 & 0 & 0\\ t_{1}^5& t_{1}^4 & t_{1}^3 & t_{1}^2 & t_{1}& 1\\ 5t_{1}^4 & 4t_{1}^3 & 3t_{1}^2 & 2t_{1} & 1 & 0\\ 20t_{1}^3 & 12t_{1}^2 & 6t_{1} & 2 & 0 & 0 \end{bmatrix}\begin{bmatrix}a_{5} \\b_{4} \\b_{3} \\b_{2} \\b_{1} \\b_{0} \end{bmatrix}=TXB Y= y(0)y(0)˙y(0)¨y(T)y(T)˙y(T)¨ = t055t0420t03t155t1420t13t044t0312t02t144t1312t12t033t026t0t133t126t1t022t02t122t12t010t110100100 a5b4b3b2b1b0 =TXB
X , Y , T X,Y,T X,Y,T已知,就可以求出 A A A和 B B B。
衡量舒适性的物理量为跃度 J e r k Jerk Jerk
J e r k = d a d t {Jerk = \frac{da}{dt}} Jerk=dtda
J e r k Jerk Jerk的绝对值越小, a a a的变化越平缓,意味着越舒适。
find f ( t ) f(t) f(t) 使得 ∫ 0 T ( d 3 f d t 3 ) 2 d t {\normalsize \int_{0}^{T} (\frac{d^3f}{dt^3})^2dt} ∫0T(dt3d3f)2dt最小
subject to :
S ( 0 ) = S 0 , S ( T ) = S n S ˙ ( 0 ) = v 0 , S ˙ ( T ) = v n S ¨ ( 0 ) = a 0 , S ¨ ( T ) = a n S(0)=S_{0},S(T)=S_{n}\\ \dot S(0)=v_{0},\dot S(T)=v_{n}\\ \ddot S(0)=a_{0},\ddot S(T)=a_{n} S(0)=S0,S(T)=SnS˙(0)=v0,S˙(T)=vnS¨(0)=a0,S¨(T)=an
显然 f ( t ) f(t) f(t)只可能是在 [ 0 , T ] [0,T] [0,T]上是有界连续函数,因为无论是无界函数,还是有界间断函数都会使 J e r k Jerk Jerk出现无穷大
f ( t ) f(t) f(t):五次多项式
化简约束:
S ( T ) − S ( 0 ) = ∫ 0 T f ˙ d t = C 0 S ˙ ( T ) − S ˙ ( 0 ) = ∫ 0 T f ¨ d t = C 1 S ¨ ( T ) − S ¨ ( 0 ) = ∫ 0 T f ( 3 ) d t = C 2 S(T)-S(0)=\int_{0}^{T} \dot fdt=C_{0}\\ \dot S(T)-\dot S(0)=\int_{0}^{T} \ddot fdt=C_{1}\\ \ddot S(T)-\ddot S(0)=\int_{0}^{T} {f}^{(3)} dt=C_{2} S(T)−S(0)=∫0Tf˙dt=C0S˙(T)−S˙(0)=∫0Tf¨dt=C1S¨(T)−S¨(0)=∫0Tf(3)dt=C2
问题描述:
问题变为求 ∫ 0 T f ( 3 ) d t \int_{0}^{T} {f}^{(3)} dt ∫0Tf(3)dt在约束 ∫ 0 T ( f ˙ − C 0 T ) d t = 0 , ∫ 0 T ( f ¨ − C 1 T ) d t = 0 , ∫ 0 T ( f ( 3 ) − C 2 T ) d t = 0 \int_{0}^{T} (\dot f-\frac{C_{0}}{T})dt=0,\int_{0}^{T} (\ddot f-\frac{C_{1}}{T})dt=0,\int_{0}^{T} ({f}^{(3)}-\frac{C_{2}}{T})dt=0 ∫0T(f˙−TC0)dt=0,∫0T(f¨−TC1)dt=0,∫0T(f(3)−TC2)dt=0下的极小值
拉格朗日法:
∫ 0 T f ( 3 ) d t + λ 1 ∫ 0 T ( f ˙ − C 0 T ) d t + λ 2 ∫ 0 T ( f ¨ − C 1 T ) d t + λ 3 ∫ 0 T ( f ( 3 ) − C 2 T ) d t = ∫ 0 T ( λ 1 f ˙ + λ 2 f ¨ + λ 3 f ( 3 ) + f ( 3 ) 2 − λ 1 C 0 T − λ 2 C 1 T − λ 3 C 2 T ) d t = ∫ 0 T L d t \int_{0}^{T} {f}^{(3)}dt+ \lambda _{1}\int_{0}^{T} (\dot f-\frac{C_{0}}{T})dt+\lambda _{2}\int_{0}^{T} (\ddot f-\frac{C_{1}}{T})dt+\lambda _{3}\int_{0}^{T} ({f}^{(3)}-\frac{C_{2}}{T})dt\\ =\ \int_{0}^{T} (\lambda _{1}\dot f +\lambda _{2}\ddot f+\lambda _{3}{f}^{(3)} +{{f}^{(3)}}^2- \lambda _{1}\frac{C_{0}}{T} -\lambda _{2}\frac{C_{1}}{T}-\lambda _{3}\frac{C_{2}}{T})dt\ =\int_{0}^{T} Ldt ∫0Tf(3)dt+λ1∫0T(f˙−TC0)dt+λ2∫0T(f¨−TC1)dt+λ3∫0T(f(3)−TC2)dt= ∫0T(λ1f˙+λ2f¨+λ3f(3)+f(3)2−λ1TC0−λ2TC1−λ3TC2)dt =∫0TLdt
用到广义 E u l e r − L a g r a r g e Euler-Lagrarge Euler−Lagrarge方程
∂ L ∂ f − d d t ( ∂ L ∂ f ˙ ) + d 2 d t 2 ( ∂ L ∂ f ¨ ) − d 3 d t 3 ( ∂ L ∂ f ( 3 ) ) = 0 \frac{\partial L}{\partial f} -\frac{d}{dt} (\frac{\partial L}{\partial \dot f} )+\frac{d^2}{dt^2} (\frac{\partial L}{\partial \ddot f})-\frac{d^3}{dt^3} (\frac{\partial L}{\partial {f}^{(3)}})=0 ∂f∂L−dtd(∂f˙∂L)+dt2d2(∂f¨∂L)−dt3d3(∂f(3)∂L)=0
将 ∂ L ∂ f = 0 , ∂ L ∂ f ˙ = λ 1 , ∂ L ∂ f ¨ = λ 2 , ∂ L ∂ f ( 3 ) = λ 3 + 2 f ( 3 ) \frac{\partial L}{\partial f} =0,\frac{\partial L}{\partial \dot f} =\lambda _{1},\frac{\partial L}{\partial \ddot f} =\lambda _{2},\frac{\partial L}{\partial {f}^{(3)} } =\lambda _{3}+2{f}^{(3)} ∂f∂L=0,∂f˙∂L=λ1,∂f¨∂L=λ2,∂f(3)∂L=λ3+2f(3)代入广义 E u l e r − L a g r a r g e Euler-Lagrarge Euler−Lagrarge方程得:
d 3 d t 3 ( λ 3 + 2 f ( 3 ) ) = 0 \frac{d^3}{dt^3}(\lambda _{3}+2{f}^{(3)})=0 dt3d3(λ3+2f(3))=0
推导过程:
f ( 6 ) ( t ) = 0 f ( 5 ) ( t ) = a 0 f ( 4 ) ( t ) = a 0 t + a 1 f ( 3 ) ( t ) = 1 2 a 0 t 2 + a 1 t + a 2 f ( 2 ) ( t ) = 1 6 a 0 t 3 + 1 2 a 1 t 2 + a 2 t + a 3 f ( 1 ) ( t ) = 1 24 a 0 t 4 + 1 6 a 1 t 3 + 1 2 a 2 t + a 3 t + a 4 f ( t ) = 1 120 a 0 t 5 + 1 24 a 1 t 4 + 1 6 a 2 t 3 + 1 2 a 3 t 2 + a 4 t + a 5 \begin{array}{l} f^{(6)}(t)=0 \\ f^{(5)}(t)=a_{0} \\ f^{(4)}(t)=a_{0} t+a_{1} \\ f^{(3)}(t)=\frac{1}{2} a_{0} t^{2}+a_{1} t+a_{2} \\ f^{(2)}(t)=\frac{1}{6} a_{0} t^{3}+\frac{1}{2} a_{1} t^{2}+a_{2} t+a_{3} \\ f^{(1)}(t)=\frac{1}{24} a_{0} t^{4}+\frac{1}{6} a_{1} t^{3}+\frac{1}{2} a_{2} t+a_{3} t+a_{4} \\ \\ \LARGE f(t)=\frac{1}{120} a_{0} t^{5}+\frac{1}{24} a_{1} t^{4}+\frac{1}{6} a_{2} t^3+\frac{1}{2} a_{3} t^2+a_{4} t+a_{5} \end{array} f(6)(t)=0f(5)(t)=a0f(4)(t)=a0t+a1f(3)(t)=21a0t2+a1t+a2f(2)(t)=61a0t3+21a1t2+a2t+a3f(1)(t)=241a0t4+61a1t3+21a2t+a3t+a4f(t)=1201a0t5+241a1t4+61a2t3+21a3t2+a4t+a5
2.2 凸优化和非凸优化
① 自动驾驶规划目标:算出一条满足约束的最优轨迹
衡量轨迹质量往往用cost function表示,代价函数越小,意味着越平滑,越舒适,当然也要满足各种约束。
指标:平滑、舒适、尽可能短
约束:轨迹连续、无碰撞、交规、车辆动力学
② 凸优化性质
(a) 代价函数只有一个极小值点且为最小值(凸函数)
(b) 约束空间是一块完整的、不破碎的空间(凸空间)
凸优化:求凸函数在凸空间的最小值问题
③ 自动驾驶避障约束空间是非凸的
(a) 静态和动态避障约束空间都不是凸空间
静态避障:
动态避障:
(b) 求解非凸问题的主要思路是找非凸问题中的凸结构
非凸空间 => 离散化 => 粗解(base) => 迭代 => 最终解
启发式算法:
先随机在约束空间采样一些离散的函数值比大小,取最小的作为迭代初值。
2.3 直角坐标与自然坐标转换
龙格现象:高次多项式拟合可能会出现震荡,慎用高次多项式。
以Cartesian坐标为准
r n ⃗ \vec{r_n} rn:车的位矢
v ⃗ \vec{v} v:车的速度
a ⃗ \vec{a} a:车的加速度
k h k_h kh:车的位矢在车的轨迹上的曲率
τ h ⃗ \vec{\tau _h} τh:车的位矢在车的轨迹上的切线方向单位向量
n h ⃗ \vec{ n_h} nh:车的位矢在车的轨迹上的法线方向单位向量
r r ⃗ \vec{r_r} rr:投影位矢
S ˙ \dot S S˙:投影速率
k r k_r kr:投影位矢在道路几何上的曲率
τ r ⃗ \vec{\tau _r} τr:投影位矢在道路几何上的切线方向单位向量
n r ⃗ \vec{n _r} nr:投影位矢在道路几何上的法线方向单位向量
已知 r ⃗ h , v ⃗ h , a ⃗ h , k h , τ ⃗ h , n ⃗ h \vec{r}_{h}, \vec{v}_{h}, \vec{a}_{h}, k_{h}, \vec{\tau}_{h}, \vec{n}_{h} rh,vh,ah,kh,τh,nh
己知 frenet 坐标系起点 ( x 0 , y 0 ) \left(x_{0}, y_{0}\right) (x0,y0)
求 s , s ˙ , s ¨ , l , l ′ , l ′ ′ s ˙ = d s d t l ′ = d l d s s, \dot{s}, \ddot{s}, l, l^{\prime}, l^{\prime \prime} \quad \dot{s}=\frac{d s}{d t} \quad l^{\prime}=\frac{d l}{d s} \quad s,s˙,s¨,l,l′,l′′s˙=dtdsl′=dsdl d s ds ds 为frenet坐标轴的弧长
主要思路
:
(1)找车在Frenet坐标系下的投影点在笛卡尔坐标下的坐标,记为 ( x r , y r , k r , θ r ) (x_r,y_r,k_r,\theta_r) (xr,yr,kr,θr)
(2)利用向量三角形,以及微积分求 s , s ˙ , s ¨ , l , l ′ , l ′ s,\dot s,\ddot s,l,l_{}',l_{}' s,s˙,s¨,l,l′,l′
核心公式
: r ⃗ r + l n ⃗ r = r ⃗ h \vec r_r+l\vec n_r=\vec r_h rr+lnr=rh
7个辅助公式:
r ˙ → h = v ⃗ = ∣ v ⃗ ∣ τ ⃗ h r ˙ ⃗ r = s ˙ τ ⃗ r τ ⃗ h = k h ∣ v ⃗ ∣ n ⃗ h n ⃗ h = − k h ∣ v ⃗ ∣ τ ⃗ h τ ˙ ⃗ r = k r s ˙ n ⃗ r n ˙ → r = − k r s ˙ τ ⃗ r a ⃗ = ∣ v ˙ ⃗ ∣ τ ⃗ h + ∣ v ⃗ ∣ 2 k h n ⃗ h \begin{array}{l} \overrightarrow{\dot{r}}_{h}=\vec{v}=|\vec{v}| \vec{\tau}_{h} \\ \vec{\dot r}_{r}=\dot{s} \vec{\tau}_{r} \\ \vec{\tau}_{h}=k_{h}|\vec{v}| \vec{n}_{h} \\ \vec{n}_{h}=-k_{h}|\vec{v}| \vec{\tau}_{h} \\ \vec{\dot \tau}_{r}=k_{r} \dot{s} \vec{n}_{r} \\ \overrightarrow{\dot{n}}_{r}=-k_{r} \dot{s} \vec{\tau}_{r} \\ \vec{a}=|\vec{\dot v}| \vec{\tau}_{h}+|\vec{v}|^{2} k_{h} \vec{n}_{h} \end{array} r˙h=v=∣v∣τhr˙r=s˙τrτh=kh∣v∣nhnh=−kh∣v∣τhτ˙r=krs˙nrn˙r=−krs˙τra=∣v˙∣τh+∣v∣2khnh
向量法详细推导见博客
s , s ˙ , s ¨ , l , l ˙ , l ¨ , l ′ , l ′ ′ s,\dot s,\ddot s,l,\dot l,\ddot l,l_{}',l_{}'' s,s˙,s¨,l,l˙,l¨,l′,l′′八个变量,独立的只有6个,因为 l ˙ , l ¨ \dot l,\ddot l l˙,l¨可以由 l ′ , l ′ ′ l_{}',l_{}'' l′,l′′推导过来
l ˙ = l ′ ∗ s ˙ l ¨ = d l d t = d ( l ′ s ˙ ) d t = d l ′ d t s ˙ + l ′ ⋅ s ¨ = d l ′ d s ⋅ d s d t ⋅ s ˙ + l ′ s ¨ = l ′ ′ s ˙ 2 + l ′ s ¨ \dot l= l'*\dot s\\ \\ \begin{aligned} \ddot{l}=\frac{d l}{d t}=\frac{d\left(l^{\prime} \dot{s}\right)}{d t} & =\frac{d l^{\prime}}{d t} \dot{s}+l^{\prime} \cdot \ddot{s} \\ & =\frac{d l^{\prime}}{d s} \cdot \frac{d s}{d t} \cdot \dot{s}+l^{\prime} \ddot{s} \\ & =l^{\prime \prime} \dot{s}^{2}+l^{\prime} \ddot{s} \end{aligned} l˙=l′∗s˙l¨=dtdl=dtd(l′s˙)=dtdl′s˙+l′⋅s¨=dsdl′⋅dtds⋅s˙+l′s¨=l′′s˙2+l′s¨
EM planner: s , s ˙ , s ¨ , l , l ′ , l ′ s,\dot s,\ddot s,l,l_{}',l_{}' s,s˙,s¨,l,l′,l′
Lattice planner: s , s ˙ , s ¨ , l , l ˙ , l ¨ s,\dot s,\ddot s,l,\dot l,\ddot l s,s˙,s¨,l,l˙,l¨
s的计算方法
:
主要思路:
用直线长度代替弧长,当路径点足够密集,误差可以忽略
这里直接给出Frenet和Cartesian互转的公式:
百度Apollo Frenet和Cartesian互转的代码:
modules/common/math/cartesian_frenet_conversion.h
static void cartesian_to_frenet(const double rs, const double rx,const double ry, const double rtheta,const double rkappa, const double rdkappa,const double x, const double y,const double v, const double a,const double theta, const double kappa,std::array<double, 3>* const ptr_s_condition,std::array<double, 3>* const ptr_d_condition);static void frenet_to_cartesian(const double rs, const double rx,const double ry, const double rtheta,const double rkappa, const double rdkappa,const std::array<double, 3>& s_condition,const std::array<double, 3>& d_condition,double* const ptr_x, double* const ptr_y,double* const ptr_theta,double* const ptr_kappa, double* const ptr_v,double* const ptr_a);
modules/common/math/cartesian_frenet_conversion.cpp
void CartesianFrenetConverter::cartesian_to_frenet(const double rs, const double rx, const double ry, const double rtheta,const double rkappa, const double rdkappa, const double x, const double y,const double v, const double a, const double theta, const double kappa,std::array<double, 3>* const ptr_s_condition,std::array<double, 3>* const ptr_d_condition) {const double dx = x - rx;const double dy = y - ry;const double cos_theta_r = std::cos(rtheta);const double sin_theta_r = std::sin(rtheta);const double cross_rd_nd = cos_theta_r * dy - sin_theta_r * dx;ptr_d_condition->at(0) =std::copysign(std::sqrt(dx * dx + dy * dy), cross_rd_nd);const double delta_theta = theta - rtheta;const double tan_delta_theta = std::tan(delta_theta);const double cos_delta_theta = std::cos(delta_theta);const double one_minus_kappa_r_d = 1 - rkappa * ptr_d_condition->at(0);ptr_d_condition->at(1) = one_minus_kappa_r_d * tan_delta_theta;const double kappa_r_d_prime =rdkappa * ptr_d_condition->at(0) + rkappa * ptr_d_condition->at(1);ptr_d_condition->at(2) =-kappa_r_d_prime * tan_delta_theta +one_minus_kappa_r_d / cos_delta_theta / cos_delta_theta *(kappa * one_minus_kappa_r_d / cos_delta_theta - rkappa);ptr_s_condition->at(0) = rs;ptr_s_condition->at(1) = v * cos_delta_theta / one_minus_kappa_r_d;const double delta_theta_prime =one_minus_kappa_r_d / cos_delta_theta * kappa - rkappa;ptr_s_condition->at(2) =(a * cos_delta_theta -ptr_s_condition->at(1) * ptr_s_condition->at(1) *(ptr_d_condition->at(1) * delta_theta_prime - kappa_r_d_prime)) /one_minus_kappa_r_d;
}void CartesianFrenetConverter::frenet_to_cartesian(const double rs, const double rx, const double ry, const double rtheta,const double rkappa, const double rdkappa,const std::array<double, 3>& s_condition,const std::array<double, 3>& d_condition, double* const ptr_x,double* const ptr_y, double* const ptr_theta, double* const ptr_kappa,double* const ptr_v, double* const ptr_a) {ACHECK(std::abs(rs - s_condition[0]) < 1.0e-6)<< "The reference point s and s_condition[0] don't match";const double cos_theta_r = std::cos(rtheta);const double sin_theta_r = std::sin(rtheta);*ptr_x = rx - sin_theta_r * d_condition[0];*ptr_y = ry + cos_theta_r * d_condition[0];const double one_minus_kappa_r_d = 1 - rkappa * d_condition[0];const double tan_delta_theta = d_condition[1] / one_minus_kappa_r_d;const double delta_theta = std::atan2(d_condition[1], one_minus_kappa_r_d);const double cos_delta_theta = std::cos(delta_theta);*ptr_theta = NormalizeAngle(delta_theta + rtheta);const double kappa_r_d_prime =rdkappa * d_condition[0] + rkappa * d_condition[1];*ptr_kappa = (((d_condition[2] + kappa_r_d_prime * tan_delta_theta) *cos_delta_theta * cos_delta_theta) /(one_minus_kappa_r_d) +rkappa) *cos_delta_theta / (one_minus_kappa_r_d);const double d_dot = d_condition[1] * s_condition[1];*ptr_v = std::sqrt(one_minus_kappa_r_d * one_minus_kappa_r_d *s_condition[1] * s_condition[1] +d_dot * d_dot);const double delta_theta_prime =one_minus_kappa_r_d / cos_delta_theta * (*ptr_kappa) - rkappa;*ptr_a = s_condition[2] * one_minus_kappa_r_d / cos_delta_theta +s_condition[1] * s_condition[1] / cos_delta_theta *(d_condition[1] * delta_theta_prime - kappa_r_d_prime);
}
3 参考线reference_line
3.0 前言
决策规划流程:
① 全局规划生成一条参考线
② 静态障碍物投影到以参考线为坐标轴的Frenet坐标系上
③ 决策算法对障碍物做决策(往左绕,往右绕,忽略)开辟最优凸空间
④ 规划算法在凸空间搜素出最优的路径
⑤ 后处理,在规划轨迹中选一个点,坐标转成Cartesian,输出给控制跟踪
需要参考线的原因:
(1)参考线是解决方案,能够解决导航路径过长,不平滑的问题
(2)过长的路径不利于坐标转换(找匹配点,计算s的长度)
(3)过长的路径,障碍物投影可能不唯一
(4)路径不平滑
在每个规划周期内,找到车在导航路径上的投影点,以投影点为坐标原点,往后取30m,往前取150m范围内的点,做平滑,平滑后的点的集合称为参考线。
3.1 参考线平滑算法
参考线的三个代价函数:
(1)平滑代价
∣ P 1 P 3 ⃗ ∣ |\vec {P_1P_3}| ∣P1P3∣衡量平滑与不平滑的标准。 ∣ P 1 P 3 ⃗ ∣ |\vec {P_1P_3}| ∣P1P3∣越小,越平滑。
(2)紧凑型代价
长度尽可能均匀、紧凑
(3)相似代价
∣ P 1 P 1 r ⃗ ∣ + ∣ P 2 P 2 r ⃗ ∣ + ∣ P 3 P 3 r ⃗ ∣ |\vec {P_1P_{1r}}|+|\vec {P_2P_{2r}}|+|\vec {P_3P_{3r}}| ∣P1P1r∣+∣P2P2r∣+∣P3P3r∣越小,越接近原路径几何。
总结:
x 1 , x 2 , x 3 x_1,x_2,x_3 x1,x2,x3为优化后的路径点。希望cost function越小越好
本质是二次规划问题
1 2 X T H X + f T X = X T ( H 2 ) X + f T X \begin{array}{l} \frac{1}{2} X^{T} H X+f^{T} X=X^{T} (\frac{H}{2} )X+f^{T} X \end{array} 21XTHX+fTX=XT(2H)X+fTX
令 H = 2 ( w smooth A 1 T A 1 + w length A 2 T A 2 + w ref A 3 T A 3 ) , f T = w ref h T H=2\left(w_{\text {smooth }} A_{1}^{T} A_{1}+w_{\text {length }} A_{2}^{T} A_{2}+w_{\text {ref }} A_{3}^{T} A_{3}\right), f^{T}=w_{\text {ref }} h^{T} H=2(wsmooth A1TA1+wlength A2TA2+wref A3TA3),fT=wref hT
约束:
x = ( x 1 , y 1 , . . . , x n , y n ) x = (x_1,y_1,...,x_n,y_n) x=(x1,y1,...,xn,yn)
x r e f = ( x 1 r , y 1 r , . . . , x n r , y n r ) x_{ref}=(x_{1r},y_{1r},...,x_{nr},y_{nr}) xref=(x1r,y1r,...,xnr,ynr)
x x x与 x r e f x_{ref} xref不要太远
曲率约束:非线性约束(一般与车的最大侧向加速度有关)
3.2 Fem smooth平滑
在Apollo中叫 Fem smoother
优势:优化变量少
缺点:无法保证曲率是连续的,添加曲率约束求解麻烦
主要步骤:
(1)找匹配点
(2)采样(前150m,后30m)
(3)Fem smooth 平滑
目标:快,节省计算量(参考线是决策规划的基础和前提,因此必须要快)
怎么快?
(1)减少规划频率,规划算法会100ms执行一次,控制算法每10ms执行一次
(2)充分利用上一个规划周期的结果
一般做法:每个周期做遍历,但是这样速度慢,导航路径可能很长,遍历很费时
改进:
如何充分利用上一个规划周期的结果
以上个周期的match point为起点做遍历,一旦有 l i + 1 > l i l_{i+1}>l_{i} li+1>li,立即退出遍历。 l i l_i li对应的点作为本周期的匹配点。
判断遍历的方向:
a ) 向前遍历: d → ⋅ τ ⃗ > 0 b ) 向后遍历: d → ⋅ τ ⃗ < 0 c ) 本周期的匹配点 = 上周期的匹配点: ∣ d → ⋅ τ ⃗ ∣ < 1 e − 3 a) 向前遍历: \overrightarrow{\mathrm{d}} \cdot \vec{\tau}>0 \\ b) 向后遍历: \overrightarrow{\mathrm{d}} \cdot \vec{\tau}<0 \\ c)本周期的匹配点=上周期的匹配点: |\overrightarrow{\mathrm{d}} \cdot \vec{\tau}|<1e-3 a)向前遍历:d⋅τ>0b)向后遍历:d⋅τ<0c)本周期的匹配点=上周期的匹配点:∣d⋅τ∣<1e−3
本方法可以大大加速找匹配点
带来的问题:只适用于上个匹配点结果附近只有一个极小值点
一般情况不会有这种问题,规划周期100ms,即使车速50m/s,车也只走了五米。五米的道路不会像上图一样扭曲的道路几何,在上一个规划周期的匹配点一般只有一个极小值。
改进:
增加increase变量记录l连续增加的次数
在Github算法里
(1)遍历
第一次运行遍历
不是第一次运行
(2)点不够的情况
(3)参考线拼接
如何拼接:
注意:
Fem smoother至少需要3个点
3.3 避障
3.3.1 规划起点
确定规划起点:
错误做法:定位得到的host_x,host_y投影到referenceline,得到SL坐标(0, l 0 l_0 l0),以此点为路径规划的起点
正确做法:判断当前定位和上一个周期规划的结果是否误差太大
以( s 0 , l 0 s_0,l_0 s0,l0)作为规划的起点
为什么不能从定位的点作为规划起点:考虑到控制不完美,导致帧与帧之间的曲线是跳变的。
判断定位与轨迹的误差不太大的话,用上个轨迹的T+100ms的点作为本周期的规划起点,若误差较大,用车辆运动学进行外推
3.3.2 轨迹拼接
结合上一节,本人觉得理解老王对于规划起点的描述有点模糊,故拜读了下面这篇大佬写的博客,对于轨迹拼接思路一下子就通了。
参考博客:百度Apollo规划算法——轨迹拼接
在apollo的规划算法中,在每一帧规划开始时会调用一个轨迹拼接函数,返回一段拼接轨迹点集,并告诉我们是否重规划以及重规划的原因。
轨迹拼接:在每一帧轨迹规划的开始,我们需要确定规划的起始点 p 0 = ( x , y , v , a , θ , k , t ) p_0=(x,y,v,a,\theta,k,t) p0=(x,y,v,a,θ,k,t),起始点给定了规划的初始状态,然后结合当前帧的环境信息并调用规划算法规划生成当前帧轨迹。
方案1
根据ADC(Autonomous Driving Car)实际位置状态信息,通过车辆运动学推导100ms后的位置状态信息,作为规划起始点状态,在Apollo中,这一方案叫做重规划Replan。
方案2:考虑的是连续帧之间轨迹的平滑性和连续性
根据当前帧时间戳以及ADC实际位置信息,在上一帧轨迹中寻找匹配点,将上一帧轨迹中匹配点往后100ms所对应的轨迹点作为当前帧的规划起始状态点,待当前帧规划生成轨迹后,再和上一帧轨迹中所选择的规划起始点前一段距离的轨迹点进行拼接,形成一条完整的车辆运动轨迹(思路一下就清晰了,牛逼!),发送给下游控制模块。
理论上,如果控制跟踪和定位完美,不会出现任何误差,则完全可以使用方案1选择规划起始点,但是,由于定位误差和控制误差/滞后的原因,会导致每一规划时刻ADC位置大概率的偏离上一帧所规划的轨迹点,如果此时仍采用方案1,会导致当前帧所规划轨迹和上一帧规划轨迹的不平滑和不连续,从而导致控制的抖动,也可能会引起ADC行驶轨迹发散,跟踪误差越来越大,偏离设定参考线。而方案2可以保证ADC附近一段范围内前后两帧轨迹的平滑性,从而不断引导ADC沿着期望轨迹行驶。
3.3.3 结合Apollo代码理解轨迹拼接的细节
选择方案1的情况:
- 上一帧不存在轨迹(一般是起步或上一帧规划失败的情况)
- 没有使能方案2进行轨迹拼接
- 没有处在自动驾驶模式
- 上一帧存在轨迹但是没有轨迹点
- 当前时间相对于上一帧的相对时间戳小于上一帧起点相对时间戳
- 时间匹配点超出上一帧轨迹点的终点实际范围
- 匹配点处不存在匹配的path_point
- ADC实际位置和匹配点横纵向偏差超过给定阈值
方案1生成轨迹拼接点主要有两种情况:
① 起步阶段:由于起步阶段属于规划算法执行的第一帧,且速度、加速度值很小,可以直接使用当前状态点作为规划的起始状态点
② 非起步阶段:则根据当前ADC状态点结合运动学模型外推T时间之后的状态点作为本帧规划起始点
[ x t + 1 y t + 1 ψ t + 1 ] = [ x t y t ψ t ] + [ cos ( ψ ) sin ( ψ ) tan ( δ f ) l ] ⋅ v \left[\begin{array}{l} x_{t+1} \\ y_{t+1} \\ \psi_{t+1} \end{array}\right]=\left[\begin{array}{l} x_{t} \\ y_{t} \\ \psi_{t} \end{array}\right]+\left[\begin{array}{c} \cos (\psi) \\ \sin (\psi) \\ \frac{\tan \left(\delta_{f}\right)}{l} \end{array}\right] \cdot v xt+1yt+1ψt+1 = xtytψt + cos(ψ)sin(ψ)ltan(δf) ⋅v
方案二(以上情况均不满足时):
该方案同时考虑了时间匹配和位置匹配
时间匹配:根据当前时间戳和上一帧轨迹起点的时间戳对比,计算当前时间ADC应该到达的时间匹配点(图中t_pos)及该匹配点在上一帧轨迹中对应的索引值t_index,若t_pos不存在路径点,则选择方案1。
位置匹配:根据ADC当前位置点在上一帧轨迹中寻找最近的匹配点(图中p_pos)及该匹配点在上一帧轨迹中对应的索引值p_index,并比较实际位置点和匹配点之间的横纵向位置偏差,若任一偏差超过给定阈值,则选择方案1。
若以上判断均通过,根据 (当前时刻的绝对时间 - 上一时刻轨迹的初始时间 + planning_cycle_time) 得到forward_rel_time,选取时间匹配索引和位置匹配索引中的较小索引作为匹配点索引值matched_index,在上一帧的轨迹上截取出matched_index往前n个点开始,至forward_rel_time的一段轨迹,作为stitching_trajectory。最后,遍历这段轨迹,对其relative_time和s进行赋值。最终,使用stitching_trajectory的最后一个点,即图中的 p 0 p_0 p0,作为当前帧轨迹规划的起始点。
选择当前时间+planning_cycle_time的时间对应的上一帧轨迹的位置点作为本帧规划起点的原因:当本帧规划需要时间planning_cycle_time,将生成轨迹送到规划模块时对应的实际时间理论上就是当前时间+planning_cycle_time。
TrajectoryStitcher::ComputeStitchingTrajectory
/* Planning from current vehicle state if:
1. the auto-driving mode is off
(or) 2. we don't have the trajectory from last planning cycle
(or) 3. the position deviation from actual and target is too high
*/
std::vector<TrajectoryPoint> TrajectoryStitcher::ComputeStitchingTrajectory(
const VehicleState& vehicle_state, const double current_timestamp,
const double planning_cycle_time, const size_t preserved_points_num,
const bool replan_by_offset, const PublishableTrajectory* prev_trajectory,
std::string* replan_reason) {//a.是否使能轨迹拼接if (!FLAGS_enable_trajectory_stitcher) {*replan_reason = "stitch is disabled by gflag.";return ComputeReinitStitchingTrajectory(planning_cycle_time, vehicle_state);}//b.上一帧是否生成轨迹if (!prev_trajectory) {*replan_reason = "replan for no previous trajectory.";return ComputeReinitStitchingTrajectory(planning_cycle_time, vehicle_state);}//c.是否处于自动驾驶模式if (vehicle_state.driving_mode() != canbus::Chassis::COMPLETE_AUTO_DRIVE) {*replan_reason = "replan for manual mode.";return ComputeReinitStitchingTrajectory(planning_cycle_time, vehicle_state);}//d.上一帧是否存在轨迹点 size_t prev_trajectory_size = prev_trajectory->NumOfPoints();if (prev_trajectory_size == 0) {*replan_reason = "replan for empty previous trajectory.";return ComputeReinitStitchingTrajectory(planning_cycle_time, vehicle_state);}const double veh_rel_time = current_timestamp - prev_trajectory->header_time();size_t time_matched_index = prev_trajectory->QueryLowerBoundPoint(veh_rel_time);//e.判断当前时间相对于上一帧的相对时间戳是否小于上一帧起点相对时间戳if (time_matched_index == 0 &&veh_rel_time < prev_trajectory->StartPoint().relative_time()) {*replan_reason ="replan for current time smaller than the previous trajectory's first ""time.";return ComputeReinitStitchingTrajectory(planning_cycle_time, vehicle_state);}//f.判断时间匹配点是否超出上一帧轨迹点范围if (time_matched_index + 1 >= prev_trajectory_size) {*replan_reason ="replan for current time beyond the previous trajectory's last time";return ComputeReinitStitchingTrajectory(planning_cycle_time, vehicle_state);}auto time_matched_point = prev_trajectory->TrajectoryPointAt(static_cast<uint32_t>(time_matched_index));//g.判断时间匹配点处是否存在path_pointif (!time_matched_point.has_path_point()) {*replan_reason = "replan for previous trajectory missed path point";return ComputeReinitStitchingTrajectory(planning_cycle_time, vehicle_state);}size_t position_matched_index = prev_trajectory->QueryNearestPointWithBuffer({vehicle_state.x(), vehicle_state.y()}, 1.0e-6);//计算实际位置点和匹配点的横纵向偏差auto frenet_sd = ComputePositionProjection(vehicle_state.x(), vehicle_state.y(),prev_trajectory->TrajectoryPointAt(static_cast<uint32_t>(position_matched_index)));//h.判断横纵向偏差if (replan_by_offset) {auto lon_diff = time_matched_point.path_point().s() - frenet_sd.first;auto lat_diff = frenet_sd.second;//h.1:横向偏差不满足条件if (std::fabs(lat_diff) > FLAGS_replan_lateral_distance_threshold) {const std::string msg = absl::StrCat("the distance between matched point and actual position is too ""large. Replan is triggered. lat_diff = ",lat_diff);*replan_reason = msg;return ComputeReinitStitchingTrajectory(planning_cycle_time,vehicle_state);}//h.2:纵向偏差不满足条件if (std::fabs(lon_diff) > FLAGS_replan_longitudinal_distance_threshold) {const std::string msg = absl::StrCat("the distance between matched point and actual position is too ""large. Replan is triggered. lon_diff = ",lon_diff);*replan_reason = msg;return ComputeReinitStitchingTrajectory(planning_cycle_time,vehicle_state);}} else {ADEBUG << "replan according to certain amount of lat and lon offset is ""disabled";}//计算当前时刻后T时刻的时间,并计算其在上一帧轨迹中对应的索引值double forward_rel_time = veh_rel_time + planning_cycle_time;size_t forward_time_index =prev_trajectory->QueryLowerBoundPoint(forward_rel_time);ADEBUG << "Position matched index:\t" << position_matched_index;ADEBUG << "Time matched index:\t" << time_matched_index;//选择时间匹配索引和位置匹配索引中的较小索引作为匹配点索引auto matched_index = std::min(time_matched_index, position_matched_index);//构建拼接轨迹,<匹配索引点前n个点,当前时刻后的T时刻所对应的匹配点>std::vector<TrajectoryPoint> stitching_trajectory(prev_trajectory->begin() +std::max(0, static_cast<int>(matched_index - preserved_points_num)),prev_trajectory->begin() + forward_time_index + 1);const double zero_s = stitching_trajectory.back().path_point().s();for (auto& tp : stitching_trajectory) {if (!tp.has_path_point()) {*replan_reason = "replan for previous trajectory missed path point";return ComputeReinitStitchingTrajectory(planning_cycle_time,vehicle_state);}//适配时间和s值tp.set_relative_time(tp.relative_time() + prev_trajectory->header_time() -current_timestamp);tp.mutable_path_point()->set_s(tp.path_point().s() - zero_s);}return stitching_trajectory;
}
3.4 避障
避障问题是非凸问题,存在多个极小值点,要怎样找到最终解?找粗解
3.4.1 避障流程
避障流程:
① 在离散空间上求最优路径,此最优路径开辟一个凸空间,在此凸空间上做二次规划,离散空间的最优路径称为粗解,粗解开辟凸空间,在这个凸空间上开辟最优解。
② 在离散空间上找到粗解:DP;在凸空间上优化最终解:QP
连接两点的曲线:五次多项式
l = f ( s ) = a 0 + a 1 s + a 2 s 2 + . . . + a 5 s 5 l = f(s)=a_0+a_1s+a_2s^2+...+a_5s^5 l=f(s)=a0+a1s+a2s2+...+a5s5
3.4.2 代价函数
代价函数:评价路径的优劣
① 平滑代价 f ( ) f() f()
越像直线越平滑,两点之间直线最短,期望 ∫ ( 1 + f ′ ( x ) 2 ) \int \sqrt{\left(1+f^{\prime}(x)^{2}\right)} ∫(1+f′(x)2)最小,等价于 ∫ ( f ′ ( x ) 2 ) \int \sqrt{\left(f^{\prime}(x)^{2}\right)} ∫(f′(x)2)最小,即 ∑ f ′ ( s i ) 2 \sum f^{\prime}\left(s_{i}\right)^{2} ∑f′(si)2最小
② 障碍物距离代价 g ( ) g() g()
③ 参考线距离代价 l ( ) l() l()
代价函数
图的最短问题:
① 每个路径都有对应的cost,问题变成了找图最优问题:A*,Dijstra
②EM planner用动态规划解决了这类问题
控制接口:
规划发出的轨迹信息包含 x , y , θ , k , v , a , t x,y,\theta,k,v,a,t x,y,θ,k,v,a,t,时间为绝对时间。控制根据当前控制周期的时间搜索轨迹
3.5 自动驾驶之轻决策与重决策
3.5.1 二次规划
求解使二次型 1 2 x T H x + f T x \frac{1}{2}x^THx+f^Tx 21xTHx+fTx值最小的x,并且满足约束 A x ≤ u b Ax≤ub Ax≤ub
无需在意二次规划的约束形式( l b ≤ u b , a ≤ b , A x ≤ b lb≤ub,a≤b,Ax≤b lb≤ub,a≤b,Ax≤b)
二次规划要求解空间是凸的,而动态规划(决策)开辟了凸空间。
3.5.2 轻决策与重决策
决策算法:轻决策(基于代价函数)和重决策(基于人给定的规则)
重决策
优点:
(1)计算量小
(2)在感知不强的情况下仍然能做决策(融合了人的智慧)
缺点:
(1)场景多无法覆盖
(2)人给出的决策所开辟的凸空间未必满足约束
if-else条件语句,不利于最优控制问题的求解。
轻决策
轻决策:无先验规则,空间离散化,设计cost function,动态规划算法求解离散空间的最优路径,该最优路径开辟凸空间。
优点:
(1)无人为规则,可以处理复杂场景
(2)有粗解,通过设计代价函数,可以使粗解“满足”硬约束(碰撞、最小曲率),这样使二次规划求解成功的机率大大增加(因为粗解在凸空间内部,所以该凸空间的“扭曲”程度至少有粗解兜底)大大缓解了基于人为规则的决策所造成的凸空间扭曲情况
缺点:
(1)复杂场景计算量很大
(2)依赖预测
(3)对感知,定位要求高
(4)代价函数设计 / 最优解未必符合人的驾驶习惯
L2+ 高速L3 => 重决策
L4 => 轻决策
轻决策和重决策的融合(博弈论)
3.5.3 二次规划算法
l m i n 1 , l m i n 2 , . . . , l m a x 1 . l m a x 2 , . . . 构成凸空间 l_{min1},l_{min2},...,l_{max1}.l_{max2},...构成凸空间 lmin1,lmin2,...,lmax1.lmax2,...构成凸空间
二次规划的求解空间就在此凸空间中搜索
Apollo 3.5/5.0新增,f(s)尽可能在凸空间的中央
分段加加速度优化法
设有 l 1 , l 1 ′ , l 1 ′ ′ , . . . , l n , l n ′ , l n ′ ′ l_1,l_1^{'},l_1^{''},...,l_n,l_n^{'},l_n^{''} l1,l1′,l1′′,...,ln,ln′,ln′′需要优化
则分段加加速度约束为
记为 A e q x = b e q A_{eq}x=b_{eq} Aeqx=beq等式约束
这样做并不准确
d 1 d_1 d1:汽车质心到车头距离
d 2 d_2 d2:汽车质心到车尾距离
w w w:汽车宽度
记为 A x ≤ b Ax≤b Ax≤b
求解出 l 1 , l 1 ′ , l 1 ′ ′ , . . . , l n , l n ′ , l n ′ ′ l_1,l_1^{'},l_1^{''},...,l_n,l_n^{'},l_n^{''} l1,l1′,l1′′,...,ln,ln′,ln′′与 s 1 , s 2 , . . . , s n s_1,s_2,...,s_n s1,s2,...,sn结合即得到二次规划下的最优路径,再转化为Cartesian坐标,路径规划完成。
3.5.4 二次规划崩溃
靠近障碍物时,二次规划崩溃
为什么在无避障时不会崩溃?
为什么规划的路径会贴着边界?
因为二次规划的性质决定,带约束的二次规划最小值只会在极小值点或边界点取得
改善二次规划崩溃的方法:
① 规划起点不管了,障碍物放大一点做安全缓冲
② 改极小值点的位置
c e n t r e l i n e = 1 2 ( l m i n + l m a x ) centre_{}line=\frac{1}{2}(l_{min}+l_{max}) centreline=21(lmin+lmax)
优点:几何稳定
缺点: w c e n t r e w_{centre} wcentre调小不起明显作用,调大了路径不平滑。
优点:dp曲线平滑相对好做
缺点: d p p a t h dp_{path} dppath几何不稳定
dp_path每一帧之间的几何形状都会有巨大变化,若二次规划以dp_path为centre line,qp_path也会震荡。相反, c e n t r e l i n e = 1 2 ( l m i n + l m a x ) centre_{}line=\frac{1}{2}(l_{min}+l_{max}) centreline=21(lmin+lmax)就很稳定
3.5.5 车控制不稳,抖动剧烈
根源:车规划的路径会天然往边界靠拢的趋势
车在边界上当方唐镜,左右横跳。
解决办法:
加不允许反复横跳的约束
新的问题:约束越多,求解越慢
(1)搜索空间增大,需要对更多的可能性进行搜索和验证
(2)约束之间的冲突,需要进行额外的处理来解决这些冲突
(3)优化目标的复杂性,约束条件的增加可能会导致优化目标的复杂性增加
解决:削二次规划的规模,由60 => 20,二次规划求解完毕后再进行插值增密。
3.5.6 决策“朝令夕改”
一会决策向右绕行,一会决策向左绕行
Apollo方法:打标签
这一帧:只对无标签的obs做动态规则决策,若障碍物已经有标签,直接使用标签的决策
3.5.7 速度规划对路径规划的影响
Path只处理静态障碍物,有问题
这种情况无论速度规划怎么规划,都会碰撞(除非倒车)
已知上一帧的速度规划为10m/s匀速,上一帧的path为直线
路径规划要拿上一帧的轨迹去判断与动态障碍物的交互
EM planner是SL planning与ST planning反复迭代的过程
上一帧的SL,ST => 当前SL => 当前ST => 下一帧SL
4 SL、ST迭代优化
4.1 ST图、预测
已有cartesian的path,作速度规划
ST图画法
4.2 SL、ST迭代求解
当前帧规划端
当前帧的规划结果是下一帧的上一次的规划结果
SL、ST迭代过程
上一帧的trajectory + 当前帧的预测 => 当前帧SL的虚拟障碍物 => 当前帧SL(几何受影响)=> 当前帧ST => 改变当前帧ST => 当前帧轨迹trajectory改变
下一帧拿当前帧的trajectory + 下一帧的预测 => 下一帧SL => 下一帧ST
EM planner核心思想
① 将SLT三维规划问题降维,分解成两个二维的SL、ST规划问题大大降低了规划难度
② 上一帧的trajectory优化当前帧的SL、SL优化ST,不断迭代
4.3 SL与ST迭代
规划的基本工具:ST图
ST图依赖预测(预测、博弈、感知最难)
预测模型:恒定 v ⃗ \vec v v 或者恒定 a ⃗ \vec a a 模型(简化问题)
SL、ST迭代问题
Apollo 1.5 EM planner 引入SL,ST解耦规划,凸空间,先决策后规划。在一些复杂工况以及强算力的这个场景下,SL和ST分开优化求解是工业界最广泛最流行的方法。
Apollo 2.5 加入了lattice planner
Apollo 3.5 大幅修改EM planner,变成了public road planner(取消了SL,ST迭代机制,SL只管静态障碍物,ST只管动态障碍物,加入scenario模块)
Apollo 5.0 再次大幅修改public road planner,SL的dp被取消
Apollo 6.0~7.0 改动不大
取消了SL,ST迭代机制:
减轻ST对SL的干扰
因为车的纵向变化能力高于横向,导致动态障碍物车的速度变化剧烈程度远高于路径,预测难以测准,预测不准导致ST图剧烈变化=>速度规划结果剧烈变化
若ST影响SL,会导致SL规划容易不稳定,朝令夕改
车百公里加速:4-5秒 汽油车:5-7秒
百公里刹停距离:32-45m不等
证明汽车在一个非常短的时间、距离内,纵向的速度可以发生剧烈的变化。变化能力远高于横向的。
SL只管静态,ST只管动态,是一种兼顾灵活性和稳定性的一种做法
有没有一种方法既能保证路径稳定,又能保证速度稳定,还能应对这个剧烈环境(动态障碍物速度)的剧烈变化,做出相应的完美策略?一般没有。
这种方法SL还是会影响ST,ST不影响SL,因为SL会生成一个路径,路径的几何形状会影响到动态障碍物的交互。所以SL影响ST,ST不影响SL。
有两种场景ST需要影响SL:
① 上一帧的host trajectory与obs trajectory在纵向s有交互的区域内,obs所走过的空间仍然是host不能去的。
② 纵向有相交,相向而行
下一帧
特殊情况:
同向而行,动态障碍物很快的方向向前行驶,主车更快,Apollo认为这种情况过于危险,通过变道解决。
百度Apollo EM planner决策规划算法到这里,理论结束。不正之处望读者指教
相关文章:

Apollo决策规划 - EM planner
旨在对b站老王所讲的百度Apollo - EM planner算法做浓缩版总结 0 决策规划背景 基于图搜索 优点: 可以得到全局层面最优解,适用于比较低维数的规划问题 缺点: 规划问题维数较高时,面临指数爆炸问题 基于采样 优点:…...

Qt: 事件过滤器的更多用法
不懂事件循环怎么回事的可以看下面的文章 Qt事件循环完整流程 常规使用 定义一个窗口MainWindow ,之后在窗口里添加一个事件过滤函数eventFilter,将窗口的某一个或一些字控件安装上事件过滤器。 这种情况下MainWindow 就是pushButton11的时间过滤器&am…...

解决:ModuleNotFoundError: No module named ‘paddle‘
错误显示: 原因: 环境中没有‘paddle’的python模块,但是您在尝试导入 解决方法: 1.普通方式安装: pip install paddlepaddle #安装命令 2.镜像源安装 pip install paddlepaddle -i https://pypi.tuna.tsinghua.e…...

上海雷卯可以解决YPbPr/ YCbCr接口 ESD/EOS静电浪涌问题
YPbPr /YCbCr 接口传输的是视频信号,不传输音频信号。YPbPr 和 YCbCr 都是视频信号的颜色编码格式,多应用于机顶盒(Set-top box),TV电视,投影仪,游戏机和DVD播放器。 YPbPr:是一种模拟视频接口…...

【FPGA/IC】CRC电路的Verilog实现
前言 在通信过程中由于存在各种各样的干扰因素,可能会导致发送的信息与接收的信息不一致,比如发送数据为 1010_1010,传输过程中由于某些干扰,导致接收方接收的数据却成了0110_1010。为了保证数据传输的正确性,工程师们…...

go语言添加代理
LiteIDE 工具->管理 https://mirrors.aliyun.com/goproxy/或https://goproxy.cn,direct 命令行 go env -w GOPROXYhttps://goproxy.cn,direct...

kafka 可视化工具
kafka可视化工具 随着科技发展,中间件也百花齐放。平时我们用的redis,我就会通过redisInsight-v2 来查询数据,mysql就会使用goland-ide插件来查询,都挺方便。但是kafka可视化工具就找了半天,最后还是觉得redpandadata…...

安康杯安全知识竞赛上的讲话稿
各位领导、同志们: 经过近半个月时间的准备,南五十家子镇平泉首届安康杯安全生产知识竞赛初赛在今天圆满落下帏幕,经过紧张激烈的角逐, 代表队、 代表队和 代表队分别获得本次竞赛的第一、二、三名让我们以热烈的掌声表示祝…...

python 基础知识点(蓝桥杯python科目个人复习计划59)
今日复习内容:做题 例题1:建造房屋 问题描述: 小蓝和小桥是两位年轻的建筑师,他们正在设计一座新的城市。 在这个城市中,有N条街道,每条街道上有M个位置可以建造房屋(一个位置只能建造一个房…...

LCR 179. 查找总价格为目标值的两个商品 - 力扣
1. 题目 购物车内的商品价格按照升序记录于数组 price。请在购物车中找到两个商品的价格总和刚好是 target。若存在多种情况,返回任一结果即可。 2. 示例 3. 分析 题目有说明为递增数组,所以可以利用单调性双指针解决。跟611. 有效的三角形个数为一类题…...

《汇编语言》- 读书笔记 - 第16章-直接定址表
《汇编语言》- 读书笔记 - 第16章-直接定址表 16.1 描述了单元长度的标号(数据标号)检测点 16.1 16.2 在其他段中使用数据标号assume通过标号取地址检测点 16.2 16.3 直接定址表(Direct Addressing Table)例1分析代码效果 例2分析…...

ChatGPT 新增朗读功能,支持 37 种语言
3 月 5 日消息,OpenAI 为其广受欢迎的聊天机器人 ChatGPT 推出了名为「朗读」(Read Aloud) 的新功能。该功能可以让 ChatGPT 用五种不同的声音朗读其回复,旨在为用户提供更加便捷的交互体验。目前,「朗读」功能已上线 ChatGPT 的网页端、iOS …...

洛谷 P8816 [CSP-J 2022] 上升点列(T4)
目录 题目传送门 算法解析 最终代码 提交结果 尾声 题目传送门 [CSP-J 2022] 上升点列 - 洛谷https://www.luogu.com.cn/problem/P8816 算法解析 k 0 且 xi, yi 值域不大时,这题是非常简单的 DP,类似「数字三角形」。 记 dp(x,y) 为「以 (x,y) …...

python爬虫(2)
继上节 查看数组维数 可以使用数组的ndim属性 代码示例如下: import numpy as np c np.random.randint(1,9,5) print(c.ndim) 结果如下: 当然这些也可以结合前面的各种用法来使用 1、选取数组元素 (1)一维数组的元素…...

外包干了8天,技术退步明显。。。。。
先说一下自己的情况,本科生,19年通过校招进入杭州某软件公司,干了接近3年的功能测试,今年年初,感觉自己不能够在这样下去了,长时间呆在一个舒适的环境会让一个人堕落!而我已经在一个企业干了四年的功能测试…...

浅谈去耦电容的作用、选择、布局及其它电容的区别!
在一些文章资料中,去耦电容器被认为是旁路电容器。在其他资料中,去耦电容和旁路电容的区别在于:“旁路电容以输入信号中的干扰为滤波对象,而去耦电容以输出信号的干扰为滤波对象,防止干扰信号返回到输出端。”力量。”…...

抖音视频评论批量采集软件|视频下载工具
《轻松搞定!视频评论批量采集软件,助您高效工作》 在短视频这个充满活力和创意的平台上,了解用户评论是了解市场和观众心声的重要途径之一。为了帮助您快速获取大量视频评论数据,我们推出了一款操作便捷、功能强大的软件ÿ…...

javaSE-----继承和多态
目录 一.初识继承: 1.1什么是继承,为什么需要继承: 1.2继承的概念与语法: 二.成员的访问: 2.1super关键字 2.2this和super的区别: 三.再谈初始化: 小结: 四.初识多态: 4.1多…...

数据库之Oracle数据导入导出
目录 一、单表导出和导入1、单表导出数据2、单表导入数据二、全表导出和导入1、远程导出全表数据2、导入本地数据三、密码带特殊字符的写法1、Windows OS写法2、Linux/Unix OS写法 四、总结 一、单表导出和导入 1、单表导出数据 --导出远程服务上的表数据 exp 用户名/密码IP…...

nRF52832——GPIOTE与外部中断
这里写目录标题 GPIOTE 原理分析GPIOTE 输入事件应用GPIOTE 事件寄存器应用GPIOTE 事件组件的应用(库函数)GPIOTE PORT 事件应用 GPIOTE 任务应用GPIOTE 任务触发 LED 寄存器操作组件方式进行任务配置 GPIOTE 原理分析 GPIO 任务和时间(GPIO…...

根据用户名称实现单点登录
一、参数格式 二、后端实现 Controller层 public class IAccessTokenLoginController extends BaseController {Autowiredprivate ISysUserService sysUserService;Autowiredprivate ISingleTokenServiceImpl tokenService;/*** 登录方法** return 结果*/PostMapping("/l…...

【设计】855. 考场就座
855. 考场就座 这段代码实现了一个考场安排座位的算法。在这个算法中,考场被模拟成一个从0到n-1的数轴,其中每个位置代表一个座位。目的是在每次学生入座时,找到一个使得所有学生之间距离最大化的座位,并在学生离开时更新座位信息…...

Android中的传感器类型和接口名称
本文将介绍传感器坐标轴、基础传感器和复合传感器(动作传感器、姿势传感器、未校准传感器和互动传感器)。 1. 传感器坐标轴 许多传感器的传感器事件值在相对于设备静止的特定坐标系中表示。 1.1 移动设备坐标轴 Sensor API 仅与屏幕的自然方向相关&a…...

解析进程 /proc/pid/maps 和 /proc/pid/smaps
目录 /proc//maps 背景 具体描述 代码实现 实践 /proc/pid/smaps smaps各子项详解 代码实现 代码调用的路径如下: 小结 /proc/<pid>/maps 背景 相对于/proc/meminfo和dumpsys meminfo可以看到系统整体的内存信息,我们还需要能够具体到…...

【MQ】消息队列概述
📝个人主页:五敷有你 🔥系列专栏:MQ ⛺️稳中求进,晒太阳 定义 消息队列:一般我们简称为MQ(Message Queue) Message Queue :消息队列中间件,很多初学者认为,MQ通过消息的发送…...

交友盲盒系统PHP开源的盲盒源码
源码介绍: 交友盲盒系统是一款基于PHP开发的开源免费盲盒系统,旨在为用户提供一个充满乐趣和惊喜的社交体验。该系统具有丰富的功能和灵活的扩展性,可以轻松地满足各种线上交友、抽奖活动等场景的需求。 安装说明: PHP版本&…...

【Flutter 面试题】什么是异步编程 Flutter中如何处理异步操作?
【Flutter 面试题】什么是异步编程 Flutter中如何处理异步操作? 文章目录 写在前面解答补充说明从网络API异步获取数据并解析 写在前面 关于我 ,小雨青年 👉 CSDN博客专家,GitChat专栏作者,阿里云社区专家博主&#x…...

处理error: remote origin already exists.及其Gitee文件上传保姆级教程
解决error: remote origin already exists.: 删除远程 Git 仓库 git remote rm origin 再添加远程 Git 仓库 git remote add origin (HTTPS) 比如这样: 然后再push过去就ok了 好多人可能还是不熟悉怎么将文件上传 Gitee:我…...

网络编程套接字(2)——Socket套接字
目录 一、概念 二、分类 1、流套接字(使用传输层TCP协议) TCP的特点 2、数据报套接字(使用传输层UDP协议) UDP的特点 3、原始套接字 一、概念 Socket套接字,是由系统提供用于网络通信的技术,是基于T…...

向量错题本
《1800》 1 看变换求和能不能成为0,为0,就是线性相关 2 矩阵等价 3 4<...