状態:ドラフト(2026-10-18)、実測。 数値はノートPC(Apple M1 Max、macOS 26.5)のPython 3.9.6・numpy 2.0.2で直接計算しました。 時間は7回繰り返した際の中央値(初回は除外)です。生データとコード:作業ディレクトリ
measure/robot-arm-basics/のm02_frames.py,m02b_float32.py,out/m02_frames.json。実際のカメラで補正を行った実験は行っていません(未実測)。参考文献は2026-10-18に閲覧しました。 作成規則:実測データを基本とし、測定できなかったものは「未実測」、確認できなかったものは「未確認」と表記します。
目次
1. 直感:同じボール、4つの説明
ボールが1つ浮いています。カメラは「自分のレンズの前3.1 m、少し左下」と言います。卓球台を基準にすると「中央からロボット側に0.8 m、 高さ25 cm」です。ロボットベースを基準にすると「前方0.9 m、上方0.4 m」であり、ラケットを基準にすると「面から3 cm前方」です。すべて同じ点です。
ロボットのソフトウェアにおけるバグの多くは、これら4つの説明を混同して使用することから生じます。そのため、座標系(frame)ごとに名前を付け、点を移動させる際には必ず変換を経由させるようにします。本シリーズでは次の4つの名称を使用します。
| 名前 | 原点と軸 | 誰が決めるか |
|---|---|---|
| {C} カメラ | レンズ中心、zが視線方向 | カメラの設置 |
| {T} 卓球台 | 上面中央、xが幅・yが長さ・zが上向き | ITTF規格とDeepMindデータセットの慣例 |
| {B} ロボットベース | 第1関節軸の上 | ロボットの設置 |
| {E} ラケット | ラケット面中心、zが面の法線 | 順運動学(第3部) |
2. 図:座標系のチェーン
- 矢印1つが4×4の変換行列1つです。カメラ→卓球台はキャリブレーションで1度求め、卓球台→ロボットベースは設置時に測定し、 ロボットベース→ラケットは関節角度が変わるたびに順運動学で再計算します。
- 表記 ᴮT_T は「{T}で表現されたものを{B}で表現するように変更する変換」です。下付き文字と次の変換の上付き文字が一致していなければ掛け合わせることができないため、この表記を守れば掛け算の順番を間違えにくくなります。
3. 計算
3.1 回転行列
回転行列(rotation matrix)Rは3×3であり、 列の1つ1つが回転した座標系の軸を元の座標系から見た単位ベクトルです。そのため常に次の2つの性質を満たします。
Rᵀ R = I (열들이 서로 수직이고 길이가 1)
det R = +1 (오른손 좌표계를 유지, -1 이면 거울상)
z軸を中心にθだけ回す回転は次のようになります。2つの回転をつなげるときは行列を掛け、元に戻すときは転置(transpose)するだけです。逆行列を個別に計算する必要はありません。
Rz(θ) = [ cos θ −sin θ 0 ]
[ sin θ cos θ 0 ]
[ 0 0 1 ]
3.2 同次変換
回転と平行移動を一度に扱うために4×4行列を使用します。これを同次変換(homogeneous transformation)と呼びます。点には第4成分として1を付加します。
T = [ R p ] 점 변환: [x'] = T [x]
[ 0 1 ] [1 ] [1]
역변환: T⁻¹ = [ Rᵀ −Rᵀ p ]
[ 0 1 ]
逆変換も通常の逆行列ではなく上記式で計算します。数値的に一段と安全かつ高速です。
3.3 オイラー角
人間が読みやすい姿勢表現はオイラー角(Euler angles)です。ロボットや航空分野で一般的なZYX順(ヨー yaw、ピッチ pitch、ロール roll)は
R = Rz(요)·Ry(피치)·Rx(롤) です。回転行列から逆向きに取り出すときは次の式を使用します(r_ijはi行j列)。
피치 = asin(−r31)
요 = atan2(r21, r11)
롤 = atan2(r32, r33)
ピッチが±90°になると、r11、r21、r32、r33がすべて0になり、ヨーとロールを個別に決定できなくなります。2つの回転軸が重なって自由度が1つ失われた状態であり、これをジンバルロック(gimbal lock)と呼びます。正確に90°のときのみ問題になるのではなく、 その近傍でノイズが大きく膨れ上がるという点が実際にはより重要です。4.3節で測定します。
3.4 クォータニオン
単位クォータニオン(unit quaternion)q = (w, x, y, z)は、回転軸kと回転角θにより q = (cos θ/2, k·sin θ/2) です。4つの数値で回転を表し、ジンバルロックがなく、2つの回転の間を滑らかに補間しやすいという利点があります。その代わり、qと−qが同じ回転を指すという点(二重被覆)を常に覚えておく必要があります。姿勢誤差を計算する際に符号を合わせないと、近い2つの姿勢が360°近く離れているという結果になってしまいます。
本シリーズのコードにおいて、クォータニオンの掛け算は純粋なPythonの関数1つで実装されています。
def qmul(a, b):
aw, ax, ay, az = a
bw, bx, by, bz = b
return (aw*bw - ax*bx - ay*by - az*bz,
aw*bx + ax*bw + ay*bz - az*by,
aw*by - ax*bz + ay*bw + az*bx,
aw*bz + ax*by - ay*bx + az*bw)
3.5 カメラから卓球台へ:マーカー点からの姿勢推定
2台のカメラで三角測量を行うと、カメラ座標系{C}における3次元点が得られます。卓球台のコーナーのように卓球台座標系{T}が既知である点N個をカメラでも測定すれば、2つの点集合を最もよく重ね合わせるRとpを求めることができます。2つの集合の中心を引いた後に共分散行列を特異値分解(SVD)する方法であり、一般にKabschアルゴリズムと呼ばれます。
def kabsch(P, Q): # Q ≈ R·P + t 가 되는 R, t (P, Q 는 N×3)
pc, qc = P.mean(0), Q.mean(0)
U, _, Vt = np.linalg.svd((P - pc).T @ (Q - qc))
d = np.sign(np.linalg.det(Vt.T @ U.T)) # 거울상(det = -1) 방지
R = Vt.T @ np.diag([1, 1, d]) @ U.T
return R, qc - R @ pc
d で符号を修正する行が抜けていると、点がほぼ同一平面上にある場合(卓球台の上面がまさにそうです)に鏡像行列が生成されてしまう可能性があります。
4. 実測
4.1 回転を長く掛け続けると直交性が崩れるのか
ジャイロの積分のように小さな回転(0.06〜0.6°)を継続的に掛け合わせていき、RᵀRが単位行列からどれほど乖離するかを測定しました。同じ回転の列をクォータニオンでも掛け合わせました。
| 掛け合わせた回数 | float64 行列 ‖RᵀR − I‖ | float64 クォータニオン |‖q‖ − 1| | float32 行列 | float32 クォータニオン |
|---|---|---|---|---|
| 1,000 | - | - | 7.8×10⁻⁶ | 2.8×10⁻⁶ |
| 10,000 | - | - | 2.9×10⁻⁵ | 5.6×10⁻⁶ |
| 100,000 | 4.7×10⁻¹³ | 2.5×10⁻¹⁴ | 2.6×10⁻⁴ | 8.6×10⁻⁵ |
| 1,000,000 | 4.4×10⁻¹² | 3.1×10⁻¹⁴ | (未測定) | (未測定) |
float64行列を1,000回ごとにSVDで最も近い回転行列に修正すると、100万回後でも1.2×10⁻¹⁵でした。
解釈は2つあります。第1に、 PCのfloat64ではドリフトを心配する必要がほとんどありません。 100万回は1 kHzの制御であれば17分に相当します。第2に、 float32を使用するマイクロコントローラーでは話が異なります。 10万回(1 kHzで100秒)での2.6×10⁻⁴は、軸が約0.015°ねじれた程度の大いさに相当し、増え続けます。モーターコントローラーやIMUのファームウェアのようにfloat32で回転を累積するコードは、定期的に再正規化を行う必要があります。クォータニオンは長さを1で割るだけでよいため、この作業を行列よりも簡単に行えます。
4.2 計算時間
| 演算 | 1回あたり(µs) |
|---|---|
| numpy 3×3 行列積 | 0.68 |
| numpy 4×4 行列積 | 0.66 |
| numpy 3×3 行列 × ベクトル | 0.60 |
| 純粋なPythonによるクォータニオン積 | 0.55 |
| 純粋なPythonによるクォータニオンでのベクトル回転 | 1.33 |
numpyは3×3であっても4×4であっても0.6〜0.7 µsでほぼ同等です。このサイズでは掛け算そのものよりも関数呼び出しのオーバーヘッドが大半を占めるためです。したがって、Pythonで表現を選択する際、速度は判断基準になりません。ジンバルロック、補間、正規化の容易さといった特性に基づいて選べば問題ありません。
4.3 ジンバルロック付近のノイズ増幅
ZYX姿勢を作成し、0.0057°(10⁻⁴ rad)の大きさの小さなランダム回転をノイズとして加算した後、3.3節の式を用いて角度を再抽出しました。ピッチを90°に次第に近づけながら、それぞれ2,000回ずつ試行しました。
| ピッチと90°の差 | ヨー・ロール誤差の中央値 | 95パーセンタイル |
|---|---|---|
| 30° | 0.007° | 0.011° |
| 10° | 0.017° | 0.032° |
| 3° | 0.058° | 0.10° |
| 1° | 0.17° | 0.31° |
| 0.3° | 0.54° | 1.0° |
| 0.1° | 1.6° | 3.1° |
| 0.03° | 5.4° | 10.5° |
傾きが対数軸上でほぼ1であるため、誤差はおおむね「ノイズ ÷ 90°までに残された角度(ラジアン)」の割合で増大します。卓球のラケットは面をほぼ垂直に立てたり寝かせたりするため、ピッチ90°付近を頻繁に通過します。 ラケットの姿勢をオイラー角のまま保存したりフィルタリングしたりせず、内部表現としては回転行列やクォータニオンを使用し、人間に表示するときのみオイラー角に変換するほうが安全です。
4.4 マーカー点によるカメラ姿勢の推定
カメラをロボット側エンドラインの後方2 m、高さ1.6 mの位置に設置し、卓球台の中央を見下ろすように配置しました。卓球台の4つのコーナー、ネットポストの上の2点、センターラインの両端を基本マーカーとして使用し、さらに多くの点が必要な場合は卓球台上の任意の点を追加しました。カメラ座標系における各点にガウスノイズσを加算し、3.5節の関数を用いて姿勢を求めました。各条件で500回試行した際の中央値です。
| 点数 | σ | 回転誤差 | カメラ位置誤差 | 反対側エンドライン上の点の誤差(中央値/95パーセンタイル) |
|---|---|---|---|---|
| 4 | 2 mm | 0.073° | 3.7 mm | 1.9 / 3.6 mm |
| 4 | 5 mm | 0.20° | 9.4 mm | 4.9 / 8.5 mm |
| 8 | 2 mm | 0.061° | 2.9 mm | 1.4 / 2.6 mm |
| 8 | 5 mm | 0.15° | 7.2 mm | 3.6 / 6.9 mm |
| 8 | 10 mm | 0.31° | 15.1 mm | 7.1 / 13.7 mm |
| 32 | 5 mm | 0.10° | 4.7 mm | 2.1 / 3.8 mm |
- カメラの位置誤差は、卓球台上の点の誤差よりも大きくなります。 カメラはマーカー点の中央から3 m以上離れているため、小さな回転誤差がテコのように拡大されてしまいますが、私たちが関心を持っているのはカメラ自体ではなく卓球台上のボールの位置です。ボールはマーカー点の付近を飛び交うため、誤差は小さく抑えられます。
- 点を8個から32個に増やしても、誤差は√4 = 2倍ではなく1.7倍の減少にとどまりました。任意に追加した点が卓球台の面上にのみ存在し、コーナーのように広く分散した点に比べて情報量が少ないためだと考えられます(推定)。
- 第1部の時間予算におけるボールの位置ノイズについては第6部で扱いますが、キャリブレーションの誤差は毎フレームのノイズとは異なり、 すべてのフレームにおいて同じ方向に残る偏り(バイアス)です。カルマンフィルターはノイズを低減できますが、偏りは低減できません。
5.制限事項
- 2台のカメラによる三角測量ですでに3次元点が得られていると仮定しました。1台のカメラの画像座標から姿勢を求めるPnP問題、レンズ歪み、2台のカメラ間の時間のずれについては扱っていません(未実測)。
- ノイズは各軸に同じ大きさで加えました。実際の三角測量におけるノイズはカメラの視線方向において大幅に大きくなります(未実測)。
6. まとめ
- 座標系ごとに名前を付け、変換には「どこからどこへ」を記述します。回転の逆は転置であり、同次変換の逆は専用の式を用います。
- 内部計算は回転行列またはクォータニオンで行います。オイラー角はピッチ90°付近でノイズを数百倍に増幅させます。
- float32で回転を長時間累積する場合は正規化が必要です。float64であれば100万回後でも10⁻¹²のレベルにとどまります。
- 標識点が8個、点ノイズが5 mmの場合、卓球台上の位置誤差は数 mmになります。この誤差はすべてのフレームに同じように残る偏りであるため、フィルターで消すことはできません。
卓球の一言。 同じボールでも、誰が見るかによって座標が変わります。ラケットを立てたり寝かせたりするときは、オイラー角ではなくクォータニオンをしっかり握っておかなければなりません!
参考
すべての資料は2026-10-18に閲覧しました。
- Lynch, Park, Modern Robotics: Mechanics, Planning, and Control, Cambridge University Press 2017, 公開プレプリント(2017年5月), 第3章 剛体運動。
- ITTF, Statutes 2026, 2.1.1·2.2.2(卓球台・ネットの柱の位置)。
- Google DeepMind, competitive_robot_table_tennis, 座標系の説明(README)、CC-BY 4.0。