StampFly Simulator¶
Note: English version follows after the Japanese section. / 日本語の後に英語版があります。
StampFly用のPythonベースフライトシミュレータ。物理エンジン、センサモデル、制御システム、HILSテスト機能を備えた教育・研究プラットフォーム。
シミュレータの選択¶
| シミュレータ | 特徴 | 用途 |
|---|---|---|
| VPython版(vpython/) | 軽量、ブラウザ3D表示、センサモデル充実 | 制御学習、SILS/HILS |
| Genesis版(genesis/) | 高精度物理エンジン、2000Hz物理演算、物理量ベース制御 | 高精度シミュレーション |
1. 概要¶
このシミュレータについて(VPython版)¶
StampFly Simulatorは、マイクロドローン「StampFly」の動力学をPythonでシミュレートするツールです。実機ファームウェア(firmware/vehicle/)と互換性のある制御アルゴリズムを実装しており、以下の用途に使用できます:
- 制御アルゴリズムの開発・検証: 安全な環境でPIDゲインの調整やフライトモードの実装
- センサフュージョンの研究: リアルなノイズモデルを持つセンサシミュレーション
- 教育: ドローン動力学と制御理論の可視化学習
- HILSテスト: 実機ファームウェアとシミュレータの連携テスト
機体構成¶
Front
FL (M4) FR (M1)
╲ ▲ ╱
╲ │ ╱ CW: M2, M4
╲ │ ╱ CCW: M1, M3
╲│╱
╳ ← Center of Mass
╱│╲
╱ │ ╲
╱ │ ╲
╱ │ ╲
RL (M3) RR (M2)
Rear
機体パラメータ¶
| パラメータ | 値 | 単位 |
|---|---|---|
| 質量 | 35 | g |
| 慣性モーメント Ixx | 9.16e-6 | kg·m² |
| 慣性モーメント Iyy | 13.3e-6 | kg·m² |
| 慣性モーメント Izz | 20.4e-6 | kg·m² |
| バッテリー電圧 | 3.7 | V |
2. セットアップ¶
必要環境¶
- Python 3.10〜3.12(推奨3.12)
- pip(パッケージマネージャ)
インストール¶
# リポジトリのルートディレクトリで実行
cd stampfly_ecosystem/simulator
# 依存パッケージのインストール
pip install -r requirements.txt
依存パッケージ¶
| パッケージ | 用途 | 必須 |
|---|---|---|
| numpy | 数値計算 | ○ |
| matplotlib | グラフ描画 | ○ |
| vpython | 3D可視化 | ○ |
| numpy-stl | STLモデル読み込み | ○ |
| Pillow | 画像処理 | ○ |
| opencv-python | 画像処理(オプティカルフロー) | ○ |
| hid | ジョイスティック入力 | △ |
| pyserial | HILS通信 | △ |
注意: hidパッケージはシステムレベルのHIDライブラリが必要です。
3. ディレクトリ構成¶
simulator/
├── README.md # 本ドキュメント
├── requirements.txt # 依存パッケージ
│
├── vpython/ # VPython版シミュレータ
│ ├── core/ # 物理エンジン
│ │ ├── physics.py # 剛体力学(RigidBody)
│ │ ├── dynamics.py # マルチコプタ動力学
│ │ └── motors.py # モーター・プロペラモデル
│ ├── sensors/ # センサモデル
│ │ ├── imu.py # BMI270 6軸IMU
│ │ ├── barometer.py # BMP280 気圧センサ
│ │ └── ... # 他センサ
│ ├── control/ # 制御システム
│ │ ├── pid.py # 不完全微分フィルタ付きPID
│ │ └── ... # 制御器
│ ├── interfaces/ # 外部インターフェース
│ ├── visualization/ # VPython 3Dレンダラ
│ └── scripts/ # 実行スクリプト
│ └── run_sim.py # メインシミュレータ
│
├── genesis/ # Genesis版シミュレータ
│ ├── scripts/ # 実行スクリプト
│ │ └── run_genesis_sim.py
│ ├── motor_model.py # モーターモデル
│ └── control_allocation.py # 制御配分
│
├── shared/ # 共有リソース
│ ├── assets/ # 3Dモデル・テクスチャ
│ ├── configs/ # 設定ファイル
│ └── scenarios/ # シナリオ定義
│
├── sandbox/ # 実験用ツール
├── tools/ # ユーティリティ
├── tests/ # テスト
└── output/ # 出力ファイル
4. 基本的な使い方¶
クイックスタート(初めての方へ)¶
コントローラがあれば、すぐにドローン操縦を体験できます!
Step 1: コントローラの準備¶
- AtomS3 + Atom JoyStickにファームウェアを書き込む(controller/README.md参照)
- コントローラの電源を入れる
- 画面(ボタン)を押してメニューを開き、右スティックで「Comm: ESP-NOW」の行を選ぶ → 右ボタンを 2 回押して「Comm: USB HID」にする(自動で再起動)
- PCにUSB接続(ゲームパッドとして認識される)
Step 2: シミュレータの起動¶
# sf CLIを使用(推奨)
sf sim run vpython
# または直接起動
cd stampfly_ecosystem/simulator/vpython/scripts
python run_sim.py
ブラウザが開き、3Dビューが表示されます。スティックを操作してドローンを飛ばしましょう!
sf CLIコマンド¶
# VPythonシミュレータを起動(デフォルト)
sf sim run
# Genesisシミュレータを起動
sf sim run genesis
# 利用可能なシミュレータを一覧
sf sim list
# ヘッドレスモード(テスト用)
sf sim headless -d 30
直接起動オプション¶
# 基本起動(ボクセルワールド、ランダム地形)
python vpython/scripts/run_sim.py
# リングワールド(円形コース)で起動
python vpython/scripts/run_sim.py --world ringworld
# シード値を指定して同じ地形を再現
python vpython/scripts/run_sim.py --seed 12345
| オプション | 短縮形 | 説明 | デフォルト |
|---|---|---|---|
--world |
-w |
ワールドタイプ(voxel/ringworld) | voxel |
--seed |
-s |
地形生成シード値(省略時ランダム) | なし |
ワールドタイプ¶
| タイプ | 説明 |
|---|---|
| voxel | ボクセルベースのランダム地形。毎回異なる地形で練習できます |
| ringworld | 円形のレースコース。周回飛行の練習に最適 |
ジョイスティック操作¶
AtomS3 + Atom JoyStickをUSB HIDモードで使用:
| 入力 | 機能 | Mode 2 | Mode 3 |
|---|---|---|---|
| スロットル | 上昇/下降 | 左Y | 右Y |
| ロール | 左右移動 | 右X | 左X |
| ピッチ | 前後移動 | 右Y | 左Y |
| ヨー | 旋回 | 左X | 右X |
ヒント: スティックがドリフトする場合は、コントローラのメニューから「Calibration」を実行してください。デッドバンドは「Deadband」から0-5%で調整できます。
コード例:基本的な物理シミュレーション¶
import numpy as np
from simulator.core import multicopter
# 機体の作成
mass = 0.037 # 37g
inertia = [
[9.16e-6, 0.0, 0.0],
[0.0, 13.3e-6, 0.0],
[0.0, 0.0, 20.4e-6]
]
drone = multicopter(mass=mass, inersia=inertia)
# 初期状態の設定
drone.set_pqr([[0.0], [0.0], [0.0]]) # 角速度 [rad/s]
drone.set_uvw([[0.0], [0.0], [0.0]]) # 速度 [m/s]
# ホバリング電圧の計算
weight = mass * 9.81
hover_voltage = drone.motor_prop[0].equilibrium_voltage(weight / 4)
# シミュレーションループ
dt = 0.001 # 1ms
for _ in range(1000):
voltage = [hover_voltage] * 4
drone.step(voltage, dt)
# 状態の取得
position = drone.body.position
euler = drone.body.euler
print(f"Position: {position.T}, Euler: {euler.T}")
コード例:センサシミュレーション¶
import numpy as np
from simulator.sensors import IMU, Barometer, Magnetometer
# センサの作成
imu = IMU(sample_rate=400) # 400Hz
baro = Barometer(sample_rate=50) # 50Hz
mag = Magnetometer(sample_rate=100) # 100Hz
# 真値からセンサ出力を生成
true_accel = np.array([0.0, 0.0, -9.81]) # m/s²
true_gyro = np.array([0.0, 0.0, 0.0]) # rad/s
true_altitude = 1.5 # m
# IMU出力(ノイズ付き)
gyro_meas, accel_meas = imu.update(true_gyro, true_accel, dt=0.0025)
# 気圧計出力
pressure, temperature = baro.update(true_altitude, dt=0.02)
# 地磁気センサ出力
mag_field = mag.update(attitude=np.array([0, 0, 0]), dt=0.01)
コード例:制御システム¶
import numpy as np
from simulator.control import RateController, MotorMixer
# 制御器の作成(ファームウェア互換ゲイン)
rate_ctrl = RateController()
mixer = MotorMixer()
# スティック入力からモーター出力を計算
gyro = np.array([0.1, -0.05, 0.02]) # 現在の角速度
throttle = 0.5
roll_out, pitch_out, yaw_out = rate_ctrl.update_from_stick(
stick_roll=0.3,
stick_pitch=-0.2,
stick_yaw=0.1,
gyro=gyro,
dt=0.0025
)
motors = mixer.mix(throttle, roll_out, pitch_out, yaw_out)
print(f"Motor outputs: {motors}") # [0.0, 1.0] の範囲
5. 制御モード¶
Rate(Acro)モード¶
角速度を直接制御するモード。スティック入力が目標角速度にマッピングされます。
from simulator.control import RateController
ctrl = RateController()
# スティック入力 [-1, 1] → 目標レート
# max_rate: Roll=220°/s, Pitch=220°/s, Yaw=200°/s
roll_out, pitch_out, yaw_out = ctrl.update_from_stick(
stick_roll=0.5, # 110°/s 目標
stick_pitch=0.0,
stick_yaw=0.0,
gyro=current_gyro,
dt=0.0025
)
Angle(Stabilize)モード¶
姿勢角を制御するモード。セルフレベリング機能付き。
from simulator.control import AttitudeController
ctrl = AttitudeController()
# スティック入力 [-1, 1] → 目標角度
# max_angle: Roll=30°, Pitch=30°
roll_out, pitch_out, yaw_out = ctrl.update(
stick_roll=0.5, # 15° 目標
stick_pitch=0.0,
stick_yaw=0.0,
attitude=current_attitude, # [roll, pitch, yaw]
gyro=current_gyro,
dt=0.0025
)
Altitude Holdモード¶
高度を維持するモード。
from simulator.control import AltitudeController
ctrl = AltitudeController(hover_throttle=0.5)
# 目標高度の設定
ctrl.set_altitude(1.5) # 1.5m
# 更新
throttle = ctrl.update(
current_altitude=1.2,
vertical_velocity=0.1,
stick_throttle=0.5,
dt=0.02
)
6. SILS/HILSテスト¶
SILS(Software-in-the-Loop)¶
Pythonで実装した制御器をシミュレータ内で実行:
from simulator.interfaces import SILSInterface, SensorData
sils = SILSInterface()
# 制御器の更新
sensor_data = SensorData(
gyro=np.array([0.0, 0.0, 0.0]),
accel=np.array([0.0, 0.0, -9.81]),
mag=np.array([20.0, 0.0, 45.0]),
pressure=101325.0,
altitude=0.0,
)
commands = sils.update(sensor_data, dt=0.0025)
motor_outputs = commands.motors
HILS(Hardware In the Loop Simulation)¶
実機ファームウェアとシリアル通信でセンサデータを注入:
注(2026-08-20): 以下はPython側インターフェースの実装例。
firmware/vehicle・firmware/controllerにはHILSの受信処理が実装されておらず、呼び出し元も無いため、実機とは現状接続できない。詳細はdocs/plans/archive/hils-firmware.mdを参照。
from simulator.interfaces import HILSInterface, HILSSimulationRunner
# HILSインターフェースの作成
hils = HILSInterface()
hils.connect(port="/dev/tty.usbserial-XXX", baudrate=921600)
# シミュレータからセンサデータを注入
hils.inject_imu(
timestamp_us=1000,
gyro=np.array([0.0, 0.0, 0.0]),
accel=np.array([0.0, 0.0, -9.81])
)
# ファームウェアからモーター出力を取得
motor_output = hils.get_motor_output(timeout=0.01)
if motor_output:
print(f"Motors: {motor_output.motors}")
7. プロトコル¶
シミュレータは protocol/spec/messages.yaml で定義されたメッセージ形式をサポート:
| メッセージ | サイズ | 方向 | 用途 |
|---|---|---|---|
| ControlPacket | 14 bytes | Controller→Vehicle | スティック入力 |
| TelemetryPacket | 22 bytes | Vehicle→Controller | ESP-NOWテレメトリ |
| TelemetryWSPacket | 108 bytes | Vehicle→GCS | WebSocketテレメトリ |
バイナリログ¶
from simulator.interfaces import BinaryLogger, BinaryLogReader
# ログの書き込み
logger = BinaryLogger("flight_log.bin")
logger.write_control(control_packet)
logger.write_telemetry(telemetry_packet)
logger.close()
# ログの読み込み
reader = BinaryLogReader("flight_log.bin")
for packet_type, packet in reader:
print(f"{packet_type}: {packet}")
8. テストの実行¶
cd stampfly_ecosystem/simulator/scripts
# センサモデルのテスト
python test_sensors.py
# プロトコルのテスト
python test_protocol.py
# 制御システムのテスト
python test_control.py
# センサ特性の可視化
python visualize_sensors.py
9. トラブルシューティング¶
VPythonが起動しない¶
# Jupyter環境では以下を試す
jupyter nbextension enable --py vpython
# または、ブラウザで直接開く
python -c "from vpython import *; sphere()"
ジョイスティックが認識されない¶
- HIDAPIライブラリがインストールされているか確認
- ジョイスティックがUSB接続されているか確認
- 他のアプリケーションがデバイスを占有していないか確認
import hid
for device in hid.enumerate():
print(f"VID: {device['vendor_id']:04x}, PID: {device['product_id']:04x}")
HILS通信エラー¶
注(2026-08-20): ファームウェア側にHILSの受信処理が未実装のため、以下の手順を試しても接続はできない。実装状況は 6. SILS/HILSテスト を参照。
- シリアルポートが正しいか確認
- ボーレート(921600)が一致しているか確認
- ファームウェアがHILSモードで起動しているか確認
10. 参考資料¶
| リソース | 説明 |
|---|---|
| MIGRATION_PLAN.md | 移植計画(開発者向け) |
| stampfly_sim | オリジナルシミュレータ |
firmware/vehicle/ |
実機ファームウェア |
protocol/spec/ |
プロトコル定義 |
Simulator Selection¶
| Simulator | Features | Use Cases |
|---|---|---|
| VPython version (this directory) | Lightweight, browser 3D, rich sensor models | Control learning, SILS/HILS |
| Genesis version (sandbox/genesis_sim/) | High-precision physics, 2000Hz, physical units | High-fidelity simulation |
1. Overview¶
About This Simulator (VPython Version)¶
StampFly Simulator is a Python-based tool for simulating the dynamics of the "StampFly" micro drone. It implements control algorithms compatible with the actual firmware (firmware/vehicle/) and can be used for:
- Control Algorithm Development: Safely tune PID gains and implement flight modes
- Sensor Fusion Research: Sensor simulation with realistic noise models
- Education: Visual learning of drone dynamics and control theory
- HILS Testing: Integration testing between firmware and simulator
Vehicle Configuration¶
Front
FL (M4) FR (M1)
╲ ▲ ╱
╲ │ ╱ CW: M2, M4
╲ │ ╱ CCW: M1, M3
╲│╱
╳ ← Center of Mass
╱│╲
╱ │ ╲
╱ │ ╲
╱ │ ╲
RL (M3) RR (M2)
Rear
Vehicle Parameters¶
| Parameter | Value | Unit |
|---|---|---|
| Mass | 35 | g |
| Inertia Ixx | 9.16e-6 | kg·m² |
| Inertia Iyy | 13.3e-6 | kg·m² |
| Inertia Izz | 20.4e-6 | kg·m² |
| Battery Voltage | 3.7 | V |
2. Setup¶
Requirements¶
- Python 3.10-3.12 (3.12 recommended)
- pip (package manager)
Installation¶
# Run from the repository root
cd stampfly_ecosystem/simulator
# Install dependencies
pip install -r requirements.txt
Dependencies¶
| Package | Purpose | Required |
|---|---|---|
| numpy | Numerical computation | ○ |
| matplotlib | Plotting | ○ |
| vpython | 3D visualization | ○ |
| numpy-stl | STL model loading | ○ |
| Pillow | Image processing | ○ |
| opencv-python | Image processing (optical flow) | ○ |
| hid | Joystick input | △ |
| pyserial | HILS communication | △ |
Note: The hid package requires system-level HID libraries.
3. Directory Structure¶
simulator/
├── README.md # This document
├── MIGRATION_PLAN.md # Migration plan (for developers)
├── requirements.txt # Dependencies
├── __init__.py
│
├── core/ # Physics engine
│ ├── physics.py # Rigid body dynamics
│ ├── dynamics.py # Multicopter dynamics
│ ├── aerodynamics.py # Aerodynamics
│ ├── motors.py # Motor/propeller model
│ └── battery.py # Battery model
│
├── sensors/ # Sensor models
│ ├── imu.py # BMI270 6-axis IMU
│ ├── magnetometer.py # BMM150 magnetometer
│ ├── barometer.py # BMP280 barometer
│ ├── tof.py # VL53L3CX ToF sensor
│ ├── opticalflow.py # PMW3901 optical flow
│ ├── power_monitor.py # INA3221 power monitor
│ └── noise_models.py # Allan variance-based noise
│
├── control/ # Control system
│ ├── pid.py # PID with incomplete derivative filter
│ ├── rate_controller.py # Rate (angular velocity) control
│ ├── attitude_controller.py # Attitude/altitude control
│ └── motor_mixer.py # X-quad motor mixer
│
├── interfaces/ # External interfaces
│ ├── joystick.py # HID joystick
│ ├── messages.py # Protocol messages
│ ├── protocol_bridge.py # Simulator state ↔ protocol conversion
│ ├── sils_interface.py # SILS interface
│ └── hils_interface.py # HILS interface (Python side only; firmware not implemented)
│
├── visualization/ # Visualization
│ └── vpython_backend.py # VPython 3D renderer
│
├── assets/ # Resources
│ ├── meshes/ # 3D models (STL)
│ ├── textures/ # Texture images
│ └── loaders/ # File loaders
│
├── scripts/ # Execution scripts
│ ├── run_sim.py # Main simulator
│ ├── sandbox.py # Development testing
│ ├── test_sensors.py # Sensor model tests
│ ├── test_protocol.py # Protocol tests
│ ├── test_control.py # Control system tests
│ └── visualize_sensors.py # Sensor visualization
│
└── output/ # Output files (.gitignore'd)
4. Basic Usage¶
Quick Start (For Beginners)¶
If you have a controller, you can experience drone piloting right away!
Step 1: Prepare the Controller¶
- Flash firmware to AtomS3 + Atom JoyStick (see controller/README.md)
- Power on the controller
- Press the screen (button) to open the menu, select the "Comm: ESP-NOW" row with the right stick → press the right button twice to reach "Comm: USB HID" (restarts automatically)
- Connect to PC via USB (recognized as a gamepad)
Step 2: Launch the Simulator¶
A browser opens showing the 3D view. Use the sticks to fly the drone!
Command Line Options¶
# Basic launch (voxel world, random terrain)
python run_sim.py
# Launch with ring world (circular course)
python run_sim.py --world ringworld
# Specify seed for reproducible terrain
python run_sim.py --seed 12345
# Ring world with specific seed
python run_sim.py --world ringworld --seed 42
| Option | Short | Description | Default |
|---|---|---|---|
--world |
-w |
World type (voxel/ringworld) | voxel |
--seed |
-s |
Terrain generation seed (random if omitted) | None |
World Types¶
| Type | Description |
|---|---|
| voxel | Voxel-based random terrain. Practice with different terrain each time |
| ringworld | Circular race course. Great for circuit flying practice |
Joystick Controls¶
Using AtomS3 + Atom JoyStick in USB HID mode:
| Input | Function | Mode 2 | Mode 3 |
|---|---|---|---|
| Throttle | Ascend/Descend | Left Y | Right Y |
| Roll | Move left/right | Right X | Left X |
| Pitch | Move forward/back | Right Y | Left Y |
| Yaw | Rotate | Left X | Right X |
Tip: If sticks are drifting, run "Calibration" from the controller menu. Deadband can be adjusted from 0-5% in the "Deadband" menu.
Code Example: Basic Physics Simulation¶
import numpy as np
from simulator.core import multicopter
# Create vehicle
mass = 0.037 # 37g
inertia = [
[9.16e-6, 0.0, 0.0],
[0.0, 13.3e-6, 0.0],
[0.0, 0.0, 20.4e-6]
]
drone = multicopter(mass=mass, inersia=inertia)
# Set initial state
drone.set_pqr([[0.0], [0.0], [0.0]]) # Angular velocity [rad/s]
drone.set_uvw([[0.0], [0.0], [0.0]]) # Velocity [m/s]
# Calculate hover voltage
weight = mass * 9.81
hover_voltage = drone.motor_prop[0].equilibrium_voltage(weight / 4)
# Simulation loop
dt = 0.001 # 1ms
for _ in range(1000):
voltage = [hover_voltage] * 4
drone.step(voltage, dt)
# Get state
position = drone.body.position
euler = drone.body.euler
print(f"Position: {position.T}, Euler: {euler.T}")
Code Example: Sensor Simulation¶
import numpy as np
from simulator.sensors import IMU, Barometer, Magnetometer
# Create sensors
imu = IMU(sample_rate=400) # 400Hz
baro = Barometer(sample_rate=50) # 50Hz
mag = Magnetometer(sample_rate=100) # 100Hz
# Generate sensor output from true values
true_accel = np.array([0.0, 0.0, -9.81]) # m/s²
true_gyro = np.array([0.0, 0.0, 0.0]) # rad/s
true_altitude = 1.5 # m
# IMU output (with noise)
gyro_meas, accel_meas = imu.update(true_gyro, true_accel, dt=0.0025)
# Barometer output
pressure, temperature = baro.update(true_altitude, dt=0.02)
# Magnetometer output
mag_field = mag.update(attitude=np.array([0, 0, 0]), dt=0.01)
Code Example: Control System¶
import numpy as np
from simulator.control import RateController, MotorMixer
# Create controllers (firmware-compatible gains)
rate_ctrl = RateController()
mixer = MotorMixer()
# Calculate motor output from stick input
gyro = np.array([0.1, -0.05, 0.02]) # Current angular velocity
throttle = 0.5
roll_out, pitch_out, yaw_out = rate_ctrl.update_from_stick(
stick_roll=0.3,
stick_pitch=-0.2,
stick_yaw=0.1,
gyro=gyro,
dt=0.0025
)
motors = mixer.mix(throttle, roll_out, pitch_out, yaw_out)
print(f"Motor outputs: {motors}") # Range [0.0, 1.0]
5. Control Modes¶
Rate (Acro) Mode¶
Direct angular velocity control. Stick input maps to target angular rates.
from simulator.control import RateController
ctrl = RateController()
# Stick input [-1, 1] → Target rate
# max_rate: Roll=220°/s, Pitch=220°/s, Yaw=200°/s
roll_out, pitch_out, yaw_out = ctrl.update_from_stick(
stick_roll=0.5, # 110°/s target
stick_pitch=0.0,
stick_yaw=0.0,
gyro=current_gyro,
dt=0.0025
)
Angle (Stabilize) Mode¶
Attitude angle control with self-leveling.
from simulator.control import AttitudeController
ctrl = AttitudeController()
# Stick input [-1, 1] → Target angle
# max_angle: Roll=30°, Pitch=30°
roll_out, pitch_out, yaw_out = ctrl.update(
stick_roll=0.5, # 15° target
stick_pitch=0.0,
stick_yaw=0.0,
attitude=current_attitude, # [roll, pitch, yaw]
gyro=current_gyro,
dt=0.0025
)
Altitude Hold Mode¶
Maintains altitude.
from simulator.control import AltitudeController
ctrl = AltitudeController(hover_throttle=0.5)
# Set target altitude
ctrl.set_altitude(1.5) # 1.5m
# Update
throttle = ctrl.update(
current_altitude=1.2,
vertical_velocity=0.1,
stick_throttle=0.5,
dt=0.02
)
6. SILS/HILS Testing¶
SILS (Software-in-the-Loop)¶
Run Python-implemented controllers within the simulator:
from simulator.interfaces import SILSInterface, SensorData
sils = SILSInterface()
# Update controller
sensor_data = SensorData(
gyro=np.array([0.0, 0.0, 0.0]),
accel=np.array([0.0, 0.0, -9.81]),
mag=np.array([20.0, 0.0, 45.0]),
pressure=101325.0,
altitude=0.0,
)
commands = sils.update(sensor_data, dt=0.0025)
motor_outputs = commands.motors
HILS (Hardware In the Loop Simulation)¶
Inject sensor data to real firmware via serial communication:
Note (2026-08-20): The example below shows the Python-side interface only. Neither
firmware/vehiclenorfirmware/controllerimplements the HILS receiver, and nothing currently calls this interface, so it cannot connect to real hardware yet. Seedocs/plans/archive/hils-firmware.mdfor details.
from simulator.interfaces import HILSInterface, HILSSimulationRunner
# Create HILS interface
hils = HILSInterface()
hils.connect(port="/dev/tty.usbserial-XXX", baudrate=921600)
# Inject sensor data from simulator
hils.inject_imu(
timestamp_us=1000,
gyro=np.array([0.0, 0.0, 0.0]),
accel=np.array([0.0, 0.0, -9.81])
)
# Get motor output from firmware
motor_output = hils.get_motor_output(timeout=0.01)
if motor_output:
print(f"Motors: {motor_output.motors}")
7. Protocol¶
The simulator supports message formats defined in protocol/spec/messages.yaml:
| Message | Size | Direction | Purpose |
|---|---|---|---|
| ControlPacket | 14 bytes | Controller→Vehicle | Stick input |
| TelemetryPacket | 22 bytes | Vehicle→Controller | ESP-NOW telemetry |
| TelemetryWSPacket | 108 bytes | Vehicle→GCS | WebSocket telemetry |
Binary Logging¶
from simulator.interfaces import BinaryLogger, BinaryLogReader
# Write log
logger = BinaryLogger("flight_log.bin")
logger.write_control(control_packet)
logger.write_telemetry(telemetry_packet)
logger.close()
# Read log
reader = BinaryLogReader("flight_log.bin")
for packet_type, packet in reader:
print(f"{packet_type}: {packet}")
8. Running Tests¶
cd stampfly_ecosystem/simulator/scripts
# Sensor model tests
python test_sensors.py
# Protocol tests
python test_protocol.py
# Control system tests
python test_control.py
# Sensor visualization
python visualize_sensors.py
9. Troubleshooting¶
VPython Won't Start¶
# In Jupyter environment, try:
jupyter nbextension enable --py vpython
# Or open directly in browser:
python -c "from vpython import *; sphere()"
Joystick Not Recognized¶
- Check if HIDAPI library is installed
- Check if joystick is connected via USB
- Check if another application is using the device
import hid
for device in hid.enumerate():
print(f"VID: {device['vendor_id']:04x}, PID: {device['product_id']:04x}")
HILS Communication Errors¶
Note (2026-08-20): The firmware does not implement a HILS receiver yet, so the steps below will not achieve a connection. See 6. SILS/HILS Testing for implementation status.
- Verify the serial port is correct
- Verify baud rate (921600) matches
- Verify firmware is running in HILS mode
10. References¶
| Resource | Description |
|---|---|
| MIGRATION_PLAN.md | Migration plan (for developers) |
| stampfly_sim | Original simulator |
firmware/vehicle/ |
Actual firmware |
protocol/spec/ |
Protocol definitions |