轨迹优化 | 基于LBFGS优化器的无约束路径平滑(附ROS C++仿真)
目录
- 0 专栏介绍
- 1 LBFGS优化器
- 1.1 拟牛顿法框架
- 1.2 `LBFGS-Lite`库
- 2 基于LBFGS的轨迹优化
- 3 ROS C++仿真
0 专栏介绍
🔥课设、毕设、创新竞赛必备!🔥本专栏涉及更高阶的运动规划算法轨迹优化实战,包括:曲线生成、碰撞检测、安全走廊、优化建模(QP、SQP、NMPC、iLQR等)、轨迹优化(梯度法、曲线法等),每个算法都包含代码实现加深理解
🚀详情:运动规划实战进阶:轨迹优化篇
1 LBFGS优化器
1.1 拟牛顿法框架
在数值优化 | 详解拟牛顿法之SR1、DFP、BFGS、L-BFGS(附案例分析与Python实现)中,我们介绍了拟牛顿法(quasi-Newton method)框架,有助于解决经典牛顿法计算Hessian矩阵的逆矩阵 H k ( x ) \boldsymbol{H}_k\left( \boldsymbol{x} \right) Hk(x)复杂度高、数值稳定性较差的问题

L-BFGS算法是一种用于求解大规模无约束优化问题的拟牛顿法。它是BFGS算法的内存优化版本,适用于高维问题(如机器学习、轨迹优化等)。具体而言,L-BFGS算法仅保留最近 m m m步的中间结果来近似Hessian矩阵的逆矩阵 H k ( x ) \boldsymbol{H}_k\left( \boldsymbol{x} \right) Hk(x)
H k + 1 = ( V k T ⋯ V k − m + 1 T ) H 0 ( V k − m + 1 ⋯ V k ) + ( V k T ⋯ V k − m + 2 T ) ρ k − m + 1 s k − m + 1 s k − m + 1 T ( V k − m + 2 ⋯ V k ) + ⋯ + ρ k s k s k T \begin{aligned}\boldsymbol{H}_{k+1}&=\left( \boldsymbol{V}_{k}^{T}\cdots \boldsymbol{V}_{k-m+1}^{T} \right) \boldsymbol{H}_0\left( \boldsymbol{V}_{k-m+1}\cdots \boldsymbol{V}_k \right) \\\,\, &+\left( \boldsymbol{V}_{k}^{T}\cdots \boldsymbol{V}_{k-m+2}^{T} \right) \rho _{k-m+1}\boldsymbol{s}_{k-m+1}\boldsymbol{s}_{k-m+1}^{T}\left( \boldsymbol{V}_{k-m+2}\cdots \boldsymbol{V}_k \right) \\\,\, &+\cdots \\\,\, &+\rho _k\boldsymbol{s}_k\boldsymbol{s}_{k}^{T}\end{aligned} Hk+1=(VkT⋯Vk−m+1T)H0(Vk−m+1⋯Vk)+(VkT⋯Vk−m+2T)ρk−m+1sk−m+1sk−m+1T(Vk−m+2⋯Vk)+⋯+ρkskskT
因此在L-BFGS算法中无需保留完整的 H k \boldsymbol{H}_k Hk,只需存储向量序列 { s i } i = k − m k \left\{ \boldsymbol{s}_i \right\} _{i=k-m}^{k} {si}i=k−mk、 { y i } i = k − m k \left\{ \boldsymbol{y}_i \right\} _{i=k-m}^{k} {yi}i=k−mk,占用存储空间 2 m n 2mn 2mn,空间复杂度为 O ( n ) O(n) O(n),下降了一个量级.更详细的推导请参考数值优化 | 详解拟牛顿法之SR1、DFP、BFGS、L-BFGS(附案例分析与Python实现)
1.2 LBFGS-Lite库
浙江大学FAST-LAB开源的L-BFGS算法库提供了一个轻量级 L-BFGS 算法实现,支持用户自定义目标函数和梯度计算,专为高效、易用和嵌入式优化设计。它基于 C++ 实现,适用于机器人、自动驾驶等领域的实时优化问题。
下面简单介绍LBFGS-Lite的使用方法:
-
定义目标函数和梯度
举例而言,设优化目标为
f ( x ) = ( 1 − x 1 ) 2 + 100 ( x 2 − x 1 2 ) 2 f(\boldsymbol{x})=(1-x_1)^2+100(x_2-x_1^2)^2 f(x)=(1−x1)2+100(x2−x12)2则梯度
{ ∂ f ( x ) ∂ x 1 = − 2 ( 1 − x 1 + 200 x 1 ( x 2 − x 1 2 ) ) ∂ f ( x ) ∂ x 2 = 200 ( x 2 − x 1 2 ) \begin{cases} \frac{\partial f\left( \boldsymbol{x} \right)}{\partial x_1}=-2\left( 1-x_1+200x_1\left( x_2-x_{1}^{2} \right) \right)\\ \frac{\partial f\left( \boldsymbol{x} \right)}{\partial x_2}=200\left( x_2-x_{1}^{2} \right)\\ \end{cases} {∂x1∂f(x)=−2(1−x1+200x1(x2−x12))∂x2∂f(x)=200(x2−x12)对应的C++代码为
static double costFunction(void *instance,const Eigen::VectorXd &x,Eigen::VectorXd &g) {const int n = x.size();double fx = 0.0;for (int i = 0; i < n; i += 2){const double t1 = 1.0 - x(i);const double t2 = 10.0 * (x(i + 1) - x(i) * x(i));g(i + 1) = 20.0 * t2;g(i) = -2.0 * (x(i) * g(i + 1) + t1);fx += t1 * t1 + t2 * t2;}return fx; } -
给定初始解并优化求解
/* Set the initial guess */ for (int i = 0; i < N; i += 2) {x(i) = -1.2;x(i + 1) = 1.0; }/* Set the minimization parameters */ lbfgs::lbfgs_parameter_t params; params.g_epsilon = 1.0e-8; params.past = 3; params.delta = 1.0e-8;/* Start minimization */ int ret = lbfgs::lbfgs_optimize(x,finalCost,costFunction,nullptr,monitorProgress,this,params);
2 基于LBFGS的轨迹优化
为了使用LBFGS-Lite进行轨迹优化,我们首先需要定义一个无约束的优化问题:选择优化变量为二维的路径序列 X = { x i = ( x i , y i ) ∣ i ∈ [ 1 , N ] } \mathcal{X} =\left\{ \boldsymbol{x}_i=\left( x_i,y_i \right) |i\in \left[ 1,N \right] \right\} X={xi=(xi,yi)∣i∈[1,N]},并基于此设计优化目标函数
f ( X ) = w o P o b s ( X ) + w κ P c u r ( X ) + w s P s m o ( X ) f\left( \mathcal{X} \right) =w_oP_{\mathrm{obs}}\left( \mathcal{X} \right) +w_{\kappa}P_{\mathrm{cur}}\left( \mathcal{X} \right) +w_sP_{\mathrm{smo}}\left( \mathcal{X} \right) f(X)=woPobs(X)+wκPcur(X)+wsPsmo(X)
其中:
- 障碍目标函数
P o b s ( X ) = ∑ x i ∈ X σ o ( ∥ x i − o min ∥ 2 − d max ) P_{\mathrm{obs}}\left( \mathcal{X} \right) =\sum_{\boldsymbol{x}_i\in \mathcal{X}}^{}{\sigma _o\left( \left\| \boldsymbol{x}_i-\boldsymbol{o}_{\min} \right\| _2-d_{\max} \right)} Pobs(X)=xi∈X∑σo(∥xi−omin∥2−dmax)
- 曲率目标函数
P c u r ( X ) = ∑ x i ∈ X σ κ ( Δ ϕ i ∥ Δ x i ∥ 2 − κ max ) P_{\mathrm{cur}}\left( \mathcal{X} \right) =\sum_{\boldsymbol{x}_i\in \mathcal{X}}^{}{\sigma _{\kappa}\left( \frac{\varDelta \phi _i}{\left\| \varDelta \boldsymbol{x}_i \right\| _2}-\kappa _{\max} \right)} Pcur(X)=xi∈X∑σκ(∥Δxi∥2Δϕi−κmax)
- 平滑目标函数
P s m o ( X ) = ∑ x i ∈ X ∥ Δ x i − Δ x i + 1 ∥ 2 2 P_{\mathrm{smo}}\left( \mathcal{X} \right) =\sum_{\boldsymbol{x}_i\in \mathcal{X}}^{}{\left\| \varDelta \boldsymbol{x}_i-\varDelta \boldsymbol{x}_{i+1} \right\| _{2}^{2}} Psmo(X)=xi∈X∑∥Δxi−Δxi+1∥22
由于LBFGS-Lite需要用户显式地定义目标函数梯度,因此需要进一步计算
- 障碍目标函数梯度
∂ P o b s ( x i ) ∂ x i = = 2 ( ∥ x i − o min ∥ 2 − d max ) x i − o min ∥ x i − o min ∥ 2 \frac{\partial P_{\mathrm{obs}}\left( \boldsymbol{x}_i \right)}{\partial \boldsymbol{x}_i}==2\left( \left\| \boldsymbol{x}_i-\boldsymbol{o}_{\min} \right\| _2-d_{\max} \right) \frac{\boldsymbol{x}_i-\boldsymbol{o}_{\min}}{\left\| \boldsymbol{x}_i-\boldsymbol{o}_{\min} \right\| _2} ∂xi∂Pobs(xi)==2(∥xi−omin∥2−dmax)∥xi−omin∥2xi−omin
- 曲率目标函数梯度
∂ κ i ∂ x i = 1 ∥ Δ x i ∥ 2 − 1 1 − cos 2 Δ ϕ ( − p 1 − p 2 ) − Δ ϕ i Δ x i ∥ Δ x i ∥ 2 3 \begin{aligned} \frac{\partial \kappa _i}{\partial \boldsymbol{x}_i}&=\frac{1}{\left\| \varDelta \boldsymbol{x}_i \right\| _2}\frac{-1}{\sqrt{1-\cos ^2\varDelta \phi}}\left( -\boldsymbol{p}_1-\boldsymbol{p}_2 \right) -\frac{\varDelta \phi _i\varDelta \boldsymbol{x}_i}{\left\| \varDelta \boldsymbol{x}_i \right\| _{2}^{3}}\\\end{aligned} ∂xi∂κi=∥Δxi∥211−cos2Δϕ−1(−p1−p2)−∥Δxi∥23ΔϕiΔxi
- 平滑目标函数梯度
∂ P s m o ( x i ) ∂ x i = 2 ( x i − 2 − 4 x i − 1 + 6 x i − 4 x i + 1 + x i + 2 ) \frac{\partial P_{\mathrm{smo}}\left( \boldsymbol{x}_i \right)}{\partial \boldsymbol{x}_i}=2\left( \boldsymbol{x}_{i-2}-4\boldsymbol{x}_{i-1}+6\boldsymbol{x}_i-4\boldsymbol{x}_{i+1}+\boldsymbol{x}_{i+2} \right) ∂xi∂Psmo(xi)=2(xi−2−4xi−1+6xi−4xi+1+xi+2)
上述目标函数的具体定义和梯度推导和轨迹优化 | 基于ESDF的共轭梯度优化算法(附ROS C++/Python仿真)是一致的,感兴趣的同学可以参考
3 ROS C++仿真
代价函数和梯度计算的核心代码如下所示
double LBFGSOptimizer::costFunction(void* ptr, const Eigen::VectorXd& x, Eigen::VectorXd& g)
{auto instance = reinterpret_cast<LBFGSOptimizer*>(ptr);const auto& origin_path = instance->path_opt_;int points_num = static_cast<int>(origin_path.size());int var_num = points_num - 4;double cost = 0.0;Eigen::Matrix2Xd grad;grad.resize(2, var_num);grad.setZero();// update opt-path during optimizationEigen::Matrix2Xd path_opt;path_opt.resize(2, points_num);path_opt(0, 0) = origin_path[0].x();path_opt(1, 0) = origin_path[0].y();path_opt(0, 1) = origin_path[1].x();path_opt(1, 1) = origin_path[1].y();path_opt.block(0, 2, 1, var_num) = x.head(var_num).transpose();path_opt.block(1, 2, 1, var_num) = x.tail(var_num).transpose();path_opt(0, points_num - 2) = origin_path[points_num - 2].x();path_opt(1, points_num - 2) = origin_path[points_num - 2].y();path_opt(0, points_num - 1) = origin_path[points_num - 1].x();path_opt(1, points_num - 1) = origin_path[points_num - 1].y();// obstacle termdouble obstacle_cost = 0.0;const auto& obstacle_grad = _calObstacleTerm(instance, path_opt, obstacle_cost);grad += obstacle_grad;cost += obstacle_cost;// smooth termdouble smooth_cost = 0.0;const auto& smooth_grad = _calSmoothTerm(instance, path_opt, smooth_cost);grad += smooth_grad;cost += smooth_cost;// curvature termdouble curvature_cost = 0.0;const auto& curvature_grad = _calCurvatureTerm(instance, path_opt, curvature_cost);grad += curvature_grad;cost += curvature_cost;g.setZero();g.head(var_num) = grad.row(0).transpose();g.tail(var_num) = grad.row(1).transpose();return cost;
}
优化过程的核心代码如下所示
bool LBFGSOptimizer::optimize(const Points3d& waypoints)
{// first two and last two points are constant (to keep start and end direction)int var_num = static_cast<int>(waypoints.size()) - 4;Eigen::VectorXd opt_var(2 * var_num);for (int i = 2; i < static_cast<int>(waypoints.size()) - 2; ++i){opt_var(i - 2) = waypoints[i].x();opt_var(i - 2 + var_num) = waypoints[i].y();}path_opt_ = waypoints;// optimizer parameterslbfgs::lbfgs_parameter_t lbfgs_params;lbfgs_params.mem_size = 256;lbfgs_params.past = 3;lbfgs_params.min_step = 1.0e-32;lbfgs_params.g_epsilon = 0.0;lbfgs_params.delta = 0.01;// optimizationdouble min_cost = 0.0;int ret =lbfgs::lbfgs_optimize(opt_var, min_cost, &LBFGSOptimizer::costFunction, nullptr, nullptr, this, lbfgs_params);// parse solutionif (ret >= 0){for (int i = 0; i < var_num; ++i){path_opt_[i + 2].setX(opt_var(i));path_opt_[i + 2].setY(opt_var(i + var_num));}}else{R_WARN << "LBFGS Optimization failed: " << lbfgs::lbfgs_strerror(ret);return false;}return true;
}
轨迹优化的效果如下,其中橙色为优化后的路径,蓝色为原始路径


完整工程代码请联系下方博主名片获取
🔥 更多精彩专栏:
- 《ROS从入门到精通》
- 《Pytorch深度学习实战》
- 《机器学习强基计划》
- 《运动规划实战精讲》
- …
相关文章:
轨迹优化 | 基于LBFGS优化器的无约束路径平滑(附ROS C++仿真)
目录 0 专栏介绍1 LBFGS优化器1.1 拟牛顿法框架1.2 LBFGS-Lite库 2 基于LBFGS的轨迹优化3 ROS C仿真 0 专栏介绍 🔥课设、毕设、创新竞赛必备!🔥本专栏涉及更高阶的运动规划算法轨迹优化实战,包括:曲线生成、碰撞检测…...
Vue2到Vue3:无痛升级之路
为什么要从 Vue2 升级到 Vue3 Vue 3 带来了众多令人瞩目的改进和新特性,这些优势使得升级到 Vue 3 对项目的长期发展具有重要意义。 性能显著提升:Vue 3 采用了基于 Proxy 的响应式系统,相比 Vue 2 使用的 Object.defineProperty,…...
第28篇 基于ARM A9处理器用C语言实现中断<四>
Q:可以改变上一期实验工程里红色LED计数的速率吗? A:在按键中断服务程序中使HPS Timer 0停止计数,修改定时器中使用的预设计数值,然后重启定时器;所有的修改都是在按键中断服务程序中完成。主程序和其他…...
Linux、Docker与Redis核心知识点与常用命令速查手册
Linux、Docker与Redis核心知识点与常用命令速查手册 一、Linux基础核心 1. 核心概念 文件系统:采用树形结构,根目录为/权限机制:rwx(读/写/执行)权限,用户分为owner/group/others软件包管理: …...
时间序列分析(四)——差分运算、延迟算子、AR(p)模型
此前篇章: 时间序列分析(一)——基础概念篇 时间序列分析(二)——平稳性检验 时间序列分析(三)——白噪声检验 一、差分运算 差分运算的定义:差分运算是一种将非平稳时间序列转换…...
《深度学习》——调整学习率和保存使用最优模型
调整学习率 在使用 PyTorch 进行深度学习训练时,调整学习率是一个重要的技巧,合适的学习率调整策略可以帮助模型更好地收敛。 PyTorch 提供了多种调整学习率的方法,下面将详细介绍几种常见的学习率调整策略及实例代码: torch.opt…...
零风险把数据盘挂载给根分区,给生产环境服务器扩容
背景 刚买服务器时,用户量不大,所以结合预算不多情况下,都是默认买个小点的系统盘挂载到服务器上,(或者默认服务器的40G),等到某一天业务量上来之后,发现抓肘见襟给自己一手措不及防…...
刷题日记4
2025.1.21 2904. 最短且字典序最小的美丽子字符串 2904. 最短且字典序最小的美丽子字符串 - 力扣(LeetCode) class Solution { public:string shortestBeautifulSubstring(string s, int k) {//遍历找到美丽子字符串,更新时候如果<res&…...
在vscode中拉取gitee里的项目并运行
拉取项目: 方法一:vscode点击查看--->终端(或者直接通过快捷键ctrol+ `打开) 在终端内通过cd命令定位到你想存放项目的文件夹 例如:cd h: 通过命令:git clone 地址 例如:git clone newbee-mall-vue-app: 前端代码 等待拉取完成即可在对应文件夹下看到项目啦 方…...
IDEA通过Contince接入Deepseek
Deepseek 的出色表现,上期【Deepseek得两种访问方式与本地部署】 安装Continue插件 第一步、下载插件 在编辑栏【File】->设置【Settiings】或快捷键【CtrlAltS】,弹窗的左侧导航树,选择【plugins】,在marketplace 搜索【Continue】,点…...
Ubuntu如何利用.ibd文件恢复MySQL数据?
## 背景:服务器中,MySQL程序坏了,也没有做定时备份的操作。为了是数据库恢复到最新的。 ## 方法:可以使用MySQL的 .ibd 文件恢复。(需要原数据库的表结构) ## 文件位置:在Ubuntu系统中&#x…...
github上文件过大无法推送问题
GitHub 对文件大小有限制,超过 100 MB 的文件无法直接推送到仓库中。 解决思路: 使用 Git Large File Storage (Git LFS) 来管理大文件不上传对应的大文件 使用Git LFS: 1. 安装 Git LFS 首先,你需要安装 Git LFS。可以按照以…...
数据结构------单向链表。
一.实现单向链表的头插,头删,尾插,尾删,按位置插,按位置删,按位置修改,按元素查找,按元素修改,按元素删除,单链表的逆置,查找倒数第几个元素&…...
(.text+0x1b): undefined reference to `main‘
使用vscode Linux g编译出现 /usr/bin/ld: /usr/lib/gcc/x86_64-linux-gnu/11/../../../x86_64-linux-gnu/Scrt1.o: in function _start: (.text0x1b): undefined reference to main collect2: error: ld returned 1 exit status make: *** [makefile:3: put] Error 1一定记得…...
各类系统Pycharm安装教程
各类系统Pycharm安装教程 一、安装前的准备 1. 系统要求 操作系统: Windows:Windows 10 或更高版本(64位)。macOS:macOS 10.14 或更高版本。Linux:Ubuntu 18.04+、Fedora 30+ 等主流发行版。硬件要求: 内存:至少 4GB(推荐 8GB 以上)。磁盘空间:至少 2.5GB 可用空间…...
算法——结合实例了解Minimax算法(极小化极大算法)
计算机科学中最有趣的事情之一就是编写一个人机博弈的程序。有大量的例子,最出名的是编写一个国际象棋的博弈机器。但不管是什么游戏,程序趋向于遵循一个被称为Minimax算法,伴随着各种各样的子算法在一块。本篇将简要介绍 minimax 算法&#…...
cornerstone3D学习笔记-MPR
最近在研究如何利用cornerstone3D (v1.70.13) 来实现MPR功能,找到它的一个demo -- volumeBasic, 运行效果如下图 看了下主程序的示例代码,非常简单,可以说corestone3D这个库把很多细节都封装起来了,使得调用者可以很简单的快速实…...
向量数据库是什么?「向量数据库详解」
目录 向量数据库详解 一、定义与核心概念 二、核心技术与组件 三、应用场景 四、与传统数据库的对比 五、典型技术框架 六、优缺点分析 七、AI领域的最新应用案例 八、总结 向量数据库详解 一、定义与核心概念 向量数据库是专门用于存储、检索和处理向量数据的数据库…...
C++ Primer 函数匹配
欢迎阅读我的 【CPrimer】专栏 专栏简介:本专栏主要面向C初学者,解释C的一些基本概念和基础语言特性,涉及C标准库的用法,面向对象特性,泛型特性高级用法。通过使用标准库中定义的抽象设施,使你更加适应高级…...
Dav_笔记14:优化程序提示 HINTs -4
指定全局表提示 指定表的提示通常是指发生提示的DELETE,SELECT或UPDATE查询块中的表,而不是指语句引用的任何视图中的表。 如果要为显示在视图中的表指定提示,Oracle建议使用全局提示,而不是在视图中嵌入提示。 您可以使用包含具…...
功率因素和电费的关系
功率因数与电费之间存在直接的关系,具体体现在功率因数调整电费上。 功率因数调整电费的定义 功率因数调整电费是指根据用户功率因数的水平高低,对用户的电费进行减收或增收的费用。这种调整机制旨在鼓励用户提高功率因数,减少无功功率的消…...
桥接模式 Bridge Pattern
桥接模式Abstraction 和 Implementor 的理解 在图书馆看到一本 通过电商项目真正实战《贯穿设计模式》。拿起来翻到了 桥接模式,感觉味道不对,和我印象中不一样。 感谢这位同学提供的源码 贯穿设计模式-适配器模式桥接模式_-CSDN博客GitHub - WeiXiao…...
C# SpinLock 类 使用详解
总目录 前言 SpinLock 是 C# 中一种轻量级的自旋锁,属于 System.Threading 命名空间,专为极短时间锁竞争的高性能场景设计。它通过忙等待(自旋)而非阻塞线程来减少上下文切换开销,适用于锁持有时间极短(如…...
Ubuntu 安装 OpenCV (C++)
版本详情: Ubuntu: 22.04 5.15.0-133-generic gcc: 11.4.0 g: 11.4.0 OpenCV: 4.7.0 1. 卸载 OpenCV 进入原先编译 opencv 的 build 目录,在该目录下打开终端,执行以下代码(如果 build 已经删除了,可以重新编译一…...
推荐两个比较好用的流程图js库
React Flow 和 Logic Flow 是两个用于构建流程图的 JavaScript 库,适用于不同的场景和需求。以下是它们的简要介绍和对比: React Flow React Flow 是一个基于 React 的流程图库,专注于构建高度可定制的节点和边。它适用于需要复杂交互和数据…...
前端模板引擎
前言 正常渲染拿到数据后渲染,三步走:格式化数据、编译模板、渲染数据 如下例 <!DOCTYPE html><html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice…...
Linux /dev/null
/dev/null 是 Linux 和类 Unix 系统中一个特殊且非常有用的设备文件,也被称为空设备。下面为你详细介绍它的特点、用途和使用示例。 特点 写入丢弃:当向 /dev/null 写入数据时,这些数据会被立即丢弃,不会被保存到任何地方&#…...
ubuntu安装docker 无法拉取问题
sudo docker run hello-world [sudo] ubuntu 的密码: Unable to find image hello-world:latest locally docker: Error response from daemon: Get "https://registry-1.docker.io/v2/": context deadline exceeded (Client.Timeout exceeded while awai…...
深入理解Kubernetes:容器编排的中流砥柱
Kubernetes容器编排 在云原生技术蓬勃发展的当下,Kubernetes(简称K8s)已成为容器编排领域的事实标准,为现代应用的部署、管理与扩展提供了强大支持。 K8s的核心优势之一是其卓越的容器编排能力 在传统应用部署模式下,…...
长尾词SEO优化软件:企业官网流量提升的软件【实测】
搜索引擎流量中68%来自长尾关键词(数据来源:Ahrefs 2025),但83%企业仍困于「高价值长尾词难挖掘内容生产跟不上」的双重困境。当同行用智能工具批量布局「孕妇防辐射服哪个牌子好」等精准词时,手动分析数据的你可能还在…...
