Eigen 中矩阵的拼接(Concatenation)与 分块(Block Access)操作使用详解和示例演示
一、矩阵分块访问(Block)
Eigen 使用 .block()
、.topLeftCorner()
、.row()
、.col()
等函数进行矩阵子块访问:
1.1 block(i, j, rows, cols)
— 访问子块
MatrixXd mat(4, 4);
mat << 1, 2, 3, 4,5, 6, 7, 8,9,10,11,12,13,14,15,16;MatrixXd sub = mat.block(1, 1, 2, 2); // 从(1,1)取2x2子矩阵
1.2 快捷方式访问角落区域
mat.topLeftCorner(2, 2);
mat.bottomRightCorner(2, 2);
mat.topRows(2);
mat.leftCols(3);
1.3 修改子块
mat.block(0, 0, 2, 2) = Matrix2d::Ones();
二、矩阵拼接(Concatenation)
2.1 横向拼接(水平拼接)
MatrixXd A(2, 2), B(2, 3);
A << 1, 2, 3, 4;
B << 5, 6, 7, 8, 9, 10;MatrixXd C(A.rows(), A.cols() + B.cols());
C << A, B; // 水平拼接
2.2 纵向拼接(垂直拼接)
MatrixXd D(3, 2);
D << 11, 12,13, 14,15, 16;MatrixXd E(A.rows() + D.rows(), A.cols());
E << A,D; // 垂直拼接
三、动态拼接(适用于未知大小)
Eigen 不直接支持 push_back
,可通过 resize + block
实现:
MatrixXd total(0, 2); // 初始空矩阵,列数固定为2
MatrixXd row(1, 2);
row << 1, 2;total.conservativeResize(total.rows() + 1, NoChange); // 保持列数不变
total.bottomRows(1) = row;
四、行列访问与赋值
Matrix3d A;
A.row(0) = RowVector3d(1, 2, 3); // 设置第1行
A.col(1) = Vector3d(4, 5, 6); // 设置第2列
五、分块迭代
可用于滑动窗口、图块处理等:
MatrixXd big = MatrixXd::Random(6, 6);
for (int i = 0; i < big.rows(); i += 3) {for (int j = 0; j < big.cols(); j += 3) {MatrixXd block = big.block(i, j, 3, 3);std::cout << "Block at (" << i << "," << j << "):\n" << block << std::endl;}
}
六、拼接 Vector(向量拼接)
Eigen 无直接 append
接口,可手动拼接:
Vector3d a(1, 2, 3);
Vector2d b(4, 5);VectorXd c(a.size() + b.size());
c << a, b; // 拼接为5维向量
七、矩阵拼接的注意事项
操作方式 | 是否推荐 | 原因说明 |
---|---|---|
conservativeResize() + 赋值 | ✅ 推荐 | 动态拼接、可扩展 |
std::vector<MatrixXd> 后合并 | ✅ 推荐 | 适合收集多个块再整体合并 |
STL push_back 模拟拼接 | ❌ 不推荐 | Eigen 矩阵不是 STL 容器,效率低 |
八、示例演示
1、信息矩阵拼接示例
图优化中常构建如下稀疏结构矩阵:
H=[H11H120H12TH22H230H23TH33] H = \begin{bmatrix} H_{11} & H_{12} & 0 \\ H_{12}^T & H_{22} & H_{23} \\ 0 & H_{23}^T & H_{33} \end{bmatrix} H=H11H12T0H12H22H23T0H23H33
MatrixXd H(9, 9);
H.setZero();MatrixXd H11 = MatrixXd::Identity(3, 3);
MatrixXd H12 = MatrixXd::Constant(3, 3, 0.5);
MatrixXd H22 = MatrixXd::Identity(3, 3) * 2;
MatrixXd H23 = MatrixXd::Constant(3, 3, 1.0);
MatrixXd H33 = MatrixXd::Identity(3, 3) * 3;H.block(0, 0, 3, 3) = H11;
H.block(0, 3, 3, 3) = H12;
H.block(3, 0, 3, 3) = H12.transpose();
H.block(3, 3, 3, 3) = H22;
H.block(3, 6, 3, 3) = H23;
H.block(6, 3, 3, 3) = H23.transpose();
H.block(6, 6, 3, 3) = H33;
2、矩阵合并示例
处理多个点云帧时,常需将坐标矩阵拼接成大块矩阵以用于 SVD 或最小二乘:
std::vector<Vector3d> cloudA = { Vector3d(1,2,3), Vector3d(4,5,6) };
std::vector<Vector3d> cloudB = { Vector3d(7,8,9), Vector3d(10,11,12) };MatrixXd matA(3, cloudA.size());
MatrixXd matB(3, cloudB.size());
for (size_t i = 0; i < cloudA.size(); ++i) {matA.col(i) = cloudA[i];matB.col(i) = cloudB[i];
}// 拼接为 6xN 点对矩阵
MatrixXd combined(6, cloudA.size());
combined << matA,matB;
3、使用 SparseMatrix 进行分块拼接(稀疏构图)示例
对于大规模问题,更推荐使用稀疏矩阵拼接:
#include <Eigen/Sparse>
using namespace Eigen;typedef SparseMatrix<double> SpMat;
std::vector<Triplet<double>> triplets;int block_size = 3;// 插入块 (i,j)
Matrix3d block = Matrix3d::Identity();
for (int i = 0; i < block_size; ++i)for (int j = 0; j < block_size; ++j)triplets.emplace_back(i + 3, j + 3, block(i,j)); // 插入到 H(3:6, 3:6)SpMat H_sparse(9, 9);
H_sparse.setFromTriplets(triplets.begin(), triplets.end());
4、构建动态扩展矩阵队列(如 sliding window)示例
可维护多帧 pose 或 observation:
std::vector<MatrixXd> pose_blocks;// 每次新状态加入
MatrixXd new_pose(6, 1); // 6 DOF
new_pose.setRandom();
pose_blocks.push_back(new_pose);// 拼接为整体大矩阵
MatrixXd all_pose(6, pose_blocks.size());
for (size_t i = 0; i < pose_blocks.size(); ++i)all_pose.col(i) = pose_blocks[i];
5、拆分一个大矩阵为多个子块示例
例如将优化后的状态拆成多个节点:
VectorXd x = VectorXd::LinSpaced(18, 1, 18); // 假设每 6 为一帧状态
int n = x.size() / 6;std::vector<VectorXd> frames;
for (int i = 0; i < n; ++i)frames.push_back(x.segment(i * 6, 6)); // 拆成多个6维状态
九、总结-拼接与分块方法对比
方法 | 用途 | 是否动态 | 备注 |
---|---|---|---|
.block(i,j,r,c) | 任意子块读写 | 否 | 高效 |
conservativeResize() + bottomRows() | 逐行拼接 | 是 | 常用于采样/流式输入 |
std::vector<MatrixXd> | 先收集后合并 | 是 | 推荐 |
稀疏矩阵 Triplet 方式 | 大规模稀疏拼接 | 是 | 图优化必备 |
.segment() | 拆向量 | 是 | 高效 |