[无人驾驶]无外参矩阵下的多激光雷达对齐

0、背景

有段时间,采集了个数据,里面包含多个激光雷达的数据,具体为顶装的40线激光雷达,侧装的32线激光雷达。但是激光雷达间并没有对齐,为了使用上数据,想了一些办法将数据对齐,即转换到同一个坐标系下。

1、思路

  1. 一开始使用ICP算法进行对齐,但是效果很差,弃之。

  2. 使用手动标点,即在不同的雷达点云中,找到表征同一个位置的点的坐标,选取几十个点,最后计算外参矩阵。缺点是手动找点工作量大,且找的对应点不准确,误差大。尝试后效果也不行,但是比ICP好一些,亦弃之。

  3. NDT算法对齐。我发现这个算法真的厉害,在X、Y轴对的很准,z轴对的不是很准,可能是数据在z轴没有什么区分度,但是也可以用了,具体采用PCL点云库实现。

2、数据介绍

(1)坐标系介绍
如下图,X轴指向车辆右方,Y轴指向车辆前方,Z轴指向车顶,遵循右手坐标系。
3个激光雷达的位置如图,1个绿色方框圈住的是顶装的40线激光雷达,2个红色方框圈住的侧装的32线激光雷达。
采集的点云数据都是以各自的激光雷达为坐标系,目的是将侧装的32线数据对齐到40线激光雷达的坐标系中,进行多传感器融合。
在这里插入图片描述
(2)数据展示
使用matlab展示了3个激光雷达的点云数据,每幅图上的标题显示了对应的传感器。
在这里插入图片描述
将3个点云画在一个坐标系中,发现根本没有对齐。
在这里插入图片描述

3、NDT算法

(1)头文件

#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>        //点类型定义头文件
#include<pcl/point_cloud.h>
#include<pcl/visualization/cloud_viewer.h>
#include <pcl/registration/icp.h>   //ICP配准类相关头文件
#include <pcl/filters/approximate_voxel_grid.h>
#include <boost/thread/thread.hpp>
#include <pcl/registration/ndt.h>

(2)NDT代码

int NDT(pcl::PointCloud<pcl::PointXYZ>::Ptr input_cloud, pcl::PointCloud<pcl::PointXYZ>::Ptr target_cloud) //normal distribution transform
{

	std::cout << "Loaded " << target_cloud->size() << " data points from target_cloud" << std::endl;

	std::cout << "Loaded " << input_cloud->size() << " data points from input_cloud" << std::endl;
	//将输入的扫描过滤到原始尺寸的大概10%以提高匹配的速度。
	pcl::PointCloud<pcl::PointXYZ>::Ptr filtered_cloud(new pcl::PointCloud<pcl::PointXYZ>);
	pcl::ApproximateVoxelGrid<pcl::PointXYZ> approximate_voxel_filter;
	approximate_voxel_filter.setLeafSize(0.2, 0.2, 0.2);
	approximate_voxel_filter.setInputCloud(input_cloud);
	approximate_voxel_filter.filter(*filtered_cloud);
	std::cout << "Filtered cloud contains " << filtered_cloud->size()
		<< " data points from input_cloud" << std::endl;
	//初始化正态分布变换(NDT)
	pcl::NormalDistributionsTransform<pcl::PointXYZ, pcl::PointXYZ> ndt;
	//设置依赖尺度NDT参数
	//为终止条件设置最小转换差异
	ndt.setTransformationEpsilon(0.01);
	//为More-Thuente线搜索设置最大步长
	ndt.setStepSize(0.1);
	//设置NDT网格结构的分辨率(VoxelGridCovariance)
	ndt.setResolution(1.0);
	//设置匹配迭代的最大次数
	ndt.setMaximumIterations(35);  
	// 设置要配准的点云
	ndt.setInputCloud(filtered_cloud);
	//设置点云配准目标
	ndt.setInputTarget(target_cloud);
	//设置使用ICP得到的初始对准估计结果                                                     
	Eigen::AngleAxisf init_rotation(0, Eigen::Vector3f::UnitZ());                      //左雷达0          右雷达 0
	//                                                                                (3)NDT函数中左/右雷达相应的初始估计
	Eigen::Translation3f init_translation(-0.704139, -0.0467357, -0.243794);             //左雷达初始估计值     
	//Eigen::Translation3f init_translation(0.674899, -0.014631, -0.231773);              //右雷达初始估计值 
	Eigen::Matrix4f init_guess = (init_translation * init_rotation).matrix();
	//计算需要的刚体变换以便将输入的点云匹配到目标点云
	pcl::PointCloud<pcl::PointXYZ>::Ptr output_cloud(new pcl::PointCloud<pcl::PointXYZ>);
	ndt.align(*output_cloud, init_guess);
	std::cout << "Normal Distributions Transform has converged:" << ndt.hasConverged()
		<< " score: " << ndt.getFitnessScore() << std::endl;
	std::cout << ndt.getFinalTransformation() << std::endl;
	//使用创建的变换对未过滤的输入点云进行变换
	pcl::transformPointCloud(*input_cloud, *output_cloud, ndt.getFinalTransformation());
	//保存转换的输入点云
	pcl::io::savePCDFileASCII("room_scan2_transformed.pcd", *output_cloud);
	// 初始化点云可视化界面
	boost::shared_ptr<pcl::visualization::PCLVisualizer>
		viewer_final(new pcl::visualization::PCLVisualizer("3D Viewer"));
	viewer_final->setBackgroundColor(0, 0, 0);
	//对目标点云着色(红色)并可视化
	pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ>
		target_color(target_cloud, 255, 0, 0);
	viewer_final->addPointCloud<pcl::PointXYZ>(target_cloud, target_color, "target cloud");
	viewer_final->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE,
		1, "target cloud");
	//对转换后的目标点云着色(绿色)并可视化
	pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ>
		output_color(output_cloud, 0, 255, 0);
	viewer_final->addPointCloud<pcl::PointXYZ>(output_cloud, output_color, "output cloud");
	viewer_final->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE,
		1, "output cloud");
	// 启动可视化
	viewer_final->addCoordinateSystem(1.0);
	viewer_final->initCameraParameters();
	//等待直到可视化窗口关闭。
	while (!viewer_final->wasStopped())
	{
		viewer_final->spinOnce(100);
		boost::this_thread::sleep(boost::posix_time::microseconds(100000));
	}
	return (0);
}

(3)计算出的矩阵
在这里插入图片描述

(4)对齐效果
使用matlab将数据进行坐标转换后,可视化。

可以看到,结果非常优秀!
在这里插入图片描述

程序说明: 本程序利用MATLAB编写,实现了激光雷达与IMU卡尔曼滤波融合,其中激光雷达数据采用简单的离散圆环面统计方式,IMU数据采用加速度计和陀螺仪数据。程序主要分为数据读入、初始化、预测、更新四个部分。具体实现流程如下: 1、数据读入:程序首先读取激光雷达数据和IMU数据,这里需要注意两个数据的时间戳需要对齐。 2、初始化:对状态量进行初始化,包括位置、速度、姿态等等。 3、预测:根据IMU数据进行卡尔曼滤波预测,更新位置、速度和姿态等状态量。 4、更新:根据激光雷达测量数据和预测值进行差值计算,利用卡尔曼滤波更新状态量和协方差矩阵。 程序中主要采用Matlab内置函数实现卡尔曼滤波,包括KF.predict、KF.update等函数。程序中给出了示例数据,可以直接运行进行仿真。 程序代码: % LIDAR and IMU fusion clc; clear; % load data load LIDAR; % LIDAR data load IMU; % IMU data % Parameter initialization dt = 0.05; % time step g = 9.81; % gravity H = [1,0,0,0,0,0; % measurement matrix 0,1,0,0,0,0; 0,0,0,0,1,0]; R = [0.1,0,0; 0,0.1,0; 0,0,0.1]; % measurement noise Q = [0.01,0,0,0,0,0; % process noise 0,0.01,0,0,0,0; 0,0,0.01,0,0,0; 0,0,0,0.01,0,0; 0,0,0,0,0.01,0; 0,0,0,0,0,0.01]; % State initialization X = [0,0,0,0,0,0]'; % state vector P = eye(6); % covariance matrix % Kalman filter for i=1:length(LIDAR)-1 % Predicted state F = [1,0,dt,0,0,0; % state transition matrix 0,1,0,dt,0,0; 0,0,1,0,dt,0; 0,0,0,1,0,dt; 0,0,0,0,1,0; 0,0,0,0,0,1]; B = [0.5*dt^2,0,0; 0,0.5*dt^2,0; dt,0,0; 0,dt,0; 0,0,0.5*dt^2; 0,0,dt]; u = [IMU.ax(i),IMU.ay(i),IMU.az(i)]'; X_prd = F*X + B*u; P_prd = F*P*F' + Q; % Update if LIDAR.t(i) == IMU.t(i) z = [LIDAR.range(i,1),LIDAR.range(i,2),LIDAR.range(i,3)]'; K = P_prd*H'/(H*P_prd*H' + R); % Kalman gain X = X_prd + K*(z - H*X_prd); P = (eye(6) - K*H)*P_prd; end end % Plot the result plot(X(1,:),X(2,:)); % plot the trajectory xlabel('X(m)'); ylabel('Y(m)'); title('LIDAR and IMU fusion'); grid on;
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值