老规矩首先上大神的链接 https://blog.csdn.net/q597967420/article/details/76099491。学习了深蓝学院的规划课程,以及拜读了大佬的博客后,做出的以下整理。
1.背景知识
机器人导航系统,是期望机器人从A点运动到B点,根据当前的物理环境规划出一条合适的路线,可以使得机器人可以移动到目标点的过程,就是运动规划。无人车其实也是机器人的一种,有着其独特的运动特性。
无人车的运动规划一般分为两个步骤:
- routing:在地图(栅格地图,RRT地图,四叉树、八叉树等)中使用搜索的方式找出一条能从A点到B点的可行路径,此时路径为一系列离散的点组成;
- planning:由于 routing 出的路点可能比较稀疏,并且路点之间的连线不够平滑,并不是很符合车辆的运动学特性,因此为了使得规划出的路线更加平滑并且符合车辆的运动学特性的平滑曲线或者更加稠密轨迹点。
一般来说,轨迹输出的方式都是用多项式来表示(五阶就可以了,有个大佬已经证明了这个结论,太高的阶增加了很大的计算量,对处理的结果改善却没有太大的作用):
p
(
t
)
=
p
0
+
p
1
t
+
p
2
t
2
…
+
p
n
t
n
=
∑
i
=
0
n
p
i
t
i
p(t)=p_{0}+p_{1}t+p_{2}t^{2} \ldots + p_{n}t^{n}=\displaystyle\sum_{i=0}^{n}p_{i}t^{i}
p(t)=p0+p1t+p2t2…+pntn=i=0∑npiti
其中,
p
0
,
p
1
,
…
,
p
n
p_{0},p_{1},\ldots,p_{n}
p0,p1,…,pn为轨迹参数,共有
n
+
1
n+1
n+1个,设参数向量
p
=
[
p
0
,
p
1
,
…
,
p
n
]
T
p=[p_{0},p_{1},\ldots,p_{n}]^{T}
p=[p0,p1,…,pn]T,则表达输出轨迹的多项式可以写为向量形式,
p
(
t
)
=
[
1
,
t
,
t
2
,
…
,
t
n
]
⋅
p
p(t)=[1,t,t^2,\ldots,t^n]\cdot p
p(t)=[1,t,t2,…,tn]⋅p
基于上式,可以根据参数计算出轨迹的位置
P
o
s
i
t
o
n
Positon
Positon,速度
V
e
l
o
c
i
t
y
Velocity
Velocity,加速度
A
c
c
e
l
e
r
a
t
i
o
n
Acceleration
Acceleration,加加速度
J
e
r
k
Jerk
Jerk,加加加速度
S
n
a
p
Snap
Snap,如下,
p
(
t
)
=
[
1
,
t
,
t
2
,
t
3
,
t
4
,
…
,
t
n
]
⋅
p
v
(
t
)
=
p
′
(
t
)
=
[
0
,
1
,
2
t
,
3
t
2
,
4
t
3
,
…
,
n
t
n
−
1
]
⋅
p
a
(
t
)
=
p
(
3
)
(
t
)
=
[
0
,
0
,
2
,
6
t
,
12
t
2
,
…
,
n
(
n
−
1
)
t
n
−
2
]
⋅
p
j
e
r
k
(
t
)
=
p
(
4
)
(
t
)
=
[
0
,
0
,
0
,
6
,
24
t
,
…
,
n
(
n
−
1
)
(
n
−
2
)
t
n
−
3
]
⋅
p
s
n
a
p
(
t
)
=
p
(
5
)
(
t
)
=
[
0
,
0
,
0
,
0
,
24
,
…
,
n
(
n
−
1
)
(
n
−
2
)
(
n
−
3
)
t
n
−
4
]
⋅
p
p(t)=[1,t,t^2,t^{3},t^{4},\ldots,t^n]\cdot p \\ v(t)=p\rq(t)=[0,1,2t,3t^2,4t^3,\ldots,nt^{n-1}]\cdot p \\ a(t)=p^{(3)}(t)=[0,0,2,6t,12t^2,\ldots,n(n-1)t^{n-2}]\cdot p \\ jerk(t)=p^{(4)}(t)=[0,0,0,6,24t,\ldots,n(n-1)(n-2)t^{n-3}]\cdot p \\ snap(t)=p^{(5)}(t)=[0,0,0,0,24,\ldots,n(n-1)(n-2)(n-3)t^{n-4}]\cdot p \\
p(t)=[1,t,t2,t3,t4,…,tn]⋅pv(t)=p′(t)=[0,1,2t,3t2,4t3,…,ntn−1]⋅pa(t)=p(3)(t)=[0,0,2,6t,12t2,…,n(n−1)tn−2]⋅pjerk(t)=p(4)(t)=[0,0,0,6,24t,…,n(n−1)(n−2)tn−3]⋅psnap(t)=p(5)(t)=[0,0,0,0,24,…,n(n−1)(n−2)(n−3)tn−4]⋅p
由于routing出的路点有很多个,很难找到一条多项式表示,为了保证多项式尽量能涵盖所有的路点(起码能最大层度上能拟合到所有的路点),所以将轨迹按时间分为很多段,每段都用一个多项式曲线来表示,如下,
p
(
t
)
=
{
[
1
,
t
,
t
2
,
…
,
t
n
]
⋅
p
1
t
0
≤
t
≤
t
1
[
1
,
t
,
t
2
,
…
,
t
n
]
⋅
p
2
t
1
≤
t
≤
t
2
…
[
1
,
t
,
t
2
,
…
,
t
n
]
⋅
p
k
t
k
−
1
≤
t
≤
t
k
p(t)=\begin{cases} [1,t,t^2,\ldots,t^n]\cdot p_{1} & \text t_0 \le t \le t_1 \\ [1,t,t^2,\ldots,t^n]\cdot p_{2} & \text t_1 \le t \le t_2 \\ \ldots \\ [1,t,t^2,\ldots,t^n]\cdot p_{k} & \text t_{k-1} \le t \le t_k \\ \end{cases}
p(t)=⎩⎪⎪⎪⎨⎪⎪⎪⎧[1,t,t2,…,tn]⋅p1[1,t,t2,…,tn]⋅p2…[1,t,t2,…,tn]⋅pkt0≤t≤t1t1≤t≤t2tk−1≤t≤tk
k
k
k为轨迹的段数,
p
i
=
[
p
i
0
,
p
i
1
,
…
,
p
i
n
]
T
p_{i}=[p_{i0},p_{i1},\ldots,p_{in}]^{T}
pi=[pi0,pi1,…,pin]T为第
i
i
i段轨迹的参数向量。
此外,实际工程问题中的轨迹往往是多维度的,通常的做法是单独求每个维度的轨迹。
可以看出,轨迹规划的最终目标就是求解出不同时间段内的轨迹多项式的参数 p 1 , p 2 , … , p k p_1,p_2,\ldots,p_k p1,p2,…,pk,同时,希望轨迹能满足一系列的约束条件,比如:设定起点和终点的位置、加速度、速度等,希望相邻的轨迹连接处足够平滑(位置连续,速度连续等),希望轨迹经过某些特定的路点,设定最大速度,最大加速度等,为了壁障还会希望轨迹在规定的空间内(corridor)。
通常,满足预先设定的约束的估计会有无数多个,而实际的工程问题中,往往需要满足特定条件的一条最优解,因此又会构建一个cost function,从而在可行的轨迹中找到一条最合适的特定轨迹。
因此,综上所述,我们就可以将目标问题就建立成了一个有约束的优化问题,如下:
m
i
n
f
(
p
)
,
s
.
t
.
A
e
q
=
b
e
q
,
A
i
e
q
=
b
i
e
q
min f(p), \hspace*{1em} s.t. \hspace*{0.5em} A_{eq}=b_{eq},A_{ieq}=b_{ieq}
minf(p),s.t.Aeq=beq,Aieq=bieq
式中,
p
=
[
p
1
T
,
p
2
T
,
…
,
p
k
T
]
T
p=[p_1^{T},p_2^{T},\ldots,p_k^{T}]^T
p=[p1T,p2T,…,pkT]T。
看到这个公式,是不是有点激动,这不是又到了最优化控制的东西了吗?是的,没错,只要解出来这个方程,就可以求解出目标轨迹参数 p p p。剩下的就是将优化问题中的 A e q A_{eq} Aeq, b e q b_{eq} beq, A i e q A_{ieq} Aieq, b i e q b_{ieq} bieq详细参数列出来,使用优化求解器求解即可。
至于cost function的选择,既然文章名字叫做 Mini-Snap,那最小化的目标函数 snap(加加加速度)肯定是可以的,也有人使用acceleration(加速度)和jerk(加加速度),至于速度和位置这两个为什么不可以做cost目标,你可以想想一下车子蜗牛速度行驶的场景。。。。。。
2.实际应用
首先给定包含起点在内的 k + 1 k+1 k+1个二维路点 ( x i , y i ) (x_i,y_i) (xi,yi),同时给定起始点的速度和加速度 v 0 , a 0 v_0,a_0 v0,a0,末端速度和加速度为 v e , a e v_e,a_e ve,ae,给定了时间T,然后需要根据以上的条件规划出一条经过所有点的平滑轨迹。
2.1初始轨迹分段与时间分配
根据路径点,将轨迹分为k段,计算每段的距离,按距离平分时间T(匀速时间分配),得到时间序列 t 0 , t 1 , … , t k t_0,t_1,\ldots,t_k t0,t1,…,tk。然后对 x , y x,y x,y维度单独规划轨迹。(后面都是单独讨论单一的维度)
在时间如何进行分配这一点上,这里稍微讨论一下,如果两个点之间的时间间隔太短,车辆可能由于自身的物理特性,导致在设置的间隔时间内无法到达下一个路点,而如果时间间隔设置的太大的话,很可能规划出的曲线会出现绕弯的情况,这两中情况都是我们不希望看到的。为了避免这个问题,时间分配的方法目前主流的大致有两种方式:
第一种,匀速分配时间。假设车辆在此段路径上按照一个固定的速度匀速行驶,然后根据每段的距离将总时间T分配到每段;
第二种,假设车辆速度变化为梯形的形式,车辆从当前速度按固定加速度加速到设定的速度,匀速行驶一段时间后,再匀减速到一个速度,也有可能是只有匀加速到固定速度后,匀速行驶,或者匀速行驶一段时间后,匀减速减速,然后根据每段的距离将总时间T分配到每段。
注意,这里的轨迹分段和时间分配都是初始分配,在迭代算法中,如果corridor check 和 feasiblility check 不满足条件,会插点或增大某一段时间,具体在后面有描述。
2.2构建优化函数
Mini-Snap的优化函数为:
min ∫ 0 T ( p ( 4 ) ( t ) ) 2 d t = min ∑ i = 1 k ∫ t i − 1 t i ( p ( 4 ) ( t ) ) 2 d t = min ∑ i = 1 k ∫ t i − 1 t i ( [ 0 , 0 , 0 , 0 , 24 , … , n ! ( n − 4 ) ! t n − 4 ] ⋅ p ) T ( [ 0 , 0 , 0 , 0 , 24 , … , n ! ( n − 4 ) ! t n − 4 ] ⋅ p ) d t = min ∑ i = 1 k p T ∫ t i − 1 t i [ 0 , 0 , 0 , 0 , 24 , … , n ! ( n − 4 ) ! t n − 4 ] T [ 0 , 0 , 0 , 0 , 24 , … , n ! ( n − 4 ) ! t n − 4 ] d t p = min ∑ i = 1 k p T Q i p \min \int_{0}^{T}(p^{(4)}(t))^{2}dt=\min \displaystyle\sum_{i=1}^{k} \int_{t_{i}-1}^{t_{i}}(p^{(4)}(t))^{2}dt\\ =\min \displaystyle\sum_{i=1}^{k} \int_{t_{i}-1}^{t_{i}} ([0,0,0,0,24,\ldots,\frac{n!}{(n-4)!}t^{n-4}]\cdot p)^{T}([0,0,0,0,24,\ldots,\frac{n!}{(n-4)!}t^{n-4}]\cdot p) \space dt \\ =\min \displaystyle\sum_{i=1}^{k} p^{T} \int_{t_{i}-1}^{t_{i}} [0,0,0,0,24,\ldots,\frac{n!}{(n-4)!}t^{n-4}]^{T}[0,0,0,0,24,\ldots,\frac{n!}{(n-4)!}t^{n-4}] dt \space p \\ =\min \displaystyle\sum_{i=1}^{k} p^{T}Q_{i}p min∫0T(p(4)(t))2dt=mini=1∑k∫ti−1ti(p(4)(t))2dt=mini=1∑k∫ti−1ti([0,0,0,0,24,…,(n−4)!n!tn−4]⋅p)T([0,0,0,0,24,…,(n−4)!n!tn−4]⋅p) dt=mini=1∑kpT∫ti−1ti[0,0,0,0,24,…,(n−4)!n!tn−4]T[0,0,0,0,24,…,(n−4)!n!tn−4]dt p=mini=1∑kpTQip
式中,
Q
i
=
∫
t
i
−
1
t
i
[
0
,
0
,
0
,
0
,
24
,
…
,
n
!
(
n
−
4
)
!
t
n
−
4
]
T
[
0
,
0
,
0
,
0
,
24
,
…
,
n
!
(
n
−
4
)
!
t
n
−
4
]
d
t
=
[
0
4
×
4
0
4
×
(
n
−
3
)
0
(
n
−
3
)
×
4
r
!
(
r
−
4
)
!
c
!
(
c
−
4
)
!
1
(
r
−
4
)
+
(
c
−
4
)
+
1
(
t
i
(
r
+
c
−
7
)
−
t
i
−
1
(
r
+
c
−
7
)
)
]
Q_{i}=\int_{t_{i}-1}^{t_{i}} [0,0,0,0,24,\ldots,\frac{n!}{(n-4)!}t^{n-4}]^{T}[0,0,0,0,24,\ldots,\frac{n!}{(n-4)!}t^{n-4}] dt \\ =\begin{bmatrix}0_{4 \times 4} & 0_{4 \times (n-3)} \\ 0_{(n-3) \times 4} & \frac{r!}{(r-4)!}\frac{c!}{(c-4)!} \frac{1}{(r-4)+(c-4)+1}(t_{i}^{(r+c-7)}-t_{i-1}^{(r+c-7)}) \end{bmatrix}
Qi=∫ti−1ti[0,0,0,0,24,…,(n−4)!n!tn−4]T[0,0,0,0,24,…,(n−4)!n!tn−4]dt=[04×40(n−3)×404×(n−3)(r−4)!r!(c−4)!c!(r−4)+(c−4)+11(ti(r+c−7)−ti−1(r+c−7))]
注意:式中r为行索引,c为列索引,索引从0开始,第一行和第一列均为0。
因此有,
Q
=
[
Q
1
Q
2
⋱
Q
k
]
Q=\begin{bmatrix} Q_{1} & \space & \space & \space \\ \space & Q_{2} & \space & \space \\ \space & \space & \ddots & \space \\ \space & \space & \space & Q_{k} \\ \end{bmatrix}
Q=⎣⎢⎢⎡Q1 Q2 ⋱ Qk⎦⎥⎥⎤
所以优化目标方程为,
min
p
T
Q
p
\min p^{T}Qp
minpTQp
这样就把Mini-Snap问题构建成了一个数学上的二次规划问题。
2.3构建等式约束
-
设定某一点的位置、速度、加速度或者更高阶的值为一个特定的值,可以构成一个等式约束。如,
位置约束: [ 1 , t 0 , t 0 2 , … , t 0 n , 0 … 0 ⏟ (k-1)(n+1) ] p = p 0 [1,t_{0},t_{0}^2,\ldots,t_{0}^n,\underbrace{0 \ldots 0}_{\text{(k-1)(n+1)}}]p=p_0 [1,t0,t02,…,t0n,(k-1)(n+1) 0…0]p=p0
速度约束: [ 0 , 1 , 2 t 0 , … , n t 0 n − 1 , 0 … 0 ⏟ (k-1)(n+1) ] p = v 0 [0,1,2t_{0},\ldots,nt_{0}^{n-1},\underbrace{0 \ldots 0}_{\text{(k-1)(n+1)}}]p=v_0 [0,1,2t0,…,nt0n−1,(k-1)(n+1) 0…0]p=v0加速度约束: [ 0 , 0 , 2 , … , n ( n − 1 ) t 0 n − 2 , 0 … 0 ⏟ (k-1)(n+1) ] p = a 0 [0,0,2,\ldots,n(n-1)t_{0}^{n-2},\underbrace{0 \ldots 0}_{\text{(k-1)(n+1)}}]p=a_0 [0,0,2,…,n(n−1)t0n−2,(k-1)(n+1) 0…0]p=a0
由于要过中间点,对中间点的位置也构建等式约束,方法同上。
-
相邻段之间的位置、速度、加速度连续可以构成一个等式约束,例如第 i , i + 1 i,i+1 i,i+1段的位置连续构成的等式约束为: [ 0 … 0 ⏟ (i-1)(n+1) , 1 , t i , t i 2 , … , t i n , − 1 , − t i + 1 , − t i + 1 2 , … , − t i + 1 n , 0 … 0 ⏟ (k-i-1)(n+1) ] p = 0 [\underbrace{0 \ldots 0}_{\text {(i-1)(n+1)}},1,t_{i},t_{i}^{2},\ldots,t_{i}^n,-1,-t_{i+1},-t_{i+1}^{2},\ldots,-t_{i+1}^n,\underbrace{0 \ldots 0}_{\text {(k-i-1)(n+1)}}]p=0 [(i-1)(n+1) 0…0,1,ti,ti2,…,tin,−1,−ti+1,−ti+12,…,−ti+1n,(k-i-1)(n+1) 0…0]p=0
速度连续构成的等式约束为: [ 0 … 0 ⏟ (i-1)(n+1) , 0 , 1 , 2 t i , … , n t i ( n − 1 ) , 0 , − 1 , − 2 t i + 1 , … , − n t i + 1 n − 1 , 0 … 0 ⏟ (k-i-1)(n+1) ] p = 0 [\underbrace{0 \ldots 0}_{\text {(i-1)(n+1)}},0,1,2t_{i},\ldots,nt_{i}^{(n-1)},0,-1,-2t_{i+1},\ldots,-nt_{i+1}^{n-1},\underbrace{0 \ldots 0}_{\text {(k-i-1)(n+1)}}]p=0 [(i-1)(n+1) 0…0,0,1,2ti,…,nti(n−1),0,−1,−2ti+1,…,−nti+1n−1,(k-i-1)(n+1) 0…0]p=0
加速度连续构成的等式约束为: [ 0 … 0 ⏟ (i-1)(n+1) , 0 , 0 , 2 , … , n ( n − 1 ) t i n − 2 , 0 , 0 , − 2 , … , − n ( n − 1 ) t i + 1 n − 2 , 0 … 0 ⏟ (k-i-1)(n+1) ] p = 0 [\underbrace{0 \ldots 0}_{\text {(i-1)(n+1)}},0,0,2,\ldots,n(n-1)t_{i}^{n-2},0,0,-2,\ldots,-n(n-1)t_{i+1}^{n-2},\underbrace{0 \ldots 0}_{\text {(k-i-1)(n+1)}}]p=0 [(i-1)(n+1) 0…0,0,0,2,…,n(n−1)tin−2,0,0,−2,…,−n(n−1)ti+1n−2,(k-i-1)(n+1) 0…0]p=0
合并所有的等式约束,有
[
1
,
t
0
,
t
0
2
,
…
,
t
0
n
,
0
…
0
⏟
(k-1)(n+1)
0
,
1
,
2
t
0
,
…
,
n
t
0
n
−
1
,
0
…
0
⏟
(k-1)(n+1)
0
,
0
,
2
,
…
,
n
(
n
−
1
)
t
0
n
−
2
,
0
…
0
⏟
(k-1)(n+1)
⋮
0
…
0
⏟
(i-1)(n+1)
,
1
,
t
i
,
t
i
2
,
…
,
t
i
n
,
0
…
0
⏟
(k-i)(n+1)
⋮
0
…
0
⏟
(k-1)(n+1)
,
1
,
t
k
,
t
k
2
,
…
,
t
k
n
0
…
0
⏟
(k-1)(n+1)
,
0
,
1
,
2
t
k
,
…
,
n
t
k
n
−
1
0
…
0
⏟
(k-1)(n+1)
,
0
,
0
,
2
,
…
,
n
(
n
−
1
)
t
k
n
−
2
0
…
0
⏟
(i-1)(n+1)
,
1
,
t
i
,
t
i
2
,
…
,
t
i
n
,
−
1
,
−
t
i
+
1
,
−
t
i
+
1
2
,
…
,
−
t
i
+
1
n
,
0
…
0
⏟
(k-i-1)(n+1)
0
…
0
⏟
(i-1)(n+1)
,
0
,
1
,
2
t
i
,
…
,
n
t
i
(
n
−
1
)
,
0
,
−
1
,
−
2
t
i
+
1
,
…
,
−
n
t
i
+
1
n
−
1
,
0
…
0
⏟
(k-i-1)(n+1)
0
…
0
⏟
(i-1)(n+1)
,
0
,
0
,
2
,
…
,
n
(
n
−
1
)
t
i
n
−
2
,
0
,
0
,
−
2
,
…
,
−
n
(
n
−
1
)
t
i
+
1
n
−
2
,
0
…
0
⏟
(k-i-1)(n+1)
]
(
4
k
+
2
)
×
[
(
n
+
1
)
k
]
⋅
p
=
[
p
0
v
0
a
0
⋮
p
i
⋮
p
k
v
k
a
k
0
⋮
0
]
\begin{bmatrix} 1,t_{0},t_{0}^2,\ldots,t_{0}^n,\underbrace{0\ldots 0}_{\text{(k-1)(n+1)}} \\ 0,1,2t_{0},\ldots,nt_{0}^{n-1},\underbrace{0 \ldots 0}_{\text{(k-1)(n+1)}} \\ 0,0,2,\ldots,n(n-1)t_{0}^{n-2},\underbrace{0 \ldots 0}_{\text{(k-1)(n+1)}} \\ \vdots \\ \underbrace{0 \ldots 0}_{\text{(i-1)(n+1)}},1,t_{i},t_{i}^{2},\ldots,t_{i}^n,\underbrace{0\ldots 0}_{\text {(k-i)(n+1)}} \\ \vdots \\ \underbrace{0 \ldots 0}_{\text{(k-1)(n+1)}},1,t_{k},t_{k}^{2},\ldots,t_{k}^n \\ \underbrace{0 \ldots 0}_{\text{(k-1)(n+1)}},0,1,2t_{k},\ldots,nt_{k}^{n-1} \\ \underbrace{0 \ldots 0}_{\text{(k-1)(n+1)}},0,0,2,\ldots,n(n-1)t_{k}^{n-2} \\ \underbrace{0 \ldots 0}_{\text{(i-1)(n+1)}},1,t_{i},t_{i}^{2},\ldots,t_{i}^n,-1,-t_{i+1},-t_{i+1}^{2},\ldots,-t_{i+1}^n,\underbrace{0\ldots 0}_{\text {(k-i-1)(n+1)}} \\ \underbrace{0 \ldots 0}_{\text{(i-1)(n+1)}},0,1,2t_{i},\ldots,nt_{i}^{(n-1)},0,-1,-2t_{i+1},\ldots,-nt_{i+1}^{n-1},\underbrace{0\ldots 0}_{\text {(k-i-1)(n+1)}} \\ \underbrace{0 \ldots 0}_{\text{(i-1)(n+1)}},0,0,2,\ldots,n(n-1)t_{i}^{n-2},0,0,-2,\ldots,-n(n-1)t_{i+1}^{n-2},\underbrace{0\ldots 0}_{\text {(k-i-1)(n+1)}} \end{bmatrix}_{(4k+2)\times[(n+1)k]} \cdot p \\ =\begin{bmatrix} p_0 \\ v_0 \\ a_0 \\ \vdots \\ p_i \\ \vdots \\ p_k \\ v_k \\ a_k \\ 0 \\ \vdots \\ 0 \end{bmatrix}
⎣⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎡1,t0,t02,…,t0n,(k-1)(n+1)
0…00,1,2t0,…,nt0n−1,(k-1)(n+1)
0…00,0,2,…,n(n−1)t0n−2,(k-1)(n+1)
0…0⋮(i-1)(n+1)
0…0,1,ti,ti2,…,tin,(k-i)(n+1)
0…0⋮(k-1)(n+1)
0…0,1,tk,tk2,…,tkn(k-1)(n+1)
0…0,0,1,2tk,…,ntkn−1(k-1)(n+1)
0…0,0,0,2,…,n(n−1)tkn−2(i-1)(n+1)
0…0,1,ti,ti2,…,tin,−1,−ti+1,−ti+12,…,−ti+1n,(k-i-1)(n+1)
0…0(i-1)(n+1)
0…0,0,1,2ti,…,nti(n−1),0,−1,−2ti+1,…,−nti+1n−1,(k-i-1)(n+1)
0…0(i-1)(n+1)
0…0,0,0,2,…,n(n−1)tin−2,0,0,−2,…,−n(n−1)ti+1n−2,(k-i-1)(n+1)
0…0⎦⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎤(4k+2)×[(n+1)k]⋅p=⎣⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎡p0v0a0⋮pi⋮pkvkak0⋮0⎦⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎤
上述等式约束的个数设定为:
3(起始位置pva)+(k-1)(中间点的p)+3(终点pva)+3(k-1)(中间点pva连续)=4k+2
这里需要注意的是,上述公式中 i i i的范围是从 1 1 1到 k − 1 k-1 k−1,所以中间点的项和最后三个有 i i i的公式并不是一项。
2.4构建不等式约束
不等式的约束和等式约束的设置方式类似,也是设置某个点的 p , v , a p,v,a p,v,a小于某一特定的值,从而构建 A i e q p ≤ b i e q A_{ieq}p \le b_{ieq} Aieqp≤bieq,不等式约束一般是在corridor中使用的比较多,后面会有详细的说明,这里就不赘述了。
2.5求解
不等式建立之后,可以直接丢给非线性求解器求解,比如说Ipopt,OOQP等,具体的自己查一下非线性求解器。
2.6Matlab 例子
这里还是借用大佬的代码,项目地址。
初始条件:
min:snap
等式约束:起点pva,终点pva,中间点的p,中间点pva连续
不等式约束:无
根据上述条件,生成 x , y x,y x,y两个维度的轨迹,合并后如下图所示(包含起始终止共5个点,用四段poly来描述,中间点也就是poly之间的交界点)。
从上图中可以看出,上图规划出的轨迹并不是很好,第二个和第三个路点之间还出现了打结的现象,另外y轴的最大加速度甚至到了20m/s,这明显超过了车辆的物理极限。造成这种现象的最主要的原因,是因为时间分配的不合理,至于时间分配的问题,下面会有详细的讨论。
因此实际轨迹需要进行 feasibility check (可行性检测),确保满足车辆的实际物理可行性,比如前轮偏角,加速度,jerk等。
3.corridor 与时间分配
为了解决上述合理分配时间的问题,现在主要有两个四路:
- 把这些“期望”加入到优化问题中;
- 调整时间分配,避免出现上述问题。
3.1corridor
为了限制规划出的轨迹的形状,引入了corridor的概念,就是人为的添加一条可行通道,如下图,使得规划出的轨迹必须在corridor内。其实也就是给QP问题加入了一个新的条件,这样就可以保证求解出的轨迹在corridor内了。
3.2corridor 如何添加
很容易想到,把之前的等式约束 A p = d Ap=d Ap=d改成不等式约束 A p ≤ d 1 Ap \le d_1 Ap≤d1和 A p ≥ d 2 Ap \ge d_2 Ap≥d2。然而,不管是等式约束还是不等式约束,都是针对一个特定时刻,然而实际希望的是对所有的时刻 t ∈ [ 0 , T ] t \in [0,T] t∈[0,T],都需要在corridor中。
一个简单粗暴的思路:在路径上多采样一些中间点,每个中间点都加corridor约束。尽管这种方法理论上只能保证采样点在corridor中,但实际过程中,如果corridor大小和采样步长设置得合理,而且不Fix waypoints,能得到比较好效果。这里不固定中间点的位置,是为了去掉中间点的强约束,尽量避免轨迹打结,实际中,我们也是不一样要求轨迹完全经过中间点,只要在那附近(corridor)内就行。
3.3构造corridor不等式约束
为了方便构造,对于每一个采样点
p
t
p_t
pt,我们施加一个矩形的corridor,即,
x
m
i
n
≤
p
t
x
≥
x
m
a
x
,
y
m
i
n
≤
p
t
y
≥
y
m
a
x
x_{min} \le p_{tx} \ge x_{max},y_{min} \le p_{ty} \ge y_{max}
xmin≤ptx≥xmax,ymin≤pty≥ymax
这里用矩形corridor是因为斜线corridor(平行四边形)不太好构造,另外我们也只是需要大致放在corridor里面就可以了,对corridor的要求并不是非常的严格,没必要设置一个严格的斜线corridor。
对于每个采样点,设矩形corridor的边长为2r,增加两个位置不等式约束:
[ 1 , t i , t i 2 , … , t i n ] ⋅ p ≤ p ( t i ) + r [ − 1 , − t i , − t i 2 , … , − t i n ] ⋅ p ≤ − [ p ( t i ) + r ] [1,t_{i},t_{i}^{2},\ldots,t_{i}^{n}] \cdot p \le p(t_{i})+r \\ [-1,-t_{i},-t_{i}^{2},\ldots,-t_{i}^{n}] \cdot p \le -[p(t_{i})+r] \\ [1,ti,ti2,…,tin]⋅p≤p(ti)+r[−1,−ti,−ti2,…,−tin]⋅p≤−[p(ti)+r]
大佬的代码在这里:https://github.com/symao/minimum_snap_trajectory_generation
初始条件:
- minmin:snap
- 等式约束:起点 p v a pva pva 3 + 终点 p v a pva pva 3 + 中间点 p v a pva pva 连续 3 × ( n p o l y − 1 ) 3 \times (n_{poly}-1) 3×(npoly−1)个
- 不等式约束:中间点位置 corridor 2 ∗ ( n p o l y − 1 ) 2*(n_{poly}-1) 2∗(npoly−1) 个
优化结果如下图所示,绿色的点为按固定步长新采样的中间点,每个中间点都有一个矩形corridor,红色为优化后的轨迹。
可以看到,去掉中间点的位置强约束(等式约束)改成弱约束(corridor不等式约束)后,轨迹比之前要好很多,而且尽管轨迹有小部分超出了corridor(因为只加了采样时刻的位置corridor约束),但基本不会超出太多。
但这带来的一个问题是,为了保证轨迹不超出corridor,需要采样比较密,但这样轨迹的段数就上来的(轨迹段数=waypoint数量-1),优化参数变多了,计算效率会下降。
针对这个问题,
- 一种思路是通过corridor合并来减少中间点、轨迹段数,从而提高计算效率;
- 另一种思路是,不采样中间点,直接规划输出轨迹之后,找打轨迹的极值点(波峰和波谷),若极值点位置超过corridor,则在极值点上添加一个corridor或者位置强约束(“压点”,把极值点压到corridor以内),以此迭代。实验证明,压有限个点(不超过路径点总数),就能把整段轨迹压到corridor内。
3.4广义corridor
上面介绍了位置corridor,也就是位置的不等式约束,同样的,corridor也适用于速度corridor、加速度
corridor甚至更高阶。
这样是不是就会想,通过这种方式来限制车辆行驶的速度和加速度了。不过,实际情况是,这么做并不合适。
为什么呢?举个简单的例子,假设一维空间规划一条轨迹从0到10,给定总时间T=1s,要求最大限速2m/s,满足要求的轨迹根本不存在,即使你加很多中间点并加速度corridor,得到的轨迹在采样点中间肯定会速度蹭的巨高。
速度、加速度之所以很大,是因为时间给的不合理(时间太小),所以,这种情况光靠加corridor是行不通的,还是要从时间分配的层面来解决。
4.时间分配
时间分配是轨迹规划中很关键的一个问题,会直接影响到规划结果的好坏。
4.1初始时间分配
- 总时间给定:通常给定一个平均速度,根据总路程计算得到总时间。
- 各段时间分配:
匀速分配:假设每段 polynomial 内速度恒定不变,这样各段 poly 的时间比就等于路程比;
梯形分配:假设每段polynomial内速度曲线为:以恒定加速度 a a a从0加速到最大速度 v m a x v_{max} vmax,再以 − a −a −a减速到0。 a a a和 v m a x v_{max} vmax事先给定,就能得出每一段所需要的时间。
设各段的时间分别为
t
1
′
,
t
2
′
,
…
,
t
n
′
t_{1}^{'},t_{2}^{'},\ldots,t_{n}^{'}
t1′,t2′,…,tn′,前
k
k
k项之和就可以得到轨迹分段的时刻向量:
t
0
=
0
t
1
=
t
1
′
t
2
=
t
1
+
t
2
′
…
t
n
=
t
n
−
1
+
t
n
′
t_{0}=0 \\ t_{1}=t_{1}^{'} \\ t_{2}=t_{1}+t_{2}^{'} \\ \ldots \\ t_{n}=t_{n-1}+t_{n}^{'} \\
t0=0t1=t1′t2=t1+t2′…tn=tn−1+tn′
4.2feasibility check
有了时间分配,就可以规划得到轨迹参数,对于轨迹参数,求速度和加速度曲线的极值,判断是否超过最大限幅,如果所有极值都小于最大限幅,则得到可行轨迹。如果不满足,则需要调整该段的时间。
调整的方法是:增大unfeasible段poly的时间,显然,增大时间会使得速度和加速度都变小。通常以一个固定的比例来调整时间如(k=1.2~1.5),并进行迭代,每调整一次,重新规划轨迹并check feasibility,如若不满足,再次加大时间,直至满足为止。
4.3corridor与时间分配的关联
不等式corridor约束实际上也有时间分配的效果,可以这么想,polynomial的分段点(轨迹中间点)在corridor内移动,实际上就相当于分段点的时刻在移动,中间点在corridor内移动,也就变相调整了前后两段poly分配的时间。
举个简单的例子,如下图所示,两段轨迹的分段点为 a a a,初始时间分配为 t 1 , t 2 t1,t2 t1,t2,比如说想减少前一段的时间 t 1 t1 t1,加大后一段的时间 t 2 t2 t2,其实就相当于把分段点 a a a向右挪。也就是说,还是前段poly仍然分配 t 1 t1 t1时间,但走的路程更多了(相当于原来路程所用的时间减少了),后一段poly在 t 2 t2 t2时间走的路程更少了(相当于原来的路程所用时间增加了)。
所以,如果corridor比较大的话,时间调整的空间就会更大,规划出的轨迹会更加的平滑。
4.4归一化时间参数
轨迹规划中容易出现一些数值问题,比如需要求t的高阶指数,如果t比较大,计算数值就会很大,而且最终轨迹的参数会很小,特别是高阶项系数,比如很有可能小于float的精度,所以一般计算过程中最好用double。
但还有一种办法,是把轨迹参数做时间归一化,将时间归一化到 [ 0 , 1 ] [0,1] [0,1]之间。
设未归一化的轨迹
p
(
t
)
=
p
0
+
p
1
t
+
…
+
p
n
t
n
p(t)=p_0+p_1t+\ldots+p_nt^n
p(t)=p0+p1t+…+pntn,令时间
t
‾
=
t
/
T
\overline{t}=t/T
t=t/T,则
p
‾
(
t
‾
)
=
p
(
t
‾
T
)
=
p
0
+
p
1
t
‾
T
+
…
+
p
n
t
‾
n
T
n
=
∑
i
=
0
n
p
‾
i
t
‾
i
\overline{p}(\overline{t})=p(\overline{t}T)=p_{0}+p_{1}\overline{t}T+\ldots+p_{n}\overline{t}^{n}T^{n}=\displaystyle \sum_{i=0}^{n}\overline{p}_{i} \overline{t}^{i}
p(t)=p(tT)=p0+p1tT+…+pntnTn=i=0∑npiti
其中, p ‾ i = p i × T i \overline{p}_{i}=p_{i} \times T^{i} pi=pi×Ti, T T T为轨迹总时间, [ p ‾ i ] i = 0 → n [\overline p_{i}]_{i=0 \to n} [pi]i=0→n就是归一化时间的轨迹参数,它们不像非归一化参数那么小,便于参数传递。
对于任意时刻
t
t
t,先除以总时间
T
T
T得到归一化时间
t
‾
\overline{t}
t,
p
(
t
)
=
p
‾
(
t
‾
)
=
∑
i
=
0
n
p
‾
i
t
‾
i
v
(
t
)
=
p
‾
(
t
‾
)
d
t
=
d
p
‾
(
t
‾
)
d
t
‾
d
t
‾
d
t
=
p
‾
i
′
(
t
‾
)
/
T
a
(
t
)
=
p
‾
i
′
′
(
t
‾
)
/
T
2
p(t)=\overline{p}(\overline{t})=\displaystyle \sum_{i=0}^{n}\overline{p}_{i} \overline{t}^{i} \\ v(t)=\frac {\overline{p}(\overline{t})}{dt}=\frac{d\overline{p}(\overline{t})}{d\overline{t}}\frac{d\overline{t}}{dt}=\overline{p}_{i}^{'}(\overline{t})/T \\ a(t)=\overline{p}_{i}^{''}(\overline{t})/T^{2} \\
p(t)=p(t)=i=0∑npitiv(t)=dtp(t)=dtdp(t)dtdt=pi′(t)/Ta(t)=pi′′(t)/T2
注意:归一化事件参数后,位置计算和原来相同,但速度计算需要除以 T T T,加速度需要除以 T 2 T^{2} T2,即每求一阶导需要除以一个 T T T。
5.闭式求解
如果QP问题只有等式约束没有不等式约束,那么是可以闭式求解(close form)的。闭式求解效率要快很多,而且只需要用到矩阵运算,不需要QPsolver。
5.1QP等式约束构建
闭式求解的
Q
Q
Q矩阵和之前的一样,请看上面的文章,但约束的形式与之前略为不同,在之前的方法中,等式约束只要构造成
A
e
q
⋅
p
=
b
A_{eq}\cdot p =b
Aeq⋅p=b的形式就可以了,而闭式求解中,每段轨迹都可以构造成
A
i
⋅
p
i
=
d
i
,
A
i
=
[
A
0
A
t
]
i
T
,
d
i
=
[
d
0
d
T
]
i
A_{i}\cdot p_{i} =d_{i},A_{i}=[A_{0} \space A_t]^{T}_i,d_{i}=[d_{0} \space d_T]_i
Ai⋅pi=di,Ai=[A0 At]iT,di=[d0 dT]i
其中, d 0 d_0 d0, d T d_T dT为第 i i i段poly的起点和终点的各阶导数组成的向量,比如只考虑 p v a pva pva时: d 0 = [ p 0 , v 0 , a 0 ] T d_0=[p_0,v_0,a_0]^T d0=[p0,v0,a0]T,当然也可以把更高阶的jerk,snap加入到向量。
注意:不管每段的 p v a pva pva是否已知,都要写到向量中。
合并各段轨迹的约束方程得到
A
t
o
t
a
l
⏟
k
(
n
+
1
)
×
6
k
[
p
1
⋮
p
k
]
=
[
d
1
⋮
d
k
]
=
[
p
1
(
t
0
)
v
1
(
t
0
)
a
1
(
t
0
)
p
1
(
t
1
)
v
1
(
t
1
)
a
1
(
t
1
)
⋮
p
k
(
t
k
−
1
)
v
k
(
t
k
−
1
)
a
k
(
t
k
−
1
)
p
k
(
t
k
)
v
k
(
t
k
)
a
k
(
t
k
)
]
⏟
6
k
×
1
\underbrace{A_{total}}_{{k(n+1)\times 6k}} \begin{bmatrix} p_{1} \\ \vdots \\ p_{k} \\ \end{bmatrix} = \begin{bmatrix} d_1 \\ \vdots \\ d_k \\ \end{bmatrix}=\underbrace{\begin{bmatrix} p_1(t_0) \\ v_1(t_0) \\ a_1(t_0) \\ p_1(t_1) \\ v_1(t_1) \\ a_1(t_1) \\ \vdots \\ p_k(t_{k-1}) \\ v_k(t_{k-1}) \\ a_k(t_{k-1}) \\ p_k(t_{k}) \\ v_k(t_{k}) \\ a_k(t_{k}) \\ \end{bmatrix}}_{6k \times 1}
k(n+1)×6k
Atotal⎣⎢⎡p1⋮pk⎦⎥⎤=⎣⎢⎡d1⋮dk⎦⎥⎤=6k×1
⎣⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎡p1(t0)v1(t0)a1(t0)p1(t1)v1(t1)a1(t1)⋮pk(tk−1)vk(tk−1)ak(tk−1)pk(tk)vk(tk)ak(tk)⎦⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎤
上式中, k k k为轨迹段数, n n n为轨迹阶数,设值考虑 p v a pva pva,这里的 A t o t a l A_{total} Atotal的阶数为 ( n + 1 ) k × 6 k (n+1)k \times 6k (n+1)k×6k。
这里为了简化,没有把每段poly的时间戳都改成从0开始,但是实际使用中,为了避免时间戳太大引起的数值问题,每段poly 的时间戳都是设置成从0开始的。
从上式可以看出, A t o t a l A_{total} Atotal是已知的,而 d d d中只有少部分(起点、终点的 p v a pva pva)是已知的,其他的大部分是未知的。如果可以求出 d d d,那么轨迹的参数 p p p可以通过 p = A − 1 d p=A^{-1}d p=A−1d直接求出。
5.2求解d
闭式法的思路是:将 d d d向量中的变量分成两部分:” d d d中所有已知量组成的Fix部分 d F d_{F} dF”和”所有未知量组成的Free部分 d P d_{P} dP”。然后通过推导,根据 d F d_{F} dF求得 d P d_{P} dP,从而得到 d d d,最后求得 p p p。
下面介绍整个推导过程。
5.2.1消除重复变量(连续性约束)
可以会发现,上面构造等式约束时,并没有加入连续性约束,连续性约束并不是直接加到等式约束中。
考虑到连续性(这里假设
p
v
a
pva
pva连续),
d
d
d向量中很多变量其实重复了,即
p
i
(
t
i
)
=
p
i
+
1
(
t
i
)
,
v
i
(
t
i
)
=
v
i
+
1
(
t
i
)
,
a
i
(
t
i
)
=
a
i
+
1
(
t
i
)
p_{i}(t_{i})=p_{i+1}(t_{i}),v_{i}(t_{i})=v_{i+1}(t_{i}),a_{i}(t_{i})=a_{i+1}(t_{i})
pi(ti)=pi+1(ti),vi(ti)=vi+1(ti),ai(ti)=ai+1(ti)
因此需要一个映射矩阵将一个变量映射到两个重复的变量上,怎么映射?
- 如 [ a a ] = [ 1 1 ] a \begin{bmatrix}a \\ a\end{bmatrix}=\begin{bmatrix}1 \\ 1\end{bmatrix}a [aa]=[11]a,将变量 a a a映射到左边向量中的两个变量。
因此,构造映射矩阵
M
6
k
×
3
(
k
+
1
)
M_{6k\times 3(k+1)}
M6k×3(k+1):
[
d
1
⋮
d
k
]
⏟
6
k
×
1
=
[
1
1
1
1
1
1
1
1
1
1
1
1
⋱
]
⏟
M
[
p
(
t
0
)
v
(
t
0
)
a
(
t
0
)
p
(
t
1
)
v
(
t
1
)
a
(
t
1
)
p
(
t
2
)
v
(
t
2
)
a
(
t
2
)
⋮
p
(
t
k
)
v
(
t
k
)
a
(
t
k
)
]
⏟
3
(
k
+
1
)
×
1
\underbrace{\begin{bmatrix} d_1 \\ \vdots \\ d_k \end{bmatrix}}_{6k \times 1}=\underbrace{ \begin{bmatrix} 1 & \space & \space & \space & \space & \space & \space & \space & \space & \space \\ \space & 1 & \space & \space & \space & \space & \space & \space & \space & \space \\ \space & \space & 1 & \space & \space & \space & \space & \space & \space & \space \\ \space & \space & \space & 1 & \space & \space & \space & \space & \space & \space \\ \space & \space & \space & \space & 1 & \space & \space & \space & \space & \space \\ \space & \space & \space & \space & \space & 1 & \space & \space & \space & \space \\ \space & \space & \space & 1 & \space & \space & \space & \space & \space & \space \\ \space & \space & \space & \space & 1 & \space & \space & \space & \space & \space \\ \space & \space & \space & \space & \space & 1 & \space & \space & \space & \space \\ \space & \space & \space & \space & \space & \space & 1 & \space & \space & \space \\ \space & \space & \space & \space & \space & \space & \space & 1 & \space & \space \\ \space & \space & \space & \space & \space & \space & \space & \space & 1 & \space \\ \space & \space & \space & \space & \space & \space & \space & \space & \space & \ddots \\ \end{bmatrix} }_{M} \underbrace{\begin{bmatrix} p(t_0) \\ v(t_0) \\ a(t_0) \\ p(t_1) \\ v(t_1) \\ a(t_1) \\ p(t_2) \\ v(t_2) \\ a(t_2) \\ \vdots \\ p(t_k) \\ v(t_k) \\ a(t_k) \\ \end{bmatrix}}_{3(k+1)\times1}
6k×1
⎣⎢⎡d1⋮dk⎦⎥⎤=M
⎣⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎡1 1 1 1 1 1 1 1 1 1 1 1 ⋱⎦⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎤3(k+1)×1
⎣⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎡p(t0)v(t0)a(t0)p(t1)v(t1)a(t1)p(t2)v(t2)a(t2)⋮p(tk)v(tk)a(tk)⎦⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎤
即 d = M d ′ d=Md^{'} d=Md′。
5.2.2向量元素置换
消除掉重复变量之后,需要调整
d
′
d^{'}
d′中的变量,把fix部分和free部分分开排列,可以构成一个置换矩阵
C
C
C,使得:
d
′
=
C
[
d
F
d
P
]
d^{'}=C\begin{bmatrix} d_{F} \\ d_{P} \\ \end{bmatrix}
d′=C[dFdP]
那么 C C C矩阵如何构造?
- 举个例子,设 d ′ = [ a b c d ] d^{'}=\begin{bmatrix}a \\ b \\ c \\ d \end{bmatrix} d′=⎣⎢⎢⎡abcd⎦⎥⎥⎤,其中 a , c , d a,c,d a,c,d是已知的( d F d_{F} dF), b b b是未知的( d P d_{P} dP),构造一个 4 × 4 4\times4 4×4的单位阵,取 d F d_{F} dF所在的 ( 1 , 3 , 4 ) (1,3,4) (1,3,4)列放在左边,再取 d P d_{P} dP所在的第二列放在右边,就构造出置换矩阵 C C C。
[ a b c d ] = [ 1 0 0 0 0 0 0 1 0 1 0 0 0 0 1 0 ] ⏟ C [ a c d b ] \begin{bmatrix} a \\ b \\ c \\ d \\ \end{bmatrix}=\underbrace{ \begin{bmatrix} 1 & 0 & 0 & 0 \\ 0 & 0 & 0 & 1 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 1 & 0 \\ \end{bmatrix}}_{C} \begin{bmatrix} a \\ c \\ d \\ b \\ \end{bmatrix} ⎣⎢⎢⎡abcd⎦⎥⎥⎤=C ⎣⎢⎢⎡1000001000010100⎦⎥⎥⎤⎣⎢⎢⎡acdb⎦⎥⎥⎤
5.2.3 转成无约束优化问题
由上面两步可得
d
=
M
C
[
d
F
d
P
]
p
=
A
−
1
d
=
A
−
1
M
C
⏟
K
[
d
F
d
P
]
=
K
[
d
F
d
P
]
d=MC\begin{bmatrix} d_{F} \\ d_{P} \end{bmatrix} \\ p=A^{-1}d=\underbrace{A^{-1}MC}_{K}\begin{bmatrix} d_{F} \\ d_{P} \end{bmatrix} =K\begin{bmatrix} d_{F} \\ d_{P} \end{bmatrix}
d=MC[dFdP]p=A−1d=K
A−1MC[dFdP]=K[dFdP]
代入优化函数
m
i
n
J
=
p
T
Q
p
J
=
[
d
F
d
P
]
T
K
T
Q
K
⏟
R
[
d
F
d
P
]
=
[
d
F
d
P
]
T
[
R
F
F
R
F
P
R
P
F
R
P
P
]
[
d
F
d
P
]
=
d
F
T
R
F
F
d
F
+
d
F
T
R
F
P
d
P
+
d
P
T
R
P
F
d
F
+
d
P
T
R
P
P
d
P
minJ=p^{T}Qp \\ J=\begin{bmatrix} d_{F} \\ d_{P} \end{bmatrix}^{T} \underbrace{K^{T}QK}_{R} \begin{bmatrix} d_{F} \\ d_{P} \end{bmatrix} = \begin{bmatrix} d_{F} \\ d_{P} \end{bmatrix}^{T} \begin{bmatrix} R_{FF} & R_{FP} \\ R_{PF} & R_{PP} \end{bmatrix} \begin{bmatrix} d_{F} \\ d_{P} \end{bmatrix} \\ =d^{T}_{F}R_{FF}d_{F}+d^{T}_{F}R_{FP}d_{P}+d^{T}_{P}R_{PF}d_{F}+d^{T}_{P}R_{PP}d_{P}
minJ=pTQpJ=[dFdP]TR
KTQK[dFdP]=[dFdP]T[RFFRPFRFPRPP][dFdP]=dFTRFFdF+dFTRFPdP+dPTRPFdF+dPTRPPdP
Q 对 称 ⇒ R 对 称 ⇒ = d F T R F F d F + 2 d F T R F P d P + d P T R P P d P Q对称 \Rarr R对称 \Rarr =d^{T}_{F}R_{FF}d_{F}+2d^{T}_{F}R_{FP}d_{P}+d^{T}_{P}R_{PP}d_{P} Q对称⇒R对称⇒=dFTRFFdF+2dFTRFPdP+dPTRPPdP
令 J J J对 d P d_{P} dP的倒数 ∂ J ∂ d P = 0 \frac{\partial J}{\partial d_{P}}=0 ∂dP∂J=0,则有极值点,
2 d F T R F P + d P T R P P d P = 0 d P = − R P P − 1 R F P T d F 2d^{T}_{F}R_{FP}+d^{T}_{P}R_{PP}d_{P}=0 \\ d_{P}=-R_{PP}^{-1}R_{FP}^{T}d_{F} 2dFTRFP+dPTRPPdP=0dP=−RPP−1RFPTdF
5.3闭式求解步骤
现在做一下总结:
- 先确定轨迹阶数(比如5阶),再确定dd向量中的约束量( p v a pva pva),进而根据各段的时间分配求得 A t o t a l A_{total} Atotal;
- 根据连续性约束构造映射矩阵 M M M,并确定 d d d向量中哪些量是Fix(比如起点终点 p v a pva pva,中间点的 p p p等),哪些量是Free,进而构造置换矩阵 C C C,并求得 K = A − 1 M C K=A^{−1}MC K=A−1MC;
- 计算QP目标函数中的Q( m i n J e r k / S n a p minJerk/Snap minJerk/Snap)并计算 R = K T Q K R=K^TQK R=KTQK,根据fix变量的长度将R拆分成 R F F , R F P , R P F , R P P R_{FF},R_{FP},R_{PF},R_{PP} RFF,RFP,RPF,RPP四块;
- 填入已知变量得到 d F d_F dF,并根据 d P = − R P P − 1 R F P T d F d_{P}=−R^{−1}_{PP}R^T_{FP}d_F dP=−RPP−1RFPTdF计算得到 d P d_P dP;
- 根据公式 p = K [ d F d P ] p=K\begin{bmatrix}d_{F}\\ d_{P}\end{bmatrix} p=K[dFdP]计算得到轨迹参数 p p p;
- 闭式法主要计算量就在A矩阵的求逆,其他计算基本上是矩阵构造,所以效率比较高,但由于没有不等式约束,所以在中间点只能加强约束,corridor不能直接加到QP问题中,只能是通过压点来实现corridor。
在对计算效率要求比较高或者不想用QPsolver时,可以使用闭式法求解。最后附上大神的代码。
参考文献:
- Richter C, Bry A, Roy N. Polynomial trajectory planning for
aggressive quadrotor flight in dense indoor
environments[M]//Robotics Research. Springer International
Publishing, 2016: 649-666. - Vijay Kumar的一系列论文:Mellinger D, Kumar V. Minimum snap trajectory
generation and control for quadrotors[C]//Robotics and Automation
(ICRA), 2011 IEEE International Conference on. IEEE, 2011:
2520-2525. - HKUST 沈劭劼老师的工作:Chen J, Liu T, Shen S. Online generation of
collision-free trajectories for quadrotor flight in unknown
cluttered environments[C]//Robotics and Automation (ICRA), 2016 IEEE
International Conference on. IEEE, 2016: 1476-1483.