livox CustomMsg、ros pointcloud2、open3d、numpy互转(python)

不同格式点云与numpy的互相转换

写在前面

  • 本文内容
    不同格式的点云:livox CustomMsg、ros pointcloud2、open3d、numpy之间的互相转换
    主要是将不同的格式都转成numpy,然后用numpy再转成其他格式

  • 平台/环境
    ros, ubuntu1804

  • 转载请注明出处:
    https://blog.csdn.net/qq_41102371/article/details/139834499

代码

依赖

import rospy
import open3d as o3d
import numpy as np
from livox_ros_driver.msg import CustomMsg
from sensor_msgs.msg import PointCloud2
import ros_numpy
import copy

livox2numpy

将xyz,intensity转化成(n, 4)的numpy,n是点云的点数

def livox2numpy(msg: CustomMsg) -> np.array:
    msg_points = msg.points
    x = np.array([p.x for p in msg_points])
    y = np.array([p.y for p in msg_points])
    z = np.array([p.z for p in msg_points])
    intensity = np.array([p.reflectivity for p in msg_points])
    xyz = np.stack((x, y, z, intensity), axis=-1)

    return xyz

rospcd22numpy

rospcd2转numpy

    def rospcd22numpy(self, rospcd2: PointCloud2) -> np.array:
        pc_array = ros_numpy.numpify(rospcd2)
        pcd_np = np.zeros([len(pc_array), 4])
        pcd_np[:, 0] = pc_array['x']
        pcd_np[:, 1] = pc_array['y']
        pcd_np[:, 2] = pc_array['z']
        pcd_np[:, 3] = pc_array['intensity']
        print("pcd_np.shape: ", pcd_np.shape)
        return pcd_np

注意发布到rospcd2的时候,如果是pcl转的sensor_msgs::PointCloud2,那么pcl的格式最好是pcl::PointXYZI,不要用pcl::PointXYZINormal,不然订阅后使用rospcd22numpy时ros_numpy会报错

pcl::PointCloud<pcl::PointXYZI>::Ptr pcd(new pcl::PointCloud<pcl::PointXYZI>());

....

sensor_msgs::PointCloud2 pcd_msg;
pcl::toROSMsg(*pcd, pcd_msg);

open3d2numpy

def open3d2numpy(pcd_o3d: o3d.geometry.PointCloud) -> np.array:
    print("convert pcd size: ", len(pcd_o3d.points))
    pcd_np = np.asarray(pcd_o3d.points)
    if pcd_o3d.has_colors():
        colos = np.asarray(pcd_o3d.colors)
        pcd_np = np.concatenate((pcd_np, colos), axis=1)
    return pcd_np

numpy2open3d


def numpy2open3d(pcd_np: np.array) -> o3d.geometry.PointCloud:
    pcd_o3d = o3d.geometry.PointCloud()
    print("numpy2open3d")
    pcd_xyz = copy.deepcopy(pcd_np[:, :3])
    pcd_o3d.points = o3d.utility.Vector3dVector(pcd_xyz)
    if pcd_np.shape[1] == 4:
        color = copy.deepcopy(pcd_np[:, 3]).reshape(
            pcd_np.shape[0], 1)/255.0
        color = np.repeat(color, 3, axis=1)
        print("color.shape: ", color.shape)
        pcd_o3d.colors = o3d.utility.Vector3dVector(color)

    if pcd_np.shape[1] == 6:
        color = copy.deepcopy(pcd_np[:, 3:])
        print("color.shape: ", color.shape)
        pcd_o3d.colors = o3d.utility.Vector3dVector(color)
        
    return pcd_o3d

numpy2rospcd2

def numpy2rospcd(pcd_np: np.array, frame_id: str = "livox_frame") -> PointCloud2:
    data = np.zeros(pcd_np.shape[0],
                    dtype=[
                        ('x', np.float32),
                        ('y', np.float32),
                        ('z', np.float32),
                        ('intensity', np.float32),
    ])
    data['x'] = pcd_np[:, 0]
    data['y'] = pcd_np[:, 1]
    data['z'] = pcd_np[:, 2]
    data['intensity'] = pcd_np[:, 3]
    rospcd2 = ros_numpy.msgify(PointCloud2, data)
    rospcd2.header.frame_id = frame_id
    rospcd2.header.stamp = rospy.Time().now()
    rospcd2.height = 1
    rospcd2.width = pcd_np.shape[0]
    print("convert points: ", rospcd2.width)
    return rospcd2

参考

https://www.open3d.org/docs/release/tutorial/geometry/working_with_numpy.html
https://wiki.ros.org/livox_ros_driver

主要做激光/影像三维重建,配准、分割等常用点云算法,熟悉open3d、pcl等开源点云库,技术交流、咨询可私信

### 将 `sensor_msgs/PointCloud2` 转换为 `livox_ros_driver/CustomMsg` 要完成从 `sensor_msgs/PointCloud2` 到 `livox_ros_driver/CustomMsg` 的转换,可以按照以下方式设计解决方案。此过程涉及解析 `Pointcloud2` 数据结构并将其重新封装成 Livox 自定义消息格式。 #### 解决方案概述 1. **理解两种数据格式** - `sensor_msgs/PointCloud2`: 这是一个通用的点云表示形式,其中包含了多个字段(如 x, y, z 坐标以及强度等)。它是一种紧凑的二进制编码格式[^1]。 - `livox_ros_driver/CustomMsg`: 此自定义消息通常用于特定硬件设备的数据传输,其内部可能包含更具体的参数设置和点云信息。 2. **编写转换逻辑** 使用 ROS 提供的消息工具来提取 `sensor_msgs::PointCloud2` 中的信息,并填充到 `livox_ros_driver::CustomPacket` 或其他相关消息类型中。 以下是具体实现代码: ```cpp #include <ros/ros.h> #include <sensor_msgs/PointCloud2.h> #include <livox_ros_driver/CustomMsg.h> void pointCloudCallback(const sensor_msgs::PointCloud2ConstPtr& input_cloud) { // 创建目标 CustomMsg 对象实例 livox_ros_driver::CustomMsg custom_msg; // 初始化变量存储 PointCloud2 字段索引 int x_idx = -1; int y_idx = -1; int z_idx = -1; int intensity_idx = -1; // 获取字段偏移量及其对应位置 for (size_t i = 0; i < input_cloud->fields.size(); ++i) { if (input_cloud->fields[i].name == "x") { x_idx = i; } if (input_cloud->fields[i].name == "y") { y_idx = i; } if (input_cloud->fields[i].name == "z") { z_idx = i; } if (input_cloud->fields[i].name == "intensity") { intensity_idx = i; } } // 检查必要字段是否存在 if (x_idx == -1 || y_idx == -1 || z_idx == -1) { ROS_ERROR("Missing required fields in the PointCloud2 message."); return; } // 遍历每个点并将它们填入 CustomMsg 结构体 size_t num_points = input_cloud->width * input_cloud->height; std::vector<livox_ros_driver::Point> points(num_points); float* data_ptr = reinterpret_cast<float*>(&input_cloud->data[0]); for (size_t idx = 0; idx < num_points; ++idx) { livox_ros_driver::Point pt; pt.x = data_ptr[idx * input_cloud->point_step + input_cloud->fields[x_idx].offset]; pt.y = data_ptr[idx * input_cloud->point_step + input_cloud->fields[y_idx].offset]; pt.z = data_ptr[idx * input_cloud->point_step + input_cloud->fields[z_idx].offset]; if (intensity_idx != -1) { pt.intensity = data_ptr[idx * input_cloud->point_step + input_cloud->fields[intensity_idx].offset]; } else { pt.intensity = 0.0f; // 如果无强度,则默认设为零 } points[idx] = pt; } // 设置 CustomMsg 的成员属性 custom_msg.points = points; custom_msg.header.stamp = input_cloud->header.stamp; custom_msg.header.frame_id = input_cloud->header.frame_id; // 发布或进一步处理 custom_msg... } int main(int argc, char** argv) { ros::init(argc, argv, "pointcloud_converter"); ros::NodeHandle nh("~"); ros::Subscriber sub = nh.subscribe("/pointcloud_topic", 1, pointCloudCallback); ros::Publisher pub_custom = nh.advertise<livox_ros_driver::CustomMsg>("custom_msg_output", 1); ros::spin(); return 0; } ``` 上述代码实现了如下功能: - 订阅输入的 `sensor_msgs::PointCloud2` 主题; - 解析该主题中的点坐标及其他可选字段(例如强度); - 构造一个新的 `livox_ros_driver::CustomMsg` 并发布至指定的主题; #### 注意事项 - 上述代码假设 `livox_ros_driver::CustomMsg` 包含了一个名为 `points` 的向量容器,用来保存所有的三维点。如果实际定义不同,请调整相应部分。 - 确保编译环境已安装好对应的依赖包(即 `livox_ros_driver` 和标准 ROS 消息头文件),并通过 CMakeLists.txt 文件正确链接这些库[^2]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

诺有缸的高飞鸟

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

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

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

打赏作者

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

抵扣说明:

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

余额充值