ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Eigen、OSQP与OSQP-Eigen:Linux下MPC求解环境搭建实战指南

Eigen、OSQP与OSQP-Eigen:Linux下MPC求解环境搭建实战指南 做机器人控制或者自动驾驶决策的同学应该都见过这三个库同时出现在一份安装教程里的场景Eigen、OSQP、OSQP-Eigen。搞过MPC模型预测控制的尤其熟悉因为OSQP是求解二次规划最快的那一档求解器而Eigen又是C里最顺手的线性代数库OSQP-Eigen刚好把两者缝起来让你用Eigen的矩阵直接喂给OSQP不用自己去写一堆类型转换。这篇文章就讲清楚这套组合在Linux下怎么装、装完怎么验证、以及我实际安装过程中踩过的坑。先说结论如果你用的是Ubuntu 20.04/22.04这类常见发行版照着apt加源码编译的混合方案大概二十分钟能搞定。如果你用的是CentOS、Arch这种或者需要在离线环境、嵌入式板子上装那本文后半部分的源码编译流程才是关键。整篇按安装前准备、各库安装、验证、常见问题四个环节来讲顺手附上一套可直接抄的CMake工程示例保证装完能立刻跑起来。1. 三个库分别解决什么问题为什么要一起装1.1 Eigen头文件式的线性代数模板库Eigen是一个纯头文件的C模板库提供矩阵、向量、线性求解、特征值分解等功能。它最大的特点是“header-only”也就是说不需要编译出独立的.so或者.a文件直接在代码里#include Eigen/Dense然后在CMake里把头文件路径指过去就行。因为没有二进制产物所以不存在链接库的麻烦这也是它能在各个平台活得这么滋润的原因。在控制、SLAM、计算机视觉这些领域Eigen基本是默认的基础依赖。它内部做了大量模板元编程优化性能非常能打而且语法跟MATLAB很像比如MatrixXd A; VectorXd b; x A.colPivHouseholderQr().solve(b);这种写法从MATLAB切过来的工程师几乎没有学习成本。1.2 OSQP专门为凸二次规划优化的求解器OSQPOperator Splitting Quadratic Program是一个求解凸二次规划问题的数值求解器。它用的算法是ADMM交替方向乘子法的一种定制版本专门针对大规模稀疏二次规划做了优化。跟传统的内点法相比OSQP的优势是迭代速度快、内存占用低尤其适合需要实时求解的场景比如模型预测控制每个控制周期都要解一个几十甚至上百维的优化问题。OSQP本身是用C语言写的所以它有一个很干净的C接口。但它原生的接口是这样的你要把目标函数里的P矩阵、q向量、约束矩阵A、上下界l和u全部以稀疏矩阵的格式填充到C风格的数据结构里然后调用osqp_setup()去配置问题。这个“填充数据”的过程写起来很痛苦一不小心行列索引就错了。1.3 OSQP-Eigen让Eigen和OSQP无缝衔接的桥接层OSQP-Eigen就是来解决上面这个痛点的。它提供了一层C封装让你可以直接用Eigen的稠密矩阵去定义Q、c、A然后用一个相对面向对象的接口去设置问题、求解、拿结果。它内部会负责将Eigen矩阵转换成OSQP需要的稀疏格式csc格式你基本感知不到这个转换过程。所以这三个库的关系可以理解成Eigen负责“描述数学对象”OSQP负责“真正算得快”OSQP-Eigen负责“让你别写一堆C接口胶水代码”。在Linux下装这套东西用包管理器装Eigen最省事而OSQP和OSQP-Eigen在某些发行版也有包但如果不是官方源的版本建议直接用源码编译版本可控、也能顺便把编译选项看明白。2. 安装前的准备确认系统环境与编译依赖2.1 确认你的Linux发行版和包管理器不同Linux发行版使用不同的包管理器安装系统依赖的命令差异很大。最常用的几类Ubuntu/Debian系列使用aptCentOS/RHEL/Fedora系列使用yum或dnfArch/Manjaro系列使用pacmanopenSUSE使用zypper这篇文章主要以Ubuntu为演示环境因为在实际开发中Ubuntu占比最高而且文档、报错信息、社区支持都是最全的。如果你的环境是CentOS思路完全一致把apt install换成yum install即可。2.2 Ubuntu下先更新软件源再装基础工具在装任何库之前第一步先更新软件源信息让系统知道最新可用的软件包版本sudo apt update sudo apt upgrade接下来安装编译需要的工具链sudo apt install -y build-essential cmake gitbuild-essential包含了gcc、g、make这一整套编译工具cmake是后面构建OSQP和OSQP-Eigen要用的构建工具git用于拉取源码和子模块。可能有人疑惑为什么Eigen不用编译后面还会用到C编译器呢因为验证工程、以及OSQP-Eigen编译时都需要一个完整的C编译环境所以这一步不能省。注意如果你是在Docker容器里操作基础镜像比如ubuntu:22.04通常没有安装任何东西这步尤其关键。我遇到过不止一次在干净容器里直接git clone然后cmake结果提示cmake: command not found然后又回头装依赖的情况。2.3 判断哪些库需要源码安装接下来要判断Eigen、OSQP、OSQP-Eigen这三个库哪些用包管理器装、哪些必须源码编译。我的建议是Eigen用系统包管理器安装方便且Eigen对版本要求不苛刻。OSQP源码编译因为很多系统源里的版本太旧或者干脆没有。源码编译还能直接指定最新的release版。OSQP-Eigen源码编译这个库相对小众系统源基本没有只能从GitHub拉源码。如果你用的系统是Ubuntu 20.04及以后版本官方源里已经有libeigen3-dev和libosqp-dev所以Eigen和OSQP都能用apt装。但我还是建议你OSQP别用apt装太旧的版本尤其是你后面要跑MPC这类对求解速度敏感的项目时新版本往往有性能优化和bug修复。3. Eigen的安装系统包管理器与源码两种方式3.1 用apt安装Eigen推荐在Ubuntu/Debian下一条命令就能搞定sudo apt install -y libeigen3-dev安装完成后Eigen的头文件路径在/usr/include/eigen3。验证一下ls /usr/include/eigen3/Eigen如果你能看到Dense、Core、Geometry这些目录说明安装成功。但这里有个小坑Eigen的头文件在/usr/include/eigen3/Eigen而很多工程或者旧版CMake模块默认找的是/usr/include/Eigen所以有时候你需要在CMakeLists里做一下路径映射或者直接把头文件软链过去sudo ln -s /usr/include/eigen3/Eigen /usr/include/Eigen这个软链操作不优雅但对某些老工程很管用。我遇到过一个第三方库死活找不到Eigen头文件的案例就是靠这个软链解决的。不过现代CMake的find_package(Eigen3)已经能正确处理这个路径所以非必要不推荐软链。3.2 源码安装Eigen什么时候需要如果你用的是CentOS这种源里Eigen版本很老的系统或者需要特定版本Eigen比如某个老工程卡在Eigen 3.3.x而系统源已经升到3.4.x那就用源码安装wget https://gitlab.com/libeigen/eigen/-/archive/3.4.0/eigen-3.4.0.tar.gz tar -xzf eigen-3.4.0.tar.gz cd eigen-3.4.0 mkdir build cd build cmake .. sudo make installmake install之后Eigen默认安装到/usr/local/include/eigen3CMake的find_package也会去/usr/local目录下找所以源码安装之后一般不需要额外配置。如果你不想污染系统目录也可以在cmake时指定-DCMAKE_INSTALL_PREFIX/your/custom/path然后在使用方工程里手动设置Eigen3_DIR为这个路径。cmake -DCMAKE_INSTALL_PREFIX$HOME/libs .. sudo make install3.3 验证Eigen是否可用建立一个临时工程目录写一个最简的C文件#include iostream #include Eigen/Dense int main() { Eigen::MatrixXd m(2, 2); m 1, 2, 3, 4; std::cout Eigen ok:\n m std::endl; return 0; }用g直接编译验证g -I /usr/include/eigen3 test_eigen.cpp -o test_eigen ./test_eigen能打印出矩阵Eigen就算装好了。4. OSQP的编译安装4.1 拉取源码和子模块OSQP使用了一些子模块最主要的是qdldl一个用于稀疏矩阵分解的小库所以克隆时必须带--recursive参数否则后面编译会报找不到头文件的错误git clone --recursive https://github.com/osqp/osqp.git cd osqp git checkout v0.6.3v0.6.3是我目前用下来比较稳定的版本。最新版本可以在GitHub releases页面查看如果你不需要最新特性建议跟随我这个版本走文档成熟、踩坑案例也多。4.2 编译安装的一般步骤mkdir build cd build cmake -DCMAKE_BUILD_TYPERelease .. cmake --build . sudo cmake --install .这套命令是CMake的标准流程。cmake --install .等价于make install但在CMake 3.15以上的版本里cmake --install是更推荐的方式因为它跟cmake --build形式保持一致也能支持--prefix临时指定安装目录。编译完成后OSQP的库文件会安装在/usr/local/lib里头文件在/usr/local/include/osqp。你可以看到这几个关键文件ls /usr/local/lib/libosqp* ls /usr/local/include/osqplibosqp.so是动态库链接时需要-losqp。头文件目录下主要是osqp.h以及一些内部头文件。4.3 处理子模块缺失的问题如果你之前克隆的时候忘记加--recursive编译时大概率会报qdldl_types.h: No such file or directory。这时候不用重新克隆进入OSQP源码目录更新子模块就行cd osqp git submodule update --init --recursive然后重新回到build目录再编译。这个报错我见过很多次尤其是在脚本里写git clone时容易漏参数。提示如果想编译静态库版本在cmake时加上-DBUILD_SHARED_LIBSOFF。静态库适合部署到不依赖目标机器环境的应用里但如果你主要做开发调试动态库更省事。5. OSQP-Eigen的编译安装5.1 依赖关系OSQP-Eigen依赖Eigen和OSQP所以必须先确保前面两个库已经装好。它通过CMake的find_package机制来查找这两个依赖所以如果它们没有安装在标准路径下就需要在cmake时用CMAKE_PREFIX_PATH指定搜索路径。5.2 源码编译安装git clone https://github.com/robotology/osqp-eigen.git cd osqp-eigen git checkout v0.6.6 mkdir build cd build cmake -DCMAKE_BUILD_TYPERelease .. make -j$(nproc) sudo make install-j$(nproc)是让make并行编译nproc会返回当前CPU核心数在八核机器上能快不少。如果你的机器内存不大比如虚拟机只给了2GB内存建议老老实实用make -j2不然编译中途可能内存爆掉。安装完成后OSQP-Eigen的头文件在/usr/local/include/OsqpEigen库文件在/usr/local/lib/libOsqpEigen.so。5.3 检查安装结果pkg-config --modversion osqp-eigen如果输出了版本号说明pkg-config的路径配置正常。但有些时候sudo make install不会把.pc文件拷贝到/usr/lib/pkgconfig下而是放到/usr/local/lib/pkgconfig这时你要手动把PKG_CONFIG_PATH加上export PKG_CONFIG_PATH/usr/local/lib/pkgconfig:$PKG_CONFIG_PATH这个环境变量最好写进~/.bashrc不然每次开新终端还得重新导。同样的道理如果你发现CMake找不到库也可以试试统一的CMAKE_PREFIX_PATHcmake -DCMAKE_PREFIX_PATH/usr/local ..6. 用一个实际工程验证整条链路6.1 编写测试代码装完库还不算完得实际编译一个工程跑起来才知道整个链条有没有真正打通。这里我给一个简单的OSQP-Eigen示例求解这个二次规划问题min 1/2 * x * P * x q * x s.t. l A * x u测试代码#include iostream #include Eigen/Dense #include OsqpEigen/OsqpEigen.h int main() { // 问题数据 Eigen::SparseMatrixdouble P(2, 2); Eigen::VectorXd q(2); Eigen::SparseMatrixdouble A(3, 2); Eigen::VectorXd l(3), u(3); // P [[4, 1], [1, 2]] P.insert(0, 0) 4.0; P.insert(0, 1) 1.0; P.insert(1, 0) 1.0; P.insert(1, 1) 2.0; q 1.0, 1.0; // 约束 A x 在 [l, u] 之间 A.insert(0, 0) 1.0; A.insert(1, 1) 1.0; A.insert(2, 0) 1.0; A.insert(2, 1) 1.0; l 0.0, 0.0, 1.0; u 1.0, 1.0, 1.0; OsqpEigen::Solver solver; solver.settings()-setVerbosity(true); solver.settings()-setWarmStart(true); if (!solver.initModel(P, q, A, l, u)) { std::cerr model init failed std::endl; return 1; } if (solver.solve() ! OsqpEigen::ErrorExitFlag::Optimal) { std::cerr solve failed std::endl; return 1; } Eigen::VectorXd solution solver.getSolution(); std::cout solution: solution.transpose() std::endl; return 0; }这个例子我最开始是从官方示例改的去掉了那些跟OSQP底层交互的部分直接走Solver类的高层接口。initModel负责把Eigen矩阵转成OSQP能吃的格式solve执行求解getSolution拿结果。6.2 编写CMakeLists.txtcmake_minimum_required(VERSION 3.10) project(osqp_eigen_test) find_package(Eigen3 REQUIRED) find_package(osqp REQUIRED) find_package(OsqpEigen REQUIRED) add_executable(quick_test quick_test.cpp) target_link_libraries(quick_test Eigen3::Eigen osqp::osqp OsqpEigen::OsqpEigen )这段CMake是OSQP-Eigen官方推荐的标准写法。三个target分别对应三个库。如果你在编译时找不到某个库大概率是安装路径不在CMake默认搜索范围内可以在cmake命令里加cmake -DEigen3_DIR/usr/share/eigen3/cmake \ -Dosqp_DIR/usr/local/lib/cmake/osqp \ -DOsqpEigen_DIR/usr/local/lib/cmake/OsqpEigen ..这三个*_DIR变量是CMake在find_package时用来定位配置文件的不同库的配置文件名不一样。Eigen3的配置文件通常在/usr/share/eigen3/cmakeOSQP和OSQP-Eigen的在/usr/local/lib/cmake下因为它们是make install到/usr/local的。6.3 编译并运行mkdir build cd build cmake .. make -j$(nproc) ./quick_test正常输出会包含OSQP迭代信息最后打印solution。如果你能看到一行类似status: solved的提示或者solution输出有两个数整套安装就成功了。7. 常见问题与排查技巧7.1 找不到头文件这类问题最常见报错往往是fatal error: Eigen/Dense: No such file or directory fatal error: osqp/osqp.h: No such file or directory fatal error: OsqpEigen/OsqpEigen.h: No such file or directory排查思路确认库真的装了ls /usr/include/eigen3、ls /usr/local/include/osqp确认编译命令或CMake里指定了正确路径Eigen在Ubuntu源里路径是/usr/include/eigen3而源码安装的路径是/usr/local/include/eigen3两者不冲突但CMake的搜索顺序可能不一样。检查CMake缓存有时候改动了环境变量后CMake还是用缓存里的旧路径。删除build目录重新配置比一个个修缓存快得多。7.2 链接阶段找不到库编译过了但链接时报undefined reference to osqp_*或者cannot find -lOsqpEigen。这种情况通常是头文件找到了但链接器没找到库文件。排查思路sudo ldconfig一下让动态链接库缓存刷新。确认库文件在标准路径ls /usr/local/lib/libosqp*如果库在非标准路径使用target_link_directories或者在CMake里加link_directories(/custom/lib/path)7.3 OSQP报“submodule not initialized”错误这个在前面提过是克隆时忘记--recursive导致的。解决方法是git submodule update --init --recursive如果你是实在没法访问GitHub或者网络拉不下来子模块可以手动下载qdldl源码放到osqp的qdldl目录下。但这个操作比较hack不推荐。7.4 Eigen版本冲突有些系统自带的Eigen是3.3.x但某些库比如装了最新OSQP-Eigen可能要求3.4以上。Ubuntu 22.04源里的libeigen3-dev版本是3.4.0一般够用。如果你的系统版本更老别用apt装Eigen了源码安装一个较新版本最省心。7.5 编译慢/内存不足OSQP-Eigen本身不算大但如果你是在1核2G的云服务器上编译别用-j$(nproc)老老实实make -j2甚至make。另外cmake --build .时加-- -j2也可以控制并行度。8. 我的安装习惯与最终建议关于这套组合我在不同的机器上装过不下十次说点个人经验。如果你是Ubuntu用户Eigen用apt装OSQP和OSQP-Eigen用源码编译这是组合最合适的方案。如果你用的是Ubuntu 22.04以上的版本apt里的libosqp-dev其实也能用版本不算太旧但OSQP-Eigen还是只能源码。另外建议把/usr/local相关的路径梳理清楚。很多库默认装到/usr/local而CMake对/usr/local和/usr的搜索优先级不同有时候你明明装好了却因为找不到而怀疑人生。我的习惯是源码安装时统一不指定CMAKE_INSTALL_PREFIX让它默认装到/usr/local这样最不会出问题如果确实需要装到自定义路径就在使用方工程里统一指定CMAKE_PREFIX_PATH不要依赖环境变量。最后分享一个小技巧每次装完库之后立刻用一个最小的验证程序跑一遍。这个验证程序平时不删就放在工程的test目录里。后面你升级系统、换电脑、或者在新环境部署CI时直接跑这个测试一分钟就能确认整条工具链是否可用省去了在项目代码里到处排查的时间。我的这个quick_test.cpp就是这么一直保留下来的实测在换新机器、配置CI镜像时帮了大忙。
返回列表