takminの書きっぱなし備忘録 @はてなブログ

主にコンピュータビジョンなど技術について、たまに自分自身のことや思いついたことなど

カメラ/IMUのキャリブレーションツールkalibrを使ってみた

はじめに

本記事は3D Sensor Adevent Calenderの第14日目です。

https://qiita.com/advent-calendar/2019/3d-sensor

最初にお詫びを

 Realsense D435iをベースに作業を行っていたため、勘違いしてました。。。

というわけで、3Dとはほとんど関係ない、IMUとカメラのキャリブレーションについてのお話です。

ご存知の通り、RealsenseやKinectなどの3Dセンサーだけでなく、スマートフォンなどにはInertial Measurement Unit (IMU)というものが入っています。これは、ジャイロスコープと加速度計からなる装置(GPSなどのセンサが付く場合もある)で、3軸の角速度および3方向の加速度を取得することができます。

例えばARなどではVisual SLAMという技術を用いて、カメラの映像から三次元のマップとカメラの位置姿勢を同時推定することで、カメラ映像にリアルタイムで3Dオブジェクトをマッピングすることができますが、この技術の欠点として、撮影物体の形はわかっても大きさまではわからなかったり、テクスチャのないのっぺりした画像に対してはうまくトラッキングできないなどの欠点があります。

f:id:takmin:20191213180729p:plain 

Visual Inertial SLAMは、カメラ映像にIMUの情報を加えることで、これらの欠点を補うことが可能です。

Visual SLAMやVisual Inertial SLAMについては、こんな記事書いてます。

takmin.hatenablog.com

Visual Inertial SLAMを使うためにはカメラやIMUについてキャリブレーションを行い、以下のパラメータを取得する必要があります。

  • カメラのレンズ歪や焦点距離等の内部パラメータ
  • IMUのノイズの分散
  • カメラとIMUとの位置関係

このカメラとIMU間のキャリブレーションを行うツールとして、近年良く使用されているのがKalibrです。

github.com

Kalibrはチューリッヒ工科大学のPaul Furgaleらによって開発された、複数カメラ、カメラ/IMU、ローリングシャッターのキャリブレーションを行うツールキットです。

この記事では、このKalibrの使い方について日本語で簡単に解説をしたいと思います。

Kalibrでキャリブレーションする方法については、以下の動画を見ていただくと、すぐイメージが出来ると思います。

www.youtube.com

インストール手順

インストールはこちらの公式ドキュメントに基づいて行いました。

https://github.com/ethz-asl/kalibr/wiki/installation

 

こちらのドキュメントではUbuntu 14.04とROS Indigoを使用していますが、私はUbuntu18.04とROS Melodicで行いました。 インストール方法にはCDEパッケージというのを用いて依存関係も含めて一括で入れる方法と、ソースからビルドする方法の2つがあり、今回はソースからビルドする方法で行います。

私の場合、すでにROS Melodicがインストール済みだったため、以下の手順でインストールを行いました。

 

1.  依存関係のあるライブラリのインストール

 # 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 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

  また、Realsenseを使用する場合は、別途Realsense SDKとrealsense-rosをインストールする必要があります。 Realsense SDKとrealsense-rosのインストール方法についてはここでは説明しません。以下のサイトを参考にしてください。

Realsense SDK

https://www.intelrealsense.com/developers/

  Realsense ROS

https://github.com/IntelRealSense/realsense-ros

2. catkinワークスペースの作成

KlibrをソースからビルドするためにROSのワークスペースを作成します。

 # mkdir -p ~/kalibr_ws/src
 # cd ~/kalibr_ws
 # source /opt/ros/melodic/setup.bash
 # catkin init
 # catkin config --extend /opt/ros/melodic
 # catkin config --merge-devel

 

3. リポジトリをcloneしてビルド

最後にKalibrのソースコードgithubよりダウンロードしてビルドします。

 # cd ~/kalibr_ws/src
 # git clone https://github.com/ethz-asl/kalibr.git
 # cd ../
 # catkin build -DCMAKE_BUILD_TYPE=Release -j4

 

と、ここまではインストールドキュメントの手順通りだったのですが、私の場合ビルド中で以下のようなEigenとOpenCVに関するエラーが出力されました。

Errors << ethz_apriltag2:cmake /home/takuya/kalibr_ws/logs/ethz_apriltag2/build.cmake.002.log
CMake Warning at /opt/ros/melodic/share/cmake_modules/cmake/Modules/FindEigen.cmake:62 (message):
The FindEigen.cmake Module in the cmake_modules package is deprecated.

Please use the FindEigen3.cmake Module provided with Eigen. Change
instances of find_package(Eigen) to find_package(Eigen3). Check the
FindEigen3.cmake Module for the resulting CMake variable names.

Call Stack (most recent call first):
CMakeLists.txt:8 (find_package)

 

CMake Warning at /opt/ros/melodic/share/catkin/cmake/catkin_package.cmake:166 (message):
catkin_package() DEPENDS on 'opencv' but neither 'opencv_INCLUDE_DIRS' nor
'opencv_LIBRARIES' is defined.
Call Stack (most recent call first):
/opt/ros/melodic/share/catkin/cmake/catkin_package.cmake:102 (_catkin_package)
CMakeLists.txt:10 (catkin_package) 

実は既にOpenCVをソースからビルドしていたのに、別にOpenCVをaptでインストールしてしまったために、OpenCVの参照先関係でエラーが起きたようです。 そこで、以下のようにOpenCVとEIGENのパスを指定したらうまくいきました。

 # catkin build -DCMAKE_BUILD_TYPE=Release -j4 -Deigen_INCLUDE_DIRS=/usr/include/eigen3 -DCMAKE_MODULE_PATH=/usr/share/OpenCV

4. catkinワークスペースのsetupをsourceするスクリプトを.bashrcに追加

 # echo "source /home/takuya/kalibr_ws/devel/setup.bash" >> ~/.bashrc
 # source /home/takuya/kalibr_ws/devel/setup.bash

 

これでKalibrを使用する準備が整いました。

 

キャリブレーション

では、実際にKalibrを使用してカメラとIMUのキャリブレーションを行います。 キャリブレーションの手順については、以下の公式ドキュメントを参考にしてください。

Camera IMU calibration · ethz-asl/kalibr Wiki · GitHub

 

大まかな手順としては

  1. IMUの初期パラメータを記載したYAMLファイルの準備
  2. チェッカーパターンを壁に貼り付けて、センサーで撮影し、データをRos bagの形で保存
  3. キャリブレーションプログラムを起動

となります。

1. IMUの初期パラメータを記載したYAMLファイルの準備

ここでは、以下の3つのYAMLファイルを用意します。

  • チェッカーパターンについて記述したtarget.yaml
  • カメラパラメータの初期値を記述したchain.yaml
  • IMUパラメータの初期値を記述したimu.yaml  

target.yamlの編集

まず、以下のサイトからチェッカーパターンとyamlファイルを取得します。 https://github.com/ethz-asl/kalibr/wiki/downloads

 

ここでは Aprilgrid 6x6 0.8x0.8 m (A0 page) をダウンロードしました。

f:id:takmin:20191213235447j:plain

 

この"april_6x6_80x80cm_A0.pdf"を印刷し、以下の図のようにAとBの実際の長さをメジャー等で測ります。

f:id:takmin:20191213235540p:plain

"april_6x6_80x80cm.yaml"をコピーし、"target.yaml"とファイル名を変更します。

測った長さは"target.yaml"の中の"tagSize"にAをメートル単位で、"tagSpacing"にAとBの比率(=B/A)を記載します。

target_type: 'aprilgrid' #gridtype
tagCols: 6               #number of apriltags
tagRows: 6               #number of apriltags
tagSize: 0.0271          #size of apriltag, edge to edge [m]
tagSpacing: 0.2214        #ratio of space between tags to tagSize

chain.yamlの編集

chain.yamlには、カメラの内部パラメータやレンズ歪、カメラとIMU間の相対位置を記載します。 ここで記載したものを初期パラメータとしてキャリブレーションを行い、パラメータを更新します。

詳しい説明はこちらのリンクを参照してください。

https://github.com/ethz-asl/kalibr/wiki/yaml-formats

このカメラパラメータの取得は、私はRealsense SDKを用いてセンサーが内部的に保有しているパラメータを取得しました。

 // Create a Pipeline - this serves as a top-level API for streaming and processing frames
    rs2::pipeline p;

    // Configure and start the pipeline
    rs2::pipeline_profile selection = p.start();

    // Extrinsic Paramter
    rs2::stream_profile color_stream = selection.get_stream(RS2_STREAM_COLOR);

    rs2::stream_profile accel_stream = selection.get_stream(RS2_STREAM_ACCEL);
    rs2_extrinsics a2c = accel_stream.get_extrinsics_to(color_stream);
    printf("T_cam_imu:");

        //print extrinsic parameter
    printf("[");
    int k = 0;
    for (int i = 0; i < 3; i++) {
        for (int j = 0; j < 3; j++) {
            printf("%f,",e.rotation[k]);
            k++;
        }
        printf("%f,\n", e.translation[i]);
    }
    printf("0.0,0.0,0.0,1.0]\n");

    // Intrinsic Parameter
    rs2::video_stream_profile video_stream = color_stream.as<rs2::video_stream_profile>();
    printf("resolution: (%d,%d)\n", video_stream.width(), video_stream.height());

    rs2_intrinsics color_intrinsic = video_stream.get_intrinsics();
    printf("distortion:");
    for (int i = 0; i < 5; i++) {
        printf("%f,", color_intrinsic.coeffs[i]);
    }
    printf("\n");
    printf("intrinsics: [%f,%f,%f,%f]\n", color_intrinsic.fx, color_intrinsic.fy, color_intrinsic.ppx, color_intrinsic.ppy);

chain.yamlは以下のようなフォーマットです。 上記のコードで得られたパラメータを記載します。

cam0:
  camera_model: pinhole
  intrinsics: [610.993591, 610.823059, 323.637970, 238.294464]
  distortion_model: radtan
  distortion_coeffs: [0.0, 0.0, 0.0, 0.0]
  T_cam_imu:
  - [0.999945,0.009040,-0.005378,0.020510]
  - [-0.009021,0.999953,0.003476,-0.004948]
  - [0.005409,-0.003428,0.999979,-0.011323]
  - [0.0, 0.0, 0.0, 1.0]
  timeshift_cam_imu: -8.121e-05
  rostopic: /camera/color/image_raw
  resolution: [640, 480]

imu.yamlの編集

次にimu.yamlを編集します。 IMUが測定する角速度および加速度は以下の式のように、真値に対しガウスノイズとバイアスを加えたものとして表現されます。

f:id:takmin:20191214014440j:plain

バイアスは微分値の変動がガウス分布に従っており、このガウスノイズの分散(noise_density)およびバイアスの微分値の分散(random_walk)をそれぞれyamlファイルに記載する必要があります。

私はアラン分散(Allan Variance)というものを用いて、これらの値を算出する方法を用いました。ただ、もしRealsense SDK経由でこれらの値が取得できるなら、その方が良いと思います。(私は調べてもわかりませんでした)

アラン分散について書くと長くなるので、ここでは割愛します。各々ググってください。

#Accelerometers
accelerometer_noise_density: 0.17503487119062797   #Noise density (continuous-time)
accelerometer_random_walk:   0.11540370899319194   #Bias random walk

#Gyroscopes
gyroscope_noise_density:     0.02955356570015007   #Noise density (continuous-time)
gyroscope_random_walk:       0.0038742360784085003   #Bias random walk

rostopic:                    /camera/imu      #the IMU ROS topic
update_rate:                 200.0      #Hz (for discretization of the values above)

 

2. ROS Bagの取得

キャリブレーションのためのデータをROS Bagという形式で取得します。 Realsenseで撮影した画像やIMUのデータは、realsense-rosを通してROS Topicという形式で配信されます。 ROS Bagはリアルタイムに配信されるROS Topicのメッセージをキャプチャしてファイルとして保存したものです。保存したROS Bagを再生すると、キャプチャ時の信号をそのまま復元できるので、実際のセンサーがなくてもその時のデータの流れを何度でも再現できるようになります。

Kalibrでは、カメラ映像とIMUの信号をROS Bagとして保存する必要があります。

ROS Bagを取得するには、まず印刷したチェッカーパターンを壁に貼り付けます。

次にrealsense-rosを起動しますが、その前に/src/realsense-ros/realsense2_camera/launch/rs_camera.launchファイル内のパラメータを修正します。 今回はcolor_fps、gyro_fps、accel_fpsを修正し(realsense viewerを起動して確認)、enable_syncをtrueにしました。

以下のコマンドでrealsense-rosが起動します。

 # roslaunch realsense2_camera rs_camera.launch

rvisというROSのVisualizationツールを立ち上げておくと、実際にセンサーが取得しているデータを目視で確認することが可能です。

 # rosluanch rviz rviz

rosbagのキャプチャを開始します。 引数には、カメラ映像、IMUデータのトピック名を指定します。

 # rosbag record /camera/color/image_raw /camera/color/camera_info /camera/accel/imu_info /camera/gyro/imu_info /camera/imu

あとは、 最初の動画で紹介したようにセンサーをチェッカーパターンの前で、x,y,z軸まわりの回転及び、x,y,z軸方向の平行移動を行うように動かします。

3. キャリブレーション

ROS Bagを取得できれば、後は実際にキャリブレーションを行うだけです。

編集したyamlファイルとrosbagを用いてkalibrを実行します。

kalibr_calibrate_imu_camera --bag 2019-10-25-16-04-59.bag --cam chain.yaml --imu imu.yaml --target target.yaml

キャリブレーションが終わるとレポート画面が立ち上がります。

f:id:takmin:20191215004212p:plain
レポート画面

また上記の例では、以下のようなキャリブレーション済みファイルが出力されます。

  • camchain-imucam-2019-10-25-16-04-59.yaml
  • imu-2019-10-25-16-04-59.yaml
  • results-imucam-2019-10-25-16-04-59.txt
  • results-imucam-2019-10-25-16-04-59.pdf

camchain-imucam-2019-10-25-16-04-59.yamlはカメラのキャリブレーション結果で、フォーマットはchain.yamlと同じです。 同様にimu-2019-10-25-16-04-59.yamlはIMUのキャリブレーション結果でフォーマットは上記のimu.yamlと同じです。

results-imucam-2019-10-25-16-04-59.txtとresults-imucam-2019-10-25-16-04-59.pdfは同じ内容を表していて、キャリブレーションの残差等の結果が記載されています。

これで、キャリブレーションは完了です。

最後に

というわけで、カメラとIMUのキャリブレーションツールであるKalibrの使い方について解説しました。 Kalibrについては日本語の資料がほとんど存在しないため、こうやってブログに残しておけば誰かの役に立つかなと思っていたのですが、なかなかまとめる時間が取れませんでした。結局、今回3Dは関係ありませんでしたが、アドベントカレンダーという締め切りを使って書かせてもらいました。

私自身、そこまでKalibrを使いまくっているわけではないので、間違っている個所などあるかもしれません。できれば公式ドキュメントを見た上で、この記事が理解の手助けになれば良いなと思います。

最後までお読みいただきありがとうございました。