feat(simulator): virtual drone fleet data generator
One process per drone: seeded patrol kinematics, sensor streams at realistic rates, detection events, hot current/ blocks sealed into Hive-partitioned Parquet, and the compact UDP state broadcast with peer RSSI derivation. Scales via docker compose --scale.
This commit is contained in:
@@ -0,0 +1,80 @@
|
||||
"""Sensor row generators. Pure functions: (pose, rng, ts) -> row dict.
|
||||
|
||||
Every row carries int64 epoch nanoseconds. Values are emitted at full float
|
||||
precision - quantization happens only in the broadcast payload, never in
|
||||
stored telemetry.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import math
|
||||
import random
|
||||
from typing import Any
|
||||
|
||||
from .flight import Pose
|
||||
|
||||
Row = dict[str, Any]
|
||||
|
||||
|
||||
def imu_row(pose: Pose, rng: random.Random, ts_ns: int) -> Row:
|
||||
return {
|
||||
"ts_ns": ts_ns,
|
||||
"accel_x": rng.gauss(0.0, 0.35),
|
||||
"accel_y": rng.gauss(0.0, 0.35),
|
||||
"accel_z": rng.gauss(-9.81, 0.25),
|
||||
"gyro_x": math.radians(rng.gauss(pose.roll, 0.8)),
|
||||
"gyro_y": math.radians(rng.gauss(pose.pitch, 0.8)),
|
||||
"gyro_z": math.radians(rng.gauss(0.0, 0.5)),
|
||||
}
|
||||
|
||||
|
||||
def baro_row(pose: Pose, rng: random.Random, ts_ns: int) -> Row:
|
||||
pressure = 101325.0 * math.exp(-pose.z / 8434.0) + rng.gauss(0.0, 4.0)
|
||||
return {"ts_ns": ts_ns, "pressure_pa": pressure, "alt_est_m": pose.z + rng.gauss(0.0, 0.3)}
|
||||
|
||||
|
||||
def temp_row(_pose: Pose, rng: random.Random, ts_ns: int) -> Row:
|
||||
return {
|
||||
"ts_ns": ts_ns,
|
||||
"cpu_c": 55.0 + rng.gauss(0.0, 3.0),
|
||||
"gpu_c": 62.0 + rng.gauss(0.0, 4.0),
|
||||
"ambient_c": 24.0 + rng.gauss(0.0, 0.5),
|
||||
}
|
||||
|
||||
|
||||
def battery_row(_pose: Pose, rng: random.Random, ts_ns: int, elapsed_s: float, duration_s: float) -> Row:
|
||||
level = max(0.0, 100.0 - 80.0 * elapsed_s / max(duration_s, 1.0) + rng.gauss(0.0, 0.2))
|
||||
return {"ts_ns": ts_ns, "level_pct": level, "voltage_v": 22.2 * (0.85 + 0.15 * level / 100.0)}
|
||||
|
||||
|
||||
DETECTION_CLASSES = ("vehicle", "person", "animal", "structure", "unknown")
|
||||
|
||||
|
||||
def detection_row(pose: Pose, rng: random.Random, ts_ns: int) -> Row:
|
||||
return {
|
||||
"ts_ns": ts_ns,
|
||||
"cls": rng.choice(DETECTION_CLASSES),
|
||||
"confidence": round(rng.uniform(0.42, 0.99), 3),
|
||||
"obj_x": pose.x + rng.uniform(-40.0, 40.0),
|
||||
"obj_y": pose.y + rng.uniform(-40.0, 40.0),
|
||||
"obj_z": rng.uniform(0.0, 5.0),
|
||||
"ego_x": pose.x,
|
||||
"ego_y": pose.y,
|
||||
"ego_z": pose.z,
|
||||
}
|
||||
|
||||
|
||||
def state_row(pose: Pose, ts_ns: int) -> Row:
|
||||
return {
|
||||
"ts_ns": ts_ns,
|
||||
"pos_x": pose.x,
|
||||
"pos_y": pose.y,
|
||||
"pos_z": pose.z,
|
||||
"roll": pose.roll,
|
||||
"pitch": pose.pitch,
|
||||
"yaw": pose.yaw,
|
||||
"vel_x": pose.vx,
|
||||
"vel_y": pose.vy,
|
||||
"vel_z": pose.vz,
|
||||
"frame_ref": 0,
|
||||
}
|
||||
Reference in New Issue
Block a user