LQR制导
引言
在ardupilot中固定翼飞机横航向位置控制(制导律)采用L1制导律,最近想将一些其他的控制理论用于ardupilot代码中,通过ardupilot论坛,看到已经有大佬曾经将L1用于固定翼制导律,并且已经向ardupilot官方github中pr了,但是由于pr的方式有误,被官方驳回了,但是我还是通过连接查到了大佬的代码,并且自己尝试了一下,已经编译通过并进行了仿真,后面我会把链接放到文章结尾。欢迎大家一起来讨论。
LQR理论
LQR(线性二次调节器)是一种最优控制方法,网上已经有很多关于其理论的介绍,在这里就不多介绍。核心思想就是通过给定合理的状态权重矩阵Q和控制权重矩阵R,求解最优控制量u,这里的控制变量为横向将速度
a
s
c
m
d
a_{scmd}
ascmd,状态量为位置误差
d
d
d及其导数及其导数
v
d
v_d
vd,即
[
d
,
v
d
]
T
[d,v_d]^T
[d,vd]T,LQR理论部分可以参考博客lqr理论。
根据LQR理论以及参考的论文,可总结为以下几点:
固定翼飞机在空中飞行的运动方程为:
d
˙
=
v
s
i
n
(
ψ
−
ψ
p
)
(theory-1)
\dot d=vsin(\psi-\psi_p)\tag {theory-1}
d˙=vsin(ψ−ψp)(theory-1)
其中,d为飞机速度方向与期望路径方向的直线距离,
ψ
\psi
ψ为航向角,
ψ
p
\psi_p
ψp为期望航向角。
侧向加速度
a
s
c
m
d
a_{scmd}
ascmd为:
a
s
c
m
d
=
u
=
v
˙
d
=
v
ψ
˙
(theory-2)
a_{scmd}=u=\dot v_d=v\dot \psi\tag {theory-2}
ascmd=u=v˙d=vψ˙(theory-2)
状态方程为:
x
˙
=
A
x
+
B
u
(theory-3)
\dot x=Ax+Bu\tag {theory-3}
x˙=Ax+Bu(theory-3)
其中:
x
=
[
d
v
d
]
,
根
据
公
式
(
2
)
可
得
:
A
=
[
0
1
0
0
]
,
B
=
[
0
1
]
x=\left[ \begin{matrix}d \\ v_d\end{matrix} \right],根据公式(2) 可得:A=\left[ \begin{matrix} 0 & 1 \\ 0 & 0\end{matrix}\right],B=\left[ \begin{matrix}0\\ 1\end{matrix} \right]
x=[dvd],根据公式(2)可得:A=[0010],B=[01]
有了AB阵便可按照LQR的步骤进行求解:
(1)选取参考矩阵Q,R。
(2)根据Riccati方程得到矩阵P。
(3)求u。
首先定义Q矩阵和R矩阵:
我们假设:
R
=
1
(theory-4)
R=1\tag {theory-4}
R=1(theory-4)
Q
=
[
q
1
2
0
0
q
2
2
]
(theory-5)
Q=\left[ \begin{matrix}q_1^2 & 0 \\ 0&q_2^2\end{matrix} \right]\tag {theory-5}
Q=[q1200q22](theory-5)
然后定义矩阵P:
P
=
[
p
11
p
12
p
21
p
22
]
(theory-6)
P=\left[ \begin{matrix}p_{11} & p_{12} \\ p_{21}&p_{22}\end{matrix} \right]\tag {theory-6}
P=[p11p21p12p22](theory-6)
根据Riccati方程可知A,B,Q,R的关系:
A
T
P
+
P
A
−
P
B
R
−
1
B
T
+
Q
=
0
(theory-7)
A^TP+PA-PBR^{-1}B^T+Q=0\tag {theory-7}
ATP+PA−PBR−1BT+Q=0(theory-7)
根据式7可得P和Q元素的对应关系:
p
12
=
q
1
(theory-8)
p_{12}=q_1 \tag {theory-8}
p12=q1(theory-8)
p
22
=
2
q
1
+
q
2
2
(theory-9)
p_{22}=\sqrt{2q_1+q_2^2}\tag {theory-9}
p22=2q1+q22(theory-9)
p
11
=
q
1
2
q
1
+
q
2
2
(theory-10)
p_{11}=q_1\sqrt{2q_1+q_2^2}\tag {theory-10}
p11=q12q1+q22(theory-10)
最优控制给出:
u
=
−
R
−
1
B
T
P
x
(theory-11)
u=-R^{-1}B^TPx \tag {theory-11}
u=−R−1BTPx(theory-11)
上文已经退出P和Q的关系,所以可以将控制量
u
u
u以已知量R,B,P表示,最终得出:
a
s
c
m
d
=
u
=
−
[
q
1
d
+
2
q
1
+
q
2
2
⋅
v
d
]
(theory-12)
a_{scmd}=u=-[q_1d+\sqrt{2q_1+q_2^2}\cdot v_d]\tag {theory-12}
ascmd=u=−[q1d+2q1+q22⋅vd](theory-12)
q
1
2
=
d
b
d
b
−
d
,
q
2
2
=
1
(theory-13)
q_1^2=\frac{d_b}{d_b-d},q_2^2=1\tag {theory-13}
q12=db−ddb,q22=1(theory-13)
所以得:
a
s
c
m
d
=
−
[
∣
d
b
d
b
−
d
∣
d
+
2
∣
d
b
d
b
−
d
∣
+
1
⋅
v
d
(theory-14)
a_{scmd}=-[\sqrt{|\frac{d_b}{d_b-d}|}d+\sqrt{2\sqrt{|\frac{d_b}{d_b-d}}|+1}\cdot v_d\tag {theory-14}
ascmd=−[∣db−ddb∣d+2∣db−ddb∣+1⋅vd(theory-14)
部分代码展示
代码与理论的对应关系:
v
d
=
V
c
o
s
(
ψ
−
ψ
)
(code-1)
v_d=V cos(\psi - {\psi}) \tag {code-1}
vd=Vcos(ψ−ψ)(code-1)
p
12
=
q
1
c
o
s
(
ψ
−
ψ
p
)
(code-2)
p_{12} =\frac{q_1}{cos(\psi - {\psi}_p)}\tag {code-2}
p12=cos(ψ−ψp)q1(code-2)
p
22
=
2
p
12
+
q
2
2
c
o
s
(
ψ
−
ψ
p
)
(code-3)
p_{22}=\frac{\sqrt{{2p_{12}+q_2^2}}}{cos(\psi - {\psi}_p)}\tag {code-3}
p22=cos(ψ−ψp)2p12+q22(code-3)
a
c
s
d
=
−
(
d
⋅
p
12
⋅
c
o
s
(
ψ
−
ψ
)
+
p
22
⋅
v
d
⋅
c
o
s
(
ψ
−
ψ
)
)
(code-4)
a_{csd}=-( d \cdot p_{12} \cdot cos(\psi - \psi)+p_{22} \cdot v_d \cdot cos(\psi - \psi)) \tag {code-4}
acsd=−(d⋅p12⋅cos(ψ−ψ)+p22⋅vd⋅cos(ψ−ψ))(code-4)
将(2)与(4)带入(4)可得:
a
s
c
m
d
=
−
[
q
1
d
+
2
p
12
+
q
2
2
⋅
v
d
]
(code-5)
a_{scmd} = -[ q_1 d+ \sqrt{2p_{12}+q_2^2} \cdot v_d]\tag {code-5}
ascmd=−[q1d+2p12+q22⋅vd](code-5)
接下来说下q的选取:
在代码中,自适应增益
q
1
q_1
q1为:
q
1
=
K
⋅
∣
d
∣
(code-6)
q_1=\sqrt{K \cdot |d|}\tag {code-6}
q1=K⋅∣d∣(code-6)
q
2
q_2
q2是自定义的值,在论文中定义
q
2
2
=
1
q_2^2=1
q22=1,在代码中
q
2
q_2
q2为地面站可调节的参数,默认值同论文。
代码:
//更新LQR控制用于航点导航
void AP_LQR_Control::update_waypoint(const struct Location &prev_WP, const struct Location &next_WP, float dist_min)
{
//定义当前位置
struct Location _current_loc;
uint32_t now = AP_HAL::micros();
float dt = (now - _last_update_waypoint_us) * 1.0e-6f;
if(dt > 0.1f){
dt = 0.1f;
}
_last_update_waypoint_us = now;
//获取当前你位置和速度
if(_ahrs.get_position(_current_loc) == false){
//如果没有GPS信号,维持上一次航向角
_data_is_stale = true;
return;
}
Vector2f _groundspeed_vector = _ahrs.groundspeed_vector();
//更新_target_bearing_cd(航向角)
_target_bearing_cd = _current_loc.get_bearing_to(next_WP);
//计算地速
float groundSpeed = _groundspeed_vector.length();
if(groundSpeed < 0.1f){
groundSpeed = 0.1f;
_groundspeed_vector = Vector2f(cosf(get_yaw_rad()), sinf(get_yaw_rad())) * groundSpeed;
}
//计算NE坐标系下B相对于A点的距离
Vector2f AB = prev_WP.get_distance_NE(next_WP);
//检查AB的长度,判断追踪距离是否太短
if(AB.length() < 1.0e-6f){
AB = _current_loc.get_distance_NE(next_WP);
if(AB.length() < 1.0e-6f){
AB = Vector2f(cosf(get_yaw_rad()), sinf(get_yaw_rad()));
}
}
//单位化
AB.normalize();
//计算飞机相对于A点NE位置
Vector2f A_air = prev_WP.get_distance_NE(_current_loc);
//计算目标追踪
_crosstrack_error = (A_air % AB)/(AB.length());
_crosstrack_error = -_crosstrack_error;
//计算自适应增益
//exp为以e为底的指数函数
float q1 = sqrtf(float(exp(_k_val * abs(_crosstrack_error))));
//计算路径上的跟踪速度
float si = get_gs_angle_cd() * 0.01f;
float si_p = (prev_WP.get_bearing_to(next_WP)) * 0.01;
float temp_sin = sinf(radians(si - si_p));
float v_d = groundSpeed * temp_sin;
float cos_term = cosf(radians(si - si_p));
if(fabs(cos_term) < 0.01)
{
if(cos_term < 0 ){
cos_term = float(-0.01);
}else{
cos_term = float(0.01);
}
}
float p12 = float(q1/fabs(cos_term));
float p22 = sqrtf((float)(2*p12 + _q2_val)) / (cos_term);
//基于当前状态和自适应增益计算横向加速度
float u = -(_crosstrack_error * p12 * cosf(radians(si - si_p)) + p22 * v_d * cosf(radians(si - si_p)));
_latAccDem = u;
_bearing_error = radians(si - si_p);
_nav_bearing = radians(si_p);
//在航路点跟踪期间,航路点捕获状态始终为false
_WPcircle = false;
_data_is_stale = false;//使用当前航路点数据正确更新状态
}
reference
- https://github.com/ArduPilot/ardupilot/pull/10037/files
- Ratnoo A, Sujit P B, Kothari M. Adaptive optimal path following for high wind flights[J]. IFAC Proceedings Volumes, 2011, 44(1): 12985-12990.