赞
踩
拿纸箱或角反射器作标记物,记录标记点gps位置,让无人机携带激光雷达与组合贯导扫描标记点,提取出含标记点的某一帧点云及其对应的四元素与gps,然后可以通过matlab提取出标记物点云坐标,同时可以根据四元素与gps计算出此时标记点在贯导坐标系中的坐标。按照这种方法,改变标记物位置,获取多组点对,然后通过ICP算法求解出雷达到贯导之间的旋转矩阵。
为了方便提取角点,保存单帧点云前先对其进行滤波处理
为了获得该帧对应的四元素与gps,对点云与贯导获取的数据先时间戳对齐
求解贯导坐标系下的标记点坐标
获取的gps并不能直接用,首先先将gps投影到墨卡托坐标系下,计算出其在地图坐标系(M)下贯导与标记点§间的平移关系:
这里的平移并不是贯导坐标系下的标记点坐标,假设贯导坐标系原点处还有一个与地图坐标系经纬轴平行的坐标系B,关系如图:
【这里为了方便理解,贯导坐标系M与B坐标系原点没有重合】
通过坐标系转换可知B坐标系下的角点坐标为:
通过四元数获得贯导坐标系M到B坐标系的旋转R,B坐标系下的角点坐标左乘旋转矩阵R的逆即可得到惯导坐标系下的角点坐标:
代码
//***************************************************************************************************** // 保存单帧点云中的角反射器角点及其在imu坐标系下的坐标 // // source devel/setup.bash && rosrun cam_laser_calib pcd_points_gps 5 111.pcd //5表示保存第5帧,111.pcd表示保存的pcd文件名 // (我直接在相机雷达标定的功能包里写的,所以包名为“cam_laser_calib” ) // pcl_viewer 111.pcd //************************************************************************************************** #include<ros/ros.h> #include<iostream> #include<fstream> #include<sstream> #include<string> #include<vector> #include <math.h> #include <stdio.h> #include<sensor_msgs/PointCloud2.h> #include "std_msgs/String.h" #include <pcl_conversions/pcl_conversions.h> #include <pcl/point_types.h> #include <pcl/PCLPointCloud2.h> #include <pcl/conversions.h> #include <pcl_ros/transforms.h> #include <pcl/visualization/cloud_viewer.h> #include <pcl/filters/filter.h> #include "tf/transform_datatypes.h" #include <tf2_msgs/TFMessage.h> #include <pcl/point_cloud.h> #include <pcl/io/pcd_io.h> #include <pcl/common/impl/io.hpp> #include <sensor_msgs/PointCloud2.h> #include <pcl/filters/passthrough.h> //直通滤波 #include <sbg_driver/SbgImuData.h> #include <sbg_driver/SbgEkfQuat.h> #include <sbg_driver/SbgGpsPos.h> #include <sbg_driver/SbgEkfNav.h> #include <message_filters/subscriber.h> #include <message_filters/synchronizer.h> #include <message_filters/sync_policies/approximate_time.h> #include <message_filters/sync_policies/exact_time.h> #include <message_filters/time_synchronizer.h> #include <pcl_ros/point_cloud.h> #include <pcl/sample_consensus/method_types.h> #include <pcl/sample_consensus/model_types.h> #include <pcl/sample_consensus/ransac.h> #include <pcl/sample_consensus/sac_model_plane.h> #include <pcl/segmentation/sac_segmentation.h> #include <pcl/visualization/cloud_viewer.h> #include <sensor_msgs/PointCloud2.h> #include <sensor_msgs/PointCloud.h> #include <Eigen/Eigen> #include <Eigen/Dense> #include <Eigen/Geometry> #include <Eigen/Eigenvalues> //角反射器的经纬坐标 double gj=126.62823496; double gw=45.71438903; double gz=138.263; int i=1; int m=0;//保存第m帧 std::string pcd_mame; #define PI 3.1415926 using namespace std; using namespace pcl; //把经纬坐标投影到Web墨卡托 void lonLat2WebMercator(double lon,double lat,double *x,double *y) { *x = lon * 20037508.34 / 180; double ly = log(tan((90+ lat)*PI/360))/(PI/180); *y = ly *20037508.34/180; } void GetDirectDistance(double srcLon, double srcLat,double gz, double destLon, double destLat,double tz,double *x,double *y,double *z) { double x1,x2,y1,y2; lonLat2WebMercator(srcLon,srcLat,&x1,&y1); lonLat2WebMercator(destLon,destLat,&x2,&y2); *x=x2-x1; *y=y2-y1; *z=tz-gz; } pcl::visualization::CloudViewer viewer("viewer"); void chatterCallback(const boost::shared_ptr<const sensor_msgs::PointCloud2>& pointmsg,const boost::shared_ptr<const sbg_driver::SbgEkfNav>& gpsmsg,const boost::shared_ptr<const sbg_driver::SbgEkfQuat>& quatmsg) { cout<<"i="<<i<<endl; pcl::PCLPointCloud2 pcl_pc2; pcl_conversions::toPCL(*pointmsg,pcl_pc2); pcl::PointCloud<pcl::PointXYZ>::Ptr tmp_cloud(new pcl::PointCloud<pcl::PointXYZ>); pcl::fromPCLPointCloud2(pcl_pc2,*tmp_cloud); // 点云直通滤波 pcl::PointCloud<pcl::PointXYZ> cloud; pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_ptr; pcl::PointCloud<pcl::PointXYZ> cloud_new; pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_filtered (new pcl::PointCloud<pcl::PointXYZ>);//点云指针对象 pcl::PassThrough<pcl::PointXYZ> pass; //设置滤波器对象 pass.setInputCloud(tmp_cloud);//设置输入点云 pass.setFilterFieldName("y"); //设置过滤时所需要的点云类型的y字段 pass.setFilterLimits(-6,6); //设置在过滤字段上的范围 pass.setFilterLimitsNegative(false); //设置保留范围内的还是过滤掉范围内的 pass.filter(*cloud_filtered); //执行滤波 cloud = *cloud_filtered; //----------------------------------------------------------------------------------- for (int k = 0; k <cloud.size(); k++) { if(cloud.points[k].x<2.5&&cloud.points[k].y<2.5&&cloud.points[k].z<2.5) { cloud.points[k].x= 0 ; cloud.points[k].y= 0 ; cloud.points[k].z= 0 ; } } cloud_ptr=cloud.makeShared();//转为指针类型 viewer.showCloud(cloud_ptr); if(i==m) { //保存单帧点云 pcl::io::savePCDFileASCII(pcd_mame,*cloud_ptr);//*tmp_cloud printf("成功保存第%d帧,并命名为:%s\n",m,pcd_mame.c_str()); //提取四元素与gps double x,y,z,w,tj,tw,tz; x = quatmsg->quaternion.x;//四元数 y = quatmsg->quaternion.y; z = quatmsg->quaternion.z; w = quatmsg->quaternion.w; tj = gpsmsg->position.y;//jin tw = gpsmsg->position.x;//wei tz = gpsmsg->position.z; //cout<<"tz==="<<tz<<endl; //平移计算 double nj,nw,nz; GetDirectDistance(gj,gw,gz,tj,tw,tz,&nj,&nw,&nz);//计算GPS变化量 Eigen::MatrixXd map_imu(3,1);//map坐标系下的imu坐标 map_imu(0,0)=nw; map_imu(1,0)=nj; map_imu(2,0)=-nz; // 四元数转换为旋转矩阵 Eigen::Quaterniond quaternion(w,x,y,z); Eigen::Matrix3d rotation_matrix; rotation_matrix=quaternion.toRotationMatrix(); Eigen::Matrix3d matrix_1;//求逆 matrix_1 =rotation_matrix.inverse(); //imu坐标系下的角点坐标 Eigen::MatrixXd imu_xyz=matrix_1*map_imu; FILE *fpWrite=fopen("imu_xyz.txt","a"); fprintf(fpWrite,"%16.10f %16.10f %16.10f\n",imu_xyz(0,0),imu_xyz(1,0),imu_xyz(2,0)); fclose(fpWrite); } i++; } int main(int argc,char** argv) { ros::init (argc, argv, "ros_points"); ros::NodeHandle n; string m_=argv[1]; m= atoi(m_.c_str()); printf("保存第%d帧\n",m); pcd_mame = argv[2]; cout<<"保存的pcd点云名为:"<<pcd_mame<<'\n'; message_filters::Subscriber<sensor_msgs::PointCloud2> points_sub(n, "/velodyne_points", 2000); message_filters::Subscriber<sbg_driver::SbgEkfNav> gps_sub(n, "/sbg/ekf_nav", 2000); message_filters::Subscriber<sbg_driver::SbgEkfQuat> quat_sub(n, "/sbg/ekf_quat", 2000); typedef message_filters::sync_policies::ApproximateTime<sensor_msgs::PointCloud2,sbg_driver::SbgEkfNav,sbg_driver::SbgEkfQuat> MySyncPolicy;//sensor_msgs::PointCloud2, message_filters::Synchronizer<MySyncPolicy> sync(MySyncPolicy(2000),points_sub,gps_sub, quat_sub); // 同步points_sub, sync.registerCallback(boost::bind(&chatterCallback, _1,_2,_3)); // _1, ros::spin (); }
方法一:通过matlab提取
将保存的单帧点云用matlab打开,拾取对应点云坐标。。。
clc
Pc=pcread('.\pcd\111.pcd');
%打开点云文件
pcshow(Pc)%显示点云
方法二:pcl程序提取
//***************************************************************************************************** // 获得单帧点云中的角反射器角点 // // 按中shift键,然后在角点处鼠标左击 // source devel/setup.bash && rosrun cam_laser_calib pcd_points_get 111.pcd //111.pcd 表示打开的pcd点云文件名 // pcl_viewer 111.pcd //************************************************************************************************** #include <string> #include <pcl/io/pcd_io.h> #include <pcl/point_cloud.h> #include <pcl/point_types.h> #include <pcl/visualization/pcl_visualizer.h> using PointT = pcl::PointXYZRGB; using PointCloudT = pcl::PointCloud<PointT>; // 用于将参数传递给回调函数的结构体 struct CallbackArgs { PointCloudT::Ptr clicked_points_3d; pcl::visualization::PCLVisualizer::Ptr viewerPtr; }; void pickPointCallback(const pcl::visualization::PointPickingEvent &event, void *args) { CallbackArgs *data = (CallbackArgs *) args; if (event.getPointIndex() == -1) return; PointT current_point; event.getPoint(current_point.x, current_point.y, current_point.z); data->clicked_points_3d->points.push_back(current_point); // 绘制红色点 pcl::visualization::PointCloudColorHandlerCustom<PointT> red(data->clicked_points_3d, 255, 0, 0); data->viewerPtr->removePointCloud("clicked_points"); data->viewerPtr->addPointCloud(data->clicked_points_3d, red, "clicked_points"); data->viewerPtr->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 10, "clicked_points"); std::cout << current_point.x << " " << current_point.y << " " << current_point.z << std::endl; FILE *fpWrite=fopen("lidar_xyz.txt","a"); fprintf(fpWrite,"%16.10f %16.10f %16.10f\n",current_point.x,current_point.y,current_point.z); fclose(fpWrite); } int main(int argc, char *argv[]) { std::string b = argv[1]; std::cout<<"打开pcd点云文件:"<<b<<'\n'; std::string file_name(b); pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>()); pcl::visualization::PCLVisualizer::Ptr viewer(new pcl::visualization::PCLVisualizer("viewer")); // 加载点云 if (pcl::io::loadPCDFile(file_name, *cloud) == -1) { std::cerr << "could not load file: " << file_name << std::endl; } std::cout << cloud->points.size() << std::endl; // 显示点云 viewer->addPointCloud(cloud, "cloud"); viewer->setCameraPosition(0, 0, -2, 0, -1, 0, 0); // 添加点拾取回调函数 CallbackArgs cb_args; PointCloudT::Ptr clicked_points_3d(new PointCloudT); cb_args.clicked_points_3d = clicked_points_3d; cb_args.viewerPtr = pcl::visualization::PCLVisualizer::Ptr(viewer); viewer->registerPointPickingCallback(pickPointCallback, (void*)&cb_args); std::cout << "Shift+click on three floor points, then press 'Q'..." << std::endl; viewer->spin(); std::cout << "done." << std::endl; while (!viewer->wasStopped()) { viewer->spinOnce(100); } return 0; }
clear; close all; clc; data_source=load('imu.txt');%惯导坐标点 data_target=load('lidar.txt');%雷达坐标点 data_source=data_source'; data_target=data_target'; %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %绘制两幅原始图像 x1=data_source(1,:); y1=data_source(2,:); z1=data_source(3,:); x2=data_target(1,:); y2=data_target(2,:); z2=data_target(3,:); figure; scatter3(x1,y1,z1,'b'); hold on; scatter3(x2,y2,z2,'r'); hold off; T_final=eye(4,4); %旋转矩阵初始值 iteration=0; Rf=T_final(1:3,1:3); Tf=T_final(1:3,4); data_target=Rf*data_target+Tf*ones(1,size(data_target,2)); %初次更新点集(代表粗配准结果) err=1; while(err>0.001) iteration=iteration+1; %迭代次数 disp(['迭代次数ieration=',num2str(iteration)]); %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %利用欧式距离找出对应点集 k=size(data_target,2); for i = 1:k data_q1(1,:) = data_source(1,:) - data_target(1,i); % 两个点集中的点x坐标之差 data_q1(2,:) = data_source(2,:) - data_target(2,i); % 两个点集中的点y坐标之差 data_q1(3,:) = data_source(3,:) - data_target(3,i); % 两个点集中的点z坐标之差 distance = data_q1(1,:).^2 + data_q1(2,:).^2 + data_q1(3,:).^2; % 欧氏距离 [min_dis, min_index] = min(distance); % 找到距离最小的那个点 data_mid(:,i) = data_source(:,min_index); % 将那个点保存为对应点 error(i) = min_dis; % 保存距离差值 end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %去中心化 data_target_mean=mean(data_target,2); data_mid_mean=mean(data_mid,2); data_target_c=data_target-data_target_mean*ones(1,size(data_target,2)); data_mid_c=data_mid-data_mid_mean*ones(1,size(data_mid,2)); %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %SVD分解 W=zeros(3,3); for j=1:size(data_target_c,2) W=W+data_mid_c(:,j)*data_target_c(:,j)'; end [U,S,V]=svd(W); Rf=U*V'; Tf=data_mid_mean-Rf*data_target_mean; err=mean(error); T_t=[Rf,Tf]; T_t=[T_t;0,0,0,1]; T_final=T_t*T_final; %更新旋转矩阵 disp(['误差err=',num2str(err)]); disp('旋转矩阵T='); disp(T_final); data_target=Rf*data_target+Tf*ones(1,size(data_target,2)); %更新点集 if iteration>=200 break end end disp(inv(T0)); %旋转矩阵真值 x1=data_source(1,:); y1=data_source(2,:); z1=data_source(3,:); x2=data_target(1,:); y2=data_target(2,:); z2=data_target(3,:); figure; scatter3(x1,y1,z1,'b'); hold on; scatter3(x2,y2,z2,'r'); hold off;
参考:
文献:一种基于点云匹配的激光雷达/IMU联合标定方法
博客(点云提取):https://blog.csdn.net/u014072827/article/details/110948996
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。