当前位置: 首页 > article >正文

轨迹优化 | 基于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=(VkTVkm+1T)H0(Vkm+1Vk)+(VkTVkm+2T)ρkm+1skm+1skm+1T(Vkm+2Vk)++ρ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=kmk { y i } i = k − m k \left\{ \boldsymbol{y}_i \right\} _{i=k-m}^{k} {yi}i=kmk,占用存储空间 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)=(1x1)2+100(x2x12)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} {x1f(x)=2(1x1+200x1(x2x12))x2f(x)=200(x2x12)

    对应的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)=xiXσo(xiomin2dmax)

  • 曲率目标函数

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)=xiXσκ(Δxi2Δϕ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)=xiXΔxiΔxi+122

由于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} xiPobs(xi)==2(xiomin2dmax)xiomin2xiomin

  • 曲率目标函数梯度

∂ κ 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=Δxi211cos2Δϕ 1(p1p2)Δxi23Δϕ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) xiPsmo(xi)=2(xi24xi1+6xi4xi+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 专栏介绍 &#x1f525;课设、毕设、创新竞赛必备&#xff01;&#x1f525;本专栏涉及更高阶的运动规划算法轨迹优化实战&#xff0c;包括&#xff1a;曲线生成、碰撞检测…...

Vue2到Vue3:无痛升级之路

为什么要从 Vue2 升级到 Vue3 Vue 3 带来了众多令人瞩目的改进和新特性&#xff0c;这些优势使得升级到 Vue 3 对项目的长期发展具有重要意义。 性能显著提升&#xff1a;Vue 3 采用了基于 Proxy 的响应式系统&#xff0c;相比 Vue 2 使用的 Object.defineProperty&#xff0c…...

第28篇 基于ARM A9处理器用C语言实现中断<四>

Q&#xff1a;可以改变上一期实验工程里红色LED计数的速率吗&#xff1f; A&#xff1a;在按键中断服务程序中使HPS Timer 0停止计数&#xff0c;修改定时器中使用的预设计数值&#xff0c;然后重启定时器&#xff1b;所有的修改都是在按键中断服务程序中完成。主程序和其他…...

Linux、Docker与Redis核心知识点与常用命令速查手册

Linux、Docker与Redis核心知识点与常用命令速查手册 一、Linux基础核心 1. 核心概念 文件系统&#xff1a;采用树形结构&#xff0c;根目录为/权限机制&#xff1a;rwx&#xff08;读/写/执行&#xff09;权限&#xff0c;用户分为owner/group/others软件包管理&#xff1a; …...

时间序列分析(四)——差分运算、延迟算子、AR(p)模型

此前篇章&#xff1a; 时间序列分析&#xff08;一&#xff09;——基础概念篇 时间序列分析&#xff08;二&#xff09;——平稳性检验 时间序列分析&#xff08;三&#xff09;——白噪声检验 一、差分运算 差分运算的定义&#xff1a;差分运算是一种将非平稳时间序列转换…...

《深度学习》——调整学习率和保存使用最优模型

调整学习率 在使用 PyTorch 进行深度学习训练时&#xff0c;调整学习率是一个重要的技巧&#xff0c;合适的学习率调整策略可以帮助模型更好地收敛。 PyTorch 提供了多种调整学习率的方法&#xff0c;下面将详细介绍几种常见的学习率调整策略及实例代码&#xff1a; torch.opt…...

零风险把数据盘挂载给根分区,给生产环境服务器扩容

背景 刚买服务器时&#xff0c;用户量不大&#xff0c;所以结合预算不多情况下&#xff0c;都是默认买个小点的系统盘挂载到服务器上&#xff0c;&#xff08;或者默认服务器的40G&#xff09;&#xff0c;等到某一天业务量上来之后&#xff0c;发现抓肘见襟给自己一手措不及防…...

刷题日记4

2025.1.21 2904. 最短且字典序最小的美丽子字符串 2904. 最短且字典序最小的美丽子字符串 - 力扣&#xff08;LeetCode&#xff09; class Solution { public:string shortestBeautifulSubstring(string s, int k) {//遍历找到美丽子字符串&#xff0c;更新时候如果<res&…...

在vscode中拉取gitee里的项目并运行

拉取项目: 方法一:vscode点击查看--->终端(或者直接通过快捷键ctrol+ `打开) 在终端内通过cd命令定位到你想存放项目的文件夹 例如:cd h: 通过命令:git clone 地址 例如:git clone newbee-mall-vue-app: 前端代码 等待拉取完成即可在对应文件夹下看到项目啦 方…...

IDEA通过Contince接入Deepseek

Deepseek 的出色表现&#xff0c;上期【Deepseek得两种访问方式与本地部署】 安装Continue插件 第一步、下载插件 在编辑栏【File】->设置【Settiings】或快捷键【CtrlAltS】,弹窗的左侧导航树&#xff0c;选择【plugins】,在marketplace 搜索【Continue】&#xff0c;点…...

Ubuntu如何利用.ibd文件恢复MySQL数据?

## 背景&#xff1a;服务器中&#xff0c;MySQL程序坏了&#xff0c;也没有做定时备份的操作。为了是数据库恢复到最新的。 ## 方法&#xff1a;可以使用MySQL的 .ibd 文件恢复。&#xff08;需要原数据库的表结构&#xff09; ## 文件位置&#xff1a;在Ubuntu系统中&#x…...

github上文件过大无法推送问题

GitHub 对文件大小有限制&#xff0c;超过 100 MB 的文件无法直接推送到仓库中。 解决思路&#xff1a; 使用 Git Large File Storage (Git LFS) 来管理大文件不上传对应的大文件 使用Git LFS&#xff1a; 1. 安装 Git LFS 首先&#xff0c;你需要安装 Git LFS。可以按照以…...

数据结构------单向链表。

一.实现单向链表的头插&#xff0c;头删&#xff0c;尾插&#xff0c;尾删&#xff0c;按位置插&#xff0c;按位置删&#xff0c;按位置修改&#xff0c;按元素查找&#xff0c;按元素修改&#xff0c;按元素删除&#xff0c;单链表的逆置&#xff0c;查找倒数第几个元素&…...

(.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算法(极小化极大算法)

计算机科学中最有趣的事情之一就是编写一个人机博弈的程序。有大量的例子&#xff0c;最出名的是编写一个国际象棋的博弈机器。但不管是什么游戏&#xff0c;程序趋向于遵循一个被称为Minimax算法&#xff0c;伴随着各种各样的子算法在一块。本篇将简要介绍 minimax 算法&#…...

cornerstone3D学习笔记-MPR

最近在研究如何利用cornerstone3D (v1.70.13) 来实现MPR功能&#xff0c;找到它的一个demo -- volumeBasic, 运行效果如下图 看了下主程序的示例代码&#xff0c;非常简单&#xff0c;可以说corestone3D这个库把很多细节都封装起来了&#xff0c;使得调用者可以很简单的快速实…...

向量数据库是什么?「向量数据库详解」

目录 向量数据库详解 一、定义与核心概念 二、核心技术与组件 三、应用场景 四、与传统数据库的对比 五、典型技术框架 六、优缺点分析 七、AI领域的最新应用案例 八、总结 向量数据库详解 一、定义与核心概念 向量数据库是专门用于存储、检索和处理向量数据的数据库…...

C++ Primer 函数匹配

欢迎阅读我的 【CPrimer】专栏 专栏简介&#xff1a;本专栏主要面向C初学者&#xff0c;解释C的一些基本概念和基础语言特性&#xff0c;涉及C标准库的用法&#xff0c;面向对象特性&#xff0c;泛型特性高级用法。通过使用标准库中定义的抽象设施&#xff0c;使你更加适应高级…...

Dav_笔记14:优化程序提示 HINTs -4

指定全局表提示 指定表的提示通常是指发生提示的DELETE&#xff0c;SELECT或UPDATE查询块中的表&#xff0c;而不是指语句引用的任何视图中的表。 如果要为显示在视图中的表指定提示&#xff0c;Oracle建议使用全局提示&#xff0c;而不是在视图中嵌入提示。 您可以使用包含具…...

功率因素和电费的关系

功率因数与电费之间存在直接的关系&#xff0c;具体体现在功率因数调整电费上。 功率因数调整电费的定义 功率因数调整电费是指根据用户功率因数的水平高低&#xff0c;对用户的电费进行减收或增收的费用。这种调整机制旨在鼓励用户提高功率因数&#xff0c;减少无功功率的消…...

桥接模式 Bridge Pattern

桥接模式Abstraction 和 Implementor 的理解 在图书馆看到一本 通过电商项目真正实战《贯穿设计模式》。拿起来翻到了 桥接模式&#xff0c;感觉味道不对&#xff0c;和我印象中不一样。 感谢这位同学提供的源码 贯穿设计模式-适配器模式桥接模式_-CSDN博客GitHub - WeiXiao…...

C# SpinLock 类 使用详解

总目录 前言 SpinLock 是 C# 中一种轻量级的自旋锁&#xff0c;属于 System.Threading 命名空间&#xff0c;专为极短时间锁竞争的高性能场景设计。它通过忙等待&#xff08;自旋&#xff09;而非阻塞线程来减少上下文切换开销&#xff0c;适用于锁持有时间极短&#xff08;如…...

Ubuntu 安装 OpenCV (C++)

版本详情&#xff1a; Ubuntu: 22.04 5.15.0-133-generic gcc: 11.4.0 g: 11.4.0 OpenCV: 4.7.0 1. 卸载 OpenCV 进入原先编译 opencv 的 build 目录&#xff0c;在该目录下打开终端&#xff0c;执行以下代码&#xff08;如果 build 已经删除了&#xff0c;可以重新编译一…...

推荐两个比较好用的流程图js库

React Flow 和 Logic Flow 是两个用于构建流程图的 JavaScript 库&#xff0c;适用于不同的场景和需求。以下是它们的简要介绍和对比&#xff1a; React Flow React Flow 是一个基于 React 的流程图库&#xff0c;专注于构建高度可定制的节点和边。它适用于需要复杂交互和数据…...

前端模板引擎

前言 正常渲染拿到数据后渲染&#xff0c;三步走&#xff1a;格式化数据、编译模板、渲染数据 如下例 <!DOCTYPE html><html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice…...

Linux /dev/null

/dev/null 是 Linux 和类 Unix 系统中一个特殊且非常有用的设备文件&#xff0c;也被称为空设备。下面为你详细介绍它的特点、用途和使用示例。 特点 写入丢弃&#xff1a;当向 /dev/null 写入数据时&#xff0c;这些数据会被立即丢弃&#xff0c;不会被保存到任何地方&#…...

ubuntu安装docker 无法拉取问题

sudo docker run hello-world [sudo] ubuntu 的密码&#xff1a; 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容器编排 在云原生技术蓬勃发展的当下&#xff0c;Kubernetes&#xff08;简称K8s&#xff09;已成为容器编排领域的事实标准&#xff0c;为现代应用的部署、管理与扩展提供了强大支持。 K8s的核心优势之一是其卓越的容器编排能力 在传统应用部署模式下&#xff0c;…...

长尾词SEO优化软件:企业官网流量提升的软件【实测】

搜索引擎流量中68%来自长尾关键词&#xff08;数据来源&#xff1a;Ahrefs 2025&#xff09;&#xff0c;但83%企业仍困于「高价值长尾词难挖掘内容生产跟不上」的双重困境。当同行用智能工具批量布局「孕妇防辐射服哪个牌子好」等精准词时&#xff0c;手动分析数据的你可能还在…...