当前位置: 首页 > news >正文

后轮位置反馈控制与算法仿真实现

文章目录

    • 1. 后轮反馈控制
    • 2. 算法原理
    • 3. 算法和仿真实现

1. 后轮反馈控制

后轮反馈控制(Rear wheel feedback)算法是利用后轮中心的跟踪偏差来进行转向控制量计算的方法,属于Frenet坐标系的一个应用。通过选择合适的李雅普诺夫函数设计控制率,利用后轮中心的跟踪偏差来进行转向控制量计算的方法。

2. 算法原理

在这里插入图片描述

后轮反馈控制算法原理如上图所示,其中

  • P P P:当前距离车辆最近的路经点;

  • e y e_y ey P P P点与车辆后轮中心点的横向偏差 A P AP AP,实际上对应的就是frenet坐标下的 l l l

  • φ \varphi φ:车辆朝向与 X X X轴正方向的夹角,即航向角;

  • φ r \varphi_{r} φr P P P点切线与 X X X轴正方向的夹角;

  • φ e \varphi_e φe:车辆航向角误差,即 φ − φ r \varphi-\varphi_{r} φφr

  • n τ ⃗ \vec{n_\tau} nτ P P P点法线的单位向量;

  • τ r ⃗ \vec{\tau_r} τr P P P点切线的单位向量;

  • L L L:轴距

  • δ f \delta_f δf:前轮转角

  • v v v:车辆的速度

由前面的文章frenet坐标与cartesian坐标相互转换与代码实现和上图的几何关系可得车辆在参考轨迹上的投影点 P P P处的线速度 s ˙ \dot{s} s˙和横向误差 e y e_y ey(即对应frenet坐标系下的 l ˙ \dot{l} l˙)的表达式为
s ˙ = v ⃗ τ r ⃗ 1 − k r l = ∣ v ⃗ ∣ ∣ τ r ⃗ ∣ c o s φ e 1 − k r e y = ∣ v ⃗ ∣ c o s φ e 1 − k r e y (1) \dot{s}=\frac{\vec{v} \vec{\tau_r}}{1-k_rl} = \frac{|\vec{v}| |\vec{\tau_r}|cos{\varphi_e}}{1-k_re_y}=\frac{|\vec{v}|cos{\varphi_e}}{1-k_re_y} \tag{1} s˙=1krlv τr =1kreyv ∣∣τr cosφe=1kreyv cosφe(1)

e y ˙ = l ˙ = v ⃗ n r ⃗ = ∣ v ⃗ ∣ ∣ n r ⃗ ∣ c o s ( π 2 − φ e ) = ∣ v ⃗ ∣ s i n φ e (2) \dot{e_y}=\dot{l}= \vec{v} \vec{n_r}= |\vec{v}| |\vec{n_r}| cos({\frac{\pi}{2}-\varphi_e}) = |\vec{v}| sin{\varphi_e} \tag{2} ey˙=l˙=v nr =v ∣∣nr cos(2πφe)=v sinφe(2)

由图中几何关系可得航向误差为
φ e = φ − φ r (3) \varphi_e = \varphi-\varphi_{r} \tag{3} φe=φφr(3)
则航向误差变化率为
φ e ˙ = φ ˙ − φ r ˙ = φ ˙ − s ˙ R r = φ ˙ − s ˙ k r (4) \dot{\varphi_e} = \dot{\varphi}-\dot{\varphi_{r}} = \dot{\varphi}-\frac{\dot{s}}{R_r }= \dot{\varphi}-\dot{s}k_r \tag{4} φe˙=φ˙φr˙=φ˙Rrs˙=φ˙s˙kr(4)

:一个刚体的角速度 = 线速度/线速度到速度瞬心的距离,上式中 R r R_r Rr为点 P P P处的瞬时圆心半径。

将(1)代入(4)可得
φ e ˙ = φ ˙ − s ˙ k r = φ ˙ − k r ∣ v ⃗ ∣ c o s φ e 1 − k r e y (4) \dot{\varphi_e} = \dot{\varphi}-\dot{s}k_r = \dot{\varphi}-\frac{k_r|\vec{v}|cos{\varphi_e}}{1-k_re_y}\tag{4} φe˙=φ˙s˙kr=φ˙1kreykrv cosφe(4)
综上可得后轮反馈控制算法对应模型的微分方程为

{ s ˙ = ∣ v ⃗ ∣ c o s φ e 1 − k r e y e y ˙ = ∣ v ⃗ ∣ s i n φ e φ e ˙ = φ ˙ − k r ∣ v ⃗ ∣ c o s φ e 1 − k r e y (5) \begin{cases} \dot{s}&=\quad \frac{|\vec{v}|cos{\varphi_e}}{1-k_re_y} \\ \dot{e_y}&=\quad |\vec{v}| sin{\varphi_e}\\ \dot{\varphi_e} &=\quad \dot{\varphi}-\frac{k_r|\vec{v}|cos{\varphi_e}}{1-k_re_y} \end{cases} \tag{5} s˙ey˙φe˙=1kreyv cosφe=v sinφe=φ˙1kreykrv cosφe(5)

有了模型的微分方程,我们可以根据李雅普诺夫第二方法设计该模型的车辆航向变化率 φ ˙ \dot{\varphi} φ˙

李雅普诺夫第二方法:

稳定的系统能量总是不断被耗散的,李雅普诺夫通过定义一个标量函数 V ( x ) V(x) V(x) (通常能代表广义能量)来分析稳定性。这种方法的避免了直接求解方程,也没有进行近似线性化,所以也一般称之为直接法。 V ( x ) V(x) V(x)需满足:

  1. V ( x ) = 0 V(x)=0 V(x)=0 当且仅当 x = 0 x =0 x=0
  2. V ( x ) > 0 V(x)>0 V(x)>0 当且仅当 x ≠ 0 x\neq0 x=0
  3. V ˙ ( x ) ≤ 0 \dot{V}(x)\leq0 V˙(x)0 x ≠ 0 x \neq0 x=0

则称系统在李雅普诺夫意义下是稳定的,特别的,若 x ≠ 0 x \neq0 x=0时,有 V ˙ ( x ) < 0 \dot{V}(x)<0 V˙(x)<0,则系统是渐进稳定的。

这里我们比较关心的就是横向误差 e y e_y ey φ e \varphi_e φe,期望他们随着时间的增长逐渐收敛于0,我们可以定义李雅普诺夫函数形式如下:
V ( e y , φ e ) = 1 2 e y 2 + 1 2 k e φ e 2 (6) V(e_y,\varphi_e)=\frac{1}{2}e_y^2+\frac{1}{2k_e}\varphi_e^2 \tag{6} V(ey,φe)=21ey2+2ke1φe2(6)
其中 k e > 0 k_e>0 ke>0

为了使 ( e y , φ e ) (e_y,\varphi_e) (ey,φe)在平衡点 ( 0 , 0 ) (0,0) (0,0)处稳定,根据李雅普诺夫第二方法的稳定判据, V ( x ) V(x) V(x)需满足:

  1. V ( e y , φ e ) = 0 V(e_y,\varphi_e)=0 V(ey,φe)=0 当且仅当 e y = 0 , φ e = 0 e_y=0,\varphi_e =0 ey=0,φe=0
  2. V ( e y , φ e ) > 0 V(e_y,\varphi_e)>0 V(ey,φe)>0 当且仅当 e y ≠ 0 , φ e ≠ 0 e_y\neq0,\varphi_e\neq0 ey=0,φe=0
  3. V ˙ ( e y , φ e ) ≤ 0 \dot{V}(e_y,\varphi_e)\leq0 V˙(ey,φe)0 e y ≠ 0 , φ e ≠ 0 e_y\neq0,\varphi_e\neq0 ey=0,φe=0

对于1、2两条,我们选取的李亚普函数 V ( e y , φ e ) V(e_y,\varphi_e) V(ey,φe)显然满足。所以满足李雅普诺夫第二方法的前两条。

对于 V ˙ ( e y , φ e ) \dot{V}(e_y,\varphi_e) V˙(ey,φe)我们结合(5)可得
V ˙ ( e y , φ e ) = e y e y ˙ + 1 k e φ e φ e ˙ = e y ∣ v ⃗ ∣ s i n φ e + 1 k e φ e ( φ ˙ − k r ∣ v ⃗ ∣ c o s φ e 1 − k r e y ) (7) \begin{aligned} \dot{V}(e_y,\varphi_e) &=e_y\dot{e_y}+\frac{1}{k_e}\varphi_e \dot{\varphi_e}\\ &=e_y|\vec{v}| sin{\varphi_e}+ \frac{1}{k_e}\varphi_e(\dot{\varphi}-\frac{k_r|\vec{v}|cos{\varphi_e}}{1-k_re_y}) \end{aligned} \tag{7} V˙(ey,φe)=eyey˙+ke1φeφe˙=eyv sinφe+ke1φe(φ˙1kreykrv cosφe)(7)
V ˙ ( e y , φ e ) = 0 \dot{V}(e_y,\varphi_e)=0 V˙(ey,φe)=0,结合(7)式可得
e y ∣ v ⃗ ∣ s i n φ e + 1 k e φ e ( φ ˙ − k r ∣ v ⃗ ∣ c o s φ e 1 − k r e y ) = 0 k e e y ∣ v ⃗ ∣ s i n φ e φ e + φ ˙ − k r ∣ v ⃗ ∣ c o s φ e 1 − k r e y = 0 (8) \begin{aligned} e_y|\vec{v}| sin{\varphi_e}+ \frac{1}{k_e}\varphi_e(\dot{\varphi}-\frac{k_r|\vec{v}|cos{\varphi_e}}{1-k_re_y})&=0\\ k_ee_y|\vec{v}| \frac{sin{\varphi_e}}{\varphi_e}+ \dot{\varphi}-\frac{k_r|\vec{v}|cos{\varphi_e}}{1-k_re_y}&=0 \end{aligned} \tag{8} eyv sinφe+ke1φe(φ˙1kreykrv cosφe)keeyv φesinφe+φ˙1kreykrv cosφe=0=0(8)
由上式可以得到零界航向变化率 φ 0 ˙ \dot{\varphi_0} φ0˙等于
φ 0 ˙ = k r ∣ v ⃗ ∣ c o s φ e 1 − k r e y − k e e y ∣ v ⃗ ∣ s i n φ e φ e (9) \dot{\varphi_0}=\frac{k_r|\vec{v}|cos{\varphi_e}}{1-k_re_y}-k_ee_y|\vec{v}| \frac{sin{\varphi_e}}{\varphi_e} \tag{9} φ0˙=1kreykrv cosφekeeyv φesinφe(9)
为了使 V ˙ ( e y , φ e ) \dot{V}(e_y,\varphi_e) V˙(ey,φe)满足要求3,我们可以设计一个调节函数 g ( e y , φ e ) = k φ ∣ v ⃗ ∣ φ e > 0 g(e_y,\varphi_e) = k_\varphi |\vec{v}|\varphi_e>0 g(ey,φe)=kφv φe>0,其中 k φ > 0 k_{\varphi}>0 kφ>0,我们可以将车辆航向的变化率 φ ˙ \dot{\varphi} φ˙设置为
φ ˙ = φ 0 ˙ − g ( e y , φ e ) = k r ∣ v ⃗ ∣ c o s φ e 1 − k r e y − k e e y ∣ v ⃗ ∣ s i n φ e φ e − k φ ∣ v ⃗ ∣ φ e (10) \dot{\varphi}=\dot{\varphi_0}-g(e_y,\varphi_e)=\frac{k_r|\vec{v}|cos{\varphi_e}}{1-k_re_y}-k_ee_y|\vec{v}| \frac{sin{\varphi_e}}{\varphi_e}-k_\varphi |\vec{v}|\varphi_e \tag{10} φ˙=φ0˙g(ey,φe)=1kreykrv cosφekeeyv φesinφekφv φe(10)
将(10)代入(7)得
V ˙ ( e y , φ e ) = e y ∣ v ⃗ ∣ s i n φ e + 1 k e φ e ( φ ˙ − k r ∣ v ⃗ ∣ c o s φ e 1 − k r e y ) = e y ∣ v ⃗ ∣ s i n φ e + 1 k e φ e ( k r ∣ v ⃗ ∣ c o s φ e 1 − k r e y − k e e y ∣ v ⃗ ∣ s i n φ e φ e − k φ ∣ v ⃗ ∣ φ e − k r ∣ v ⃗ ∣ c o s φ e 1 − k r e y ) = e y ∣ v ⃗ ∣ s i n φ e + 1 k e φ e ( − k e e y ∣ v ⃗ ∣ s i n φ e φ e − k φ ∣ v ⃗ ∣ φ e ) = − k φ k e ∣ v ⃗ ∣ φ e 2 < 0 (11) \begin{aligned} \dot{V}(e_y,\varphi_e) &=e_y|\vec{v}| sin{\varphi_e}+ \frac{1}{k_e}\varphi_e(\dot{\varphi}-\frac{k_r|\vec{v}|cos{\varphi_e}}{1-k_re_y})\\ &=e_y|\vec{v}| sin{\varphi_e}+ \frac{1}{k_e}\varphi_e(\frac{k_r|\vec{v}|cos{\varphi_e}}{1-k_re_y}-k_ee_y|\vec{v}| \frac{sin{\varphi_e}}{\varphi_e}-k_\varphi |\vec{v}|\varphi_e-\frac{k_r|\vec{v}|cos{\varphi_e}}{1-k_re_y}) \\ &=e_y|\vec{v}| sin{\varphi_e}+\frac{1}{k_e}\varphi_e(-k_ee_y|\vec{v}| \frac{sin{\varphi_e}}{\varphi_e}-k_\varphi |\vec{v}|\varphi_e) \\ &=-\frac{k_\varphi}{k_e} |\vec{v}|\varphi_e^2 < 0 \end{aligned} \tag{11} V˙(ey,φe)=eyv sinφe+ke1φe(φ˙1kreykrv cosφe)=eyv sinφe+ke1φe(1kreykrv cosφekeeyv φesinφekφv φe1kreykrv cosφe)=eyv sinφe+ke1φe(keeyv φesinφekφv φe)=kekφv φe2<0(11)
因此我们设计的公式(10)即车辆航向的变化率 φ ˙ \dot{\varphi} φ˙满足李雅普诺夫的稳定性条件。

根据前面文章介绍过的车辆运动学模型下的几何关系
t a n δ f = L R (12) tan\delta_f=\frac{L}{R} \tag{12} tanδf=RL(12)
车辆的航向变化率与车速的转弯半径关系 φ ˙ = v / R \dot{\varphi}=v/R φ˙=v/R,结合上式可得
δ f = a r c t a n φ ˙ L v (13) \delta_f=arctan\frac{\dot{\varphi}L}{v} \tag{13} δf=arctanvφ˙L(13)

3. 算法和仿真实现

rear_wheel_feedback.py

import math
import numpy as npclass C:# System configK_theta = 1.0K_e = 0.5def normalize_angle(angle):a = math.fmod(angle + np.pi, 2 * np.pi)if a < 0.0:a += (2.0 * np.pi)return a - np.pidef rear_wheel_feedback_control(vehicle, ref_path):theta_e, er, k, ind = calc_preparation(vehicle, ref_path)vr = vehicle.vdot_phi = vr * k * math.cos(theta_e) / (1.0 - k * er) - \C.K_theta * abs(vr) * theta_e - C.K_e * vr * math.sin(theta_e) * er / (theta_e + 1e-19)delta = math.atan2(vehicle.L * dot_phi, vr)return delta, ind, erdef calc_preparation(vehicle, ref_path):"""计算角度误差theta_e、横向误差er、曲率rk和索引index"""rx, ry, ref_yaw, ref_kappa = ref_path[:, 0], ref_path[:, 1], ref_path[:, 2], ref_path[:, 4]dx = [vehicle.x - icx for icx in rx]dy = [vehicle.y - icy for icy in ry]d = np.hypot(dx, dy)index = np.argmin(d)rk = ref_kappa[index]ryaw = ref_yaw[index]vec_nr = np.array([math.cos(ryaw + math.pi / 2.0),math.sin(ryaw + math.pi / 2.0)])vec_target_2_rear = np.array([vehicle.x - rx[index],vehicle.y - ry[index]])er = np.dot(vec_target_2_rear, vec_nr)theta_e = normalize_angle(vehicle.yaw - ryaw)return theta_e, er, rk, index

kinematic_bicycle_model.py

import math
import numpy as npclass Vehicle:def __init__(self,x=0.0,y=0.0,yaw=0.0,v=0.0,dt=0.1,l=3.0):self.steer = 0self.x = xself.y = yself.yaw = yawself.v = vself.dt = dtself.L = l  # 轴距self.x_front = x + l * math.cos(yaw)self.y_front = y + l * math.sin(yaw)def update(self, a, delta, max_steer=np.pi):delta = np.clip(delta, -max_steer, max_steer)self.steer = deltaself.x = self.x + self.v * math.cos(self.yaw) * self.dtself.y = self.y + self.v * math.sin(self.yaw) * self.dtself.yaw = self.yaw + self.v / self.L * math.tan(delta) * self.dtself.v = self.v + a * self.dtself.x_front = self.x + self.L * math.cos(self.yaw)self.y_front = self.y + self.L * math.sin(self.yaw)class VehicleInfo:# Vehicle parameterL = 3.0  #轴距W = 2.0  #宽度LF = 3.8  #后轴中心到车头距离LB = 0.8  #后轴中心到车尾距离MAX_STEER = 0.6  # 最大前轮转角TR = 0.5  # 轮子半径TW = 0.5  # 轮子宽度WD = W  #轮距LENGTH = LB + LF  # 车辆长度def draw_trailer(x, y, yaw, steer, ax, vehicle_info=VehicleInfo, color='black'):vehicle_outline = np.array([[-vehicle_info.LB, vehicle_info.LF, vehicle_info.LF, -vehicle_info.LB, -vehicle_info.LB],[vehicle_info.W / 2, vehicle_info.W / 2, -vehicle_info.W / 2, -vehicle_info.W / 2, vehicle_info.W / 2]])wheel = np.array([[-vehicle_info.TR, vehicle_info.TR, vehicle_info.TR, -vehicle_info.TR, -vehicle_info.TR],[vehicle_info.TW / 2, vehicle_info.TW / 2, -vehicle_info.TW / 2, -vehicle_info.TW / 2, vehicle_info.TW / 2]])rr_wheel = wheel.copy() #右后轮rl_wheel = wheel.copy() #左后轮fr_wheel = wheel.copy() #右前轮fl_wheel = wheel.copy() #左前轮rr_wheel[1,:] += vehicle_info.WD/2rl_wheel[1,:] -= vehicle_info.WD/2#方向盘旋转rot1 = np.array([[np.cos(steer), -np.sin(steer)],[np.sin(steer), np.cos(steer)]])#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([[vehicle_info.L], [-vehicle_info.WD / 2]])fl_wheel += np.array([[vehicle_info.L], [vehicle_info.WD / 2]])fr_wheel = np.dot(rot2, fr_wheel)fr_wheel[0, :] += xfr_wheel[1, :] += yfl_wheel = np.dot(rot2, fl_wheel)fl_wheel[0, :] += xfl_wheel[1, :] += yrr_wheel = np.dot(rot2, rr_wheel)rr_wheel[0, :] += xrr_wheel[1, :] += yrl_wheel = np.dot(rot2, rl_wheel)rl_wheel[0, :] += xrl_wheel[1, :] += yvehicle_outline = np.dot(rot2, vehicle_outline)vehicle_outline[0, :] += xvehicle_outline[1, :] += yax.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')

path_generator.py

"""
路径轨迹生成器
"""import math
import numpy as npclass Path:def __init__(self):self.ref_line = self.design_reference_line()self.ref_yaw = self.cal_yaw()self.ref_s = self.cal_accumulated_s()self.ref_kappa = self.cal_kappa()def design_reference_line(self):rx, ry = [], []step_curve = 0.005 * math.pistep_line = 0.25for ix in np.arange(5, 80, step_line):rx.append(ix)ry.append(60)cx, cy, cr = 80, 45, 15theta = np.arange(math.pi/2, -math.pi/2, -step_curve)for itheta in theta:rx.append(cx + cr * math.cos(itheta))ry.append(cy + cr * math.sin(itheta))for ix in np.arange(80, 15, -step_line):rx.append(ix)ry.append(30)cx, cy, cr = 15, 15, 15theta = np.arange(math.pi/2, math.pi * 1.5, step_curve)for itheta in theta:rx.append(cx + cr * math.cos(itheta))ry.append(cy + cr * math.sin(itheta))for ix in np.arange(15, 90, step_line):rx.append(ix)ry.append(0)return np.column_stack((rx, ry))def cal_yaw(self):yaw = []for i in range(len(self.ref_line)):if i == 0:yaw.append(math.atan2(self.ref_line[i + 1, 1] - self.ref_line[i, 1],self.ref_line[i + 1, 0] - self.ref_line[i, 0]))elif i == len(self.ref_line) - 1:yaw.append(math.atan2(self.ref_line[i, 1] - self.ref_line[i - 1, 1],self.ref_line[i, 0] - self.ref_line[i - 1, 0]))else:yaw.append(math.atan2(self.ref_line[i + 1, 1] - self.ref_line[i -1, 1],self.ref_line[i + 1, 0] - self.ref_line[i - 1, 0]))return yawdef cal_accumulated_s(self):s = []for i in range(len(self.ref_line)):if i == 0:s.append(0.0)else:s.append(math.sqrt((self.ref_line[i, 0] - self.ref_line[i-1, 0]) ** 2+ (self.ref_line[i, 1] - self.ref_line[i-1, 1]) ** 2))return sdef cal_kappa(self):# 计算曲线各点的切向量dp = np.gradient(self.ref_line.T, axis=1)# 计算曲线各点的二阶导数d2p = np.gradient(dp, axis=1)# 计算曲率kappa = (d2p[0] * dp[1] - d2p[1] * dp[0]) / ((dp[0] ** 2 + dp[1] ** 2) ** (3 / 2))return kappadef get_ref_line_info(self):return self.ref_line[:, 0], self.ref_line[:, 1], self.ref_yaw, self.ref_s, self.ref_kappa

main.py

from kinematic_bicycle_model import Vehicle, VehicleInfo, draw_trailer
from rear_wheel_feedback import rear_wheel_feedback_control
from path_generator import Path
import numpy as np
import matplotlib.pyplot as plt
import imageio.v2 as imageioMAX_SIMULATION_TIME = 200.0  # 程序最大运行时间200*dtdef main():# 设置跟踪轨迹rx, ry, ref_yaw, ref_s, ref_kappa = Path().get_ref_line_info()ref_path = np.column_stack((rx, ry, ref_yaw, ref_s, ref_kappa))# 假设车辆初始位置为(5,55),航向角yaw=pi/6,速度为2m/s,时间周期dt为0.1秒vehicle = Vehicle(x=5.0,y=55.0,yaw=np.pi/6,v=2.0,dt=0.1,l=VehicleInfo.L)time = 0.0  # 初始时间target_ind = 0# 记录车辆轨迹trajectory_x = []trajectory_y = []lat_err = []  # 记录横向误差i = 0image_list = []  # 存储图片plt.figure(1)last_idx = ref_path.shape[0] - 1  # 跟踪轨迹的最后一个点的索引while MAX_SIMULATION_TIME >= time and last_idx > target_ind:time += vehicle.dt  # 累加一次时间周期# rear_wheel_feedbackdelta_f, target_ind, e_y = rear_wheel_feedback_control(vehicle, ref_path)# 横向误差lat_err.append(e_y)# 更新车辆状态vehicle.update(0.0, delta_f, np.pi / 10)  # 由于假设纵向匀速运动,所以加速度a=0.0trajectory_x.append(vehicle.x)trajectory_y.append(vehicle.y)# 显示动图plt.cla()plt.plot(ref_path[:, 0], ref_path[:, 1], '-.b', linewidth=1.0)draw_trailer(vehicle.x, vehicle.y, vehicle.yaw, vehicle.steer, plt)plt.plot(trajectory_x, trajectory_y, "-r", label="trajectory")plt.plot(ref_path[target_ind, 0], ref_path[target_ind, 1], "go", label="target")plt.axis("equal")plt.grid(True)plt.pause(0.001)plt.savefig("temp.png")#     i += 1#     if (i % 50) > 0:#         image_list.append(imageio.imread("temp.png"))## imageio.mimsave("display.gif", image_list, duration=0.01)plt.figure(2)plt.subplot(2, 1, 1)plt.plot(ref_path[:, 0], ref_path[:, 1], '-.b', linewidth=1.0)plt.plot(trajectory_x, trajectory_y, 'r')plt.title("actual tracking effect")plt.subplot(2, 1, 2)plt.plot(lat_err)plt.title("lateral error")plt.show()if __name__ == '__main__':main()

运行效果:

控制效果和横向误差:
在这里插入图片描述

以上内容仅是个人理解,如有漏误欢迎批评指正!

文章首发公众号:iDoitnow如果喜欢话,可以关注一下

相关文章:

后轮位置反馈控制与算法仿真实现

文章目录 1. 后轮反馈控制2. 算法原理3. 算法和仿真实现 1. 后轮反馈控制 后轮反馈控制&#xff08;Rear wheel feedback&#xff09;算法是利用后轮中心的跟踪偏差来进行转向控制量计算的方法&#xff0c;属于Frenet坐标系的一个应用。通过选择合适的李雅普诺夫函数设计控制率…...

实战 vue3 使用百度编辑器ueditor

前言 在开发项目由于需求vue自带对编辑器不能满足使用&#xff0c;所以改为百度编辑器&#xff0c;但是在网上搜索发现都讲得非常乱&#xff0c;所以写一篇使用流程的文章 提示&#xff1a;以下是本篇文章正文内容&#xff0c;下面案例可供参考 一、下载ueditor编辑器 一个“…...

N种方法解决1(CTF)

这里遇到的问题&#xff1a;一开始采用的base64解码平台有问题&#xff1b;默认解密出的格式为GBK格式&#xff1b;直接复制粘贴发现无法还原图片&#xff1b;又尝试了其他编码的&#xff1b;发现只有hex格式可以保证图片正常还原&#xff1b; 图片是以二进制存储的&#xff1…...

Istio实战:Istio Kiali部署与验证

目录 前言一、Istio安装小插曲 注意事项 二、Kiali安装三、Istio测试参考资料 前言 前几天我就开始捣腾Istio。前几天在执行istioctl install --set profiledemo -y 的时候老是在第二步就报错了&#xff0c;开始我用的istio版本是1.6.8。 后面查看k8s与istio的版本对应关系后发…...

ASPxGridView中使用PopupEditForm表单字段联动填充

c#中devexpress的控件ASPxGridView中使用PopupEditForm表单字段联动填充 //选择项目名称&#xff0c;自动填充项目编号 <Columns><dx:GridViewDataTextColumn FieldName"id" ReadOnly"True" VisibleIndex"0" Visible"False"…...

基于Pytorch的猫狗图片分类【深度学习CNN】

猫狗分类来源于Kaggle上的一个入门竞赛——Dogs vs Cats。为了加深对CNN的理解&#xff0c;基于Pytorch复现了LeNet,AlexNet,ResNet等经典CNN模型&#xff0c;源代码放在GitHub上&#xff0c;地址传送点击此处。项目大纲如下&#xff1a; 文章目录 一、问题描述二、数据集处理…...

flutter sliver 多种滚动组合开发指南

flutter sliver 多种滚动组合开发指南 视频 https://youtu.be/4mho1kZ_YQU https://www.bilibili.com/video/BV1WW4y1d7ZC/ 前言 有不少同学工作中遇到需要把几个不同滚动行为组件&#xff08;顶部 appBar、内容固定块、tabBar 切换、tabBarView视图、自适应高度、横向滚动&a…...

kafka生产者2

1.数据可靠 • 0&#xff1a;生产者发送过来的数据&#xff0c;不需要等数据落盘应答。 风险&#xff1a;leader挂了之后&#xff0c;follower还没有收到消息。。。。 • 1&#xff1a;生产者发送过来的数据&#xff0c;Leader收到数据后应答。 风险&#xff1a;leader应答…...

【LNMP】云导航项目部署及环境搭建(复杂)

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、项目介绍1.1项目环境架构LNMP1.2项目代码说明 二、项目环境搭建2.1 Nginx安装2.2 php安装2.3 nginx配置和php配置2.3.1 修改nginx文件2.3.2 修改vim /etc/p…...

nginx之状态页 日志分割 自定义图表 证书

5.1 网页的状态页 基于nginx 模块 ngx_http_stub_status_module 实现&#xff0c;在编译安装nginx的时候需要添加编译参数 --with-http_stub_status_module&#xff0c;否则配置完成之后监测会是提示语法错误注意: 状态页显示的是整个服务器的状态,而非虚拟主机的状态 server{…...

数字人的未来:数字人对话系统 Linly-Talker + 克隆语音 GPT-SoVITS

&#x1f680;数字人的未来&#xff1a;数字人对话系统 Linly-Talker 克隆语音 GPT-SoVITS https://github.com/Kedreamix/Linly-Talker 2023.12 更新 &#x1f4c6; 用户可以上传任意图片进行对话 2024.01 更新 &#x1f4c6; 令人兴奋的消息&#xff01;我现在已经将强…...

SpringMVC 学习(五)之域对象

目录 1 域对象介绍 2 向 request 域对象共享数据 2.1 通过 ServletAPI (HttpServletRequest) 向 request 域对象共享数据 2.2 通过 ModelAndView 向 request 域对象共享数据 2.3 通过 Model 向 request 域对象共享数据 2.4 通过 map 向 request 域对象共享数据 2.5 通过…...

✅技术社区项目—JWT身份验证

通用的JWT鉴权方案 JWT鉴权流程 基本流程分三步: ● 用户登录成功之后&#xff0c;后端将生成的jwt返回给前端&#xff0c;然后前端将其保存在本地缓存; ● 之后前端与后端的交互时&#xff0c;都将iwt放在请求头中&#xff0c;比如可以将其放在Http的身份认证的请求头 Author…...

5.2 Ajax 数据爬取实战

目录 1. 实战内容 2、Ajax 分析 3、爬取内容 4、存入MySQL 数据库 4.1 创建相关表 4.2 数据插入表中 5、总代码与结果 1. 实战内容 爬取Scrape | Movie的所有电影详情页的电影名、类别、时长、上映地及时间、简介、评分&#xff0c;并将这些内容存入MySQL数据库中。 2、…...

276.【华为OD机试真题】矩阵匹配(二分法—JavaPythonC++JS实现)

🚀点击这里可直接跳转到本专栏,可查阅顶置最新的华为OD机试宝典~ 本专栏所有题目均包含优质解题思路,高质量解题代码(Java&Python&C++&JS分别实现),详细代码讲解,助你深入学习,深度掌握! 文章目录 一. 题目-矩阵匹配二.解题思路三.题解代码Python题解代码…...

java——多线程基础

目录 线程的概述多线程的创建方式一&#xff1a;继承Thread类方式二&#xff1a;实现Runnable接口方式三&#xff1a;利用Callable接口、FutureTask类来实现。Thread常用的方法 线程安全问题线程安全问题概述线程安全问题案例取钱案例描述模拟代码如下&#xff1a;执行结果 线程…...

Python服务器监测测试策略与工具:确保应用的高可用性!

在构建高可用性的应用程序时&#xff0c;服务器监测测试是至关重要的一环。Python作为一种强大的编程语言&#xff0c;提供了丰富的工具和库来帮助我们进行服务器监测测试。本文将介绍一些关键的策略和工具&#xff0c;帮助你确保应用的高可用性。 1. 监测策略的制定&#xff…...

Spring Security源码学习

Spring Security本质是一个过滤器链 过滤器链本质是责任链设计模型 1. HttpSecurity 【第五篇】深入理解HttpSecurity的设计-腾讯云开发者社区-腾讯云 在以前spring security也是采用xml配置的方式&#xff0c;在<http>标签中配置http请求相关的配置&#xff0c;如用户…...

大数据面试总结三

1、hdfs作为分布式存储系统&#xff0c;底层的实现的方式&#xff08;可能不正确&#xff09; 1、底层是一个分布式存储的&#xff0c;底层会将数据进行切分多个block块&#xff08;128M&#xff09;&#xff0c;并存储在不同的节点上面&#xff0c;这种分布式方式有助于提高数…...

AI赚钱套路总结和教程

最近李一舟和Sora 很火&#xff0c;作为第一批使用Sora赚钱的男人&#xff0c;一个清华学美术的跟人讲AI&#xff0c;信的人太多了&#xff0c;钱太好赚了。3年时间&#xff0c;李一舟仅通过卖课就赚了1.75亿元&#xff0c;其中《每个人的人工智能课》收入2786万元&#xff0c;…...

Linux安装jdk、tomcat、MySQL离线安装与启动

一、JDK和Tomcat的安装 1.JDK安装 直接上传到Linux服务器的&#xff0c;上传jdk、tomcat安装包 解压JDK安装包 //解压jdk tar -zxvf jdk-8u151-linux-x64.tar.gz 置环境变量(JAVA_HOME和PATH) vim /etc/profile 在文件末尾添加以下内容&#xff1a; //java environment expo…...

Python爬虫-使用代理伪装IP

爬虫系列&#xff1a;http://t.csdnimg.cn/WfCSx 前言 我们在做爬虫的过程中经常会遇到这样的情况&#xff0c;最初爬虫正常运行&#xff0c;正常抓取数据&#xff0c;一切看起来都是那么的美好&#xff0c;然而一杯茶的功夫可能就会出现错误&#xff0c;比如 403 Forbidden&…...

Typora结合PicGo + 使用Github搭建个人免费图床

文章目录 一、国内图床比较二、使用Github搭建图床三、PicGo整合Github图床1、下载并安装PicGo2、设置图床3、整合jsDelivr具体配置介绍 4、测试5、附录 四、Typora整合PicGo实现自动上传 每次写博客时&#xff0c;我都会习惯在Typora写好&#xff0c;然后再复制粘贴到对应的网…...

【Redis】redis简介与安装

Redis 简介 Redis 是完全开源的&#xff0c;遵守 BSD 协议&#xff08;Berkeley Software Distribution 意思是"伯克利软件发行版&#xff09;&#xff0c;是一个高性能的 key-value 数据库。具有以下几个比较明显的特点&#xff1a; 性能极高 – Redis能读的速度可以达…...

【xss跨站漏洞】xss漏洞利用工具beef的安装

安装环境 阿里云服务器&#xff0c;centos8.2系统&#xff0c;docker docker安装 前提用root用户 安装docker yum install docker 重启docker systemctl restart docker beef安装 安装beef docker pull janes/beef 绑定到3000端口 docker run --rm -p 3000:3000 janes/beef …...

编程笔记 html5cssjs 086 JavaScript 内置对象

编程笔记 html5&css&js 086 JavaScript 内置对象 一、Object二、Array三、String四、Number五、Math六、Date七、RegExp八、Function九、示例小结 JavaScript 内置对象是 JavaScript 语言本身定义的一系列预定义的对象&#xff0c;这些对象在全局作用域中可以直接使用&…...

AttributeError: ‘DataFrame‘ object has no attribute ‘set_value‘怎么修改问题的解决

在jupyternotebook中运行&#xff1a; def remplacement_df_keywords(df, dico_remplacement, roots False):df_new df.copy(deep True)for index, row in df_new.iterrows():chaine row[plot_keywords]if pd.isnull(chaine): continuenouvelle_liste []for s in chaine.…...

Jmeter内置变量 vars 和props的使用详解

JMeter是一个功能强大的负载测试工具&#xff0c;它提供了许多有用的内置变量来支持测试过程。其中最常用的变量是 vars 和 props。 vars 变量 vars 变量是线程本地变量&#xff0c;它们只能在同一线程组内的所有线程中使用&#xff08;线程组内不同线程之间变量不共享&#…...

c#高级-正则表达式

正则表达式是由普通字符和元字符&#xff08;特殊符号&#xff09;组成的文字形式 应用场景 1.用于验证输入的邮箱是否合法。 2.用于验证输入的电话号码是否合法。 3.用于验证输入的身份证号码是否合法。等等 正则表达式常用的限定符总结&#xff1a; 几种常用的正则简写表达式…...

说说UE5中的几种字符串类

在Unreal Engine 5 (UE5) 的C中&#xff0c;与字符串相关的类主要包括&#xff1a; FString&#xff1a; Unreal Engine中用于处理字符串的主要类&#xff0c;提供了丰富的字符串操作方法和功能。 FText&#xff1a; 用于表示本地化文本的类&#xff0c;可以包含多种语言的文本…...

(done) 如何判断一个矩阵是否可逆?

参考视频&#xff1a;https://www.bilibili.com/video/BV15H4y1y737/?spm_id_from333.337.search-card.all.click&vd_source7a1a0bc74158c6993c7355c5490fc600 这个视频里还暗含了一些引理 1.若 AX XB 且 X 和 A,B 同阶可逆&#xff0c;那么 A 和 B 相似。原因&#xff1…...

洗眼镜用的超声波清洗机哪一家更好一点?好用超声波清洗机排名

在我们日常生活中&#xff0c;眼镜、首饰、手表等细小物件的清洁一直是一个让人头疼的问题。传统的清洁方法不仅耗时耗力&#xff0c;还可能因为不当的操作而损伤到这些精细的物品。那么&#xff0c;有没有一种既快捷又安全的清洁方式呢&#xff1f;答案就是使用超声波清洗机。…...

(二十二)Flask之上下文管理第三篇【收尾—讲一讲g】

目录: 每篇前言:g到底是什么?生命周期在请求周期内保持数据需要注意的是:拓展—面向对象的私有字段深入讲解一下那句:每篇前言: 🏆🏆作者介绍:【孤寒者】—CSDN全栈领域优质创作者、HDZ核心组成员、华为云享专家Python全栈领域博主、CSDN原力计划作者🔥🔥本文已…...

五种多目标优化算法(MOGWO、MOJS、NSWOA、MOPSO、MOAHA)性能对比,包含6种评价指标,9个测试函数(提供MATLAB代码)

一、5种多目标优化算法简介 1.1MOGWO 1.2MOJS 1.3NSWOA 1.4MOPSO 1.5MOAHA 二、5种多目标优化算法性能对比 为了测试5种算法的性能将其求解9个多目标测试函数&#xff08;zdt1、zdt2 、zdt3、 zdt4、 zdt6 、Schaffer、 Kursawe 、Viennet2、 Viennet3&#xff09;&#xff0…...

istio实战:springboot项目在istio中服务调用

目录 一、前言二、准备工作三、问题排查四、总结参考资料 一、前言 在经过前面几天k8s和Istio的安装之后&#xff0c;开始进入最核心的阶段。微服务在抛弃传统的服务注册和服务发现之后&#xff0c;是怎么在istio怎么做服务间的调用的呢&#xff1f;本次实战花费了我2-3天的时…...

随机分布模型

目录 前言 一、离散型随机变量 1.1 0-1分布 1.2 二项分布 1.3 帕斯卡分布 1.4 几何分布 1.5 超几何分布 1.6 泊松分布 二、连续型随机变量 2.1 均匀分布 2.2 指数分布 2.3 高斯分布/正态分布 2.4 分布&#xff08;抽样分布&#xff09; 2.5 t分布&#xff08;抽样…...

Visual Studio:Entity设置表之间的关联关系

1、选择表并右键-》新增-》关联 2、设置关联的表及关联关系并“确定”即可...

每日五道java面试题之spring篇(二)

目录&#xff1a; 第一题 Spring事务传播机制第二题 Spring事务什么时候会失效?第三题 什么是bean的⾃动装配&#xff0c;有哪些⽅式&#xff1f;第四题 Spring中的Bean创建的⽣命周期有哪些步骤&#xff1f;第五题 Spring中Bean是线程安全的吗&#xff1f; 第一题 Spring事务…...

287.【华为OD机试真题】字符串序列判定(贪心算法—JavaPythonC++JS实现)

🚀点击这里可直接跳转到本专栏,可查阅顶置最新的华为OD机试宝典~ 本专栏所有题目均包含优质解题思路,高质量解题代码(Java&Python&C++&JS分别实现),详细代码讲解,助你深入学习,深度掌握! 文章目录 一. 题目-字符串序列判定二.解题思路三.题解代码Python题…...

Sentinel微服务流量治理组件实战上

目录 分布式系统遇到的问题 解决方案 Sentinel 是什么&#xff1f; Sentinel 工作原理 Sentinel 功能和设计理念 流量控制 熔断降级 Sentinel工作主流程 Sentinel快速开始 Sentinel资源保护的方式 基于API实现 SentinelResource注解实现 Spring Cloud Alibaba整合…...

Three.js加载PLY文件

这是官方的例子 three.js webgl - PLY 我在Vue3中使用&#xff0c;测试了好久始终不显示点云数据。在网上查询后发现ply文件要放置在public目录下才行 <el-row><el-button type"primary" class"el-btn" click"IniThree1">PLY</…...

chrome闪退后打不开问题 打开立即闪退

今天刚遇到&#xff0c;大概率是某些网站引起的闪退&#xff0c;重启和清理也不能解决问题 网上到处都是答非所问&#xff0c;还有什么打开chrome再进行设置这种回答 在此解决下 注意该方法根据我测试&#xff0c;唯一会损失的是chrome扩展&#xff0c;可以提前去 C:\Users…...

Slave被误写入数据如何恢复到主库

背景 在GreatSQL主从复制环境中&#xff0c;有时候可能会出现一些误操作&#xff0c;将本应该写入到主库的数据写入到了从库&#xff0c;导致主从数据不一致&#xff0c;影响数据同步。是否可以将写入从库的数据同步写入主库呢&#xff1f; 测试环境 角色IP地址数据库开放端…...

kafka生产者

1.原理 2.普通异步发送 引入pom&#xff1a; <dependencies><dependency><groupId>org.apache.kafka</groupId><artifactId>kafka-clients</artifactId><version>3.0.0</version></dependency><dependency><g…...

前后端分离Vue+node.js在线学习考试系统gqw7o

与其它应用程序相比&#xff0c;在线学习平台的设计主要面向于学校&#xff0c;旨在为管理员和学生、教师、院系提供一个在线学习平台。学生、教师、院系可以通过系统及时查看公告信息等。 在线学习平台是在Windows操作系统下的应用平台。为防止出现兼容性及稳定性问题&#xf…...

关于el-select值的回显问题 : 框内显示label值还是value值

<el-form-item label"状态" prop""><el-selectv-model"roleForm.state"class"m-2"size"large"style"width: 240px"placeholder"请选择状态"value-key"value"//value-key 与下面的ke…...

MCU多核异构通信原理

摘要&#xff1a; 本文结合瑞萨RZ/G2L 多核处理器&#xff0c;给大家讲述一下多核异构设计及通信的原理。 随着电子技术的不断发展&#xff0c;以及市场需求的日益增长&#xff0c;嵌入式系统不仅要求执行复杂的控制任务&#xff0c;还需要实时地采集和处理数据。 为了满足这…...

在autodl搭建stable-diffusion-webui+sadTalker

本文介绍在autodl.com搭建gpu服务器&#xff0c;实现stable-diffusion-webuisadTalker功能&#xff0c;图片音频 可生成视频。 autodl租GPU 自己本地部署SD环境会遇到各种问题&#xff0c;网络问题&#xff08;比如huggingface是无法访问&#xff09;&#xff0c;所以最好的方…...

【办公类-16-10-01】“2023下学期 中4班 自主游戏观察记录(python 排班表系列)

背景需求 上学期的周安排里&#xff0c;每班每周的自主游戏会轮到多个不同的内容 因此在每周的自主游戏观察有2次记录&#xff0c;观察的项目可以写不一样的&#xff0c; 如一位老师写沙水游戏&#xff0c;另一位写表演游戏 本学期&#xff0c;中班的自主游戏全部是户外的&am…...

机器学习:SVM算法(Python)

一、核函数 kernel_func.py import numpy as npdef linear():"""线性核函数:return:"""def _linear(x_i, x_j):return np.dot(x_i, x_j)return _lineardef poly(degree3, coef01.0):"""多项式核函数:param degree: 阶次:param …...