SLAM センサーのキャリブレーションの概要: カメラ/IMU/LiDAR


カメラと IMU の内部基準キャリブレーション、カメラ、IMU、LiDAR 間のジョイント キャリブレーション方法、ツールキットのインストール環境は Ubuntu20.04 環境にあり、使用されるツールは次のとおりです。MATLAB ツールボックス、ROS キャリブレーション ツールキット、kalibr 、OpenCV、imu_utils、calibration_camera_lidar、lidar_imu_calib

1. カメラ (単眼) 内部パラメータのキャリブレーション

1.1 解決策 1: MATLAB ツールボックス

これが最も便利です。(記事) を参照し、matlab を開き、APP を見つけ、三角形をドロップダウンしてその他のオプションを表示し、Camera Calibrator を見つけます (デュアル ターゲット設定用のプログラムも提供します。ここでは単眼のデモを示します)。

ここに画像の説明を挿入

撮影したキャリブレーション ボードの写真を追加します (プラン 2 で保存した一連の画像でもかまいません) キャリブレーション ボードを撮影するときは、画面の半分以上を占めるように、前後左右を平行移動させて撮影するのが最適です。 、右、および回転。

ここに画像の説明を挿入

キャリブレーション ボードの隣接するチェッカーボード間の実際の距離を入力します。キャリブレーションの精度は、キャリブレーション ボードの精度に大きく依存します。ここでは 35mm です。[OK] をクリックすると、プログラムは自動的に角の点を抽出して一致させます。結果は次のとおりです。 :

ここに画像の説明を挿入

半径方向の歪みと接線方向の歪みの両方のキャリブレーションされた歪みパラメーター タイプを選択し、[キャリブレーション] をクリックして計算します。

ここに画像の説明を挿入

右側には、フレームごとの平均再投影誤差とすべてのフレームにわたる平均再投影誤差が、各画像フレームのポーズの視覚化とともに表示されます。2 つのフレームの再投影誤差が明らかに大きすぎることがわかります. クリックしてモーション ブラーのある画像を表示します. 左側の対応するフレームの画像を右クリックして削除し, 再計算することができます. 平均再投影最終結果の誤差は 0.16 ピクセルで、精度はすでに非常に高いです。

ここに画像の説明を挿入

[出力カメラ キャリブレーション パラメーター] をクリックして、キャリブレーション パラメーター変数を表示します。

ここに画像の説明を挿入

1.2 解決策 2: ROS キャリブレーション ツールキットを使用する

私の記事 (リンク)のセクション 7.1 を参照してください

1.3 解決策 3: キャリブレーション ツール kalibr を使用する

Kalibr は、マルチ IMU キャリブレーション、カメラ IMU キャリブレーション、およびマルチカメラ キャリブレーションの機能を提供します.ここで、マルチカメラ キャリブレーション コマンドは、単一のカメラをキャリブレーションするために使用されます。

1.3.1 kalibr のインストール

# for Ubuntu18.04
# 安装依赖
sudo apt update
sudo apt-get install python-setuptools python-rosinstall ipython libeigen3-dev libboost-all-dev doxygen libopencv-dev ros-melodic-vision-opencv ros-melodic-image-transport-plugins ros-melodic-cmake-modules python-software-properties software-properties-common libpoco-dev python-matplotlib python-scipy python-git python-pip ipython libtbb-dev libblas-dev liblapack-dev python-catkin-tools libv4l-dev
sudo pip install python-igraph --upgrade
# 创建工作空间并下载源码
mkdir -p kalibr_ws/src
cd kalibr_ws/src
git clone https://github.com/ethz-asl/kalibr.git
# 编译
cd .. 
source /opt/ros/melodic/setup.bash
catkin init
catkin config --extend /opt/ros/melodic
catkin config --merge-devel # Necessary for catkin_tools >= 0.4.
catkin config --cmake-args -DCMAKE_BUILD_TYPE=Release
catkin_make -DCMAKE_BUILD_TYPE=Release -j4
# for Ubuntu20.04
# 安装依赖
sudo apt update
sudo apt-get install python3-setuptools python3-rosinstall ipython3 libeigen3-dev libboost-all-dev doxygen libopencv-dev ros-noetic-vision-opencv ros-noetic-image-transport-plugins ros-noetic-cmake-modules python3-software-properties software-properties-common libpoco-dev python3-matplotlib python3-scipy python3-git python3-pip libtbb-dev libblas-dev liblapack-dev libv4l-dev python3-catkin-tools python3-igraph libsuitesparse-dev libgtk-3-dev
# 下面两个可能没反映或者错误,请等待和多尝试几次
pip3 install attrdict
pip3 install wxPython
# 创建工作空间并下载源码
mkdir -p kalibr_ws/src
cd kalibr_ws/src
git clone https://github.com/ethz-asl/kalibr.git
# 编译
cd .. 
source /opt/ros/noetic/setup.bash
catkin init
catkin config --extend /opt/ros/noetic
catkin config --merge-devel # Necessary for catkin_tools >= 0.4.
catkin config --cmake-args -DCMAKE_BUILD_TYPE=Release
catkin_make -DCMAKE_BUILD_TYPE=Release -j8

1.3.2 キャリブレーションプレートの準備

下図のように、kalibrが推奨するキャリブレーションボードであるAprilgridキャリブレーションボードを用意します。公式 Web サイトにアクセスして、著者が提供するキャリブレーション ボードをダウンロードできますが、科学的にインターネットにアクセスする必要があるため、著者の関数パッケージを使用して生成できます。

rosrun kalibr kalibr_create_target_pdf --type apriltag --nx [NUM_COLS] --ny [NUM_ROWS] --tsize [TAG_WIDTH_M] --tspace [TAG_SPACING_PERCENT]
eg: rosrun kalibr kalibr_create_target_pdf --type apriltag --nx 8 --ny 8 --tsize 0.1 --tspace 0.3
# 作者给的:
eg: rosrun kalibr kalibr_create_target_pdf --type apriltag --nx 6 --ny 6 --tsize 0.088 --tspace 0.3

エラーが報告された場合:「pyx」という名前のモジュールはありません。解決するには、github の問題を参照してください。

sudo apt install python-pyx #18.04
sudo apt install python3-pyx #20.04

現在のディレクトリに pdf ファイルが生成されます。

ここに画像の説明を挿入

1.3.3 校正方法

(1) 最初にデータを記録し、自分のカメラを駆動し、データを記録するときに上下左右に移動し、前後に 3 回ずつ回転し、最後にロバスト性を高めるためのランダムな動きがあります。

rosbag record /image_raw -O images.bag

kalibr が推奨するフレーム レートは 4Hz です. これはフレーム ドロップ コマンドです. 元の rosbag の old_topic を 4hz の new_topic にドロップし, rosbag の再生中に new_topic を記録します:

rosrun topic_tools throttle messages old_topic 4.0 new_topic

(2) その後、校正することができます

source devel/setup.bash
rosrun kalibr kalibr_calibrate_cameras --target april_6x6_24x24mm.yaml --bag images.bag --bag-from-to 5 20 --models pinhole-radtan --topics /image_raw --show-extraction

その中で、いくつかのパラメータ ファイルに注意を払う必要があります。

  • --bag images.bag: 記録されたパケット パス。
  • --bag-from-to 5 20袋の5秒目から20秒目までを読むことを指し、最初と最後に多少の激しいジッタがあり、エラーの原因となる可能性があるため、最初と最後の一部を切り取ります。プロセス全体がスムーズで、途切れることはありません。
  • --target april_6x6_24x24mm.yaml: tagSize と tagSpacing の 2 つのパラメータの作者が具体的に意味を説明したキャリブレーション ボード パラメータ ファイルを、自分で印刷したキャリブレーション ボードに合わせて変更します。
target_type: 'aprilgrid'   #gridtype
tagCols: 6              #number of apriltags
tagRows: 6             #number of apriltags
tagSize: 0.088          #size of apriltag, edge to edge [m]
tagSpacing: 0.3         #ratio of space between tags to tagSize
codeOffset: 0           #code offset for the first tag in the aprilboard

ここに画像の説明を挿入

キャリブレーションが完了すると、描画結果が自動的にポップアップ表示されます (使用したデータは正しくありません。再投影エラーが少し大きいため、さらにいくつかのデータを記録する必要があります)。

ここに画像の説明を挿入

ここに画像の説明を挿入

次の結果ファイルを同時に生成します。

  • カメラの内部パラメータ:
cam0:
  cam_overlaps: []
  camera_model: pinhole
  distortion_coeffs: [-0.294719325148729, 0.08601391290708914, -0.0003238494917967103, -0.00041999959826033857]
  distortion_model: radtan
  intrinsics: [461.14152593310064, 459.7917242849438, 363.2138823275693, 235.61324563304655]
  resolution: [752, 480]
  rostopic: /cam0/image_raw 
  • キャリブレーション結果のtxtファイル。
  • キャリブレーション結果のプロットの pdf。

1.4 解決策 4: キャリブレーションのために OpenCV を呼び出すプログラムを作成する

書籍「OpenCV4クイックスタート」を参照

#include <opencv2/opencv.hpp>
#include <fstream>
#include <iostream>
#include <vector>
#include <cstring>
#include <sstream>
#include <cstdlib>
using namespace std;
using namespace cv;

void initUndistAndRemap(vector<Mat> imgs, Mat cameraMatrix, Mat distCoeffs, Size imageSize, vector<Mat> &undistImgs)
{
    
    
    // 计算映射坐标矩阵
    Mat R = Mat::eye(3, 3, CV_32F);
    Mat mapx = Mat(imageSize, CV_32FC1);
    Mat mapy = Mat(imageSize, CV_32FC1);
    // 内参矩阵/畸变系数/...
    initUndistortRectifyMap(cameraMatrix, distCoeffs, R, cameraMatrix, imageSize, CV_32FC1, mapx, mapy);
    for (int i = 0; i < imgs.size(); i++)
    {
    
    
        Mat undistImg;
        remap(imgs[i], undistImg, mapx, mapy, INTER_LINEAR);
        undistImgs.push_back(undistImg);
    }
}

void undist(vector<Mat> imgs, Mat cameraMatrix, Mat distCoeffs, vector<Mat> &undistImgs)
{
    
    
    for (int i = 0; i < imgs.size(); i++)
    {
    
    
        Mat undistImg;
        // 单幅图像去畸变:畸变图像/去畸变后的图像/内参矩阵/畸变系数
        undistort(imgs[i], undistImg, cameraMatrix, distCoeffs);
        undistImgs.push_back(undistImg);
    }
}

bool LoadData(const string &imagePath, const string &imageFilename, vector<Mat> &imgs)
{
    
    
    ifstream Left;
    Left.open(imageFilename.c_str());
    while (!Left.eof())
    {
    
    
        string s;
        getline(Left, s);
        if (!s.empty())
        {
    
    
            stringstream ss;
            ss << s;
            double t;
            string imageName;
            ss >> t;
            ss >> imageName;
            Mat img = imread(imagePath + "/" + imageName);
            imgs.push_back(img);
            if (!img.data)
            {
    
    
                cout << "请输入正确的图像文件" << endl;
                return 0;
            }
        }
    }

    return 1;
}

int main(int argc, char **argv)
{
    
    
    if (argc != 6)
    {
    
    
        cerr << endl
             << "Usage: ./CameraCalibration path_to_CalibrateImage path_to_calibdata.txt board_size_cols board_size_rows corners_of_checkerboard(mm)" << endl
             << "eg: ./CameraCalibration ../CalibrateData ../CalibrateData/calibdata.txt 9 6 10" << endl;

        return 1;
    }

    // -1 读取数据
    vector<Mat> imgs;
    LoadData(argv[1], argv[2], imgs);
    // 棋盘格内角点行列数
    int bcols, brows;
    // 棋盘格每个方格的真实尺寸
    double side_length;
    stringstream ss;
    ss << argv[3];
    ss >> bcols;
    ss.clear();
    ss << argv[4];
    ss >> brows;
    ss.clear();
    ss << argv[5];
    ss >> side_length;
    Size board_size = Size(bcols, brows);

    // -2 提取并计算标定板角点像素坐标
    // 多副图像分别放入vector<vector<Point2f>>
    vector<vector<Point2f>> imgsPoints;
    for (int i = 0; i < imgs.size(); i++)
    {
    
    
        Mat img1 = imgs[i], gray1;
        cvtColor(img1, gray1, COLOR_BGR2GRAY);
        vector<Point2f> img1_points;
        // 第一个参数是输入的棋盘格图像
        // 第二个参数是棋盘格内部的角点的行列数(注意:不是棋盘格的行列数,而内部角点-不包括边缘-的行列数)
        // 第三个参数是检测到的棋盘格角点,类型为std::vector<cv::Point2f>
        bool ret = findChessboardCorners(gray1, board_size, img1_points);

        // 细化标定板角点坐标(亚像素),Size(5, 5)为细化方格坐标领域范围
        find4QuadCornerSubpix(gray1, img1_points, Size(5, 5));

        // 第一个参数是棋盘格图像(8UC3)既是输入也是输出
        // 第二个参数是棋盘格内部角点的行、列
        // 第三个参数是检测到的棋盘格角点
        // 第四个参数是cv::findChessboardCorners()的返回值。
        drawChessboardCorners(img1, board_size, img1_points, ret);
        // string windowNumber = to_string(i);
        // imshow("chessboard corners"+windowNumber, img1);

        imgsPoints.push_back(img1_points);
    }

    // -3 使用棋盘格每个内角点的世界坐标
    vector<vector<Point3f>> objectPoints; // 空间三维坐标(位于一个平面内,以此为xy坐标平面)
    for (int i = 0; i < imgsPoints.size(); i++)
    {
    
    
        vector<Point3f> tempPointSet;
        for (int j = 0; j < board_size.height; j++)
        {
    
    
            for (int k = 0; k < board_size.width; k++)
            {
    
    
                // 假设标定板为世界坐标系的z平面,即z=0
                Point3f realPoint;
                realPoint.x = j * side_length;
                realPoint.y = k * side_length;
                realPoint.z = 0;
                tempPointSet.push_back(realPoint);
            }
        }
        objectPoints.push_back(tempPointSet);
    }

    // -4 内参及畸变标定
    Size imageSize; // 图像尺寸
    imageSize.width = imgs[0].cols;
    imageSize.height = imgs[0].rows;
    // 定义内外参
    Mat cameraMatrix = Mat(3, 3, CV_32FC1, Scalar::all(0)); // 相机内参数矩阵
    Mat distCoeffs = Mat(1, 5, CV_32FC1, Scalar::all(0));   // 相机的5个畸变系数
    vector<Mat> rvecs, tvecs;                               // 每幅图像的旋转向量/平移向量
    // 调用OpenCV标定函数
    calibrateCamera(objectPoints, imgsPoints, imageSize, cameraMatrix, distCoeffs, rvecs, tvecs, 0);
    cout << "相机的内参矩阵K=" << endl
         << cameraMatrix << endl;
    cout << "相机畸变系数:" << endl
         << distCoeffs << endl;

    // -5 保存数据
    ofstream calibrateFile;
    // 写入数据:
    calibrateFile.open("../calibrateCamera.txt"); // 没有自动创建
    if (!calibrateFile.is_open())                 // 文件是否打开
    {
    
    
        exit(EXIT_FAILURE); // 终止程序
    }
    calibrateFile << fixed;     // 开始写入,与cout相同
    calibrateFile.precision(5); // 写入小数精度
    calibrateFile << "cameraMatrix K=" << endl
                  << cameraMatrix; // 写入数据(覆盖
    calibrateFile << endl << "distCoeffs=" << endl
                  << distCoeffs;
    calibrateFile.close();

    // -6 图像去畸变
    vector<Mat> undistImgs;
    // 使用initUndistortRectifyMap()函数和remap()函数校正图像
    initUndistAndRemap(imgs, cameraMatrix, distCoeffs, imageSize, undistImgs); // 畸变图像/前文计算得到的内参矩阵/畸变系数/图像尺寸/去畸变后的图像,自定义函数是为了处理多副图像
    // undist(imgs, cameraMatrix, distCoeffs, undistImgs);//用undistort()函数直接计算校正图像,自定义函数是为了处理多副图像
    // 显示校正前后的图像(一张示例)
    for (int i = 0; i < 1; i++)
    {
    
    
        string windowNumber = to_string(i);
        imshow("chessboard corners without undistort -- image" + windowNumber, imgs[i]);
        imshow("chessboard corners with undistort -- image" + windowNumber, undistImgs[i]);
        imwrite("../chessboard corners without undistort.png", imgs[i]);
        imwrite("../chessboard corners with undistort.png", undistImgs[i]);
    }
    waitKey(0);

    // -7 单目投影(重投影):根据成像模型及空间点三位坐标计算图像二维坐标
    vector<vector<Point2f>> imagePoints; // 存放二维坐标
    // 根据三维坐标和相机与世界坐标系时间的关系估计内角点像素坐标
    for (int i = 0; i < imgs.size(); i++)
    {
    
    
        Mat rvec = rvecs[i], tvec = tvecs[i];
        vector<Point3f> PointSets = objectPoints[i];
        vector<Point2f> imagePoint;                                                 // 存放二维坐标
        projectPoints(PointSets, rvec, tvec, cameraMatrix, distCoeffs, imagePoint); // 输入三维点/旋转及平移向量/前文计算得到的内参矩阵和畸变矩阵/输出二维点
        imagePoints.push_back(imagePoint);
    }

    // -8 计算重投影误差
    vector<vector<double>> ReProjectionError;
    vector<vector<double>> ReProjectionErrorX;
    vector<vector<double>> ReProjectionErrorY;
    double e = 0.0;
    for (int i = 0; i < imgs.size(); i++)
    {
    
    
        vector<Point2f> imagePoint = imagePoints[i]; // 存放二维坐标
        vector<double> er;
        vector<double> erX;
        vector<double> erY;
        for (int j = 0; j < imagePoint.size(); j++)
        {
    
    
            double eX = imagePoint[j].x - imgsPoints[i][j].x;
            double eY = imagePoint[j].y - imgsPoints[i][j].y;
            double error = sqrt(pow(eX, 2) + pow(eY, 2));
            erX.push_back(eX);
            erY.push_back(eY);
            er.push_back(error);
            e += error;
        }
        ReProjectionError.push_back(er);
        ReProjectionErrorX.push_back(erX);
        ReProjectionErrorY.push_back(erY);
    }
    // 计算估计值和图像中计算的真实时之间的平均误差
    cout << "平均重投影误差:" << e / (imagePoints[0].size() * imgs.size()) << endl;

    // -9 保存重投影误差数据
    ofstream ReProjectionErrorFile;
    ReProjectionErrorFile.open("../ReProjectionError.txt");
    ofstream ReProjectionErrorFileX;
    ReProjectionErrorFileX.open("../ReProjectionErrorX.txt");
    ofstream ReProjectionErrorFileY;
    ReProjectionErrorFileY.open("../ReProjectionErrorY.txt");
    if (!ReProjectionErrorFile.is_open() || !ReProjectionErrorFileX.is_open() || !ReProjectionErrorFileY.is_open())
    {
    
    
        exit(EXIT_FAILURE);
    }
    ReProjectionErrorFile << fixed;
    ReProjectionErrorFile.precision(5);
    ReProjectionErrorFileX << fixed;
    ReProjectionErrorFileX.precision(5);
    ReProjectionErrorFileY << fixed;
    ReProjectionErrorFileY.precision(5);
    for (int i = 0; i < imgs.size(); i++)
    {
    
    
        for (int j = 0; j < imagePoints[i].size(); j++)
        {
    
    
            ReProjectionErrorFile << ReProjectionError[i][j] << " ";
            ReProjectionErrorFileX << ReProjectionErrorX[i][j] << " ";
            ReProjectionErrorFileY << ReProjectionErrorY[i][j] << " ";
        }
        ReProjectionErrorFile << endl;
        ReProjectionErrorFileX << endl;
        ReProjectionErrorFileY << endl;
    }
    ReProjectionErrorFile.close();
    ReProjectionErrorFileX.close();
    ReProjectionErrorFileY.close();

    /*
    // 圆形标定板角点提取
    Mat img2 = imread("../CalibrateData/circle.png");
    Mat gray2;
    cvtColor(img2, gray2, COLOR_BGR2GRAY);
    Size board_size2 = Size(7, 7);          //圆形标定板圆心数目(行,列)
    vector<Point2f> img2_points;//检测角点,单副图像放入vector<Point2f>
    findCirclesGrid(gray2, board_size2, img2_points);           //计算圆形标定板检点
    find4QuadCornerSubpix(gray2, img2_points, Size(5, 5));      //细化圆形标定板角点坐标
    drawChessboardCorners(img2, board_size2, img2_points, true);
    imshow("圆形标定板角点检测结果", img2);
    waitKey();
    */

    return 0;
}

本のデータを例に取ります。

ここに画像の説明を挿入

歪み補正前後の違いが分かり、画像の直線が歪まなくなりました

元の画像

ここに画像の説明を挿入
Python を使用して再投影誤差をプロットします。

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# 编     写:ZARD帧心
import numpy as np
import matplotlib.pyplot as plt

errorX = np.genfromtxt('ReProjectionErrorX.txt')  # 将文件中数据加载到数组里
errorY = np.genfromtxt('ReProjectionErrorY.txt')
(m, n) = np.shape(errorX)
error = (errorX**2 + errorY**2)**0.5

# 画每帧图像的重投影误差均值
error_image =  np.average(error, axis=1)  # 按行求均值
image = range(m)
plt.bar(image, error_image)
plt.xlabel('Image number',{
    
    'Size':30})
plt.ylabel('ReProjection Error (pixel)',{
    
    'Size':30})
plt.xticks(fontsize=30) #x轴刻度字体大小
plt.yticks(fontsize=30) #y轴刻度字体大小
plt.show()

# 画所有角点的重投影误差
x = errorX.reshape(m*n,1)
y = errorY.reshape(m*n,1)
r = error.reshape(m*n,1) # 用于映射颜色的数值
# np.set_printoptions(threshold=np.inf)
# print(r)
# print(max(r))
h = plt.scatter(x, y, c=r, marker='*', alpha=0.8, cmap='viridis')
plt.xlabel('X error (pixel)',{
    
    'Size':30})
plt.ylabel('Y error (pixel)',{
    
    'Size':30})
plt.xticks(fontsize=30) #x轴刻度字体大小
plt.yticks(fontsize=30) #y轴刻度字体大小
cb=plt.colorbar(h) # 显示颜色条
font = {
    
    'weight' : 'normal',
        'size'   : 30, }
cb.ax.tick_params(labelsize=30)  #设置色标刻度字体大小。
cb.set_label('ReProjection Error',fontdict=font) #设置colorbar的标签字体及其大小
plt.show()

以下はScheme 1 MATLABと同じデータを描画した結果で、MATLABとはかけ離れています. 私のコードの書き方に問題があるのか​​もしれません. 誰かがそれを変更する必要がある場所を見たら, 教えてください, ありがとうあなた
ここに画像の説明を挿入

ここに画像の説明を挿入

その後、単純な方法を使用して全体的なエラーを排除し、次の結果を得ました。これは、より優れていますが、MATLAB にはまだほど遠いものです。

ここに画像の説明を挿入

ここに画像の説明を挿入

2. IMU 内部パラメータのキャリブレーション

imu_utils を使用して IMU を調整します。以下を参照してください:記事
(1) 依存関係のインストール

sudo apt-get install libdw-dev

(2) imu_utilscode_utilsをダウンロードしますが、gaowenliang のimu_utilsにはユニットの問題があります。imu_utils の mintar バージョンを使用してください
(3) コンパイルする前に、いくつかの問題に注意してください。

  • code_utils はeigen 、 OpenCV3ceresに依存しています。これらは私の以前の記事へのリンクであり、それらのインストール チュートリアルがあります。
  • imu_utils と code_utils を同時にコンパイルしないでください。imu_utils は code_utils に依存しているため、最初に code_utils をコンパイルしてから imu_utils をコンパイルしてください。

(4) ワークスペースに配置してコンパイルします。

catkin_make
  • コンパイル エラー: code_utils-master/src/sumpixel_test.cpp:2:24: fatal error: backward.hpp:No such file or directory、 code_utils の下に見つかりましたsumpixel_test.cpp。変更します
// #include "backward.hpp"
// 为 
#include “code_utils/backward.hpp”
  • C++ 標準ライブラリで多くのエラーが発生した場合は、まず C++ 標準を変更することを検討してください。
# set(CMAKE_CXX_FLAGS "-std=c++11")
set(CMAKE_CXX_FLAGS "-std=c++14")

(4) imu.bag を記録し、IMU を 30 ~ 120 分間静止させます。長いほど、IMU のバッグを記録します。

rosbag record /imu -O imu.bag

(5) IMU を調整します。例として、imu_utils のサンプル データを次に示します。

rosbag play -r 200 imu_utils/imu.bag  #(这里要写你录制的包的路径,200倍速播放)
source ./devel/setup.bash
roslaunch imu_utils A3.launch #示例数据

ここのファイルには、A3.launchIMU トピックと名前、キャリブレーション期間などのいくつかのパラメーターが含まれていることに注意してください。必要に応じて変更します。

<launch>
    <node pkg="imu_utils" type="imu_an" name="imu_an" output="screen">
        <param name="imu_topic" type="string" value= "/djiros/imu"/>
        <param name="imu_name" type="string" value= "A3"/>
        <param name="data_save_path" type="string" value= "$(find imu_utils)/data/"/>
        <param name="max_time_min" type="int" value= "150"/>
        <param name="max_cluster" type="int" value= "100"/>
    </node>
</launch>

キャリブレーションが完了すると、mynteye_imu_param.yaml と複数の txt ファイルが $(find imu_utils)/data/ の下に生成されます。
ここに画像の説明を挿入

3. カメラと IMU の共同キャリブレーション

以前にインストールされたキャリブレーション ツール kalibr を使用します。他のチュートリアルで指定されている推奨周波数は、カメラの場合は 20Hz、IMU の場合は 200Hz です。科学的にネットサーフィンができないので、著者から提供されたサンプルデータをダウンロードすることはできません. ここでのデータは、次のブログから取得されます: Ubuntu18.04+kalibr キャリブレーション ツールボックスのインストールとコンパイル
(1) 記録
2 つのニュースを購読する、画像、imu トピック:

rosbag record /imu_raw /image_raw

記録するときは、できるだけスムーズに動くようにしてください.速度が速すぎると、カメラで得られる画質が低下しすぎて、遅すぎてはいけません.イムの各軸を刺激するために、著者はデモンストレーションを提供しますデータ収集の参考に動画で、回転を各 3 回、不規則な動きの期間 (激しく運動しすぎないように注意) を行います。キャリブレーション ボードは完全に印刷されており、データ パケットを記録する際のカメラ距離は 0.8m を推奨します。

ここに画像の説明を挿入

ここに画像の説明を挿入

(2) キャリブレーション

source devel/setup.bash
rosrun kalibr kalibr_calibrate_imu_camera --target dynamic/april_6x6.yaml --bag dynamic/dynamic.bag --cam dynamic/camchain_mono.yaml --imu dynamic/imu_adis16448.yaml --bag-from-to 5 45 --imu-models scale-misalignment --timeoffset-padding 0.1 --show-extraction

1.3 と同様、パラメーター:

  • --dynamic/dynamic.bag: 記録されたパケット パス。
  • --bag-from-to 5 45バッグ 5 秒から 45 秒まで読み取ります。
  • --target april_6x6.yaml: キャリブレーション ボード パラメータ ファイル。
target_type: 'aprilgrid'   #gridtype
tagCols: 6              #number of apriltags
tagRows: 6             #number of apriltags
tagSize: 0.088          #size of apriltag, edge to edge [m]
tagSpacing: 0.3         #ratio of space between tags to tagSize
codeOffset: 0           #code offset for the first tag in the aprilboard
  • --cam dynamic/camchain.yaml: 以前にキャリブレーションされたカメラの内部基準 (このデータを直接使用するのは正しくありません)。kalibr でキャリブレーションされていない場合は、次の形式に従ってファイルを変更する必要がある場合があります。
cam0:
  cam_overlaps: [1, 3]
  camera_model: pinhole
  distortion_coeffs: [-0.0016509958435871643, 0.02437222940989351, -0.03582816956989852,
    0.019860839087717054]
  distortion_model: equidistant
  intrinsics: [461.487246372674, 460.1113992557959, 356.39105303227853, 231.15719697054647]
  resolution: [752, 480]
  rostopic: /cam0/image_raw
  • --imu dynamic/imu_adis16448.yaml: 前の方法で校正された IMU 内部基準を使用し、次の形式に記入します。
rostopic: /imu0
update_rate: 200.0 #Hz

accelerometer_noise_density: 0.01 #continous
accelerometer_random_walk: 0.0002

gyroscope_noise_density: 0.005 #continous
gyroscope_random_walk: 4.0e-06
  • --imu-models scale-misalignment: IMUのパラメトリックモデル
  • --timeoffset-padding 0.1: 値が大きいほど、キャリブレーションの実行時間が長くなります

キャリブレーションが完了すると、結果の図面が自動的にポップアップ表示されます。
ここに画像の説明を挿入
同時に、以下を含むいくつかの結果ファイルがデータ フォルダーに生成されます。

  • カメラと imu の内部および外部パラメーター間の時間同期の差 (カメラに対する IMU の遅延):
cam0:
  T_cam_imu:
  - [0.01676354570202121, 0.9998590327410452, -0.000947724452812317, 0.06843047675330072]
  - [-0.9998594579273542, 0.016763745309816058, 0.0002030674908090371, -0.014939612219976138]
  - [0.00021892627629230532, 0.0009441871264906492, 0.999999530290868, -0.0038646995667698156]
  - [0.0, 0.0, 0.0, 1.0]
  cam_overlaps: [1, 3]
  camera_model: pinhole
  distortion_coeffs: [-0.0016509958435871643, 0.02437222940989351, -0.03582816956989852, 0.019860839087717054]
  distortion_model: equidistant
  intrinsics: [461.487246372674, 460.1113992557959, 356.39105303227853, 231.15719697054647]
  resolution: [752, 480]
  rostopic: /cam0/image_raw
  timeshift_cam_imu: 5.9040012306710654e-05
  • IMU の内部および外部パラメータ:
imu0:
  T_i_b:
  - [1.0, 0.0, 0.0, 0.0]
  - [0.0, 1.0, 0.0, 0.0]
  - [0.0, 0.0, 1.0, 0.0]
  - [0.0, 0.0, 0.0, 1.0]
  accelerometer_noise_density: 0.01
  accelerometer_random_walk: 0.0002
  gyroscope_noise_density: 0.005
  gyroscope_random_walk: 4.0e-06
  model: calibrated
  rostopic: /imu0
  time_offset: 0.0
  update_rate: 200.0
  • キャリブレーション結果のtxtファイル
  • 校正結果図面のpdf文書

ここに画像の説明を挿入

4. カメラと LiDAR の共同キャリブレーション

キャリブレーション方法はAutowareから分離されたキャリブレーションパッケージcalibration_camera_lidar
です(1) cmakeはnloptを正常にインストールします

git clone https://github.com/stevengj/nlopt.git
cd nlopt
mkdir build && cd build
cmake ..
make
sudo make install

インストールに sudo apt-get install を使用しないように注意してください。catkin_make は後で問題に遭遇します
(2) calibration_camera_lidar をコンパイルします。

catkin_make
  • 古いパッケージはメロディックまたはノエティック バージョンの ROS サポートを追加する必要があり、CMakelists.txt 行 72/94/114 を変更します。
# if ("{ROS_VERSION}" MATCHES "(indigo|jade|kinetic)")
if ("{ROS_VERSION}" MATCHES "(indigo|jade|kinetic|melodic|noetic)")
  • エラーが報告された場合
jsk_recognition_msgsConfig.cmake
jsk_recognition_msgs-config.cmake

コンパイル用の jsk-recognition-msgs パッケージがないことを説明し、インストールします。

sudo apt-get install ros-<ROS VERSION NAME>-jsk-recognition-msgs
  • エラー:error: ‘CV_RGB’ was not declared in this scope; did you mean ‘CV_YUV2RGB’?位置プロンプトに従って、エラー位置の CV_RGB を cvScalar に変更します
  • Error: error: no match for ‘operator=’ (operand types are ‘CvMat’ and ‘cv::Mat’)
    これは CvMat と cv::Mat の相互変換の問題です. 間違った 2 つの文を次のように変更してください:
*m_intrinsic =  cvMat(m_intrinsic_opencv2to1);
*m_dist = cvMat(m_dist_opencv2to1);
  • エラー: 、cv_image->imageエラーcv_image->imageを に変更しますcvIplImage(cv_image->image)
    。異なるバージョンでは、cvIplimage が Iplimage と記述され、cvMat が CvMat と記述される場合があることに注意してください。

上記はすべて OpenCV のバージョンの問題であり、コードは opencv2 をベースに記述されており、opencv3/4 とは互換性がありません。

  • CMakeFiles/calibration_publisher.dir/nodes/calibration_publisher/calibration_publisher.cpp.o: 関数内void cv::operator>><cv::Mat>(cv::FileNode const&, cv::Mat&)': /usr/local/include/opencv2/core/persistence.hpp:1261: undefined reference to cv::Mat::Mat()'
    CMakeLists.txt 内の calibration_publisher ノードの opencv をリンクします
target_link_libraries(calibration_publisher
        ${catkin_LIBRARIES}
        ${OpenCV_LIBS})

(3) 録画パッケージ: 車の固定、キャリブレーション ボードの移動を選択します。キャリブレーション ボードの動きは、カメラのキャリブレーションに似ています。車の前面に対して、遠近、左右、さまざまな傾斜姿勢を基準にします。 (Autoware または記録の使用を選択できます); (4) 実行機能rosbasg record TOPICパッケージ
:

source devel/setup.bash
# 新开一个终端执行roscore
rosrun calibration_camera_lidar calibration_toolkit

(5) キャリブレーション方法
1) Autoware を使用してデータ セットを再生および一時停止する;
2) 起動後に画像トピックを選択する (公開したトピックに従って選択する);
3) キャリブレーション タイプを選択する: カメラからベロダインへ;
ツールがオートウェアに基づいているため、ここでは点群トピックを に変換する必要があります/points_raw。点群トピックが間違っている場合は、次の 2 つの方法で変更できます。

  • レーダー ドライバのパラメータ設定で、フレーム ID を velodyne に直接変更し、トピックを /points_raw に変更します。
  • rosbag をプレイするときは、トピックを変更します
    。たとえば、次のようになります。
rosbag play autoware-20221006.bag /rslidar_points:=/points_raw

4) [OK] をクリックしてキャリブレーション インターフェイスをポップアップします. インターフェイスの上のテキスト ボックスに入力するパターン サイズは、キャリブレーション ボード上のチェッカーボードの辺の長さ (m) であり、パターン番号はコーナー サイズです (注:これは、内部コーナー ポイントの数、つまりチェッカーボード グリッドの長さと幅です。-1)

5) キャリブレーションの前に、カメラの内部パラメーターによってキャリブレーションされた yml ファイルをインポートします (この関数パッケージを使用したキャリブレーションの結果である必要があります。次の手順でカメラの内部パラメーターを同時にキャリブレーションするため、この手順は必要ありません)。 、[読み込み] をクリックしてファイルをインポートします。インポートが完了すると、cameramat /distcoeff /imagesize に対応する内部パラメーター値が表示されます。

6) ウィンドウのサイズを調整し、黒い部分をマウスでクリックし、b を押して色を調整し、2 を押してモードを切り替え、視野角を調整し、点群データを表示します LiDAR 可視化調整方法: 点群を移動: 点群の回転: モードの切り替え:
視野上下左右方向键、PgUp、PgDn
a、d、w、s、q、e
ズーム数字1和数字2
:减号缩小、加号放大
点群のサイズ:o键缩小点云、p放大点云(建议放大,较为清晰)
点群ウィンドウの背景色の変更:b(建议背景全部调整成白色,清晰)

適切なフレームを再生した後、データ セットを一時停止し (キャリブレーション ボードが完成し、画像とポイント クラウドで明確になっています)、[グラブ] をクリックしてデータのフレームを代替キャリブレーション シーケンスに追加します。次に、座標軸がキャリブレーション ボードの中心に当たるように右下隅のレーザー データの角度を調整し、左ボタンをクリックして赤い中心をマークし、キャリブレーション フレームに追加します。赤いマークが間違っている場合は、右クリックして赤いマークをキャンセルできます. この代替フレームに満足できない場合は、右上隅の [削除] をクリックします. 以下に示すように:

ここに画像の説明を挿入

約 30 フレームをマークし、[調整] をクリックして調整結果の計算を開始すると、CameraExtrinsicMat /cameramat /distcoeff /imagesize に対応するパラメーター値が表示されます。結果が計算されたら、プロジェクトをクリックしてキャリブレーション ボードのレーザー ポイントを画像に投影し、キャリブレーションが正しいかどうか、画像の赤い点を大まかに確認し、最後に左上隅の [保存] をクリックして、キャリブレーション結果を保存します(カメラの内側と外側を含むジョイントキャリブレーション結果のみを保存します参照:カメラ座標系からレーダー座標系へ。.ymlファイルとして保存します。キャリブレーションに使用されるデータは保存しないでください。が大きすぎる場合は、必要に応じて保存できます)

ここに画像の説明を挿入

(7) 結果が保存された後、Autoware を介してカメラと LiDAR キャリブレーションの融合結果を視覚化し、シミュレーションでデータを再生し、センシング インターフェイスで [キャリブレーション パブリッシャー] をクリックして、キャリブレーション結果ファイル、ライダー フレーム ID を読み込みます。 target_frame のキャリブレーション データ; camera_frame ID のキャリブレーション データ内のビジュアル カメラ フレーム; ref 選択列で、yml ファイルを読み込みます

ポイント画像をもう一度クリックし、rviz を開きます。メニュー バーの Panels - Add New Panel を開き、ImageViewPlugin プラグインを選択し、画像と点群のトピックを選択してから、データ セットを再生し続けると、画像と点群の融合を見ることができます

5. LiDAR と IMU の共同キャリブレーション

キャリブレーション パッケージの lidar_imu_calib を使用します。前の 2 つの章のキャリブレーションの後、LiDAR と IMU の外部パラメーターを直接計算できますが、以前のキャリブレーションが正しいかどうか、またはデバイスに視覚センサーがないかどうかを確認することもできます。

5.1 解決策 1: 浙江大学のオープン ソース lidar_IMU_calib

5.2 解決策 2: ライダーの位置合わせ

5.3 解決策 3: lidar_imu_calib

この関数パッケージは、ライダーと imu の間の変換で姿勢コンポーネントのみを調整します (1) github
からソース コードをダウンロードし、独自のワークスペースでコンパイルします。

catkin_make

多数の PCL エラーが発生した場合は、ubuntu 20.04 の ROS バージョンが noetic であり、独自の pcl が pcl-1.10 であるためです.このバージョンは、c++14 以降でコンパイルし、CMakeLists で変更する必要があります. txt (2
) 記録パッケージ: 記録環境は、劣化した環境 (多数の樹木、低木、またはトンネル) ではなく、豊富な構造を持つ建物など、多くの平面および線の特徴を持つ場所である必要があり、車はゆっくりと動きます ( 3)
キャリブレーション

  • Launch ファイルで IMU、LiDAR メッセージのトピック名とバッグのパスを変更します。
<launch>
    <node pkg="lidar_imu_calib" type="calib_exR_lidar2imu_node" name="calib_exR_lidar2imu_node" output="screen">
        <param name="/lidar_topic" value="/velodyne_points"/>
        <param name="/imu_topic" value="/imu/data"/>
        <param name="/bag_file" value="/media/zard/SLAMData/2017-06-08-15-52-45_3.bag"/>
    </node>
</launch>
  • 実行機能パッケージ:
roslaunch lidar_imu_calib calib_exR_lidar2imu.launch

関数パッケージは、徐々に追加された LIDAR メッセージを表示し、最後に LIDAR メッセージの数と imu の数を表示し、制約の数を形成し、しばらくすると計算結果が表示されます。

ここに画像の説明を挿入

おすすめ

転載: blog.csdn.net/zardforever123/article/details/130030767