- Authors

- Name
- Youngju Kim
- @fjvbn20031
- はじめに
- なぜ3D認識は難しいのか
- 深度を得るセンサーたち
- SLAM — 同時的自己位置推定と地図作成
- 3D表現 — 点群・ボクセル・occupancy
- 6D姿勢推定
- 深層学習による認識 — 検出と分割
- NeRFと3Dガウシアン・スプラッティング
- 座標系と変換 — 認識の隠れた背骨
- センサーフュージョンの深掘り
- 実戦パイプラインのスケッチ
- 認識性能をどう評価するか
- 単眼深度推定 — 片目で深度を見る
- 動的環境SLAMの概要
- 実務で出会う落とし穴
- おわりに
- 参考資料
はじめに
人は目を開けた瞬間、特に努力もせず部屋の構造を把握します。机がどこにあり、ドアまで何歩か、コップが手の届く距離にあるかを即座に知ります。ところがロボットにとって、これは決して当たり前ではありません。カメラが渡してくるのはピクセルの格子、つまり色の数値の並びにすぎません。ここから「3次元空間で私は今どこにいて、周りには何があるのか」という問いに答えることが、ロボット認識(perception)の核心です。
この記事では、ロボットが世界を見る方法を段階的に解きほぐします。まずどのセンサーで深度を得るのかを見て、そのデータを地図に編み上げるSLAMを説明します。続いて点群やボクセルといった3D表現、物体の位置と向きを推定する6D姿勢推定、深層学習による検出と分割、そして最近注目のNeRFと3Dガウシアン・スプラッティングがロボット認識にとってどんな意味を持つかまで扱います。
正確さを優先しつつ、概念を直感的につかめるよう図と簡単なコードを添えます。
なぜ3D認識は難しいのか
2D画像は3D世界を平面に投影した結果です。投影の過程で深度情報が失われるため、1つのピクセルだけを見ても、その点がカメラからどれほど離れているかはわかりません。これを奥行きのあいまいさ(depth ambiguity)と呼びます。
世界(3D) 画像(2D)
┌───────────┐ ┌───────────┐
│ ● 物体A(近い) │ 投影 │ ● │
│ ● 物体B(遠い)┼────────▶ │ ● │ 同じピクセル位置でも
│ │ │ │ 深度はわからない
└───────────┘ └───────────┘
カメラ光線上のすべての点が1つのピクセルに重なる
深度を復元するには追加の情報が必要です。両目(ステレオ)の視差、能動的に放つ光の往復時間、あるいは学習された事前知識(単眼深度推定)などがその手段です。ロボット認識は結局「失われた次元を取り戻す」作業だとまとめられます。
深度を得るセンサーたち
ステレオカメラ
人の両目のように、一定間隔(baseline)離れた2台のカメラを使います。同じ物体が2枚の画像で異なる位置に写り、この差を視差(disparity)と呼び、深度に反比例します。
深度 Z = (焦点距離 f x 基線 B) / 視差 d
左カメラ 右カメラ
│ \ / │
│ \ / │
│ \ / │
│ \ ● / │ ● が左右画像で写る
│ \ / │ ピクセル位置の差 = 視差 d
└───B(基線)────┘
視差が大きいほど物体が近くにあります。計算量があり、テクスチャのない壁のような場所では対応点探索が難しいという限界がありますが、受動(passive)センサーなので屋外でもよく動作します。
RGB-Dカメラ
色(RGB)と深度(Depth)を同時に提供するセンサーです。代表的に構造光(structured light)方式とToF(Time of Flight)方式があります。構造光は既知の模様を物体に投影しその歪みを読んで深度を計算し、ToFは光を放って戻ってくる時間を測ります。
RGB-D出力 = カラーフレーム + 整列された深度フレーム
ピクセル (u, v) ──▶ 色 (R, G, B)
└─▶ 深度 Z(メートル)
カメラ内部パラメータ(intrinsics)で3D点を復元:
X = (u - cx) * Z / fx
Y = (v - cy) * Z / fy
Z = Z
RGB-Dは屋内で安価で便利ですが、強い日差し(赤外線の干渉)やガラス・鏡のような表面には弱いです。
LiDAR
レーザーを回転させながら四方に放ち、各方向から戻る時間で距離を測ります。結果は非常に精密な3D点群です。自動運転で広く使われ、照明条件に強く長距離精度が高い一方、色の情報がなく比較的高価です。
| センサー | 深度の原理 | 長所 | 限界 |
|---|---|---|---|
| ステレオ | 2視点の視差 | 安価・屋外可・色あり | テクスチャなし面に弱い |
| RGB-D(構造光/ToF) | 能動光の投影/往復時間 | 屋内で精密・色あり | 日差し・ガラスに弱い |
| LiDAR | レーザー往復時間 | 長距離・高精度・照明無関係 | 高価・色なし |
実際のロボットはこれらを組み合わせ、互いの弱点を補います。これをセンサーフュージョン(sensor fusion)と呼びます。
SLAM — 同時的自己位置推定と地図作成
SLAM(Simultaneous Localization and Mapping)は、ロボットが未知の環境を動き回りながら同時に二つを解く問題です。一つは「今どこにいるか」(自己位置推定、localization)、もう一つは「周囲がどうなっているか」(地図作成、mapping)です。
問題は、この二つが鶏と卵の関係にある点です。正確な地図があれば自分の位置がわかり、正確な位置がわかれば地図を描けますが、最初はどちらもありません。SLAMはこの二つの推定を同時に最適化します。
SLAMパイプライン
センサー入力 フロントエンド バックエンド
┌────────┐ ┌──────────────────┐ ┌──────────────────┐
│ カメラ │──▶ │ 特徴抽出/マッチ │──▶ │ 姿勢グラフ最適化 │
│ LiDAR │ │ オドメトリ推定 │ │ バンドル調整(BA) │
│ IMU │──▶ │ (フレーム間移動) │ │ ループ閉合の反映 │
└────────┘ └────────┬─────────┘ └────────┬─────────┘
│ │
▼ ▼
┌──────────────┐ ┌──────────────┐
│ 局所地図更新 │◀──────│ 大域地図整合 │
└──────────────┘ └──────────────┘
│
▼
ループ閉合検出 ──▶「前に来た場所」認識 → 累積誤差を補正
- フロントエンドは連続するフレーム間の相対移動を素早く推定します。これを視覚オドメトリ(visual odometry)と呼び、フレームごとに少しずつ誤差が積もります(ドリフト)。
- ループ閉合(loop closure)は、ロボットが以前訪れた場所に戻ったことを認識する段階です。「ここはさっきの廊下だ」と気づくと、それまで累積した誤差を一度に補正する手がかりが得られます。
- バックエンドは複数時点の姿勢を一つの大きな最適化問題にまとめ、大域的に一貫した地図を作ります。バンドル調整(bundle adjustment)と姿勢グラフ最適化(pose graph optimization)が中心的な道具です。
誤差の累積とループ閉合
視覚オドメトリだけを使うと、時間が経つにつれ位置推定が少しずつずれていきます。100メートル回って出発点に戻ったのに、地図上では数メートル離れた場所にいる、といった具合です。ループ閉合はこの不一致を検出し、姿勢グラフ全体を調整し直して誤差を分散させます。
ループ閉合の前 ループ閉合の後
開始 ● 開始 ●
\ \
\ (ドリフトで │ (誤差が経路全体に
\ 広がる) │ 均等に分散)
● ← 実は同じ場所 ● ← 整合された
3D表現 — 点群・ボクセル・occupancy
センサーが与えた深度をロボットが扱うには、適切なデータ構造で表現する必要があります。
点群
3D空間に散りばめられた点の集合です。各点は座標 (x, y, z) と、時には色や反射強度を持ちます。最も直接的な表現ですが、点の数が膨大で、順序がなく(unordered)、密度が不均一という特性があります。
import numpy as np
# 深度画像を点群へ逆投影
def depth_to_pointcloud(depth, fx, fy, cx, cy):
h, w = depth.shape
u, v = np.meshgrid(np.arange(w), np.arange(h))
z = depth
x = (u - cx) * z / fx
y = (v - cy) * z / fy
points = np.stack([x, y, z], axis=-1).reshape(-1, 3)
# 深度が0(無効)の点を除去
valid = points[:, 2] > 0
return points[valid]
ボクセル
3D空間を均一な立方体の格子に分けた表現です。2Dピクセルの3D版と考えればよいでしょう。規則的で扱いやすい一方、解像度を上げるとメモリが3乗で増えます。この問題を和らげるため、オクトリー(octree)ベースのOctoMapのような階層構造が使われます。
Occupancy grid
各格子セルが「占有/空き/未知」の確率を持つ地図です。ロボットのナビゲーションで特に有用で、どこを通れるかを直接表現するからです。
Occupancy grid(2Dスライス例)
. = 自由空間 # = 障害物 ? = 未探索
? ? ? ? ? ? ?
? . . . # # ?
? . R . # . ? R = ロボットの現在位置
? . . . . . ?
? # # . . . ?
? ? ? ? ? ? ?
| 表現 | 長所 | 限界 | 主な用途 |
|---|---|---|---|
| 点群 | 原本に忠実・精密 | 無秩序・大容量 | 位置合わせ・再構成 |
| ボクセル | 規則的・処理が容易 | メモリが3乗で増加 | 3D深層学習・衝突検査 |
| Occupancy | 走行可能性を直接表現 | 細かい形状を失う | 経路計画・ナビゲーション |
6D姿勢推定
物体をつかむロボット(マニピュレーション)にとって、物体が画面のどこにあるか(2D)だけでは足りません。3次元位置 (x, y, z) と3軸の向き(roll, pitch, yaw)まで知って初めて、手を正確に伸ばせます。この6つの自由度を合わせて6D姿勢(6-DoF pose)と呼びます。
6D姿勢 = 位置3 + 回転3
z
│ ↺ yaw
│ /
│ /
└──────── y
/│
/ │ ↺ pitch
x │
↺ roll
つかむには: どこに(位置) + どの向きで(回転) 手を置くかを決める
アプローチは大きく二つに分かれます。既知の3Dモデルと観測を位置合わせする方法(例: ICP、Iterative Closest Point)と、深層学習で画像から姿勢を直接回帰する方法です。実務では深層学習で初期推定を得てICPで精密補正する組み合わせがよく使われます。
深層学習による認識 — 検出と分割
深度と形状を知ったら、次は「それが何か」を知る意味的認識です。
- 物体検出(detection): 画像から物体を見つけ、境界ボックスとクラスを予測します。
- 意味的分割(semantic segmentation): ピクセルごとにクラスを付与します(このピクセルは床、あのピクセルは人)。
- インスタンス分割(instance segmentation): 同じクラスでも個別の物体を区別します(人1、人2)。
入力画像 検出 意味的分割
┌──────────┐ ┌──────────┐ ┌──────────┐
│ 人 コップ │ ──▶ │ [人] ボックス│ ──▶ │ ピクセル別 │
│ 机 │ │ [コップ]箱 │ │ 色分け │
└──────────┘ └──────────┘ └──────────┘
ロボットではここに3D情報を結合します。たとえば「コップ」と分割されたピクセル領域を深度フレームと重ねると、コップの3D位置が得られ、そのまま把持計画へ渡せます。最近ではプロンプトベースの分割(例: Segment Anything系)のように、事前学習された汎用モデルをロボット認識の前段として使う流れもあります。ただし実環境への配備では、照明・遮蔽・ドメイン差による性能低下を常に検証する必要があります。
NeRFと3Dガウシアン・スプラッティング
最近、3D再構成の分野を揺さぶった二つの技法があります。
- NeRF(Neural Radiance Fields): 複数視点の画像から場面を一つのニューラルネットワーク関数として学習します。空間中の任意の点と視線方向を入れると、その地点の色と密度を出力し、これを光線に沿って積分して新しい視点の画像をレンダリングします。非常に写実的な再構成が可能ですが、学習とレンダリングが重いという限界がありました。
- 3Dガウシアン・スプラッティング(3D Gaussian Splatting): 場面を多数の3次元ガウシアン(位置・大きさ・向き・色・不透明度を持つ滑らかな塊)の集合として表現します。これを画面に投影して(splatting)レンダリングしますが、NeRFよりはるかに速く、リアルタイムレンダリングが可能で大きな注目を集めました。
従来表現 NeRF 3Dガウシアン・スプラッティング
点/ボクセル 連続関数(MLP) 3Dガウシアン集合
┌─┬─┬─┐ f(x, y, z, θ, φ) ○ ◦ ● ◦ ○ (各塊が
├─┼─┼─┤ ──▶ = (色, 密度) ──▶ ● ◦ ○ ● ◦ 位置・色・不透明度)
└─┴─┴─┘ 光線積分でレンダー 投影・合成でレンダー(速い)
ロボットの観点からこれらは魅力的です。高品質な3D地図を作ったり、シミュレーション用の写実的な場面を再構成したり、ロボットがまだ見ていない視点を予測したりに使えます。実際、SLAMにガウシアン表現を接ぎ木しようとする研究が活発です。ただしこうした表現はたいてい静的な場面を仮定しており、動く物体やリアルタイムの大規模環境にはまだ実用的な制約があります。どのシステムにいつ導入するかは慎重に判断すべきです。
座標系と変換 — 認識の隠れた背骨
3D認識で最も多くのバグが潜む場所は、意外にも座標系(coordinate frame)です。ロボットシステムには複数の座標系が共存します。カメラが見る世界はカメラ座標系、ロボットの指先はエンドエフェクタ座標系、ロボットの胴体はベース座標系、そして部屋全体をまたぐワールド座標系があります。ある物体を「つかむ」には、カメラ座標系で認識した物体位置を、ロボットが腕を動かすベース座標系へ変換する必要があります。
座標系変換のチェーン(物体をカメラからロボットベースへ移す)
ワールド座標系(部屋)
│ T_world_base(ロボットが部屋のどこに)
▼
ベース座標系(ロボット胴体) ◀──── ここで腕の計画を立てる
│ T_base_cam(カメラがロボットのどこに付いているか)
▼
カメラ座標系 ────▶ 物体位置(ここで認識される)
物体_ベース = T_base_cam x 物体_カメラ
こうした変換は4x4同次変換行列(homogeneous transformation)で表現されます。回転と並進を一つの行列に収め、行列の掛け算だけで座標系を行き来できるようにしたものです。ロボットミドルウェアは、これらの変換の関係をツリー構造で管理する道具(例: ROSのtf)を提供します。
変換ツリー(tf tree)
world
└── base_link
├── camera_link
│ └── camera_optical_frame
└── arm_base
└── ... ── end_effector
各エッジが一つの変換。任意の二つのフレーム間の変換は、
ツリーの経路に沿って行列を掛けて得る。
変換でよくある間違いは、方向(どのフレームからどのフレームへ)を取り違えることと、回転表現(オイラー角、クォータニオン、回転行列)の間の変換で軸の順序を間違えることです。クォータニオンはジンバルロック(gimbal lock)がなくロボットで広く使われますが、人が直感的に読むのは難しいです。
センサーフュージョンの深掘り
先に、複数のセンサーが互いの弱点を補うと述べました。このフュージョンには大きく二つの層があります。
- 低レベルフュージョン: 生の測定値を直接結合します。たとえばカメラ画像とLiDAR点を同じ座標系に整列し、各ピクセルに深度を与えたり、各3D点に色を付けたりします。
- 高レベルフュージョン: 各センサーが独立に推定した結果(例: 物体位置)を確率的に結合します。
時間軸のフュージョンで核心となる道具は状態推定フィルタです。代表的にカルマンフィルタ(Kalman filter)とその拡張が、ロボットの位置・速度推定に広く使われます。
カルマンフィルタの二段階(反復)
予測(predict) 更新(update)
┌──────────────┐ ┌──────────────┐
│ 運動モデルで │ │ 新しい観測で │
│ 状態を前へ │──────────▶│ 予測を補正 │
│ 転がす(不確実↑)│ │ (不確実 ↓) │
└──────────────┘ └──────────────┘
▲ │
└──────── 次の周期 ◀─────────┘
核心: 予測の不確実性と観測の不確実性の加重平均 →
より信頼できる側に重みを置く
IMU(慣性計測装置)は加速度と角速度を非常に速く与えますが、時間が経つと漂流します。一方、カメラ・LiDARは遅いですが絶対的な基準を与えます。この二つをフィルタで織り合わせると(例: 視覚慣性オドメトリ、VIO)、速く安定した姿勢推定が得られます。これがSLAMのフロントエンドの強力な土台になります。
実戦パイプラインのスケッチ
これまでの断片を一つの流れにつなぐと、移動型ロボットの典型的な認識・行動ループは次のようになります。
認識 → 計画 → 制御 ループ
[センサー] ──▶ [前処理] ──▶ [SLAM/状態推定] ──▶ [意味認識]
カメラ ノイズ除去 位置+地図 検出/分割
LiDAR 位置合わせ アフォーダンス
IMU │
▼
[制御] ◀── [モーション計画] ◀── [作業計画] ◀── [場面理解]
関節命令 衝突のない経路 何をするか どこに何が
│
▼
ロボット動作 ──▶ 世界の変化 ──▶ (ループ反復)
各ブロックは、前の節で扱った技法で埋められます。重要なのは、このループが実時間の予算内で回らねばならないことです。認識に200ミリ秒かかり、ロボットが毎秒1メートルで動くと、判断する頃には世界はすでに20センチ移動した後です。そのため実務では、認識を複数の周期に分けて(速い安全反応ループ + 遅い意味理解ループ)回す設計がよくあります。
# 概念的な認識・行動ループ(擬似コードに近い例)
def perception_action_loop(robot):
while robot.is_active():
frame = robot.sensors.grab_synced() # 時間同期されたセンサー取得
cloud = preprocess(frame) # ノイズ除去、座標整列
pose, local_map = slam.update(cloud) # 自己位置推定 + 地図更新
objects = detector.detect(frame.rgb) # 深層学習の検出
objects = lift_to_3d(objects, frame.depth) # 2D検出を3Dへ
goal = task_planner.decide(objects, pose) # 何をするか
path = motion_planner.plan(goal, local_map) # 衝突のない経路
robot.controller.follow(path) # 力/速度の制限を順守
このコードは実際のシステムの骨格を単純化したものです。実戦では各段階が別プロセスに分かれてメッセージで通信し、失敗時の安全停止のような例外処理が緻密に入ります。
認識性能をどう評価するか
「認識がうまくいく」という言葉は測定可能でなければなりません。課題ごとに異なる指標が使われます。
| 課題 | 代表的な指標 | 意味 |
|---|---|---|
| 物体検出 | mAP(平均適合率) | 複数の閾値での適合率・再現率の均衡 |
| 意味的分割 | mIoU(平均交差/和集合) | 予測領域と正解領域の重なり |
| 深度推定 | RMSE、絶対相対誤差 | 推定深度と実際の深度の差 |
| SLAM | ATE(絶対軌道誤差) | 推定経路と実際の経路のずれ |
| 6D姿勢 | ADD系 | 推定姿勢で置いたモデル点と正解点の距離 |
指標を見るときの注意点があります。ベンチマークの高得点が現場性能を保証しません。データセットが偏っているかもしれず、ロボットが実際に出会う照明・物体分布がベンチマークと異なるかもしれません。だから成熟したチームは、ベンチマークの得点とともに、自分のロボットの実際の任務での成功率を最終指標にします。
単眼深度推定 — 片目で深度を見る
ステレオやLiDARなしに、カメラ1台で深度を推定する方法もあります。単眼深度推定(monocular depth estimation)は、深層学習で画像から深度を直接予測します。物理的な原理の代わりに、膨大なデータから学んだ事前知識を使います。たとえば「遠くの物体は小さく見え、地平線側は遠く、見慣れた物体(ドア・椅子)の大きさはおおよそ決まっている」という手がかりをニューラルネットが内在化します。
単眼深度推定の原理(直感)
一枚の画像 ──▶ ニューラルネット ──▶ ピクセル別の深度マップ
│
├─ 手がかり1: 物体の大きさ(見慣れた物体を参照)
├─ 手がかり2: 遠近・消失点
├─ 手がかり3: ぼけ・遮蔽の関係
└─ 手がかり4: 質感の密度変化
注意: 絶対スケールがあいまいになり得る(相対深度だけ確実な場合が多い)
単眼方式の限界はスケールのあいまいさです。写真だけからは「小さな物体が近くに」あるのか「大きな物体が遠くに」あるのか、原理的に区別しにくいのです。だからロボットでは、単眼推定を他のセンサーと結合したり、ロボットの移動で生じる視点変化(motion parallax)を併用したりすることが多いです。
動的環境SLAMの概要
先に、ほとんどの古典的SLAMが静的な世界を仮定すると述べました。しかし現実には歩く人、転がるカート、開くドアがあります。こうした動的要素はSLAMを二つの点で乱します。第一に、動く物体の特徴点を地図に誤って入れ、地図が汚染されます。第二に、動く背景を基準に自己位置を誤って推定します。
動的SLAMのアプローチ
1) 検出: 何が動いているか?
- 意味分割で「人・車両」のような動的候補を識別
- 幾何的一貫性の違反で外れ値を検出
│
▼
2) 分離: 静的背景 vs 動的物体
- 静的な特徴だけをSLAMに使用(安定した地図)
- 動的物体は別に追跡するか無視
│
▼
3) (任意) 追跡: 動的物体を別に予測
- 人がどこへ行くかを予測し回避に活用
核心となる考えは、動的要素を外れ値として除いて静的な地図を守りつつ、安全のためにその動的要素を完全には無視しないという均衡です。静的な背景で地図を作り、人のような動く対象は回避計画へ渡す、という具合です。
実務で出会う落とし穴
- キャリブレーション: センサーの内部・外部パラメータが少しずれるだけで3D復元が大きく狂います。カメラ-LiDAR間の外部キャリブレーションは特に厄介です。
- 時間同期: 複数センサーのタイムスタンプがずれると、ロボットが速く動くときにデータが別々の瞬間を指し、位置合わせが崩れます。
- 動的環境: ほとんどの古典的SLAMは静的な世界を仮定します。人や車両が動くと、これを外れ値として除外する必要があります。
- リアルタイム制約: 認識はロボットの制御周期内に終わらねばなりません。精度と遅延のバランスが常に鍵です。
- ドメイン差: 実験室でうまくいった認識モデルが、照明・素材・背景の異なる現場で崩れることはよくあります。配備前に現場データで検証する習慣が大切です。
おわりに
ロボット認識は「失われた3次元を取り戻す」ことから始まり、その空間を地図に編み(SLAM)、適切に表現し(点群・ボクセル・occupancy)、物体の姿勢を推定し(6D pose)、意味を与える(検出・分割)複数の層から成ります。NeRFや3Dガウシアンのような新しい表現は、この絵を広げ続けています。
肝心なのは、どれか一つの技法が万能ではないという点です。センサーごとに長所と短所が異なり、表現ごとに用途が異なります。良いロボット認識システムは、これらを問題に合わせて組み合わせ、実環境の落とし穴を着実に検証しながら磨いていきます。次の記事では、こうして世界を見たロボットがどう安全に行動できるかを扱います。