【VS2022 配置 casadi 求解 NLP问题】
1. 前言
最近在研究 船舶NMPC 控制时,主要是非线性约束的凸二次优化问题属于NLP问题,找到了 casadi 这个求解库,最初是在MATLAB平台上搭建的,虽然最终的运行效果挺好,但是团队现在软件是基于Visual Studio From架构设计的测试软件,之后再系统的移植过去。
- 开发工具:Visual Studio 2022(.NET Framework 4.7.2+)环境搭建参考博客:【VS2019安装+QT配置】
给大家看看PID控制小船跑的几种常见轨迹,后续会更新其他控制效果:
源码地址:https://download.csdn.net/download/VOR234/90890781
1.1 来回扁椭圆
1.2 椭圆轨迹
1.3 月牙形轨迹
2. 部署优化求解工具 casadi
下载 ipopt 的 vs2022 预编译程序
在 ipopt 的 github 官方仓库中找到 vs2022 的预编译版本,就是基于 vs2022 已经编译好的,不需要自行编译。可以直接选择较新的版本,记得要选择后面带有 msvs2022 的压缩包
3. 下载 casadi 的源码
在 casadi 的 github 官方仓库中找到适合的版本,下载对应的 Source Code 压缩包即可
4. 组合编译casadi和ipopt
由于下载的 ipopt 不需要编译,所以就只需要编译 casadi 即可。把上述的 ipopt 和 casadi 的源码放到一个不会被删掉的地方,建议是有一个比较统一存放 C++ 依赖库的地方,如下图所示
然后在 casadi 的目录下新建一个 install 目录
下,并且在 casadi 目录下新建一个 run.bat
文件,这个文件中需要写入 casadi 的编译和安装脚本,但是一般双击 .bat 文件时会直接运行,所以可以先新建一个 run.txt 文件,之后再重命名即可。在文件中写入如下指令
cmake -G "Visual Studio 17 2022" -A x64 -B build -DWITH_IPOPT=ON -DIPOPT_LIBRARIES:FILEPATH="ipopt的目录/lib/ipopt.dll.lib" -DIPOPT_INCLUDE_DIRS:PATH="ipopt的目录/include/coin-or" -DCMAKE_INSTALL_PREFIX:PATH="casadi的目录/install"
cmake --build build --config Release
cmake --install build
下面是我自己的配置参考
cmake -G "Visual Studio 17 2022" -A x64 -B build -DWITH_IPOPT=ON -DIPOPT_LIBRARIES:FILEPATH="E:/cpplibrary/Ipopt/lib/ipopt.dll.lib" -DIPOPT_INCLUDE_DIRS:PATH="E:/cpplibrary/Ipopt/include/coin-or" -DCMAKE_INSTALL_PREFIX:PATH="E:/cpplibrary/casadi/install"
cmake --build build --config Release
cmake --install build
后面demo只支持Release x64版本,进行二次封装可以解除版本限制就可以支持Debug x64版本
之后双击运行,等待其编译安装结束
打开install文件夹后,就会生成casadi的release版本的调用DLL库
5. 在 vs 中配置 casadi
编译完成之后需要在 vs2022 的 IDE 中进行配置。就可以看到在 Release x64 ,然后点击项目右键打开其属性,开始编辑Visual Studio2022项目属性
先新建项目
选C++控制台应用,点击下一步
项目名称取NLP
项目程序是打印Hello world,可以基于release x64环境运行,终端窗口打印可以Hello world,就可以开始配置Casadi 非线性优化问题了,
5.1 包含目录
位于 VC++ 目录下,在其中添加新项目,如下
casadi存放目录\install\casadi\include
casadi存放目录\install\casadi
5.2 外部包含目录
位于 VC++ 目录下,在其中添加新项目
casadi存放目录\install\casadi
casadi存放目录\install\casadi\include
5.3 库目录
位于 VC++ 目录下,在其中添加新项目
casadi存放目录\build\Release
casadi存放目录
5.4 附加依赖项
位于链接器-输入下,在其中添加新项目,如下
casadi.lib
最后点击应用
6. 火箭发射模型
casadi求解器的C++demo在源码路径中E:\cpplibrary\casadi\docs\examples\cplusplus
我们这次采用rocket_ipopt.cpp
实例
6.1 rocket_ipopt.cpp
实例
将其源码复制到Hello world代码中
/*
* MIT No Attribution
*
* Copyright (C) 2010-2023 Joel Andersson, Joris Gillis, Moritz Diehl, KU Leuven.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this
* software and associated documentation files (the "Software"), to deal in the Software
* without restriction, including without limitation the rights to use, copy, modify,
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
#include <iostream>
#include <fstream>
#include <ctime>
#include <casadi/casadi.hpp>
using namespace casadi;
int main(){
std::cout << "program started" << std::endl;
// Dimensions
int nu = 20; // Number of control segments
int nj = 100; // Number of integration steps per control segment
// optimization variable
SX u = SX::sym("u", nu); // control
SX s_0 = 0; // initial position
SX v_0 = 0; // initial speed
SX m_0 = 1; // initial mass
SX dt = 10.0/(nj*nu); // time step
SX alpha = 0.05; // friction
SX beta = 0.1; // fuel consumption rate
// Trajectory
std::vector<SX> s_k, v_k, m_k;
// Integrate over the interval with Euler forward
SX s = s_0, v = v_0, m = m_0;
for(int k=0; k<nu; ++k){
for(int j=0; j<nj; ++j){
s += dt*v;
v += dt / m * (u(k)- alpha * v*v);
m += -dt * beta*u(k)*u(k);
}
s_k.push_back(s);
v_k.push_back(v);
m_k.push_back(m);
}
SX s_all=vertcat(s_k), v_all=vertcat(v_k), m_all=vertcat(m_k);
// Objective function
SX f = dot(u, u);
// Terminal constraints
SX g = vertcat(s, v, v_all);
// Create the NLP
SXDict nlp = {{"x", u}, {"f", f}, {"g", g}};
// Allocate an NLP solver and buffers
Function solver = nlpsol("solver", "ipopt", nlp);
// Bounds on g
std::vector<double> gmin = {10, 0};
std::vector<double> gmax = {10, 0};
gmin.resize(2+nu, -std::numeric_limits<double>::infinity());
gmax.resize(2+nu, 1.1);
// Solve the problem
DMDict arg = {{"lbx", -10},
{"ubx", 10},
{"x0", 0.4},
{"lbg", gmin},
{"ubg", gmax}};
DMDict res = solver(arg);
// Print the optimal cost
double cost(res.at("f"));
std::cout << "optimal cost: " << cost << std::endl;
// Print the optimal solution
std::vector<double> uopt(res.at("x"));
std::cout << "optimal control: " << uopt << std::endl;
// Get the state trajectory
Function xfcn("xfcn", {u}, {s_all, v_all, m_all});
std::vector<double> sopt, vopt, mopt;
xfcn({uopt}, {&sopt, &vopt, &mopt});
std::cout << "position: " << sopt << std::endl;
std::cout << "velocity: " << vopt << std::endl;
std::cout << "mass: " << mopt << std::endl;
// Create Matlab script to plot the solution
std::ofstream file;
std::string filename = "rocket_ipopt_results.m";
file.open(filename.c_str());
file << "% Results file from " __FILE__ << std::endl;
file << "% Generated " __DATE__ " at " __TIME__ << std::endl;
file << std::endl;
file << "cost = " << cost << ";" << std::endl;
file << "u = " << uopt << ";" << std::endl;
// Save results to file
file << "t = linspace(0,10.0," << nu << ");"<< std::endl;
file << "s = " << sopt << ";" << std::endl;
file << "v = " << vopt << ";" << std::endl;
file << "m = " << mopt << ";" << std::endl;
// Finalize the results file
file << std::endl;
file << "% Plot the results" << std::endl;
file << "figure(1);" << std::endl;
file << "clf;" << std::endl << std::endl;
file << "subplot(2,2,1);" << std::endl;
file << "plot(t,s);" << std::endl;
file << "grid on;" << std::endl;
file << "xlabel('time [s]');" << std::endl;
file << "ylabel('position [m]');" << std::endl << std::endl;
file << "subplot(2,2,2);" << std::endl;
file << "plot(t,v);" << std::endl;
file << "grid on;" << std::endl;
file << "xlabel('time [s]');" << std::endl;
file << "ylabel('velocity [m/s]');" << std::endl << std::endl;
file << "subplot(2,2,3);" << std::endl;
file << "plot(t,m);" << std::endl;
file << "grid on;" << std::endl;
file << "xlabel('time [s]');" << std::endl;
file << "ylabel('mass [kg]');" << std::endl << std::endl;
file << "subplot(2,2,4);" << std::endl;
file << "plot(t,u);" << std::endl;
file << "grid on;" << std::endl;
file << "xlabel('time [s]');" << std::endl;
file << "ylabel('Thrust [kg m/s^2]');" << std::endl << std::endl;
file.close();
std::cout << "Results saved to \"" << filename << "\"" << std::endl;
return 0;
}
6.2 添加动态链接库文件
动态链接库 dll 文件是运行 exe 文件时实时调用的,因此需要将dll文件复制粘贴到生成的 exe 文件同级目录C:\Users\DMU\source\repos\nlp\x64\Release
,将编译生成的 casadi.dll 以及 casadi_nlpsol_ipopt.dll 文件粘贴到和生成的 exe 文件同级的C:\Users\DMU\source\repos\nlp\x64\Release
文件夹内
一般这两个文件都存在于 casadi 的install安装目录下,即 casadi存放目录\install\casadi
6.3 配置环境变量
添加过链接库之后,还会提示找不到 casadi_nlpsol_ipopt.dll 文件的错误,就需要将 ipopt 的程序路径添加到系统环境变量中
6.4 运行结果
配置Release x64环境,一切都配置好后点击运行
运行结果如下,我的电脑运行还是非常快滴🤞🤞🤞
NOTE: Other linear solvers might be more efficient (see Ipopt documentation).
Number of nonzeros in equality constraint Jacobian...: 40
Number of nonzeros in inequality constraint Jacobian.: 210
Number of nonzeros in Lagrangian Hessian.............: 210
Total number of variables............................: 20
variables with only lower bounds: 0
variables with lower and upper bounds: 20
variables with only upper bounds: 0
Total number of equality constraints.................: 2
Total number of inequality constraints...............: 20
inequality constraints with only lower bounds: 0
inequality constraints with lower and upper bounds: 0
inequality constraints with only upper bounds: 20
iter objective inf_pr inf_du lg(mu) ||d|| lg(rg) alpha_du alpha_pr ls
0 3.2000000e+00 6.01e+00 7.73e-02 -1.0 0.00e+00 - 0.00e+00 0.00e+00 0
1 2.6179074e+00 2.76e+00 4.77e+01 -1.0 2.21e+00 - 1.09e-02 5.35e-01f 1
2 4.1657553e+00 5.88e-01 1.18e+01 -1.0 7.57e-01 - 5.81e-01 7.51e-01h 1
3 5.0636497e+00 3.55e-02 1.14e+00 -1.0 3.45e-01 - 9.82e-01 9.04e-01h 1
4 5.0061347e+00 1.84e-03 1.04e-02 -1.0 1.13e-01 - 1.00e+00 1.00e+00f 1
5 4.3948828e+00 1.10e-02 8.37e-01 -2.5 2.39e-01 - 6.20e-01 1.00e+00f 1
6 4.1377025e+00 2.44e-03 1.83e-02 -2.5 1.11e-01 - 1.00e+00 1.00e+00f 1
7 4.0166442e+00 3.62e-04 7.13e-02 -3.8 4.27e-02 - 7.51e-01 1.00e+00f 1
8 4.0011959e+00 1.63e-05 7.41e-05 -3.8 9.52e-03 - 1.00e+00 1.00e+00h 1
9 3.9985771e+00 1.94e-07 1.21e-06 -5.7 9.59e-04 - 1.00e+00 1.00e+00h 1
iter objective inf_pr inf_du lg(mu) ||d|| lg(rg) alpha_du alpha_pr ls
10 3.9985414e+00 3.20e-11 1.97e-10 -8.6 1.20e-05 - 1.00e+00 1.00e+00h 1
Number of Iterations....: 10
(scaled) (unscaled)
Objective...............: 3.9985414424319439e+00 3.9985414424319439e+00
Dual infeasibility......: 1.9744241264419726e-10 1.9744241264419726e-10
Constraint violation....: 3.2043783425306227e-11 3.2043783425306227e-11
Complementarity.........: 2.6648571233142114e-09 2.6648571233142114e-09
Overall NLP error.......: 2.6648571233142114e-09 2.6648571233142114e-09
Number of objective function evaluations = 11
Number of objective gradient evaluations = 11
Number of equality constraint evaluations = 11
Number of inequality constraint evaluations = 11
Number of equality constraint Jacobian evaluations = 11
Number of inequality constraint Jacobian evaluations = 11
Number of Lagrangian Hessian evaluations = 10
Total CPU secs in IPOPT (w/o function evaluations) = 0.059
Total CPU secs in NLP function evaluations = 0.041
EXIT: Optimal Solution Found.
solver : t_proc (avg) t_wall (avg) n_eval
nlp_f | 0 ( 0) 15.30us ( 1.39us) 11
nlp_g | 0 ( 0) 833.10us ( 75.74us) 11
nlp_grad_f | 0 ( 0) 58.10us ( 4.84us) 12
nlp_hess_l | 30.00ms ( 3.00ms) 27.38ms ( 2.74ms) 10
nlp_jac_g | 11.00ms (916.67us) 9.10ms (758.09us) 12
total | 102.00ms (102.00ms) 101.97ms (101.97ms) 1
optimal cost: 3.99854
optimal control: [1.32045, 0.701825, 0.138416, 0.0605, 0.0605, 0.0605, 0.0605, 0.0605, 0.0605, 0.0605, 0.0605, 0.0605, 0.0605, 0.0605, 0.0605, 0.0605, 0.0605, -0.00694958, -0.601974, -1.15307]
position: [0.167788, 0.602475, 1.14116, 1.69116, 2.24116, 2.79116, 3.34116, 3.89116, 4.44116, 4.99116, 5.54116, 6.09116, 6.64116, 7.19116, 7.74116, 8.29116, 8.84116, 9.38191, 9.82234, 10]
velocity: [0.686414, 1.05473, 1.1, 1.1, 1.1, 1.1, 1.1, 1.1, 1.1, 1.1, 1.1, 1.1, 1.1, 1.1, 1.1, 1.1, 1.1, 1.06302, 0.696855, -3.20438e-11]
mass: [0.912821, 0.888193, 0.887235, 0.887052, 0.886869, 0.886686, 0.886503, 0.88632, 0.886137, 0.885954, 0.885771, 0.885588, 0.885405, 0.885222, 0.885039, 0.884856, 0.884673, 0.884671, 0.866552, 0.800073]
Results saved to "rocket_ipopt_results.m"
C:\Users\DMU\source\repos\nlp\x64\Release\nlp.exe (进程 42480)已退出,代码为 0。
要在调试停止时自动关闭控制台,请启用“工具”->“选项”->“调试”->“调试停止时自动关闭控制台”。
按任意键关闭此窗口. . .
7 总结
恭喜你,学习了VS2022 配置 casadi 求解 NLP问题,从而实现对外部世界进行感知,充分认识这个有机与无机的环境,科学地合理地进行创作和发挥效益,然后为人类社会发展贡献一点微薄之力。🤣🤣🤣
- 我会持续更新对应专栏博客,非常期待你的三连!!!🎉🎉🎉
- 如果鹏鹏有哪里说的不妥,还请大佬多多评论指教!!!👍👍👍
- 下面有我的🐧🐧🐧群推广,欢迎志同道合的朋友们加入,期待与你的思维碰撞😘😘😘