车辆动力学方程推导和代码实现
车辆动力学模型是描述汽车运动规律的微分方程,一般用于分析汽车的平顺性和操纵稳定性。二自由度的车辆动力学模型基于单车模型假设,只考虑轮胎侧偏特性,其应用前提是忽略轮胎力的纵横向耦合关系。不考虑载荷的左右转移。忽略悬架运动、路面坡度和横纵向空气动力学等非线性效应。
1. 车辆动力学模型
车辆动力学模型是描述汽车运动规律的微分方程,一般用于分析汽车的平顺性和操纵稳定性。二自由度的车辆动力学模型基于单车模型假设,只考虑轮胎侧偏特性,其应用前提是
- 忽略轮胎力的纵横向耦合关系。
- 不考虑载荷的左右转移。
- 忽略悬架运动、路面坡度和横纵向空气动力学等非线性效应。
2. 车辆动力学建模
由于车辆动力学模型忽略了空气动力学和地面坡度等因素,因此汽车受到的外力均来自轮胎受到的地面力,其模型的几何结构和受力分析如下图所示:
其中:
- v v v:质心 C C C处的速度,即车辆的速度。
- v x v_x vx:质心速度 v v v在纵向的分量。
- v y v_y vy:质心速度 v v v在横向的分量。
- v f v_f vf:前轮的速度。
- v f x v_{fx} vfx:前轮的速度 v f v_f vf在纵向的分量。
- v f y v_{fy} vfy:前轮的速度 v f v_f vf在横向的分量。
- v r v_r vr:后轮的速度。
- v r x v_{rx} vrx:后轮的速度 v r v_r vr在纵向的分量。
- v r y v_{ry} vry:后轮的速度 v r v_r vr在横向的分量。
- β \beta β:质心侧偏角度,质心速度与车辆纵向的夹角。
- φ \varphi φ:横摆角,即车辆轴线(纵向)与X轴的夹角。
- C C C:质心。
- L f L_f Lf:质心到前轴的距离。
- L r L_r Lr:质心到后轴的距离。
- δ f \delta_f δf:前轮转角,即前轮朝向与车身纵向的夹角。
- θ v f \theta_{vf} θvf:前轮速度偏角度(一般是由于轮胎形变引起的),即前轮速度 v f v_f vf与车身纵向夹角。
- α f \alpha_f αf:前轮侧偏角,即前轮速度 v f v_f vf与前轮朝向的夹角。
- δ r \delta_r δr:后轮转角,我们的模型是后轮不转向,因此 δ r = 0 \delta_r=0 δr=0,图中未标注。
- θ v r \theta_{vr} θvr:后轮速度偏角,即后轮速度 v r v_r vr与车身纵向夹角。
- α r \alpha_r αr:后轮侧偏角,即后轮速度 v r v_r vr与后轮朝向的夹角,图中未标注。
- F y f F_{yf} Fyf:前轮受到的横向力。
- F x f F_{xf} Fxf:前轮受到的纵向力。
- F y r F_{yr} Fyr:后轮受到的横向力。
- F c f F_{cf} Fcf:前轮侧向力。
- F c r F_{cr} Fcr:后轮侧向力。
忽略地面坡度,沿着车身
y
y
y轴(横向)应用牛顿第二定律可得
m
a
y
=
F
y
f
+
F
y
r
(1)
ma_y=F_{yf}+F_{yr} \tag{1}
may=Fyf+Fyr(1)
m
m
m为车辆的质量,
a
y
a_y
ay为车辆在质心
C
C
C处横向的惯性加速度,其主要由横向加速度
y
¨
\ddot{y}
y¨和向心加速度
v
x
φ
˙
v_x\dot{\varphi}
vxφ˙组成,即
a
y
=
y
¨
+
v
x
φ
˙
(2)
a_y=\ddot{y}+v_x\dot{\varphi} \tag{2}
ay=y¨+vxφ˙(2)
将(2)代入(1)可得
m
(
y
¨
+
v
x
φ
˙
)
=
F
y
f
+
F
y
r
(3)
m(\ddot{y}+v_x\dot{\varphi} ) = F_{yf}+F_{yr} \tag{3}
m(y¨+vxφ˙)=Fyf+Fyr(3)
同理,沿着车身
x
x
x轴(纵向)可得
a
x
=
v
x
˙
−
v
y
φ
˙
(4)
a_x = \dot{v_x}-v_y\dot{\varphi} \tag{4}
ax=vx˙−vyφ˙(4)
m ( v x ˙ − v y φ ˙ ) = F a − F x f (5) m(\dot{v_x}-v_y\dot{\varphi} )= F_a- F_{xf} \tag{5} m(vx˙−vyφ˙)=Fa−Fxf(5)
其中 F a F_a Fa为车辆动力系统提供的纵向力。
车辆的横向运动并不是完全的侧向平移,而是需要通过一定程度的转向来完成,也就是横摆运动,由车辆绕
z
z
z轴的旋转平衡可以得到车辆的的横摆动力学方程
I
z
φ
¨
=
L
f
F
y
f
−
L
r
F
y
r
(6)
I_z\ddot{\varphi}=L_fF_{yf}-L_rF_{yr} \tag{6}
Izφ¨=LfFyf−LrFyr(6)
其中
I
z
I_z
Iz为车辆绕
z
z
z轴的转动惯量,
φ
¨
\ddot{\varphi}
φ¨为横摆角的加速度。
由图中角的几何关系,可得前轮侧偏角
α
f
=
δ
f
−
θ
v
f
(7)
\alpha_f = \delta_f - \theta_{vf} \tag{7}
αf=δf−θvf(7)
后轮侧偏角
α
r
=
−
θ
v
r
(8)
\alpha_r=-\theta_{vr} \tag{8}
αr=−θvr(8)
由于轮胎在侧偏角较小的时候,轮胎的侧向力与侧偏角近似成正比,因此有
F
c
f
=
C
α
f
(
δ
f
−
θ
v
f
)
(9)
F_{cf}=C_{\alpha f}(\delta_f-\theta_{vf})\tag{9}
Fcf=Cαf(δf−θvf)(9)
F c r = C α r ( − θ v r ) (10) F_{cr} = C_{\alpha r}(-\theta_{vr}) \tag{10} Fcr=Cαr(−θvr)(10)
C α f C_{\alpha f} Cαf为前轮侧向力与前轮侧偏角的比例系数,称为前轮轮胎的侧扁刚度, C α r C_{\alpha r} Cαr为后轮侧向力与后轮侧偏角的比例系数,即后轮轮胎的侧扁刚度。
注:这里推导没有乘以2,是因为我们是将车辆看作是一个两轮的自行车模型, C α f C_{\alpha f} Cαf和 C α r C_{\alpha r} Cαr分别可以理解为两个前轮的侧扁刚度和,以及两个后轮的侧扁刚度和,在实际应用中,需要根据车辆的实际情况作适当的调整。后面的代码仿真中,我们在设置 C α f C_{\alpha f} Cαf和 C α r C_{\alpha r} Cαr的时候,会自动将其乘以2,来表示两个前轮侧扁刚度和和两个后轮侧扁刚度和。
由图中力的几何关系可得
F
x
f
=
F
c
f
s
i
n
δ
f
=
C
α
f
(
δ
f
−
θ
v
f
)
s
i
n
δ
f
(11)
F_{xf} = F_{cf}sin\delta_f=C_{\alpha f}(\delta_f-\theta_{vf})sin\delta_f \tag{11}
Fxf=Fcfsinδf=Cαf(δf−θvf)sinδf(11)
F y f = F c f c o s δ f = C α f ( δ f − θ v f ) c o s δ f (12) F_{yf} = F_{cf}cos\delta_f=C_{\alpha f}(\delta_f-\theta_{vf})cos\delta_f \tag{12} Fyf=Fcfcosδf=Cαf(δf−θvf)cosδf(12)
F y r = F c r = C α r ( − θ v r ) (13) F_{yr} = F_{cr} = C_{\alpha r}(-\theta_{vr}) \tag{13} Fyr=Fcr=Cαr(−θvr)(13)
由于前后轮的横向速度由车辆自身横向速度和绕质心的转动速度组成,因此
v
f
y
=
y
˙
+
L
f
φ
˙
(14)
v_{fy}=\dot{y}+L_f\dot{\varphi} \tag{14}
vfy=y˙+Lfφ˙(14)
v r y = y ˙ − L r φ ˙ (15) v_{ry}=\dot{y}-L_r \dot{\varphi} \tag{15} vry=y˙−Lrφ˙(15)
其中 y ˙ \dot{y} y˙为车辆横向速度, φ ˙ \dot{\varphi} φ˙为车辆的向心速度。
由于前后轮的纵向速度和车辆自身的纵向速度相等,即
v
f
x
=
v
r
x
=
v
x
v_{fx} = v_{rx} = v_x
vfx=vrx=vx,结合图中的几何关系可得
t
a
n
θ
v
f
=
v
f
y
v
f
x
=
y
˙
+
L
f
φ
˙
v
x
(16)
tan\theta_{vf} = \frac{v_{fy}}{v_{fx}}= \frac{\dot{y}+L_f\dot{\varphi}}{v_{x}} \tag{16}
tanθvf=vfxvfy=vxy˙+Lfφ˙(16)
t a n θ v r = v r y v r x = y ˙ − L r φ ˙ v x (17) tan\theta_{vr} = \frac{v_{ry}}{v_{rx}}= \frac{\dot{y}-L_r \dot{\varphi}}{v_{x}} \tag{17} tanθvr=vrxvry=vxy˙−Lrφ˙(17)
由(16)和(17)可得
θ
v
f
=
a
r
c
t
a
n
y
˙
+
L
f
φ
˙
v
x
(18)
\theta_{vf} = arctan{\frac{\dot{y}+L_f\dot{\varphi}}{v_{x}}} \tag{18}
θvf=arctanvxy˙+Lfφ˙(18)
θ v r = a r c t a n y ˙ − L r φ ˙ v x (19) \theta_{vr} = arctan{\frac{\dot{y}-L_r \dot{\varphi}}{v_{x}}} \tag{19} θvr=arctanvxy˙−Lrφ˙(19)
将(18)和(19)代入到(11)、(12)和(13)可得
F
x
f
=
C
α
f
(
δ
f
−
a
r
c
t
a
n
y
˙
+
L
f
φ
˙
v
x
)
s
i
n
δ
f
(20)
F_{xf} = C_{\alpha f}(\delta_f-arctan{\frac{\dot{y}+L_f\dot{\varphi}}{v_{x}}})sin\delta_f \tag{20}
Fxf=Cαf(δf−arctanvxy˙+Lfφ˙)sinδf(20)
F y f = C α f ( δ f − a r c t a n y ˙ + L f φ ˙ v x ) c o s δ f (21) F_{yf}=C_{\alpha f}(\delta_f-arctan{\frac{\dot{y}+L_f\dot{\varphi}}{v_{x}}})cos\delta_f \tag{21} Fyf=Cαf(δf−arctanvxy˙+Lfφ˙)cosδf(21)
F y r = C α r ( − a r c t a n y ˙ − L r φ ˙ v x ) (22) F_{yr}= C_{\alpha r}(-arctan{\frac{\dot{y}-L_r \dot{\varphi}}{v_{x}}}) \tag{22} Fyr=Cαr(−arctanvxy˙−Lrφ˙)(22)
由(5)可得纵向加速度
v
x
˙
\dot{v_x}
vx˙等于
v
x
˙
=
F
a
−
F
x
f
m
+
v
y
φ
˙
(23)
\dot{v_x}=\frac{F_a- F_{xf}}{m}+v_y\dot{\varphi} \tag{23}
vx˙=mFa−Fxf+vyφ˙(23)
由(3)可得横向加速度
v
y
˙
\dot{v_y}
vy˙等于
v
y
˙
=
y
¨
=
F
y
f
+
F
y
r
m
−
v
x
φ
˙
(24)
\dot{v_y}=\ddot{y}=\frac{F_{yf}+F_{yr}}{m}-v_x \dot{\varphi} \tag{24}
vy˙=y¨=mFyf+Fyr−vxφ˙(24)
由(6)可得
φ
¨
=
L
f
F
y
f
−
L
r
F
y
r
I
z
(25)
\ddot{\varphi}=\frac{L_fF_{yf}-L_rF_{yr}}{I_z} \tag{25}
φ¨=IzLfFyf−LrFyr(25)
另外对于全局坐标
X
˙
\dot{X}
X˙和
Y
˙
\dot{Y}
Y˙类似坐标的旋转,所以有
X
˙
=
v
x
c
o
s
φ
−
v
y
s
i
n
φ
(26)
\dot{X}=v_xcos\varphi-v_ysin\varphi \tag{26}
X˙=vxcosφ−vysinφ(26)
Y ˙ = v x s i n φ + v y c o s φ (27) \dot{Y}=v_xsin\varphi+v_ycos\varphi \tag{27} Y˙=vxsinφ+vycosφ(27)
综上可得
{
v
x
˙
=
a
−
F
x
f
m
+
v
y
φ
˙
v
y
˙
=
F
y
f
+
F
y
r
m
−
v
x
φ
˙
φ
¨
=
L
f
F
y
f
−
L
r
F
y
r
I
z
X
˙
=
v
x
c
o
s
φ
−
v
y
s
i
n
φ
Y
˙
=
v
x
s
i
n
φ
+
v
y
c
o
s
φ
(28)
\begin{cases} \dot{v_x}=a-\frac{ F_{xf}}{m}+v_y\dot{\varphi}\\ \dot{v_y}=\frac{F_{yf}+F_{yr}}{m}-v_x \dot{\varphi}\\ \ddot{\varphi}=\frac{L_fF_{yf}-L_rF_{yr}}{I_z}\\ \dot{X}=v_xcos\varphi-v_ysin\varphi \\ \dot{Y}=v_xsin\varphi+v_ycos\varphi \end{cases} \tag{28}
⎩
⎨
⎧vx˙=a−mFxf+vyφ˙vy˙=mFyf+Fyr−vxφ˙φ¨=IzLfFyf−LrFyrX˙=vxcosφ−vysinφY˙=vxsinφ+vycosφ(28)
其中
{
F
x
f
=
C
α
f
(
δ
f
−
θ
v
f
)
s
i
n
δ
f
F
y
f
=
C
α
f
(
δ
f
−
a
r
c
t
a
n
y
˙
+
L
f
φ
˙
v
x
)
c
o
s
δ
f
F
y
r
=
C
α
r
(
−
a
r
c
t
a
n
y
˙
−
L
r
φ
˙
v
x
)
(29)
\begin{cases} F_{xf} = C_{\alpha f}(\delta_f-\theta_{vf})sin\delta_f \\ F_{yf}=C_{\alpha f}(\delta_f-arctan{\frac{\dot{y}+L_f\dot{\varphi}}{v_{x}}})cos\delta_f\\ F_{yr}= C_{\alpha r}(-arctan{\frac{\dot{y}-L_r \dot{\varphi}}{v_{x}}}) \end{cases} \tag{29}
⎩
⎨
⎧Fxf=Cαf(δf−θvf)sinδfFyf=Cαf(δf−arctanvxy˙+Lfφ˙)cosδfFyr=Cαr(−arctanvxy˙−Lrφ˙)(29)
3. 模型实现
import math
import matplotlib.pyplot as plt
import numpy as np
# import imageio
# 车辆参数信息
L = 2.9 # 轴距[m]
Lf = L / 2.0 # 车辆中心点到前轴的距离[m]
Lr = L - Lf # 车辆终点到后轴的距离[m]
W = 2.0 # 宽度[m]
LF = 3.8 # 后轴中心到车头距离[m]
LB = 0.8 # 后轴中心到车尾距离[m]
TR = 0.5 # 轮子半径[m]
TW = 0.5 # 轮子宽度[m]
WD = W # 轮距[m]
Iz = 2250.0 # 车辆绕z轴的转动惯量[kg/m2]
Cf = 1600.0 * 2.0 # 前轮侧偏刚度[N/rad]
Cr = 1700.0 * 2.0 # 后轮侧偏刚度[N/rad]
m = 1500.0 # 车身质量[kg]
LENGTH = LB + LF # 车辆长度[m]
MWA = np.radians(30.0) # 最大轮转角(Max Wheel Angle)[rad]
def normalize_angle(angle):
a = math.fmod(angle + np.pi, 2 * np.pi)
if a < 0.0:
a += (2.0 * np.pi)
return a - np.pi
class DynamicBicycleModel:
def __init__(self, x=0.0, y=0.0, yaw=0.0, vx=0.01, vy=0.0, omega=0.0):
self.x = x
self.y = y
self.yaw = yaw
self.vx = vx
self.vy = vy
self.omega = omega
self.delta = 0.0
def update(self, a, delta, dt=0.1):
self.delta = np.clip(delta, -MWA, MWA)
self.x = self.x + self.vx * math.cos(self.yaw) * dt - self.vy * math.sin(self.yaw) * dt
self.y = self.y + self.vx * math.sin(self.yaw) * dt + self.vy * math.cos(self.yaw) * dt
self.yaw = self.yaw + self.omega * dt
self.yaw = normalize_angle(self.yaw)
f_cf = Cf * normalize_angle(self.delta - math.atan2((self.vy + Lf * self.omega) / self.vx, 1.0))
f_cr = Cr * math.atan2((Lr * self.omega-self.vy) / self.vx, 1.0)
f_xf = f_cf * math.sin(self.delta)
f_yf = f_cf * math.cos(self.delta)
f_yr = f_cr
self.vx = self.vx + (a - f_xf / m + self.vy * self.omega) * dt
self.vy = self.vy + ((f_yr + f_yf) / m - self.vx * self.omega) * dt
self.omega = self.omega + (Lf * f_yf - f_yr * Lr) / Iz * dt
def draw_vehicle(x, y, yaw, delta, ax, color='black'):
vehicle_outline = np.array(
[[-LB, LF, LF, -LB, -LB],
[W / 2, W / 2, -W / 2, -W / 2, W / 2]])
wheel = np.array([[-TR, TR, TR, -TR, -TR],
[TW / 2, TW / 2, -TW / 2, -TW / 2, TW / 2]])
rr_wheel = wheel.copy() # 右后轮
rl_wheel = wheel.copy() # 左后轮
fr_wheel = wheel.copy() # 右前轮
fl_wheel = wheel.copy() # 左前轮
rr_wheel[1, :] += WD/2
rl_wheel[1, :] -= WD/2
# 方向盘旋转
rot1 = np.array([[np.cos(delta), -np.sin(delta)],
[np.sin(delta), np.cos(delta)]])
# yaw旋转矩阵
rot2 = np.array([[np.cos(yaw), -np.sin(yaw)],
[np.sin(yaw), np.cos(yaw)]])
fr_wheel = np.dot(rot1, fr_wheel)
fl_wheel = np.dot(rot1, fl_wheel)
fr_wheel += np.array([[L], [-WD / 2]])
fl_wheel += np.array([[L], [WD / 2]])
fr_wheel = np.dot(rot2, fr_wheel)
fr_wheel[0, :] += x
fr_wheel[1, :] += y
fl_wheel = np.dot(rot2, fl_wheel)
fl_wheel[0, :] += x
fl_wheel[1, :] += y
rr_wheel = np.dot(rot2, rr_wheel)
rr_wheel[0, :] += x
rr_wheel[1, :] += y
rl_wheel = np.dot(rot2, rl_wheel)
rl_wheel[0, :] += x
rl_wheel[1, :] += y
vehicle_outline = np.dot(rot2, vehicle_outline)
vehicle_outline[0, :] += x
vehicle_outline[1, :] += y
ax.plot(fr_wheel[0, :], fr_wheel[1, :], color)
ax.plot(rr_wheel[0, :], rr_wheel[1, :], color)
ax.plot(fl_wheel[0, :], fl_wheel[1, :], color)
ax.plot(rl_wheel[0, :], rl_wheel[1, :], color)
ax.plot(vehicle_outline[0, :], vehicle_outline[1, :], color)
# ax.axis('equal')
if __name__ == "__main__":
vehicle = DynamicBicycleModel(0.0, 0.0, 0.0, 1.0, 1.0, 0.0)
trajectory_x = []
trajectory_y = []
fig = plt.figure()
# 保存动图用
# i = 0
# image_list = [] # 存储图片
plt.figure(1)
for i in range(500):
plt.cla()
plt.gca().set_aspect('equal', adjustable='box')
vehicle.update(0, np.pi / 10)
draw_vehicle(vehicle.x, vehicle.y, vehicle.yaw, vehicle.delta, plt)
trajectory_x.append(vehicle.x)
trajectory_y.append(vehicle.y)
plt.plot(trajectory_x, trajectory_y, 'blue')
plt.xlim(-15, 12)
plt.ylim(-2.5, 21)
plt.pause(0.001)
# i += 1
# if (i % 50) > 0:
# plt.savefig("temp.png")
# image_list.append(imageio.imread("temp.png"))
#
# imageio.mimsave("display.gif", image_list, duration=0.1)
运行效果:
文章首发公众号:iDoitnow如果喜欢话,可以关注一下
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)