qpOASES:使用说明(翻译)


本文将在几分钟内向您解释如何通过qpOASES解决二次规划(QP)问题或整个序列问题。 最后,提供了一个教程示例,可以作为您自己的QP的模板。

1 说明

qpOASES的核心是QProblem类,它能够使用在线有效集策略存储、处理和解决凸二次规划;它使用了几个辅助类。 除特殊情况外,QProblem类是qpOASES功能的唯一用户接口。

为了解决一系列具有固定Hessian和约束矩阵的凸二次规划,需要采取以下步骤:

  1. 创建QProblem类的实例。
  2. 初始化QProblem对象并求解第一个QP(由其QP矩阵(matrices)和向量(vectors)指定)。
  3. 再将向量传递给QProblem对象来解决每个后续QP。

现在,我们将更详细地解释这三个步骤。 在后面的章节中将介绍各种变体和特殊情况,以便于演示。

2 主要步骤

创建QProblem类的实例

通过以下构造函数创建QProblem对象

QProblem( int_t nV, int_t nC );

通过构造函数,可获得二次规划的变量数nV和约束数nC。 目前,还不能解决具有变化维度的QP序列。
第一步概述:可以使用命令创建QProblem类的实例示例:QProblem example( nV,nC )

第一个QP的初始化和求解

第二步需要初始化QProblem对象的所有内部数据结构和序列中第一个QP的求解。 只需调用以下函数即可满足这两个要求:

returnValue init( const real_t* const H,
const real_t* const g,
const real_t* const A,
const real_t* const lb,
const real_t* const ub,
const real_t* const lbA,
const real_t* const ubA,
int_t& nWSR,
real_t* const cputime
);

其中, H H H是(半)正定矩阵,且 H ∈ R n V × n V H\in R^{nV\times nV} HRnV×nV;梯度向量 g ∈ R n V g \in R^{nV} gRnV,约束矩阵 A ∈ R n C × n R A\in R^{nC\times nR} ARnC×nR,自变量的下边界和上边界向量 l b , u b ∈ R n V lb,ub \in R^{nV} lb,ubRnV,约束的下边界和上边界向量为 l b A , u b A ∈ R n C lbA,ubA \in R^{nC} lbA,ubARnC。等式约束是将上、下(约束)边界向量的相应条目设置为相同的值。
所有这些数据必须存储在具有适当尺寸的real_t类型的数组中(按行存储的矩阵,在一维数组中,C风格)。

  • 例如,如果QP公式中没有上边界,则可以传递空指针而不是向量lb1。
  • 所有init函数都会对所有向量参数进行深拷贝(deep copy),因此您必须自己释放内存
  • 矩阵参数H和A不会被深拷贝,因此连续调用qpOASES时不能更改它们

函数init初始化所有内部数据结构。例如,矩阵因子分解,并使用在线有效集策略的初始同伦思想解决第一个二次规划。

整数参数nWSR指定在初始同伦期间要执行的最大工作集重新计算次数(在输出时它包含实际执行的工作集重新计算的次数!)。
如果cputime不是空指针,则它包含整个初始化的最大允许CPU时间(以秒为单位)(以及输出时实际需要的CPU时间)。

函数init会返回一个状态代码(类型为returnValue),表示初始化是否成功。可能的值是:
SUCCESSFUL_RETURN :初始化成功(包括第一个QP的求解)。
RET_MAX_NWSR_REACHED:在给定的工作集重新计算数量内无法解决初始QP。
RET_INIT_FAILED(或更详细的错误代码):初始化失败。
如果init返回SUCCESSFUL_RETURN,则可以通过多个函数获取有关第一个QP解决方案的信息。 其中,最重要的几个函数是:

returnValue getPrimalSolution( real t* const xOpt ) const
  • 将最优原始解向量(dimension:nV)写入数组xOpt,该数组必须由用户分配(和释放)
returnValue getDualSolution( real t* const yOpt ) const
  • 将最佳双解决方案向量(维度:nV + nC)写入数组yOpt,该数据必须由用户分配(和释放)
real t getObjVal( ) const

返回最优目标函数值。
第二步总结:创建了QProblem对象示例后,可以使用以下命令将其与第一个QP一起初始化:
example.init(H,g,A,lb,ub,lbA,ubA,nWSR,cputime)

求解后续QP

如果不是只有一个二次规划而是整个QP序列都要求解,这是MPC问题的通常情况,下一个QP可以用以下函数求解:

returnValue hotstart( const real_t* const g_new,
const real_t* const lb_new,
const real_t* const ub_new,
const real_t* const lbA_new,
const real_t* const ubA_new,
int_t& nWSR,
real_t* const cputime
);

通过传递其梯度向量g_new,其下限和上限向量lb_newub_new以及其约束的上、下边界向量lbA_newubA_new(假设QP矩阵是常数)来确定下一个QP。它通过在线有效集策略使用大多数nWSR工作集重新计算或大多数CPU时间的cputime秒(如果不为空)来求解。
在输出时,nWSRcputime分别包含实际执行的工作集重新计算的数量和求解下一个QP所需的实际CPU时间。
与大多数qpOASES函数一样,hotstart返回状态代码。可能的值是:
SUCCESSFUL_RETURN:QP已经求解成功。
RET_MAX_NWSR_REACHED:在给定数量的工作集重新计算中无法求解QP。
RET_HOTSTART_FAILED(或更详细的错误代码):QP求解失败。
第三步总结:创建并初始化一个Problem对象示例后,下一个QP可以如下方式求解:
example.hotstart(g new,lb new,ub new,lbA new,ubA new,nWLR,cpu time)

一个实列

文件 / example /example1.cpp中给出了使用qpOASES求解两个非常简单的二次规划的完整示例:

#include <qpOASES.hpp>
int main( )
{
USING_NAMESPACE_QPOASES
/* Setup data of first QP. */
real_t H[2*2] = { 1.0, 0.0, 0.0, 0.5 };
real_t A[1*2] = { 1.0, 1.0 };
real_t g[2] = { 1.5, 1.0 };
real_t lb[2] = { 0.5, -2.0 };
real_t ub[2] = { 5.0, 2.0 };
real_t lbA[1] = { -1.0 };
real_t ubA[1] = { 2.0 };
/* Setup data of second QP. */
real_t g_new[2] = { 1.0, 1.5 };
real_t lb_new[2] = { 0.0, -1.0 };
real_t ub_new[2] = { 5.0, -0.5 };
real_t lbA_new[1] = { -2.0 };
real_t ubA_new[1] = { 1.0 };
/* Setting up QProblem object. */
QProblem example( 2,1 );
/* Solve first QP. */
int_t nWSR = 10;
example.init( H,g,A,lb,ub,lbA,ubA, nWSR );
/* Solve second QP. */
nWSR = 10;
example.hotstart( g_new,lb_new,ub_new,lbA_new,ubA_new, nWSR );
/* Get and print solution of second QP. */
real_t xOpt[2];
example.getPrimalSolution( xOpt );
printf( "\n xOpt = [ %e, %e ]; objVal = %e\n\n",
xOpt[0],xOpt[1],example.getObjVal() );
return 0;
}

为了通过QProblem类访问qpOASES软件包的功能,需要包含头文件qpOASES.hpp

  • 首先,主函数定义了两个非常小规模的QP的数据。
  • 然后,创建一个QProblem对象,然后将其初始化并求解第一个QP。
  • 最后,使用hotstart函数用于求解第二个QP。

你可能想知道使用命名空间qpOASES的命令,它就在主函数的最开始。使用它是因为qpOASES软件包的所有类、全局函数和变量都收集在一个名为qpOASES的公共命名空间中

设置自己的实例

设置自己的示例的最简单方法,可以使用现有示例作为模板。进行此操作时,请执行以下步骤:
1.复制现有示例:

cd <install-dir>/examples
cp example1.cpp yourexample.cpp

2.编辑示例Makefile:
打开文件<install-dir> / example / Makefile并添加新目标

yourexample: yourexample.o
@$fECHOg "Creating" $@
@$fCPPg $fDEF TARGETg $fCPPFLAGSg $< $fQPOASES LINKg $fLINK LIBRARIESg
(Do not forget to add its name to the all target.)

3.实现你自己的例子:
修改文件<install -dir> /examples/yourexample.cpp并使用make指令。 名为<install-dir> / bin / yourexample的可执行文件就会出现。

  • 15
    点赞
  • 118
    收藏
    觉得还不错? 一键收藏
  • 11
    评论
使用上一帧轨迹作为当前MPC轨迹规划的初始解时,可以将上一帧轨迹的控制量作为当前轨迹规划问题的初始值。然后,将此初始值传递给qpOASES求解器,在其内部使用此初始值进行优化求解。 具体实现时,可以使用qpOASES提供的方法`setPrimalInitialGuess()`和`setDualInitialGuess()`来设置初始解。其中,`setPrimalInitialGuess()`用于设置变量的初值,`setDualInitialGuess()`用于设置对偶变量的初值。 以下是一个简单的示例代码: ```c++ qpOASES::real_t x0[N]; // 上一帧轨迹的控制量 qpOASES::real_t lambda0[N]; // 对偶变量的初值 // 设置初始值 for (int i = 0; i < N; i++) { x0[i] = ...; // 设置上一帧轨迹的控制量 lambda0[i] = 0.0; // 对偶变量的初值一般设为0 } // 创建qpOASES求解器 qpOASES::QProblem qp(N, m); // 设置问题参数 qp.set...; // 设置初始值 qp.setPrimalInitialGuess(x0); qp.setDualInitialGuess(lambda0); // 求解问题 qp.init(...); qp.getPrimalSolution(...); qp.getDualSolution(...); ``` 在上述代码中,`N`表示控制量的数量,`m`表示约束条件的数量。`qpOASES::QProblem`是qpOASES求解器的一个类,用于创建和求解优化问题。在创建求解器时,需要指定控制量的数量和约束条件的数量。然后,可以使用`set...()`方法设置问题的参数,如目标函数、约束条件等。最后,使用`setPrimalInitialGuess()`和`setDualInitialGuess()`方法设置初始值,并使用`init()`方法初始化求解器。调用`getPrimalSolution()`和`getDualSolution()`方法获取优化结果。
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值