1. 车辆运动学模型选择
采用以后轴为中心的自行车模型(简化二维平面运动):
{ x ˙ = v ⋅ cos ( ψ ) y ˙ = v ⋅ sin ( ψ ) ψ ˙ = v L ⋅ tan ( δ ) \begin{cases} \dot{x} = v \cdot \cos(\psi) \\ \dot{y} = v \cdot \sin(\psi) \\ \dot{\psi} = \frac{v}{L} \cdot \tan(\delta) \\ \end{cases} ⎩ ⎨ ⎧x˙=v⋅cos(ψ)y˙=v⋅sin(ψ)ψ˙=Lv⋅tan(δ)
- (x,y):车辆后轴中心坐标
- ( ψ \psi ψ):航向角
- (v):纵向速度(假设恒定)
- (L):轴距
- ( δ \delta δ):前轮转向角(控制输入)
2. 横向误差定义
- 航向误差:( e ψ e_{\psi} eψ = ψ r e f \psi_{ref} ψref - ψ \psi ψ)
- 横向位置误差:( e y e_y ey = y r e f y_{ref} yref - y y y)
- 误差动态方程:( e ˙ y \dot{e}_y e˙y = v ⋅ sin ( e ψ ) v \cdot \sin(e_{\psi}) v⋅sin(eψ))
3. 滑模面设计
选择包含误差和积分项的滑模面:
s
=
e
y
+
λ
∫
e
y
d
t
+
k
⋅
e
ψ
s = e_y + \lambda \int e_y dt + k \cdot e_{\psi}
s=ey+λ∫eydt+k⋅eψ
- ( λ \lambda λ, k k k):权重系数,调节动态响应
4. 控制律推导
等效控制:使滑模面导数为零
s
˙
=
e
˙
y
+
λ
e
y
+
k
e
˙
ψ
=
0
\dot{s} = \dot{e}_y + \lambda e_y + k \dot{e}_{\psi} = 0
s˙=e˙y+λey+ke˙ψ=0
代入动态方程后解得:
δ
e
q
=
arctan
(
L
v
⋅
(
λ
e
y
+
k
⋅
ψ
˙
r
e
f
v
)
)
\delta_{eq} = \arctan\left( \frac{L}{v} \cdot \left( \frac{\lambda e_y + k \cdot \dot{\psi}_{ref}}{v} \right) \right)
δeq=arctan(vL⋅(vλey+k⋅ψ˙ref))
期望航向角变化率由路径曲率
κ
\kappa
κ决定,即
ψ
˙
r
e
f
=
v
κ
\dot{\psi}_{ref}=v\kappa
ψ˙ref=vκ
切换控制:补偿模型不确定性和扰动
δ
s
w
=
−
K
⋅
sat
(
s
/
Φ
)
\delta_{sw} = -K \cdot \text{sat}(s/\Phi)
δsw=−K⋅sat(s/Φ)
- ( K K K):切换增益
- ( Φ \Phi Φ):边界层厚度
- ( s a t {sat} sat):饱和函数(减少抖振)
总控制量:
δ
=
δ
e
q
+
δ
s
w
\delta = \delta_{eq} + \delta_{sw}
δ=δeq+δsw
C++代码实现框架
#include <cmath>
#include <vector>
// 车辆状态结构体
struct VehicleState {
double x; // X坐标
double y; // Y坐标
double psi; // 航向角
double v; // 速度
};
// 参考路径点结构体
struct ReferencePoint {
double x_ref; // 参考X坐标
double y_ref; // 参考Y坐标
double psi_ref; // 参考航向角
double kappa_ref; // 参考曲率
};
class SlidingModeController {
private:
double L; // 轴距
double lambda; // 积分项权重
double k; // 航向误差权重
double K; // 切换增益
double phi; // 边界层厚度
double integral_ey; // 横向误差积分
public:
SlidingModeController(double wheelbase, double lambda_coeff,
double k_coeff, double K_gain, double boundary)
: L(wheelbase), lambda(lambda_coeff), k(k_coeff),
K(K_gain), phi(boundary), integral_ey(0.0) {}
// 计算前轮转向角
double computeSteeringAngle(const VehicleState& current_state,
const ReferencePoint& ref_point,
double dt) {
// 计算横向误差和航向误差
double e_y = ref_point.y_ref - ref_point.y;
double e_psi = ref_point.psi_ref - current_state.psi;
// 更新误差积分
integral_ey += e_y * dt;
// 计算滑模面
double s = e_y + lambda * integral_ey + k * e_psi;
// 等效控制项(需根据模型推导具体形式)
double psi_ref_dot = current_state.v * ref_point.kappa_ref;
double delta_eq = atan((L / current_state.v) *
(lambda * e_y + k * psi_ref_dot) / current_state.v);
// 切换控制项
double delta_sw = -K * saturation(s / phi);
// 总控制量
double delta = delta_eq + delta_sw;
return delta;
}
private:
// 饱和函数代替符号函数减少抖振
double saturation(double value) {
if (value > 1.0) return 1.0;
if (value < -1.0) return -1.0;
return value;
}
};
// 示例用法
int main() {
// 初始化控制器参数
SlidingModeController controller(2.8, 0.5, 0.3, 1.2, 0.1);
// 当前车辆状态
VehicleState state{0.0, 0.0, 0.0, 10.0}; // x=0, y=0, psi=0, v=10m/s
// 参考路径点(假设由规划模块提供)
ReferencePoint ref{5.0, 1.0, 0.1}; // x_ref=5, y_ref=1, psi_ref=0.1rad
// 控制周期(例如0.02秒)
double dt = 0.02;
// 计算转向角指令
double delta = controller.computeSteeringAngle(state, ref, dt);
// 输出delta给执行机构
return 0;
}
关键实现细节说明
模块 | 实现说明 |
---|---|
误差计算 | 横向误差(e_y)通过当前车辆位置与参考路径差值计算,航向误差(e_{\psi})同理 |
积分项处理 | 使用欧拉法进行离散积分,避免累计误差溢出 |
饱和函数 | 用tanh 或分段线性函数替代符号函数,减少高频抖振 |
参数调试 | 通过仿真调整(\lambda, k, K, \Phi)平衡响应速度与稳定性 |
参考路径接口 | 实际需实现轨迹插值或预瞄点搜索(如Pure Pursuit逻辑) |
扩展优化方向
- 动态调整边界层:根据车速自适应调整(\Phi),高速时增大减少抖动。
- 前馈补偿:加入路径曲率前馈项提升跟踪精度。
- 抗积分饱和:限制积分项幅值,避免长时间误差累积。
- 联合仿真验证:通过CarSim/Simulink验证算法鲁棒性。