多个Livox雷达点云合成及使用ROS发布

该代码示例展示了如何在ROS环境中处理三个Livox雷达的数据,将它们的点云数据转换成PointCloud2格式进行拼接,再转换回自定义消息格式供Fast-LIO使用。通过message_filters实现不同话题的同步,确保数据一致性。同时,使用Eigen矩阵进行坐标变换以合并点云。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

项目场景:

因为单个Livox avia的FOV只有70°,无法覆盖车前方的所有范围,所以用了三个Livox avia以实现180°前方位覆盖。但由于三个雷达均是独自采集,所以需要对每个雷达采集的各帧点云进行合并,用于建图。以下工作均建立于已经知道各雷达之间的外参。
在这里插入图片描述


问题描述

由于Fast-LIO输入的是Livox自定义的Msg,所以需要先订阅每个雷达的topic,将其格式转换成PointCloud2格式,在该格式下对三个雷达的点云进行拼接,最后将拼接后的点云转回Livox自定义的CustomMsg即可输入给Fast-LIO,代码如下所示

#include <ros/ros.h>
#include <livox_ros_driver/CustomMsg.h>
#include <sensor_msgs/PointCloud2.h>
#include <pcl_conversions/pcl_conversions.h>
#include <pcl/common/transforms.h>
#include <pcl/point_cloud.h>
#include <pcl/point_types.h>
#include <pcl/io/io.h>
#include <message_filters/subscriber.h>
#include <message_filters/synchronizer.h>
#include <message_filters/sync_policies/approximate_time.h>
#include <livox_ros_driver/CustomMsg.h>
#include <iostream>

typedef pcl::PointXYZINormal PointType;
typedef pcl::PointCloud<PointType> PointCloudXYZI;

class SubscribeAndPublish
{
public:
	SubscribeAndPublish(){
		pub = node.advertise<livox_ros_driver::CustomMsg>("/livox/lidar", 10);
		sub_mid = new message_filters::Subscriber<livox_ros_driver::CustomMsg>(node, "/livox/lidar_3JEDK5D0011F371", 200000);
		sub_left = new message_filters::Subscriber<livox_ros_driver::CustomMsg>(node,  "/livox/lidar_3JEDK620011H571", 200000);
		sub_right = new message_filters::Subscriber<livox_ros_driver::CustomMsg>(node, "/livox/lidar_3JEDK6G00134651", 200000);
		
		typedef message_filters::sync_policies::ApproximateTime<livox_ros_driver::CustomMsg,
																													              livox_ros_driver::CustomMsg, 
		   																											 			  livox_ros_driver::CustomMsg> syncPolicy;
		message_filters::Synchronizer<syncPolicy> sync(syncPolicy(9), *sub_mid, *sub_left, *sub_right);
		sync.registerCallback(boost::bind(&SubscribeAndPublish::callBack, this,  _1, _2, _3));

		ros::spin();
	}

	void callBack(const livox_ros_driver::CustomMsgConstPtr& lidar_mid, 
								 const livox_ros_driver::CustomMsgConstPtr& lidar_left,
								 const livox_ros_driver::CustomMsgConstPtr& lidar_right){
			PointCloudXYZI pointCloud_mid;
			PointCloudXYZI pointCloud_left;
			PointCloudXYZI pointCloud_left_out;
			PointCloudXYZI pointCloud_right;
			PointCloudXYZI pointCloud_right_out;
			PointCloudXYZI finalPointCloud;
			convert2PointCloud2(lidar_mid, pointCloud_mid);
			convert2PointCloud2(lidar_left, pointCloud_left);
			convert2PointCloud2(lidar_right, pointCloud_right);
			Eigen::Matrix4f transform2Left = Eigen::Matrix4f::Identity(); 
				transform2Left(0, 0) = 0.6380962; 
				transform2Left(0, 1) = -0.7699567;
				transform2Left(0, 2) = 0;
				transform2Left(0, 3) = -0.05;
				transform2Left(1, 0) =  0.7699567; 
				transform2Left(1, 1) = 0.6380962;
				transform2Left(1, 2) = 0;
				transform2Left(1, 3) = 0.15;
				transform2Left(2, 0) = 0;
				transform2Left(2, 1) = 0;
				transform2Left(2, 2) = 1;
				transform2Left(2, 3) = 0;

			Eigen::Matrix4f transform2Right = Eigen::Matrix4f::Identity(); 
				transform2Right(0, 0) = 0.6461240; 
				transform2Right(0, 1) = 0.7632325;
				transform2Right(0, 2) =  0;
				transform2Right(0, 3) = -0.05;
				transform2Right(1, 0) =  -0.7632325; 
				transform2Right(1, 1) = 0.6461240;
				transform2Right(1, 2) = 0;
				transform2Right(1, 3) = -0.15;
				transform2Right(2, 0) = 0;
				transform2Right(2, 1) = 0;
				transform2Right(2, 2) = 1;
				transform2Right(2, 3) = 0;

				pcl::transformPointCloud(pointCloud_left, pointCloud_left_out, transform2Left);
				pcl::transformPointCloud(pointCloud_right, pointCloud_right_out, transform2Right);

				finalPointCloud = pointCloud_mid + pointCloud_left_out ;
				finalPointCloud = finalPointCloud + pointCloud_right_out;

				livox_ros_driver::CustomMsg finalMsg;
				finalMsg.header = lidar_mid->header;
				finalMsg.timebase = lidar_mid->timebase;
				finalMsg.point_num = finalPointCloud.size();
				finalMsg.lidar_id = lidar_mid->lidar_id;
				
				for(unsigned int i = 0; i < finalMsg.point_num; i++)
				{
					livox_ros_driver::CustomPoint p;
					p.x = finalPointCloud[i].x;
					p.y = finalPointCloud[i].y;
					p.z = finalPointCloud[i].z;
					p.reflectivity = finalPointCloud[i].intensity;
					p.offset_time = finalPointCloud[i].curvature * float(1000000);
					finalMsg.points.push_back(p);
				}

				pub.publish(finalMsg);

	}

	void convert2PointCloud2(const livox_ros_driver::CustomMsgConstPtr& lidarMsg, PointCloudXYZI& pclPointCloud ){
		for(unsigned int i = 0; i < lidarMsg->point_num; i++)
		{
			PointType point;
			point.x = lidarMsg->points[i].x;
			point.y = lidarMsg->points[i].y;
			point.z = lidarMsg->points[i].z;
			point.intensity = lidarMsg->points[i].reflectivity;
			point.curvature = lidarMsg->points[i].offset_time / float(1000000); 
			pclPointCloud.push_back(point);
		}
	}

	


private:
	ros::NodeHandle node;
	ros::Publisher pub;
	message_filters::Subscriber<livox_ros_driver::CustomMsg>* sub_mid;
	message_filters::Subscriber<livox_ros_driver::CustomMsg>* sub_right;
	message_filters::Subscriber<livox_ros_driver::CustomMsg>* sub_left;
};

int main(int argc, char* argv[]){
	ros::init(argc, argv, "pointCloudMerge");

	SubscribeAndPublish sap;

	return 0;
}

代码分析:

上述代码实现了在同一个节点中订阅多个topic并发布topic的功能。由于订阅了多个topic,所以需要对不同的topoic进行时间同步,时间同步用到了ROS中的ApproximateTime的功能,通过message_filters::Synchronizer 统一接收多个主题,只有在所有的topic都有相同的时间戳时,才会产生一个同步结果的回调函数,在回调函数里处理同步时间后的数据。还需要注意的是由于作用域的问题,在main函数中使用ros::spin()会使得回调函数得不到调用,如果跟我一样使用类来完成在一个节点中订阅和发布的话,则需要跟Subscriber在同一个代码块中才能够成功唤起回调函数。使用message_filters需要在cmakelist和package.xml中更改相应的代码以便在编译的时候能找到相关依赖。

参考网站

https://www.cnblogs.com/long5683/p/13223458.html
https://blog.youkuaiyun.com/u013172864/article/details/106614962
http://zhaoxuhui.top/blog/2019/10/20/ros-note-7.html
https://blog.youkuaiyun.com/Laney_Midory/article/details/120040285
https://blog.youkuaiyun.com/qq_37683987/article/details/96432874

### Livox 雷达点云数据格式转换的方法和工具 #### 工具介绍 为了便于将 Livox SDK 生成的 LVX 格式点云数据转换为其他常见格式,存在一个专门为此目的开发的工具。该工具支持将LVX格式的数据转化为PCD、LAS以及TXT三种常用点云文件格式[^1]。 #### 特性概述 此转换工具有如下几个显著特性: - **多格式支持**:可以将原始的LVX格式转成PCD、LAS或是TXT格式。 - **高性能处理能力**:针对大规模点云数据进行了优化,确保高效的转换速度。 - **简易操作界面**:提供了直观易懂的命令行参数设置方式,降低了用户的入门门槛。 #### 实际应用案例 在实际应用场景下,当获取到由Livox激光雷达设备收集而来的LVX格式点云资料之后,可以通过上述提到的专用软件将其转变为更适合进一步加工分析的形式——比如PCD或LAS格式。这样的转变不仅有助于简化后续工作流程中的数据管理环节,同时也促进了不同平台间的信息共享与交流[^2]。 #### 使用指南片段 对于具体的操作步骤,在准备阶段可能涉及到解压缩所获得的数据包,例如执行`unzip Livox\ Mid-100\ Point\ Cloud\ Data\ 1.zip`来释放目标文件的内容[^3];而在正式实施转换之前,则需参照特定版本的文档说明来进行必要的配置调整并运行对应的脚本程序完成整个过程。 ```bash # 解压数据示例 unzip "Livox Mid-100 Point Cloud Data 1.zip" ```
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值