ch32雷达的参数如下:
前后左右面分别部署一个,需要标定出他们之间的外参
开源代码1:
使用一个360度的机械旋转式雷达,进行a-loam建图和一个Livox进行匹配,得到他们之间的外参,详细资料可以看里边的readme文件
这似乎不满足我的需求,我是希望能通过两两之间的固态雷达能够直接进行外参标定的
但是这个ch32雷达垂直视场角很小,扫到的东西非常有限,这种情况下想通过两个固态雷达完成标定,我觉得是很有难度的,雷达扫描的样本如下:
深度录屏_选择区域_20230804105751-CSDN直播https://live.csdn.net/v/316862
开源代码2:
首先先用作者提供的数据试一试
结果会报错如下:
Start building local map...
Loaded 1182 frames from Base-LiDAR
100% [||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||]
Saving submap...
Mapping done!
Start calibration...
Reading H-LiDAR map data...
Loaded 1085713 data points from H_LiDAR_Map.pcd
Loaded 1182 frames from Target-LiDAR
0% [ ]terminate called after throwing an instance of 'std::length_error'
what(): vector::reserve
Aborted (core dumped)
Read 1 data
需要将 calibration.cpp里的 viewer_final->spinOnce(10); 注释掉才能运行成功
但是如此一来,就看不到 Target里的每帧点云与Base构建好的地图匹配情况了
问题1:
viewer_final->spinOnce(10);
这个窗口显示更新点云函数,有问题
报错:
terminate called after throwing an instance of 'std::length_error'
what(): vector::reserve
非常奇怪,明明只是显示 match cloud 其他并没有影响
只能注释掉,但是注释掉后也不会显示点云,无法看到匹配情况
准备尝试下载pcl1.7版本
1.7版本太老了,下都下不下来
下了个1.8.1的试一试吧
这需要多版本共存了,可能会出问题,但是最终也没有安装多版本,太懒了!
解决:
通过阅读代码,我发现问题主要是 匹配点云(match cloud)部分,有问题
viewer_final->addPointCloud<pcl::PointXYZ>(H_LiDAR_Map, match_color, "match cloud");
H_LiDAR_Map 已经被添加到 target cloud,下面又加到"match cloud"
问题就出在"match cloud"这块,初始化问题,然后在下边的循环更新成第一帧的点云
因此我用一个空地图来初始化"match cloud"
pcl::PointCloud<pcl::PointXYZ>::Ptr Frame_Map(new pcl::PointCloud<pcl::PointXYZ>);
自己定义一个空地图
viewer_final->addPointCloud<pcl::PointXYZ>(Frame_Map, match_color, "match cloud");
用这个空的地图,初始化 匹配点云
此时不用在注释上边的代码,也能能跑通,而且还能看到每帧地图的匹配情况,拟合程度
遍历Target中的每一帧,并根地图中对应到的帧进行匹配,icp计算score
首先一定要明白坐标系变换和点云变换是有区别的
我们填写的Init_Matrix是base坐标系到Target坐标系的变换矩阵,也就是Target坐标系下的点云到Base坐标系点云的变换
Base坐标系到Target坐标系的变换:就是Target坐标系在Base坐标系下的位姿表示
以下图为例,左雷达为Base 前雷达为Target
现在得到的这个变换矩阵,既可以说是A坐标系到B坐标系的变换矩阵,有可以说是B坐标系的点云到A坐标系的点云的变换矩阵
由此可以得知想要知道两个坐标系的变换,只需要知道一个坐标系在另一个坐标系下的位姿表示就可以了
现在知道B在A下的坐标表示,得到了A坐标系到B坐标系的变换矩阵T,假如B坐标系有一点P(x,y),将其转化成A坐标系下的表示的P'(x',y')则 P'=T*P(x,y)
所以一定要分清楚 坐标系变换,点云变换这两个概念
代码标定部分大致流程:
*frames点云(用给的初始矩阵将该帧点云变换到base的world下了)
|
*trans_output_cloud(根据处理的当前帧位姿找到对应在base下的该帧位姿,并将点云变换)
|
*final_output_cloud(变换完成后可以从map找到该帧点云在map下的近邻点)
|
neightbors_L(map中找到的最近邻点云)
|
neightbors_trans(用T_Matrix_Inverse *neightbors_L 将点云换到world下 )
就可以用icp进行迭代了
icp.InputSource(trans_output_cloud) 源点云
icp.InputTarget(neighbors_trans) 目标点云
通过求解源点云到目标点云的微小变换,Tiny_T,score<1 就更新初始init_guess
当sore<0.1 就会得到一次calib的结果,以此类推,直到将Target中所有frames遍历完
calib.txt会将所有满足score<0.1的,结果保存出来
使用流程:
首先需要将bag转成pcd文件,并将各自的话题进行区分
rosrun pcl_ros bag_to_pcd <*.bag> <topic> <output_directory>
rosrun pcl_ros bag_to_pcd 2023-06-27-11-46-11.bag /minibus/left/lslidar_point_cloud new-11-46/left/
会将pcd文件以时间戳命名,还需要稍微转化一下
代码输入是100000.pcd依次往下命名
用一个脚本把名字转化一下( resort.py 该脚本可以将文件夹下的pcd文件按照顺序重命名)
import os
# 输入文件夹路径
folder_path = './front'
# 获取文件列表并根据时间戳排序
file_list = os.listdir(folder_path)
file_list.sort() # 这里假设文件名的时间戳部分是可以直接按字母顺序排序的
# 开始重新命名并按顺序命名
new_number = 100000
for old_name in file_list:
# 提取旧的时间戳部分
timestamp = old_name.split('.')[0]
# 构建新文件名
new_name = f"{new_number:06d}.pcd"
new_number += 1
# 构建文件的完整路径
old_path = os.path.join(folder_path, old_name)
new_path = os.path.join(folder_path, new_name)
# 重命名文件
os.rename(old_path, new_path)
print("重新命名完成!")
转化完成后,将雷达数据做好的pcd文件根据需求放进(Base-Frame,Target-Frame)
就可以运行起来辣!!
但是我在运行过程中发现了很多问题
由于lslidar_ch32垂直市场角太小,扫描到的信息很少,再加上我当时的录包的场景也太过开阔,所以标定的结果非常不好,因为如何我当前帧看到的是一堵墙的一部分,但是我的地图有墙的完整部分,那我这部分墙只要跟地图上的墙匹配上就可以了,虽然score<0.1了但是当前扫到的墙和匹配上的墙根本就不是正确的匹配,因此得到的结果也就不正确了
而且他会记录所有满足条件下得到的calib结果,如果你提供的pcd文件刚好是在得到不准确匹配的部分时结束的,那么将最后得到的结果作为最终结果,也是不科学的。
我有一个比较苯的方法
因为我可以看到每帧匹配在地图上的效果,所以我就看当我的当前帧遍历到 显著性物体时的拟合程度,如果拟合的挺好,那这个结果肯定是差不多的,但是像这样的帧也有很多个,具体以哪个结果为准,也成立一个难题,唉,为什么要有那么恶心的雷达,真心绝望了!