在点云数据处理过程中,旋转、缩放和平移是常见的几何变换操作,通常用于对点云进行预处理、对齐、或者数据增强等任务。PCL点云库 提供了多种方式来实现这些操作。下面是如何使用 PCL点云库 对点云执行这些基本操作的讲解示例代码,这些基本操作会在Qt+PCL联合开发中经常使用,因此学习并且掌握是十分有必要的。
1 、旋转矩阵和平移基本概念
旋转矩阵和平移(Translation)是计算机图形学、机器人学、SLAM、计算机视觉等领域的基础知识,属于刚体变换(Rigid Transformation)的一部分。以下是对它们的简单讲解:
1.1、旋转矩阵(Rotation Matrix)
1. 定义
旋转矩阵 R ∈ R 3 × 3 \mathbf{R} \in \mathbb{R}^{3 \times 3} R∈R3×3 是一个 正交矩阵,满足:
R ⊤ R = I , det ( R ) = 1 \mathbf{R}^\top \mathbf{R} = \mathbf{I}, \quad \det(\mathbf{R}) = 1 R⊤R=I,det(R)=1
它表示绕原点的三维旋转,保持向量长度不变,常用于刚体姿态表示。
2. 常见旋转矩阵
- 绕 x x x 轴旋转角度 θ \theta θ:
R x ( θ ) = [ 1 0 0 0 cos θ − sin θ 0 sin θ cos θ ] \mathbf{R}_x(\theta) = \begin{bmatrix} 1 & 0 & 0 \\ 0 & \cos\theta & -\sin\theta \\ 0 & \sin\theta & \cos\theta \\ \end{bmatrix} Rx(θ)= 1000cosθsinθ0−sinθcosθ
- 绕 y y y 轴旋转:
R y ( θ ) = [ cos θ 0 sin θ 0 1 0 − sin θ 0 cos θ ] \mathbf{R}_y(\theta) = \begin{bmatrix} \cos\theta & 0 & \sin\theta \\ 0 & 1 & 0 \\ -\sin\theta & 0 & \cos\theta \\ \end{bmatrix} Ry(θ)= cosθ0−sinθ010sinθ0cosθ
- 绕 z z z 轴旋转:
R z ( θ ) = [ cos θ − sin θ 0 sin θ cos θ 0 0 0 1 ] \mathbf{R}_z(\theta) = \begin{bmatrix} \cos\theta & -\sin\theta & 0 \\ \sin\theta & \cos\theta & 0 \\ 0 & 0 & 1 \\ \end{bmatrix} Rz(θ)= cosθsinθ0−sinθcosθ0001
3. 组合旋转(欧拉角)
可以通过先后绕不同轴旋转构建组合旋转:
R = R z ( γ ) R y ( β ) R x ( α ) \mathbf{R} = \mathbf{R}_z(\gamma) \mathbf{R}_y(\beta) \mathbf{R}_x(\alpha) R=Rz(γ)Ry(β)Rx(α)
(取决于顺序和坐标系约定)
4. 李群 SO(3)
旋转矩阵属于 特殊正交群 SO(3),是李群的一个例子,其对应的李代数是 so(3),相关内容有,这里不详细展开:
- so(3):反对称矩阵
- 使用指数映射和对数映射
1.2、平移向量(Translation Vector)
1. 定义
平移是将一个点沿某方向移动,使用一个三维向量 t ∈ R 3 \mathbf{t} \in \mathbb{R}^3 t∈R3 表示。
例如:将点 p \mathbf{p} p 平移为 p ′ \mathbf{p}' p′:
p ′ = p + t \mathbf{p}' = \mathbf{p} + \mathbf{t} p′=p+t
1.3、刚体变换(Rigid Body Transformation)
结合旋转和平移,构成刚体变换:
p ′ = R p + t \mathbf{p}' = \mathbf{R} \mathbf{p} + \mathbf{t} p′=Rp+t
这可以表示为一个齐次矩阵形式(4x4):
T = [ R t 0 ⊤ 1 ] , p hom ′ = T ⋅ p hom \mathbf{T} = \begin{bmatrix} \mathbf{R} & \mathbf{t} \\ \mathbf{0}^\top & 1 \end{bmatrix}, \quad \mathbf{p}'_{\text{hom}} = \mathbf{T} \cdot \mathbf{p}_{\text{hom}} T=[R0⊤t1],phom′=T⋅phom
1.4、应用场景
- SLAM 与视觉里程计:估计从一帧到另一帧的旋转和平移。
- 3D 点云配准:如 ICP、NDT,目标是求解最优 R , t \mathbf{R}, \mathbf{t} R,t。
- 机器人运动学:描述关节或坐标系之间的位置关系。
2、旋转点云
点云旋转通常是通过应用一个旋转矩阵
(
R
)
(R)
(R)来完成。PCL 提供了 pcl::transformPointCloud
方法,在头文件pcl/common/transforms.h
中,该方法允许我们应用一个 4x4 的变换矩阵
(
T
)
(T)
(T)(包括旋转
(
R
)
(R)
(R)和位移
(
t
)
(t)
(t))来变换点云。
示例代码:旋转点云
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
#include <pcl/point_cloud.h>
#include <Eigen/Dense>
#include <iostream>
int main()
{
// 创建一个点云对象并填充数据
pcl::PointCloud<pcl::PointXYZ> cloud;
cloud.width = 5;
cloud.height = 1;
cloud.is_dense = false;
cloud.points.resize(cloud.width * cloud.height);
for (size_t i = 0; i < cloud.points.size(); ++i)
{
cloud.points[i].x = i;
cloud.points[i].y = i;
cloud.points[i].z = i;
}
// 定义一个旋转矩阵(绕 Z 轴旋转 90 度)
Eigen::Affine3f transform = Eigen::Affine3f::Identity();
transform.rotate(Eigen::AngleAxisf(M_PI / 2, Eigen::Vector3f::UnitZ()));
// 应用变换
pcl::PointCloud<pcl::PointXYZ> cloud_rotated;
pcl::transformPointCloud(cloud, cloud_rotated, transform);
// 输出旋转后的点云
std::cout << "Rotated point cloud: " << std::endl;
for (size_t i = 0; i < cloud_rotated.points.size(); ++i)
{
std::cout << cloud_rotated.points[i].x << " "
<< cloud_rotated.points[i].y << " "
<< cloud_rotated.points[i].z << std::endl;
}
return 0;
}
代码解析:
- 点云定义:创建了一个简单的点云,包含 5 个点。
- 旋转矩阵:通过
Eigen::AngleAxisf
创建一个绕 Z 轴旋转 90 度的旋转矩阵。 - 旋转变换:使用
pcl::transformPointCloud
将旋转矩阵应用到点云上,得到旋转后的点云。 - 输出结果:打印旋转后的点云坐标。
3、缩放点云
缩放点云是通过对每个点的坐标进行按比例缩放来实现的。PCL点云库 提供了类似的变换方法,可以通过应用缩放因子来完成。
示例代码:缩放点云
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
#include <pcl/point_cloud.h>
#include <Eigen/Dense>
#include <iostream>
int main()
{
// 创建一个点云对象并填充数据
pcl::PointCloud<pcl::PointXYZ> cloud;
cloud.width = 5;
cloud.height = 1;
cloud.is_dense = false;
cloud.points.resize(cloud.width * cloud.height);
for (size_t i = 0; i < cloud.points.size(); ++i)
{
cloud.points[i].x = i;
cloud.points[i].y = i;
cloud.points[i].z = i;
}
// 定义一个缩放矩阵(缩放因子为 2)
Eigen::Affine3f transform = Eigen::Affine3f::Identity();
transform.scale(2.0); // 缩放因子为 2
// 应用变换
pcl::PointCloud<pcl::PointXYZ> cloud_scaled;
pcl::transformPointCloud(cloud, cloud_scaled, transform);
// 输出缩放后的点云
std::cout << "Scaled point cloud: " << std::endl;
for (size_t i = 0; i < cloud_scaled.points.size(); ++i)
{
std::cout << cloud_scaled.points[i].x << " "
<< cloud_scaled.points[i].y << " "
<< cloud_scaled.points[i].z << std::endl;
}
return 0;
}
代码解析:
- 点云定义:创建一个简单的点云,包含 5 个点。
- 缩放矩阵:通过
transform.scale(2.0)
定义一个缩放因子为 2 的变换。 - 缩放变换:使用
pcl::transformPointCloud
将缩放变换应用到点云上,得到缩放后的点云。 - 输出结果:打印缩放后的点云坐标。
4、 平移点云
平移点云的操作非常简单,只需要对每个点的坐标添加一个平移向量即可。与旋转和缩放一样,PCL点云 也提供了 transformPointCloud
来实现平移。
示例代码:平移点云
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
#include <pcl/point_cloud.h>
#include <Eigen/Dense>
#include <iostream>
int main()
{
// 创建一个点云对象并填充数据
pcl::PointCloud<pcl::PointXYZ> cloud;
cloud.width = 5;
cloud.height = 1;
cloud.is_dense = false;
cloud.points.resize(cloud.width * cloud.height);
for (size_t i = 0; i < cloud.points.size(); ++i)
{
cloud.points[i].x = i;
cloud.points[i].y = i;
cloud.points[i].z = i;
}
// 定义一个平移向量(平移 2 单位)
Eigen::Affine3f transform = Eigen::Affine3f::Identity();
transform.translation() << 2.0, 3.0, 4.0; // 平移向量 (x, y, z)
// 应用变换
pcl::PointCloud<pcl::PointXYZ> cloud_translated;
pcl::transformPointCloud(cloud, cloud_translated, transform);
// 输出平移后的点云
std::cout << "Translated point cloud: " << std::endl;
for (size_t i = 0; i < cloud_translated.points.size(); ++i)
{
std::cout << cloud_translated.points[i].x << " "
<< cloud_translated.points[i].y << " "
<< cloud_translated.points[i].z << std::endl;
}
return 0;
}
代码解析:
- 点云定义:创建一个简单的点云,包含 5 个点。
- 平移矩阵:使用
transform.translation()
定义平移向量(2.0, 3.0, 4.0)
,将点云向右、向上、向外平移。 - 平移变换:通过
pcl::transformPointCloud
应用平移变换,得到平移后的点云。 - 输出结果:打印平移后的点云坐标。
5、 组合变换
你可以组合旋转、缩放和平移变换,通过多个变换矩阵的组合来实现更复杂的变换。
示例代码:组合旋转、缩放和平移
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
#include <pcl/point_cloud.h>
#include <Eigen/Dense>
#include <iostream>
int main()
{
// 创建一个点云对象并填充数据
pcl::PointCloud<pcl::PointXYZ> cloud;
cloud.width = 5;
cloud.height = 1;
cloud.is_dense = false;
cloud.points.resize(cloud.width * cloud.height);
for (size_t i = 0; i < cloud.points.size(); ++i)
{
cloud.points[i].x = i;
cloud.points[i].y = i;
cloud.points[i].z = i;
}
// 创建变换:旋转 90 度、缩放 2.0、平移 (2, 3, 4)
Eigen::Affine3f transform = Eigen::Affine3f::Identity();
transform.rotate(Eigen::AngleAxisf(M_PI / 2, Eigen::Vector3f::UnitZ())); // 旋转
transform.scale(2.0); // 缩放
transform.translation() << 2.0, 3.0, 4.0; // 平移
// 应用组合变换
pcl::PointCloud<pcl::PointXYZ> cloud_transformed;
pcl::transformPointCloud(cloud, cloud_transformed, transform);
// 输出变换后的点云
std::cout << "Transformed point cloud: " << std::endl;
for (size_t i = 0; i < cloud_transformed.points.size(); ++i)
{
std::cout << cloud_transformed.points[i].x << " "
<< cloud_transformed.points[i].y << " "
<< cloud_transformed.points[i].z << std::endl;
}
return 0;
}
6、 总结
- 旋转:通过旋转矩阵(例如绕 Z 轴旋转)来变换点云。
- 缩放:通过缩放因子来改变点云大小。
- 平移:通过平移向量将点云平移到新的位置。
- 组合变换:可以将旋转、缩放和平移操作组合在一起,形成更复杂的变换。
PCL 提供了强大的功能来处理和变换点云数据。可以根据需求对点云进行旋转、缩放、平移等操作,以便对点云进行更精细的分析和处理。
至此完成第10讲的相关内容,欢迎喜欢的朋友订阅