PID横向控制和仿真实现

文章目录

    • 1. PID介绍
    • 2. PID横向控制原理
    • 3. 算法和仿真实现

1. PID介绍

PID是一种常见的控制算法,全称为Proportional-Integral-Derivative,即比例-积分-微分控制器。PID控制器是一种线性控制器,它将设定值与实际值进行比较,根据误差的大小,控制器会相应地调整系统的比例、积分和微分系数,以减小误差。

PID控制器的基本公式为:

u ( t ) = K p ∗ e ( t ) + K i ∗ ∫ 0 t e ( τ ) d τ + K d ∗ d d t e ( t ) (1) u(t) = K_p * e(t) + K_i * \int^{t}_{0}{e(\tau)} d\tau + K_d *\frac{d}{dt} e(t) \tag{1} u(t)=Kpe(t)+Ki0te(τ)dτ+Kddtde(t)(1)

其中,$u(t) 是控制器的输出, 是控制器的输出, 是控制器的输出,e(t)$ 是误差信号(设定值与实际值之差), K p K_p Kp K i K_i Ki K d K_d Kd是控制器的比例、积分和微分系数。

PID控制器在工程、科学和工业等领域中有着广泛的应用。例如,在汽车定速巡航系统、空调系统、工业自动化生产线等系统中都可以看到PID控制器的身影。此外,PID控制器还广泛应用于机器人控制、化工生产、航天器控制等领域。

将公式(1)转换为离散形式,则有

u ( n ) = K p ∗ e ( n ) + K i ∗ T ∗ ∑ i = 0 n e ( i ) + K d ∗ e ( n ) − e ( n − 1 ) T (2) u(n) = K_p * e(n) + K_i * T*\sum^{n}_{i=0}{e(i)} + K_d *\frac{e(n)-e(n-1)}{T} \tag{2} u(n)=Kpe(n)+KiTi=0ne(i)+KdTe(n)e(n1)(2)

其中 T T T为采样周期,由此公式可以得到PID代码的实现如下

#PID.pyclass PIDController:def __init__(self, Kp, Ki, Kd):self.Kp = Kpself.Ki = Kiself.Kd = Kdself.previous_error = 0self.integral = 0def cal_output(self, error, dt):derivative = error - self.previous_erroru = self.Kp * error + self.Ki * self.integral * dt + self.Kd * derivative / dtself.integral += errorself.previous_error = errorreturn u

2. PID横向控制原理

在自动驾驶横向控制中,主要通过控制方向盘的角度来控制车辆的横向距离误差,因此我们可以通过横向距离误差 e y e_y ey来作为PID的输入,输出可以作为方向盘转角 δ f \delta_f δf,结合之前我们的车辆运动学模型(这里我们假设方向盘转角与前轮转角比是1),横向误差计算的几何结构如下图所示:

在这里插入图片描述

图中

  • P P P:当前车辆的目标点车
  • l d l_d ld:车辆后轴中心点 A A A F F F的距离
  • θ \theta θ l d l_d ld与车轴的夹角
  • φ \varphi φ:车辆的航向角
  • e y e_y ey:横向偏差

A A A P P P的坐标可以计算得

β = a r c t a n y 1 − y 0 x 1 − x 0 (3) \beta = arctan\frac{y_1-y_0}{x_1-x_0} \tag{3} β=arctanx1x0y1y0(3)

由图中的几何关系,我们可以得到

e y = l d s i n θ = l d s i n ( β − φ ) (4) e_y = l_d sin\theta =l_d sin(\beta - \varphi)\tag{4} ey=ldsinθ=ldsin(βφ)(4)

其中 φ \varphi φ为车辆的航向角yaw,其实现方法详见bicycle_model.py

3. 算法和仿真实现

bicycle_model.py

#!/usr/bin/python
# -*- coding: UTF-8 -*-import math
import matplotlib.pyplot as plt
import numpy as np
from celluloid import Cameraclass 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')if __name__ == "__main__":vehicle = Vehicle(x=0.0,y=0.0,yaw=0,v=0.0,dt=0.1,l=VehicleInfo.L)vehicle.v = 1trajectory_x = []trajectory_y = []fig = plt.figure()# 保存动图用# camera = Camera(fig)for i in range(600):plt.cla()plt.gca().set_aspect('equal', adjustable='box')vehicle.update(0, np.pi / 10)draw_trailer(vehicle.x, vehicle.y, vehicle.yaw, vehicle.steer, plt)trajectory_x.append(vehicle.x)trajectory_y.append(vehicle.y)plt.plot(trajectory_x, trajectory_y, 'blue')plt.xlim(-12, 12)plt.ylim(-2.5, 21)plt.pause(0.001)#     camera.snap()# animation = camera.animate(interval=5)# animation.save('trajectory.gif')

main.py

from scipy.spatial import KDTree
from bicycle_model import Vehicle, VehicleInfo, draw_trailer
from PID import PIDController
import numpy as np
import matplotlib.pyplot as plt
import math
import imageioMAX_SIMULATION_TIME = 200.0  # 程序最大运行时间200*dt
PID = PIDController(2, 0.001, 3)def NormalizeAngle(angle):a = math.fmod(angle + np.pi, 2 * np.pi)if a < 0.0:a += (2.0 * np.pi)return a - np.pidef main():# 设置跟踪轨迹ref_path = np.zeros((1000, 2))ref_path[:, 0] = np.linspace(0, 50, 1000)  # x坐标ref_path[:, 1] = 10 * np.sin(ref_path[:, 0] / 20.0)  # y坐标ref_tree = KDTree(ref_path)# 假设车辆初始位置为(0,0),航向角yaw=0.0,速度为2m/s,时间周期dt为0.1秒vehicle = Vehicle(x=0.0,y=0.0,yaw=np.pi/2,v=2.0,dt=0.1,l=VehicleInfo.L)time = 0.0  # 初始时间# 记录车辆轨迹trajectory_x = []trajectory_y = []lat_err = []  # 记录横向误差i = 0image_list = []  # 存储图片plt.figure(1)last_idx = ref_path.shape[0] - 1  # 跟踪轨迹的最后一个点的索引old_idx = 0  # 记录上一次的索引点target_ind = 0  # 第一个目标点的索引while MAX_SIMULATION_TIME >= time and last_idx > target_ind:time += vehicle.dt  # 累加一次时间周期vehicle_positon = np.zeros(2)vehicle_positon[0] = vehicle.xvehicle_positon[1] = vehicle.ydistance, target_ind = ref_tree.query(vehicle_positon)  # 在跟踪轨迹上查找离车辆最近的点if old_idx > target_ind:print("ERROR: Find the point behind the vehicle.")target_ind = old_idx + 1  # 查找到车辆后面的点,将目标点索引置为上一次的索引点idx+1old_idx = target_ind  # 记录本次索引点idxalpha = math.atan2(ref_path[target_ind, 1] - vehicle_positon[1], ref_path[target_ind, 0] - vehicle_positon[0])l_d = np.linalg.norm(ref_path[target_ind] - vehicle_positon)  # 目标点与车定位点距离l_dtheta_e = NormalizeAngle(alpha - vehicle.yaw)e_y = l_d * math.sin(theta_e)  # 计算实际误差,0为目标距离lat_err.append(e_y)delta_f = PID.cal_output(e_y, vehicle.dt)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 += 1if (i % 50) > 0:image_list.append(imageio.imread("temp.png"))imageio.mimsave("trailer_display.gif", image_list, duration=0.1)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如果喜欢话,可以关注一下

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/623441.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Python轴承故障诊断 (11)基于VMD+CNN-BiGRU-Attenion的故障分类

目录 往期精彩内容&#xff1a; 前言 模型整体结构 1 变分模态分解VMD的Python示例 2 轴承故障数据的预处理 2.1 导入数据 2.2 故障VMD分解可视化 2.3 故障数据的VMD分解预处理 3 基于VMD-CNN-BiGRU-Attenion的轴承故障诊断分类 3.1 定义VMD-CNN-BiGRU-Attenion分类网…

【C#】当重复使用一段代码倒计时时,使用静态类和静态方法,实现简单的this扩展方法

欢迎来到《小5讲堂》 大家好&#xff0c;我是全栈小5。 这是《C#》序列文章&#xff0c;每篇文章将以博主理解的角度展开讲解&#xff0c; 特别是针对知识点的概念进行叙说&#xff0c;大部分文章将会对这些概念进行实际例子验证&#xff0c;以此达到加深对知识点的理解和掌握。…

多特征变量序列预测(三)——CNN-Transformer风速预测模型

目录 往期精彩内容&#xff1a; 前言 1 多特征变量数据集制作与预处理 1.1 导入数据 1.2 数据集制作与预处理 2 基于Pytorch的CNN-Transformer 预测模型 2.1 定义CNN-Transformer预测模型 2.2 设置参数&#xff0c;训练模型 3 模型评估与可视化 3.1 结果可视化 3.2 …

高效构建Java应用:Maven入门和进阶(五)

高效构建Java应用&#xff1a;Maven入门和进阶&#xff08;五&#xff09; Maven实战案例&#xff1a;搭建微服务Maven工程框架5.1 项目需求和结构分析5.2项目搭建和统一构建 总结 Maven实战案例&#xff1a;搭建微服务Maven工程框架 5.1 项目需求和结构分析 需求案例&#xf…

SSH镜像、systemctl镜像、nginx镜像、tomcat镜像

目录 一、SSH镜像 二、systemctl镜像 三、nginx镜像 四、tomcat镜像 五、mysql镜像 一、SSH镜像 1、开启ip转发功能 vim /etc/sysctl.conf net.ipv4.ip_forward 1sysctl -psystemctl restart docker 2、 cd /opt/sshd/vim Dockerfile 3、生成镜像 4、启动容器并修改ro…

【编码魔法师系列_构建型4】原型模式(Prototype Pattern)

学会设计模式&#xff0c;你就可以像拥有魔法一样&#xff0c;在开发过程中解决一些复杂的问题。设计模式是由经验丰富的开发者们&#xff08;GoF&#xff09;凝聚出来的最佳实践&#xff0c;可以提高代码的可读性、可维护性和可重用性&#xff0c;从而让我们的开发效率更高。通…

10.抽象工厂模式

江湖上再也没人找林家的麻烦了。因为林平之一怒之下将辟邪剑谱公诸天下。一下子印出去几万份&#xff0c;江湖上人人都能轻而易举的得到这本无尚武学&#xff1b;然而江湖人士却陷入深深的矛盾之中&#xff1a; 不练&#xff0c;别人练了&#xff0c;分分钟秒杀你&#xff1b;练…

SV-7041T 30W网络有源音箱校园教室广播音箱,商场广播音箱,会议广播音箱,酒店广播音箱,工厂办公室广播音箱

SV-7041T 30W网络有源音箱 校园教室广播音箱&#xff0c;商场广播音箱&#xff0c;会议广播音箱&#xff0c;酒店广播音箱&#xff0c;工厂办公室广播音箱 SV-7041T是深圳锐科达电子有限公司的一款2.0声道壁挂式网络有源音箱&#xff0c;具有10/100M以太网接口&#xff0c;可将…

我为什么要写RocketMQ消息中间件实战派上下册这本书?

我与RocketMQ结识于2018年&#xff0c;那个时候RocketMQ还不是Apache的顶级项目&#xff0c;并且我还在自己的公司做过RocketMQ的技术分享&#xff0c;并且它的布道和推广&#xff0c;还是在之前的首席架构师的带领下去做的&#xff0c;并且之前有一个技术神经质的人&#xff0…

爬虫入门学习(二)——response对象

大家好&#xff01;我是码银&#xff0c;代码的码&#xff0c;银子的银&#x1f970; 欢迎关注&#x1f970;&#xff1a; CSDN&#xff1a;码银 公众号&#xff1a;码银学编程 前言 在本篇文章&#xff0c;我们继续讨论request模块。从上一节&#xff08;爬虫学习(1)--reque…

【C++】异常机制

异常 一、传统的处理错误的方式二、C异常概念三、异常的使用1. 异常的抛出和捕获&#xff08;1&#xff09;异常的抛出和匹配原则&#xff08;2&#xff09;在函数调用链中异常栈展开匹配原则 2. 异常的重新抛出3. 异常安全4. 异常规范 四、自定义异常体系五、C 标准库的异常体…

leetcode第365题:水壶问题

有两个水壶&#xff0c;容量分别为 jug1Capacity 和 jug2Capacity 升。水的供应是无限的。确定是否有可能使用这两个壶准确得到 targetCapacity 升。 如果可以得到 targetCapacity 升水&#xff0c;最后请用以上水壶中的一或两个来盛放取得的 targetCapacity 升水。 你可以&a…

【VTKExamples::PolyData】第一期 凸包计算

很高兴在雪易的CSDN遇见你 VTK技术爱好者 QQ:870202403 前言 本文分享VTKExamples中的凸包计算样例,希望对各位小伙伴有所帮助! 感谢各位小伙伴的点赞+关注,小易会继续努力分享,一起进步! 你的点赞就是我的动力(^U^)ノ~YO ​​​​​​​ 目录 前言 1. 凸包…

GZ075 云计算应用赛题第7套

2023年全国职业院校技能大赛&#xff08;高职组&#xff09; “云计算应用”赛项赛卷7 某企业根据自身业务需求&#xff0c;实施数字化转型&#xff0c;规划和建设数字化平台&#xff0c;平台聚焦“DevOps开发运维一体化”和“数据驱动产品开发”&#xff0c;拟采用开源OpenSt…

geemap学习笔记047:边缘检测

前言 边缘检测适用于众多的图像处理任务&#xff0c;除了上一节[[geemap046&#xff1a;线性卷积–低通滤波器和拉普拉斯算子|线性卷积]]中描述的边缘检测核之外&#xff0c;Earth Engine 中还有几种专门的边缘检测算法。其中Canny 边缘检测算法使用四个独立的滤波器来识别对角…

嵌入式学习-网络编程-Day1

Day1 思维导图 作业 实现一下套接字通信 代码 #include<myhead.h>int main(int argc, const char *argv[]) {//1、创建套接字int sfd socket(AF_INET, SOCK_STREAM, 0);//参数1&#xff1a;通信域&#xff1a;使用的是ipv4通信//参数2&#xff1a;表示使用tcp通信//参…

算法通关村第十四关—数据流的中位数(黄金)

数据流中中位数的问题 LeetCode295,中位数是有序列表中间的数。如果列表长度是偶数&#xff0c;中位数则是中间两个数的平均值。 例如&#xff1a;[2,3,4]的中位数是3 [2,3]的中位数是(23)/22.5 实现 MedianFinder 类: MedianFinder() 初始化 MedianFinder 对象。void addNum(…

从零学Java 多线程(基础)

Java 多线程(基础) 文章目录 Java 多线程(基础)1 多线程1.1 多任务1.2 多线程1.3 普通方法调用和多线程 2 进程和线程2.1 什么是进程(Process)?2.2 什么是线程(Thread)?2.3 进程和线程的区别 3 线程的实现3.1 线程的组成3.2 线程执行特点3.3 线程的创建3.3.1 继承Thread类3.3…

[杂项]如何快速制作CSDN封面

这里写目录标题 一、封面尺寸二、封面制作工具 一、封面尺寸 CSDN的封面尺寸为尺寸 240 135 比例16&#xff1a;9。 二、封面制作工具 制作工具 进入工具后设置好宽度高度背景色&#xff0c;调节好自定义内容后点击最下方的下载按钮&#xff0c;将文件下载到本地后修改文件后…

CAN总线记录仪在车企服务站的应用

CAN总线记录仪在车企服务站的应用 CAN总线记录仪在车企服务站中有着广泛的应用。这种设备可以记录车上的CAN总线数据&#xff0c;方便工程师进行分析&#xff0c;以找出可能存在的问题。CAN记录仪一般采用TF卡来存储数据&#xff0c;实现离线脱机实时存储。数据存储完毕后&…