第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.cmakeeigen3-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 是动态尺寸,运行时才确定行列数。但如果你在编译期就知道尺寸,尽量用固定尺寸(如 Matrix3dVector4f)。固定尺寸的矩阵没有动态内存分配,性能好得多。我在做实时控制项目时,所有矩阵都写成固定尺寸,速度提升很明显。

提示:编译加速

Eigen是模板库,编译比较慢。如果项目大,建议把Eigen头文件放到预编译头里,或者用 EIGEN_NO_DEBUG 宏关闭调试检查(Release模式下默认关闭)。

知识体系总览

下面这张图,帮你理清Eigen集成的核心逻辑:

Eigen库集成知识体系 Eigen 集成 集成方式 find_package(Eigen3 REQUIRED) 手动指定头文件路径 核心模块 Dense(稠密矩阵) Sparse(稀疏矩阵) Geometry(几何变换) 注意事项 内存对齐问题 固定尺寸 vs 动态尺寸 编译速度优化 纯头文件库 · 无需编译 · 模板元编程优化

总结

Eigen的集成,说白了就是让CMake找到头文件路径。两种方式——find_package 和手动指定——各有适用场景。我个人更推荐手动指定,版本可控,团队协作省心。

集成之后,记得注意内存对齐和编译速度的问题。这两个坑,我当年都踩过。尤其是对齐问题,运行时崩溃特别难排查。你如果遇到莫名其妙的段错误,先检查一下是不是Eigen对象放进了STL容器。

好了,Eigen集成就聊到这。代码不多,但实用。你拿去用,有问题随时翻回来看看。


公众号:蓝海资料掘金营,微信deep3321