1. 为什么不用标定板?聊聊无标定板外参标定的那些事儿

大家好,我是老张,在工业视觉和机器人感知这块摸爬滚打了十来年,经手过的相机和雷达标定项目少说也有上百个。今天想和大家深入聊聊一个特别“接地气”的场景:不用标定板,怎么把海康威视的工业相机和Livox Avia激光雷达给“对上眼”

传统的标定方法,无论是用棋盘格还是AprilTag标定板,大家都懂。你得先做个高精度的板子,然后让人或者机械臂拿着它在相机和雷达面前晃来晃去,确保两者都能同时、清晰地看到它。在实验室里,这没问题,慢工出细活嘛。但一旦到了现场,比如高速运转的自动化产线,或者需要快速部署的移动机器人上,问题就来了。产线不可能为你停机,让你慢慢悠悠地去摆弄标定板;户外机器人部署环境复杂,风吹草动都可能让标定板位置偏移,更别提那些空间狭小或者有安全隐患,人根本进不去的地方了。这时候,传统方法就显得笨重又低效。

所以,当港大Mars实验室开源了这套无需标定板的标定工具时,我真是眼前一亮。它的核心思路特别聪明:利用环境中自然存在的、具有丰富几何或纹理特征的物体(比如墙角、桌沿、设备轮廓)作为天然的“标定参照物”。我们不再需要人为引入一个已知的、精确的标定物,而是让算法自己去寻找相机图像和激光雷达点云中,那些来自同一物理实体的特征,并通过优化算法,反推出相机和雷达之间的位置和姿态关系,也就是我们说的外参(主要是旋转矩阵R和平移向量t)。

这种方法听起来很美好,但对传感器和数据有要求。为什么是海康威视工业相机和Livox Avia雷达的组合特别适合呢?首先,海康的工业相机成像稳定,尤其是全局快门型号,在动态场景下不容易产生运动模糊,能提供清晰的纹理信息。而Livox Avia作为一款非重复扫描式激光雷达,它的点云图案是花瓣状的,在短时间内就能覆盖视场角内的大部分区域,点云密度高,对于捕捉环境中的边缘、角点等几何特征非常有利。两者结合,一个提供丰富的二维纹理,一个提供精确的三维结构,就为无标定板标定提供了高质量的“原材料”。

我实测下来,这套方法在对标定效率、自动化程度要求高的工业场景里,优势非常明显。你只需要让设备在目标场景中自然地运动一段(比如装在机器人上走一圈),采集一段同步的数据,剩下的交给算法离线计算就行,不干扰正常生产。当然,它也不是万能的,对环境特征有一定要求,太空旷或者太单一纹理的环境效果会打折扣。但无论如何,这为我们提供了一种全新的、更灵活的标定思路。接下来,我就手把手带你走通整个流程,从环境搭建到最终出参数,把我踩过的坑和总结的经验都分享给你。

2. 实战第一步:搭建你的标定工作环境

工欲善其事,必先利其器。在开始标定之前,我们需要一个稳定、兼容的软件环境。整个流程基于ROS(Robot Operating System),所以第一步就是准备好ROS。这里我强烈建议使用 Ubuntu 20.04 + ROS Noetic 这个组合,这是目前社区支持最广泛、最稳定的版本,能避开很多莫名其妙的依赖问题。

2.1 核心工具安装:Livox驱动与标定工具包

整个标定系统的核心是港大Mars实验室开源的 livox_camera_calib 工具包。但在这之前,我们需要先为Livox Avia雷达准备好“驱动程序”。

第一步,安装Livox SDK和ROS驱动。 如果你之前跑过像FAST-LIVO这类Livox相关的SLAM算法,那这一步可能已经完成了。如果没有,也别担心,跟着做就行。Livox SDK是官方提供的底层通讯库,而livox_ros_driver则是连接SDK和ROS的桥梁。

打开终端,我们一步步来(假设你的工作空间是 ~/catkin_ws):

# 1. 安装Livox SDK
cd ~
git clone https://github.com/Livox-SDK/Livox-SDK.git
cd Livox-SDK
mkdir build && cd build
cmake ..
make
sudo make install

# 2. 安装livox_ros_driver
cd ~/catkin_ws/src
git clone https://github.com/Livox-SDK/livox_ros_driver.git
cd ~/catkin_ws
catkin_make

编译过程中如果报错,大概率是缺少依赖。常见的依赖包括 pcl-ros, roscpp, sensor_msgs 等,你可以用 sudo apt-get install ros-noetic-[包名] 来安装。记住一个原则:看报错信息的前几行,它通常直接告诉你缺什么

第二步,安装主角:livox_camera_calib。 这才是我们今天要用的标定工具。把它克隆到你的ROS工作空间:

cd ~/catkin_ws/src
git clone https://github.com/hku-mars/livox_camera_calib.git
cd ~/catkin_ws
catkin_make

这里有个非常重要的经验:安装各种依赖包时,最好不要盲目追求最新版本。这个开源算法已经稳定运行了一段时间,其依赖库的版本在作者开发时是固定的。如果你系统里某些库(比如OpenCV、PCL)版本太新,可能会导致接口不兼容而编译失败。我的建议是,如果catkin_make报错,先检查错误信息是否指向某个函数或头文件找不到,这很可能是版本差异。这时候,去网上搜索“Ubuntu 20.04 ROS Noetic 安装 [库名]”来安装指定版本,往往能解决问题。

2.2 验证安装:用示例数据跑通流程

安装完成后,先别急着用自己的数据。作者很贴心地提供了示例数据包,我们应该先用它来验证整个工具链是否通畅。

首先,下载示例数据(通常可以在GitHub仓库的README或issue里找到链接)。假设你下载的bag文件叫 example.bag

然后,按照流程处理数据:

# 启动标定程序,并指定示例数据的配置文件
roslaunch livox_camera_calib calib.launch

在启动前,你需要检查一下 livox_camera_calib 包内 config 文件夹下的YAML配置文件(比如 example.yaml),确保里面的 bag_path 指向你存放 example.bag 的正确路径。

如果一切顺利,你会看到Rviz窗口弹出,里面同时显示点云和图像。算法开始自动提取特征并进行匹配优化。这个过程可能会比较长,取决于你的电脑性能和数据长度,耐心等待即可。最终,终端里会输出优化后的外参矩阵,以及一些重投影误差等评估指标。

成功跑通示例数据是至关重要的一步。这不仅能确认你的环境没问题,还能让你直观地看到标定过程应该是什么样子,对后面调试自己的数据有极大帮助。如果在这里卡住了,一定要根据终端打印的错误信息,去GitHub的Issues里搜索,你遇到的问题大概率别人已经遇到并解决了。

3. 数据采集:如何获取高质量的“原料”

环境搭好了,示例也跑通了,接下来就是重头戏:采集你自己的相机和雷达数据。俗话说“垃圾进,垃圾出”,数据质量直接决定标定结果的精度和成功率。这一部分,我会结合海康威视相机和Livox Avia的特性,详细说说采集的门道。

3.1 传感器准备与话题配置

首先,确保你的海康威视工业相机和Livox Avia雷达已经正确上电,并通过千兆网线或USB3.0线缆连接到工控机上。对于海康相机,你需要使用官方的MVS软件或者像hikrobot_camera这样的ROS驱动包,将其图像数据发布到ROS话题中。Livox Avia则通过前面安装的livox_ros_driver来发布点云数据。

关键点在于话题同步。 标定工具需要时间上对齐的图像和点云。最理想的方式是使用硬件同步线,触发相机曝光和雷达扫描的起始时间。但很多情况下我们没有这个条件,那就用软件同步来近似。

你需要确保两件事:

  1. 时间戳同步:工控机的系统时间要准确,最好配置NTP网络对时。相机和雷达驱动都会给每条数据打上基于系统的时间戳。
  2. 话题频率匹配:调整相机帧率和雷达的扫描模式,使它们的数据率不要相差太远。例如,相机设为10Hz,Livox Avia也配置为10Hz(在livox_ros_driver的配置文件中设置)。这样,在录制ROS Bag时,rosbag record工具会尽最大努力将相近时间戳的消息打包在一起。

启动你的传感器节点后,用 rostopic list 命令查看一下。你应该能看到类似 /hik_camera/image_raw(海康相机图像话题,名称可能不同)和 /livox/lidar(Livox雷达点云话题)这样的输出。记下这两个话题的准确名称,下一步录制要用。

3.2 场景选择与运动采集技巧

这是无标定板标定成败的核心。既然我们没有人工标定板,就必须依赖环境自身的特征。一个好的采集场景应该具备:

  • 丰富的几何边缘:比如房间的墙角、门窗边缘、桌子的棱角、设备的外轮廓。这些在点云中会表现为清晰的线特征,在图像中则是明显的边缘。
  • 一定的纹理变化:纯色、光滑的墙面或地面不是好选择。有些许纹理的墙面、地板砖缝、摆放的杂物等,能为特征提取提供更多信息。
  • 适中的复杂度:不要太空旷(特征太少),也不要太杂乱无章(特征太多且难以匹配)。一个普通的、有桌椅和设备的实验室或车间环境就很好。

采集时,你需要让传感器对共同视场内的场景进行运动。 运动是必须的!静止状态下采集的数据,视角单一,无法提供足够的约束条件来解算外参。运动可以是:

  • 平移运动:手持设备或装在移动机器人上,在场景中缓慢地前后、左右移动。
  • 旋转运动:在原地缓慢地旋转设备,改变传感器对环境的观测角度。
  • 复合运动:结合平移和旋转,走一个“8”字形或绕场一周,效果通常更好。

采集命令如下: 打开一个新终端,切换到你想保存数据的目录,然后运行:

rosbag record -O calibration_data.bag /livox/lidar /hik_camera/image_raw

这里的 -O 指定输出bag文件的名字,后面跟的是你需要录制的两个话题名,请替换成你实际的话题名。录制时间建议在20-30秒左右。时间太短,数据量可能不足;时间太长,数据包巨大,后期处理慢,而且容易引入过多的累计运动误差。录制时,平稳、缓慢地移动你的设备,确保相机和雷达的公共视野里始终有丰富的特征。

4. 数据处理:从ROS Bag到标定输入

数据录好了,是一个 .bag 文件。但标定工具不能直接处理bag,我们需要从中提取出算法需要的特定格式的点云和图像数据。这一步如果做错了,后面全白费。

4.1 点云数据提取:关键的一步转换

对于Livox雷达的点云,不能用常见的 rosrun pcl_ros bag_to_pcd 这样的命令来转换。因为那种方法会把每一帧激光数据存为一个独立的PCD文件。而无标定板标定算法需要的是累积一段时间内的点云,形成一个相对稠密的“点云地图”,以便从中提取稳定的几何特征。

幸运的是,livox_camera_calib 工具包的作者已经为我们写好了专用的转换工具。使用以下launch文件:

roslaunch livox_camera_calib bag_to_pcd.launch

在运行这个命令之前,你需要修改对应的配置文件(通常是 bag_to_pcd.launch 里引用的某个YAML文件,或者通过参数传递),指定你的 calibration_data.bag 的路径和希望输出的PCD文件路径。这个工具会读取bag中的点云话题,进行必要的去畸变和运动补偿(如果提供了IMU数据),然后输出一个或多个包含了多帧累积点云的PCD文件。这是后续标定能成功的基础,务必使用这个专用工具。

4.2 图像数据提取:保持序列与命名

图像提取相对直接,我们可以写一个简单的Python脚本。在你的bag文件同级目录下,创建一个名为 extract_images.py 的文件,复制以下代码:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import os
import argparse
import cv2
import rosbag
from sensor_msgs.msg import Image
from cv_bridge import CvBridge

def main():
    parser = argparse.ArgumentParser(description="从ROS bag中提取图像。")
    parser.add_argument("bag_file", help="输入的ROS bag文件路径。")
    parser.add_argument("output_dir", help="输出图像的文件夹路径。")
    parser.add_argument("image_topic", help="图像话题名称。")
    args = parser.parse_args()

    print(f"正在从 {args.bag_file} 的话题 {args.image_topic} 提取图像到 {args.output_dir}")
    os.makedirs(args.output_dir, exist_ok=True)

    bag = rosbag.Bag(args.bag_file, 'r')
    bridge = CvBridge()
    count = 0

    for topic, msg, t in bag.read_messages(topics=[args.image_topic]):
        try:
            # 将ROS Image消息转换为OpenCV格式
            cv_img = bridge.imgmsg_to_cv2(msg, desired_encoding="bgr8")
            # 保存图像,使用6位数字序号,便于排序
            image_filename = os.path.join(args.output_dir, f"frame{count:06d}.png")
            cv2.imwrite(image_filename, cv_img)
            print(f"已保存图像: {image_filename}")
            count += 1
        except Exception as e:
            print(f"转换或保存图像时出错: {e}")
            continue

    bag.close()
    print(f"图像提取完成,共 {count} 张图像。")

if __name__ == '__main__':
    main()

保存脚本后,首先查看一下你的bag里图像话题的确切名称:

rosbag info calibration_data.bag

在输出信息中找到你的相机话题,比如 /hik_camera/image_raw。然后运行提取脚本:

python3 extract_images.py calibration_data.bag ./extracted_images /hik_camera/image_raw

这样,所有的图像就会按照时间顺序,以 frame000000.png, frame000001.png... 的命名方式保存在 ./extracted_images 文件夹里。这种有序的命名对于后续标定程序按顺序读取至关重要。

5. 配置与运行:启动标定并解读结果

数据准备就绪,现在来到了临门一脚:修改配置文件并启动标定程序。

5.1 配置文件详解

进入 livox_camera_calib/config 目录,你会看到一些YAML示例文件(如 calib_config.yaml)。我们需要复制一份,并根据自己的数据路径进行修改。关键的配置项包括:

# 数据路径配置
bag_path: "/home/yourname/catkin_ws/data/calibration_data.bag" # 你的bag文件绝对路径
image_path: "/home/yourname/catkin_ws/data/extracted_images"   # 提取的图像文件夹路径
pcd_path: "/home/yourname/catkin_ws/data/xxx.pcd"              # 上一步生成的PCD文件路径

# 传感器话题配置
lidar_topic: "/livox/lidar"          # 雷达点云话题
image_topic: "/hik_camera/image_raw" # 相机图像话题

# 相机内参(必须提前标定好!)
camera_intrinsic:
  fx: 800.5   # 焦距x (像素单位)
  fy: 800.2   # 焦距y
  cx: 640.3   # 光心x
  cy: 512.1   # 光心y
  k1: -0.1    # 径向畸变系数1
  k2: 0.05    # 径向畸变系数2
  p1: 0.001   # 切向畸变系数1
  p2: -0.001  # 切向畸变系数2

# 标定参数(可调整,影响标定速度和精度)
calib:
  max_iterations: 100  # 最大迭代次数
  feature_threshold: 0.1 # 特征提取阈值
  # ... 其他参数

特别注意camera_intrinsic 里面的参数,必须是你事先用张正友标定法等工具对海康威视相机单独标定得到的内参和畸变系数。无标定板外参标定强烈依赖精确的相机内参,如果这里填错了,外参标定不可能准确。

5.2 启动标定与结果分析

配置完成后,就可以启动标定了:

roslaunch livox_camera_calib calib.launch config_file:=/path/to/your/calib_config.yaml

启动后,Rviz会打开。你会看到点云和图像窗口。算法会开始工作,大致分为几个阶段:

  1. 特征提取:从点云中提取边缘点(几何特征),从图像中提取角点或边缘(纹理特征)。
  2. 初始匹配与运动估计:利用特征点,结合传感器运动(通过点云或视觉里程计估计),进行初步的关联。
  3. 联合优化:构建一个庞大的优化问题,同时优化所有帧中相机和雷达的相对位姿(外参)以及特征点的空间位置,最小化重投影误差(图像特征点反投影到3D,再投影到另一视角图像上的误差)。

这个过程可能会运行几分钟到几十分钟,取决于数据量和电脑性能。我的经验是,如果场景特征丰富,运动充分,通常迭代几十次后误差就会收敛。

如何判断标定结果的好坏? 程序结束后,终端会打印出最终优化的外参矩阵(一个4x4的变换矩阵,包含旋转和平移),以及一个平均重投影误差(单位通常是像素)。这个误差值是关键的评价指标。一般来说,对于分辨率为1280x1024的工业相机,平均重投影误差如果能控制在1-2个像素以内,就算是非常好的结果了;3-5个像素是可以接受的;如果超过10个像素,可能就需要检查数据质量或内参准确性了。

除了看数字,更直观的方法是验证。你可以将标定得到的外参矩阵应用到点云上,将其投影到图像空间,看看雷达探测到的物体边缘是否与图像中的物体边缘对齐。例如,墙角的点云线是否正好落在图像中墙角的像素位置上。工具包通常提供验证脚本或可以在Rviz中手动加载变换来观察。

6. 避坑指南与精度提升技巧

走完整个流程,你可能一次成功,也可能遇到各种问题。这里我总结几个常见的“坑”和提升精度的小技巧,希望能帮你少走弯路。

坑1:环境特征不足。 这是失败最常见的原因。如果你在一个四面白墙、空空如也的房间做标定,算法根本找不到足够的特征来建立匹配。解决办法就是主动创造特征:临时搬一些纸箱、椅子进去,或者选择有丰富纹理和结构的走廊、办公室角落。

坑2:传感器运动不当。 运动太快会导致图像模糊(尤其是卷帘快门相机)和点云畸变严重;运动太慢或幅度太小,则无法提供多视角约束。我的经验是,以大约0.2-0.5米/秒的速度,进行包含平移和旋转的复合运动,让公共视野中的物体从不同角度被观测到。

坑3:相机内参不准。 再强调一遍,无标定板标定对内参极其敏感。务必用高质量标定板,在不同距离、角度下采集足够多的图像(比如20-30张),进行严谨的相机内参标定,并确认重投影误差很小。

精度提升技巧:

  • 多时段多次采集:在同一场景下,更换光照条件(如开灯/关灯),或者稍微改变场景中物体的位置,采集多组数据分别标定。最后将得到的外参取平均值或中位数,可以降低单次标定的随机误差。
  • 融合IMU数据:如果你的设备上有IMU,并且在录制bag时同时记录了IMU话题,可以在配置文件中启用IMU辅助。IMU可以提供高频的运动信息,帮助进行更精确的点云去畸变和运动估计,尤其在快速运动时效果显著。
  • 手动辅助初值:如果自动标定完全失败,可以尝试在Rviz中手动调整雷达和相机之间的粗略变换,将这个粗略变换作为初始值写入配置文件,再启动优化。一个好的初值能帮助算法更快、更准地收敛。
  • 后处理验证与微调:标定结果出来后,不要直接用到最终产品中。应该设计一个独立的验证环节。例如,让机器人走到一个已知位置,用雷达建图,同时用相机拍照,看看根据标定外参将地图点投影到图像上,是否与真实物体吻合。如果存在系统性偏差,可以进行微调。

最后我想说,无标定板标定是一项非常实用的技术,它解放了我们对特定标定物的依赖,让标定过程更灵活、更自动化。但它也对工程师的场景理解、数据采集能力和问题调试能力提出了更高要求。这套流程我跟着港大的开源代码实践过很多次,也根据项目需求做过一些修改。最大的体会就是,耐心和细心非常重要,从环境准备、数据采集到参数调整,每一步的严谨程度都会体现在最终的结果上。当你看到经过标定后,激光点云和相机图像严丝合缝地对齐在一起时,那种成就感是非常棒的。希望这篇详细的实战指南能帮你顺利搞定海康相机和Livox Avia的标定,如果在实际操作中遇到新问题,也欢迎一起交流探讨。

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐