前言
本文记录了自己用于学习MPC控制移动底盘的代码,目的在于学习移动底盘运动模型的建立和使用CppAD库求解控制量以使得底盘可以跟踪预先给定的路径。
为了简单起见,选取了最简单的差速底盘
作为对象,而不是阿克曼底盘。
实现效果:
mpc-1
整体代码运行在ROS
中
零、涉及到的坐标系
在路径跟踪中主要涉及到3个坐标系
:
- 全局坐标系 World
- 机器人底盘坐标系 Robot
- 参考轨迹中的frenet坐标 Frenet
一、控制量
差速底盘的控制量非常简单:
U
=
[
v
x
,
ω
]
\bm{U}=[v_x, \omega]
U=[vx,ω] 其中
v
x
v_x
vx是在车坐标系下沿x轴方向的速度,
ω
\omega
ω为旋转角速度。
二、状态变量
为了跟踪路径,设置状态变量如下:
X
=
[
x
,
y
,
δ
,
c
t
e
,
p
e
s
i
]
\bm{X}=[x, y, \delta, cte, pesi]
X=[x,y,δ,cte,pesi]这里面最主要的问题是确定状态变量是处于哪个坐标系
为了更好表示车辆和轨迹之间的误差,选择将状态在frenet坐标系下表示。
补充
有很多路径跟踪MPC的实现选用了在机器人底盘坐标系下进行实现,可以理解为“线找车”,将底盘坐标系作为参考,这样就需要重新定义误差(其实就是取相反值),两种状态变量所在坐标系的选择方式在工程中的表现还有待测试。
x
x
x: frenet坐标系下x坐标
y y y: frenet坐标系下y坐标
δ \delta δ: frenet坐标系下 车辆的航向角
c t e cte cte: frenet坐标系下 车辆对于轨迹的横向偏离误差
p e s i pesi pesi: frenet坐标系下航向角的偏离误差
这里我在一开始有一个绕进去的点,从图中看, c t e cte cte的值就是 y y y的值, p e s i pesi pesi的值就是 δ \delta δ的值。所以似乎不需要这两个值,但是实际上在这一时刻进行预测控制的时候,会生成多个预测的状态,而这些预测状态中的 x x x, y y y, δ \delta δ都是在这一时刻的frenet坐标系下,如果以目标是使 y y y , δ \delta δ为0,则会导致预测以这一时刻的 X F X_F XF轴为参考曲线。
与 x x x, y y y, δ \delta δ更新方式不同,在进行这一时刻的 c t e cte cte和 p e s i pesi pesi的值的预测的时候,会把轨迹的变化情况作为参考值进行更新加入更新的方程中。其中轨迹的变化情况可以使用3次或者5次多项式曲线的方式对离散的坐标点进行描述。
c
t
e
cte
cte和
p
e
s
i
pesi
pesi的表示与参考轨迹的变化有关(误差 = 测量值 - 参考值):
c
t
e
k
=
y
k
−
y
r
e
f
e
p
s
i
k
=
δ
k
−
δ
r
e
f
cte_k = y_k -y_{ref} \\ epsi_k = \delta_k - \delta_{ref} \\
ctek=yk−yrefepsik=δk−δref当使用多项式曲线对轨迹进行拟合参数化之后,参考轨迹在
x
k
x_k
xk处的横向距离
y
r
e
f
y_{ref}
yref的值为
f
(
x
k
)
f(x_k)
f(xk)
而在 x k x_k xk处的航向角参考值为 a r c t a n ( f ′ ( x k ) ) arctan(f'(x_k)) arctan(f′(xk)),换言之,曲线上各处的参考值的变化是由 x x x的函数。
三、微分约束
x
˙
=
v
x
c
o
s
δ
y
˙
=
v
x
s
i
n
δ
δ
˙
=
ω
\dot{x} = v_x cos\delta \\ \dot{y} = v_x sin\delta \\ \dot{\delta} = \omega \\
x˙=vxcosδy˙=vxsinδδ˙=ω控制量
v
x
v_x
vx在frenet坐标系下对机器人在x轴,y轴变化的贡献量,以及角速度
ω
\omega
ω对于
δ
\delta
δ贡献量显而易见。
但是控制是离散的,所以需要将运动模型的更新和误差的更新离散化(省略速度控制量
v
x
v_x
vx的下标x):
x
k
+
1
−
x
k
=
v
k
⋅
c
o
s
δ
⋅
d
t
y
k
+
1
−
y
k
=
v
k
⋅
s
i
n
δ
⋅
d
t
δ
k
+
1
−
δ
k
=
ω
k
⋅
d
t
c
t
e
k
+
1
−
c
t
e
k
=
v
k
⋅
s
i
n
δ
⋅
d
t
e
p
s
i
k
+
1
−
e
p
s
i
k
=
ω
k
⋅
d
t
x_{k+1}-x_k = v_k\cdot cos\delta \cdot dt \\ y_{k+1}-y_k = v_k \cdot sin\delta \cdot dt \\ \delta_{k+1}-\delta_k = \omega_k \cdot dt \\ cte_{k+1} - cte_k = v_k \cdot sin\delta \cdot dt \\ epsi_{k+1} - epsi_k = \omega_k \cdot dt
xk+1−xk=vk⋅cosδ⋅dtyk+1−yk=vk⋅sinδ⋅dtδk+1−δk=ωk⋅dtctek+1−ctek=vk⋅sinδ⋅dtepsik+1−epsik=ωk⋅dt离散化后的模型建立了时间上相邻的两个状态之间的关系。实际底盘的移动需要满足这一组模型。
对于误差的更新,不妨将参考值的表示加入进来:
c
t
e
k
+
1
=
y
k
−
y
r
e
f
+
v
k
⋅
s
i
n
δ
⋅
d
t
e
p
s
i
k
+
1
=
δ
k
−
δ
r
e
f
+
ω
k
⋅
d
t
cte_{k+1} = y_k - y_{ref} + v_k \cdot sin\delta \cdot dt \\ epsi_{k+1} = \delta_k - \delta_{ref} + \omega_k \cdot dt
ctek+1=yk−yref+vk⋅sinδ⋅dtepsik+1=δk−δref+ωk⋅dt我对于这两个式子的理解是,在
k
+
1
k+1
k+1时刻的误差,是上一时刻
k
k
k的误差加上两个时刻之间相应状态或者控制量的对误差的影响。
上边建立的微分关系将会作为优化问题的等式约束加入进去。
四、目标函数
- 控制量尽可能小
J 1 = ∑ k N p − 1 ( w v ⋅ ( v k − v r e f ) 2 + w ω ⋅ ( ω k ) 2 ) J_1 = \sum_k^{N_p - 1}( w_v \cdot(v_k - v_{ref})^2 + w_{\omega} \cdot(\omega_k)^2) J1=k∑Np−1(wv⋅(vk−vref)2+wω⋅(ωk)2) - 跟踪误差尽可能小
J 2 = ∑ k N p ( w c t e ⋅ ( c t e k ) 2 + w e p s i ⋅ ( e p s i k ) 2 ) J_2 = \sum_k^{N_p}( w_{cte} \cdot(cte_k)^2 + w_{epsi} \cdot(epsi_k)^2) J2=k∑Np(wcte⋅(ctek)2+wepsi⋅(epsik)2)其中 N p N_p Np是预测的步长,控制量减一是因为最后一个时间不长可以不预测。
最基础可以使用 J 1 + J 2 J_1 + J_2 J1+J2作为最小化的目标函数,如果想要更平滑一些,也可以限制相邻控制量之间的变化等等。