kalibr进行相机内参以及相机imu的融合标定

1、采集标定数据

参考该视频教程:

https://www.youtube.com/watch?app=desktop\&v=puNXsnrYWTY

2、转rosbag包

首先安装好Docker

在linux下运行如下命令,获取 VideoIMUCapture-Android 的github库

bash 复制代码
git clone https://github.com/DavidGillsjo/VideoIMUCapture-Android.git

进入对应文件夹

bash 复制代码
cd VideoIMUCapture-Android/calibration

需要以root权限进入容器,那么需要输入类似命令,换为自己的路径

bash 复制代码
SUDO=1 DUSER=root DATA=/home/ljy18/VideoIMUCapture-Android/dataset ./run_dockerhub.sh

容器是退出后删除,文件夹挂在到容器内后对应的目录为/data

需要转为euroc格式的数据集bag包,原始文件夹格式需要为

dataset_name/

├── mav0/

│ ├── cam0/

│ │ ├── data/

│ │ │ ├── 1403....png

│ │ │ └── ...

│ │ ├── data.csv

│ ├── imu0/

│ │ ├── data.csv

其中:

mav0/cam0/data.csv的格式为

#timestamp [ns],filename

1403636579763555584,1403636579763555584.png

1403636579863555584,1403636579863555584.png

...

mav0/imu0/data.csv的格式为

#timestamp [ns],w_RS_S_x [rad s^-1],w_RS_S_y [rad s^-1],w_RS_S_z [rad s^-1],

a_RS_S_x [m s^-2],a_RS_S_y [m s^-2],a_RS_S_z [m s^-2]

1403636579763555584,-0.0015,0.0023,0.0009,9.8085,-0.0321,0.0230

...

Imu频率要远大于图片帧频率

用当前目录下的kalibr_bagcreater.py(见附录)去替换掉原本针对旧数据集格式的kalibr_bagcreater:

bash 复制代码
cp /data/kalibr_bagcreater.py /kalibr_workspace/src/Kalibr/aslam_offline_calibration/kalibr/python/kalibr_bagcreater

转为bag包

bash 复制代码
kalibr_bagcreater \

  --folder /data/VideoTV00001_EuRoC \

  --output-bag /data/VideoTV00001_calibration/VideoTV00001_euroc.bag

查看话题名称是否正确

bash 复制代码
rosbag info /data/VideoTV00001_calibration/VideoTV00001_euroc.bag

出现类似如下内容即格式正确

bash 复制代码
types:       sensor_msgs/Image [060021388200f6f0f447d0fcd9c64743]

             sensor_msgs/Imu   [6a62c6daae103f4ff57a132d6f95cec2]

topics:      /cam0/image_raw    2154 msgs    : sensor_msgs/Image

             /imu0             13542 msgs    : sensor_msgs/Imu

3、相机内参标定

进入标定文件夹

bash 复制代码
cd /data/VideoTV00001_calibration

在该目录下新建一个target.yaml文件,这个是所用标定板的配置文件,参考标定板如图:

target.yaml文件内容为

bash 复制代码
tagCols: 6

tagRows: 6

tagSize: 0.021

tagSpacing: 0.3

target_type: aprilgrid

其中tagSize是打印后每个格子的实际尺寸,单位是米。需要根据实际调整。

进行相机内参标定

bash 复制代码
kalibr_calibrate_cameras --bag VideoTV00001_euroc.bag --target target.yaml --models pinhole-radtan --topics /cam0/image_raw

标定完成后会弹出可视化结果并输出标定结果文件至该目录下

4、相机imu外参标定

需要再新建imu.yaml和camchain.yaml文件

imu.yaml是imu内参的配置文件,可在产品手册中获取或者获取一小时以上imu的静置数据进行imu内参标定。Imu.yaml的格式如下:

bash 复制代码
accelerometer_noise_density: 0.00061667

accelerometer_random_walk: 0.0000043333

gyroscope_noise_density: 0.000098902

gyroscope_random_walk: 0.0000012928

rostopic: /imu0

update_rate: 188.58097757972

单位为

|-------------------------|--------|---------------|
| gyroscope_noise_density | 角速度白噪声 | rad/s/√Hz |

|-----------------------------|--------|--------------|
| accelerometer_noise_density | 加速度白噪声 | m/s²/√Hz |

|-----------------------|-----------|----------------|
| gyroscope_random_walk | 陀螺仪偏置随机游走 | rad/s²/√Hz |

|---------------------------|------------|--------------|
| accelerometer_random_walk | 加速度计偏置随机游走 | m/s³/√Hz |

update_rate根据实际频率填写

camchain.yaml是相机内参的配置文件,根据刚才相机内参的标定结果填写,camchain.yaml的内容格式如下:

bash 复制代码
cam0:

  cam_overlaps: []

  camera_model: pinhole

  distortion_coeffs: [0.07936144147573729, -0.09439727502429124, -0.001944215543450636,

    -0.0013778851088523114]

  distortion_model: radtan

  intrinsics: [643.3140528909328, 642.1840798849714, 986.8635102770212, 495.85841930165645]

  resolution: [1920, 1080]

  rostopic: /cam0/image_raw

进行外参标定

bash 复制代码
kalibr_calibrate_imu_camera --target target.yaml --imu imu.yaml --cams camchain.yaml --bag VideoTV00001_euroc.bag

标定完成后会弹出可视化结果并输出标定结果文件至该目录下

**附录:**新的kalibr_bagcreater.py内容如下,可以放在/data/目录下:

python 复制代码
#!/usr/bin/env python

# -*- coding: utf-8 -*-

# Compatible with EuRoC dataset format

# Structure:

# dataset_name/

# ├── mav0/

# │   ├── cam0/

# │   │   ├── data/

# │   │   │   ├── 1403....png

# │   │   │   └── ...

# │   │   ├── data.csv

# │   ├── imu0/

# │   │   ├── data.csv

#

# Output topics:

#   /cam0/image_raw

#   /imu0



print("importing libraries...")



import rosbag

import rospy

from sensor_msgs.msg import Image

from sensor_msgs.msg import Imu

import ImageFile

import time, sys, os

import argparse

import cv2

import numpy as np

import csv



parser = argparse.ArgumentParser(description='Create a ROS bag using EuRoC dataset (images + IMU).')

parser.add_argument('--folder', metavar='folder', required=True, help='Root folder of dataset (contains mav0)')

parser.add_argument('--output-bag', metavar='output_bag', default="output.bag", help='ROS bag output path')

parsed = parser.parse_args()



# === Helper functions ===



def read_image_list(cam_dir):

    """Read image timestamps and filenames from camX/data.csv"""

    csv_path = os.path.join(cam_dir, "data.csv")

    data_dir = os.path.join(cam_dir, "data")

    images = []

    timestamps = []

    if not os.path.exists(csv_path):

        print(" Image CSV not found:", csv_path)

        return []

    with open(csv_path, 'r') as f:

        reader = csv.reader(f)

        header = next(reader, None)

        for row in reader:

            if len(row) < 2:

                continue

            ts, filename = row[0], row[1]

            full_path = os.path.join(data_dir, filename)

            if os.path.exists(full_path):

                images.append((ts, full_path))

            else:

                print(" Missing image file:", full_path)

    # sort by timestamp

    images.sort(key=lambda x: int(x[0]))

    return images





def read_imu_data(imu_dir):

    """Read IMU measurements from imu0/data.csv"""

    imu_csv = os.path.join(imu_dir, "data.csv")

    if not os.path.exists(imu_csv):

        print(" IMU CSV not found:", imu_csv)

        return []

    imu_data = []

    with open(imu_csv, 'r') as f:

        reader = csv.reader(f)

        header = next(reader, None)

        for row in reader:

            if len(row) < 7:

                continue

            timestamp = row[0]

            gyro = [float(x) for x in row[1:4]]

            accel = [float(x) for x in row[4:7]]

            imu_data.append((timestamp, gyro, accel))

    return imu_data





def load_image_to_rosmsg(filename, timestamp_str):

    """Convert image to ROS Image message with timestamp"""

    image_np = cv2.imread(filename, cv2.IMREAD_GRAYSCALE)

    if image_np is None:

        print(" Failed to load:", filename)

        return None, None



    secs = int(timestamp_str[0:-9])

    nsecs = int(timestamp_str[-9:])

    timestamp = rospy.Time(secs, nsecs)



    rosimg = Image()

    rosimg.header.stamp = timestamp

    rosimg.height = image_np.shape[0]

    rosimg.width = image_np.shape[1]

    rosimg.encoding = "mono8"

    rosimg.step = rosimg.width

    rosimg.data = image_np.tostring()



    return rosimg, timestamp





def create_imu_msg(timestamp_str, gyro, accel):

    """Convert IMU sample to ROS Imu message"""

    secs = int(timestamp_str[0:-9])

    nsecs = int(timestamp_str[-9:])

    timestamp = rospy.Time(secs, nsecs)



    msg = Imu()

    msg.header.stamp = timestamp

    msg.angular_velocity.x = gyro[0]

    msg.angular_velocity.y = gyro[1]

    msg.angular_velocity.z = gyro[2]

    msg.linear_acceleration.x = accel[0]

    msg.linear_acceleration.y = accel[1]

    msg.linear_acceleration.z = accel[2]

    return msg, timestamp





# === Main logic ===



try:

    bag = rosbag.Bag(parsed.output_bag, 'w')

    print(" Creating bag:", parsed.output_bag)



    # Camera

    cam0_dir = os.path.join(parsed.folder, "mav0", "cam0")

    image_list = read_image_list(cam0_dir)

    print(" Found {} images".format(len(image_list)))

    for ts, filename in image_list:

        rosimg, t = load_image_to_rosmsg(filename, ts)

        if rosimg is not None:

            bag.write("/cam0/image_raw", rosimg, t)



    # IMU

    imu0_dir = os.path.join(parsed.folder, "mav0", "imu0")

    imu_list = read_imu_data(imu0_dir)

    print(" Found {} imu samples".format(len(imu_list)))

    for ts, gyro, accel in imu_list:

        imumsg, t = create_imu_msg(ts, gyro, accel)

        bag.write("/imu0", imumsg, t)



    print(" Finished writing bag file.")



finally:

    bag.close()

    print(" Bag closed successfully.")
相关推荐
欧云服务器2 天前
怎么让脚本命令可以同时在centos、debian、ubuntu执行?
ubuntu·centos·debian
智渊AI2 天前
Ubuntu 20.04/22.04 下通过 NVM 安装 Node.js 22(LTS 稳定版)
ubuntu·node.js·vim
The️3 天前
Linux驱动开发之Read_Write函数
linux·运维·服务器·驱动开发·ubuntu·交互
云卓SKYDROID3 天前
无人机实时信号传输技术要点解析!
科技·无人机·高科技·云卓科技
再战300年3 天前
Samba在ubuntu上安装部署
linux·运维·ubuntu
qwfys2003 天前
How to install golang 1.26.0 to Ubuntu 24.04
ubuntu·golang·install
木尧大兄弟3 天前
Ubuntu 系统安装 OpenClaw 并接入飞书记录
linux·ubuntu·飞书·openclaw
AI能见度3 天前
硬核:如何用大疆 SRT 数据实现高精度 AR 视频投射?
ar·无人机·webgl
云卓SKYDROID3 天前
无人机精准降落技术要点难点
无人机·高科技·云卓科技·物流无人机
小虾爬滑丫爬3 天前
ubuntu上设置Tomcat 开机启动
ubuntu·tomcat·开机启动