roverdevkit / data /analytical /SCHEMA.md
jjreif's picture
Deploy roverdevkit @ 2676a67
b3d14e3
|
Raw
History Blame Contribute Delete
13.3 kB
# Analytical Dataset Column Schema
Produced by `roverdevkit.surrogate.dataset.build_dataset` from
`LHSSample`s generated by `roverdevkit.surrogate.sampling.generate_samples`.
Each row is **one** `(design, scenario, soil)` triple evaluated by
`roverdevkit.mission.evaluator.evaluate_verbose`, flattened into a
single Parquet row.
- **Schema version:** `v9` (see `dataset.SCHEMA_VERSION`). Scientific
payload is an explicit mission requirement, carried by the
scenario-side inputs `scenario_payload_mass_kg` and
`scenario_payload_power_w`. Payload mass enters the total vehicle
mass as a line item outside the AIAA S-120A dry-mass growth margin
(`m_total = m_dry + m_margin + m_payload`); payload power adds to the
continuous ops-time electrical load (alongside avionics) and to the
hot-case thermal dissipation. Both are sampled uniform and
family-agnostic (`payload_mass_kg` in `[0, 30]`,
`payload_power_w` in `[0, 30]`) so the entire webapp Mission-Inputs
slider range is in-distribution. Payload lives on `MissionScenario`
(a requirement set by the mission), not on `DesignVector` (a variable
the designer trades); the design vector is 11-D. A per-call override
on `evaluate` / `/evaluate` / `/predict` lets callers substitute a
specific payload.
- **Fidelity level (this file):** `analytical` β€” the Bekker-Wong
terramechanics path solved inside `traverse_sim.run_traverse`.
- **Canonical filename:** `lhs_v9.parquet` β€” the current training set,
40k rows at 10k Γ— 4 scenario families. Pilot (`lhs_pilot.parquet`)
and challenge (`challenge_v1.parquet`) files are generated on demand
from `scripts/build_dataset.py`; only the canonical training set is
treated as a tracked artifact.
Dataset-level metadata is written to the Parquet file's schema footer;
use `read_parquet_metadata(path)` to recover it (seed, sampler version,
scenario families, val/test fractions, UTC build timestamp, evaluator
version, free-form notes).
## Column groups
Prefix conventions:
- `design_*` β€” inputs from the 11-D `DesignVector`.
- `scenario_*` β€” inputs from the `MissionScenario`, plus the sampler's
jittered Bekker soil parameters (`scenario_soil_*`).
- `stat_*` β€” aggregate statistics (mean / p95 / max / final) reduced
from the per-step `TraverseLog` time series.
- Unprefixed columns with physical units (e.g. `range_km`) β€” targets
from `MissionMetrics`.
- Otherwise β€” dataset metadata.
### Dataset metadata (5 columns)
| Column | dtype | Description |
| --- | --- | --- |
| `sample_index` | int64 | Monotonic row id from the sampler. Stable across re-runs with the same seed. |
| `split` | category | `train` / `val` / `test`, assigned at sample time with a deterministic RNG independent of row ordering. |
| `stratum_id` | int | `0 = 4-wheel`, `1 = 6-wheel`. Matches `design_n_wheels`. |
| `fidelity` | category | `analytical` for this file β€” the Bekker-Wong terramechanics path. No separate fidelity tier is shipped. |
| `status` | category | `ok` if evaluator succeeded, else the exception class name (e.g. `ValueError`). Numeric target columns are NaN on non-`ok` rows; boolean targets are `False`. |
### Design vector (11 columns)
All `design_*` columns mirror the `DesignVector` pydantic schema.
| Column | dtype | Range | Description |
| --- | --- | --- | --- |
| `design_wheel_radius_m` | float64 | [0.05, 0.20] | Wheel radius R |
| `design_wheel_width_m` | float64 | [0.03, 0.20] | Wheel width W |
| `design_grouser_height_m` | float64 | [0.0, 0.020] | Grouser height |
| `design_grouser_count` | int64 | [0, 24] | Number of grousers per wheel |
| `design_n_wheels` | int64 | {4, 6} | Wheel count (kept in sync with architecture) |
| `design_mobility_architecture` | category | `rigid_4wheel`, `rocker_bogie_6wheel` | Primary mobility-architecture trade in the evaluator/optimizer. Not yet present in the shipped `lhs_v9.parquet` surrogate training set; the surrogate still keys off `design_n_wheels` until the dataset is rebuilt. |
| `design_chassis_mass_kg` | float64 | [0.5, 50.0] | Dry chassis mass (structural chassis only) |
| `design_wheelbase_m` | float64 | [0.3, 1.2] | Wheelbase |
| `design_solar_area_m2` | float64 | [0.1, 1.5] | Solar array area |
| `design_battery_capacity_wh` | float64 | [5.0, 500.0] | Usable battery energy |
| `design_avionics_power_w` | float64 | [5.0, 40.0] | Continuous avionics draw |
| `design_peak_wheel_torque_nm` | float64 | [0.05, 20.0] | Per-wheel hub torque capacity. Cruise speed is derived inside the evaluator from torque + slip + power balance (see `roverdevkit/drivetrain/motor.py::cruise_speed`). LHS is log-uniform around a per-row anchor rather than uniform on these bounds β€” see `roverdevkit/surrogate/sampling.py::_peak_wheel_torque_anchor_for_row`. |
### Scenario inputs (18 columns)
Family-fixed columns (`scenario_family`, `scenario_terrain_class`,
`scenario_soil_simulant`, `scenario_sun_geometry`,
`scenario_traverse_distance_m`) take one of four canonical values per
family. The remaining columns are jittered per sample.
| Column | dtype | Notes |
| --- | --- | --- |
| `scenario_family` | category | One of `equatorial_mare_traverse`, `polar_prospecting`, `highland_slope_capability`, `crater_rim_survey`. Use for per-scenario accuracy breakdown. |
| `scenario_name` | category | Mirrors `scenario_family` in this dataset (validation-only scenarios live elsewhere). |
| `scenario_latitude_deg` | float64 | Family-specific range; see `sampling.FAMILIES`. |
| `scenario_traverse_distance_m` | float64 | Family-fixed, non-binding β€” deliberately above the energy-/duty-limited reach so `range_km` stays a continuous signal instead of saturating at a distance cap. |
| `scenario_terrain_class` | category | `mare_nominal`, `mare_loose`, `highland_dense`, `polar_regolith`. |
| `scenario_soil_simulant` | category | Family nominal; the *actual* Bekker numbers used by the evaluator are the `scenario_soil_*` columns below. |
| `scenario_mission_duration_earth_days` | float64 | Family-specific range. |
| `scenario_max_slope_deg` | float64 | Family-specific range. |
| `scenario_operational_duty_cycle` | float64 | Drive duty cycle the rover would actually run on the ground β€” sets `Ξ΄_eff = clamp(Ξ΄_ops, 0, 0.6)` in the traverse loop. Sampled per row uniform on `[0, 0.6]` independently of family, so the surrogate keys off it as a true continuous input. The per-family default is kept on `ScenarioFamily` for canonical YAML / UI initial slider position. |
| `scenario_sun_geometry` | category | `continuous` / `diurnal` / `polar_intermittent`. |
| `scenario_soil_n` | float64 | Bekker sinkage exponent, jitter bounds [0.8, 1.2]. |
| `scenario_soil_k_c` | float64 | Cohesive modulus, [0.5, 2.0] kN/m^(n+1). |
| `scenario_soil_k_phi` | float64 | Frictional modulus, [400, 1200] kN/m^(n+2). |
| `scenario_soil_cohesion_kpa` | float64 | Soil cohesion, [0.1, 1.0] kPa. |
| `scenario_soil_friction_angle_deg` | float64 | Internal friction angle, [30, 50]Β°. |
| `scenario_soil_shear_modulus_k_m` | float64 | Janosi-Hanamoto K, [0.010, 0.025] m. |
| `scenario_payload_mass_kg` | float64 | Scientific-payload mass (mission requirement). Per-row LHS feature uniform on `[0, 30]` independently of family. Added to total vehicle mass as a line item outside the dry-mass growth margin; the per-scenario default is kept on the YAML / `ScenarioFamily` for canonical webapp slider position. |
| `scenario_payload_power_w` | float64 | Scientific-payload continuous ops-time power (mission requirement). Per-row LHS feature uniform on `[0, 30]`. Added to the continuous electrical load (alongside avionics) in the traverse budget and to the hot-case thermal dissipation. |
| `scenario_required_obstacle_height_m` | float64 | Minimum traversable obstacle/step height (m). Defaults to 0 on the canonical smooth-regolith scenarios. Evaluator-only today: obstacle metrics (`obstacle_capability_m`, `obstacle_margin_m`) are computed from `mobility_architecture` and wheel radius; the surrogate does not yet predict them. |
### Mission-metric targets (8 columns)
Mirror `MissionMetrics` fields. `range_km` and `energy_margin_raw_pct`
are the primary regression targets (no saturation); `*_pct` and the
boolean flag are secondary reporting/classification targets.
`thermal_survival` is **not** in this group: the evaluator still
computes it as a diagnostic, but the mass model treats RHU power and
MLI quality as free, so it reduces to a near-trivial gate with no real
design trade-off and the surrogate does not consume or predict it.
| Column | dtype | Notes |
| --- | --- | --- |
| `range_km` | float64 | Energy-feasible mission range. `run_traverse` applies an in-traverse throttle that drops effective duty when the battery floors and load exceeds solar. |
| `energy_margin_pct` | float64 | Clipped 0-100, SOC-based reporting metric. |
| `energy_margin_raw_pct` | float64 | Unclipped mission-integrated `(E_in - E_out)/E_out Γ— 100`; primary surrogate target. |
| `slope_capability_deg` | float64 | Max climbable slope on this soil. |
| `total_mass_kg` | float64 | Mass-model output. |
| `peak_motor_torque_nm` | float64 | Observed peak wheel torque during traverse. |
| `sinkage_max_m` | float64 | Observed peak sinkage during traverse. |
| `stalled` | bool | Single feasibility classifier target (1 = infeasible). Captures whether the rover failed the slip-balance solve at any traverse step (Brent solver could not find a slip that satisfied force balance under the available drawbar pull and torque envelope). |
Evaluator-only architecture metrics (present on live `/evaluate` and optimizer outputs, not in the shipped `lhs_v9.parquet` targets):
| Column | dtype | Notes |
| --- | --- | --- |
| `obstacle_capability_m` | float64 | Estimated max traversable obstacle height from architecture proxy ($h_{\mathrm{obs}} = k_{\mathrm{arch}} R$). |
| `obstacle_margin_m` | float64 | Capability minus `scenario_required_obstacle_height_m`. |
| `obstacle_requirement_met` | bool | Whether `obstacle_margin_m \ge 0`. |
| `architecture_mass_kg` | float64 | Rocker-bogie suspension/linkage mass charged in the bottom-up mass model. |
### Traverse-log aggregate statistics (β‰₯24 columns)
Reduced from the per-step `TraverseLog` time series. Used to measure
where the surrogate needs to be accurate (peak-load versus
steady-state regimes) and as auxiliary diagnostics for the baselines.
Numeric aggregates (mean / p95 / max over the whole traverse, absolute
value for signed quantities like slip and torque):
- `stat_power_in_{mean,p95,max}_w` β€” solar input power.
- `stat_power_out_{mean,p95,max}_w` β€” total electrical draw (mobility + avionics).
- `stat_mobility_power_{mean,p95,max}_w` β€” mobility subsystem draw alone.
- `stat_slip_{mean,p95,max}` β€” wheel slip magnitude in [0, ~0.95].
- `stat_sinkage_{mean,p95}_m` β€” peak is already `sinkage_max_m` above.
- `stat_wheel_torque_{mean,p95}_nm` β€” peak is already `peak_motor_torque_nm` above.
- `stat_sun_elevation_{mean,max}_deg` β€” degrees above horizon.
- `stat_soc_final` / `stat_soc_min` β€” end-of-mission and deepest SOC.
Boolean end-of-run flags:
- `stat_rover_stalled` β€” Brent slip solve failed at some step.
- `stat_battery_floored` β€” SOC hit the 15% DoD floor during the run.
- `stat_reached_distance` β€” the rover reached the scenario's (non-binding) distance budget.
Categorical:
- `stat_terminated_reason` β€” a free-form short string from the sim layer
(e.g. `"mission_duration"`, `"evaluator_error"`). Use as a
post-hoc diagnostic; not suitable as a model input.
## Layer-1 registry sanity scope
`roverdevkit.surrogate.baselines.predict_for_registry_rovers` produces
a `registry_sanity.csv` artifact with one row per `(rover, algorithm,
target)` tuple plus an `is_primary` boolean. The split is enforced
by the `LAYER1_PRIMARY_TARGETS` / `LAYER1_DIAGNOSTIC_TARGETS`
constants in `roverdevkit/surrogate/baselines.py`:
- **Primary (is_primary=True):** `total_mass_kg`,
`slope_capability_deg`, `stalled`. Design-axis metrics where the LHS
bounds put every flown / design-target rover inside the surrogate's
training support. Treated as the main registry sanity set.
- **Diagnostic (is_primary=False):** `range_km`,
`energy_margin_raw_pct`. Both are scenario-OOD for the registry:
Pragyan β‰ˆ 100 m, Yutu-2 β‰ˆ 25 m / lunar day, MoonRanger and
Rashid-1 β‰ˆ 1 km published mission distances against LHS family
budgets of 20–80 km (intentionally non-binding so `range_km` stays
a continuous training signal). The relative error for these
targets is dominated by the absolute-scale mismatch and reflects
scenario-OOD rather than a surrogate-calibration failure. Reported
for transparency only.
Slope MAPE on Pragyan and MoonRanger runs elevated relative to mass:
published rover slope-capability specs come from real-rover-specific
design choices the analytical Bekker-Wong kernel's feature space cannot
fully resolve.
## Column count sanity
Metadata (5) + design (11) + scenario (18) + metrics (8) + stats (β‰₯24)
= β‰₯66 columns at `SCHEMA_VERSION = v9`. Future versions appending,
removing, or re-binding columns β€” *or* changing the LHS support so a
surrogate trained on one version would be OOD on the next β€” *must* bump
`SCHEMA_VERSION` so downstream code can detect a mismatch.