点群データの処理をしたくて、データを探していたところ、.bin
と.bag
という2種類の形式のファイルを見つけました。
.bag
の方が生データに近い形で保存されているファイルだと思うのですが、点群データはフォーマットが色々あって処理に至るまでの読み取りだけでも苦労します。
今回はとりあえず、この二種類のファイルを読んでいくことにしましょう。
1. .bin
ファイルの読み取り
こちらは Python
を使えば比較的簡単に読み取ることができます。
1.1. データの準備
今回は有名な点群データセットの一つである、KITTI
と呼ばれるデータセットを使ってみましょう。
KITTI
の中にもデータが色々あって、色々論文を読んでいてもどのデータセットを指しているのかわからないことも多いのですが、今回は road
の Velodyne laser point extension
を使いましょう。
KITTI の公式ページからアクセスして探してみてください。
少し面倒ですが、データをダウンロードする URL を取得するためにはアカウントを作ってログインしないといけないようです。
URL を手に入れたらとりあえず、ダウンロードして zip を解凍しておきましょう。
$ wget <手に入れたURL>
$ unzip data_road_velodyne.zip
データは train
と test
に分かれていて、それぞれにデータが入っています。
それぞれのデータの接頭辞は以下のような意味だそうです。 (今回はデータを読むだけなので関係ないですが…)
uu
: urban unmarkedum
: urban markedumm
: urban multiple marked lanes
では、今回は training/velodyne/uu_000000.bin
のファイルを Python
を使って読み込んでみましょう。
1.2. Python
で読み取る
numpy
の fromfile
を使えばバイナリファイルを簡単に読み取ってくれるので、これを使って以下のようなコードを書いてみました。
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
なら melodic
、20.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
ファイルを読み込むときは pyyaml
を pip
でインストールして、以下のコードを実行すれば、連想配列の形で読み込めます。
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
のタブの中から LaserScan
や PointCloud2
を選んで追加すると、以下のように点群が表示されるようになります。
3. まとめ
長くなってしまいましたが、.bin
と .bag
の二種類の点群データを読み取る方法を紹介しました。
特に .bag
は少しクセのある形式なので、読み取るのが大変でしたが、何とか最終的には使いやすい形に整形できたのではないかと思います。
これでようやく処理を書くことに集中できます…!!