4. 横向控制基础(一):车辆运动学模型(自行车模型)、横向误差动力学建模
各位同学,欢迎来到横向控制的第一课。
说实话,做控制这么多年,我最大的体会就是:模型是控制的天花板。你算法再花哨,模型不准,上了车就是一顿乱晃。今天咱们先把横向控制最底层的两个东西讲透——自行车模型和横向误差动力学。
4.1 为什么是自行车模型?
你想想看,一辆四轮车,四个轮子各有转向、各有速度,建模起来多复杂?
但在横向控制里,我们其实只关心一件事:车头往哪转,车尾怎么跟。所以业界普遍的做法是——把四轮车简化成两轮车。这就是自行车模型(Bicycle Model)。
核心假设:
- 忽略车辆的侧倾、俯仰(当成刚体)
- 左右轮合并为一个虚拟轮(前轮转向、后轮驱动)
- 轮胎侧偏特性线性化(小角度假设)
我在项目里遇到过一个问题:某次做高速车道保持,用四轮模型算出来的控制量抖得厉害。后来换成自行车模型,反而稳了。为什么?因为自行车模型把不必要的自由度砍掉了,控制器反而更鲁棒。
4.2 自行车模型的数学表达
咱们直接上干货。自行车模型的状态量通常取:
状态向量: [x, y, ψ, v, δ]
- x, y: 车辆后轴中心位置(大地坐标系)
- ψ: 航向角(车头朝向与X轴夹角)
- v: 纵向速度(沿车体方向)
- δ: 前轮转角
运动学方程长这样:
dx/dt = v * cos(ψ)
dy/dt = v * sin(ψ)
dψ/dt = v * tan(δ) / L
dv/dt = a(纵向加速度,控制输入)
dδ/dt = ω(转向角速度,控制输入)
其中 L 是轴距。嗯,这里要注意:tan(δ) 在小角度下可以近似为 δ,但如果你做高速控制(比如80km/h以上),千万别这么近似——我吃过这个亏,高速下小角度假设会带来明显的稳态误差。
| 参数 | 含义 | 典型值(乘用车) |
|---|---|---|
| L | 轴距 | 2.5~3.0 m |
| δ_max | 最大前轮转角 | ±30° ~ ±40° |
| v | 纵向速度 | 0~30 m/s |
个人习惯:我一般把自行车模型写成离散形式,采样时间取10ms~20ms。这样方便直接丢进MPC或者LQR里用。
4.3 横向误差动力学建模
有了自行车模型,我们就能描述车辆怎么动了。但控制的目标是什么?是让车沿着一条期望路径走。所以我们需要一个误差模型——描述车辆实际状态与期望状态之间的偏差。
横向误差通常定义两个量:
- 横向位置误差 e_y:车辆质心到期望路径的最短距离
- 航向角误差 e_ψ:车辆实际航向角与路径切线方向的夹角
我建议你记住下面这个推导过程,面试经常考:
设期望路径的曲率为 κ_ref,期望航向角为 ψ_ref
则:
e_y_dot = v * sin(e_ψ) ≈ v * e_ψ(小角度近似)
e_ψ_dot = ψ_dot - ψ_ref_dot
= v * tan(δ)/L - v * κ_ref
写成状态空间形式:
[e_y_dot] [0 v] [e_y] [0] [0 ]
[e_ψ_dot] = [0 0] [e_ψ] + [v/L] * δ + [-v] * κ_ref
我曾经踩过的坑:这个模型里隐含了一个假设——期望路径的曲率变化缓慢。如果你做泊车场景,路径曲率突变很大,这个模型会失效。那时候就得用更复杂的动力学模型了。
4.4 误差模型的物理意义
说白了,横向控制就是干两件事:
- 把车拉回路径上(消除 e_y)
- 让车头对准路径方向(消除 e_ψ)
你想想看,如果 e_y 很大但 e_ψ 很小,车会平行于路径但偏离很远——这时候需要大角度转向切入。反过来,如果 e_ψ 很大但 e_y 很小,车在路径上但车头歪着——需要快速回正方向。
我在做车道保持项目时,发现一个有趣的现象:新手工程师往往只盯着 e_y 调参数,结果车来回晃。其实 e_ψ 的权重往往要更大,因为航向误差会积分成位置误差。
4.5 代码实现示例
下面给一个简单的Python实现,把自行车模型和误差模型串起来:
import numpy as np
class BicycleModel:
def __init__(self, L=2.8, dt=0.02):
self.L = L # 轴距
self.dt = dt # 采样时间
def update(self, state, v, delta):
"""
state: [x, y, psi]
v: 纵向速度
delta: 前轮转角
"""
x, y, psi = state
x += v * np.cos(psi) * self.dt
y += v * np.sin(psi) * self.dt
psi += v * np.tan(delta) / self.L * self.dt
return np.array([x, y, psi])
def compute_lateral_error(self, state, ref_path):
"""
计算横向误差和航向误差
ref_path: 期望路径点 [x_ref, y_ref, psi_ref]
"""
x, y, psi = state
x_ref, y_ref, psi_ref = ref_path
# 横向位置误差(简化版:点到直线距离)
e_y = -(x - x_ref) * np.sin(psi_ref) + (y - y_ref) * np.cos(psi_ref)
# 航向角误差
e_psi = psi - psi_ref
e_psi = np.arctan2(np.sin(e_psi), np.cos(e_psi)) # 归一化到[-pi, pi]
return e_y, e_psi
注意:实际工程中,e_y 的计算要考虑路径的曲率,不能简单用点到直线距离。我一般用Frenet坐标系下的投影法,后面章节会详细讲。
4.6 本章小结
今天的内容是横向控制的基石。总结三点:
- 自行车模型:把四轮车简化为两轮,抓住转向和横摆的核心关系
- 横向误差动力学:用 e_y 和 e_ψ 描述车辆偏离路径的程度
- 模型局限性:低速、小曲率场景好用;高速或急转弯需要更精细的模型
下一章我们会基于这个误差模型,设计第一个横向控制器——纯跟踪(Pure Pursuit)。到时候你会发现,模型建好了,控制器设计就是水到渠成的事。
好,今天就到这里。有问题欢迎在课程群里讨论,或者直接微信找我。