☰
MATLAB Simulink车辆运动学仿真:从自行车模型到实时位置姿态输出
2026/10/16 8:01:03 网站建设 项目流程

最近在做一个基于 MATLAB Simulink 的车辆运动学仿真模块,需求很直接:给定期望车速和前轮转角,模型要实时输出车辆在大地坐标系下的位置 (x, y) 和车身姿态角,也就是偏航角 ψ。这类需求在轨迹跟踪算法验证、ADAS 功能开发以及底盘控制器测试里非常常见。真正动手搭模型并不难,但要让位置和姿态输出在几百秒仿真后依然稳定,并且和理论解完全对得上,中间涉及步长设置、初始条件、角度单位、坐标系约定等一系列容易踩坑的细节。这篇文章就把整个项目从公式推导、Simulink 搭建到问题排查完整拆一遍。

1. 项目拆解:车辆运动学仿真到底在算什么

1.1 为什么选择运动学模型而不是动力学模型

很多人一听到车辆仿真,第一反应就是动力学模型,觉得要上复杂的轮胎模型、车身自由度、悬架刚度。但在这个项目里,我刻意把问题收敛在运动学层面。运动学只研究物体的几何运动关系,完全不考虑力、力矩、质量、惯量这些东西。也就是说,我们只关心“给定速度和转角,车大概会往哪走”,不关心“方向盘打过去之后,车能不能跟上这个转角”。

这个取舍不是偷懒,而是有明确工程背景的。我当时的任务是为一个轨迹跟踪控制算法提供被控对象模型。控制算法的核心逻辑,是基于当前位置和航向角去计算前轮转角。这里面最关键的反馈量就是车辆的实时位置和车身姿态。此时如果引入复杂的动力学模型,参数标定会占用大量时间,而且由于轮胎非线性特性,模型一旦没标定好,反而会掩盖控制逻辑本身的问题。

所以我的做法是先用运动学模型做算法验证,把控制器的行为曲线、稳态误差、超调量这些基本指标调好,再考虑是否切换到动力学模型做极限工况测试。这也是很多整车厂和零部件供应商在项目前期常用的套路:先粗粒度验证逻辑,再细粒度验证硬件和物理边界。运动学模型的计算开销极小,可以在 Simulink 里跑得飞快,也方便做实时仿真,非常适合放在控制闭环里反复迭代。

1.2 实时位置与车身姿态的准确定义

在开始搭模型之前,必须先明确输出量是什么。这里有一个容易含糊的点:车身姿态到底包含什么?完整的姿态一般包括横滚角 Roll、俯仰角 Pitch、偏航角 Yaw 三个自由度,但纯运动学模型是基于最简单的自行车模型推导的,它只输出偏航角,也就是航向角 ψ。原因很简单,自行车模型假设车辆在地平面上运动,没有侧倾和俯仰,只有绕竖直轴旋转的自由度。

位置量则是指车辆某个参考点在全局坐标系下的坐标。运动学自行车模型里最常用的参考点是后轴中心,因为从几何关系看,后轴中心的速度方向始终与车身纵轴一致,推导方便。全局坐标系一般定义成大地坐标系,X 轴指向正东,Y 轴指向正北,航向角 ψ 表示车身纵轴与 X 轴的夹角,逆时针为正。

输出量符号单位坐标系
横向位置xm全局坐标系(东向为 X)
纵向位置ym全局坐标系(北向为 Y)
偏航角ψrad全局坐标系下相对 X 轴

这里说的“实时”,在 Simulink 仿真环境下指的是每个仿真步长都根据当前输入计算一次状态输出。模型自身是无延迟的,只要车速和转角信号进入模型,位置和姿态马上更新。当配合固定步长求解器使用时,仿真步长可以严格对齐控制器周期,比如控制器是 10ms 周期,求解器也设定为 10ms 固定步长。如果后续做硬件在环测试,把模型部署到实时目标机上,就能在真实时间尺度下持续输出位置和姿态信息,供外部设备读取。

2. 核心模型搭建:从公式到三个积分器

2.1 自行车模型的公式推导

自行车模型也叫单轨模型,它把车辆的前后轮分别简化成一条轮胎印迹,车辆本身抽象成一根连杆。设轴距为 L,前轮转角为 δ,车辆后轴中心速度为 v,车辆航向角为 ψ。在低速情况下,我们可以忽略轮胎侧偏,认为后轴中心的速度方向始终沿着车身纵轴方向。

在这个假设下,后轴中心在全局坐标系下的速度分量就是:

ẋ = v · cos(ψ)
ẏ = v · sin(ψ)

这个式子的几何含义很直白:速度 v 是沿车身纵轴方向的,把它分解到全局坐标系的 X 轴和 Y 轴上,就是上面两个分量。

航向角的变化率,也就是偏航角速度 ψ̇,和前轮转角 δ 的关系稍微复杂一点。对于自行车模型,有一个经典几何推导:前轮转角为 δ 时,车辆的瞬时转向半径 R 满足 R = L / tan(δ)。而车辆绕瞬时中心转动的角速度等于线速度除以半径,所以:

ψ̇ = v / R = v · tan(δ) / L

这三个一阶微分方程就构成了完整的车辆运动学模型。这个模型有一个使用边界:它假设车轮没有侧偏,所以只适用于侧向加速度较低的场景,一般要求横向加速度低于 0.4g,换算到速度上大概就是低速到中速的范围。如果做高速紧急避险这种工况,就必须换用包含侧偏特性的动力学模型了。

2.2 在 Simulink 中一步步搭出运动学模块

模型搭建的第一步不是拖模块,而是先把接口想清楚。我的设计思路是:增加一个 MATLAB Function 作为纯计算核心,输入当前时刻的车速 v、前轮转角 δ、模型内部积分的航向角 ψ 以及轴距 L,输出三个导数 ẋ、ẏ、ψ̇。然后这三个导数分别连接到积分器上,积分得到 x、y、ψ。其中 ψ 作为状态又反馈回 MATLAB Function,形成一个闭环。

打开 Simulink 后,按下面的步骤操作:

  1. 新建一个空白模型,保存命名为 vehicle_kinematics.slx。
  2. 打开模型库浏览器,添加三个 Constant 模块,分别作为车速 v、前轮转角 delta、轴距 L 的数值输入。我用常量是为了先做开环验证,实际工程中这些输入会来自上一级模块或者工作空间的变量。
  3. 在空白画布上双击,输入 MATLAB Function,拖入一个函数模块。双击函数模块,在代码编辑区写入运动学方程。
  4. 添加三个 Integrator 积分器模块,分别命名 x、y、psi。积分器的初始条件在模块参数对话框的 Initial condition 字段里设置。
  5. 按照信号流连接:速度 v 和转角 delta 连进函数输入端,psi 作为第三个输入。函数输出三个导数,分别连到三个积分器的输入。
  6. 三个积分器的输出引出来,一部分连到 Scope 或者 To Workspace,psi 的值另外再反馈回函数输入端。

MATLAB Function 内部代码如下:

function [xdot, ydot, psidot] = kinematics(v, delta, psi, L) % 车辆运动学自行车模型:计算状态导数 xdot = v * cos(psi); ydot = v * sin(psi); psidot = v * tan(delta) / L; end

这里有个很重要的细节:MATLAB Function 的输入端口顺序和模型连线顺序必须完全一致。我在实践中的一个习惯是,进入函数内部之前,把每个输入信号都加一个注释名称,比如 v、delta、psi、L,这样后续排查接线问题时一眼就能看出信号是否接错。

2.3 为什么必须用积分器而不是直接累加

第一次做这个模型的人,很容易把它做成这样:直接用 x = x + xdot * dt 这种方式,在 S-Function 里或者在 MATLAB Function 内部用持久变量累加。这个方法不是不行,但它把积分过程写死了,固定了步长 dt,一旦系统步长变化,结果就对不上,而且误差会一路累积下去。

Simulink 的积分器模块才是正道。它内部会保存状态值,支持连续求解器对状态进行数值积分。你可以把积分器理解成一个小储物间,每次计算得到导数后,积分器在当前状态值上加上一个微小的增量。这个过程由求解器自动控制,步长变化时它依然能保持精度。

整个闭环可以概括成一句话:计算导数 → 积分更新状态 → 用新状态计算下一时刻导数。这也是所有基于微分方程的状态空间仿真的通用框架。只要把 x、y、ψ 定义成状态量,剩下的模型结构就自然而然地确定了。

我在搭建时还做了一点优化:由于公式里的轴距 L 是常量,可以把 tan(delta) * v 先算出来,再通过一个 Gain 模块除 L,这样可以让关键参数在外面可视化,调试时直接改 Constant 的数值就能看到轨迹变化。不过在最终交付给团队的版本里,我还是改成了 MATLAB Function 方案,因为后面接了控制器之后,输入接口会更统一。

3. 实时输出车身姿态:单位、初始值与参考系

3.1 航向角 ψ 与坐标系映射关系

模型里定义的航向角 ψ 是从全局坐标系 X 轴算起的逆时针角度。但在实际车辆工程中,航向角的参考基准经常不是这个。最常见的冲突是:惯导系统输出的航向角通常相对真北方向,从正北顺时针计算,这叫方位角。而 Simulink 模型里如果按照“东向为 0°”来定义,两者会差一个 90 度以上的偏移。

举例说明,某车辆实际朝正北行驶,惯导输出的方位角是 0°,但在模型坐标系里,车辆纵轴方向就是 Y 轴正方向,此时 ψ 应该等于 π/2。如果不事先统一坐标系,位置积分出来的 x、y 轨迹会整体旋转,而且航向角反馈进控制律以后,控制器看到的误差方向全是错的。

我的解决方法是:在模型最外层统一约定,全局坐标系一律采用“东北天”坐标系的水平面投影,其中 X 轴为东、Y 轴为北。如果外部数据源给的是方位角,就在接入模型之前用 MATLAB 脚本做一次转换,把方位角 θ 转换成模型用的 ψ。公式是 ψ = π/2 - θ。这个转换处理放在模型外部,一方面保持模型内部公式的纯粹,另一方面也方便后续换数据源时只改接口层。

3.2 角度单位检查清单

角度单位的错误是这类仿真里概率最高的事故源。我在项目中专门总结了一份检查清单,每次运行前都过一遍:

  • 外部输入的前轮转角 delta,如果来源是方向盘转角除以传动比,通常习惯用度,进模型前必须先 deg2rad。
  • tan 函数的输入量必须是弧度。哪怕 delta 只差 0.1 度,tan 计算出的结果也会偏差不少,反映到轨迹上就是转弯半径明显改变。
  • cos 和 sin 函数的输入 ψ 必须是弧度,这个最容易漏,因为 Simulink 模块里默认支持三角函数用度或者弧度,一不小心就看到一个 cos(30) 出来。
  • Scope 显示航向角时,如果希望以度显示,应该在信号路径上增加一个 Gain 模块,增益设置为 180/pi,不要在积分器里存储度数。
  • 积分器输出的 ψ 单位是 rad,作为反馈量送入某些控制器时,需要确认控制器内部期望的单位。

这一套检查下来,大概能挡住九成的低级错误。我记忆里最深刻的一次排查,是仿真轨迹半径怎么算都偏小,后来发现前轮转角在进模型前已经转了度数到弧度,但我在 MATLAB Function 里又做了一次 deg2rad,等于转了两次,转角变成 0.0175 倍大小,半径自然就大了几十倍。

3.3 初始航向角和初始位置的影响

三个积分器都有初始条件。x 和 y 的初始值意味着车辆在大地坐标系下的起始点,这个很好理解。但 ψ 的初始值常常被人忽略,而且它造成的后果非常隐蔽——不是简单的固定偏差,而是让整个仿真轨迹绕原点旋转。

举例,真实车辆初始航向是 0.2 rad,但模型里 ψ0 设置成了 0。仿真跑 10 秒,车速 10 m/s,横向偏差可能看起来不大,但跑到 100 秒之后,模型里的车已经转了一个角度,实际位置和模型位置之间的差距会越来越大。这种感觉很像地图导航里初始方向没对准,走着走着就歪到一边去。

所以我的原则是:模型启动前,必须根据实车状态初始化 x0、y0、ψ0。如果用 GPS 数据,x0 和 y0 可以取当前经纬度转换后的相对坐标;ψ0 则需要用惯导航向角或者陀螺仪积分值来初始化。如果初始航向完全未知,也可以用零初始状态先仿真,但需要在结果分析时明确记录这一点,不能把这样的仿真结果当成真实轨迹。

4. 仿真闭环验证与常见问题排查

4.1 用恒定转角跑一个圆来验证模型

运动学模型搭建完成后,第一步验证我强烈建议做直线和定圆测试。直线太简单,直接证明模型没有初始方向错误;定圆更有价值,它能一次性暴露转角单位、轴距参数、积分器增益这类问题。

原理是这样的:当车速 v 恒定、前轮转角 δ 恒定时,车辆转弯半径 R = L / tan(δ)。假设 L = 2.7 m,δ = 5 度,也就是大约 0.0873 rad,那么:

R = 2.7 / tan(0.0873) ≈ 30.9 m

绕一整圈的周长是 2πR,约 194.2 m,车速 10 m/s,所以跑一整圈大约需要 19.4 秒。仿真设置 20 秒,把得到的 x、y 轨迹画出来,应该是一个几乎封闭的圆。

我提供了这样一个理论曲线来做对照:

L = 2.7; delta_deg = 5; delta = deg2rad(delta_deg); v = 10; R = L / tan(delta); % 理论轨迹,起始位置在原点,初始航向为0,左转 theta = linspace(0, 2*pi, 500); x_theory = R * sin(theta); y_theory = R - R * cos(theta); % 将理论圆和仿真轨迹画在一起 figure; plot(x_theory, y_theory, 'r--', 'LineWidth', 1.5); hold on; plot(x_sim, y_sim, 'b-', 'LineWidth', 1); axis equal; grid on; legend('理论圆轨迹', 'Simulink仿真轨迹');

如果仿真结束后,两条曲线几乎重合,说明模型核心计算没有大问题。我这里特意用了 x_sim 和 y_sim 作为变量名,实际使用时它们来自 To Workspace 模块的输出。画图的时候记得 axis equal,否则圆会被坐标轴拉伸成椭圆,视觉上反而不好判断。

4.2 常见错误速查表

在整个项目过程中,我把遇到的典型问题整理成了一个速查表。你现在照着排,基本能覆盖掉运动学模块 95% 的翻车现场。

现象可能原因解决办法
轨迹整体旋转偏移航向角初始值 ψ0 设置错误用惯导航向或相对基准换算 ψ0
轨迹半径明显错误前轮转角单位或轴距 L 错误检查 delta 是否重复转换,L 单位是否为 m
仿真曲线出现锯齿固定步长太大,求解器精度不足将步长设为控制器周期的 1/10 或更小
车辆转向方向反了转角 δ 的符号约定不一致统一约定左转为正,检查输入信号极性
航向角输出在 ±π 之间跳变在控制器里用 atan2 反算航向角直接使用积分器输出的 ψ,不要用 atan2 反推
位置随时间逐渐发散纯积分漂移,外部无修正引入 GPS/IMU 融合修正,或缩短仿真时间
模型运行速度极慢使用了变步长求解器且容差过小更换为 ode4 固定步长,容差设置合理

这里稍微展开一下航向角跳变的问题。很多人习惯从位置坐标反算航向:ψ = atan2(y, x),这在某些定位算法里很常见,但在运动学模型里是绝对不可取的。atan2 的输出范围是 -π 到 π,车辆每转一圈,ψ 要穿过 π 与 -π 的边界,输出值会发生跳变。控制律如果用这个跳变的信号,控制器瞬间会误判航向误差超过 360 度,然后使劲打方向。而积分器输出的 ψ 是连续累积的,转了两圈就是 4π,没有任何跳变,这才是真实的姿态状态。

4.3 实时位置漂移问题与真实车辆联动

如果你把这个 Simulink 模型接到真实硬件上,比如通过 CAN 采集实车车速、通过 IMU 采集航向角速度,那么纯积分出来的位置一定会漂移。原因很简单:任何传感器都有噪声和零偏,积分过程会把误差无限放大。车速采集中一个小的常值偏差,积分 30 秒之后位置就偏出好几米。

解决这个问题有两条路。第一条,在仿真阶段就加入虚拟传感器的概念,不直接用理想信号,而是给车速加噪声、给转向角加延迟,检验算法在非理想信号下的表现。第二条,在做硬件在环时引入融合算法,用 GPS 的绝对位置修正航向积分引起的漂移,用 IMU 的角速度积分提高航向的短期动态响应,两者通过互补滤波或者卡尔曼滤波做加权融合。

这里只提一下思路,不展开讲融合算法细节,因为那已经是另一个“定位”专题了。但你必须清楚,运动学模型提供的是一个“理想被控对象”,在把它当作真实车辆反馈之前,需要明确它是否包含传感器误差链路。我在项目中实际做的是:模型内部输出理想位置姿态,再在外部故意加上传输延迟和量化误差,这样既保留了模型的可解释性,又能模拟真实总线的限制。

5. 从位置姿态到轨迹跟踪控制扩展

5.1 为什么轨迹跟踪需要实时位置与姿态

运动学模块产出的实时位置和姿态,最直接的用武之地就是轨迹跟踪控制器。最经典的 Stanley 控制律对反馈量的依赖非常高。它的大致思路是,控制器根据当前车辆位置与参考轨迹上最近点的偏差,计算一个横向误差 e,再根据当前航向角 ψ 与参考航向之间的偏差,综合计算出前轮转角。

Stanley 控制律可简写为:

δ = θ_des - ψ + atan2(k · e, v)

其中 θ_des 是参考轨迹上离当前车辆最近点的期望航向角,e 是横向误差,k 是一个可调增益。如果没有运动学模块输出的实时 ψ 和位置,这个控制律根本无从下手。所以前面搭的模块这时就不再是孤立的被控对象,而是整个闭环仿真系统的一个先决条件。

我在扩展测试时,把之前搭的运动学模块整体封装成了 Subsystem,输入端保留 v 和 delta,输出端引出 x、y、ψ。然后新建一个顶层模型,把控制器模块放在运动学子系统前面,控制器读取运动学子系统的位置和航向输出,结合参考轨迹计算出 delta,再送回到运动学子系统。点击运行之后,车辆就会自己沿着参考路径走了。

5.2 控制器仿真评估的小经验

在评估轨迹跟踪效果时,最直观的指标是横向误差曲线。我一般会把运动学模块输出的 x、y 与参考轨迹做最小距离差得到 e,然后画出 e 随时间的变化。如果 e 快速收敛并在几秒内保持稳定,说明控制律基本正确。如果 e 出现振荡,通常要调小 Stanley 增益 k,或者给转角信号增加限幅和斜率限制。

还有一个经验,纯跟踪类算法对前视距离的敏感性很高。低速工况下,前视距离我习惯取 1.5 到 2 倍轴距,高速工况取 2 到 3 倍轴距。这个经验同样适用于用运动学模型验证的纯跟踪控制器。相比动力学模型,运动学模型对前视距离的宽容度更高一些,一旦切换成动力学模型,低速下可能稳定,高速下就容易出现摆动,这属于正常的模型落差。

5.3 封装复用与版本管理的建议

随着项目推进,运动学模块会同时被多个模型引用。我的建议是不要直接复制子系统到处粘贴,而是使用 Simulink 的 Model Reference 功能,把运动学模型保存成独立文件,然后在多个仿真工程里引用它。这样改一个模型文件,所有引用它的工程都会同步更新,避免不同版本之间轨迹不一致。

另外,在模型里加一个子系统的 Mask 遮罩,把关键参数 L(轴距)、初始条件 x0、y0、psi0 都暴露给使用者。这样别人拿到模型时,不需要打开子系统内部就能配置参数,错误操作空间也会大幅减小。我发现自己交付模型给别人使用,最后花在沟通参数上的时间远比搭模型多。

最后再分享一个小习惯,我每次建新工程都会在模型的顶层加一个 Description 字符串,当下项目里的坐标系、单位、参数来源,都写清楚。下次再回来看模型,不用靠回忆猜当初为什么这么接线。车辆运动学仿真看起来简单,但正因为简单,基础约定才更要做到滴水不漏。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询