PID控制器轨迹跟随 实现

参考博客:
【自动驾驶】PID实现轨迹跟踪 | python实现 | C++实现
【C++ matplotlib 画图 Linux】
【无人车系统(一):运动学模型及其线性化】

1 运动学模型及其线性化

无人车运动学模型
在这里插入图片描述 v v v:无人车的速度
x ˙ \dot x x˙:无人车在世界坐标系中X轴方向上的分速度
y ˙ \dot y y˙:无人车在世界坐标系中Y轴方向上的分速度
θ \theta θ:无人车在世界坐标中的航向角
θ ˙ \dot \theta θ˙:无人车的角速度

两个主要控制对象: v v v w w w

在这里插入图片描述运动学模型线性化:

在这里插入图片描述测量具有离散的本质,现有的物理系统只能被离散的观测与控制。时间维度上的状态都被离散化为一个个状态序列 x 0 , x 1 , . . . , x t , . . . x_0,x_1,...,x_t,... x0,x1,...,xt,...。物理系统本质上是连续的,那些状态更新间隔,因为仅仅能够离散的控制、影响这些系统。
模型的离散化是对物理系统在时间维度的近似。离散化后的差分模型与系统真实的连续模型是有误差的。

有误差,还要将就采用离散的差分形式?

不能立马是指控制指令并不能立马从当前值变成期望的控制输入值,就算用连续模型加上阶梯形式控制输入函数计算状态的响应输出也是不准确的。与其不准,不如用形式相对简洁的差分模型。另外,并不是所有的系统模型都是可导的,这使得绝大多数情况是被迫采用差分模型。
差不多就可以 由于控制是按一定的周期观测系统内部真实的状态,也即系统的状态初值是不断更新。如果我们的更新周期不是太长,在短时间内,差分函数的状态预测误差是在可接受的范围。

在这里插入图片描述无人车运动学模型在低速时可以较好的预测(估计)系统未来的状态,但是随着车速的提高,汽车侧向动力学特性的影响越来越明显。因此,在高速运行时,一个更加准确的无人车模型必须加入汽车的动力学特性

2 运动模型参考代码

参考了【自动驾驶】PID实现轨迹跟踪 | python实现 | C++实现
KinematicModel.h

#pragma once
#include <iostream>
#include <vector>
#include <cmath>
#include <Eigen/Dense>
using namespace std;
using namespace Eigen;class KinematicModel {
public:KinematicModel();KinematicModel(double x, double y, double psi, double v, double L, double dt);vector<double> getState();void updateState(double a, double delta);vector<MatrixXd> stateSpace(double ref_delta, double ref_yaw);
public:double x, y, v, L, dt, psi;
};

KinematicModel.cpp

#include "KinematicModel.h"KinematicModel::KinematicModel(double x, double y, double psi, double v, double L, double dt) : x(x), y(y), psi(psi),v(v), L(L), dt(dt) {}
void KinematicModel::updateState(double a, double delta)
{x = x + v * cos(psi) * dt;y = y + v * sin(psi) * dt;psi = psi + v / L * tan(delta) * dt;v = v + a * dt;
}vector<double> KinematicModel::getState()
{return {x, y, psi, v};
}
// 将模型离散化后的状态空间表达
vector<MatrixXd> KinematicModel::stateSpace(double ref_delta, double ref_yaw)
{MatrixXd A(3, 3), B(3, 2);A << 1.0, 0.0, -v * sin(ref_yaw) * dt,0.0, 1.0, v * cos(ref_yaw) * dt,0.0, 0.0, 1.0;B << cos(ref_yaw) * dt, 0,sin(ref_yaw) * dt, 0,tan(ref_delta) * dt / L, v * dt / (L * cos(ref_delta) * cos(ref_delta));return {A, B};
}

3 PID控制器

任何闭环控制系统的首要任务是要稳、准、快的响应命令。PID的主要工作就是如何实现这一任务。
PID控制器的比例单元 ( P) 、积分单元(I)和微分单元(D)分别对应目前误差、过去累计误差及未来误差。若是不知道受控系统的特性,一般认为PID控制器是最适用的控制器。

P:增大比例加快系统的响应,它的作用于输出值较快,但不能很好稳定在一个理想的数值。Kp过大,会产生超调,并产生振荡。
I:在P的基础上消除余差,对稳定后有累积误差的系统进行误差修整,减小稳态误差
D:可以使系统超调量减小,减小振荡,增加稳定性。

位置式PID:当前系统的实际位置,与你想要达到的预期位置的偏差,进行PID控制

在这里插入图片描述当采样时间足够小时,能够获得最够精确的结果,离散控制过程与连续过程非常接近。

位置式PID在积分项达到饱和时,误差仍然会在积分作用下继续累积,一旦误差开始反向变化,系统需要一定时间从饱和区退出,所以在u(k)达到最大和最小时,要停止积分作用,并且要有积分限幅和输出限幅。

抗积分饱和:如果上一次的输出控制量超过了饱和值,饱和值为正,则这一次只积分负的偏差,饱和值为负,则这一次只积分正的偏差,从而避免系统长期留在饱和区!

pid_controller.h

#pragma once
#include <iostream>using namespace std;class PID_controller {
private:double Kp, Ki, Kd, target, upper, lower;double error = 0.0, pre_error = 0.0, sum_error = 0.0;
public:PID_controller(double Kp, double Ki, double Kd, double target, double upper, double lower);void setTarget(double target);void setK(double Kp, double Ki, double Kd);void setBound(double upper, double lower);double calOutput(double state);void reset();void setSumError(double sum_error);
};

pid_controller.cpp

#include "pid_controller.h"PID_controller::PID_controller(double Kp, double Ki, double Kd, double target, double upper, double lower) : Kp(Kp),Ki(Ki),Kd(Kd),target(target),upper(upper),lower(lower) {}
void PID_controller::setTarget(double target) {PID_controller::target = target;
}void PID_controller::setK(double Kp, double Ki, double Kd) {this->Kp = Kp;this->Ki = Ki;this->Kd = Kd;
}void PID_controller::setBound(double upper, double lower) {this->upper = upper;this->lower = lower;
}double PID_controller::calOutput(double state) {this->error = this->target - state;double u = this->error * this->Kp + this->sum_error * this->Ki + (this->error - this->pre_error) * this->Kd;if (u < this->lower) u = this->lower;else if (u > this->upper) u = this->upper;this->pre_error = this->error;this->sum_error = this->sum_error + this->error;return u;
}void PID_controller::reset() {error = 0.0;pre_error = 0.0;sum_error = 0.0;
}void PID_controller::setSumError(double sum_error) {this->sum_error = sum_error;
}

4 车辆横向跟踪误差

横向跟踪误差(cross track error, 简称CTE)为车辆中心点 ( r x , r y ) (r_x,r_y) (rx,ry)到最近路径点 ( p x , p y ) (p_x,p_y) (px,py)的距离
在这里插入图片描述在这里插入图片描述 e y = l d s i n θ e e_y=l_dsin\theta_e ey=ldsinθe
main.cpp

#include "KinematicModel.h"
#include "pid_controller.h"
#include <algorithm>
#include "matplotlibcpp.h"
namespace plt = matplotlibcpp;#define PI 3.1415926
// 得到距离参考轨迹最近点的下标
// robot_state 机器人状态(x,y)
// ref_path 参考路径
// return 距离参考轨迹最近点的下标
double calTargetIndex(vector<double> robot_state, vector<vector<double>> ref_path) {vector<double> dists;for (vector<double> xy : ref_path) {double dist = sqrt(pow(xy[0] - robot_state[0], 2) + pow(xy[1] - robot_state[1], 2));dists.push_back(dist);}return min_element(dists.begin(), dists.end()) - dists.begin();
}int main()
{vector<vector<double>> ref_path(1000, vector<double>(2));vector<double> ref_x, ref_y;//保存参考数据用于画图// 生成参考轨迹for (int i = 0; i < 1000; i++) {ref_path[i][0] = 0.1 * i;ref_path[i][1] = 2 * sin(ref_path[i][0] / 3.0);ref_x.push_back(ref_path[i][0]);ref_y.push_back(ref_path[i][1]);}// 运动学模型KinematicModel model(0, -1, 0.5, 2, 2, 0.1);// PID控制器PID_controller PID(2, 0.01, 100, 0, PI / 6, -PI / 6);// 保存机器人(小车)运动过程中的轨迹vector<double> x_, y_;// 机器人状态vector<double> robot_state(2);for (int i = 0; i < 500; i++) {plt::clf();robot_state[0] = model.x;robot_state[1] = model.y;double min_ind = calTargetIndex(robot_state, ref_path);double alpha = atan2(ref_path[min_ind][1] - robot_state[1], ref_path[min_ind][0] - robot_state[0]);double ld = sqrt(pow(ref_path[min_ind][0]-robot_state[0],2)+pow(ref_path[min_ind][1]-robot_state[1],2));double theta_error = alpha - model.psi;double e_y = -ld * sin(theta_error);double delta = PID.calOutput(e_y);model.updateState(0, delta);x_.push_back(model.x);y_.push_back(model.y);plt::plot(ref_x, ref_y, "r--");plt::plot(x_, y_, "b");plt::grid(true);plt::ylim(-3.0, 3.0);plt::pause(0.01);}const char* filename = "./pid.png";plt::save(filename);plt::show();return 0;
}

详细步骤:

mkdir -p catkin_ws/src
cd catkin_ws/src
catkin_init_workspace
catkin_create_pkg PID_Controller roscpp std_msgs

在include文件夹下面放入 KinematicModel.h,matplotlibcpp.h,pid_controller.h
在src下放入KinematicModel.cpp,main.cpp,pid_controller.cpp

CMakeList.txt

cmake_minimum_required(VERSION 3.0.2)
project(PID_Controller)set(CMAKE_CXX_STANDARD 11)file(GLOB_RECURSE PYTHON2.7_LIB "/usr/lib/python2.7/config-x86_64-linux-gnu/*.so")
set(PYTHON2.7_INLCUDE_DIRS "/usr/include/python2.7")find_package(catkin REQUIRED COMPONENTSroscppstd_msgs
)catkin_package(
#  INCLUDE_DIRS include
#  LIBRARIES huatu
#  CATKIN_DEPENDS roscpp std_msgs
#  DEPENDS system_lib
)include_directories(include${PYTHON2.7_INLCUDE_DIRS}
)add_executable(pid_controller src/pid_controller.cppsrc/KinematicModel.cppsrc/main.cpp)
target_link_libraries(pid_controller ${PYTHON2.7_LIB})

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

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

相关文章

蓝牙BLE学习-安全

1.基本概念 蓝牙标准规定了5种基本的安全服务 身份验证:根据通信设备的蓝牙地址验证其身份。蓝牙不提供本地用户身份验证。保密性:确保只有授权的设备才能访问和查看传输的数据&#xff0c;防止窃听造成的信息泄露。授权(Authorization):在允许设备使用某项服务之前&#xff…

第三篇【传奇开心果微博系列】Python微项目技术点案例示例:爱的表达

传奇开心果微博系列 系列微博目录Python微项目技术点案例示例系列 微博目录一、微项目目标二、雏形示例代码三、扩展思路四、添加加载图片和小视频功能示例代码五、添加音效和背景音乐六、添加用户交互示例代码七、添加定时提醒功能示例代码八、自定义界面示例代码九、多语言支…

MySQL表的增删查改(基础)

新增&#xff08;Create) 1.全列插入 全列单行插入 insert into 表名 values(值&#xff0c;值……)&#xff1b; 也可以全列且多行插入 insert into 表名 values (值&#xff0c;值……)&#xff0c;(值&#xff0c;值……)……&#xff1b; 2.指定列插入 insert into 表…

五.实战软件部署 1-3实战章节-前言MYSQL 5.7版本在centos系统安装MYSQL 8.0版本在centos系统安装

目录 五.实战软件部署 1-实战章节-前言 五.实战软件部署 2-MYSQL 5.7版本在centos系统安装 1-配置yum仓库 2-使用yum安装mysql 3-安装完成后&#xff0c;启动mysql并配置开机自启动 4-检查mysql的运行状态 --配置 1-获取mysql的初识密码 2-登录mysql数据库系统 3-修改…

无人机飞行控制系统功能,多旋翼飞行控制系统概述

飞行控制系统存在的意义 行控制系统通过高效的控制算法内核&#xff0c;能够精准地感应并计算出飞行器的飞行姿态等数据&#xff0c;再通过主控制单元实现精准定位悬停和自主平稳飞行。 在没有飞行控制系统的情况下&#xff0c;有很多的专业飞手经过长期艰苦的练习&#xff0…

【c++基础】国王的魔镜

说明 国王有一个魔镜&#xff0c;可以把任何接触镜面的东西变成原来的两倍——只是&#xff0c;因为是镜子嘛&#xff0c;增加的那部分是反的。 比如一条项链&#xff0c;我们用AB来表示&#xff0c;不同的字母表示不同颜色的珍珠。如果把B端接触镜面的话&#xff0c;魔镜会把…

CPython:表达式的求值顺序(evaluation order)

相关阅读 Pythonhttps://blog.csdn.net/weixin_45791458/category_12403403.html?spm1001.2014.3001.5482 C中表达式的求值 C语言针对表达式的计算&#xff0c;设置了操作符的优先级和结合性这两个特性&#xff0c;优先级用于解析不同优先级的符号&#xff0c;结合性用于解析…

Linux环境下配置HTTP代理服务器教程

大家好&#xff0c;我是你们可爱的Linux小助手&#xff01;今天&#xff0c;我将带你们一起探索如何在Linux环境下配置一个HTTP代理服务器。请注意&#xff0c;这不是一次火箭科学的实验&#xff0c;而是一次简单而有趣的冒险。 首先&#xff0c;我们需要明确什么是HTTP代理服…

消息中间件面试篇

消息中间件 RabbitMQ 消息不丢失 可能导致消息丢失的情况&#xff1a; 生产者发送消息丢失消息队列宕机消费者服务宕机&#xff0c;未接收到消息 生产者确认机制 该机制解决了生产者发送消息有可能丢失的问题。 RabbitMQ提供了publisher confirm机制来避免消息发送到MQ过程…

【python量化交易】qteasy使用教程02 - 获取和管理金融数据

qteasy教程2 - 获取并管理金融数据 qteasy教程2 - 获取并管理金融数据开始前的准备工作获取基础数据以及价格数据下载交易日历和基础数据查看股票和指数的基础数据下载沪市股票数据从本地获取股价数据生成K线图 数据类型的查找回顾总结 qteasy教程2 - 获取并管理金融数据 qtea…

【见微知著】OpenCV中C++11 lambda方式急速像素遍历

学习《OpenCV应用开发&#xff1a;入门、进阶与工程化实践》一书 做真正的OpenCV开发者&#xff0c;从入门到入职&#xff0c;一步到位&#xff01; C11 lambda语法 C11中引入了lambda表达式&#xff0c;它支持定义一个内联(inline)的函数&#xff0c;作为一个本地的对象或者…

如何才能学好JVM?——零基础入门篇

1. JVM是什么&#xff1f; JVM是Java Virtual Machine的简称&#xff0c;它是一个虚拟的计算机&#xff0c;专门为执行Java程序而设计。 你可以想象它是一个能够运行Java字节码的平台&#xff0c;无论你的程序在Windows、Mac还是Linux上&#xff0c;它们都能通过JVM在这些系统…