基于动力学的MPC控制器设计盲点解析
文章目录
-
- Apollo MPC控制器的设计架构
- 误差模型和离散化
- 预测模型推导
- 目标函数和约束设计
- 优化求解
- 优化OSQP求解器
- 参考文献
Apollo MPC控制器的设计架构
误差模型和离散化
状态变量和控制变量
1、Apollo MPC控制器中状态变量主要有如下6个
matrix_state_ = Matrix::Zero(basic_state_size_, 1);// State matrix update;matrix_state_(0, 0) = debug->lateral_error();matrix_state_(1, 0) = debug->lateral_error_rate();matrix_state_(2, 0) = debug->heading_error();matrix_state_(3, 0) = debug->heading_error_rate();matrix_state_(4, 0) = debug->station_error();matrix_state_(5, 0) = debug->speed_error();
状态变量计算:
控制变量有两个:
其中
为前轮转角,
为加速度补偿
Eigen::MatrixXd control_matrix(controls_, 1);control_matrix << 0, 0;
在代码中控制量的计算和输出:
// 解mpc,输出一个vector,control内有10个control_matrix SolveLinearMPC(matrix_ad_, matrix_bd_, matrix_cd_, matrix_q_updated_,matrix_r_updated_, lower_bound, upper_bound, matrix_state_, reference,mpc_eps_, mpc_max_iteration_, &control) // 已知,mpc仅取第一组解为当前时刻最优控制解,即control[0] // steer_single_direction_max_degree_ --- the maximum turn of steer // control中第一个矩阵中的第一个值用于计算方向盘转角 double steer_angle_feedback = control[0](0, 0) * 180 / M_PI *steer_transmission_ratio_ /steer_single_direction_max_degree_ * 100;double steer_angle = steer_angle_feedback + steer_angle_feedforwardterm_updated_ +steer_angle_ff_compensation + steer_angle_feedback_augment;// control中第一个矩阵中的第二个值control[0](1, 0),用于计算加速度 = 加速度补偿 + 期望加速度 + 坡度加速度补偿 debug->set_acceleration_cmd_closeloop(control[0](1, 0)); double acceleration_cmd = control[0](1, 0) + debug->acceleration_reference() + control_conf_.enable_slope_offset() * debug->slope_offset_compensation();
动力学误差模型
代码实现:
// 初始化矩阵 Status MPCController::Init(const ControlConf *control_conf) {...// Matrix init operations.matrix_a_ = Matrix::Zero(basic_state_size_, basic_state_size_);matrix_ad_ = Matrix::Zero(basic_state_size_, basic_state_size_);...// TODO(QiL): expand the model to accomendate more combined states.matrix_a_coeff_ = Matrix::Zero(basic_state_size_, basic_state_size_);...matrix_b_ = Matrix::Zero(basic_state_size_, controls_);matrix_bd_ = Matrix::Zero(basic_state_size_, controls_);...matrix_bd_ = matrix_b_ * ts_;matrix_c_ = Matrix::Zero(basic_state_size_, 1);...matrix_cd_ = Matrix::Zero(basic_state_size_, 1);...}// 更新矩阵 void MPCController::UpdateMatrix(SimpleMPCDebug *debug) {const double v = VehicleStateProvider::instance()->linear_velocity();matrix_a_(1, 1) = matrix_a_coeff_(1, 1) / v;matrix_a_(1, 3) = matrix_a_coeff_(1, 3) / v;matrix_a_(3, 1) = matrix_a_coeff_(3, 1) / v;matrix_a_(3, 3) = matrix_a_coeff_(3, 3) / v;Matrix matrix_i = Matrix::Identity(matrix_a_.cols(), matrix_a_.cols());matrix_ad_ = (matrix_i + ts_ * 0.5 * matrix_a_) *(matrix_i - ts_ * 0.5 * matrix_a_).inverse();matrix_c_(1, 0) = (lr_ * cr_ - lf_ * cf_) / mass_ / v - v;matrix_c_(3, 0) = -(lf_ * lf_ * cf_ + lr_ * lr_ * cr_) / iz_ / v;matrix_cd_ = matrix_c_ * debug->heading_error_rate() * ts_; }// 求解MPC // discrete linear predictive control solver, with control format // x(i + 1) = A * x(i) + B * u (i) + C bool SolveLinearMPC(const Matrix &matrix_a, const Matrix &matrix_b,const Matrix &matrix_c, const Matrix &matrix_q,const Matrix &matrix_r, const Matrix &matrix_lower,const Matrix &matrix_upper,const Matrix &matrix_initial_state,const std::vector<Matrix> &reference, const double eps,const int max_iter, std::vector<Matrix> *control)
apollo使用的是双线性变换离散法的方法:
matrix_ad_ = (matrix_i + ts_ * 0.5 * matrix_a_) *(matrix_i - ts_ * 0.5 * matrix_a_).inverse();matrix_bd_ = matrix_b_ * ts_;matrix_cd_ = matrix_c_ * debug->heading_error_rate() * ts_;
补充:
Apollo采用的中点欧拉法和向前欧拉法的结合:
预测模型推导
为什么不用上述推导出的离散化的状态空间方程来推导我们的预测模型,以此来设计MPC控制器?
主要考虑在设计目标函数的时候,想将我们的控制增量设计到我们的目标函数中去,而非直接使用控制量,以控制增量为代价,能有效改善控制的平滑性。
此处我们需要注意的是:
- 控制步长为Nc,因此当预测步长为k+Nc+1时,此时预测状态y(k+Nc+1)的最后一项也只写到k+Nc-1。因为到k+Nc时刻及以后的控制量有两种处理策略:第一种就是控制步长Nc之后的预测控制量保持不变;第二种就是Nc之后的预测控制量全部置零。我们此处达到控制步长之后的控制量全置零;
- 最终推导出的系统的预测模型Y, 其可以根据系统当前的状态量,以及施加一个未来一段时间Nc序列的控制增量,我们就可以知道系统未来(Np步)的表现是什么样子的(系统输出),即y(k+1)~y(k+Np);
目标函数和约束设计
设计目标:
- 希望我们的路径跟踪误差在预测时域内尽可能趋于0,也即希望目标函数的第一项的几个状态量尽可能趋于0,这样我们的车辆才能更好的跟随规划路径;
- 其次,我们希望目标函数的第二项控制增量的代价越小越好,也即希望前后两帧的控制量变化越小越好,这样控制的效果越平滑,对应前轮转角和加速度变化越平滑;
- 目标函数最后一项引入松弛因子(在程序中是一个具体的数值),其主要作用就是改善优化问题的可行解,提高求解效率;
在MPC控制规律中,将控制序列中的第一个参数作为控制量,输入给被控系统。
对于车辆控制来说,就是找到一组合适的控制量,如
,
其中
为前轮转角,
为刹车/油门系数,使车辆沿参考轨迹行驶,且误差最小、控制最平稳、舒适度最高等。因此在设计目标函数的时候可以考虑以下几点:
1、设计代价函数时候,一般设计为二次型的样式,为的是避免在预测时域内,误差忽正忽负,导致误差相互抵消;
2、考虑的代价主要有如下:
- 横向误差:指实际轨迹点与参考轨迹点间的距离;
- 航向误差:车辆当前航向与参考轨迹点航向的偏差;
- 速度误差:车辆当前速度与规划轨期望车速的偏差;
- 刹车/油门调节量:目的是为了保证刹车/油门变化的平稳性;
3、约束条件:
- 最大前轮转角
- 最大前轮转角变化量
- 最大刹车/油门调节量
- 最大方向盘转角
- 最大车速
- 最大加速度等
此处关于控制增量的约束,比如说,我们的前轮转角打30°需要2s,则1s需要打15°,然而我们的控制周期T=0.05s,则控制周期我们需要下发的转角最大为0.75°,也即我们下发转角的最大速率不超过0.75°/T。
关于软约束,也即代价函数,使其值越小越好。
优化求解
滚动优化求解的目的是为了求最优控制解,是一种在线优化,它每一时刻都会针对当前误差重新计算控制量,通过使某一或某些性能指标达到最优来实现控制作用。因此,滚动优化可能不会得到全局最优解,但是却能对每一时刻的状态进行最及时的响应,达到局部最优。
第一步:目标函数如何转化为标准二次型
注意:
目标函数化简后的每一项其实都是一个具体的数,第三项表示具体数的转置等于其自身。因此合并第二项和第三项,二者实质相等。其次化成二次型后,G属于已知项,且Q也是已知项,故二次型的最后一项其实不影响最终的优化求解最小值问题,因此放在最后一项,且求解过程可以忽略。
第二步:约束的转化
将
作为此时的控制量输入给系统,直到下一个控制时刻,系统根据新的状态信息预测下一时段内的输出,然后通过优化得到一组新的控制序列。如此反复,直至完成整个控制过程。
OSQP求解器
带参构造函数MpcOsqp 求解
输入矩阵Ad,Bd,Q,R,初始状态阵X0,控制量上下边界,状态量上下边界,参考状态(0矩阵),最大迭代次数mpc_max_iteration_,预测时域周期数horizon_,求解精度mpc_eps_,用输入参数去初始化类对象的数据成员
MpcOsqp::MpcOsqp(const Eigen::MatrixXd &matrix_a,const Eigen::MatrixXd &matrix_b,const Eigen::MatrixXd &matrix_q,const Eigen::MatrixXd &matrix_r,const Eigen::MatrixXd &matrix_initial_x,const Eigen::MatrixXd &matrix_u_lower,const Eigen::MatrixXd &matrix_u_upper,const Eigen::MatrixXd &matrix_x_lower,const Eigen::MatrixXd &matrix_x_upper,const Eigen::MatrixXd &matrix_x_ref, const int max_iter,const int horizon, const double eps_abs): matrix_a_(matrix_a),matrix_b_(matrix_b),matrix_q_(matrix_q),matrix_r_(matrix_r),matrix_initial_x_(matrix_initial_x),matrix_u_lower_(matrix_u_lower),matrix_u_upper_(matrix_u_upper),matrix_x_lower_(matrix_x_lower),matrix_x_upper_(matrix_x_upper),matrix_x_ref_(matrix_x_ref),max_iteration_(max_iter),horizon_(horizon),eps_abs_(eps_abs) {state_dim_ = matrix_b.rows();control_dim_ = matrix_b.cols();ADEBUG << "state_dim" << state_dim_;ADEBUG << "control_dim_" << control_dim_;num_param_ = state_dim_ * (horizon_ + 1) + control_dim_ * horizon_; }
OSQP求解结果输出:
std::vector<double> control_cmd(controls_, 0);apollo::common::math::MpcOsqp mpc_osqp(matrix_ad_, matrix_bd_, matrix_q_updated_, matrix_r_updated_,matrix_state_, lower_bound, upper_bound, lower_state_bound,upper_state_bound, reference_state, mpc_max_iteration_, horizon_,mpc_eps_); //取控制序列的第一个作为输出的控制量if (!mpc_osqp.Solve(&control_cmd)) {AERROR << "MPC OSQP solver failed";} else {ADEBUG << "MPC OSQP problem solved! ";control[0](0, 0) = control_cmd.at(0);control[0](1, 0) = control_cmd.at(1);}//第一个元素前轮反馈转角steer_angle_feedback = Wheel2SteerPct(control[0](0, 0));//第二个元素加速度补偿acc_feedback = control[0](1, 0);
参考文献
本文针对运动学MPC控制器进行回顾总结,详细可以参考下述文献。
1、【基础】自动驾驶控制算法第五讲 连续方程的离散化与离散LQR原理_哔哩哔哩_bilibili
2、基于MPC轨迹跟踪~理论篇上_哔哩哔哩_bilibili
3、Apollo代码学习(六)—模型预测控制(MPC)_apollo mpc-CSDN博客
相关文章:
基于动力学的MPC控制器设计盲点解析
文章目录 Apollo MPC控制器的设计架构误差模型和离散化预测模型推导目标函数和约束设计优化求解优化OSQP求解器参考文献 Apollo MPC控制器的设计架构 误差模型和离散化 状态变量和控制变量 1、Apollo MPC控制器中状态变量主要有如下6个 matrix_state_ Matrix::Zero(basic_stat…...

Java重要面试名词整理(十六):SpringBoot
由于SpringBoot和Spring、SpringMVC重合度较高,更多详细内容请参考https://blog.csdn.net/weixin_73195042/article/details/144632385 本文着重于SpringBoot的启动流程 文章目录 概念启动流程底层分析构造SpringApplication对象run(String... args)方法SpringBoo…...

在K8S中,如何部署kubesphere?
在Kubernetes集群中,对于一些基础能力较弱的群体来说K8S控制面板操作存在一定的难度,此时kubesphere可以有效的解决这类难题。以下是部署kubesphere的操作步骤: 操作部署: 1. 部署nfs共享存储目录 yum -y install nfs-server e…...

算法-查找缺失的数字
给定一个包含 [0, n] 中 n 个数的数组 nums ,找出 [0, n] 这个范围内没有出现在数组中的那个数。 示例 1: 输入:nums [3,0,1] 输出:2 解释:n 3,因为有 3 个数字,所以所有的数字都在范围 [0,3…...

antd-vue - - - - - a-date-picker限制选择范围
antd-vue - - - - - a-date-picker限制选择范围 1. 效果展示2. 代码展示 1. 效果展示 如图:限制选择范围为 今年 & 去年 的 月份. 2. 代码展示 <template><a-date-picker:disabledDate"disabledDate"picker"month"/> &l…...

计算机网络练习题
学习这么多啦,那就简单写几个选择题巩固一下吧! 1. 在IPv4分组各字段中,以下最适合携带隐藏信息的是(D) A、源IP地址 B、版本 C、TTL D、标识 2. OSI 参考模型中,数据链路层的主要功能是(…...

redis的集群模式与ELK基础
一、redis的集群模式 1.主从复制 (1)概述 主从模式:这是redis高可用的基础,哨兵和集群都是建立在此基础之上。 主从模式和数据库的主从模式是一样的,主负责写入,然后把写入的数据同步到从服务器ÿ…...

STM32-笔记18-呼吸灯
1、实验目的 使用定时器 4 通道 3 生成 PWM 波控制 LED1 ,实现呼吸灯效果。 频率:2kHz,PSC71,ARR499 利用定时器溢出公式 周期等于频率的倒数。故Tout 1/2KHZ;Ft 72MHZ PSC71(喜欢设置成Ft的倍数&…...

Vue3 + ElementPlus动态合并数据相同的单元格(超级详细版)
最近的新项目有个需求需要合并单元列表。ElementPlus 的 Table 提供了合并行或列的方法,可以参考一下https://element-plus.org/zh-CN/component/table.html 但项目中,后台数据返回格式和指定合并是动态且没有规律的,Element 的示例过于简单&…...

【JavaWeb后端学习笔记】MySQL的数据控制语言(Data Control Language,DCL)
MySQL DCL 1、管理用户2、控制权限 DCL英文全称是Data Control Language(数据控制语言),用来管理数据库用户、控制数据库访问权限。 1、管理用户 管理用户的操作都需要在MySQL自带的 mysql 数据库中进行。 -- 查询用户 -- 需要先切换到MyS…...

libvirt学习
文章目录 libvirt 简介节点、Hypervisor和域libvirt 安装和配置libvirt的XML配置文件libvirt APIMain libvirt APIsError handlingSpecial specific APIs 建立到Hypervisor的连接libvirt API使用编译libvirt工具virshvirt-clonevirt-dfvirt-imagevirt-installvirt-topvirt-what…...

STM32-笔记19-串口打印功能
复制项目文件夹03-流水灯,重命名为19-串口打印功能 打开项目 在主函数中,添加头文件、和串口初始化函数(设置波特率)和输出函数,如图所示: 软件部分就设置好了 下面是硬件部分 接线:使用USB…...

概率论与数理统计
概率论占比更多,三分之二左右 数理统计会少一些 事件之间的概率 ab互斥,不是ab独立 古典概型吃高中基础,考的不会很多 条件概率公式,要记 公式不要全记,很多有名称的公式是通过基础公式转换而来的 目的在于解决一…...

统信系统设置代理的问题
统信系统设置代理的问题 问题表现方式一方式二 问题表现 统信系统下有系统代理和应用代理两个代理。设置系统代理时,git不能经过代理拉取代码。但是设置应用代理时,可以用git通过代理拉代码。 这是系统代理,在这里设置 ip 端口,…...

TCP 为什么采用三次握手和四次挥手以及 TCP 和 UDP 的区别
1. TCP 为什么采用三次握手和四次挥手 采用三次握手的原因: 确认双方的收发能力。第一次握手,客户端发送 SYN 报文,告诉服务器自身具备发送数据的能力,第二次握手,服务器回应 SYN ACK 报文,表名自己既能…...

springboot配置并使用RestTemplate
目录 一、RestTemplate配置 1、将RestTemplate初始化为Bean 2、使用HttpClient作为RestTemplate客户端 (1)引入HttpClient依赖 (2)修改RestTemplate配置类 3、设置拦截器 (1)新增拦截器类 …...

人工智能-Python网络编程-TCP
1 TCP-概念版 服务端 import socket # 1 创建服务端套接字对象 # socket.AF_INET IPV4 # socket.SOCK_STREAM TCP # socket.SOCK_DGRAM UDP tcp_server_socket socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 2 绑定端口号 tcp_server_socket.bind((192.…...

【Java回顾】Day3 继承|Override/Ovverload|多态|抽象类|封装|接口|枚举
学习资料 菜鸟教程 https://www.runoob.com/java/java-interfaces.html 继承|Override/Ovverload|多态|抽象类|封装|接口|枚举 继承 创建分等级层次的类,子类继承父类的特征、行为、方法 class 父类{ } class 子类 extends 父类{ super(); }一些性质 Java 不支持…...

SpringMVC(四)响应
目录 数据处理及跳转 1. 结果跳转方式 ①.ModelAndView ②.ServletAPI 1、通过HttpServletResponse进行输出 2、通过HttpServletResponse实现请求转发 3、通过HttpServletResponse实现重定向 ③.SpringMVC 1.直接输出 2.请求转发 3.重定向 2.ResponseBody响应json数…...

vim 的基础使用
目录 一:vim 介绍二:vim 特点三:vim 配置四:vim 使用1、vim 语法格式2、vim 普通模式(1)保存退出(2)光标跳转(3)文本删除(4)文本查找&…...

关于flinkCDC监控mysql binlog时,datetime类型自动转换成时间戳类型问题
flinkCDC监控mysql binlog时,datetime类型自动转换成时间戳类型 问题解决1.自定义转换器类2.代码引用 结果 问题 flink版本:1.18.1,mysql版本:8.0.40 使用FlinkCDC的MySqlSource 连接mysql,对于datetime 类型字段&…...

基于Springboot校园失物招领系统【附源码】
基于Springboot校园失物招领系统 效果如下: 系统登陆页面 物品页面 系统首页面 失物招领管理页面 失物认领页面 宣传视频页面 物品挂失留言管理页面 宣传视频类型管理页面 研究背景 在校园环境中,失物招领是一个常见的问题。传统的失物招领方式主要依…...

单片机端口操作和独立引脚操作
单片机端口操作和独立引脚操作 在单片机编程中,控制I/O端口是最基础的操作之一。通过控制端口,我们可以实现对外设(如LED、按键、继电器等)的控制。在51单片机中,有两种常见的端口操作方式:整体控制&#…...

【Vim Masterclass 笔记03】S03L10 + S03L11:Vim 中的文本删除操作以及 Vim 思维习惯的培养(含 DIY 拓展知识点)
文章目录 Section 3:Vim Essentials(Vim 核心知识)S03L10 Vim 核心浏览命令同步练习点评课S03L11 Deleting Text and "Thinking in Vim" 文本的删除及 Vim 思维习惯的培养1 删除单个字符2 删除一个单词2.1 推广1:D HJK…...

ARM200~500部署
前提:数据库已经安装好,并且正常运行 1.修改hostname,将里面的AR-A 改为hzx vi /etc/hostname 2.重启网络服务 sudo systemctl restart NetworkManager 3.修改community-admin.service 文件,更改小区名称和IP,并将文件上传到/…...

word中插入zotero引用
1、参考文献末尾没有文献? 在文献条目要显示的地方点击“refresh” 2、参考文献条目没有悬挂缩进? 把“书目”添加到样式库中,修改样式为悬挂缩进1.5字符 3、交叉引用? 宏 新建一个宏 粘贴下面代码 Public Sub ZoteroLinkCita…...

需求上线,为什么要刷缓存?
在需求上线的过程中,刷缓存主要有以下几个重要原因: 一、保证数据的准确性 旧数据残留问题 缓存是为了加快数据访问速度而存储的数据副本。在需求更新后,之前缓存中的数据可能已经不符合新的业务逻辑。例如,一个电商网站修改了商…...

TVS二极管选型【EMC】
TVS器件并联在电路中,当电路正常工作时,他处于截止状态(高阻态),不影响线路正常工作,当线路处于异常过压并达到其击穿电压时,他迅速由高阻态变为低阻态,给瞬间电流提供一个低阻抗导通…...

《从入门到精通:蓝桥杯编程大赛知识点全攻略》(一)-递归实现指数型枚举、递归实现排列型枚举
本篇博客将聚焦于通过递归来实现两种经典的枚举方法:指数型枚举和排列型枚举。这两种枚举方式在计算机科学和算法竞赛中都有广泛应用,无论是在解题中,还是在实际工作中都极具价值。 目录 前言 斐波那契数列递归 递归实现指数型枚举 算法思…...

C#对线程同步的应用
什么是线程同步?线程同步的应用场景有哪些?在C#中有哪些线程同步方式?下面对这些问题做一个总结,让大家在面试的时候遇到这些问题能够游刃有余。 线程同步是指在多线程环境下,多个线程同时访问共享资源时,确…...