ARTICLE DETAIL

资讯详情

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

Ubuntu下安装Eigen 3.3.7:源码编译与CMake版本锁定实战指南

Ubuntu下安装Eigen 3.3.7:源码编译与CMake版本锁定实战指南 上周有个朋友发消息问我CMake 报错说系统里的 Eigen 是 3.4.0但项目锁死了必须用 3.3.7怎么在 Ubuntu 上装这个旧版本。我第一反应是“这东西不是 apt 装一下就行吗”等他甩过来报错截图我才意识到问题没那么简单——他用的 Ubuntu 22.04 默认源里只有 3.4.0压根没有 3.3.7。这大概也是你能搜到这篇文章的原因。Eigen 是 C 里最常用的线性代数模板库做矩阵运算、求解器、几何变换都离不开它在机器人、SLAM、自动驾驶、物理仿真、点云处理这些方向几乎是人手一份。它最大的特点是 header-only也就是说它不需要像 OpenCV 那样编译一个 .so 动态库核心工作就是“把一堆头文件放到编译器能找到的地方”。听起来很简单但真的操作起来版本冲突、路径优先级、CMake 找错版本这些问题一个比一个折腾。这篇文章我就按自己实际踩坑的顺序把 Ubuntu 上安装 Eigen 3.3.7 的完整思路和步骤捋一遍不管你是 20.04 还是 22.04、24.04都能找到对应的方案。1. 为什么偏偏是 3.3.7版本与依赖的现实问题1.1 先搞清楚 Eigen 是什么安装到底意味着什么Eigen 不是一个需要编译成二进制库的软件包它是一整套模板头文件。你在代码里写#include Eigen/Dense编译器在预处理阶段展开这些头文件把所有模板实现直接编译进你的程序里。所以“安装 Eigen”这个概念本质上就是“把 Eigen 的头文件目录放到编译器默认搜索路径下”以及“把它的 CMake 配置文件放好让 find_package 能定位到”。这一点很重要因为很多人会惯性思维地问“Eigen 装完要不要 ldconfig”“需不需要加到 LD_LIBRARY_PATH”答案都是不用。Eigen 没有共享库这些操作对一个 header-only 库来说完全没有意义。真正需要关心的只有两件事头文件路径对不对CMake 配置版本对不对。1.2 3.3.7 的江湖地位为什么到现在还有人找这个版本Eigen 3.3.7 是 2019 年 3 月发布的属于 3.3 系列的后期版本。虽然 3.3.8 后来也发布过但很多项目从诞生起就锁定在 3.3.7尤其是以下这几类场景非常常见ROS 生态里的老项目比如某些 Ubuntu 20.04 ROS Noetic 的组合教程里写的就是 3.3.7照着重现最稳。视觉 SLAM 和机器人算法仓库很多仓库的 CMakeLists.txt 里直接用find_package(Eigen3 3.3.7 EXACT REQUIRED)锁死版本源码作者开发时用的就是 3.3.7升级到 3.4.0 之后某些 API 行为有细微差别会有潜在风险。芯片原厂的 SDK 与文档部分嵌入式、自动驾驶域控制器的 SDK 文档直接写“依赖 Eigen 3.3.7”工程师照着文档装不敢随便用新版。换句话说3.3.7 就是那种“不会出错”的基准版本。它本身没有多惊艳但大量存量项目都锚定在它上面。1.3 Ubuntu 不同版本自带 Eigen 版本对照这一步非常关键。很多人在 22.04 上直接apt install libeigen3-dev装完发现是 3.4.0然后开始怀疑人生。我先给你一张对照表省得再去翻Ubuntu 版本代号apt 默认 Eigen 版本18.04bionic3.3.420.04focal3.3.722.04jammy3.4.024.04noble3.4.0结论很直接如果你用的是 Ubuntu 20.04直接 apt 装就是 3.3.7最省事。如果你用的是 22.04 或 24.04apt 拿不到 3.3.7要么走源码编译要么想办法指定旧源但旧源容易把系统依赖搞乱所以源码编译是更干净的选择。这些方案我在后面会完整展开。2. 动手前先摸清家底查版本、查冲突、定方案很多安装翻车案例问题不在安装本身而在动手之前没搞清楚系统里已经有什么。Eigen 这玩意特别容易被“预装”而且不是通过 dpkg 管理的预装比如 ROS、conda、某些 SDK 都可能自带一份。不排查清楚直接装新版很容易出现“头文件用的是 A 路径的 3.4.0CMake 配置用的是 B 路径的 3.3.7”这种精神分裂状态。2.1 确认你的 Ubuntu 版本和系统架构先看看系统是什么版本这直接决定安装策略cat /etc/os-release lsb_release -a uname -m我见过不少人想装 3.3.7结果发现系统是 20.04那其实一条 apt 命令就解决了根本不用折腾源码。所以第一步永远是先确认版本别急着下载源码包。2.2 系统里已经有 Eigen 了吗用下面这组命令盘点一下现状# 查 dpkg 管理的 Eigen dpkg -l | grep -i eigen # 查常见路径下是否已有头文件 ls /usr/include/eigen3 2/dev/null ls /usr/local/include/eigen3 2/dev/null # 查 pkg-config 是否能定位到 pkg-config --modversion eigen3 2/dev/null # 查 CMake 能找到的版本 cmake --find-package -DNAMEEigen3 -DCOMPILER_IDGNU -DLANGUAGECXX -DMODEEXIST 2/dev/null如果你的系统里同时存在/usr/include/eigen3和/usr/local/include/eigen3那你就要格外小心了这两个路径默认都会被编译器搜索且优先级不同。这个问题我在第 5 章会专门讲。2.3 查看已有 Eigen 的具体版本很多时候你只知道有 Eigen但不知道是什么版本。头文件里其实有个版本宏定义手动 grep 一下就知道grep -R define EIGEN_WORLD_VERSION\|define EIGEN_MAJOR_VERSION\|define EIGEN_MINOR_VERSION /usr/include/eigen3/Eigen/src/Core/util/Macros.h3.3.7 对应输出应该是 3、3、7 三个数字。如果 Macros.h 路径不存在说明这个路径下没有 Eigen换/usr/local/include/eigen3再查一次。2.4 留意 ROS 和 conda 这些“隐性携带者”如果你机器上装了 ROSEigen 几乎必然会跟着出现。ROS 的 eigen_conversions、tf2 这些包都依赖 Eigen它们通常通过 apt 依赖自动装上了 libeigen3-dev。另外 conda 环境里也可能有独立的 Eigen 包# 查 conda 环境里的 eigen conda list | grep eigen # 查 ROS 自带的 eigen 相关路径 ls /opt/ros/*/include/eigen3 2/dev/null这一步排查的意义在于不要轻易卸载系统里已有的 Eigen。你可能只是想让某个项目用 3.3.7但系统里其他软件正在依赖现有的 libeigen3-dev。贸然卸载可能导致 ROS 或者别的包被牵连。更好的思路是让 3.3.7 “共存”通过路径优先级和 CMake 配置去控制具体怎么做看第 4 章和第 5 章。3. 最省事的路线apt 源里自带 3.3.7 时的安装与验证3.1 哪些情况可以直接 apt 安装当你确认系统是 Ubuntu 20.04并且没有特殊定制源的时候直接sudo apt update sudo apt install libeigen3-dev装完以后可以快速确认版本pkg-config --modversion eigen3在 Ubuntu 20.04 上输出应该是3.3.7。这时候什么都不用再折腾了普通项目直接#include Eigen/Dense就能用CMake 用find_package(Eigen3 REQUIRED)也能正常找到。3.2 apt 安装后文件到底放在了哪里理解 apt 版 Eigen 的文件布局很重要因为你后面排查问题时大概率用得上。用下面命令看一下包安装的文件列表dpkg -L libeigen3-dev关键路径主要有这几类路径作用/usr/include/eigen3/Eigen/...全部头文件这是核心/usr/include/eigen3/unsupported/...非官方标准模块比如 FFT、EulerAngles、Splines 等/usr/share/eigen3/cmake/Eigen3Config.cmakeCMake 的 find_package 定位文件/usr/share/pkgconfig/eigen3.pcpkg-config 配置文件这个布局意味着你的代码里如果想显式指定头文件路径应该是-I/usr/include/eigen3然后代码里写#include Eigen/Dense而不是#include eigen3/Eigen/Dense。很多新手在这两种写法之间反复横跳最后越绕越晕。3.3 apt 版的一个小坑Ubuntu 版本升级带来的“版本漂移”如果你用的系统本来在 20.04 上 apt 装了 3.3.7后来整体升级到 22.04Eigen 也会被升级到 3.4.0。这时候即使你之前锁定的是 3.3.7升级后已经变了。所以如果你有“必须锁死 3.3.7”的硬性需求一定要意识到apt 版本是跟着系统软件源走的只有源码安装在固定路径下才真正由你掌控。4. 源码编译装 3.3.7完整步骤、路径选择与共存策略如果你用的是 22.04 或 24.04或者你希望 3.3.7 不受到系统包管理器升级影响源码编译是最干净、最可控的方式。Eigen 源码安装并没有想象中那么复杂它不需要编译 C 库文件只是“走个流程把文件复制到系统目录”而已。4.1 下载 3.3.7 源码包从 Eigen 官方 GitLab 仓库下载 3.3.7 的源码归档cd ~/Downloads wget https://gitlab.com/libeigen/eigen/-/archive/3.3.7/eigen-3.3.7.tar.gz下载完以后如果你在意完整性可以用官方提供的 SHA256 校验值核对一下。对我自己来说Eigen 这种纯头文件库即使内容完整性问题也不大但核对一下总归没坏处。注意这里不要用 git clone 默认分支那会拉到最新版不是 3.3.7。如果需要用 git 方式一定要git checkout 3.3.7切换到对应 tag。4.2 解压并进入源码目录tar -xzf eigen-3.3.7.tar.gz cd eigen-3.3.74.3 用 CMake 配置和安装mkdir build cd build cmake .. sudo make install整个过程通常几十秒就结束了。make install默认安装到/usr/local也就是说头文件会复制到/usr/local/include/eigen3CMake 配置文件会放到/usr/local/share/eigen3/cmakepkg-config 配置文件会放到/usr/local/share/pkgconfig。这里有一个很实用的进阶选项如果你不想污染系统目录想给特定项目独立用一份 Eigen 3.3.7可以在 cmake 时指定安装前缀cmake .. -DCMAKE_INSTALL_PREFIX/opt/eigen-3.3.7 sudo make install这样 Eigen 会完整安装到/opt/eigen-3.3.7下头文件在/opt/eigen-3.3.7/include/eigen3CMake 配置文件在/opt/eigen-3.3.7/share/eigen3/cmake。项目里用的时候通过-D Eigen3_DIR/opt/eigen-3.3.7/share/eigen3/cmake指定给它就行。隔离性好不会碰系统其他组件。4.4 安装过程到底做了什么很多人make install完了心里没底不知道装了个啥。其实这里发生的事情很朴素把Eigen目录和unsupported目录整体复制到安装前缀下的include/eigen3目录。生成并安装 CMake 配置文件Eigen3Config.cmake 等到share/eigen3/cmake。生成并安装 pkg-config 的eigen3.pc到share/pkgconfig。顺带安装一份版权声明等文档。全程没有任何 .so 文件生成也没有任何二进制库被安装。你可以理解成“Eigen 的安装就是一个高度定制化的文件复制流程”。4.5 源码安装之后要不要卸载 apt 版 Eigen我的建议是默认不要卸载。尤其是你的环境里还有其他依赖 Eigen 的软件。卸不掉的场景很典型你装了 ROS它依赖 libeigen3-dev。你如果强行sudo apt remove libeigen3-dev要么 ROS 直接被卸掉要么 apt 提示有一堆包依赖它根本不让删。与其跟系统对着干不如让两版共存。共存的逻辑是apt 版在/usr/include/eigen3源码版在/usr/local/include/eigen3。GCC 在默认搜索头文件时/usr/local/include的优先级本来就比/usr/include高。也就是说源码版一旦装进/usr/local大部分情况下编译器会自动优先命中 3.3.7系统里那些依赖 Eigen 的软件本身写死了版本用 apt 版还是源码版对它们正常编译运行没有影响。但这里有个非常容易翻车的情况如果你之前用 apt 装过后来又源码装到/usr/local两个路径都有 Eigen 3.x 的头文件某些项目如果显式把/usr/include/eigen3写在 CMake include 最前面它还是会用到 3.4.0。这时候问题的本质已经变成了“include 路径顺序”和安装本身无关了。具体怎么控制第 5 章会讲。5. 让 CMake 和编译器正确找到 3.3.7路径优先级与 find_package 玩法5.1 为什么会出现“装对了却找不到”的情况源码装完 3.3.7 到/usr/local后最常出现的问题是CMake 里find_package(Eigen3 REQUIRED)报出来的版本还是 3.4.0或者干脆报找不到。原因通常是 CMake 的搜索优先级和你预期不一致。CMake 的find_package会按照CMAKE_PREFIX_PATH、可能的Eigen3_DIR缓存变量、系统默认路径包括/usr/local和/usr这些顺序去查找。在大多数 Ubuntu 上/usr/local/share/eigen3/cmake会优先于/usr/share/eigen3/cmake被搜索到所以你源码装完CMake 应该能发现 3.3.7。如果没发现最大的嫌疑是Eigen3_DIR这个变量在 CMakeCache 里被缓存成了旧路径。这种“缓存路径指到了旧版本”的情况特别坑因为你重新 cmake 时它会安静地复用缓存根本不重新搜索。5.2 用显式变量锁定版本最不容易出错的写法如果你想百分百确保当前项目用的是 3.3.7在 CMakeLists.txt 里可以这样写cmake_minimum_required(VERSION 3.10) project(eigen_test) # 指定 Eigen3_DIR 指向源码安装的 cmake 配置目录 set(Eigen3_DIR /usr/local/share/eigen3/cmake) # EXACT 强制精确匹配版本 find_package(Eigen3 3.3.7 EXACT REQUIRED NO_MODULE) add_executable(eigen_test main.cpp) target_link_libraries(eigen_test Eigen3::Eigen)或者如果你不想在 CMakeLists.txt 里写死路径可以在命令行传入cmake .. -DEigen3_DIR/usr/local/share/eigen3/cmake这样find_package会直接从这个路径读取Eigen3Config.cmake配置里记录的版本就是源码安装时的 3.3.7精确匹配不会出错。5.3 头文件路径优先级两个 Eigen 并存时谁说了算GCC 编译时的 include 路径搜索顺序大概是这样的-I显式指定的路径按出现顺序从左到右环境变量CPLUS_INCLUDE_PATH/usr/local/include/usr/include这个顺序决定了如果系统里/usr/local/include/eigen3和/usr/include/eigen3同时存在你没有额外指定-I那编译器默认会先搜/usr/local/include也就是先命中源码安装的 3.3.7。这算是默认比较安全的一种局。但如果你在命令里写了-I/usr/include/eigen3那就等于你亲手把搜索顺序改成了“先 3.4.0 后 3.3.7”最后编译用的哪个版本完全取决于-I顺序。所以遇到“版本怎么还对不上”的时候第一件事不是去重装而是去查编译命令里有没有显式的-I路径。5.4 pkg-config 方式怎么用如果你不用 CMake而是直接g编译可以用 pkg-config 来获取路径# 查看搜索路径里是否有 3.3.7 的 pc 文件 pkg-config --list-all | grep eigen # 编译时通过 pkg-config 获取 include 路径 g -stdc11 main.cpp $(pkg-config --cflags eigen3) -o main源码安装到/usr/local后eigen3.pc会放在/usr/local/share/pkgconfig/pkg-config 默认会搜索这个目录所以直接使用通常没问题。如果你把 Eigen 装到了自定义前缀比如/opt/eigen-3.3.7那需要设置环境变量export PKG_CONFIG_PATH/opt/eigen-3.3.7/share/pkgconfig:$PKG_CONFIG_PATH6. 安装后的实测验证与常见报错自救手册6.1 写一个 5 分钟测试程序确认版本和功能装完不能光看版本号实际编译一个能跑的 C 程序才踏实。我每次装 Eigen 都会用一个极简的矩阵程序测试核心功能和版本号#include iostream #include Eigen/Dense int main() { // 打印编译期版本号 std::cout Eigen version: EIGEN_WORLD_VERSION . EIGEN_MAJOR_VERSION . EIGEN_MINOR_VERSION std::endl; // 简单矩阵运算验证 Dense 模块可用 Eigen::Matrix3d m; m 1, 2, 3, 4, 5, 6, 7, 8, 9; std::cout m \n m std::endl; std::cout m * m \n (m * m) std::endl; std::cout m.sum() m.sum() std::endl; std::cout m.determinant() m.determinant() std::endl; return 0; }编译运行g -stdc11 test_eigen.cpp -o test_eigen ./test_eigen如果你是通过源码装到/usr/local/include/eigen3GCC 默认搜索路径能直接命中不需要额外加-I。但如果你把 Eigen 装到了/opt/eigen-3.3.7这类自定义路径编译时就要加g -stdc11 -I/opt/eigen-3.3.7/include/eigen3 test_eigen.cpp -o test_eigen程序输出里如果能看到Eigen version: 3.3.7并且矩阵乘法结果正常那说明安装是完好的。6.2 用 CMake 方式再验证一遍 find_package命令行直接编译通过还不够因为很多复杂项目是走 CMake 的。建议再建一个最小 CMake 工程验证一下cmake_minimum_required(VERSION 3.10) project(verify_eigen) find_package(Eigen3 3.3.7 EXACT REQUIRED NO_MODULE) add_executable(verify main.cpp) target_link_libraries(verify Eigen3::Eigen)然后mkdir build cd build cmake .. make如果find_package和编译都通过说明 CMake 链路也通了。这一步能提前暴露路径问题不然等项目结构大了再排查会痛苦得多。6.3 常见报错的针对性解决我在多个 Ubuntu 版本上装过 Eigen也远程帮别人排过不少故障。下面这几个问题出现频率最高可以直接对照处理报错现象可能原因解决方式Eigen3Config.cmake not foundCMake 搜索路径里没有配置文件cmake .. -DEigen3_DIR/usr/local/share/eigen3/cmakeFound Eigen3: 3.4.0 (Required is at least 3.3.7)apt 版和源码版并存默认被旧路径抢了检查并清理 CMakeCache显式指定Eigen3_DIR指向 3.3.7fatal error: Eigen/Dense: No such file or directory编译器搜索路径里没有 Eigen 头文件用find / -name Dense -path *Eigen*找到实际路径确认是不是安装前缀问题版本明明是 3.3.7但程序里某些 API 编译失败项目代码本身可能用了 3.4 的新接口或 include 路径混用确认所有-I总是指向同一个 Eigen 头文件目录别一半 3.3.7 一半 3.4.0pkg-config 找不到 eigen3pc 文件不在搜索路径export PKG_CONFIG_PATH/usr/local/share/pkgconfig最魔幻的一种情况是程序里#include Eigen/Dense时编译命令的-I同时包含两个不同版本的 Eigen 目录结果头文件从 A 路径引入但某些宏定义又被 B 路径覆盖最后报出一堆莫名其妙的模板错误。遇到这种问题别急着改代码先把你编译命令里的-I全部列出来一行一行排查。6.4 顺便说下让 Eigen 跑得更快的编译选项Eigen 是模板库它的性能和你开启的编译器优化选项高度相关。调试模式下默认不开优化Eigen 的矩阵运算会慢很多但这并不是 Eigen 的问题。实机测试时我发现开启编译优化对 Eigen 的求解性能影响非常大。建议在 Release 模式下加这些选项g -O3 -DNDEBUG -marchnative -stdc11 main.cpp -o main-O3开启高度优化Eigen 模板表达式必须靠它才能发挥真正性能。-DNDEBUG关闭 Eigen 内部的边界检查断言消除运行时开销。-marchnative针对当前 CPU 指令集优化尤其是矩阵乘法这类计算密集型操作提升非常明显。如果你的项目用 CMake在CMakeLists.txt里设置set(CMAKE_BUILD_TYPE Release)测试同一个矩阵运算开启优化前后的耗时差距可能在两倍以上。很多人跑 Eigen 运算觉得卡多半是忘了在 Release 模式下编译。最后再分享一个我自己比较习惯的小技巧我现在在 Ubuntu 22.04 上装完 Eigen 3.3.7 之后习惯性地会在项目根目录放一个cmake/FindEigen3.cmake或者直接在命令行里把Eigen3_DIR路径记录下来。这样下次重新 clone 或者换人接手项目时不用再重新陷入版本匹配的泥潭。还有一点如果你经常在几个 Ubuntu 版本之间切来切去建议养成用 Docker 容器装固定版本 Eigen 的习惯。把 Eigen 3.3.7 的源码安装写进 Dockerfile开发环境需要哪个版本就起哪个容器宿主机永远不用动。这个方法帮我省掉了非常多环境冲突的问题。对于这种“版本写死”的老库容器隔离是目前体验最顺的解法。
返回列表