.bin と .bag の点群データを何とかして読みたい

公開日:
.bin と .bag の点群データを何とかして読みたい

点群データの処理をしたくて、データを探していたところ、.bin.bag という2種類の形式のファイルを見つけました。

.bag の方が生データに近い形で保存されているファイルだと思うのですが、点群データはフォーマットが色々あって処理に至るまでの読み取りだけでも苦労します。

今回はとりあえず、この二種類のファイルを読んでいくことにしましょう。

1. .bin ファイルの読み取り

こちらは Python を使えば比較的簡単に読み取ることができます。

1.1. データの準備

今回は有名な点群データセットの一つである、KITTI と呼ばれるデータセットを使ってみましょう。

KITTI の中にもデータが色々あって、色々論文を読んでいてもどのデータセットを指しているのかわからないことも多いのですが、今回は roadVelodyne laser point extension を使いましょう。

KITTI の公式ページからアクセスして探してみてください。
少し面倒ですが、データをダウンロードする URL を取得するためにはアカウントを作ってログインしないといけないようです。

URL を手に入れたらとりあえず、ダウンロードして zip を解凍しておきましょう。

$ wget <手に入れたURL>
$ unzip data_road_velodyne.zip

データは traintest に分かれていて、それぞれにデータが入っています。
それぞれのデータの接頭辞は以下のような意味だそうです。 (今回はデータを読むだけなので関係ないですが…)

  • uu: urban unmarked
  • um: urban marked
  • umm: urban multiple marked lanes

では、今回は training/velodyne/uu_000000.bin のファイルを Python を使って読み込んでみましょう。

1.2. Python で読み取る

numpyfromfile を使えばバイナリファイルを簡単に読み取ってくれるので、これを使って以下のようなコードを書いてみました。

import numpy as np
binFileName = 'training/velodyne/uu_000000.bin'
data = np.fromfile(binFileName, dtype=np.float32).reshape((-1, 4)).astype(np.float64)
print(data.shape)
print("[x, y, z, intensity] =", data[0])

実行結果は以下のようになります。

$ python loader.py
(115698, 4)
[x, y, z, intensity] = [53.375       4.81599998  2.02099991  0.        ]

LiDAR を原点とした三次元空間の x、y、z 成分と跳ね返ってきたセンサーの輝度の値からなる 4 次元のデータが、115698 個並んでいるという形のデータになります。

ここまで読み取れてしまえば、後は自分の扱いたい形 (2次元の画像にマッピングしたり、Voxel の形にしたり) に変形して、処理していけば OK です。

ちなみに、私がこれを使って道路のセグメンテーションをしたコードは以下に公開しています!

2. .bag ファイルの読み取り

今度は .bin ファイルより生のデータを扱うために .bag ファイルを読み込んでみます。
こちらは先程の .bin ファイルより少し厄介です。

2.1. .bag ファイルとは

.bag ファイルは以下の記事にあるように、ロボット開発で使われる ROS (Robot Operating System) におけるファイルフォーマットの一つです。

Python でも rospy をいうパッケージを使って ROS を扱うことができますが、Python2 でしか使えなかったりもするので、少し不便ですし、機械学習などをするのに毎回 ROS を起動するのも面倒です。

ということで、とりあえず .bag ファイルの中身をうまく読み出して、テキストファイルに直すことができれば、うまくパースして自由に使えるのではないかということで、テキストファイルに直すことをひとまずの目標にしましょう。

2.2. データの準備

今回は “Real-Time Streaming Point Cloud Compression for 3D LiDAR Sensor Using U-Net” (IEEE Access 2019) で使われていたデータセットが Google Drive に上がっていたので、それをお借りすることにします。
これに入っているデータは Velodyne HDL-64E S2 と呼ばれる LiDAR で撮影されたデータになります。

その中の Parking lot.bag でもダウンロードしておきましょう。
大文字とスペースがあると面倒なので (主観ですが)、parking-lot.bag に名前を変えておきます。

2.2. 【Appendix】strings でヘッダを読んでみる

このセクションは全然しなくてもいいんですが、とりあえず試してみたことを書いておきます。

Linux にはバイナリファイルを確認するために strings というコマンドがあります。

これを使って初めの45行を読んでみると以下のようになりました。

$ strings parking-lot.bag | head -45
#ROSBAG V2.0
chunk_count=-
conn_count=
index_pos=
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           )
compression=none
size=
conn=
topic=/velodyne_packets
callerid=/play_1463128354155151244
latching=0'
md5sum=50804fc9533a0e579e6322c04ae70566
message_definition=# Velodyne LIDAR scan packets.
Header           header         # standard ROS message header
VelodynePacket[] packets        # vector of raw packets
================================================================================
MSG: std_msgs/Header
# Standard metadata for higher-level stamped data types.
# This is generally used to communicate timestamped data
# in a particular coordinate frame.
# sequence ID: consecutively increasing ID
uint32 seq
#Two-integer timestamp that is expressed as:
# * stamp.secs: seconds (stamp_secs) since epoch
# * stamp.nsecs: nanoseconds since stamp_secs
# time-handling sugar is provided by the client library
time stamp
#Frame this data is associated with
# 0: no frame
# 1: global frame
string frame_id
================================================================================
MSG: velodyne_msgs/VelodynePacket
# Raw Velodyne LIDAR packet.
time stamp              # packet timestamp
uint8[1206] data        # packet contents
topic=/velodyne_packets
type=velodyne_msgs/VelodyneScan&
conn=
time={
velodyne\
([]"P
J0!,d#'
Tr!-K%g
 a<-&r$

ここから、ROSBAG V2.0/velodyne_packets というトピックを使えば、std_msgs/Header という型 (?) のヘッダーと velodyne_msgs/VelodynePacket という型 (?) の中身が得られそうだということがわかります。
特に、uint8[1206] data # packet contents がパケットの中身なので一番ほしいデータなのではないでしょうか。

また、最後の方の行は文字化けしていて読めませんが、この辺りから最後までがデータの中身なのではないかと推測できます。

ということで、ここからはROS を使ってこのファイルをテキストファイルの形で出力していきましょう。

2.4. ROS のインストール

余計なものが入ってしまうかもしれませんが、Ubuntu でとりあえず使えればいいということであれば、ryuichiueda さんの GitHub に上がっているスクリプトを使えばインストールが完了します。

Ubuntu18.04 の部分はご自身の使っている Ubuntu のバージョンに合わせて変更してください。

$ cd ~
$ git clone https://github.com/ryuichiueda/ros_setup_scripts_Ubuntu18.04_server
$ cd ros_setup_scripts_Ubuntu18.04_server/
$ ./step1.bash
$ source ~/.bashrc

それ以外の場合は詳しく解説しませんが、ROS Wiki のページなどを見ればいいのではないかと思います。(ちょっと試してみましたが、結構つまりそうだったので、上のスクリプトを使ってインストールすることをお勧めします😇)

続いて、必要なパッケージ (ros-<version>-velodyne) をインストールしておきましょう。

<version> の部分は Ubuntu のバージョンによって違うので、ROS Wiki のページを参考にしてください。
新しいのだけ書いておくと、18.04 なら melodic20.04 なら noetic です。

18.04の場合以下のようにすれば、インストールできます。

$ sudo apt install ros-melodic-velodyne

2.5. .bag をテキストファイルに変換

ようやくテキストファイルに変換するところまで来ました。
ここからは以下の stack overflow の記事がとても参考になりました。

まず、一つターミナルを立ち上げて、以下のコマンドを実行しましょう。

$ roscore # Terminal 1
... logging to /home/ユーザ名/.ros/log/謎の文字列/roslaunch-謎の文字列.log
Checking log directory for disk usage. This may take a while.
Press Ctrl-C to interrupt
Done checking log file disk usage. Usage is <1GB.

started roslaunch server http://localhost:40579/
ros_comm version 1.15.11

次に、別のターミナルから、以下のコマンドを実行します。
parking-lot.txt の部分は保存したいファイル名にしてください。

$ rostopic echo /velodyne_packets > parking-lot.txt # Terminal 2

最後にダウンロードしていた parking-lot.bag があるディレクトリでターミナルを開いて、以下のコマンドを実行してください。

$ rosbag play parking-lot.bag # Terminal 3

これで、Terminal 3 で実行したコマンドで点群の動画が再生されて、それを Terminal 2 で実行しているコマンドで読み込んで保存してくれます。
この間の通信をするために Terminal 1 で実行したコマンドが必要というイメージでしょうか。 (違っていたらすいません… 🙏)

2.6. 読み込まれたファイルを確認する

読み込まれたファイルを確認すると以下のようになっています。
かなり大きいサイズになっているので、 VSCode などで開こうとするとフリーズしてしまうかもしれないので注意してください。

$ ls -l parking-lot.txt
-rw-rw-r-- 1 ユーザ名 グループ名 1064400402 Sep 18 16:46 parking-lot.txt
$ cat bag.txt | head -12
header:
  seq: 2989
  stamp:
    secs: 1426824827
    nsecs: 700004339
  frame_id: "velodyne"
packets:
  -
    stamp:
      secs: 1426824827
      nsecs: 600265026
    data: [255, 238, 116, 34, 0, 0, 19, 83, 26, 189, 199, ... (省略)

このデータは以下のような形になっています。

  • 約0.1秒ごとに 1 packet (10 Hz なので 1 周分で、60秒ほどのデータなので 600 packets 程度ある)
  • 各 packet に data は 348個
  • data の中身は 上下 32 個ずつのレーザの値が 6 組
  • 各 data のフォーマットは velodyne のサポートページに上がっているもの (LiDAR の種類は少し違いますがおそらく同じだと思われます)

2.7. ファイルをパースしてキャリブレーションする

ここまでで、ひとまずの目標であった .bag をテキストファイルに変換するところが完了しました 👏
後はこのデータをパースして、キャリブレーションすればデータが得られます。 (まだまだ道のりは長い…)

とりあえず、ファイルをパースする部分は作業なので、以下のページにあげておきました。

raw_data = readFile(data_name, progress=True) で得られた raw_data がファイルそのままの情報になります (タイムスタンプなど不要かもしれないものも全て含んでいます)。

次に、キャリブレーションですが、Python に全て移植するのは面倒だったので、自分で必要な距離のデータだけしかキャリブレーションしていません…

ちなみに、距離のキャリブレーションの式は、元の値 x 距離の解像度 (今回は 0.002) + dist_correction の値 となります。

キャリブレーションについてのドキュメントが見つからなかったので、Google Drive に上がっていた 64S2.yaml ファイルを使って、以下の GitHub のコードを参考にしながらキャリブレーションしました。

キャリブレーションに必要なパラメータは以下のファイルに定義されていました。

後は、自分で必要な部分を頑張って移植してください… 🙏

ちなみに、Python.yaml ファイルを読み込むときは pyyamlpip でインストールして、以下のコードを実行すれば、連想配列の形で読み込めます。

import yaml
yamlFileName = '64S2.yaml'
calibration = yaml.load(yamlFileName, Loader=yaml.SafeLoader)

最後は用途によっても必要な処理が変わってくるので、少しふわっとした終わり方になってしまいましたが、これで何とか .bag ファイルを簡単に操作できる形に変形できたのではないかと思います…!

ちなみに、私がこれを使って点群データの圧縮を実装したコードは以下に公開しています!

2.8. 【Appendix】.bag ファイルを可視化してみる

ターミナルを4つ開いてそれぞれで以下のコマンドを実行します。

$ roscore # Terminal 1
$ roslaunch velodyne_pointcloud 64e_S3.launch # Terminal 2
$ rosrun rviz rviz -f velodyne # Terminal 3
$ rosbag play parking-lot.bag # Terminal 4

Terminal 2 で実行しているのが、データをキャリブレーションして送信してくれるもので、Terminal 3 で実行しているコマンドが可視化のためのアプリケーションを開くものです。(おそらく)

Terminal 2 で実行しているものは以下のようなエラーログが出るかもしれませんが、特に問題はなさそうです。

[ WARN] [1631953718.152705644]: Velodyne poll() timeout
[ERROR] [1631953718.152783765]: DriverNodelet::devicePoll - Failed to poll device.

Terminal 3 で立ち上げたアプリケーションの Displays のタブの下の方に Add というボタンがあるので、それを押して、By topic のタブの中から LaserScanPointCloud2 を選んで追加すると、以下のように点群が表示されるようになります。

可視化した点群の様子
可視化した点群の様子

3. まとめ

長くなってしまいましたが、.bin.bag の二種類の点群データを読み取る方法を紹介しました。

特に .bag は少しクセのある形式なので、読み取るのが大変でしたが、何とか最終的には使いやすい形に整形できたのではないかと思います。

これでようやく処理を書くことに集中できます…!! 👏

コメントを残す

メールアドレスが公開されることはありません。

CAPTCHA