├── README.md ├── data ├── carsim_u_equals_1.csv ├── carsim_u_equals_10.csv ├── carsim_u_equals_12.csv ├── carsim_u_equals_13.csv ├── carsim_u_equals_15.csv ├── carsim_u_equals_2.csv ├── carsim_u_equals_3.csv ├── carsim_u_equals_4.csv ├── carsim_u_equals_5.csv ├── carsim_u_equals_6.csv ├── carsim_u_equals_7.csv ├── carsim_u_equals_8.csv ├── carsim_u_equals_9.csv ├── simulink_sine_u_equals_8.mat └── simulink_step_u_equals_8.mat ├── dynamic_vs_kinematic.py ├── forward_vs_backward.py └── sufficient condition test ├── A_norm.m ├── construct_A_hat.m └── single_track_3DOF.slx /README.md: -------------------------------------------------------------------------------- 1 | # IEEE-IV2021-numerically-stable-dynamic-bicycle-model 2 | Numerically Stable Dynamic Bicycle Model for Discrete-time Control, accepted by IEEE IV2021 workshop 3 | Authors: Qiang Ge, Qi Sun, Shengbo Eben Li, Sifa Zheng*, Wei Wu and Xi Chen 4 | Affiliation: School of Vehicle and Mobility, Tsinghua University 5 | Contact: gq17@mails.tsinghua.edu.cn 6 | 7 | Abstract: Dynamic/Kinematic model is of great significance in decision and control of intelligent vehicles. However, due to the singularity of dynamic models at low speed, kinematic models have been the only choice under such driving scenarios. Inspired by the concept of backward Euler method, this paper presents a discrete dynamic bicycle model feasible at any low speed. We further give a sufficient condition, based on which the numerical stability is proved. Simulation verifies that (1) the proposed model is numerically stable while the forward-Euler discretized dynamic model diverges; (2) the model reduces forecast error by up to 65% compared to the kinematic model. As far as we know, it is the first time that a dynamic bicycle model is qualified for urban driving scenarios involving stop-and-go tasks. 8 | 9 | For readers/users: Thank you for your attention! You may find the discrete model named 'dynamic_linear_backward(x0,u0,T)' in dynamic_vs_kinematic.py or forward_vs_backward.py. You may run dynamic_vs_kinematic.py to check the accuracy, or run forward_vs_backward.py to check the numerical stability. Variable 'speed' and steplength 'T' could be adjusted to observe the trends. 10 | 11 | -------------------------------------------------------------------------------- /data/simulink_sine_u_equals_8.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gqgit/IEEE-IV2021-numerically-stable-dynamic-bicycle-model/52bdd0f2f6e38ada009ada3814160f6a0a9a25bc/data/simulink_sine_u_equals_8.mat -------------------------------------------------------------------------------- /data/simulink_step_u_equals_8.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gqgit/IEEE-IV2021-numerically-stable-dynamic-bicycle-model/52bdd0f2f6e38ada009ada3814160f6a0a9a25bc/data/simulink_step_u_equals_8.mat -------------------------------------------------------------------------------- /dynamic_vs_kinematic.py: -------------------------------------------------------------------------------- 1 | """ 2 | Numerically Stable Dynamic Bicycle Model for Discrete-time Control (IEEE IV2021 workshop paper) 3 | this code is the demonstration of simulation accuracy of our method versus kinematic model. 4 | @Author: Qiang Ge 5 | @Date: 2021.06.03 6 | @email: gq17@mails.tsinghua.edu.cn 7 | """ 8 | 9 | import numpy as np 10 | import matplotlib.pyplot as plt 11 | import csv 12 | 13 | T = 0.1 14 | STEPS = int(4/T)+1 15 | 16 | speed = 8 17 | 18 | Rw = 0.325 #rolling radius of drive wheel, m 19 | m = 1412.0 #mass of vehicle, kg 20 | Iz = 1536.7 #yaw inertia of vehicle, kg*m^2 21 | a = 1.06 #distance between C.G. and front axle, m 22 | b = 1.85 #distance between C.G. and rear axle, m 23 | kf = -128915.5 #equivalent sideslip force stiffness of front axle, N/rad 24 | kr = -85943.6 #equivalent sideslip force stiffness of rear axle, N/rad 25 | 26 | family = 'Times New Roman' 27 | FONT_SIZE = 18 28 | FONT_SIZE_ = 14 29 | 30 | def dynamic_linear_backward(x0,u0,T): 31 | ''' 32 | dynamic 3DoF bicycle model, analytical, defferentiable. 33 | linear tire sideslip force, with stiffness estimation: kf and kr. 34 | the discretization is a tailored method inspired by (but not exactly be) backward Euler method. 35 | ''' 36 | 37 | x1 = np.zeros(len(x0)) 38 | 39 | x1[0] = x0[0] + T*(x0[3]*np.cos(x0[2])-x0[4]*np.sin(x0[2])) 40 | x1[1] = x0[1] + T*(x0[4]*np.cos(x0[2])+x0[3]*np.sin(x0[2])) 41 | x1[2] = x0[2] + T* x0[5] 42 | x1[3] = x0[3] + T* u0[0] 43 | x1[4] = (-(a*kf-b*kr)*x0[5]+kf*u0[1]*x0[3]+m*x0[5]*x0[3]*x0[3]-m*x0[3]*x0[4]/T)/(kf+kr-m*x0[3]/T) 44 | x1[5] = (-Iz*x0[5]*x0[3]/T-(a*kf-b*kr)*x0[4]+a*kf*u0[1]*x0[3])/((a*a*kf+b*b*kr)-Iz*x0[3]/T) 45 | 46 | return x1 47 | 48 | def kinematic_forward(x0,u0,T): 49 | ''' 50 | kinematic 2DoF bicycle model, analytical, defferentiable. 51 | the discretization is via standard forward Euler method. 52 | ''' 53 | 54 | x1 = np.zeros(len(x0)) 55 | 56 | x1[0] = x0[0] + T*(x0[3]*np.cos(x0[2]) - b*np.sin(x0[2])*x0[3]*np.tan(u0[1])/(a+b)) 57 | x1[1] = x0[1] + T*(x0[3]*np.sin(x0[2]) + b*np.cos(x0[2])*x0[3]*np.tan(u0[1])/(a+b)) 58 | x1[2] = x0[2] + T*(x0[3]*np.tan(u0[1])/(a+b)) 59 | x1[3] = x0[3] + T* u0[0] 60 | x1[4] = 0. 61 | x1[5] = 0. 62 | return x1 63 | 64 | 65 | def trajectory(x0,ur,T,N,type): 66 | traj = [] 67 | traj.append(x0) 68 | 69 | if type == 'kinematic': 70 | for i in range(N): 71 | result = kinematic_forward(traj[i],ur[i],T) 72 | traj.append(result) 73 | elif type == 'linear_backward': 74 | for i in range(N): 75 | result = dynamic_linear_backward(traj[i],ur[i],T) 76 | traj.append(result) 77 | 78 | return traj 79 | 80 | if __name__ == "__main__": 81 | 82 | x0 = [0.,0.,0.,speed,0.,0.] 83 | 84 | ####step input 85 | '''input: desired acceleration [m/s^2] and front wheel steer angle [rad] 86 | desired acceleration = driving torque/m/Rw 87 | ''' 88 | ur = [[0.,0.2674]]*STEPS #step input 89 | 90 | 91 | x_carsim_str = [] 92 | y_carsim_str = [] 93 | v_carsim_str = [] 94 | w_carsim_str = [] 95 | t_carsim_str = [] 96 | string_speed = str(speed) 97 | string_speed_no_dot = '' 98 | for i in string_speed: 99 | if i != '.': 100 | string_speed_no_dot = string_speed_no_dot + i 101 | csv_reader = csv.reader(open("data/carsim_u_equals_"+string_speed_no_dot+".csv")) 102 | 103 | kk = 0 104 | for i in csv_reader: 105 | if kk > 0: 106 | t_carsim_str.append(i[0]) #time, 1 column, s 107 | x_carsim_str.append(i[551]) #x, 552 column, m 108 | y_carsim_str.append(i[571]) #vx, 572 column, kph 109 | v_carsim_str.append(i[525]) #vy, 526 column, kph 110 | w_carsim_str.append(i[34]) #omega, 35 column, deg/s 111 | kk = kk + 1 112 | t_carsim = list(map(eval,t_carsim_str)) 113 | x_carsim = list(map(eval,x_carsim_str)) 114 | y_carsim = list(map(eval,y_carsim_str)) 115 | v_carsim = list(map(eval,v_carsim_str)) 116 | w_carsim = list(map(eval,w_carsim_str)) 117 | 118 | tra_f = trajectory(x0,ur,T,STEPS,'kinematic') 119 | tra_b = trajectory(x0,ur,T,STEPS,'linear_backward') 120 | 121 | x_f = []; x_b = [] 122 | y_f = []; y_b = [] 123 | yaw_f = []; yaw_b = [] 124 | u_f = []; u_b = [] 125 | w_b = [] 126 | v_b = [] 127 | 128 | for i in range(STEPS+1): 129 | x_f.append(tra_f[i][0]) 130 | y_f.append(tra_f[i][1]) 131 | yaw_f.append(tra_f[i][2]) 132 | u_f.append(tra_f[i][3]) 133 | 134 | x_b.append(tra_b[i][0]) 135 | y_b.append(tra_b[i][1]) 136 | yaw_b.append(tra_b[i][2]) 137 | u_b.append(tra_b[i][3]) 138 | v_b.append(tra_f[i][4]) 139 | w_b.append(tra_f[i][5]) 140 | 141 | 142 | time_prev = [] 143 | err_forw = []; err_forw_rms = [] 144 | err_back = []; err_back_rms = [] 145 | for i in range(STEPS): 146 | for j in range(len(t_carsim)): 147 | if abs(i*T - t_carsim[j]) < 0.0001:#numerical accuracy issue of data, no special meaning 148 | time_prev.append(i*T) 149 | err_forw.append( ((x_carsim[j]+1.058-x_f[i])**2+(y_carsim[j]-y_f[i])**2) ) 150 | err_back.append( ((x_carsim[j]+1.058-x_b[i])**2+(y_carsim[j]-y_b[i])**2) ) 151 | err_forw_rms.append( (sum(err_forw)/len(err_forw))**0.5 ) 152 | err_back_rms.append( (sum(err_back)/len(err_back))**0.5 ) 153 | 154 | 155 | F1 = plt.figure(figsize=(4.5,4.5),dpi=100) 156 | 157 | ax1 = F1.add_subplot(1,1,1) 158 | ax1.plot(x_f,y_f,'-r',label='kinematic (forward Euler)') 159 | ax1.plot(x_b,y_b,'-b',label='dynamic (our method)') 160 | ax1.plot([i+1.058 for i in x_carsim],y_carsim,'-g',label='CarSim')#+1.058 because CarSim reference point is at front axle instead of Center of Gravity (C.G.) 161 | ax1.set_xlabel('x location (m)', fontdict={'family':family,'size': FONT_SIZE} ) 162 | ax1.set_ylabel('y location (m)', fontdict={'family':family,'size': FONT_SIZE} ) 163 | plt.xticks(fontproperties = family, size = FONT_SIZE_) 164 | plt.yticks(fontproperties = family, size = FONT_SIZE_) 165 | ax1.legend(loc='center left',prop={'family': family,'size': FONT_SIZE_}, framealpha = 0.5) 166 | ax1.axis('equal') 167 | ax1.axis([-10,13,-1,23]) 168 | plt.tight_layout() 169 | 170 | F2 = plt.figure(figsize=(4.5,4.5),dpi=100) 171 | ax11 = F2.add_subplot(1,1,1) 172 | ax11.plot(time_prev,err_forw_rms,'-r',label='kinematic (forward Euler)') 173 | ax11.plot(time_prev,err_back_rms,'-b',label='dynamic (our method)') 174 | ax11.set_xlabel('prediction time (s)', fontdict={'family':family,'size': FONT_SIZE} ) 175 | plt.xticks(fontproperties = family, size = FONT_SIZE_) 176 | plt.yticks([0,1,2,3,4],fontproperties = family, size = FONT_SIZE_) 177 | ax11.set_ylabel('predictive rms error (m)', fontdict={'family':family,'size': FONT_SIZE} ) 178 | ax11.legend(loc='upper left',prop={'family': family,'size': FONT_SIZE_}, framealpha = 0.5) 179 | 180 | plt.tight_layout() 181 | 182 | print(' speed: ',speed) 183 | print(' err_dyna: ',err_back_rms[-1]) 184 | print(' err_kine: ',err_forw_rms[-1]) 185 | print('improved by: ',(err_forw_rms[-1] - err_back_rms[-1])/err_forw_rms[-1]) 186 | 187 | plt.show() 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | -------------------------------------------------------------------------------- /forward_vs_backward.py: -------------------------------------------------------------------------------- 1 | """ 2 | Numerically Stable Dynamic Bicycle Model for Discrete-time Control (IEEE IV2021 workshop paper) 3 | this code is the demonstration of numerical stability of our method versus forward-Euler discretization. 4 | @Author: Qiang Ge 5 | @Date: 2021.06.03 6 | @email: gq17@mails.tsinghua.edu.cn 7 | """ 8 | 9 | import numpy as np 10 | import matplotlib.pyplot as plt 11 | from scipy.io import loadmat 12 | 13 | T = 0.01 14 | T_benchmark = 0.001 15 | STEPS = int(4/T) 16 | 17 | speed = 8 18 | 19 | Rw = 0.325 #rolling radius of drive wheel, m 20 | m = 1412.0 #mass of vehicle, kg 21 | Iz = 1536.7 #yaw inertia of vehicle, kg*m^2 22 | a = 1.06 #distance between C.G. and front axle, m 23 | b = 1.85 #distance between C.G. and rear axle, m 24 | kf = -128915.5 #equivalent sideslip force stiffness of front axle, N/rad 25 | kr = -85943.6 #equivalent sideslip force stiffness of rear axle, N/rad 26 | 27 | 28 | FONT_SIZE = 18 29 | FONT_SIZE_ = 14 30 | family = 'Times New Roman' 31 | 32 | def dynamic_linear_backward(x0,u0,T): 33 | ''' 34 | dynamic 3DoF bicycle model, analytical, defferentiable. 35 | linear tire sideslip force, with stiffness estimation: kf and kr. 36 | the discretization is a tailored method inspired by (but not exactly be) backward Euler method. 37 | ''' 38 | 39 | x1 = np.zeros(len(x0)) 40 | 41 | x1[0] = x0[0] + T*(x0[3]*np.cos(x0[2])-x0[4]*np.sin(x0[2])) 42 | x1[1] = x0[1] + T*(x0[4]*np.cos(x0[2])+x0[3]*np.sin(x0[2])) 43 | x1[2] = x0[2] + T* x0[5] 44 | x1[3] = x0[3] + T* u0[0] 45 | x1[4] = (-(a*kf-b*kr)*x0[5]+kf*u0[1]*x0[3]+m*x0[5]*x0[3]*x0[3]-m*x0[3]*x0[4]/T)/(kf+kr-m*x0[3]/T) 46 | x1[5] = (-Iz*x0[5]*x0[3]/T-(a*kf-b*kr)*x0[4]+a*kf*u0[1]*x0[3])/((a*a*kf+b*b*kr)-Iz*x0[3]/T) 47 | 48 | return x1 49 | 50 | 51 | def dynamic_linear_forward(x0,u0,T): 52 | ''' 53 | dynamic 3DoF bicycle model, analytical, defferentiable. 54 | linear tire sideslip force, with stiffness estimation: kf and kr. 55 | the discretization is via standard forward Euler method. 56 | drawback: this model could be numerically instable (e.g. when T = 0.1 sec) 57 | and has a singularity issue when speed approaches zero. 58 | ''' 59 | 60 | x1 = np.zeros(len(x0)) 61 | 62 | x1[0] = x0[0] + T*(x0[3]*np.cos(x0[2])-x0[4]*np.sin(x0[2])) 63 | x1[1] = x0[1] + T*(x0[4]*np.cos(x0[2])+x0[3]*np.sin(x0[2])) 64 | x1[2] = x0[2] + T* x0[5] 65 | x1[3] = x0[3] + T*(u0[0] + x0[4]*x0[5] - kf*(x0[4]+a*x0[5]-u0[1]*x0[3])*np.sin(u0[1])/m/x0[3]) 66 | x1[4] = x0[4] + T*(kf*(x0[4]+a*x0[5]-u0[1]*x0[3])+kr*(x0[4]-b*x0[5])-m*x0[3]*x0[3]*x0[5])/m/x0[3] 67 | x1[5] = x0[5] + T*(a*kf*(x0[4]+a*x0[5]-u0[1]*x0[3])-b*kr*(x0[4]-b*x0[5]))/Iz/x0[3] 68 | 69 | return x1 70 | 71 | 72 | def trajectory(x0,ur,T,N,type): 73 | #used for taylor expansion and linearization! 74 | traj = [] 75 | traj.append(x0) 76 | 77 | if type == 'linear_forward': 78 | for i in range(N): 79 | result = dynamic_linear_forward(traj[i],ur[i],T) 80 | traj.append(result) 81 | else: 82 | for i in range(N): 83 | result = dynamic_linear_backward(traj[i],ur[i],T) 84 | traj.append(result) 85 | 86 | return traj 87 | 88 | if __name__ == "__main__": 89 | 90 | x0 = [0.,0.,0.,speed,0.,0.] 91 | 92 | ####step input 93 | '''input: desired acceleration [m/s^2] and front wheel steer angle [rad] 94 | desired acceleration = driving torque/m/Rw 95 | ''' 96 | ur_back = [[0.,0.2674]]*STEPS 97 | ur_forw = [[0.,0.2674]]*STEPS 98 | 99 | ####sine input 100 | # ur_back = [] 101 | # ur_forw = [] 102 | # for i in range(STEPS): 103 | # ur_back.append([0.,0.3 * np.sin(np.pi*i*T)]) 104 | # ur_forw.append([0.,0.3 * np.sin(np.pi*i*T)]) 105 | 106 | 107 | ###simulink step response### 108 | benchmark = loadmat("data/simulink_step_u_equals_"+str(speed)+".mat") 109 | # benchmark = loadmat("data/simulink_sine_u_equals_"+str(speed)+".mat") 110 | w_benchmark = benchmark["omega"].tolist() 111 | v_benchmark = benchmark["v"].tolist() 112 | t_benchmark = [i*T_benchmark for i in list(range(len(w_benchmark)))] 113 | benchmark_label = 'Simulink' 114 | y_lim_v = [-0.05,1.25] 115 | y_lim_w = [-0.05,1.25] 116 | 117 | 118 | tra_f = trajectory(x0,ur_forw,T,STEPS,'linear_forward') 119 | tra_b = trajectory(x0,ur_back,T,STEPS,'linear_backward') 120 | 121 | x_f = []; x_b = [] 122 | y_f = []; y_b = [] 123 | yaw_f = []; yaw_b = [] 124 | u_f = []; u_b = [] 125 | v_f = []; v_b = [] 126 | w_f = []; w_b = [] 127 | 128 | for i in range(STEPS+1): 129 | x_f.append(tra_f[i][0]) 130 | y_f.append(tra_f[i][1]) 131 | yaw_f.append(tra_f[i][2]) 132 | u_f.append(tra_f[i][3]) 133 | v_f.append(tra_f[i][4]) 134 | w_f.append(tra_f[i][5]) 135 | 136 | x_b.append(tra_b[i][0]) 137 | y_b.append(tra_b[i][1]) 138 | yaw_b.append(tra_b[i][2]) 139 | u_b.append(tra_b[i][3]) 140 | v_b.append(tra_b[i][4]) 141 | w_b.append(tra_b[i][5]) 142 | 143 | 144 | 145 | F2 = plt.figure(figsize=(8,4),dpi=100) 146 | 147 | ax9 = F2.add_subplot(1,2,1) 148 | ax9.plot([i * T for i in range(STEPS+1)],v_f,'-r',label='forward Euler') 149 | ax9.plot([i * T for i in range(STEPS+1)],v_b,'-b',label='our method') 150 | ax9.plot(t_benchmark, v_benchmark,'-g',label=benchmark_label) 151 | ax9.set_xlabel('time (s)', fontdict={'family':family,'size': FONT_SIZE}) 152 | ax9.set_ylabel('lateral velocity (m/s)', fontdict={'family':family,'size': FONT_SIZE} ) 153 | ax9.set_ylim(y_lim_v) 154 | plt.xticks(fontproperties = family, size = FONT_SIZE_) 155 | plt.yticks([0.0,0.4,0.8,1.2],fontproperties = family, size = FONT_SIZE_) 156 | ax9.legend(loc='center right',prop={'family': family,'size': FONT_SIZE_}) 157 | 158 | ax10 = F2.add_subplot(1,2,2) 159 | ax10.plot([i * T for i in range(STEPS+1)],w_f,'-r',label='forward Euler') 160 | ax10.plot([i * T for i in range(STEPS+1)],w_b,'-b',label='our method') 161 | ax10.plot(t_benchmark, w_benchmark,'-g',label=benchmark_label) 162 | ax10.set_xlabel('time (s)', fontdict={'family':family,'size': FONT_SIZE}) 163 | ax10.set_ylabel('yaw rate (rad/s)', fontdict={'family':family,'size': FONT_SIZE}); 164 | ax10.set_ylim(y_lim_w) 165 | plt.xticks(fontproperties = family, size = FONT_SIZE_) 166 | plt.yticks([0.0,0.4,0.8,1.2],fontproperties = family, size = FONT_SIZE_) 167 | legend = ax10.legend(loc='upper right',prop={'family': family,'size': FONT_SIZE_}) 168 | 169 | 170 | plt.tight_layout() 171 | 172 | plt.show() 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | -------------------------------------------------------------------------------- /sufficient condition test/A_norm.m: -------------------------------------------------------------------------------- 1 | %Numerically Stable Dynamic Bicycle Model for Discrete-time Control (IEEE IV2021 workshop paper) 2 | %this code checks the sufficient condition of numerical stability of our method with varying discretization steplength h. 3 | %Author: Qiang Ge 4 | %Date: 2021.06.03 5 | %email: gq17@mails.tsinghua.edu.cn 6 | u=0:0.1:15; 7 | h1=0.01; 8 | size = 15; 9 | wid = 1; 10 | 11 | norm2 = u; 12 | for i = 1:length(u) 13 | norm2(i)=norm(construct_A_hat(u(i), h1)); 14 | end 15 | plot(u,norm2,'linewidth',wid) 16 | %grid on 17 | hold on 18 | 19 | h2=0.05; 20 | for i = 1:length(u) 21 | norm2(i)=norm(construct_A_hat(u(i), h2)); 22 | end 23 | plot(u,norm2,'linewidth',wid) 24 | %grid on 25 | hold on 26 | 27 | h3=0.1; 28 | for i = 1:length(u) 29 | norm2(i)=norm(construct_A_hat(u(i), h3)); 30 | end 31 | plot(u,norm2,'linewidth',wid) 32 | %grid on 33 | 34 | axis([0,max(u),0,1]); 35 | hl = legend(strcat('$T_s$ = ',num2str(h1),' s'),strcat('$T_s$ = ',num2str(h2),' s'), strcat('$T_s$ = ',num2str(h3),' s'),'Location','Southeast'); 36 | set(hl,'Box','off','Interpret','latex','FontSize',size); 37 | xlabel('longitudinal velocity $u_k$ (m/s)','Interpret','latex','FontSize',size) 38 | ylabel('$\|\hat{A_k}\|_2$','Interpret','latex','FontSize',size) 39 | yticks([0 0.5 1]) 40 | yticklabels({'0','0.5','1'}) 41 | set(gca,'FontName','Times New Roman','FontSize',14) 42 | %ylabel('2-norm of $\hat A_k$','Interpret','latex','FontSize',size) 43 | 44 | 45 | -------------------------------------------------------------------------------- /sufficient condition test/construct_A_hat.m: -------------------------------------------------------------------------------- 1 | function [A] = construct_A_hat(u,h) 2 | %Author: Qiang Ge 3 | %Date: 2021.06.03 4 | %email: gq17@mails.tsinghua.edu.cn 5 | m = 1412; 6 | Iz = 1536.7; 7 | a = 1.06; 8 | b = 1.85; 9 | k1 = -128916; 10 | k2 = -85944; 11 | 12 | A = [ m*u/(m*u-h*(k1+k2)) (h*(a*k1-b*k2)-h*m*u^2)/(m*u-h*(k1+k2)); 13 | h*(a*k1-b*k2)/(Iz*u-h*(a^2*k1+b^2*k2)) Iz*u/(Iz*u-h*(a^2*k1+b^2*k2)); ]; 14 | 15 | end -------------------------------------------------------------------------------- /sufficient condition test/single_track_3DOF.slx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gqgit/IEEE-IV2021-numerically-stable-dynamic-bicycle-model/52bdd0f2f6e38ada009ada3814160f6a0a9a25bc/sufficient condition test/single_track_3DOF.slx --------------------------------------------------------------------------------