第57例:Eigen库——集成Eigen线性代数库(头文件库)
线性代数在工程计算里有多重要,不用我多说。做机器人、做图形学、做SLAM、做控制仿真……哪哪都离不开矩阵运算。而Eigen,就是C++世界里最顺手的线性代数库之一。
为什么说它「顺手」?因为它是个纯头文件库。你不需要编译它,不需要链接动态库,甚至不需要安装。只要把头文件路径指给CMake,就能直接用。我当年第一次用Eigen的时候,简直不敢相信——就这么简单?
嗯,就是这么简单。但集成到CMake里,还是有几个小细节值得注意。咱们今天就把这事聊透。
Eigen是什么?为什么选它?
Eigen是一个C++模板库,专门做线性代数运算。它支持:
- 矩阵和向量的基本运算(加减乘除、转置、求逆)
- 特征值分解、SVD、QR分解等数值算法
- 稀疏矩阵操作
- 几何变换(旋转、平移、四元数)
- 与OpenCV、ROS等常见框架无缝配合
它的核心优势就两条:快 和 无依赖。Eigen用模板元编程做了大量编译期优化,性能上不输MKL。而且它不依赖任何第三方库,下载即用。
核心要点:Eigen是纯头文件库,集成时不需要编译.so或.a文件。你只需要让CMake找到它的头文件路径。
集成方式一:find_package(推荐)
如果你的系统里已经安装了Eigen(比如通过apt、brew、vcpkg),那最简单的方式就是用find_package。
# CMakeLists.txt
cmake_minimum_required(VERSION 3.10)
project(EigenDemo)
# 查找Eigen3
find_package(Eigen3 REQUIRED)
# 添加可执行文件
add_executable(demo main.cpp)
# 链接Eigen的头文件路径
target_link_libraries(demo Eigen3::Eigen)
这里有个坑,我提一下。find_package(Eigen3 REQUIRED) 找的是 Eigen3Config.cmake 或 eigen3-config.cmake。如果你是用apt安装的,一般在 /usr/lib/cmake/eigen3/ 下。如果找不到,八成是安装路径没在CMake的搜索范围内。
解决办法?手动指定路径:
find_package(Eigen3 REQUIRED PATHS /path/to/eigen/cmake)
或者用 set(Eigen3_DIR /path/to/eigen/cmake) 提前设置。
集成方式二:手动指定头文件路径
有时候你不想安装Eigen,或者项目里需要固定版本。直接把Eigen源码丢到项目里,也是常见做法。
# 项目结构
my_project/
├── CMakeLists.txt
├── third_party/
│ └── eigen/ # Eigen源码,直接解压到这里
└── src/
└── main.cpp
CMakeLists.txt这样写:
cmake_minimum_required(VERSION 3.10)
project(EigenDemo)
# 手动指定Eigen头文件路径
set(EIGEN_INCLUDE_DIR "${CMAKE_SOURCE_DIR}/third_party/eigen")
add_executable(demo src/main.cpp)
target_include_directories(demo PRIVATE ${EIGEN_INCLUDE_DIR})
我个人习惯用这种方式。为什么?因为版本可控。项目里锁死Eigen 3.4.0,不管用户机器上装了什么版本,编译结果都一样。这在团队协作里特别重要。
小技巧:Eigen的源码里有个 Eigen/ 子目录,里面才是真正的头文件。你include的时候写 #include <Eigen/Dense>,所以 target_include_directories 要指向包含 Eigen/ 的那个父目录。别指错了。
写个Demo试试
集成完了,总得跑个例子验证一下。下面这个代码,我建议你直接复制到项目里试试:
// main.cpp
#include <iostream>
#include <Eigen/Dense>
int main() {
// 定义一个3x3矩阵
Eigen::Matrix3d mat;
mat << 1, 2, 3,
4, 5, 6,
7, 8, 9;
std::cout << "矩阵:\n" << mat << std::endl;
// 求逆矩阵
Eigen::Matrix3d inv = mat.inverse();
std::cout << "逆矩阵:\n" << inv << std::endl;
// 验证:原矩阵 * 逆矩阵 ≈ 单位矩阵
std::cout << "验证:\n" << mat * inv << std::endl;
// 解线性方程组 Ax = b
Eigen::Vector3d b(1, 2, 3);
Eigen::Vector3d x = mat.colPivHouseholderQr().solve(b);
std::cout << "解 x:\n" << x << std::endl;
return 0;
}
编译运行:
mkdir build && cd build
cmake ..
make
./demo
你会看到矩阵、逆矩阵、验证结果和解向量。如果一切正常,说明Eigen集成成功了。
Eigen的模块结构
Eigen不是一个大而全的头文件,它分了很多模块。你按需include就行,不用全拉进来。
| 模块 | 头文件 | 功能 |
|---|---|---|
| Core | Eigen/Core | 矩阵、向量、数组基础运算 |
| Dense | Eigen/Dense | Core + Geometry + LU + Cholesky + SVD + QR + Eigenvalues |
| Geometry | Eigen/Geometry | 旋转、平移、四元数、仿射变换 |
| Sparse | Eigen/Sparse | 稀疏矩阵存储与运算 |
| IterativeLinearSolvers | Eigen/IterativeLinearSolvers | 迭代法求解线性方程组 |
日常开发,#include <Eigen/Dense> 基本够用。它把最常用的稠密矩阵功能全包含了。
避坑指南
集成Eigen时,有几个坑我踩过,分享给你:
警告1:对齐问题
Eigen使用SSE/AVX指令集加速,要求内存对齐到16字节或32字节。如果你在STL容器里放Eigen对象(比如 std::vector<Eigen::Vector4d>),必须用Eigen提供的分配器:
std::vector<Eigen::Vector4d, Eigen::aligned_allocator<Eigen::Vector4d>> vec;
或者直接用 EIGEN_MAKE_ALIGNED_OPERATOR_NEW 宏。否则运行时可能崩溃。
警告2:动态矩阵的尺寸
Eigen的 MatrixXd 是动态尺寸,运行时才确定行列数。但如果你在编译期就知道尺寸,尽量用固定尺寸(如 Matrix3d、Vector4f)。固定尺寸的矩阵没有动态内存分配,性能好得多。我在做实时控制项目时,所有矩阵都写成固定尺寸,速度提升很明显。
提示:编译加速
Eigen是模板库,编译比较慢。如果项目大,建议把Eigen头文件放到预编译头里,或者用 EIGEN_NO_DEBUG 宏关闭调试检查(Release模式下默认关闭)。
知识体系总览
下面这张图,帮你理清Eigen集成的核心逻辑:
总结
Eigen的集成,说白了就是让CMake找到头文件路径。两种方式——find_package 和手动指定——各有适用场景。我个人更推荐手动指定,版本可控,团队协作省心。
集成之后,记得注意内存对齐和编译速度的问题。这两个坑,我当年都踩过。尤其是对齐问题,运行时崩溃特别难排查。你如果遇到莫名其妙的段错误,先检查一下是不是Eigen对象放进了STL容器。
好了,Eigen集成就聊到这。代码不多,但实用。你拿去用,有问题随时翻回来看看。