车辆运动学模型:从自行车到阿克曼

各位同学好,我是你们的老朋友。今天咱们聊聊自动驾驶仿真里最基础、也最核心的一块——车辆运动学模型。

说实话,我刚入行那会儿,总觉得运动学模型太简单,不就是个几何关系嘛。直到有一次,我在做路径跟踪仿真时,发现车辆怎么都跑不直,方向盘明明打了,车却像喝醉了一样。排查了半天,原来是模型里少考虑了阿克曼几何的约束。嗯,从那以后,我再也不敢小看这个“简单”的模型了。

为什么是自行车模型?

你想想看,一辆真车有四个轮子,每个轮子都有自己的转向角、滑移角,建模起来非常复杂。但在很多场景下,我们其实不需要那么精细。

自行车模型的核心思想很简单:把左右两个前轮合并成一个虚拟前轮,左右后轮合并成一个虚拟后轮。这样一来,四轮车就变成了两轮车。

适用场景:

  • 低速行驶(通常 < 10 m/s)
  • 路径跟踪控制
  • 轨迹规划中的状态预测
  • 仿真验证的快速原型

注意:自行车模型假设轮胎没有侧偏,也就是说不考虑轮胎的侧向力。高速过弯时,这个模型就不太准了。我在做高速避障仿真时吃过这个亏,后来换成了动力学模型才搞定。

阿克曼转向几何

好,问题来了。既然我们把两个前轮合并成一个,那这个虚拟前轮的转向角该怎么算?

这就引出了阿克曼转向几何。说白了,它解决的是:车辆转弯时,内侧轮和外侧轮的转弯半径不一样,所以它们的转向角也不一样。

你看,如果左右前轮平行转向,车辆会怎么走?它会侧滑,因为内侧轮转的弯更急,外侧轮转的弯更缓。阿克曼几何就是让内侧轮多转一点,外侧轮少转一点,这样所有轮子都绕着同一个圆心转。

数学上,阿克曼转向关系可以表示为:

δ_inner = arctan(L / (R - W/2))
δ_outer = arctan(L / (R + W/2))

其中:

  • L:轴距(前后轮中心的距离)
  • W:轮距(左右轮中心的距离)
  • R:转弯半径

而在自行车模型中,我们通常使用一个等效的前轮转向角 δ,它近似等于内外轮转向角的平均值。

我的经验:实际项目中,阿克曼几何的偏差会导致路径跟踪误差。我曾经在一个泊车项目中,因为忽略了阿克曼修正,车辆倒车入库时总是偏右20厘米。后来加上阿克曼补偿,一把就进去了。

车辆状态表示:x, y, yaw, velocity

有了模型,我们还得知道车辆当前在哪儿、朝哪儿、跑多快。这就是车辆状态表示。

在二维平面中,我们用四个量来描述车辆状态:

状态量 符号 单位 含义
横向位置 x m 车辆后轴中心在全局坐标系下的x坐标
纵向位置 y m 车辆后轴中心在全局坐标系下的y坐标
航向角 yaw rad 车辆纵轴与全局x轴的夹角
速度 velocity m/s 车辆后轴中心的瞬时速度

这里有个细节:为什么选后轴中心作为参考点?因为后轴中心在自行车模型中就是后轮的位置,而且后轮没有转向角,计算起来最方便。

运动学方程

有了状态,我们还得知道状态怎么随时间变化。这就是运动学方程:

dx/dt = v * cos(yaw)
dy/dt = v * sin(yaw)
dyaw/dt = v * tan(δ) / L
dv/dt = a

其中:

  • δ:前轮转向角(控制输入)
  • a:加速度(控制输入)
  • L:轴距

你看,这个方程组其实很直观。x和y的变化就是速度在x和y方向上的投影。航向角的变化率,说白了就是角速度,它等于速度除以转弯半径,而转弯半径 R = L / tan(δ)。

核心公式记忆法:

我个人的习惯是这么记的:

  • 位置变化 = 速度 × 方向余弦
  • 方向变化 = 速度 × 曲率
  • 曲率 = tan(转向角) / 轴距

离散化:从连续到离散

仿真嘛,肯定是在离散时间步上跑的。所以我们需要把上面的连续方程离散化。

最常用的方法是欧拉法:

x(k+1) = x(k) + v(k) * cos(yaw(k)) * dt
y(k+1) = y(k) + v(k) * sin(yaw(k)) * dt
yaw(k+1) = yaw(k) + v(k) * tan(δ(k)) / L * dt
v(k+1) = v(k) + a(k) * dt

dt 是仿真步长,一般取 0.01s 到 0.1s 之间。

避坑指南:我曾经在仿真中把dt设得太大(0.5s),结果车辆状态发散得一塌糊涂。后来才意识到,欧拉法对步长很敏感。如果你发现仿真结果不稳定,先检查dt是不是太大了。

知识体系总览

为了让大家更直观地理解本章的知识结构,我画了一张图:

车辆运动学模型知识体系 自行车模型 阿克曼转向几何 车辆状态表示 核心假设 • 左右轮合并 • 无侧偏角 转向关系 • 内外轮转角不同 • 绕同一圆心转动 状态量 • x, y, yaw, v • 后轴中心为参考点 运动学方程(连续 → 离散) dx/dt = v·cos(yaw) | dy/dt = v·sin(yaw) dyaw/dt = v·tan(δ)/L | dv/dt = a 离散化方法:欧拉法 | 步长建议:0.01s ~ 0.1s 应用场景:路径跟踪仿真、轨迹规划、控制算法验证

代码实现:一个简单的运动学仿真

光说不练假把式。咱们写个简单的 Python 代码,把自行车模型跑起来:

import numpy as np

class BicycleModel:
    def __init__(self, L=2.5, dt=0.05):
        self.L = L          # 轴距,单位米
        self.dt = dt        # 仿真步长,单位秒
        self.x = 0.0
        self.y = 0.0
        self.yaw = 0.0
        self.v = 0.0
    
    def update(self, delta, a):
        """
        更新车辆状态
        delta: 前轮转向角 (rad)
        a: 加速度 (m/s^2)
        """
        self.x += self.v * np.cos(self.yaw) * self.dt
        self.y += self.v * np.sin(self.yaw) * self.dt
        self.yaw += self.v * np.tan(delta) / self.L * self.dt
        self.v += a * self.dt
        
        return self.x, self.y, self.yaw, self.v

# 使用示例
car = BicycleModel(L=2.5, dt=0.05)
for t in range(100):
    # 给一个恒定的转向角和加速度
    x, y, yaw, v = car.update(delta=0.1, a=0.5)
    if t % 20 == 0:
        print(f"t={t*0.05:.2f}s: x={x:.2f}, y={y:.2f}, yaw={yaw:.2f}, v={v:.2f}")

小技巧:实际仿真中,我习惯把车辆状态封装成一个类,这样方便扩展。比如后面要加传感器噪声、路面坡度等,直接在 update 方法里加就行,不用改调用处的代码。

总结一下

这一章我们聊了三个核心内容:

  • 自行车模型:把四轮简化为两轮,适用于低速场景
  • 阿克曼转向几何:保证车辆转弯时所有轮子绕同一圆心转动
  • 车辆状态表示:用 (x, y, yaw, velocity) 描述车辆位姿和速度

这些内容看起来简单,但它们是整个路径跟踪仿真的基石。我见过不少同学一上来就搞复杂的动力学模型,结果连最基本的运动学都没搞明白,仿真结果自然一塌糊涂。

记住:先把简单的跑通,再往复杂里加。这是我在项目中摔过跟头后总结出来的经验。

一句话总结:自行车模型 + 阿克曼几何 + 状态表示 = 路径跟踪仿真的起点。把这个基础打牢了,后面的控制算法才能站得住脚。

可可精品资料库