基于LQR算法的机器人轨迹跟踪控制详解

   本文摘要

   本文详细介绍了基于线性二次型调节器(LQR)算法的机器人轨迹跟踪控制方法。首先,文章通过建立基于运动学模型的离散状态方程,来描述机器人的当前状态与目标状态之间的关系,并利用此模型进行状态误差的计算。接着,通过设定状态量和控制量的权重矩阵(Q和R),使用LQR算法求解控制输入u,旨在最小化状态偏差和控制成本,以实现精确的轨迹跟踪。此外,文章还分析了LQR算法在ros_motion_planning运动规划库中的实现,通过源代码详解了误差量化、状态方程动态矩阵的建立、反馈控制增益的计算以及最终控制向量的生成等关键技术步骤。


   一、根据运动学模型建立离散的状态方程

   本部分内容已经在之前的文章中详细介绍过了,链接如下:


   车辆/单车运动学建模、线性化、离散化过程详细推导【点击可跳转】


   设机器人当前的位姿为 ( x c , y c , φ c ) (x_c, y_c, φ_c) xc,yc,φc, 其中 ( x c , y c , ) (x_c, y_c,) xc,yc, 为其当前位置坐标, φ c φ_c φc为其当前姿态角,当前前轮的转向角为 δ c δ_c δc,当前的后轮车速为 v c v_c vc,前轮车速为 v f v_f vf,前后轮距为 l l l,默认采用后轮驱动模式,即可控的是后轮车速 v c v_c vc,如下图所示:

   这里我们直接放一下上文的结论(想了解过程中,可以去看一下上面链接中的文章),将状态量和输入量取为当前量与期望值的差值,即

   X = [ x c − x r y c − y r φ c − φ r ] , u = [ v c − v r δ c − δ r ] {{X}}=\begin{bmatrix}{x_{c}}-x_{r}\\ {y_c}-{y}_{r}\\ {\varphi_c}-{\varphi}_{r}\end{bmatrix},\mathbf{{u}}=\begin{bmatrix}v_{c}-v_{r}\\ \delta_{c}-\delta_{r}\end{bmatrix} X= xcxrycyrφcφr ,u=[vcvrδcδr]

   此时:

   X ( k + 1 ) = [ 1 0 − T v r sin ⁡ ψ r 0 1 T v r cos ⁡ ψ r 0 0 1 ] X ( k ) + [ T cos ⁡ ψ r 0 T sin ⁡ ψ r 0 T tan ⁡ δ r l T v r l cos ⁡ 2 δ r ] u ( k ) = A X ( k ) + B u ( k ) \begin{aligned}\mathbf{X}(\mathrm{k}+1) & \left.=\left[\begin{array}{ccc}1 & 0 & -\mathrm{Tv}_{\mathrm{r}}\sin\psi_{\mathrm{r}}\\ 0 & 1 & \mathrm{Tv}_{\mathrm{r}}\cos\psi_{\mathrm{r}}\\ 0 & 0 & 1\end{array}\right.\right]\mathbf{X}(\mathrm{k})+\left[\begin{array}{ccc}\mathrm{T}\cos\psi_{\mathrm{r}} & 0\\ \mathrm{T}\sin\psi_{\mathrm{r}} & 0\\ \frac{\mathrm{T}\tan\delta_{\mathrm{r}}}{l} & \frac{T\mathrm{v}_{\mathrm{r}}}{l\cos^2\delta_{\mathrm{r}}}\end{array}\right]\mathbf{u}(\mathrm{k})\\ & \mathrm{=AX(k)+Bu(k)}\end{aligned} X(k+1)= 100010TvrsinψrTvrcosψr1 X(k)+ TcosψrTsinψrlTtanδr00lcos2δrTvr u(k)=AX(k)+Bu(k)

   有了上面的离散化线性运动学模型的状态空间方程,就可以基于此来使用LQR、MPC等控制算法进行轨迹跟踪控制或完成其他控制任务了


   二、使用LQR求解控制输入u

   有了第一部分得到的离散的状态空间方程,轨迹跟踪问题就是如何选取合适的输入量u来使得状态量X趋于0,即使得当前状态与期望状态趋于相等(即差值为0),在LQR中目标函数被设定为下式

   J = ∑ k = 1 n ( X T Q X + u T R u ) J = \sum_{k=1}^n (X^T QX + u^T Ru) J=k=1n(XTQX+uTRu)

   其中,Q为半正定的状态加权矩阵, R为正定的控制加权矩阵,且两者通常取为对角阵;这两个权重矩阵也就是LQR算法需要调节的参数,如果增大Q矩阵中某个元素值,意味着我们期望状态量X中对应的状态能够快速趋近于零,加强跟踪效果,如果增大R矩阵中某个元素值,意味着我们期望输入量u中对应的输入能够快速趋近于零,减少能量损耗。

【补充说明:正定矩阵】给定一个大小为n × n 的实对称矩阵A ,若对于任意长度为n 的非零向量x ,有 x T A x > 0 x^TA x > 0 xTAx>0,则为矩阵A为正定矩阵,有 x T A x > = 0 x^TA x >= 0 xTAx>0,则为矩阵A为半正定矩阵。

   因为,上述LQR目标函数的表达式本质上是一个典型的多目标优化最优控制问题,某一项的权重越大,那么在优化时,减少该项的值,对目标函数值的减少就越明显,自然会优先减少该项的值,从而达到使得该项更快的趋于0。在轨迹跟踪中,前一项优化目标表示跟踪过程路径偏差的累积大小,第二项优化目标表示跟踪过程控制能量的损耗,这样就将轨迹跟踪控制问题转化为一个最优控制问题,两个权重矩阵Q和R就用来平衡这两项的权重。

   对于上述目标函数的优化求解,使用线性二次型调节器 ( Linear-Quadratic Regulator)进行求解,解出的最优控制规律u是关于状态变量X的线性函数,如下所示:

   u = − ( R + B T P B ) − 1 B T P A x = K x u = - (R + B^T P B)^{-1} B^T P A x = Kx u=(R+BTPB)1BTPAx=Kx

   其中,

   P = A T P A − A T P B ( R + B T P B ) − 1 B T P A + Q P = A^T P A - A^T P B (R + B^T P B)^{-1} B^T P A + Q P=ATPAATPB(R+BTPB)1BTPA+Q

   可以发现解出的u的表达式中仅有P是未知的,P可通过上式进行求解,然而可以发现,上式直接求不好求,所以,我们可以选择求其近似解,

【补充说明:黎卡提方程】
形如 y ′ = P ( x ) y 2 + Q ( x ) y + R ( x ) \mathrm{y^{\prime}=P(x)y^2+Q(x)y+R(x)} y=P(x)y2+Q(x)y+R(x)的非线性微分方程称为黎卡提方程。

   针对黎卡提方程,可以采用循环迭代的思想求解P,具体方法如下:

   ①将等式右边的P初始化为P_old=Q;

   ② 然后用P_old取代等式右边的P来计算等式右边的值,计算结果为P_new

   ③ 比较P_old和P_new,若两者的差值小于预设值, 则认为等式两边相等,我们已经得到了P的近似解,结束循环;否则再令P_old=P_new,继续循环②~③步,直至得到P的近似解或者达到最大迭代次数。

   所以在求解控制输入u时,先通过上面的循环迭代,求出近似的P,然后代入u的表达式,即可得到当前的控制输入u,这里需要注意的是,我们定义的输入u,实际上是当前值与期望值的差值,需要再加上期望值 u r u_r ur才是当前的控制输出指令给机器人执行,进行轨迹跟踪控制。


   三、ros_motion_planning运动规划库中实现LQR算法的函数源码解析

   1、源码如下:

/*** @brief Execute LQR control process* @param s   current state* @param s_d desired state* @param u_r refered control* @return u  control vector*/
Eigen::Vector2d LQRPlanner::_lqrControl(Eigen::Vector3d s, Eigen::Vector3d s_d, Eigen::Vector2d u_r)
{Eigen::Vector2d u;Eigen::Vector3d e(s - s_d);e[2] = regularizeAngle(e[2]);// state equation on errorEigen::Matrix3d A = Eigen::Matrix3d::Identity();A(0, 2) = -u_r[0] * sin(s_d[2]) * d_t_;A(1, 2) = u_r[0] * cos(s_d[2]) * d_t_;Eigen::MatrixXd B = Eigen::MatrixXd::Zero(3, 2);B(0, 0) = cos(s_d[2]) * d_t_;B(1, 0) = sin(s_d[2]) * d_t_;B(2, 1) = d_t_;// discrete iteration Ricatti equationEigen::Matrix3d P, P_;P = Q_;for (int i = 0; i < max_iter_; ++i){Eigen::Matrix2d temp = R_ + B.transpose() * P * B;P_ = Q_ + A.transpose() * P * A - A.transpose() * P * B * temp.inverse() * B.transpose() * P * A;if ((P - P_).array().abs().maxCoeff() < eps_iter_)break;P = P_;}// feedbackEigen::MatrixXd K = -(R_ + B.transpose() * P_ * B).inverse() * B.transpose() * P_ * A;u = u_r + K * e;return u;
}

   2、主要作用:

   函数_lqrControl执行LQR(线性二次调节器)控制过程,其主要作用是根据当前状态、期望状态和参考控制输入来计算控制向量。这个控制向量旨在最小化路径追踪误差和控制输入的成本,是实现精确路径跟踪的关键。下面将逐段详细介绍这个函数的代码和其功能。

   3、结合第二部分的流程进行详细介绍

Eigen::Vector2d u;
Eigen::Vector3d e(s - s_d);
e[2] = regularizeAngle(e[2]);

   这段代码首先定义控制向量u,然后计算当前状态s和期望状态s_d之间的误差向量e。为了确保角度误差在合理的范围内(-π 到 π),使用regularizeAngle函数处理e[2],即角度误差部分。

   注:这里的s和s_d分别就是本文第一和第二部分中介绍中当前状态 X c X_c Xc和期望状态 X r X_r Xr,这里的e就是当前状态与期望状态的差值,也就本文介绍的 X X X

// state equation on error
Eigen::Matrix3d A = Eigen::Matrix3d::Identity();
A(0, 2) = -u_r[0] * sin(s_d[2]) * d_t_;
A(1, 2) = u_r[0] * cos(s_d[2]) * d_t_;Eigen::MatrixXd B = Eigen::MatrixXd::Zero(3, 2);
B(0, 0) = cos(s_d[2]) * d_t_;
B(1, 0) = sin(s_d[2]) * d_t_;
B(2, 1) = d_t_;

   这一部分定义了基于误差的状态方程的动态矩阵A和控制矩阵B。这些矩阵描述了当前状态误差如何随时间演变,以及控制输入如何影响状态误差的演变。A矩阵初始化为单位矩阵,然后通过调整以包含对状态误差动态的描述。B矩阵初始化为零矩阵,然后根据期望状态和控制间隔时间d_t_进行填充。

   注: 这里的A矩阵,跟本文前面给出的A矩阵完全相同,B矩阵的前两行相同,但第三行不同,是因为本文给出的矩阵A和B是按照单车模型进行建模得到的,而该程序是对差速模型进行建模得到的,所以这里不一样

// discrete iteration Ricatti equation
Eigen::Matrix3d P, P_;
P = Q_;
for (int i = 0; i < max_iter_; ++i)
{Eigen::Matrix2d temp = R_ + B.transpose() * P * B;P_ = Q_ + A.transpose() * P * A - A.transpose() * P * B * temp.inverse() * B.transpose() * P * A;if ((P - P_).array().abs().maxCoeff() < eps_iter_)break;P = P_;
}

   这一段代码通过使用前文介绍的离散迭代黎卡提(Riccati)方程来计算反馈增益矩阵K的中间变量PP是一个对称正定矩阵,用于评估状态误差的二次成本。这个过程反复迭代,直到P的变化小于设定的阈值eps_iter_或达到最大迭代次数max_iter_

   注:该过程与前文介绍完全吻合

// feedback
Eigen::MatrixXd K = -(R_ + B.transpose() * P_ * B).inverse() * B.transpose() * P_ * A;u = u_r + K * e;

   最后,根据计算出的P来,计算反馈增益矩阵K,并使用它来调整参考控制u_r,以最小化误差。这个反馈机制是LQR控制的核心,它确保了即使在存在状态误差的情况下,控制系统也能产生接近最优的控制动作。

   注:该过程与前文介绍完全吻合

   综上所述,_lqrControl函数通过计算基于当前和期望状态的控制动作,实现了对机器人的精确控制,以跟踪给定的路径。这一过程包括误差的量化、系统动态的建模、反馈控制增益的计算,以及最终控制向量的生成。

在这里插入图片描述
在这里插入图片描述


   参考资料:

   1、基础算法 - LQR - 离散时间有限边界

   2、路径规划与轨迹跟踪系列算法学习_第12讲_线性二次型调节器(LQR)法

   3、【自动驾驶】LQR控制实现轨迹跟踪 | python实现 | c++实现

   4、ai-winter/ros_motion_planning


在这里插入图片描述
在这里插入图片描述


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

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

相关文章

「Unity3D」TextMeshPro-Text(UI)无法拖放到TextMeshPro的属性面板上

继承MonoBehaviour&#xff0c;然后定义public TextMeshPro textPro&#xff0c;属性面板上就会有TextMeshPro的拖放槽&#xff08;slot&#xff09;&#xff0c;以配置含有TextMeshPro的组件对象&#xff08;GameObject&#xff09;。 但此时会发现&#xff0c;含有TextMeshPr…

Linux10 三剑客 正则表达式

三剑客 grep 擅长过滤&#xff0c;按行过滤 首先要把多个虚拟机的网络改成一种形式 爆破攻击&#xff1a;‌爆破攻击是一种尝试通过穷举法&#xff08;即尝试所有可能的组合&#xff09;来破解密码或身份验证的方法。这种攻击通常用于尝试登录到系统、网络或应用程序&#…

C#方法

在 C&#xff03;中&#xff0c;方法的定义包括任意方法修饰符&#xff08;如方法的可访问性&#xff09;、返回值的类型&#xff0c; 然后依次是方法名、输入参数的列表&#xff08;用圆括号括起来&#xff09;和方法体&#xff08;用花括号括起来&#xff09;。 [修饰符] 返…

【昂贵的婚礼】

题目 代码 #include<bits/stdc.h> using namespace std; typedef pair<int, int> PII; #define x first #define y second const int N 110, M 10110; int h[N], e[M], ne[M], w[M], idx; bool st[N]; int n, m; int dist[N]; int tier[N]; void add(int a, int…

爬虫 Web Js 逆向:RPC 远程调用获取加密参数(1)WebSocket 协议介绍

RPC (Remote Procedure Call) 是远程调用的意思。 在 Js 逆向时&#xff0c;本地可以和浏览器以服务端和客户端的形式通过 WebSocket 协议进行 RPC 通信&#xff0c;这样可以直接调用浏览器中的一些函数方法&#xff0c;不必去在意函数具体的执行逻辑&#xff0c;可以省去大量…

超详细!!!electron-vite-vue开发桌面应用之Electron Forge打包项目(三)

云风网 云风笔记 云风知识库 electronforge可将前端静态页面打包成.exe、.deb和.rpm等&#xff0c;能适配各种平台 一、安装依赖 cd my-app npm install --save-dev electron-forge/cli npm exec --packageelectron-forge/cli -c "electron-forge import"安装后pack…

简化工作流连线以及让工作流易于操作的插件:rgthree-comfy与cg-use-everywhere

当我们想要在工作流中进行多种不同配置、功能模块&#xff0c;并在多种不同配置、功能模块间进行切换&#xff0c;每次可以执行不同配置、功能模块&#xff0c;用原来简单的连线方式连接各个节点时&#xff0c;除了连线会十分复杂外&#xff0c;要切换不同配置、功能模块&#…

直接插入排序(C语言)

一、图解 思想: 把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中&#xff0c;直到所有的记录插入完为 止&#xff0c;得到一个新的有序序列 。 当插入第i(i>1)个元素时&#xff0c;前面的array[0],array[1],…,array[i-1]已经排好序&#xff0c;此时…

【Java】如何使用jdbc连接并操作MySQL,一文读懂不迷路,小白也能轻松学会

JDBC的原理 JDBC&#xff08;Java Database Connectivity&#xff09;是Java提供的用于连接和操作数据库的API。它允许Java应用程序与各种数据库进行交互&#xff0c;以下是JDBC的基本原理&#xff1a; 驱动程序管理&#xff1a;JDBC使用不同的数据库驱动程序来连接不同类型的…

兼容并蓄,高效集成:EasyCVR视频综合接入能力助力多元化项目需求

随着视频技术的不断进步&#xff0c;视频监控、视频直播、执法记录仪、语音可视对讲、无人机等视频资源的应用场景日益丰富。这些视频资源不仅在数量上快速增长&#xff0c;而且在质量、格式、编码标准等方面也呈现出多样化的特点。因此&#xff0c;为了有效整合这些资源&#…

2024年06月 Python(二级)真题解析#中国电子学会#全国青少年软件编程等级考试

Python等级考试(1~6级)全部真题・点这里 一、单选题(共25题,共50分) 第1题 列表 fruit = [‘西瓜’, ‘菠萝’, ‘哈密瓜’, ‘葡萄’],以下哪个选项,可以获取列表最后一个元素?( ) A:fruit[len(fruit)] B:fruit[len(fruit) - 1] C:fruit[len(fruit) + 1] D:fr…

echarts学习:绘制地图

前言 经过之前一段时间的磨砺&#xff0c;我具备了基本的使用echarts绘制图表的能力。但是在最近这几个月里我接连遇到了几个棘手的任务&#xff0c;这大大的提升了我的echarts水平。其中我遇到的第一个高难度任务就是使用echarts绘制如下的地图&#xff1a; 简单的分析一下&a…

Android studio 引入Json文件

Xcode引入json文件非常简单&#xff0c;没想到Android Studio是有讲究的&#xff0c;必须指定位置。 跟 lib 同一级目录下 创建一个assets&#xff08;如果不存在就创建&#xff09; future: DefaultAssetBundle.of(context).loadString(assets/appcenter.json),针对我这个路…

使用Leaks定位iOS内存泄漏问题并解决

使用Leaks定位iOS内存泄漏问题并解决 前言 内存泄漏问题一直是程序开发中最令人头疼的问题&#xff0c;特别是C/C。虽然C/C在C11之后引入了许多新特性&#xff0c;包括智能指针&#xff0c;自动类型推导等&#xff0c;但C中动态内存的分配和释放仍然需要程序员来显式地进行。…

【网络协议】精讲TCP通信原理!

前言 TCP 把连接作为最基本的对象&#xff0c;每一条 TCP 连接都有两个端点&#xff0c;这种端点我们叫作套接字&#xff08;socket&#xff09;&#xff0c;它的定义为端口号拼接到 IP 地址即构成了套接字&#xff0c;例如&#xff0c;若 IP 地址为 192.3.4.16 而端口号为 80&…

CSS设置文本超出显示省略号

一、单行文本显示省略号 <div class"box"><p>测试文本测试文本测试文本测试文本测试文本测试文本测试文本测试文本测试文本测试文本测试文本测试文本测试文本测试文本测试文本测试文本测试文本测试文本测试文本测试文本测试文本测试文本测试文本测试文本…

C# 静态方法和实例方法

一、静态成员&#xff0c;实例成员&#xff0c;静态方法&#xff0c;实例方法 静态成员就是用static修饰的字段&#xff1b; 实例成员就是没有被static修饰的字段&#xff1b; 静态方法就是用static修饰的方法&#xff1b; 实例方法就是没有被static修饰的方法&#xff1b;…

无人机之如何避免飞行错误篇

在无人机飞行中&#xff0c;飞手可能会遇到各种问题&#xff0c;这些问题不仅会影响飞行效果&#xff0c;还可以带来安全隐患。以下是一些常见的错误及避免方法&#xff0c;帮助飞手提高飞行稳定性和安全性&#xff1a; 一、校准传感器 IMU&#xff08;惯性测量单位&#xff0…

HttpClient在ASP.NET Core中的最佳实践:实现高效的HTTP请求

引言 在现代Web开发中&#xff0c;HTTP请求的高效性和可靠性对于应用的整体性能至关重要。ASP.NET Core提供了HttpClient类&#xff0c;它是一个强大且灵活的工具&#xff0c;可以用来发送HTTP请求并处理响应。然而&#xff0c;如何在ASP.NET Core中实现高效的HTTP请求&#x…

【Linux】手把手教你从零上手Vim编辑器

&#x1f984;个人主页:修修修也 &#x1f38f;所属专栏:Linux ⚙️操作环境:Xshell (操作系统:CentOS 7.9 64位) 目录 &#x1f4cc;写在前面 &#x1f4cc;Vim是什么 &#x1f4cc;手把手教你开始使用Vim &#x1f38f;使用Vim编辑第一个程序 &#x1f38f;Normal(命令模…