如何使用OSQP-Eigen

更多的关于Eigen的学习参考
更多的关于OSQP的学习参考
我们在github上找到了使用eigen库封装起来的OSQP库,比原来的OSQP写代码时更好用一些。
我们对照mathworks对quadprog的例子解释对库的使用

一个标准的QP问题可以以如下的标准形式进行描述:
在这里插入图片描述
注意这里面的写法不同的书本有些不同的表达:H , f 有时候也被写成 P, q.

接下来给出几个典型的不同约束的二次规划形式:

1,具有线性约束的二次规划

在这里插入图片描述
matlab的调用方法是:

H = [1 -1; -1 2]; 
f = [-2; -6];
A = [1 1; -1 2; 2 1];
b = [2; 2; 3];
%调用 quadprog。
[x,fval,exitflag,output,lambda] = ...
   quadprog(H,f,A,b);

可以看到,这里的等式约束没有就可以不写,solver会,默认不去管它。
OSQP-Eigen库的c++调用是:

  // 具有线性约束的二次规划
hessian.resize(2, 2);
  hessian.insert(0, 0) = 1;
  hessian.insert(1, 0) = -1;
  hessian.insert(0, 1) = -1;
  hessian.insert(1, 1) = 2;
  std::cout << "hessian:" << std::endl
            << hessian << std::endl;

  gradient.resize(2);
  gradient << -2, -6;

  std::cout << "gradient:" << std::endl
            << gradient << std::endl;

  linearMatrix.resize(3, 2);
  linearMatrix.insert(0, 0) = 1;
  linearMatrix.insert(0, 1) = 1;
  linearMatrix.insert(1, 0) = -1;
  linearMatrix.insert(1, 1) = 2;
  linearMatrix.insert(2, 0) = 2;
  linearMatrix.insert(2, 1) = 1;
  std::cout << "linearMatrix:" << std::endl
            << linearMatrix << std::endl;

  lowerBound.resize(3);
  lowerBound << -OsqpEigen::INFTY, -OsqpEigen::INFTY, -OsqpEigen::INFTY;
  std::cout << "lowerBound:" << std::endl
            << lowerBound << std::endl;

  upperBound.resize(3);
  upperBound << 2, 2, 3;
  std::cout << "upperBound:" << std::endl
            << upperBound << std::endl;

  int NumberOfVariables = 2;   //A矩阵的列数
  int NumberOfConstraints = 3; //A矩阵的行数
  

我们可以看到c++代码里面是需要声明不等式约束的上下界的,如果没有就写无穷:

lowerBound << -OsqpEigen::INFTY, -OsqpEigen::INFTY, -OsqpEigen::INFTY;

接下来,是调用c++ solver的通用部分:

  OsqpEigen::Solver solver;

  // settings
  //solver.settings()->setVerbosity(false);
  solver.settings()->setWarmStart(true);

  // set the initial data of the QP solver
  //矩阵A为m*n矩阵
  solver.data()->setNumberOfVariables(NumberOfVariables);     //设置A矩阵的列数,即n
  solver.data()->setNumberOfConstraints(NumberOfConstraints); //设置A矩阵的行数,即m
  if (!solver.data()->setHessianMatrix(hessian))
    return 1; //设置P矩阵
  if (!solver.data()->setGradient(gradient))
    return 1; //设置q or f矩阵。当没有时设置为全0向量
  if (!solver.data()->setLinearConstraintsMatrix(linearMatrix))
    return 1; //设置线性约束的A矩阵
  if (!solver.data()->setLowerBound(lowerBound))
    return 1; //设置下边界
  if (!solver.data()->setUpperBound(upperBound))
    return 1; //设置上边界

  // instantiate the solver
  if (!solver.initSolver())
    return 1;

  Eigen::VectorXd QPSolution;

  // solve the QP problem
  if (!solver.solve())
    return 1;

  // get the controller input
  clock_t time_start = clock();
  clock_t time_end = clock();
  time_start = clock();
  QPSolution = solver.getSolution();
  time_end = clock();
  std::cout << "time use:" << 1000 * (time_end - time_start) / (double)CLOCKS_PER_SEC << "ms" << std::endl;

  std::cout << "QPSolution:" << std::endl
            << QPSolution << std::endl;

具有线性等式约束的二次规划

在这里插入图片描述
matlab的求解方式写成:

[x,fval,exitflag,output,lambda] = ...
   quadprog(H,f,[],[],Aeq,beq);

注意,这里没有不等式约束就写上[], [], 后面再接上等式约束。

/
  具有线性等式约束的二次规划
      hessian.resize(2,2);
      hessian.insert(0,0) = 1;
      hessian.insert(1,0) = -1;
      hessian.insert(0,1) = -1;
      hessian.insert(1,1) = 2;
      std::cout << "hessian:" << std::endl << hessian << std::endl;

      gradient.resize(2);
      gradient << -2, -6;

      std::cout << "gradient:" << std::endl << gradient << std::endl;

      linearMatrix.resize(1,2);
      linearMatrix.insert(0,0) = 1;
      linearMatrix.insert(0,1) = 1;
      std::cout << "linearMatrix:" << std::endl << linearMatrix << std::endl;

      lowerBound.resize(1);
      lowerBound << 0;
      std::cout << "lowerBound:" << std::endl << lowerBound << std::endl;

      upperBound.resize(1);
      upperBound << 0;
      std::cout << "upperBound:" << std::endl << upperBound << std::endl;

      int NumberOfVariables = 2; //A矩阵的列数
      int NumberOfConstraints = 1; //A矩阵的行数

我们可以看到,c++对等式约束的实现就是把上下界都写成同一个数字:
并且不等式约束和等式约束都要放在A矩阵中。

  lowerBound.resize(1);
  lowerBound << 0;
  upperBound.resize(1);
  upperBound << 0;

具有线性约束和边界的二次最小化

在这里插入图片描述
matlab的求解是:

x = quadprog(H,f,[],[],Aeq,beq,lb,ub)

依旧是没有不等式约束,就写成 [ ], [ ].
c++ 的写法为:

hessian.resize(3,3);
  hessian.insert(0,0) = 1;
  hessian.insert(1,0) = -1;
  hessian.insert(2,0) = 1;
  hessian.insert(0,1) = -1;
  hessian.insert(1,1) = 2;
  hessian.insert(2,1) = -2;
  hessian.insert(0,2) = 1;
  hessian.insert(1,2) = -2;
  hessian.insert(2,2) = 4;
  std::cout << "hessian:" << std::endl << hessian << std::endl;

  gradient.resize(3);
  gradient << 2, -3, 1;

  std::cout << "gradient:" << std::endl << gradient << std::endl;

  linearMatrix.resize(4,3);
  linearMatrix.insert(0,0) = 1;
  linearMatrix.insert(1,0) = 0;
  linearMatrix.insert(2,0) = 0;
  linearMatrix.insert(3,0) = 1;

  linearMatrix.insert(0,1) = 0;
  linearMatrix.insert(1,1) = 1;
  linearMatrix.insert(2,1) = 0;
  linearMatrix.insert(3,1) = 1;

  linearMatrix.insert(0,2) = 0;
  linearMatrix.insert(1,2) = 0;
  linearMatrix.insert(2,2) = 1;
  linearMatrix.insert(3,2) = 1;
  std::cout << "linearMatrix:" << std::endl << linearMatrix << std::endl;

  lowerBound.resize(4);
  lowerBound << 0, 0, 0, 0.5;
  std::cout << "lowerBound:" << std::endl << lowerBound << std::endl;

  upperBound.resize(4);
  upperBound << 1, 1, 1, 0.5;
  std::cout << "upperBound:" << std::endl << upperBound << std::endl;

  int NumberOfVariables = 3; //A矩阵的列数
  int NumberOfConstraints = 4; //A矩阵的行数

可以看到打印出来的 LinearMatrix A需要包含等式约束和不等式约束:

1 0 0 
0 1 0 
0 0 1 
1 1 1 
lowerBound:
  0
  0
  0
0.5
upperBound:
  1
  1
  1
0.5

这个里面就包含了前三个不等式约束,和最有一个相加为0.5的等式约束了。

最后我们验证一个比较有用的问题的解法:接一个带约束的五阶多项式问题:
这个问题的描述已经在之前的blog里面做了详细解释
最后文章也通过matlab quadprog得出了解,这里我们用c++再做一次实现:

hessian.resize(6, 6);
  hessian1.resize(6, 6);
  hessian2.resize(6, 6);
  double Te = 5;
  double d0 = 1;
  double v0 = 0.2;
  double a0 = 0;
  double LB = 2.5;
  double UB = 3.5;

  for (int i = 0; i < 6; i++)
  {
    hessian1.insert(0, i) = pow(Te, (i + 1)) / (i + 1);
    hessian1.insert(1, i) = pow(Te, (i + 2)) / (i + 2);
    hessian1.insert(2, i) = pow(Te, (i + 3)) / (i + 3);
    hessian1.insert(3, i) = pow(Te, (i + 4)) / (i + 4);
    hessian1.insert(4, i) = pow(Te, (i + 5)) / (i + 5);
    hessian1.insert(5, i) = pow(Te, (i + 6)) / (i + 6);
  }

  for (int i = 0; i < 6; i++)
  {
    for (int j = 0; j < 3; j++)
    {
      hessian2.insert(j, i) = 0;
    }
  }

  for (int i = 3; i < 6; i++)
  {
    for (int j = 0; j < 3; j++)
    {
      hessian2.insert(i, j) = 0;
    }
  }
  hessian2.insert(3, 3) = 36 * Te;
  hessian2.insert(3, 4) = 72 * Te * Te;
  hessian2.insert(3, 5) = 120 * Te * Te * Te;
  hessian2.insert(4, 3) = 72 * Te * Te;
  hessian2.insert(4, 4) = 192 * Te * Te * Te;
  hessian2.insert(4, 5) = 360 * Te * Te * Te * Te;
  hessian2.insert(5, 3) = 120 * Te * Te * Te;
  hessian2.insert(5, 4) = 360 * Te * Te * Te * Te;
  hessian2.insert(5, 5) = 720 * Te * Te * Te * Te * Te;

  hessian = hessian1 + hessian2;
  // hessian.insert(0, 0) = 1;
  // hessian.insert(1, 0) = -1;
  // hessian.insert(0, 1) = -1;
  // hessian.insert(1, 1) = 2;
  std::cout
      << "hessian:" << std::endl
      << hessian << std::endl;

  gradient.resize(6);
  gradient << 0, 0, 0, 0, 0, 0;

  std::cout << "gradient:" << std::endl;
  std::cout << gradient << std::endl;

  linearMatrix.resize(6, 6);
  // the first row
  for (int i = 0; i < 6; i++)
  {
    linearMatrix.insert(0, i) = pow(Te, i);
  }
  //the second row
  for (int i = 1; i < 6; i++)
  {
    linearMatrix.insert(1, i) = 0;
  }
  linearMatrix.insert(1, 0) = 1;

  // the third row
  for (int i = 2; i < 6; i++)
  {
    linearMatrix.insert(2, i) = 0;
  }
  linearMatrix.insert(2, 0) = 0;
  linearMatrix.insert(2, 1) = 1;
  // the fourth row
  linearMatrix.insert(3, 0) = 0;
  linearMatrix.insert(3, 1) = 1;
  linearMatrix.insert(3, 2) = 2 * Te;
  linearMatrix.insert(3, 3) = 3 * Te * Te;
  linearMatrix.insert(3, 4) = 4 * Te * Te * Te;
  linearMatrix.insert(3, 5) = 5 * Te * Te * Te * Te;
  // the fifth row
  linearMatrix.insert(4, 0) = 0;
  linearMatrix.insert(4, 1) = 0;
  linearMatrix.insert(4, 2) = 2;
  linearMatrix.insert(4, 3) = 0;
  linearMatrix.insert(4, 4) = 0;
  linearMatrix.insert(4, 5) = 0;
  // the sixth row
  linearMatrix.insert(5, 0) = 0;
  linearMatrix.insert(5, 1) = 0;
  linearMatrix.insert(5, 2) = 2;
  linearMatrix.insert(5, 3) = 6 * Te;
  linearMatrix.insert(5, 4) = 12 * Te * Te;
  linearMatrix.insert(5, 5) = 20 * Te * Te * Te;

  std::cout << "linearMatrix:" << std::endl
            << linearMatrix << std::endl;

  lowerBound.resize(6);
  // lowerBound << -OsqpEigen::INFTY, -OsqpEigen::INFTY, -OsqpEigen::INFTY;
  lowerBound << (d0 + LB), d0, v0, 0, a0, 0;
  std::cout << "lowerBound:" << std::endl;
  std::cout << lowerBound << std::endl;

  upperBound.resize(6);
  upperBound << (d0 + UB), d0, v0, 0, a0, 0;
  std::cout << "upperBound:" << std::endl;
  std::cout << upperBound << std::endl;

  int NumberOfVariables = 6;   //A矩阵的列数
  int NumberOfConstraints = 6; //A矩阵的行数

得出的解为:

iter   objective    pri res    dua res    rho        time
   1   0.0000e+00   3.50e+00   6.96e+05   1.00e-01   1.58e-04s
  75   1.6280e+01   2.74e-10   6.54e-07   3.46e+01   2.68e-04s

status:               solved
number of iterations: 75
optimal objective:    16.2803
run time:             2.84e-04s
optimal rho estimate: 4.40e+00

time use:0.008ms
QPSolution:
           1
         0.2
-1.71031e-17
       0.152
     -0.0472
     0.00384

如果有兴趣可以关注下里面的数据:比如dual-residual 对偶间隙,看得出是通过对偶问题的方式求解的。
我们获得了和matlab一样的解,对比matlab和c++的求解效率,使用同一个硬件平台进行比较:

c++: time use:0.008ms
matlab: tiem use:80.8ms

最后是一些编译上的问题:

1 编译osqp-eigen库时报下面的错误:
CMake Error at cmake/OsqpEigenDependencies.cmake:12 (find_package):
 Could not find a configuration file for package "Eigen3" that is compatible
 with requested version "3.2.92".

 The following configuration files were considered but not accepted:

   /usr/lib/cmake/eigen3/Eigen3Config.cmake, version: unknown

Call Stack (most recent call first):
 CMakeLists.txt:63 (include)

是Eigen库安装的问题,建议重装eigen:

step1:卸载eigen3(如果已经是最新的,就不要卸载了)
sudo rm -rf /usr/include/eigen3
sudo rm -rf /usr/lib/cmake/eigen3

step2: 安装最新eigen3
https://gitlab.com/libeigen/eigen/-/releases/3.3.9

下载tar.gz到任意位置,然后在保存文件的位置执行:

tar -zxvf eigen-3.3.9.tar.gz
cd eigen-3.3.9/     (这里是你的版本号,2021.3.21为止的最高版本为3.9)
 
mkdir build
 
cd build
 
cmake ..
 
sudo make
 
sudo make install
step2: 拷贝移动文件位置,需要让你的usr/local/include下面直接有eigen3, 一般安装完是不会有的,而我们调用
#include<Eigen/Dense>的时候,编译器会默认去这个地方找Eigen,除非你已经写好了Eigen的搜索位置
这一步很重要:复制Eigen库到 /usr/local/include 中 (这一步很重要,一定要执行,否则后面编译一些程序,会提示没有Eigen库)
sudo cp -r /usr/local/include/eigen3/Eigen /usr/local/include

然后编译
cd osqp-eigen
mkdir build && cd build
cmake ..
make
[sudo] make install

最后提供一个测试版本,如果正常运行就说明安装成功了:
cd osqp-eigen/example
mkdir build && cd build
cmake ..
make

执行
./SimpleExample

其他的问题可以看到这个博文

  • 18
    点赞
  • 96
    收藏
    觉得还不错? 一键收藏
  • 9
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值