• pcl 使用矩阵变换(旋转、平移)点云


    目录

    1. 场景

    2. 代码详解

    2.1 头文件

    2.2 参数解析

    2.3 解析参数获取pcd和ply文件索引

    2.4 加载pcd和ply点云数据

    2.5 定义变换矩阵

    2.5.1 预备知识

    2.5.2 实操

     2.6 变换

    2.7 可视化

    3. 整合代码


    1. 场景

    定义一个4x4的转换矩阵,去转换点云数据。里面包含的知识点:

    (1)加载点云数据(pcd or ply);

    (2)转换点云;

    (3)可视化点云数据(设置点云颜色、背景颜色)。

    2. 代码详解

    2.1 头文件

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include // pcl::console::find_switch
    6. #include // pcl::transformPointCloud
    7. #include // pcl::visualization::PCLVisualizer

    2.2 参数解析

    通过命令行指定点云文件,如果没有设置,则会打印help信息提醒。

    1. // This function displays the help
    2. // 用于提醒输出参数
    3. void
    4. showHelp(char* program_name)
    5. {
    6. std::cout << std::endl;
    7. std::cout << "Usage: " << program_name << " cloud_filename.[pcd|ply]" << std::endl;
    8. std::cout << "-h: Show this help." << std::endl;
    9. }
    10. // This is the main function
    11. int
    12. main(int argc, char** argv)
    13. {
    14. // Show help,find_switch:argv命令行中是否有-h,有则运行下面函数
    15. if (pcl::console::find_switch(argc, argv, "-h") || pcl::console::find_switch(argc, argv, "--help")) {
    16. showHelp(argv[0]);
    17. return 0;
    18. }
    19. }

    2.3 解析参数获取pcd和ply文件索引

    1. // Fetch point cloud filename in arguments | Works with PCD and PLY files
    2. std::vector<int> filenames_idx; // 存储文件名索引的数组
    3. bool file_is_pcd = false;
    4. // 返回文件名索引的数组, filenames[0]是第0个文件名的索引,
    5. // argv[filenames[0]]第0个文件名
    6. filenames_idx = pcl::console::parse_file_extension_argument(argc, argv, ".ply");
    7. if (filenames_idx.size() != 1) { // 不是ply文件
    8. filenames_idx = pcl::console::parse_file_extension_argument(argc, argv, ".pcd");
    9. if (filenames_idx.size() != 1) { // 也不是pcd文件
    10. showHelp(argv[0]);
    11. return -1;
    12. }
    13. else {
    14. file_is_pcd = true;
    15. }
    16. }

    2.4 加载pcd和ply点云数据

    1. // Load file | Works with PCD and PLY files
    2. pcl::PointCloud::Ptr source_cloud(new pcl::PointCloud());
    3. if (file_is_pcd) {
    4. if (pcl::io::loadPCDFile(argv[filenames_idx[0]], *source_cloud) < 0) { // 默认只有一个文件,该文件名:argv[filenames_idx[0]]
    5. std::cout << "Error loading point cloud " << argv[filenames_idx[0]] << std::endl << std::endl;
    6. showHelp(argv[0]);
    7. return -1;
    8. }
    9. }
    10. else {
    11. if (pcl::io::loadPLYFile(argv[filenames_idx[0]], *source_cloud) < 0) {
    12. std::cout << "Error loading point cloud " << argv[filenames_idx[0]] << std::endl << std::endl;
    13. showHelp(argv[0]);
    14. return -1;
    15. }
    16. }

    2.5 定义变换矩阵

    4x4的变换矩阵,前3x3控制旋转,最右边一列的前3行控制平移。

    刚体变换:物体的位置(平移变换)和朝向(旋转变换)发生改变,而形状不变。

    2.5.1 预备知识

    (1)绕x轴逆时针旋转\theta角度 

    \begin{bmatrix} 1 & 0 & 0 & 0 \\ 0 & cos\theta & -sin\theta & 0 \\ 0 & sin\theta & cos\theta & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix} \begin{bmatrix} x \\ y \\ z \\ 1 \end{bmatrix} = \begin{bmatrix} x \\ ycos\theta - zsin\theta \\ ysin\theta + zcos\theta \\ 1 \end{}" role="presentation">\begin{bmatrix} 1 & 0 & 0 & 0 \\ 0 & cos\theta & -sin\theta & 0 \\ 0 & sin\theta & cos\theta & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix} \begin{bmatrix} x \\ y \\ z \\ 1 \end{bmatrix} = \begin{bmatrix} x \\ ycos\theta - zsin\theta \\ ysin\theta + zcos\theta \\ 1 \end{}

    (2)绕y轴逆时针旋转\theta角度

    \begin{bmatrix} cos\theta & 0 & sin\theta & 0 \\ 0 & 1 & 0 & 0 \\ -sin\theta & 0 & cos\theta & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix} \begin{bmatrix} x \\ y \\ z \\ 1 \end{bmatrix} = \begin{bmatrix} xcos\theta + zsin\theta \\ y \\ -xsin\theta + zcos\theta \\ 1 \end{}" role="presentation">\begin{bmatrix} cos\theta & 0 & sin\theta & 0 \\ 0 & 1 & 0 & 0 \\ -sin\theta & 0 & cos\theta & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix} \begin{bmatrix} x \\ y \\ z \\ 1 \end{bmatrix} = \begin{bmatrix} xcos\theta + zsin\theta \\ y \\ -xsin\theta + zcos\theta \\ 1 \end{}

    (3)绕z轴逆时针旋转\theta角度 

    \begin{bmatrix} cos\theta& -sin\theta & 0 & 0 \\ sin\theta & cos\theta & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix} \begin{bmatrix} x \\ y \\ z \\ 1 \end{bmatrix} = \begin{bmatrix} xcos\theta - ysin\theta \\ xsin\theta + ycos\theta \\ z \\ 1 \end{}" role="presentation">\begin{bmatrix} cos\theta& -sin\theta & 0 & 0 \\ sin\theta & cos\theta & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix} \begin{bmatrix} x \\ y \\ z \\ 1 \end{bmatrix} = \begin{bmatrix} xcos\theta - ysin\theta \\ xsin\theta + ycos\theta \\ z \\ 1 \end{}

    (4)平移

    点A(a,b,c)和点B(x+a,y+b,z+c),点A沿着AB平移到B.

    \begin{bmatrix} 1 & 0 & 0 & a \\ 0 & 1 & 0 & b \\ 0 & 0 & 1 & c \\ 0 & 0 & 0 & 1 \end{bmatrix} \begin{bmatrix} x \\ y \\ z \\ 1 \end{bmatrix} = \begin{bmatrix} x + a \\ y + b \\ z + c \\ 1 \end{}" role="presentation">\begin{bmatrix} 1 & 0 & 0 & a \\ 0 & 1 & 0 & b \\ 0 & 0 & 1 & c \\ 0 & 0 & 0 & 1 \end{bmatrix} \begin{bmatrix} x \\ y \\ z \\ 1 \end{bmatrix} = \begin{bmatrix} x + a \\ y + b \\ z + c \\ 1 \end{}

    2.5.2 实操

    (1)手动定义4x4变换矩阵

     \begin{bmatrix} cos\theta & -sin\theta & 0 & 2.5 \\ sin\theta & cos\theta & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 1 \end{}" role="presentation">\begin{bmatrix} cos\theta & -sin\theta & 0 & 2.5 \\ sin\theta & cos\theta & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 1 \end{}

    即绕着z轴旋转\theta角度,再沿着x轴平移2.5。

    1. // 举个例子,说明下转换矩阵是如何工作的。
    2. /* Reminder: how transformation matrices work :
    3. 最右边一列的前3行是控制平移,所以x,y,z这三个值会平移数据。
    4. 其中,x值控制沿着x轴上的平移
    5. |-------> This column is the translation。平移
    6. | 1 0 0 x | \
    7. | 0 1 0 y | }-> The identity 3x3 matrix (no rotation) on the left。旋转。全部是1,则没有变化
    8. | 0 0 1 z | /
    9. | 0 0 0 1 | -> We do not use this line (and it has to stay 0,0,0,1)。这一行不会改变数据。
    10. METHOD #1: Using a Matrix4f
    11. This is the "manual" method, perfect to understand but error prone !
    12. 这个是手动设置的4x4矩阵值,容易理解,但是也容易出错!
    13. */
    14. // 定义4x4转换矩阵
    15. // 前面3x3控制旋转,最右列控制平移
    16. Eigen::Matrix4f transform_1 = Eigen::Matrix4f::Identity(); // 4x4的单位矩阵
    17. // Define a rotation matrix (see https://en.wikipedia.org/wiki/Rotation_matrix)
    18. float theta = M_PI / 4; // The angle of rotation in radians(旋转角度,即弧度)
    19. transform_1(0, 0) = std::cos(theta); // 设置这4个值,可以绕z轴旋转theta角度
    20. transform_1(0, 1) = -sin(theta);
    21. transform_1(1, 0) = sin(theta);
    22. transform_1(1, 1) = std::cos(theta);
    23. // Define a translation of 2.5 meters on the x axis.
    24. transform_1(0, 3) = 2.5; // 沿着x平移2.5
    25. // Print the transformation
    26. printf("Method #1: using a Matrix4f\n");
    27. std::cout << transform_1 << std::endl;

     

     沿着z轴旋转,导致兔子头朝下(右手法则,rgb颜色轴分别是xyz轴)。

    (2)使用Affine3f定义4x4矩阵

    因为是通过函数定义,所以此法更简单,且不容易出错

    1. Eigen::Affine3f transform_2 = Eigen::Affine3f::Identity();
    2. // Define a translation of 2.5 meters on the x axis.
    3. // 1,定义平移
    4. transform_2.translation() << 2.5, 0.0, 0.0;
    5. // The same rotation matrix as before; theta radians around Z axis
    6. // 2,定义然后Z旋转
    7. transform_2.rotate(Eigen::AngleAxisf(theta, Eigen::Vector3f::UnitZ())); // UnitZ 即z轴

    两种方法的矩阵值是一样的。 

     2.6 变换

    1. // Executing the transformation
    2. // 定义存放变换后的点云对象。
    3. pcl::PointCloud::Ptr transformed_cloud(new pcl::PointCloud());
    4. // You can either apply transform_1 or transform_2; they are the same
    5. pcl::transformPointCloud(*source_cloud, *transformed_cloud, transform_2);

    2.7 可视化

    1. // Visualization
    2. printf("\nPoint cloud colors : white = original point cloud\n"
    3. " red = transformed point cloud\n");
    4. pcl::visualization::PCLVisualizer viewer("Matrix transformation example"); // 定义可视化对象
    5. // 1,设置原始点云颜色
    6. // Define R,G,B colors for the point cloud
    7. pcl::visualization::PointCloudColorHandlerCustom source_cloud_color_handler(source_cloud, 255, 255, 255);
    8. // We add the point cloud to the viewer and pass the color handler
    9. viewer.addPointCloud(source_cloud, source_cloud_color_handler, "original_cloud");
    10. // 2,设置新点云颜色
    11. pcl::visualization::PointCloudColorHandlerCustom transformed_cloud_color_handler(transformed_cloud, 230, 20, 20); // Red
    12. viewer.addPointCloud(transformed_cloud, transformed_cloud_color_handler, "transformed_cloud");
    13. // 3,其他设置:坐标、背景颜色、渲染
    14. viewer.addCoordinateSystem(1.0, "cloud", 0);
    15. viewer.setBackgroundColor(0.05, 0.05, 0.05, 0); // Setting background to a dark grey
    16. viewer.setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 2, "original_cloud");
    17. viewer.setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 2, "transformed_cloud");
    18. //viewer.setPosition(800, 400); // Setting visualiser window position
    19. while (!viewer.wasStopped()) { // Display the visualiser until 'q' key is pressed
    20. viewer.spinOnce();
    21. }

    3. 整合代码

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include // pcl::console::find_switch
    6. #include // pcl::transformPointCloud
    7. #include // pcl::visualization::PCLVisualizer
    8. // This function displays the help
    9. // 用于提醒输出参数
    10. void
    11. showHelp(char* program_name)
    12. {
    13. std::cout << std::endl;
    14. std::cout << "Usage: " << program_name << " cloud_filename.[pcd|ply]" << std::endl;
    15. std::cout << "-h: Show this help." << std::endl;
    16. }
    17. // This is the main function
    18. int
    19. main(int argc, char** argv)
    20. {
    21. // Show help,find_switch:argv命令行中是否有-h,有则运行下面函数
    22. if (pcl::console::find_switch(argc, argv, "-h") || pcl::console::find_switch(argc, argv, "--help")) {
    23. showHelp(argv[0]);
    24. return 0;
    25. }
    26. // Fetch point cloud filename in arguments | Works with PCD and PLY files
    27. std::vector<int> filenames_idx; // 存储文件名索引的数组
    28. bool file_is_pcd = false;
    29. // 返回文件名索引的数组, filenames[0]是第0个文件名的索引,
    30. // argv[filenames[0]]第0个文件名
    31. filenames_idx = pcl::console::parse_file_extension_argument(argc, argv, ".ply");
    32. if (filenames_idx.size() != 1) { // 不是ply文件
    33. filenames_idx = pcl::console::parse_file_extension_argument(argc, argv, ".pcd");
    34. if (filenames_idx.size() != 1) { // 也不是pcd文件
    35. showHelp(argv[0]);
    36. return -1;
    37. }
    38. else {
    39. file_is_pcd = true;
    40. }
    41. }
    42. // Load file | Works with PCD and PLY files
    43. pcl::PointCloud::Ptr source_cloud(new pcl::PointCloud());
    44. if (file_is_pcd) {
    45. if (pcl::io::loadPCDFile(argv[filenames_idx[0]], *source_cloud) < 0) { // 默认只有一个文件,该文件名:argv[filenames_idx[0]]
    46. std::cout << "Error loading point cloud " << argv[filenames_idx[0]] << std::endl << std::endl;
    47. showHelp(argv[0]);
    48. return -1;
    49. }
    50. }
    51. else {
    52. if (pcl::io::loadPLYFile(argv[filenames_idx[0]], *source_cloud) < 0) {
    53. std::cout << "Error loading point cloud " << argv[filenames_idx[0]] << std::endl << std::endl;
    54. showHelp(argv[0]);
    55. return -1;
    56. }
    57. }
    58. // 举个例子,说明下转换矩阵是如何工作的。
    59. /* Reminder: how transformation matrices work :
    60. 最右边一列的前3行是控制平移,所以x,y,z这三个值会平移数据。
    61. 其中,x值控制沿着x轴上的平移
    62. |-------> This column is the translation。平移
    63. | 1 0 0 x | \
    64. | 0 1 0 y | }-> The identity 3x3 matrix (no rotation) on the left。旋转。全部是1,则没有变化
    65. | 0 0 1 z | /
    66. | 0 0 0 1 | -> We do not use this line (and it has to stay 0,0,0,1)。这一行不会改变数据。
    67. METHOD #1: Using a Matrix4f
    68. This is the "manual" method, perfect to understand but error prone !
    69. 这个是手动设置的4x4矩阵值,容易理解,但是也容易出错!
    70. */
    71. // 定义4x4转换矩阵
    72. // 前面3x3控制旋转,最右列控制平移
    73. Eigen::Matrix4f transform_1 = Eigen::Matrix4f::Identity(); // 4x4的单位矩阵
    74. // Define a rotation matrix (see https://en.wikipedia.org/wiki/Rotation_matrix)
    75. float theta = M_PI / 4; // The angle of rotation in radians(旋转角度,即弧度)
    76. transform_1(0, 0) = std::cos(theta); // 设置这4个值,可以绕z轴旋转theta角度
    77. transform_1(0, 1) = -sin(theta);
    78. transform_1(1, 0) = sin(theta);
    79. transform_1(1, 1) = std::cos(theta);
    80. // Define a translation of 2.5 meters on the x axis.
    81. transform_1(0, 3) = 2.5; // 沿着x平移2.5
    82. // Print the transformation
    83. printf("Method #1: using a Matrix4f\n");
    84. std::cout << transform_1 << std::endl;
    85. /* METHOD #2: Using a Affine3f
    86. This method is easier and less error prone
    87. */
    88. Eigen::Affine3f transform_2 = Eigen::Affine3f::Identity();
    89. // Define a translation of 2.5 meters on the x axis.
    90. // 1,定义平移
    91. transform_2.translation() << 2.5, 0.0, 0.0;
    92. // The same rotation matrix as before; theta radians around Z axis
    93. // 2,定义然后Z旋转
    94. transform_2.rotate(Eigen::AngleAxisf(theta, Eigen::Vector3f::UnitZ())); // UnitZ 即z轴
    95. // Print the transformation
    96. printf("\nMethod #2: using an Affine3f\n");
    97. std::cout << transform_2.matrix() << std::endl;
    98. // Executing the transformation
    99. // 定义存放变换后的点云对象。
    100. pcl::PointCloud::Ptr transformed_cloud(new pcl::PointCloud());
    101. // You can either apply transform_1 or transform_2; they are the same
    102. pcl::transformPointCloud(*source_cloud, *transformed_cloud, transform_2);
    103. // Visualization
    104. printf("\nPoint cloud colors : white = original point cloud\n"
    105. " red = transformed point cloud\n");
    106. pcl::visualization::PCLVisualizer viewer("Matrix transformation example"); // 定义可视化对象
    107. // 1,设置原始点云颜色
    108. // Define R,G,B colors for the point cloud
    109. pcl::visualization::PointCloudColorHandlerCustom source_cloud_color_handler(source_cloud, 255, 255, 255);
    110. // We add the point cloud to the viewer and pass the color handler
    111. viewer.addPointCloud(source_cloud, source_cloud_color_handler, "original_cloud");
    112. // 2,设置新点云颜色
    113. pcl::visualization::PointCloudColorHandlerCustom transformed_cloud_color_handler(transformed_cloud, 230, 20, 20); // Red
    114. viewer.addPointCloud(transformed_cloud, transformed_cloud_color_handler, "transformed_cloud");
    115. // 3,其他设置:坐标、背景颜色、渲染
    116. viewer.addCoordinateSystem(1.0, "cloud", 0);
    117. viewer.setBackgroundColor(0.05, 0.05, 0.05, 0); // Setting background to a dark grey
    118. viewer.setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 2, "original_cloud");
    119. viewer.setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 2, "transformed_cloud");
    120. //viewer.setPosition(800, 400); // Setting visualiser window position
    121. while (!viewer.wasStopped()) { // Display the visualiser until 'q' key is pressed
    122. viewer.spinOnce();
    123. }
    124. return 0;
    125. }

    参考:Using a matrix to transform a point cloud — Point Cloud Library 0.0 documentation

  • 相关阅读:
    Web安全—Web漏扫工具Nikto安装与使用
    《MySQL实战45讲》——学习笔记12 “InnoDB刷脏页的控制策略“
    Ubuntu下qt编译问题
    【WSN通信】基于最佳簇半径的无线传感器网络分簇路由算法附matlab代码
    SpringBoot定时任务实现方式--超好用
    数据结构 基数排序的优化
    Zoho Mail荣登福布斯小型企业企业邮箱排行榜
    Lyapunov稳定性分析3(离散时间系统)
    [LeetCode]-138. 随机链表的复制
    net转java学习笔记-sqlserver的问题记录
  • 原文地址:https://blog.csdn.net/jizhidexiaoming/article/details/127653543