Coordinate Systems / 座標系¶
Note: English version follows after the Japanese section. / 日本語の後に英語版があります。
1. 概要¶
このドキュメントについて¶
StampFlyエコシステムで使用する座標系の定義と、各コンポーネント間の座標変換について説明します。
対象読者¶
- シミュレータ開発者
- ファームウェア開発者
- 可視化ツール開発者
2. 座標系の定義¶
計算用座標系: NED (North-East-Down)¶
制御計算・シミュレーションで使用する座標系です。
| 軸 | 正方向 | 説明 |
|---|---|---|
| X | 北 / 前方 | 機体の前進方向 |
| Y | 東 / 右 | 機体の右方向 |
| Z | 下 | 重力方向(地面向き) |
使用箇所: - ファームウェア(姿勢制御、位置制御) - シミュレーション(物理演算) - 制御設計(モデル、伝達関数)
シミュレーション用座標系: Genesis (Z-up)¶
Genesis物理エンジンで使用する座標系です。ロボットシミュレーションの標準的なZ-up座標系です。
| 軸 | 正方向 | 説明 |
|---|---|---|
| X | 水平 | 右方向(慣例) |
| Y | 水平 | 前方向(慣例) |
| Z | 上 | 重力の逆方向 |
使用箇所: - Genesis物理シミュレーション - 強化学習環境
重力設定: gravity=(0, 0, -9.81) (Z軸負方向)
表示用座標系: WebGL/Three.js (Y-up, Z-forward)¶
3D可視化で使用する座標系です。機体後方からのカメラ視点を基準とします。
| 軸 | 正方向 | 説明 |
|---|---|---|
| X | 左 | 機体の左方向(右手系維持のため) |
| Y | 上 | 画面上方向 |
| Z | 前 | 機体の前進方向 |
使用箇所: - STLファイル(3Dメッシュデータ) - WebGLビュワー - Three.jsベースの可視化ツール
3. 位置の座標変換¶
NED → WebGL 変換¶
シミュレーション結果を可視化する際の座標変換です。右手系を維持します。
| WebGL | = | NED | 説明 |
|---|---|---|---|
| x | = | -y | 東/右 → 左(符号反転) |
| y | = | -z | 下 → 上(符号反転) |
| z | = | +x | 北/前 → 前 |
// NED座標からWebGL座標への変換(右手系維持)
// Convert from NED to WebGL coordinates (preserves handedness)
function nedToWebGL(ned) {
return {
x: -ned.y, // East/Right → Left (sign inverted)
y: -ned.z, // Down → Up (sign inverted)
z: ned.x // North/Forward → Forward
};
}
WebGL → NED 変換¶
3Dモデルの位置をシミュレーションに取り込む際の座標変換です。
| NED | = | WebGL | 説明 |
|---|---|---|---|
| x | = | +z | 前 → 北/前 |
| y | = | -x | 左 → 東/右(符号反転) |
| z | = | -y | 上 → 下(符号反転) |
// WebGL座標からNED座標への変換
// Convert from WebGL to NED coordinates
function webglToNED(webgl) {
return {
x: webgl.z, // Forward → North
y: -webgl.x, // Left → East/Right (sign inverted)
z: -webgl.y // Up → Down (sign inverted)
};
}
変換行列¶
NED → Genesis 変換¶
制御計算結果をGenesisシミュレーションに渡す際の座標変換です。
| Genesis | = | NED | 説明 |
|---|---|---|---|
| x | = | +y | 東/右 → X |
| y | = | +x | 北/前 → Y |
| z | = | -z | 下 → 上(符号反転) |
# NED座標からGenesis座標への変換
# Convert from NED to Genesis coordinates
def ned_to_genesis(ned):
return {
'x': ned['y'], # East/Right → X
'y': ned['x'], # North/Forward → Y
'z': -ned['z'] # Down → Up (sign inverted)
}
Genesis → NED 変換¶
Genesisシミュレーション結果を制御計算に渡す際の座標変換です。
| NED | = | Genesis | 説明 |
|---|---|---|---|
| x | = | +y | Y → 北/前 |
| y | = | +x | X → 東/右 |
| z | = | -z | 上 → 下(符号反転) |
# Genesis座標からNED座標への変換
# Convert from Genesis to NED coordinates
def genesis_to_ned(genesis):
return {
'x': genesis['y'], # Y → North/Forward
'y': genesis['x'], # X → East/Right
'z': -genesis['z'] # Up → Down (sign inverted)
}
WebGL(STL) → Genesis 変換¶
STLファイル(WebGL座標)をGenesisシミュレーションに読み込む際の座標変換です。
| Genesis | = | WebGL | 説明 |
|---|---|---|---|
| x | = | -x | 左 → 右(符号反転) |
| y | = | +z | 前 → 前 |
| z | = | +y | 上 → 上 |
# WebGL(STL)座標からGenesis座標への変換
# Convert from WebGL (STL) to Genesis coordinates
def webgl_to_genesis(webgl):
return {
'x': -webgl['x'], # Left → Right (sign inverted)
'y': webgl['z'], # Forward → Forward
'z': webgl['y'] # Up → Up
}
変換行列:
Genesis → WebGL(STL) 変換¶
Genesisシミュレーション結果をWebGLで可視化する際の座標変換です。
| WebGL | = | Genesis | 説明 |
|---|---|---|---|
| x | = | -x | 右 → 左(符号反転) |
| y | = | +z | 上 → 上 |
| z | = | +y | 前 → 前 |
# Genesis座標からWebGL座標への変換
# Convert from Genesis to WebGL coordinates
def genesis_to_webgl(genesis):
return {
'x': -genesis['x'], # Right → Left (sign inverted)
'y': genesis['z'], # Up → Up
'z': genesis['y'] # Forward → Forward
}
GenesisでのSTL読み込み例¶
import genesis as gs
# STLファイルを読み込む際、Genesisが自動的に座標を解釈
# ただし、軸の対応を明示的に変換する場合:
mesh = scene.add_entity(
gs.morphs.Mesh(
file='frame.stl',
scale=0.001, # mm → m
# 必要に応じて回転でWebGL→Genesis座標変換
# euler=(90, 0, 0) などで調整
),
)
注意: Genesisは多くのSTL形式を自動認識しますが、軸の向きが期待と異なる場合はeulerパラメータで回転調整が必要です。
4. 回転の座標変換¶
回転軸の対応¶
| NED回転軸 | WebGL回転軸 | 備考 |
|---|---|---|
| X (Roll) | Z | 同じ向き |
| Y (Pitch) | -X | 向き反転 |
| Z (Yaw) | -Y | 向き反転 |
なぜPitchとYawが符号反転?¶
位置の軸対応から:
NED Y (右) → WebGL -X (左) → Pitch軸が反転
NED Z (下) → WebGL -Y (上方向の負) → Yaw軸が反転
NED: +Pitch = 機首上げ(Y軸右向き、右手法則)
WebGL: +X回転 = 機首下げ(X軸左向き、右手法則)
→ 同じ「機首上げ」には符号反転が必要
NED: +Yaw = 右旋回(Z軸下向き、右手法則)
WebGL: +Y回転 = 左旋回(Y軸上向き、右手法則)
→ 同じ「右旋回」には符号反転が必要
NED回転 → WebGL回転 変換¶
// NED姿勢からWebGL回転への変換(右手系維持)
// Convert from NED attitude to WebGL rotation (preserves handedness)
function nedRotationToWebGL(roll, pitch, yaw) {
return {
x: -pitch, // Pitch → WebGL X rotation (sign inverted)
y: -yaw, // Yaw → WebGL Y rotation (sign inverted)
z: roll // Roll → WebGL Z rotation
};
}
Three.jsでの実装¶
// 回転順序の設定(NED ZYX → WebGL YXZ)
// Set rotation order (NED ZYX → WebGL YXZ)
mesh.rotation.order = 'YXZ';
// NED姿勢を適用
// Apply NED attitude to mesh
function applyNEDAttitude(mesh, roll, pitch, yaw) {
mesh.rotation.set(
-pitch, // X: Pitch (sign inverted)
-yaw, // Y: Yaw (sign inverted)
roll // Z: Roll
);
}
検証例¶
| NED姿勢 | WebGL回転 | 見た目 |
|---|---|---|
| Roll = +30° | rotation.z = +30° | 右翼下げ |
| Pitch = +30° | rotation.x = -30° | 機首上げ |
| Yaw = +30° | rotation.y = -30° | 右旋回 |
5. 回転の表現(NED座標系)¶
オイラー角¶
| 角度 | 軸 | 正方向 | 説明 |
|---|---|---|---|
| Roll (φ) | X軸 | 右翼下げ | 横揺れ |
| Pitch (θ) | Y軸 | 機首上げ | 縦揺れ |
| Yaw (ψ) | Z軸 | 右旋回 | 偏揺れ |
回転順序¶
ZYX順(Yaw → Pitch → Roll)を使用します。
6. 設計方針¶
アセットとロジックの分離¶
┌─────────────────────┐ ┌─────────────────────┐
│ Simulation │ │ Visualization │
│ (NED座標系) │────▶│ (WebGL座標系) │
│ │ 変換 │ │
│ - 姿勢制御 │ │ - Three.js描画 │
│ - 位置制御 │ │ - WebGLレンダリング │
│ - 物理演算 │ │ │
└─────────────────────┘ └─────────────────────┘
▲
│ 変換不要
┌─────┴─────┐
│ STL Files │
│(WebGL座標)│
└───────────┘
理由¶
| 方針 | 理由 |
|---|---|
| STLはWebGL座標で保存 | 表示時に変換不要、3Dツールとの互換性 |
| 計算はNEDで実行 | 航空工学の標準、制御理論との整合性 |
| 変換は可視化レイヤで | 単一の変換ポイント、保守性向上 |
7. 関連ファイル¶
| ファイル | 説明 |
|---|---|
simulator/sandbox/coord_transformer/ |
座標変換ツール |
simulator/sandbox/webgl_viewer/ |
STLビュワー |
simulator/sandbox/genesis_sim/ |
Genesis物理シミュレータ |
simulator/assets/meshes/parts/ |
STLファイル(WebGL座標) |
8. 座標系比較サマリー¶
| システム | 右 | 上 | 前 | 重力 |
|---|---|---|---|---|
| NED | +Y | -Z | +X | +Z |
| Genesis | +X | +Z | +Y | -Z |
| WebGL | -X | +Y | +Z | -Y |
9. IMU 軸変換(BMI270 → 機体座標系)¶
BMI270 の物理軸配置¶
BMI270 は StampFly 基板上で以下のように実装されている:
| BMI270 軸 | 基板上の方向 |
|---|---|
| +X | 機体右方向 |
| +Y | 機体前方向 |
| +Z | 機体上方向 |
センサー座標系 → NED 機体座標系の変換¶
軸変換自体は、firmware/vehicle(旧 vehicle_new。2026年のpromotionで現行機に昇格、レイヤードの旧ファームは firmware/vehicle_old として凍結)では、タスク層ではなくHALドライバ本体(firmware/vehicle/components/sf_hal_bmi270/src/bmi270_wrapper.cpp)で吸収する(方針: ドライバは呼び出し側に機体軸[FRD]の量を返し、チップ軸を意識させない)。加速度はここでは単位[g]のまま軸のみ変換され、[m/s²]への換算は呼び出し側(firmware/vehicle/tasks/imu_task.cpp)で行う:
| NED機体軸 | = | BMI270軸 | 説明 |
|---|---|---|---|
| body_x(前方) | = | +sensor_y | BMI270 Y → NED X |
| body_y(右方) | = | +sensor_x | BMI270 X → NED Y |
| body_z(下方) | = | -sensor_z | BMI270 Z → NED -Z(符号反転) |
// bmi270_wrapper.cpp での変換(軸のみ。単位変換[g]→[m/s²]は呼び出し側imu_task.cppが行う)
// BMI270チップ座標系 → 機体座標系(NED/FRD) 変換
static AccelData toBodyFrame(const bmi270_accel_t& c)
{
return AccelData{c.y, c.x, -c.z};
}
static GyroData toBodyFrame(const bmi270_gyro_t& c)
{
return GyroData{c.y, c.x, -c.z};
}
注: この対応(body.x=chip.y, body.y=chip.x, body.z=-chip.z)は、飛行実績のある firmware/vehicle_old(旧 imu_task 実装)で確認済みの搭載向きをそのまま踏襲したもの。
変換行列¶
注意: この変換はハードウェア基板設計に依存する。基板リビジョンが変わった場合は実装を確認すること。
1. Overview¶
About This Document¶
This document defines the coordinate systems used in the StampFly ecosystem and explains coordinate transformations between components.
Target Audience¶
- Simulator developers
- Firmware developers
- Visualization tool developers
2. Coordinate System Definitions¶
Computation Coordinate System: NED (North-East-Down)¶
Used for control calculations and simulation.
| Axis | Positive Direction | Description |
|---|---|---|
| X | North / Forward | Aircraft forward direction |
| Y | East / Right | Aircraft right direction |
| Z | Down | Gravity direction (toward ground) |
Used in: - Firmware (attitude control, position control) - Simulation (physics computation) - Control design (models, transfer functions)
Simulation Coordinate System: Genesis (Z-up)¶
Coordinate system used by the Genesis physics engine. Standard Z-up coordinate system for robot simulation.
| Axis | Positive Direction | Description |
|---|---|---|
| X | Horizontal | Right (convention) |
| Y | Horizontal | Forward (convention) |
| Z | Up | Opposite to gravity |
Used in: - Genesis physics simulation - Reinforcement learning environments
Gravity setting: gravity=(0, 0, -9.81) (negative Z direction)
Display Coordinate System: WebGL/Three.js (Y-up, Z-forward)¶
Used for 3D visualization. Based on rear-following camera view.
| Axis | Positive Direction | Description |
|---|---|---|
| X | Left | Aircraft left (to preserve right-handedness) |
| Y | Up | Screen up |
| Z | Forward | Aircraft forward direction |
Used in: - STL files (3D mesh data) - WebGL viewer - Three.js-based visualization tools
3. Position Coordinate Transformations¶
NED → WebGL Transformation¶
Coordinate transformation for visualizing simulation results. Preserves right-handedness.
| WebGL | = | NED | Description |
|---|---|---|---|
| x | = | -y | East/Right → Left (sign inverted) |
| y | = | -z | Down → Up (sign inverted) |
| z | = | +x | North/Forward → Forward |
// Convert from NED to WebGL coordinates (preserves handedness)
function nedToWebGL(ned) {
return {
x: -ned.y, // East/Right → Left (sign inverted)
y: -ned.z, // Down → Up (sign inverted)
z: ned.x // North/Forward → Forward
};
}
WebGL → NED Transformation¶
Coordinate transformation for importing 3D model positions into simulation.
| NED | = | WebGL | Description |
|---|---|---|---|
| x | = | +z | Forward → North/Forward |
| y | = | -x | Left → East/Right (sign inverted) |
| z | = | -y | Up → Down (sign inverted) |
// Convert from WebGL to NED coordinates
function webglToNED(webgl) {
return {
x: webgl.z, // Forward → North
y: -webgl.x, // Left → East/Right (sign inverted)
z: -webgl.y // Up → Down (sign inverted)
};
}
Transformation Matrix¶
NED → Genesis Transformation¶
Coordinate transformation for passing control computation results to Genesis simulation.
| Genesis | = | NED | Description |
|---|---|---|---|
| x | = | +y | East/Right → X |
| y | = | +x | North/Forward → Y |
| z | = | -z | Down → Up (sign inverted) |
# Convert from NED to Genesis coordinates
def ned_to_genesis(ned):
return {
'x': ned['y'], # East/Right → X
'y': ned['x'], # North/Forward → Y
'z': -ned['z'] # Down → Up (sign inverted)
}
Genesis → NED Transformation¶
Coordinate transformation for passing Genesis simulation results to control computation.
| NED | = | Genesis | Description |
|---|---|---|---|
| x | = | +y | Y → North/Forward |
| y | = | +x | X → East/Right |
| z | = | -z | Up → Down (sign inverted) |
# Convert from Genesis to NED coordinates
def genesis_to_ned(genesis):
return {
'x': genesis['y'], # Y → North/Forward
'y': genesis['x'], # X → East/Right
'z': -genesis['z'] # Up → Down (sign inverted)
}
WebGL (STL) → Genesis Transformation¶
Coordinate transformation for loading STL files (WebGL coordinates) into Genesis simulation.
| Genesis | = | WebGL | Description |
|---|---|---|---|
| x | = | -x | Left → Right (sign inverted) |
| y | = | +z | Forward → Forward |
| z | = | +y | Up → Up |
# Convert from WebGL (STL) to Genesis coordinates
def webgl_to_genesis(webgl):
return {
'x': -webgl['x'], # Left → Right (sign inverted)
'y': webgl['z'], # Forward → Forward
'z': webgl['y'] # Up → Up
}
Transformation Matrix:
Genesis → WebGL (STL) Transformation¶
Coordinate transformation for visualizing Genesis simulation results in WebGL.
| WebGL | = | Genesis | Description |
|---|---|---|---|
| x | = | -x | Right → Left (sign inverted) |
| y | = | +z | Up → Up |
| z | = | +y | Forward → Forward |
# Convert from Genesis to WebGL coordinates
def genesis_to_webgl(genesis):
return {
'x': -genesis['x'], # Right → Left (sign inverted)
'y': genesis['z'], # Up → Up
'z': genesis['y'] # Forward → Forward
}
Loading STL in Genesis¶
import genesis as gs
# When loading STL files, Genesis automatically interprets coordinates
# However, to explicitly transform axes:
mesh = scene.add_entity(
gs.morphs.Mesh(
file='frame.stl',
scale=0.001, # mm → m
# Adjust with rotation for WebGL→Genesis coordinate transform if needed
# euler=(90, 0, 0) etc.
),
)
Note: Genesis auto-recognizes many STL formats, but if axis orientation differs from expectation, use the euler parameter for rotation adjustment.
4. Rotation Coordinate Transformation¶
Rotation Axis Mapping¶
| NED Rotation Axis | WebGL Rotation Axis | Note |
|---|---|---|
| X (Roll) | Z | Same direction |
| Y (Pitch) | -X | Direction inverted |
| Z (Yaw) | -Y | Direction inverted |
Why are Pitch and Yaw Sign Inverted?¶
From position axis mapping:
NED Y (Right) → WebGL -X (Left) → Pitch axis inverted
NED Z (Down) → WebGL -Y (negative Up) → Yaw axis inverted
NED: +Pitch = Nose up (Y-axis points right, right-hand rule)
WebGL: +X rotation = Nose down (X-axis points left, right-hand rule)
→ To get "nose up", sign must be inverted
NED: +Yaw = Turn right (Z-axis points down, right-hand rule)
WebGL: +Y rotation = Turn left (Y-axis points up, right-hand rule)
→ To get "turn right", sign must be inverted
NED Rotation → WebGL Rotation Transformation¶
// Convert from NED attitude to WebGL rotation (preserves handedness)
function nedRotationToWebGL(roll, pitch, yaw) {
return {
x: -pitch, // Pitch → WebGL X rotation (sign inverted)
y: -yaw, // Yaw → WebGL Y rotation (sign inverted)
z: roll // Roll → WebGL Z rotation
};
}
Three.js Implementation¶
// Set rotation order (NED ZYX → WebGL YXZ)
mesh.rotation.order = 'YXZ';
// Apply NED attitude to mesh
function applyNEDAttitude(mesh, roll, pitch, yaw) {
mesh.rotation.set(
-pitch, // X: Pitch (sign inverted)
-yaw, // Y: Yaw (sign inverted)
roll // Z: Roll
);
}
Verification Examples¶
| NED Attitude | WebGL Rotation | Appearance |
|---|---|---|
| Roll = +30° | rotation.z = +30° | Right wing down |
| Pitch = +30° | rotation.x = -30° | Nose up |
| Yaw = +30° | rotation.y = -30° | Turn right |
5. Rotation Representation (NED Coordinate System)¶
Euler Angles¶
| Angle | Axis | Positive Direction | Description |
|---|---|---|---|
| Roll (φ) | X-axis | Right wing down | Bank angle |
| Pitch (θ) | Y-axis | Nose up | Elevation angle |
| Yaw (ψ) | Z-axis | Turn right | Heading angle |
Rotation Order¶
ZYX order (Yaw → Pitch → Roll) is used.
6. Design Principles¶
Separation of Assets and Logic¶
┌─────────────────────┐ ┌─────────────────────┐
│ Simulation │ │ Visualization │
│ (NED coordinates) │────▶│ (WebGL coordinates) │
│ │transform│ │
│ - Attitude control │ │ - Three.js render │
│ - Position control │ │ - WebGL rendering │
│ - Physics engine │ │ │
└─────────────────────┘ └─────────────────────┘
▲
│ No transform needed
┌─────┴─────┐
│ STL Files │
│ (WebGL) │
└───────────┘
Rationale¶
| Principle | Reason |
|---|---|
| Store STL in WebGL coordinates | No transform for display, compatibility with 3D tools |
| Compute in NED | Aerospace standard, consistency with control theory |
| Transform in visualization layer | Single transformation point, improved maintainability |
7. Related Files¶
| File | Description |
|---|---|
simulator/sandbox/coord_transformer/ |
Coordinate transformation tool |
simulator/sandbox/webgl_viewer/ |
STL viewer |
simulator/sandbox/genesis_sim/ |
Genesis physics simulator |
simulator/assets/meshes/parts/ |
STL files (WebGL coordinates) |
8. Coordinate System Comparison Summary¶
| System | Right | Up | Forward | Gravity |
|---|---|---|---|---|
| NED | +Y | -Z | +X | +Z |
| Genesis | +X | +Z | +Y | -Z |
| WebGL | -X | +Y | +Z | -Y |
9. IMU Axis Mapping (BMI270 → Body Frame)¶
BMI270 Physical Axis Orientation¶
The BMI270 is mounted on the StampFly PCB with the following orientation:
| BMI270 Axis | PCB Direction |
|---|---|
| +X | Aircraft right |
| +Y | Aircraft forward |
| +Z | Aircraft up |
Sensor Frame → NED Body Frame Transformation¶
In firmware/vehicle (formerly vehicle_new; promoted to the primary firmware in 2026, with the earlier layered firmware frozen at firmware/vehicle_old), the axis swap itself is absorbed inside the HAL driver rather than the task layer: firmware/vehicle/components/sf_hal_bmi270/src/bmi270_wrapper.cpp (policy: drivers return body-frame [FRD] quantities to callers, never chip axes). Acceleration stays in [g] here; conversion to [m/s²] happens separately in the caller (firmware/vehicle/tasks/imu_task.cpp):
| NED Body Axis | = | BMI270 Axis | Description |
|---|---|---|---|
| body_x (forward) | = | +sensor_y | BMI270 Y → NED X |
| body_y (right) | = | +sensor_x | BMI270 X → NED Y |
| body_z (down) | = | -sensor_z | BMI270 Z → NED -Z (sign inverted) |
// Transformation in bmi270_wrapper.cpp (axis only; unit conversion [g]->[m/s^2]
// happens in the caller, imu_task.cpp)
// BMI270 chip coordinate system -> body coordinate system (NED/FRD)
static AccelData toBodyFrame(const bmi270_accel_t& c)
{
return AccelData{c.y, c.x, -c.z};
}
static GyroData toBodyFrame(const bmi270_gyro_t& c)
{
return GyroData{c.y, c.x, -c.z};
}
Note: This mapping (body.x=chip.y, body.y=chip.x, body.z=-chip.z) simply carries over the mounting orientation verified on the flight-proven firmware/vehicle_old (the earlier imu_task implementation).
Transformation Matrix¶
| 0 1 0 |
T_BMI270→NED = | 1 0 0 | det(T) = -1 → handedness changes, hence sign inversion needed
| 0 0 -1 |
Note: This transformation depends on the hardware PCB layout. Verify the implementation if the board revision changes.