🐼

PandaSet チュートリアル

2025/01/26に公開

概要

現在, 国内外では自動運転技術の開発が着々と進められている. 国内では ウーブン・バイ・トヨタ, Tier4, Turing など, また海外では Tesla, Waymo などが技術開発を進めている. これら各企業では技術開発のために車両にカメラやLiDARなどを取り付けて, 独自でデータを収集している。これらデータについては一部公開されているが, 研究利用を前提としていることが多い. そこで, 本記事では商用利用可能な自動運転関連のデータ PandaSet を紹介する.

データ (データセット)

データセットの概要

本データセットは機械的回転式 LiDAR x1, (固定式) 前向き LiDARx1, カメラx6, GPS/IMU からデータを収集し, アノテーションが付与されている. データセットの内容は以下のとおりである.

  • 48,000+ camera images
  • 16,000+ LiDAR sweeps
  • 100+ scenes of 8s each
  • 28 annotation classes
  • 37 semantic segmentation labels

ダウンロード

ここでは, 公式のデータセットのリンクに問題があるため, kaggle で公開されているデータセットを使用する. データセットのダウンロード方法は以下のとおりである.

download_dataset.py
import kagglehub

path = kagglehub.dataset_download("usharengaraju/pandaset-dataset")

print("Path to dataset files:", path)

データに触れる

ここでは, ダウンロードした PandaSet に実際に触れ, 自動運転で利用されるデータの内容を確認する. データを操作をするときは, PandaSet のデータ公開元から提供されている pandaset-devkit を利用すると簡単にデータの読み込みや変換ができる. しかしながら, 今回使用するデータセットの拡張子は pkl である一方で, 公開元のソースコードは pkl.gz をロードように作られている. このため, 本記事では pandaset-devkit を修正したソースコードでデータを操作する. なお, 以降で紹介するソースコードは本家GitHubリポジトリを参照して作成したものである.

Camera データの探索

初めに, カメラのデータを確認する. 関連ソースは walkaround_camera_data.py にある.

DataSetの初期化とシーケンスの確認
PandaSet で使用できるシーケンスを確認することができる.

dataset = DataSet(args.data_root)

for sequence in dataset.sequences():
    print(f"Sequence: {sequence}")
# Sequence: 029
# Sequence: 028
# Sequence: 006
# Sequence: 027
# ...

特定のシーケンスの取得 (ロード) と使用できるカメラの確認
実際のデータにアクセスするためには, load() を実行する必要がある. load() を実行することで利用可能なセンサデータやメタデータなどがロードされる. (※点群や画像がメモリにロードされるわけではなく, ロードされるディレクトリが準備される.)

seq021 = dataset[args.seq_id]
seq021.load()

print(f"Camera keys are {seq021.camera.keys()}")
# Camera keys are dict_keys(['left_camera', 'front_left_camera', 'back_camera', 'front_camera', 'front_right_camera', 'right_camera'])

※なお, 以下のような特定のデータをロードするためのメソッドも用意されている.

seq021.load_lidar().load_cuboids()

特定のカメラデータの取得と可視化

front_camera = seq021.camera["front_camera"]

print(f"The type of front camera data is {type(front_camera[0])}")
# The type of front camera data is <class 'PIL.Image.Image'>

front_camera[0].show()

以下のような画像が得られるfront_camera[0]

カメラの外部パラメータや内部パラメータなどの確認

print(f"Camera pose as in world coordinates system: {front_camera.poses[0]}")
# Camera pose as in world coordinates system: {'position': {'x': -0.10183915761624543, 'y': 0.2475485175954002, 'z': 1.8111254529001093}, 'heading': {'w': 0.669361122849436, 'x': -0.6752971251731775, 'y': -0.21475012932979395, 'z': 0.22318571169190157}}

print(f"Camera intrinsic parameters (fx, fy, cx, cy): {front_camera.intrinsics.fx}, {front_camera.intrinsics.fy},
{front_camera.intrinsics.cx}, {front_camera.intrinsics.cy}")
# Camera intrinsic parameters(fx, fy, cx, cy): 1970.0131, 1970.0091, 970.0002, 483.2988

print(f"Timestamp of the recording: {front_camera.timestamps[0]}")
# Timestamp of the recording: 1557540905.250561

LiDAR データの探索

続いて, LiDAR データを確認する.関連ソースは
walkaround_lidar_data.py にある.

DataSetの初期化と特定のシーケンスの取得 (再喝)
シーケンス ID を指定することで特定のシーケンスを取得できる.

dataset = DataSet(args.data_root)
seq021 = dataset[args.seq_id]

LiDAR データのロード (取得) と Shape の確認
PandaSet の LiDAR データは回転式と固定式のデータが存在するが, デフォルトのデータは両データを含むものである.

seq021.load_lidar()

# Default data of LiDAR is all point cloud (mechanical 360° LiDAR and front-facing LiDAR)
pcd = seq021.lidar[0]
print(f"Default shape of point cloud is {pcd.shape}")
# Default shape of point cloud is (168371, 6)

型の確認
Point Cloud のデータ型は pandas.DataFrames である. このため values から numpy.ndarray 型のデータを取得することができる. なお, LiDARの点群情報は世界座標系で保存されている.

print(f"The type of point cloud is {type(pcd)}")
# The type of point cloud is <class 'pandas.core.frame.DataFrame'>

# You can also access the point cloud data as a numpy array
print(f"The type of values of point cloud is {type(pcd.values)}")
# The type of values of point cloud is <class 'numpy.ndarray'>

# The values are stored n a world coordinate system
print(f"The point cloud values are :\n{pcd.values}")
# The point cloud values are
# [[ 3.24978447e+01 -4.21920967e+01  7.69186020e-01  0.00000000e+00
   1.55754091e+09  0.00000000e+00]...

外部パラメータの取得 (front facing LiDAR)
カメラと同様に LiDAR に関しても外部パラメータを取得できる.

print(seq021.lidar.poses[0])
# {'position': {'x': 0.0, 'y': 0.0, 'z': 0.0}, 'heading': {'w': 0.9521639452791718, 'x': -0.008971387743415438, 'y': 0.013956220434414187, 'z': 0.30513695191429313}}

特定のセンサ (mechanical 360° LiDAR) データの取得
set_sensor() メソッドを使用することで特定の LiDAR データを取得することができる. 回転式 LiDAR の場合は以下の set_sensor(0) のように0を指定して該当のデータを取得する.

seq021.lidar.set_sensor(0)
pcd360 = seq021.lidar[0]
print(f"The shape of mechanical 360° LiDAR: {pcd360.shape}")
# The shape of mechanical 360° LiDAR: (103967, 6)

特定のセンサ (front facing LiDAR) データの取得
同様に set_sensor(1) のように1を指定して固定式 LiDAR のデータを取得する.

seq021.lidar.set_sensor(1)
pcd_front = seq021.lidar[0]
print(f"The shape of front-facing LiDAR: {pcd_front.shape}")
# The shape of front-facing LiDAR: (64404, 6)

点群の可視化

続いて, 先ほど確認した LiDAR データの点群を可視化する. 関連ソースは visualize_pcd.py にある

LiDAR データのロードと取得
以下で LiDAR データをロードし, LiDAR データを取得する. なお, 回転式と固定式の LiDAR データを色分けするために, それぞれを分けて取得する.

dataset = DataSet(args.data_root)
seq021 = dataset[args.seq_id]
seq021.load_lidar().load_semseg()

# get mechanial LiDAR points
seq021.lidar.set_sensor(0)
points360 = seq021.lidar[args.frame_idx].to_numpy()

# get front-faceing LiDAR points
seq021.lidar.set_sensor(1)
points_front = seq021.lidar[args.frame_idx].to_numpy()

自車座標系に変換
元の LiDAR の座標系は世界座標系である. ここでは geometry.lidar_points_to_ego() メソッドを使用して, LiDAR データを自車座標系に変換する. なお, 自車座標系に変換しなくても, 可視化そのものはできる.

ego_points360 = geometry.lidar_points_to_ego(
    points360[:, :3],
    seq021.lidar.poses[args.frame_idx],
)

ego_points_front = geometry.lidar_points_to_ego(
    points_front[:, :3],
    seq021.lidar.poses[args.frame_idx],
)

データの可視化
最後に Open3D (o3d) を使用して, 点群を可視化する. 今回は回転式 LiDAR の点群を青色で, 固定式 LiDAR の点群を赤色で可視化した. なお Open3D で色していする場合は, RGBの順に色の値を0~1で指定する (0~255ではない).

ego_pcd360 = o3d.geometry.PointCloud()
ego_pcd360.points = o3d.utility.Vector3dVector(ego_points360)
ego_pcd360.paint_uniform_color(COLOR_BLUE)

ego_pcd_front = o3d.geometry.PointCloud()
ego_pcd_front.points = o3d.utility.Vector3dVector(ego_points_front)
ego_pcd_front.paint_uniform_color(COLOR_RED)

o3d.visualization.draw_geometries(
    [axis_pcd, ego_pcd360, ego_pcd_front],
    window_name="Ego Coordinate System",
)

以下のような点群が可視化される. (青色: 回転式 LiDARの点群, 赤色: 固定式 LiDAR の点群)

point_cloud.jpg

画像と点群のフュージョン

最後にこれまで扱ってきた画像と点群フュージョンしたデータを可視化する. 関連ソースは visualize_pcd_on_image.py にある.

画像のみ
まず, 本記事の冒頭で示した画像を可視化する. (データがロードされていることを前提とする.)

lidar = seq021.lidar

# get xyz coordinates of the LiDAR points
points3d_lidar_xyz = lidar.data[args.frame_idx].to_numpy()[:, :3]
choosen_camera = seq021.camera[args.camera_name]

points2d_camera, points3d_camera, inliner_indices = geometry.projection(
    lidar_points=points3d_lidar_xyz,
    camera_data=choosen_camera[args.frame_idx],
    camera_pose=choosen_camera.poses[args.frame_idx],
    camera_intrinsics=choosen_camera.intrinsics,
    filter_outliers=True,
)

# image before projection
ori_image = seq021.camera[args.camera_name][args.frame_idx]
plt.imshow(ori_image)
plt.show()

以下の画像が得られる.

camera_only.jpg

画像と点群 (距離情報で色付け)
次の点群情報をカメラ座標系に変換して, 点群を画像に埋め込む. ここではカメラから点群までの距離を用いて, 点群に色を付与する.

distances = np.sqrt(np.sum(np.square(points3d_camera), axis=-1))
colors = cm.jet(distances / np.max(distances))
plt.imshow(ori_image)
plt.gca().scatter(points2d_camera[:, 0], points2d_camera[:, 1], color=colors, s=1)
plt.show()

以下の画像が得られる.

fusion.jpg

画像と点群 (アノテーション情報で色付け)
最後に先ほどと同様に点群を画像に埋め込む. ここでは 点群のアノテーション情報を使用し, 点群をクラスごとに色分けしたものを画像に埋め込む.

# get semantic segmentation data
semseg = seq021.semseg[args.frame_idx].to_numpy()
# get semantic segmentation on image by filting outside points
semseg_on_image = semseg[inliner_indices].flatten()

# generate random color for each class
max_seg_id = np.max(semseg_on_image)
color_maps = [
    (random.random(), random.random(), random.random()) for _ in range(max_seg_id + 1)
]
colors = np.array([color_maps[seg_id] for seg_id in semseg_on_image])
plt.imshow(ori_image)
plt.gca().scatter(points2d_camera[:, 0], points2d_camera[:, 1], color=colors, s=1)
plt.show()

以下の画像が得られる.

fusion_with_seg.jpg

おわりに

本記事では, PandaSet について紹介した. このように自動運転では, 画像, 点群などのさまざまなセンサデータが使用され, 現在もデータ収集が行われている. Waymo は2025年から東京でのデータ収集を開始し, さらなる技術開発が進められるだろう. 今後も自動運転関連の技術に注目したい.

参照先

Discussion