Qt+PCL手把手教材(第10讲)——点云的基本操作(旋转、缩放、平移)附C++代码

在点云数据处理过程中,旋转、缩放和平移是常见的几何变换操作,通常用于对点云进行预处理、对齐、或者数据增强等任务。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} RR3×3 是一个 正交矩阵,满足:

R ⊤ R = I , det ⁡ ( R ) = 1 \mathbf{R}^\top \mathbf{R} = \mathbf{I}, \quad \det(\mathbf{R}) = 1 RR=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θ0sinθ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θ0sinθ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θ0sinθ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 tR3 表示。

例如:将点 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=[R0t1],phom=Tphom


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;
}
代码解析:
  1. 点云定义:创建了一个简单的点云,包含 5 个点。
  2. 旋转矩阵:通过 Eigen::AngleAxisf 创建一个绕 Z 轴旋转 90 度的旋转矩阵。
  3. 旋转变换:使用 pcl::transformPointCloud 将旋转矩阵应用到点云上,得到旋转后的点云。
  4. 输出结果:打印旋转后的点云坐标。

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;
}
代码解析:
  1. 点云定义:创建一个简单的点云,包含 5 个点。
  2. 缩放矩阵:通过 transform.scale(2.0) 定义一个缩放因子为 2 的变换。
  3. 缩放变换:使用 pcl::transformPointCloud 将缩放变换应用到点云上,得到缩放后的点云。
  4. 输出结果:打印缩放后的点云坐标。

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;
}
代码解析:
  1. 点云定义:创建一个简单的点云,包含 5 个点。
  2. 平移矩阵:使用 transform.translation() 定义平移向量 (2.0, 3.0, 4.0),将点云向右、向上、向外平移。
  3. 平移变换:通过 pcl::transformPointCloud 应用平移变换,得到平移后的点云。
  4. 输出结果:打印平移后的点云坐标。

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讲的相关内容,欢迎喜欢的朋友订阅

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

点云SLAM

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值