1、标定准备
(1)标定板选择,棋盘格标定板,一定要长方形,否则会导致后续标定时标定工具无法判断方向。且标定板尽可能大,雷达扫描的会更准确。
(2)准备rosbag包,
首先,需要开雷达驱动和相机驱动,然后检查雷达话题和相机话题,我的是rslidar_points和usb_cam/image_raw,然后使用录制指令,需要将雷达和相机话题更换为你自己的。
rosbag record /rslidar_points /usb_cam/image_raw
需要注意激光雷达和相机的采样频率不一样,导致点云和图像不能帧间匹配,网上有很多方法,1、使用GPS对时间进行硬同步。2、可以使用抽帧的方法进行时间软同步。
解决时间同步的问题很简单,我用了一个很笨的办法,我使用rosbag实现“拍照”的效果。即对每一次标定板的姿态录一个bag包,取点云和图像的第一帧,从而达到点云和图像匹配的效果。
对标定板每个姿态录了包之后用下面的代码将rosbag转换为img和pcd的格式,从而实现了一对点云-图像对,重复上述操作,。就能得到n对点云-图像对。
代码使用教程:创建文件夹,在文件夹下创建extract_bag.py文件和data文件夹,将下面的代码填充进extract_bag.py;在data文件夹下面创建images和pointclouds文件夹,用来保存提取的点云图像。需要修改的地方有话题,bag路径,提取bag里的点云和图像的保存路径。
#! /usr/bin/python # coding=utf-8 import os import rosbag import cv2 from cv_bridge import CvBridge from tqdm import tqdm class ExtractBagData(object): def __init__(self, bagfile_path, camera_topic, pointcloud_topic, root): self.bagfile_path = bagfile_path self.camera_topic = camera_topic self.pointcloud_topic = pointcloud_topic self.root = root self.image_dir = os.path.join(root, "images") self.pointcloud_dir = os.path.join(root, "pointcloud") #创建提取图片和点云的目录 ./root/images root/pointcloud if not os.path.exists(self.image_dir): os.makedirs(self.image_dir) if not os.path.exists(self.pointcloud_dir): os.makedirs(self.pointcloud_dir) def extract_camera_topic(self): bag = rosbag.Bag(self.bagfile_path, "r") bridge = CvBridge() bag_data_imgs = bag.read_messages(self.camera_topic) index = 0 pbar = tqdm(bag_data_imgs) for topic, msg, t in pbar: # type: ignore pbar.set_description("Processing extract image id: %s" % (index+1)) cv_image = bridge.imgmsg_to_cv2(msg, "bgr8") # 如果你需要使用时间戳对提取的图片命名,可以使用msg.header.stamp.to_sec()获取时间戳 timestr = "%.6f" % msg.header.stamp.to_sec() cv2.imwrite(os.path.join(self.image_dir, str(timestr) + ".jpg"), cv_image) # type: ignore index += 1 def extract_pointcloud_topic(self): ''' # 提取点云数据为pcd后缀文件,默认提取以为时间戳命名 # 提取命令:rosrun pcl_ros bag_to_pcd result.bag /velodyne_points ./pointcloud # 提取点云以时间戳命令:1616554905.476288682.pcd :return: ''' cmd = "rosrun pcl_ros bag_to_pcd %s /rslidar_points %s" % (self.bagfile_path, self.pointcloud_dir) os.system(cmd) # 再读取提取的pcd点云数据,把文件名修改为按照顺序索引名 #pcd_files_list = os.listdir(self.pointcloud_dir) # 因为提取的pcd是以时间戳命令的,但是存放到列表中并不是按照时间戳从小到大排列,这里对时间戳进行重新排序 #pcd_files_list_sorted = sorted(pcd_files_list) # print(zip(pcd_files_list, pcd_files_list_sorted)) #index = 0 #pbar = tqdm(pcd_files_list_sorted) #for pcd_file in pbar: # pbar.set_description("Processing extract poindcloud id: %s" % (index + 1)) # os.rename(os.path.join(self.pointcloud_dir, pcd_file), # os.path.join(self.pointcloud_dir, str(index) + ".pcd")) # print("pcd_file name: ", pcd_file) # index += 5 if __name__ == '__main__': #需要处理的bag包的路径 bagfile_path = '/media/happy406/046dcd53-ee66-4d3b-b556-5f9559acab84/0508/55.bag' camera_topic = "/usb_cam/image_raw" pointcloud_topic = "/rslidar_points" #将bag转换为pcd和jpg格式的保存路径 extract_bag = ExtractBagData(bagfile_path, camera_topic, pointcloud_topic, "/media/happy406/046dcd53-ee66-4d3b-b556-5f9559acab84/0508") extract_bag.extract_camera_topic() extract_bag.extract_pointcloud_topic()
2、使用标定工具
1、打开matlab(我的是2023a版本,2022版以上才有联合标定工具),打开标定工具。
标定工具重点项讲解:
Open Session:打开保存过的会话
Import:导入点云-图像对
Compute Intrinsic:导入数据后自动计算相机内参
Use Fixed Intrisic:使用修正后的相机内参,这项可以通过matlab的相机标定工具来标定,然后将内参导出至matlab工作区,再导入到联合标定工具中。一般来说,可以直接使用联合标定工具计算的内参。
Edit ROI:感兴趣区域的选择,这一步可以不做更改
Select Checkboard:选择棋盘格,导入数据后会有很多不符合要求的图像点云对,此时需要手动选择棋盘格,相当于告诉标定工具,棋盘格在哪。(这一步尤为关键!!)
Cluster Threshold:聚类阈值
Dimension Tolerance:尺寸误差容忍度,设置为0.2。容忍度越高,被接受的数据越多,但是过大的容忍度也会导致后续标定误差过大
Detect:检测数据是否能被检测到棋盘格。
Calibrate:开始标定。
2、流程:
(1)导入数据,Square size是标定板的每格的长宽大小。
(2)导入后没有任何可接受的数据,这时需要调整聚类阈值和误差容忍度。
(3)选择棋盘格,一张一张的选择标定板,弄完后,apply,再detect一下,检查有多少数据被接受。标完后最好save session,不然等下筛选标定数据后又要重新选择棋盘格,会很麻烦。
我因为标定板比较小,激光雷达扫描效果不好,所以录了比较多的数据。如果标定板大的话可以少录一点。
4、标定
一开始误差特别大
点击柱状线,或者拖动离群值可以选择抛弃的数据。最好一个个的删除误差比较大的数据,因为删除一个数据都可能导致整个误差有很大的波动。优先删除重投影大的数据同时旋转误差大的数据,没有这类数据就删除重投影误差大的数据。
如果数据删的都快没了。或者变得太大,那需要重新调整,打开刚才保存的session,继续标定,然后重复刚才的动作,一遍一遍地调整,直到误差在10左右。(这一步最关键,一定要有耐心!!)
如果误差比较小了,就检查棋盘格是否完好的投在了图像上,如果基本贴合图像且误差较小,就可以直接导出标定数据了。
导入到工作区,这样就得到了我的标定数据,matlab给的是转置矩阵,使用时需转置后使用。