設定ファイルから見るLiDARシミュレータの世界

この記事はLiDAR Advent Calendar 2023の12/06分の記事です。

いろんなシミュレータのLiDARに関する設定ファイルの読書感想文です。

3D LiDARの簡単な分類

シミュレータの話に入る前に、一度シミュレーション対象であるLiDARをみておきましょう。 ここでは細かな動作原理の違いを考えずに出力される点群の特徴をざっくりと捉えていきます。

回転式LiDAR

昔からよくあり、現在でも主流のLiDARの種類です。 レーザーと受光センサのユニットを水平に回転させるため、水平360°にセンシングできることが特徴です。 1ユニットでは縦方向に視野を広げることができないため、ピッチ角を変化させた複数ユニットを用いることが多いです。 垂直方向の点群密度を増やすにはユニットを増やす必要があるため、水平方向の点群密度に対して縦方向の点群密度は粗くなりがちです。

SolidState式LiDAR

回転式(機械式)と対比されることが多いSolidState式は機械的な可動部が存在しない(または小さい*1)LiDARの種類です。

回転式とは違って光源を動かさずに光を曲げたり加工したりして走査する必要があるため、回転式に比べて水平視野はどうしても狭くなります。 一方で構造次第で垂直方向にも自由度を持てるので、垂直方向の点群密度を容易に上げることが可能です。 カメラの画像のような縦横比がほぼ同じ解像度をもつLiDARが多いですが、独自のスキャンパターンを持つLiDARも少なくありません。

シミュレータ選手紹介

今回見ていくシミュレータの選手紹介です。 オープンソースで提供されているもののみを対象としています。 3D LiDARは自動運転で使われることが多い関係上、自動運転向けシミュレータが多くなりました。

シミュレータ 概要
Gazebo ROS向けデフォルトのシミュレータ
Choreonoid 産総研発の国産シミュレータ
UnitySensors Unityでセンサをシミュレーションするためのシミュレータ、というよりはライブラリ?
CARLA 自動運転業界でデファクトスタンダードになっているシミュレータ
LGSVL 旧Autoware向け標準シミュレータ
AWSIM 現Autoware向け標準シミュレータ

各シミュレータの設定ファイル

一番最初に触れたように3D LiDARはそれぞれ独自の構成を持っています。 LiDARシミュレータで多くの種類のLiDARに対応するために、構造の違いをパラメータの違いで表現できるように一般化して実装されます。 この構造の一般化方式が色濃く出るのがLiDARセンサのために設定ファイルです。
さぁ、設定ファイルからLiDARシミュレータの世界に飛び込んでみましょう。

※一部ソースコードも含みますが、一般化できていれば設定ファイルとして扱います。

Gazebo(Classic)

垂直・水平方向に過不足なく表現力を持ったフォーマットです。 LiDAR以外にも深度カメラでも使えるように配慮されたフォーマットのように思えます。

<ray>
  <scan>
    <horizontal>
      <samples>32</samples>
      <resolution>1</resolution>
      <min_angle>-3.14156</min_angle>
      <max_angle>3.14156</max_angle>
    </horizontal>
    <vertical>
      <samples>3</samples>
      <resolution>1</resolution>
      <min_angle>-0.02</min_angle>
      <max_angle>0.02</max_angle>
    </vertical>
  </scan>
  <range>
    <min>0.05</min>
    <max>70</max>
    <resolution>0.02</resolution>
  </range>
</ray>

Choreonoid

最低限のシンプルなパラメータで構造を表現しています。 視野角を下限上限の2パラメータではなく角度の1パラメータで表現しているため、 上方向などある方向に偏った視野をもつ3D LiDARを表現できないことに注意です。

yawRange: 180
yawStep:  0.4
pitchRange: 30.0
pitchStep: 2.0
scanRate:  20
maxDistance: 100.0

参考:URL

UnitySensors

このシミュレータは設定ファイルを持たず、それぞれのセンサーに専用実装を持っています。 しかし、以下のように実質設定ファイルとなっている箇所もあります。

[SerializeField]
private RotatingLiDARScanPattern _scanPattern;
[SerializeField]
private float _minDistance = 0.0f;
[SerializeField]
private float _maxDistance = 100.0f;
[SerializeField]
private float _maxIntensity = 255.0f;
[SerializeField]
private float _gaussianNoiseSigma = 0.0f;

参考:URL

スキャンパターンは現状以下のようなものがサポートされているようです

名前 特徴
RotatingLiDARScanPattern 回転式LiDAR用の実装。垂直方向の角度は細かく設定できるようになっているのが特徴
CSVLiDARScanPattern CSVファイルからスキャンパターンを読み込む。Livoxのような複雑なパターンも再現可

CARLA

回転式LiDARに特化した設定ファイルとなっています。 回転式以外の表現は難しいですが、流石自動運転向けと言ったところで大気減衰率やドロップオフ(点群の消失)に関するパラメータが充実しており、 天候によるLiDARセンシングの変化もシミュレーションできるようになっているようです。

Blueprint attribute Type Default
channels int 32
range float 10.0
points_per_second int 56000
rotation_frequency float 10.0
upper_fov float 10.0
lower_fov float -30.0
horizontal_fov float 360.0
atmosphere_attenuation_rate float 0.004
dropoff_general_rate float 0.45
dropoff_intensity_limit float 0.8
dropoff_zero_intensity float 0.4
sensor_tick float 0.0
noise_stddev float 0.0

参考:URL

LGSVL

こちらも回転式LiDARに特化した設定ファイルになっています。 カスタムLiDARでは一様にならないことも多い各センサーの垂直方向の角度は細かく設定できるようになっているのが特徴で、 形状の表現力としてはCARLAよりも上になります。

Name = "Lidar32-NonUniform",
LaserCount = 32,
MinDistance = 0.5f,
MaxDistance = 100.0f,
RotationFrequency = 10, // 5 .. 20
MeasurementsPerRotation = 1500, // 900 .. 3600
FieldOfView = 41.33f,
VerticalRayAngles = new List<float>
{
  -25.0f, -1.0f, -1.667f, -15.639f,
  -11.31f, 0.0f, -0.667f, -8.843f,
  -7.254f, 0.333f, -0.333f, -6.148f,
  -5.333f, 1.333f, 0.667f, -4.0f,
  -4.667f, 1.667f, 1.0f, -3.667f,
  -3.333f, 3.333f, 2.333f, -2.667f,
  -3.0f, 7.0f, 4.667f, -2.333f,
  -2.0f, 15.0f, 10.333f, -1.333f
},
CenterAngle = 10.0f,

参考:URL

AWSIM

UnitySensorsのCSVLiDARScanPatternに似てレーザー一本一本を設定することができます。 注目すべきはtimeOffsetでタイミングのズレを設定できるところです。 UnitySensorsも同じく任意形状のスキャンパターンを設定できますが、時刻の設定ができないためLivoxのようなレーザーが一本しかないLiDARしか表現できません。 一方で、AWSIMは複数本のレーザーを持つLiDARはもちろんの事、フラッシュ式のようなタイミングが特殊なLiDARに対する表現力を持っています。 また、この設定項目があるということは、スキャン中にセンサーが移動することによる点群の歪み*2もシミュレーションできるのかもしれません。

public static LaserArray VelodyneVLP16 => new LaserArray
{
    centerOfMeasurementLinearOffsetMm = new Vector3(0.0f, 37.7f, 0.0f),
    focalDistanceMm = 0.0f,
    lasers = new[]
    {
        new Laser {verticalAngularOffsetDeg = +15.0f, verticalLinearOffsetMm = +11.2f, ringId = 0, timeOffset = 0.0f },
        new Laser {verticalAngularOffsetDeg = -1.0f, verticalLinearOffsetMm = -0.7f, ringId = 8, timeOffset = 0.002304f },
        new Laser {verticalAngularOffsetDeg = +13.0f, verticalLinearOffsetMm = +9.7f, ringId = 1, timeOffset = 0.004608f },
        new Laser {verticalAngularOffsetDeg = -3.0f, verticalLinearOffsetMm = -2.2f, ringId = 9, timeOffset = 0.006912f },
        new Laser {verticalAngularOffsetDeg = +11.0f, verticalLinearOffsetMm = +8.1f, ringId = 2, timeOffset = 0.009216f },
        new Laser {verticalAngularOffsetDeg = -5.0f, verticalLinearOffsetMm = -3.7f, ringId = 10, timeOffset = 0.011520f },
        new Laser {verticalAngularOffsetDeg = +9.0f, verticalLinearOffsetMm = +6.6f, ringId = 3, timeOffset = 0.013824f },
        new Laser {verticalAngularOffsetDeg = -7.0f, verticalLinearOffsetMm = -5.1f, ringId = 11, timeOffset = 0.016128f },
        new Laser {verticalAngularOffsetDeg = +7.0f, verticalLinearOffsetMm = +5.1f, ringId = 4, timeOffset = 0.018432f },
        new Laser {verticalAngularOffsetDeg = -9.0f, verticalLinearOffsetMm = -6.6f, ringId = 12, timeOffset = 0.020736f },
        new Laser {verticalAngularOffsetDeg = +5.0f, verticalLinearOffsetMm = +3.7f, ringId = 5, timeOffset = 0.023040f },
        new Laser {verticalAngularOffsetDeg = -11.0f, verticalLinearOffsetMm = -8.1f, ringId = 13, timeOffset = 0.025344f },
        new Laser {verticalAngularOffsetDeg = +3.0f, verticalLinearOffsetMm = +2.2f, ringId = 6, timeOffset = 0.027648f },
        new Laser {verticalAngularOffsetDeg = -13.0f, verticalLinearOffsetMm = -9.7f, ringId = 14, timeOffset = 0.029952f },
        new Laser {verticalAngularOffsetDeg = +1.0f, verticalLinearOffsetMm = +0.7f, ringId = 7, timeOffset = 0.032256f },
        new Laser {verticalAngularOffsetDeg = -15.0f, verticalLinearOffsetMm = -11.2f, ringId = 15, timeOffset = 0.034560f },
    }
};

参考:URL

最後に

本当はもっと色々なことを書こうと思っていたのですが、想像以上に長くなりそうだったので別の記事に分けようと思います。 それではまた次の記事で。

*1:これをSolidStateに含めるかは会社や文献によりまちまちです。 小さい可動部が存在する広義のSolidStateLiDARと差別化するため、True-Solid-Stateを名乗るLiDARもあります

*2:イメージし辛い方はカメラで発生するローリングシャッター現象のLiDAR版、と言えば分かるでしょうか?いや、もっと分かりにくいか...