Spaces:
Sleeping
Sleeping
Deploy roverdevkit @ 2676a67
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- .dockerignore +71 -0
- .gitattributes +0 -34
- .gitignore +123 -0
- Dockerfile +94 -0
- LICENSE +21 -0
- Makefile +89 -0
- README.md +22 -5
- data/README.md +47 -0
- data/analytical/.gitkeep +0 -0
- data/analytical/SCHEMA.md +201 -0
- data/mass_validation_set.csv +9 -0
- data/published_traverse_data.csv +3 -0
- data/rovers.yaml +202 -0
- data/soil_simulants.csv +11 -0
- data/validation/README.md +84 -0
- data/validation/raw/.gitkeep +0 -0
- data/validation/single_wheel_experiments.csv +28 -0
- data/validation/wong_layer3_reference.csv +8 -0
- deploy/huggingface/README.md +28 -0
- environment.yml +24 -0
- fig_system_architecture.png +0 -0
- models/README.md +23 -0
- models/surrogate_v9/quantile_bundles.joblib +3 -0
- pyproject.toml +120 -0
- reports/pareto_fronts/front_crater_rim_survey.csv +51 -0
- reports/pareto_fronts/front_crater_rim_survey.metadata.json +35 -0
- reports/pareto_fronts/front_equatorial_mare_traverse.csv +51 -0
- reports/pareto_fronts/front_equatorial_mare_traverse.metadata.json +35 -0
- reports/pareto_fronts/front_highland_slope_capability.csv +51 -0
- reports/pareto_fronts/front_highland_slope_capability.metadata.json +36 -0
- reports/pareto_fronts/front_polar_prospecting.csv +51 -0
- reports/pareto_fronts/front_polar_prospecting.metadata.json +35 -0
- reports/pareto_fronts/manifest.json +143 -0
- roverdevkit/__init__.py +24 -0
- roverdevkit/architecture.py +88 -0
- roverdevkit/drivetrain/__init__.py +28 -0
- roverdevkit/drivetrain/motor.py +325 -0
- roverdevkit/mass/__init__.py +38 -0
- roverdevkit/mass/parametric_mers.py +474 -0
- roverdevkit/mass/validation.py +251 -0
- roverdevkit/mission/__init__.py +22 -0
- roverdevkit/mission/capability.py +129 -0
- roverdevkit/mission/configs/cadre_polar_unit.yaml +41 -0
- roverdevkit/mission/configs/chandrayaan3_pragyan.yaml +34 -0
- roverdevkit/mission/configs/change4_yutu2_per_lunar_day.yaml +39 -0
- roverdevkit/mission/configs/crater_rim_micro.yaml +38 -0
- roverdevkit/mission/configs/crater_rim_survey.yaml +24 -0
- roverdevkit/mission/configs/equatorial_mare_traverse.yaml +29 -0
- roverdevkit/mission/configs/highland_micro.yaml +42 -0
- roverdevkit/mission/configs/highland_slope_capability.yaml +35 -0
.dockerignore
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Keep the Docker build context small. The build context is the
|
| 2 |
+
# repo root (see `webapp/Dockerfile`), so anything not needed at
|
| 3 |
+
# image-build time should be excluded here.
|
| 4 |
+
|
| 5 |
+
# VCS / IDE
|
| 6 |
+
.git
|
| 7 |
+
.gitignore
|
| 8 |
+
.github
|
| 9 |
+
.cursor
|
| 10 |
+
.vscode
|
| 11 |
+
.idea
|
| 12 |
+
|
| 13 |
+
# Python build / cache artifacts
|
| 14 |
+
__pycache__
|
| 15 |
+
*.pyc
|
| 16 |
+
*.pyo
|
| 17 |
+
*.pyd
|
| 18 |
+
*.egg-info
|
| 19 |
+
.pytest_cache
|
| 20 |
+
.mypy_cache
|
| 21 |
+
.ruff_cache
|
| 22 |
+
.tox
|
| 23 |
+
htmlcov
|
| 24 |
+
build
|
| 25 |
+
dist
|
| 26 |
+
|
| 27 |
+
# Conda / venv
|
| 28 |
+
.conda
|
| 29 |
+
.venv
|
| 30 |
+
venv
|
| 31 |
+
env
|
| 32 |
+
|
| 33 |
+
# Notebook outputs (notebooks themselves stay; outputs are large)
|
| 34 |
+
.ipynb_checkpoints
|
| 35 |
+
|
| 36 |
+
# Frontend build outputs (re-built inside the Dockerfile from source).
|
| 37 |
+
webapp/frontend/node_modules
|
| 38 |
+
webapp/frontend/dist
|
| 39 |
+
webapp/frontend/.cache
|
| 40 |
+
|
| 41 |
+
# Docker scaffolding (don't recurse the Dockerfile into itself).
|
| 42 |
+
**/Dockerfile
|
| 43 |
+
**/docker-compose*.yml
|
| 44 |
+
.dockerignore
|
| 45 |
+
|
| 46 |
+
# Local-only logs and reports
|
| 47 |
+
*.log
|
| 48 |
+
reports/**/*.log
|
| 49 |
+
|
| 50 |
+
# Datasets / artifacts that aren't needed at runtime. The runtime
|
| 51 |
+
# image only needs:
|
| 52 |
+
# - data/ (soils, scenarios, validation set)
|
| 53 |
+
# - models/surrogate_v9/quantile_bundles.joblib
|
| 54 |
+
# - reports/pareto_fronts/
|
| 55 |
+
# Heavy training-time parquets and superseded surrogate versions stay
|
| 56 |
+
# out so the image fits inside typical free-tier hosting limits.
|
| 57 |
+
data/analytical/
|
| 58 |
+
reports/baselines_*
|
| 59 |
+
reports/surrogate_v7_1/
|
| 60 |
+
reports/surrogate_v8/
|
| 61 |
+
reports/tuned_v7/
|
| 62 |
+
reports/tuned_v8/
|
| 63 |
+
reports/tuned_v9/
|
| 64 |
+
reports/rediscovery_loo_*/
|
| 65 |
+
reports/validation_*/
|
| 66 |
+
reports/week*/
|
| 67 |
+
reports/intervals_*/
|
| 68 |
+
|
| 69 |
+
# OS noise
|
| 70 |
+
.DS_Store
|
| 71 |
+
Thumbs.db
|
.gitattributes
CHANGED
|
@@ -1,35 +1 @@
|
|
| 1 |
-
*.7z filter=lfs diff=lfs merge=lfs -text
|
| 2 |
-
*.arrow filter=lfs diff=lfs merge=lfs -text
|
| 3 |
-
*.bin filter=lfs diff=lfs merge=lfs -text
|
| 4 |
-
*.bz2 filter=lfs diff=lfs merge=lfs -text
|
| 5 |
-
*.ckpt filter=lfs diff=lfs merge=lfs -text
|
| 6 |
-
*.ftz filter=lfs diff=lfs merge=lfs -text
|
| 7 |
-
*.gz filter=lfs diff=lfs merge=lfs -text
|
| 8 |
-
*.h5 filter=lfs diff=lfs merge=lfs -text
|
| 9 |
*.joblib filter=lfs diff=lfs merge=lfs -text
|
| 10 |
-
*.lfs.* filter=lfs diff=lfs merge=lfs -text
|
| 11 |
-
*.mlmodel filter=lfs diff=lfs merge=lfs -text
|
| 12 |
-
*.model filter=lfs diff=lfs merge=lfs -text
|
| 13 |
-
*.msgpack filter=lfs diff=lfs merge=lfs -text
|
| 14 |
-
*.npy filter=lfs diff=lfs merge=lfs -text
|
| 15 |
-
*.npz filter=lfs diff=lfs merge=lfs -text
|
| 16 |
-
*.onnx filter=lfs diff=lfs merge=lfs -text
|
| 17 |
-
*.ot filter=lfs diff=lfs merge=lfs -text
|
| 18 |
-
*.parquet filter=lfs diff=lfs merge=lfs -text
|
| 19 |
-
*.pb filter=lfs diff=lfs merge=lfs -text
|
| 20 |
-
*.pickle filter=lfs diff=lfs merge=lfs -text
|
| 21 |
-
*.pkl filter=lfs diff=lfs merge=lfs -text
|
| 22 |
-
*.pt filter=lfs diff=lfs merge=lfs -text
|
| 23 |
-
*.pth filter=lfs diff=lfs merge=lfs -text
|
| 24 |
-
*.rar filter=lfs diff=lfs merge=lfs -text
|
| 25 |
-
*.safetensors filter=lfs diff=lfs merge=lfs -text
|
| 26 |
-
saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
| 27 |
-
*.tar.* filter=lfs diff=lfs merge=lfs -text
|
| 28 |
-
*.tar filter=lfs diff=lfs merge=lfs -text
|
| 29 |
-
*.tflite filter=lfs diff=lfs merge=lfs -text
|
| 30 |
-
*.tgz filter=lfs diff=lfs merge=lfs -text
|
| 31 |
-
*.wasm filter=lfs diff=lfs merge=lfs -text
|
| 32 |
-
*.xz filter=lfs diff=lfs merge=lfs -text
|
| 33 |
-
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
-
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
-
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
*.joblib filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.gitignore
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Python
|
| 2 |
+
__pycache__/
|
| 3 |
+
*.py[cod]
|
| 4 |
+
*$py.class
|
| 5 |
+
*.so
|
| 6 |
+
.Python
|
| 7 |
+
build/
|
| 8 |
+
develop-eggs/
|
| 9 |
+
dist/
|
| 10 |
+
downloads/
|
| 11 |
+
eggs/
|
| 12 |
+
.eggs/
|
| 13 |
+
# `lib/` and `lib64/` are anchored to the repo root so the Python venv
|
| 14 |
+
# layout doesn't accidentally swallow `webapp/frontend/src/lib/`.
|
| 15 |
+
/lib/
|
| 16 |
+
/lib64/
|
| 17 |
+
parts/
|
| 18 |
+
sdist/
|
| 19 |
+
var/
|
| 20 |
+
wheels/
|
| 21 |
+
*.egg-info/
|
| 22 |
+
.installed.cfg
|
| 23 |
+
*.egg
|
| 24 |
+
MANIFEST
|
| 25 |
+
|
| 26 |
+
# Virtual envs
|
| 27 |
+
.env
|
| 28 |
+
.env.*
|
| 29 |
+
!.env.example
|
| 30 |
+
.venv
|
| 31 |
+
env/
|
| 32 |
+
venv/
|
| 33 |
+
ENV/
|
| 34 |
+
|
| 35 |
+
# Testing / coverage
|
| 36 |
+
.tox/
|
| 37 |
+
.nox/
|
| 38 |
+
.coverage
|
| 39 |
+
.coverage.*
|
| 40 |
+
.cache
|
| 41 |
+
htmlcov/
|
| 42 |
+
.pytest_cache/
|
| 43 |
+
coverage.xml
|
| 44 |
+
*.cover
|
| 45 |
+
|
| 46 |
+
# Type checkers
|
| 47 |
+
.mypy_cache/
|
| 48 |
+
.pyre/
|
| 49 |
+
.pytype/
|
| 50 |
+
.ruff_cache/
|
| 51 |
+
|
| 52 |
+
# Jupyter
|
| 53 |
+
.ipynb_checkpoints/
|
| 54 |
+
*.ipynb_checkpoints
|
| 55 |
+
|
| 56 |
+
# IDE
|
| 57 |
+
.vscode/
|
| 58 |
+
.idea/
|
| 59 |
+
*.swp
|
| 60 |
+
*.swo
|
| 61 |
+
|
| 62 |
+
# macOS
|
| 63 |
+
.DS_Store
|
| 64 |
+
|
| 65 |
+
# Project data — large artifacts are not checked in.
|
| 66 |
+
# We keep the directory structure via .gitkeep files.
|
| 67 |
+
data/analytical/*
|
| 68 |
+
!data/analytical/.gitkeep
|
| 69 |
+
!data/analytical/SCHEMA.md
|
| 70 |
+
data/scm/*
|
| 71 |
+
!data/scm/.gitkeep
|
| 72 |
+
!data/scm/SCHEMA.md
|
| 73 |
+
data/validation/raw/*
|
| 74 |
+
!data/validation/raw/.gitkeep
|
| 75 |
+
|
| 76 |
+
# Generated reports / model artifacts.
|
| 77 |
+
# Top-level reports/ subdirectories are ignored by default; specific
|
| 78 |
+
# ship-with-repo artifacts are unignored below. The .joblib/.parquet
|
| 79 |
+
# globals still catch large binaries inside any unignored subtree.
|
| 80 |
+
reports/*
|
| 81 |
+
!reports/pareto_fronts/
|
| 82 |
+
|
| 83 |
+
# Shipped ML model artifacts (runtime bundle for webapp / scripts).
|
| 84 |
+
models/*
|
| 85 |
+
!models/README.md
|
| 86 |
+
!models/surrogate_v9/
|
| 87 |
+
models/surrogate_v9/*
|
| 88 |
+
!models/surrogate_v9/quantile_bundles.joblib
|
| 89 |
+
*.joblib
|
| 90 |
+
!models/surrogate_v9/quantile_bundles.joblib
|
| 91 |
+
*.parquet
|
| 92 |
+
|
| 93 |
+
# Internal planning notes
|
| 94 |
+
/project_*.md
|
| 95 |
+
|
| 96 |
+
# Paper drafting workspace and generated manuscript figures.
|
| 97 |
+
# The reproducible figure-generation scripts live under scripts/ and are tracked.
|
| 98 |
+
/paper/
|
| 99 |
+
/reports/figures/
|
| 100 |
+
|
| 101 |
+
# Logs
|
| 102 |
+
logs/
|
| 103 |
+
*.log
|
| 104 |
+
|
| 105 |
+
# Hugging Face Space deploy mirror (local clone of the Space repo,
|
| 106 |
+
# rebuilt on each `make deploy-space`).
|
| 107 |
+
.hf-space/
|
| 108 |
+
|
| 109 |
+
# Project-specific scratch
|
| 110 |
+
scratch/
|
| 111 |
+
tmp/
|
| 112 |
+
.tmp_*_venv/
|
| 113 |
+
/package-lock.json
|
| 114 |
+
|
| 115 |
+
# webapp webapp build / install artifacts
|
| 116 |
+
webapp/frontend/node_modules/
|
| 117 |
+
webapp/frontend/dist/
|
| 118 |
+
webapp/frontend/.vite/
|
| 119 |
+
webapp/frontend/coverage/
|
| 120 |
+
webapp/frontend/playwright-report/
|
| 121 |
+
webapp/frontend/test-results/
|
| 122 |
+
webapp/backend/.coverage
|
| 123 |
+
webapp/.env.local
|
Dockerfile
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# RoverDevKit webapp — multi-stage Dockerfile
|
| 2 |
+
#
|
| 3 |
+
# Stage 1 (`frontend-build`) builds the Vite production bundle with
|
| 4 |
+
# Node 20 LTS. Stage 2 (`runtime`) installs the Python package + the
|
| 5 |
+
# `[webapp]` extras on top of `python:3.12-slim`, copies the package
|
| 6 |
+
# source / on-disk artifacts / built frontend bundle in, and runs
|
| 7 |
+
# uvicorn as a non-root user.
|
| 8 |
+
#
|
| 9 |
+
# Build context expectation: this file is invoked from the repo
|
| 10 |
+
# root so it can reach `pyproject.toml`, `roverdevkit/`, `data/`,
|
| 11 |
+
# `models/`, `reports/`, and `webapp/` in one COPY plane:
|
| 12 |
+
#
|
| 13 |
+
# docker build -f webapp/Dockerfile -t roverdevkit/webapp:dev .
|
| 14 |
+
#
|
| 15 |
+
# The image bakes in:
|
| 16 |
+
# - the analytical Bekker-Wong mission evaluator,
|
| 17 |
+
# - the v9 quantile-XGB surrogate bundles
|
| 18 |
+
# (`models/surrogate_v9/quantile_bundles.joblib`),
|
| 19 |
+
# - the canonical Pareto fronts (`reports/pareto_fronts/`),
|
| 20 |
+
# - the built React frontend (`/app/static/`).
|
| 21 |
+
|
| 22 |
+
# ---------------------------------------------------------------------------
|
| 23 |
+
# Stage 1: build the frontend bundle
|
| 24 |
+
# ---------------------------------------------------------------------------
|
| 25 |
+
|
| 26 |
+
FROM node:20-bookworm-slim AS frontend-build
|
| 27 |
+
|
| 28 |
+
WORKDIR /build
|
| 29 |
+
|
| 30 |
+
# Install dependencies first so the npm cache is reusable across edits
|
| 31 |
+
# of `webapp/frontend/src/`. Lockfile copy + `npm ci` gives a
|
| 32 |
+
# reproducible install.
|
| 33 |
+
COPY webapp/frontend/package.json webapp/frontend/package-lock.json ./
|
| 34 |
+
RUN npm ci --no-audit --no-fund
|
| 35 |
+
|
| 36 |
+
COPY webapp/frontend/ ./
|
| 37 |
+
RUN npm run build
|
| 38 |
+
|
| 39 |
+
# ---------------------------------------------------------------------------
|
| 40 |
+
# Stage 2: runtime image
|
| 41 |
+
# ---------------------------------------------------------------------------
|
| 42 |
+
|
| 43 |
+
FROM python:3.12-slim-bookworm AS runtime
|
| 44 |
+
|
| 45 |
+
ENV PYTHONDONTWRITEBYTECODE=1 \
|
| 46 |
+
PYTHONUNBUFFERED=1 \
|
| 47 |
+
PIP_DISABLE_PIP_VERSION_CHECK=1 \
|
| 48 |
+
PIP_NO_CACHE_DIR=1 \
|
| 49 |
+
ROVERDEVKIT_STATIC_DIR=/app/static
|
| 50 |
+
|
| 51 |
+
# `libgomp1` is needed by xgboost on Linux for the OpenMP runtime.
|
| 52 |
+
RUN apt-get update \
|
| 53 |
+
&& apt-get install -y --no-install-recommends libgomp1 \
|
| 54 |
+
&& rm -rf /var/lib/apt/lists/*
|
| 55 |
+
|
| 56 |
+
WORKDIR /app
|
| 57 |
+
|
| 58 |
+
# Install Python dependencies first so a code-only edit doesn't bust
|
| 59 |
+
# the heavy ML wheel cache. Editable installs need the package
|
| 60 |
+
# source in the same layer; copy the build metadata here and the
|
| 61 |
+
# rest in the next layer.
|
| 62 |
+
COPY pyproject.toml README.md LICENSE ./
|
| 63 |
+
COPY roverdevkit/ ./roverdevkit/
|
| 64 |
+
RUN pip install --no-cache-dir ".[webapp]"
|
| 65 |
+
|
| 66 |
+
# Webapp backend + on-disk artifacts.
|
| 67 |
+
COPY webapp/backend/ ./webapp/backend/
|
| 68 |
+
COPY data/ ./data/
|
| 69 |
+
COPY models/ ./models/
|
| 70 |
+
COPY reports/ ./reports/
|
| 71 |
+
|
| 72 |
+
# Built frontend bundle from stage 1 → mounted at /app/static via
|
| 73 |
+
# the ROVERDEVKIT_STATIC_DIR env var above.
|
| 74 |
+
COPY --from=frontend-build /build/dist/ ./static/
|
| 75 |
+
|
| 76 |
+
# Run as non-root to satisfy the standard hosting-platform contract
|
| 77 |
+
# (Fly.io, HF Spaces, K8s pod security policies, etc.). UID 1000 is
|
| 78 |
+
# arbitrary; pick whatever your hosting environment prefers.
|
| 79 |
+
RUN useradd --create-home --uid 1000 roverdevkit \
|
| 80 |
+
&& chown -R roverdevkit:roverdevkit /app
|
| 81 |
+
USER roverdevkit
|
| 82 |
+
|
| 83 |
+
EXPOSE 8000
|
| 84 |
+
|
| 85 |
+
# `--proxy-headers` lets the deployment reverse proxy (Fly's edge,
|
| 86 |
+
# HF Spaces' router, etc.) pass through the original client IP and
|
| 87 |
+
# scheme. `--forwarded-allow-ips='*'` is safe behind a trusted
|
| 88 |
+
# proxy and avoids 403s on the WebSocket / SSE probes some hosts
|
| 89 |
+
# use.
|
| 90 |
+
CMD ["uvicorn", "webapp.backend.main:app", \
|
| 91 |
+
"--host", "0.0.0.0", \
|
| 92 |
+
"--port", "8000", \
|
| 93 |
+
"--proxy-headers", \
|
| 94 |
+
"--forwarded-allow-ips=*"]
|
LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
MIT License
|
| 2 |
+
|
| 3 |
+
Copyright (c) 2026 Autonomous Mission Systems Lab, Duke University
|
| 4 |
+
|
| 5 |
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
| 6 |
+
of this software and associated documentation files (the "Software"), to deal
|
| 7 |
+
in the Software without restriction, including without limitation the rights
|
| 8 |
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
| 9 |
+
copies of the Software, and to permit persons to whom the Software is
|
| 10 |
+
furnished to do so, subject to the following conditions:
|
| 11 |
+
|
| 12 |
+
The above copyright notice and this permission notice shall be included in all
|
| 13 |
+
copies or substantial portions of the Software.
|
| 14 |
+
|
| 15 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
| 16 |
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
| 17 |
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
| 18 |
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
| 19 |
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
| 20 |
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
| 21 |
+
SOFTWARE.
|
Makefile
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Top-level developer-experience targets.
|
| 2 |
+
#
|
| 3 |
+
# All targets are .PHONY; this Makefile is a typing shortcut, not a
|
| 4 |
+
# build system. The canonical build paths are still ``pytest``,
|
| 5 |
+
# ``uvicorn``, and ``npm`` invoked directly. Targets here just spell
|
| 6 |
+
# out the conventional invocation so a new contributor can boot the
|
| 7 |
+
# webapp with one command.
|
| 8 |
+
#
|
| 9 |
+
# Convention:
|
| 10 |
+
# make webapp-dev → boot backend on :8000 and frontend on :5173
|
| 11 |
+
# make webapp-backend → backend only
|
| 12 |
+
# make webapp-frontend → frontend only
|
| 13 |
+
# make webapp-test → backend pytest + frontend lint + frontend build
|
| 14 |
+
# make webapp-build → frontend production build only
|
| 15 |
+
# make pareto-fronts → (re)generate canonical Pareto fronts under reports/
|
| 16 |
+
# make figures → (re)render every manuscript figure under paper/figures/
|
| 17 |
+
# make optimizer-robustness → run multi-seed NSGA-II robustness sweep
|
| 18 |
+
# make deploy-space → push current HEAD to the Hugging Face Space (manual)
|
| 19 |
+
#
|
| 20 |
+
# Override ports with `UVICORN_PORT=8001 make webapp-backend`.
|
| 21 |
+
# Override the conda env used by python targets with `CONDA_ENV=other`.
|
| 22 |
+
|
| 23 |
+
.PHONY: webapp-dev webapp-backend webapp-frontend webapp-test webapp-build pareto-fronts optimizer-robustness architecture-crossover figures deploy-space
|
| 24 |
+
|
| 25 |
+
UVICORN_PORT ?= 8000
|
| 26 |
+
VITE_PORT ?= 5173
|
| 27 |
+
CONDA_ENV ?= roverdevkit
|
| 28 |
+
|
| 29 |
+
# Boot both servers in one command. `trap 'kill 0'` propagates Ctrl+C
|
| 30 |
+
# to every backgrounded child so the cleanup story stays sane on
|
| 31 |
+
# macOS GNU make 3.81 (Apple's bundled version) without `.ONESHELL`.
|
| 32 |
+
webapp-dev:
|
| 33 |
+
@echo ">> backend → http://localhost:$(UVICORN_PORT)"
|
| 34 |
+
@echo ">> frontend → http://localhost:$(VITE_PORT)"
|
| 35 |
+
@trap 'kill 0' INT TERM EXIT; \
|
| 36 |
+
uvicorn webapp.backend.main:app --reload --port $(UVICORN_PORT) & \
|
| 37 |
+
(cd webapp/frontend && npm run dev -- --port $(VITE_PORT)) & \
|
| 38 |
+
wait
|
| 39 |
+
|
| 40 |
+
webapp-backend:
|
| 41 |
+
uvicorn webapp.backend.main:app --reload --port $(UVICORN_PORT)
|
| 42 |
+
|
| 43 |
+
webapp-frontend:
|
| 44 |
+
cd webapp/frontend && npm run dev -- --port $(VITE_PORT)
|
| 45 |
+
|
| 46 |
+
webapp-test:
|
| 47 |
+
pytest webapp/backend/tests -q
|
| 48 |
+
cd webapp/frontend && npm run lint && npm run build
|
| 49 |
+
|
| 50 |
+
webapp-build:
|
| 51 |
+
cd webapp/frontend && npm run build
|
| 52 |
+
|
| 53 |
+
# Regenerate the canonical evaluator-driven Pareto fronts that ship with
|
| 54 |
+
# the repo. The Pareto Explorer tab in the webapp loads these via
|
| 55 |
+
# `/pareto/fronts`, so a fresh clone gets a working explorer without
|
| 56 |
+
# anyone running NSGA-II live. Re-run after editing scenario configs.
|
| 57 |
+
# Defaults (50 pop × 60 gens, ~4 min
|
| 58 |
+
# total for all four scenarios) are tuned for offline use; pass extra
|
| 59 |
+
# args via SCRIPT_ARGS.
|
| 60 |
+
pareto-fronts:
|
| 61 |
+
conda run -n $(CONDA_ENV) --no-capture-output python scripts/generate_pareto_fronts.py $(SCRIPT_ARGS)
|
| 62 |
+
|
| 63 |
+
optimizer-robustness:
|
| 64 |
+
conda run -n $(CONDA_ENV) --no-capture-output python scripts/run_optimizer_robustness.py $(SCRIPT_ARGS)
|
| 65 |
+
|
| 66 |
+
architecture-crossover:
|
| 67 |
+
conda run -n $(CONDA_ENV) --no-capture-output python scripts/run_architecture_obstacle_crossover.py $(SCRIPT_ARGS)
|
| 68 |
+
|
| 69 |
+
# Re-render every manuscript figure from the committed artifacts under
|
| 70 |
+
# reports/ into paper/figures/ (the directory main.tex reads). Each figure
|
| 71 |
+
# has a dedicated scripts/make_*_figure.py regenerator (no notebook), so this
|
| 72 |
+
# target is the single one-command rebuild of all paper figures. Run
|
| 73 |
+
# `make pareto-fronts` first if the fronts changed.
|
| 74 |
+
figures:
|
| 75 |
+
conda run -n $(CONDA_ENV) --no-capture-output python scripts/make_pareto_fronts_figure.py
|
| 76 |
+
conda run -n $(CONDA_ENV) --no-capture-output python scripts/make_rediscovery_distance_figure.py
|
| 77 |
+
conda run -n $(CONDA_ENV) --no-capture-output python scripts/make_rediscovery_overlay_figure.py
|
| 78 |
+
conda run -n $(CONDA_ENV) --no-capture-output python scripts/make_peak_solar_figure.py
|
| 79 |
+
conda run -n $(CONDA_ENV) --no-capture-output python scripts/make_terramechanics_experiment_figure.py
|
| 80 |
+
conda run -n $(CONDA_ENV) --no-capture-output python scripts/make_terramechanics_sensitivity_figure.py
|
| 81 |
+
conda run -n $(CONDA_ENV) --no-capture-output python scripts/make_architecture_obstacle_crossover_figure.py
|
| 82 |
+
|
| 83 |
+
# Manually deploy the webapp to the dedicated Hugging Face Space (Docker
|
| 84 |
+
# SDK). Pushes the current committed HEAD; it does NOT run on git push.
|
| 85 |
+
# Requires HF_SPACE_REMOTE to point at the Space git URL and git-lfs to
|
| 86 |
+
# be installed (the ~26 MB surrogate bundle exceeds HF's plain-git limit).
|
| 87 |
+
# See scripts/deploy_hf_space.sh for the full contract.
|
| 88 |
+
deploy-space:
|
| 89 |
+
bash scripts/deploy_hf_space.sh
|
README.md
CHANGED
|
@@ -1,11 +1,28 @@
|
|
| 1 |
---
|
| 2 |
-
title:
|
| 3 |
-
emoji:
|
| 4 |
-
colorFrom:
|
| 5 |
-
colorTo:
|
| 6 |
sdk: docker
|
|
|
|
| 7 |
pinned: false
|
| 8 |
license: mit
|
| 9 |
---
|
| 10 |
|
| 11 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
---
|
| 2 |
+
title: RoverDevKit
|
| 3 |
+
emoji: 🛰️
|
| 4 |
+
colorFrom: gray
|
| 5 |
+
colorTo: blue
|
| 6 |
sdk: docker
|
| 7 |
+
app_port: 8000
|
| 8 |
pinned: false
|
| 9 |
license: mit
|
| 10 |
---
|
| 11 |
|
| 12 |
+
# RoverDevKit — hosted demo
|
| 13 |
+
|
| 14 |
+
Interactive tradespace explorer for conceptual design of lunar micro-rovers:
|
| 15 |
+
physics-based mission evaluator, calibrated surrogate predictions, parametric
|
| 16 |
+
sweeps, NSGA-II multi-objective optimization, and SHAP-style design
|
| 17 |
+
explanations.
|
| 18 |
+
|
| 19 |
+
- Source code: <https://github.com/Autonomous-Mission-Systems-Lab/roverdevkit>
|
| 20 |
+
- Paper preprint: <https://arxiv.org/abs/2606.21755>
|
| 21 |
+
|
| 22 |
+
This Space runs the single-container build from
|
| 23 |
+
[`webapp/Dockerfile`](https://github.com/Autonomous-Mission-Systems-Lab/roverdevkit/blob/main/webapp/Dockerfile):
|
| 24 |
+
one `uvicorn` process serves the FastAPI backend and the React single-page app
|
| 25 |
+
from the same origin on port 8000.
|
| 26 |
+
|
| 27 |
+
> This README (with its Spaces front matter) is generated for the hosted demo
|
| 28 |
+
> by `scripts/deploy_hf_space.sh` and is not the repository's main README.
|
data/README.md
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Data
|
| 2 |
+
|
| 3 |
+
Small, curated datasets and citations live here. Large generated datasets
|
| 4 |
+
(LHS samples) are git-ignored — see `.gitignore`.
|
| 5 |
+
|
| 6 |
+
## Rover data
|
| 7 |
+
|
| 8 |
+
Three consumers describe the same set of rovers for verification, each holding
|
| 9 |
+
*purpose-specific* values, all reconciled against one canonical facts file:
|
| 10 |
+
|
| 11 |
+
- `rovers.yaml` — **canonical published-facts reference** (single source of
|
| 12 |
+
truth). Holds only published/citable facts (mass, wheels, grousers, landing
|
| 13 |
+
latitude, traverse/peak-solar/thermal truth, ...) with **per-field
|
| 14 |
+
provenance** (`value` + `provenance` ∈ {published, derived, imputed} +
|
| 15 |
+
`source`). Loaded by `roverdevkit/validation/rover_facts.py`. It deliberately
|
| 16 |
+
excludes modeling-derived quantities (chassis mass, torque anchor, panel
|
| 17 |
+
efficiency, thermal architecture, scenario duty cycles) that legitimately
|
| 18 |
+
differ per consumer. `tests/test_rover_facts.py` enforces that the consumers
|
| 19 |
+
below agree with the `published`/`derived` facts here, so the sources cannot
|
| 20 |
+
silently drift.
|
| 21 |
+
- `mass_validation_set.csv` — published-rover mass and subsystem inputs used
|
| 22 |
+
by `roverdevkit/mass/validation.py` to check the bottom-up mass model.
|
| 23 |
+
Source/provenance details live in each row's `citation` and `imputation_notes`.
|
| 24 |
+
- `published_traverse_data.csv` — flown-rover traverse, peak-solar, thermal,
|
| 25 |
+
and mission-duration truth data used by `roverdevkit/validation/rover_comparison.py`.
|
| 26 |
+
Source details live in each row's `citation` and `notes`.
|
| 27 |
+
- `roverdevkit/validation/rover_registry.py` (code, not data) — executable
|
| 28 |
+
design vectors + scenarios + thermal/panel architecture consumed by the
|
| 29 |
+
evaluator, rediscovery, surrogate sanity check, and webapp.
|
| 30 |
+
|
| 31 |
+
## Other files
|
| 32 |
+
- `soil_simulants.csv` — Bekker parameters (n, k_c, k_phi, cohesion,
|
| 33 |
+
friction angle) for common lunar soil simulants: FJS-1, JSC-1A, GRC-1,
|
| 34 |
+
plus Apollo regolith estimates.
|
| 35 |
+
- `validation/` — single-wheel testbed data digitized from published
|
| 36 |
+
papers (Ding 2011, Iizuka & Kubota 2011, Wong's datasets). Used as
|
| 37 |
+
held-out data to sanity-check the evaluator — never used for training.
|
| 38 |
+
- `analytical/` — generated LHS samples from the analytical evaluator.
|
| 39 |
+
Git-ignored except for schema documentation.
|
| 40 |
+
|
| 41 |
+
## Citation discipline
|
| 42 |
+
|
| 43 |
+
Every curated data row must carry a citation or provenance note. Prefer the
|
| 44 |
+
canonical `rovers.yaml` per-field provenance for published rover facts; use the
|
| 45 |
+
dedicated `citation` column where present; otherwise document sources and
|
| 46 |
+
imputations in `notes` / `imputation_notes`. If you can't cite it, don't fit
|
| 47 |
+
on it.
|
data/analytical/.gitkeep
ADDED
|
File without changes
|
data/analytical/SCHEMA.md
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Analytical Dataset Column Schema
|
| 2 |
+
|
| 3 |
+
Produced by `roverdevkit.surrogate.dataset.build_dataset` from
|
| 4 |
+
`LHSSample`s generated by `roverdevkit.surrogate.sampling.generate_samples`.
|
| 5 |
+
Each row is **one** `(design, scenario, soil)` triple evaluated by
|
| 6 |
+
`roverdevkit.mission.evaluator.evaluate_verbose`, flattened into a
|
| 7 |
+
single Parquet row.
|
| 8 |
+
|
| 9 |
+
- **Schema version:** `v9` (see `dataset.SCHEMA_VERSION`). Scientific
|
| 10 |
+
payload is an explicit mission requirement, carried by the
|
| 11 |
+
scenario-side inputs `scenario_payload_mass_kg` and
|
| 12 |
+
`scenario_payload_power_w`. Payload mass enters the total vehicle
|
| 13 |
+
mass as a line item outside the AIAA S-120A dry-mass growth margin
|
| 14 |
+
(`m_total = m_dry + m_margin + m_payload`); payload power adds to the
|
| 15 |
+
continuous ops-time electrical load (alongside avionics) and to the
|
| 16 |
+
hot-case thermal dissipation. Both are sampled uniform and
|
| 17 |
+
family-agnostic (`payload_mass_kg` in `[0, 30]`,
|
| 18 |
+
`payload_power_w` in `[0, 30]`) so the entire webapp Mission-Inputs
|
| 19 |
+
slider range is in-distribution. Payload lives on `MissionScenario`
|
| 20 |
+
(a requirement set by the mission), not on `DesignVector` (a variable
|
| 21 |
+
the designer trades); the design vector is 11-D. A per-call override
|
| 22 |
+
on `evaluate` / `/evaluate` / `/predict` lets callers substitute a
|
| 23 |
+
specific payload.
|
| 24 |
+
- **Fidelity level (this file):** `analytical` — the Bekker-Wong
|
| 25 |
+
terramechanics path solved inside `traverse_sim.run_traverse`.
|
| 26 |
+
- **Canonical filename:** `lhs_v9.parquet` — the current training set,
|
| 27 |
+
40k rows at 10k × 4 scenario families. Pilot (`lhs_pilot.parquet`)
|
| 28 |
+
and challenge (`challenge_v1.parquet`) files are generated on demand
|
| 29 |
+
from `scripts/build_dataset.py`; only the canonical training set is
|
| 30 |
+
treated as a tracked artifact.
|
| 31 |
+
|
| 32 |
+
Dataset-level metadata is written to the Parquet file's schema footer;
|
| 33 |
+
use `read_parquet_metadata(path)` to recover it (seed, sampler version,
|
| 34 |
+
scenario families, val/test fractions, UTC build timestamp, evaluator
|
| 35 |
+
version, free-form notes).
|
| 36 |
+
|
| 37 |
+
## Column groups
|
| 38 |
+
|
| 39 |
+
Prefix conventions:
|
| 40 |
+
|
| 41 |
+
- `design_*` — inputs from the 11-D `DesignVector`.
|
| 42 |
+
- `scenario_*` — inputs from the `MissionScenario`, plus the sampler's
|
| 43 |
+
jittered Bekker soil parameters (`scenario_soil_*`).
|
| 44 |
+
- `stat_*` — aggregate statistics (mean / p95 / max / final) reduced
|
| 45 |
+
from the per-step `TraverseLog` time series.
|
| 46 |
+
- Unprefixed columns with physical units (e.g. `range_km`) — targets
|
| 47 |
+
from `MissionMetrics`.
|
| 48 |
+
- Otherwise — dataset metadata.
|
| 49 |
+
|
| 50 |
+
### Dataset metadata (5 columns)
|
| 51 |
+
|
| 52 |
+
| Column | dtype | Description |
|
| 53 |
+
| --- | --- | --- |
|
| 54 |
+
| `sample_index` | int64 | Monotonic row id from the sampler. Stable across re-runs with the same seed. |
|
| 55 |
+
| `split` | category | `train` / `val` / `test`, assigned at sample time with a deterministic RNG independent of row ordering. |
|
| 56 |
+
| `stratum_id` | int | `0 = 4-wheel`, `1 = 6-wheel`. Matches `design_n_wheels`. |
|
| 57 |
+
| `fidelity` | category | `analytical` for this file — the Bekker-Wong terramechanics path. No separate fidelity tier is shipped. |
|
| 58 |
+
| `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`. |
|
| 59 |
+
|
| 60 |
+
### Design vector (11 columns)
|
| 61 |
+
|
| 62 |
+
All `design_*` columns mirror the `DesignVector` pydantic schema.
|
| 63 |
+
|
| 64 |
+
| Column | dtype | Range | Description |
|
| 65 |
+
| --- | --- | --- | --- |
|
| 66 |
+
| `design_wheel_radius_m` | float64 | [0.05, 0.20] | Wheel radius R |
|
| 67 |
+
| `design_wheel_width_m` | float64 | [0.03, 0.20] | Wheel width W |
|
| 68 |
+
| `design_grouser_height_m` | float64 | [0.0, 0.020] | Grouser height |
|
| 69 |
+
| `design_grouser_count` | int64 | [0, 24] | Number of grousers per wheel |
|
| 70 |
+
| `design_n_wheels` | int64 | {4, 6} | Wheel count (kept in sync with architecture) |
|
| 71 |
+
| `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. |
|
| 72 |
+
| `design_chassis_mass_kg` | float64 | [0.5, 50.0] | Dry chassis mass (structural chassis only) |
|
| 73 |
+
| `design_wheelbase_m` | float64 | [0.3, 1.2] | Wheelbase |
|
| 74 |
+
| `design_solar_area_m2` | float64 | [0.1, 1.5] | Solar array area |
|
| 75 |
+
| `design_battery_capacity_wh` | float64 | [5.0, 500.0] | Usable battery energy |
|
| 76 |
+
| `design_avionics_power_w` | float64 | [5.0, 40.0] | Continuous avionics draw |
|
| 77 |
+
| `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`. |
|
| 78 |
+
|
| 79 |
+
### Scenario inputs (18 columns)
|
| 80 |
+
|
| 81 |
+
Family-fixed columns (`scenario_family`, `scenario_terrain_class`,
|
| 82 |
+
`scenario_soil_simulant`, `scenario_sun_geometry`,
|
| 83 |
+
`scenario_traverse_distance_m`) take one of four canonical values per
|
| 84 |
+
family. The remaining columns are jittered per sample.
|
| 85 |
+
|
| 86 |
+
| Column | dtype | Notes |
|
| 87 |
+
| --- | --- | --- |
|
| 88 |
+
| `scenario_family` | category | One of `equatorial_mare_traverse`, `polar_prospecting`, `highland_slope_capability`, `crater_rim_survey`. Use for per-scenario accuracy breakdown. |
|
| 89 |
+
| `scenario_name` | category | Mirrors `scenario_family` in this dataset (validation-only scenarios live elsewhere). |
|
| 90 |
+
| `scenario_latitude_deg` | float64 | Family-specific range; see `sampling.FAMILIES`. |
|
| 91 |
+
| `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. |
|
| 92 |
+
| `scenario_terrain_class` | category | `mare_nominal`, `mare_loose`, `highland_dense`, `polar_regolith`. |
|
| 93 |
+
| `scenario_soil_simulant` | category | Family nominal; the *actual* Bekker numbers used by the evaluator are the `scenario_soil_*` columns below. |
|
| 94 |
+
| `scenario_mission_duration_earth_days` | float64 | Family-specific range. |
|
| 95 |
+
| `scenario_max_slope_deg` | float64 | Family-specific range. |
|
| 96 |
+
| `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. |
|
| 97 |
+
| `scenario_sun_geometry` | category | `continuous` / `diurnal` / `polar_intermittent`. |
|
| 98 |
+
| `scenario_soil_n` | float64 | Bekker sinkage exponent, jitter bounds [0.8, 1.2]. |
|
| 99 |
+
| `scenario_soil_k_c` | float64 | Cohesive modulus, [0.5, 2.0] kN/m^(n+1). |
|
| 100 |
+
| `scenario_soil_k_phi` | float64 | Frictional modulus, [400, 1200] kN/m^(n+2). |
|
| 101 |
+
| `scenario_soil_cohesion_kpa` | float64 | Soil cohesion, [0.1, 1.0] kPa. |
|
| 102 |
+
| `scenario_soil_friction_angle_deg` | float64 | Internal friction angle, [30, 50]°. |
|
| 103 |
+
| `scenario_soil_shear_modulus_k_m` | float64 | Janosi-Hanamoto K, [0.010, 0.025] m. |
|
| 104 |
+
| `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. |
|
| 105 |
+
| `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. |
|
| 106 |
+
| `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. |
|
| 107 |
+
|
| 108 |
+
### Mission-metric targets (8 columns)
|
| 109 |
+
|
| 110 |
+
Mirror `MissionMetrics` fields. `range_km` and `energy_margin_raw_pct`
|
| 111 |
+
are the primary regression targets (no saturation); `*_pct` and the
|
| 112 |
+
boolean flag are secondary reporting/classification targets.
|
| 113 |
+
`thermal_survival` is **not** in this group: the evaluator still
|
| 114 |
+
computes it as a diagnostic, but the mass model treats RHU power and
|
| 115 |
+
MLI quality as free, so it reduces to a near-trivial gate with no real
|
| 116 |
+
design trade-off and the surrogate does not consume or predict it.
|
| 117 |
+
|
| 118 |
+
| Column | dtype | Notes |
|
| 119 |
+
| --- | --- | --- |
|
| 120 |
+
| `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. |
|
| 121 |
+
| `energy_margin_pct` | float64 | Clipped 0-100, SOC-based reporting metric. |
|
| 122 |
+
| `energy_margin_raw_pct` | float64 | Unclipped mission-integrated `(E_in - E_out)/E_out × 100`; primary surrogate target. |
|
| 123 |
+
| `slope_capability_deg` | float64 | Max climbable slope on this soil. |
|
| 124 |
+
| `total_mass_kg` | float64 | Mass-model output. |
|
| 125 |
+
| `peak_motor_torque_nm` | float64 | Observed peak wheel torque during traverse. |
|
| 126 |
+
| `sinkage_max_m` | float64 | Observed peak sinkage during traverse. |
|
| 127 |
+
| `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). |
|
| 128 |
+
|
| 129 |
+
Evaluator-only architecture metrics (present on live `/evaluate` and optimizer outputs, not in the shipped `lhs_v9.parquet` targets):
|
| 130 |
+
|
| 131 |
+
| Column | dtype | Notes |
|
| 132 |
+
| --- | --- | --- |
|
| 133 |
+
| `obstacle_capability_m` | float64 | Estimated max traversable obstacle height from architecture proxy ($h_{\mathrm{obs}} = k_{\mathrm{arch}} R$). |
|
| 134 |
+
| `obstacle_margin_m` | float64 | Capability minus `scenario_required_obstacle_height_m`. |
|
| 135 |
+
| `obstacle_requirement_met` | bool | Whether `obstacle_margin_m \ge 0`. |
|
| 136 |
+
| `architecture_mass_kg` | float64 | Rocker-bogie suspension/linkage mass charged in the bottom-up mass model. |
|
| 137 |
+
|
| 138 |
+
### Traverse-log aggregate statistics (≥24 columns)
|
| 139 |
+
|
| 140 |
+
Reduced from the per-step `TraverseLog` time series. Used to measure
|
| 141 |
+
where the surrogate needs to be accurate (peak-load versus
|
| 142 |
+
steady-state regimes) and as auxiliary diagnostics for the baselines.
|
| 143 |
+
|
| 144 |
+
Numeric aggregates (mean / p95 / max over the whole traverse, absolute
|
| 145 |
+
value for signed quantities like slip and torque):
|
| 146 |
+
|
| 147 |
+
- `stat_power_in_{mean,p95,max}_w` — solar input power.
|
| 148 |
+
- `stat_power_out_{mean,p95,max}_w` — total electrical draw (mobility + avionics).
|
| 149 |
+
- `stat_mobility_power_{mean,p95,max}_w` — mobility subsystem draw alone.
|
| 150 |
+
- `stat_slip_{mean,p95,max}` — wheel slip magnitude in [0, ~0.95].
|
| 151 |
+
- `stat_sinkage_{mean,p95}_m` — peak is already `sinkage_max_m` above.
|
| 152 |
+
- `stat_wheel_torque_{mean,p95}_nm` — peak is already `peak_motor_torque_nm` above.
|
| 153 |
+
- `stat_sun_elevation_{mean,max}_deg` — degrees above horizon.
|
| 154 |
+
- `stat_soc_final` / `stat_soc_min` — end-of-mission and deepest SOC.
|
| 155 |
+
|
| 156 |
+
Boolean end-of-run flags:
|
| 157 |
+
|
| 158 |
+
- `stat_rover_stalled` — Brent slip solve failed at some step.
|
| 159 |
+
- `stat_battery_floored` — SOC hit the 15% DoD floor during the run.
|
| 160 |
+
- `stat_reached_distance` — the rover reached the scenario's (non-binding) distance budget.
|
| 161 |
+
|
| 162 |
+
Categorical:
|
| 163 |
+
|
| 164 |
+
- `stat_terminated_reason` — a free-form short string from the sim layer
|
| 165 |
+
(e.g. `"mission_duration"`, `"evaluator_error"`). Use as a
|
| 166 |
+
post-hoc diagnostic; not suitable as a model input.
|
| 167 |
+
|
| 168 |
+
## Layer-1 registry sanity scope
|
| 169 |
+
|
| 170 |
+
`roverdevkit.surrogate.baselines.predict_for_registry_rovers` produces
|
| 171 |
+
a `registry_sanity.csv` artifact with one row per `(rover, algorithm,
|
| 172 |
+
target)` tuple plus an `is_primary` boolean. The split is enforced
|
| 173 |
+
by the `LAYER1_PRIMARY_TARGETS` / `LAYER1_DIAGNOSTIC_TARGETS`
|
| 174 |
+
constants in `roverdevkit/surrogate/baselines.py`:
|
| 175 |
+
|
| 176 |
+
- **Primary (is_primary=True):** `total_mass_kg`,
|
| 177 |
+
`slope_capability_deg`, `stalled`. Design-axis metrics where the LHS
|
| 178 |
+
bounds put every flown / design-target rover inside the surrogate's
|
| 179 |
+
training support. Treated as the main registry sanity set.
|
| 180 |
+
- **Diagnostic (is_primary=False):** `range_km`,
|
| 181 |
+
`energy_margin_raw_pct`. Both are scenario-OOD for the registry:
|
| 182 |
+
Pragyan ≈ 100 m, Yutu-2 ≈ 25 m / lunar day, MoonRanger and
|
| 183 |
+
Rashid-1 ≈ 1 km published mission distances against LHS family
|
| 184 |
+
budgets of 20–80 km (intentionally non-binding so `range_km` stays
|
| 185 |
+
a continuous training signal). The relative error for these
|
| 186 |
+
targets is dominated by the absolute-scale mismatch and reflects
|
| 187 |
+
scenario-OOD rather than a surrogate-calibration failure. Reported
|
| 188 |
+
for transparency only.
|
| 189 |
+
|
| 190 |
+
Slope MAPE on Pragyan and MoonRanger runs elevated relative to mass:
|
| 191 |
+
published rover slope-capability specs come from real-rover-specific
|
| 192 |
+
design choices the analytical Bekker-Wong kernel's feature space cannot
|
| 193 |
+
fully resolve.
|
| 194 |
+
|
| 195 |
+
## Column count sanity
|
| 196 |
+
|
| 197 |
+
Metadata (5) + design (11) + scenario (18) + metrics (8) + stats (≥24)
|
| 198 |
+
= ≥66 columns at `SCHEMA_VERSION = v9`. Future versions appending,
|
| 199 |
+
removing, or re-binding columns — *or* changing the LHS support so a
|
| 200 |
+
surrogate trained on one version would be OOD on the next — *must* bump
|
| 201 |
+
`SCHEMA_VERSION` so downstream code can detect a mismatch.
|
data/mass_validation_set.csv
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
rover_name,mass_total_kg,wheel_radius_m,wheel_width_m,n_wheels,chassis_mass_kg,solar_area_m2,battery_capacity_wh,avionics_power_w,grouser_height_m,grouser_count,payload_mass_kg,in_class,citation,imputation_notes
|
| 2 |
+
Rashid,10.0,0.10,0.08,4,2.0,0.4,50.0,10.0,0.015,14,1.5,true,"Hurrell et al. 2025 Space Science Reviews 221:37; Els et al. LPSC 2021 #1905; MBRSC Emirates Lunar Mission materials","wheel_width, grouser_height_m, and grouser_count updated to Hurrell et al. 2025 flight-wheel values; chassis is structural-only (35%-of-m_total ROT bucket minus the science payload); battery + avionics scaled from mass class. payload_mass_kg ~ 1.5 kg = 4 cameras + microscopic imager + Langmuir probe per Emirates Lunar Mission (Rashid) instrument list (schema v9: payload is a separate mission requirement, no longer folded into chassis)"
|
| 3 |
+
Sojourner,10.6,0.065,0.08,6,2.0,0.22,40.0,10.0,0.010,12,1.5,true,"Wilcox & Nguyen 1998; NASA Mars Pathfinder / Sojourner rover mission materials","chassis structural-only ~ 33%-of-m_total per Wilcox & Nguyen 1998 minus payload; grouser_count estimated at 12; Mars rover used as lunar-micro proxy (gravity correction pending real-rover validation). payload_mass_kg ~ 1.5 kg = APXS (~0.55 kg) + 3 cameras + electronics"
|
| 4 |
+
CADRE-unit,2.0,0.08,0.04,4,0.5,0.1,10.0,5.0,0.0,0,0.3,false,"Rothenbuchner et al. 2023 IEEE Aerospace #2300; NASA/JPL CADRE project materials","In the design-space class after the 2026-05-27 schema floor widening (chassis 0.5 kg / torque 0.05 Nm / battery 5 Wh), but OUT of the bottom-up mass model's calibration regime — MassModelParams specific-mass constants are calibrated to 5-50 kg micro-rovers, and at 2 kg the fixed-cost terms (4 x motor_base 0.15 kg + avionics_base 0.3 kg + ...) over-predict total mass by ~100%. Specs per Rothenbuchner 2023 IEEE Aerospace + NASA/JPL CADRE press. chassis structural-only ~ 40%-of-m_total ROT minus payload; no grousers (smooth wire-spoke rims). payload_mass_kg ~ 0.3 kg = stereo-camera + small comms-ranging payload"
|
| 5 |
+
Resilience-Tenacious,5.0,0.06,0.04,4,1.7,0.15,25.0,8.0,0.005,12,0.3,true,"ispace HAKUTO-R Mission 2 mission overview and press materials; ispace Mission 2 updates","In-class after the 2026-05-27 ultra-micro floor widening. Specs per iSpace HAKUTO-R M2 mission overview; chassis structural-only ~ 40%-of-m_total ROT minus payload; small grousers visible in iSpace press imagery. payload_mass_kg ~ 0.3 kg = HD camera payload"
|
| 6 |
+
ExoMy,8.0,0.055,0.06,6,3.0,0.10,30.0,10.0,0.008,12,0.0,true,"ESA ExoMy open-hardware documentation","Earth educational rover; chassis = 37.5% of m_total; specs approximate per ESA ExoMy docs. payload_mass_kg = 0 (educational platform, no dedicated science instrument)"
|
| 7 |
+
Pragyan,26.0,0.085,0.07,6,6.5,0.5,60.0,20.0,0.008,12,3.5,true,"ISRO Chandrayaan-3 press materials; Chandrayaan-3 Pragyan instrument-suite materials","wheel_width, solar_area, battery, avionics all scaled from class and mission duration (6 lunar hours); chassis structural-only ~ 38%-of-m_total ROT minus payload. payload_mass_kg ~ 3.5 kg = APXS + LIBS spectrometers per Chandrayaan-3 Pragyan instrument suite"
|
| 8 |
+
Yutu-2,135.0,0.15,0.15,6,45.0,1.3,130.0,40.0,0.0,0,25.0,false,"Di et al. 2020 Icarus; Ding et al. 2022 Acta Astronautica; Chang'e-4 / Yutu-2 payload manifest","out-of-class (medium, >50 kg ceiling); chassis structural-only = published-derived bucket minus the ~25 kg science payload. payload_mass_kg ~ 25 kg = Lunar Penetrating Radar + VNIS + APXS + panoramic/navigation cameras per Chang'e-4 Yutu-2 payload manifest; solar deployable 2-wing"
|
| 9 |
+
MARSOKHOD-proto,70.0,0.17,0.13,6,24.0,0.5,100.0,30.0,0.015,12,8.0,false,"Kemurjian et al. 1993","out-of-class (medium); chassis structural-only ~ 46%-of-m_total ROT minus payload; specs per Kemurjian et al. 1993. payload_mass_kg ~ 8 kg = instrument mast + manipulator/sampling proto payload"
|
data/published_traverse_data.csv
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
rover_name,scenario_name,traverse_m_published,traverse_m_low,traverse_m_high,peak_solar_power_w_published,peak_solar_power_w_low,peak_solar_power_w_high,thermal_survival_published,mission_duration_published_days,citation,notes
|
| 2 |
+
Pragyan,chandrayaan3_pragyan,101.4,80.0,140.0,50.0,40.0,70.0,false,10.0,"ISRO Chandrayaan-3 mission updates (Aug-Sep 2023); Nature SR 14:24178 (2024)","Traverse over Lunar Day 1 only. Rover did NOT survive lunar night (no RHUs); thermal_survival_published=false matches the sim's full-mission hot+cold steady-state check. traverse_m_published is the in-mission total."
|
| 3 |
+
Yutu-2,change4_yutu2_per_lunar_day,25.0,10.0,60.0,135.0,110.0,160.0,true,5.0,"Di et al. 2020 Icarus; Ding et al. 2022 Acta Astronautica; CNSA dispatches","Per-lunar-day drive distance, first ~2 years. Yutu-2 carries Pu-238 RHUs for lunar-night survival (surviving 60+ lunar days as of 2025); thermal_survival_published=true conditional on registry's RHU-carrying architecture."
|
data/rovers.yaml
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Canonical published-facts reference for the rovers used in verification.
|
| 2 |
+
#
|
| 3 |
+
# Purpose
|
| 4 |
+
# -------
|
| 5 |
+
# Single source of truth for the *published, citable facts* about each real
|
| 6 |
+
# (or reference) rover. The mass-model validation set
|
| 7 |
+
# (``data/mass_validation_set.csv``), the flown-rover truth table
|
| 8 |
+
# (``data/published_traverse_data.csv``), and the executable design registry
|
| 9 |
+
# (``roverdevkit/validation/rover_registry.py``) all describe these same
|
| 10 |
+
# rovers; this file is where the underlying facts live so the three consumers
|
| 11 |
+
# cannot silently drift apart. ``tests/test_rover_facts.py`` enforces that the
|
| 12 |
+
# consumers agree with the published/derived facts recorded here.
|
| 13 |
+
#
|
| 14 |
+
# Facts vs modeling values
|
| 15 |
+
# ------------------------
|
| 16 |
+
# This file holds ONLY published facts. It deliberately does NOT hold
|
| 17 |
+
# modeling-derived quantities that legitimately differ per consumer:
|
| 18 |
+
# - chassis_mass_kg (back-solved differently by the mass model and
|
| 19 |
+
# the evaluator registry)
|
| 20 |
+
# - avionics_power_w (steady-state vs peak, model-specific)
|
| 21 |
+
# - peak_wheel_torque_nm (sizing anchor)
|
| 22 |
+
# - panel_efficiency / panel_dust_factor / thermal architecture
|
| 23 |
+
# - scenario duty cycles, slopes, soil simulants
|
| 24 |
+
# Those stay in their respective consumers.
|
| 25 |
+
#
|
| 26 |
+
# Per-field provenance
|
| 27 |
+
# --------------------
|
| 28 |
+
# Every leaf is ``{value, provenance, source}``:
|
| 29 |
+
# - provenance: published -> taken directly from the cited source
|
| 30 |
+
# derived -> computed from other published values
|
| 31 |
+
# imputed -> estimated (class heritage / back-solve); the
|
| 32 |
+
# source field explains the basis
|
| 33 |
+
# Only ``published`` and ``derived`` fields are consistency-enforced against
|
| 34 |
+
# the consumers; ``imputed`` fields may differ per model.
|
| 35 |
+
#
|
| 36 |
+
# Latitude sign convention: positive = lunar north, negative = lunar south.
|
| 37 |
+
|
| 38 |
+
schema_version: 1
|
| 39 |
+
|
| 40 |
+
rovers:
|
| 41 |
+
- name: Pragyan
|
| 42 |
+
aliases: []
|
| 43 |
+
status: { value: flown, provenance: published, source: "ISRO Chandrayaan-3 mission updates (Aug-Sep 2023)" }
|
| 44 |
+
agency: { value: ISRO, provenance: published, source: "ISRO Chandrayaan-3 press materials" }
|
| 45 |
+
launch_year: { value: 2023, provenance: published, source: "ISRO Chandrayaan-3 (launch Jul 2023, landing 23 Aug 2023)" }
|
| 46 |
+
landing_latitude_deg: { value: -69.4, provenance: published, source: "ISRO Chandrayaan-3 press materials (~69.37 S, Shiv Shakti point)" }
|
| 47 |
+
landing_site: { value: "South polar highlands", provenance: published, source: "ISRO Chandrayaan-3 press materials" }
|
| 48 |
+
mass_total_kg: { value: 26.0, provenance: published, source: "ISRO Chandrayaan-3 press kit" }
|
| 49 |
+
n_wheels: { value: 6, provenance: published, source: "ISRO Chandrayaan-3 press kit" }
|
| 50 |
+
wheel_radius_m: { value: 0.085, provenance: published, source: "ISRO Chandrayaan-3 press materials (~170 mm wheel dia.)" }
|
| 51 |
+
wheel_width_m: { value: 0.07, provenance: imputed, source: "scaled from 6-wheel geometry (not published)" }
|
| 52 |
+
grouser_height_m: { value: 0.008, provenance: imputed, source: "class heritage (Yutu/Rashid-style 8 mm grousers)" }
|
| 53 |
+
grouser_count: { value: 12, provenance: imputed, source: "class heritage" }
|
| 54 |
+
wheelbase_m: { value: 0.5, provenance: imputed, source: "estimated from published rover imagery" }
|
| 55 |
+
solar_area_m2: { value: 0.5, provenance: imputed, source: "power-budget back-solve (single lunar-day ops)" }
|
| 56 |
+
battery_capacity_wh: { value: 60.0, provenance: imputed, source: "scaled from mass class and mission duration" }
|
| 57 |
+
payload_mass_kg: { value: 3.5, provenance: published, source: "Chandrayaan-3 Pragyan instrument suite (APXS + LIBS)" }
|
| 58 |
+
truth:
|
| 59 |
+
scenario_name: { value: chandrayaan3_pragyan, provenance: derived, source: "validation scenario key" }
|
| 60 |
+
traverse_m: { value: 101.4, low: 80.0, high: 140.0, provenance: published, source: "ISRO Chandrayaan-3 mission updates (Aug-Sep 2023); Nature SR 14:24178 (2024)" }
|
| 61 |
+
peak_solar_power_w: { value: 50.0, low: 40.0, high: 70.0, provenance: published, source: "ISRO Chandrayaan-3 power-system reporting" }
|
| 62 |
+
thermal_survival: { value: false, provenance: published, source: "No RHUs; rover did not survive lunar night" }
|
| 63 |
+
mission_duration_days: { value: 10.0, provenance: published, source: "Lunar Day 1 operations only" }
|
| 64 |
+
|
| 65 |
+
- name: Yutu-2
|
| 66 |
+
aliases: []
|
| 67 |
+
status: { value: flown, provenance: published, source: "CNSA Chang'e-4 mission dispatches" }
|
| 68 |
+
agency: { value: CNSA, provenance: published, source: "CNSA Chang'e-4 program" }
|
| 69 |
+
launch_year: { value: 2018, provenance: published, source: "Chang'e-4 launched 7 Dec 2018 (landing 3 Jan 2019)" }
|
| 70 |
+
landing_latitude_deg: { value: -45.5, provenance: published, source: "Di et al. 2020 Icarus (45.44 S, Von Karman crater, SPA basin)" }
|
| 71 |
+
landing_site: { value: "Von Karman crater, South Pole-Aitken basin (farside)", provenance: published, source: "Di et al. 2020 Icarus" }
|
| 72 |
+
mass_total_kg: { value: 135.0, provenance: published, source: "Di et al. 2020 Icarus; Ding et al. 2022 Acta Astronautica" }
|
| 73 |
+
n_wheels: { value: 6, provenance: published, source: "Di et al. 2020 Icarus" }
|
| 74 |
+
wheel_radius_m: { value: 0.15, provenance: published, source: "Di et al. 2020 Icarus (~300 mm wheel dia.)" }
|
| 75 |
+
wheel_width_m: { value: 0.15, provenance: published, source: "Di et al. 2020 Icarus" }
|
| 76 |
+
grouser_height_m: { value: 0.012, provenance: imputed, source: "Yutu-class grousered wheels (estimated from imagery)" }
|
| 77 |
+
grouser_count: { value: 18, provenance: imputed, source: "estimated from published wheel imagery" }
|
| 78 |
+
wheelbase_m: { value: 1.0, provenance: imputed, source: "estimated from published rover imagery" }
|
| 79 |
+
solar_area_m2: { value: 1.3, provenance: imputed, source: "two-wing deployable array (estimated)" }
|
| 80 |
+
battery_capacity_wh: { value: 130.0, provenance: imputed, source: "Li-ion pack (estimated from class)" }
|
| 81 |
+
payload_mass_kg: { value: 25.0, provenance: published, source: "Chang'e-4 Yutu-2 payload manifest (LPR + VNIS + APXS + cameras)" }
|
| 82 |
+
truth:
|
| 83 |
+
scenario_name: { value: change4_yutu2_per_lunar_day, provenance: derived, source: "validation scenario key" }
|
| 84 |
+
traverse_m: { value: 25.0, low: 10.0, high: 60.0, provenance: published, source: "Di et al. 2020 Icarus; CNSA dispatches (per-lunar-day drive)" }
|
| 85 |
+
peak_solar_power_w: { value: 135.0, low: 110.0, high: 160.0, provenance: published, source: "Chang'e-4 Yutu-2 power-system reporting" }
|
| 86 |
+
thermal_survival: { value: true, provenance: published, source: "Pu-238 RHUs; survived 60+ lunar days" }
|
| 87 |
+
mission_duration_days: { value: 5.0, provenance: imputed, source: "active-ops window per lunar day (not full 14 days)" }
|
| 88 |
+
|
| 89 |
+
- name: Rashid
|
| 90 |
+
aliases: [Rashid-1]
|
| 91 |
+
status: { value: lost_on_landing, provenance: published, source: "Lost on Hakuto-R Mission 1 lander failure (Apr 2023)" }
|
| 92 |
+
agency: { value: MBRSC/UAE, provenance: published, source: "MBRSC Emirates Lunar Mission" }
|
| 93 |
+
launch_year: { value: 2022, provenance: published, source: "Launched Dec 2022 on Hakuto-R Mission 1" }
|
| 94 |
+
landing_latitude_deg: { value: 47.5, provenance: published, source: "MBRSC Emirates Lunar Mission (Atlas Crater, 47.5 N 44.4 E, Mare Frigoris)" }
|
| 95 |
+
landing_site: { value: "Atlas Crater, Mare Frigoris", provenance: published, source: "MBRSC Emirates Lunar Mission" }
|
| 96 |
+
mass_total_kg: { value: 10.0, provenance: published, source: "Hurrell et al. 2025 Space Science Reviews 221:37" }
|
| 97 |
+
n_wheels: { value: 4, provenance: published, source: "Hurrell et al. 2025 SSR 221:37" }
|
| 98 |
+
wheel_radius_m: { value: 0.10, provenance: published, source: "Hurrell et al. 2025 SSR 221:37 (radius 100 mm)" }
|
| 99 |
+
wheel_width_m: { value: 0.08, provenance: published, source: "Hurrell et al. 2025 SSR 221:37 (width 80 mm)" }
|
| 100 |
+
grouser_height_m: { value: 0.015, provenance: published, source: "Hurrell et al. 2025 SSR 221:37 (15 mm flight grouser)" }
|
| 101 |
+
grouser_count: { value: 14, provenance: published, source: "Hurrell et al. 2025 SSR 221:37" }
|
| 102 |
+
wheelbase_m: { value: 0.50, provenance: published, source: "Els et al. LPSC 2021 #1905 (footprint 0.535 x 0.539 m)" }
|
| 103 |
+
solar_area_m2: { value: 0.25, provenance: imputed, source: "power-budget back-solve (0.5 x 0.5 m chassis)" }
|
| 104 |
+
battery_capacity_wh: { value: 50.0, provenance: imputed, source: "class-typical for 10 kg rover" }
|
| 105 |
+
payload_mass_kg: { value: 1.5, provenance: published, source: "Els et al. LPSC 2021 #1905 (2 cameras + CAM-M + CAM-T + Langmuir probes)" }
|
| 106 |
+
|
| 107 |
+
- name: Tenacious
|
| 108 |
+
aliases: [Resilience-Tenacious]
|
| 109 |
+
status: { value: lost_on_landing, provenance: published, source: "Resilience lander hard landing (Jun 2025)" }
|
| 110 |
+
agency: { value: ispace, provenance: published, source: "ispace HAKUTO-R Mission 2" }
|
| 111 |
+
launch_year: { value: 2025, provenance: published, source: "Launched 15 Jan 2025 on Falcon 9" }
|
| 112 |
+
landing_latitude_deg: { value: 60.5, provenance: published, source: "ispace Mission 2 target (Mare Frigoris, 60.5 N 4.6 W)" }
|
| 113 |
+
landing_site: { value: "Mare Frigoris", provenance: published, source: "ispace Mission 2 landing-zone announcement" }
|
| 114 |
+
mass_total_kg: { value: 5.0, provenance: published, source: "ispace HAKUTO-R Mission 2 mission overview" }
|
| 115 |
+
n_wheels: { value: 4, provenance: published, source: "ispace HAKUTO-R Mission 2 mission overview" }
|
| 116 |
+
wheel_radius_m: { value: 0.06, provenance: imputed, source: "estimated from ispace press imagery (scaled from Rashid by mass)" }
|
| 117 |
+
wheel_width_m: { value: 0.04, provenance: imputed, source: "estimated from ispace press imagery" }
|
| 118 |
+
grouser_height_m: { value: 0.005, provenance: imputed, source: "small grousers visible in ispace imagery" }
|
| 119 |
+
grouser_count: { value: 12, provenance: imputed, source: "class-typical 12-tooth pattern" }
|
| 120 |
+
wheelbase_m: { value: 0.30, provenance: imputed, source: "small-chassis class typical" }
|
| 121 |
+
solar_area_m2: { value: 0.15, provenance: imputed, source: "small body-mounted array (estimated)" }
|
| 122 |
+
battery_capacity_wh: { value: 25.0, provenance: imputed, source: "class-typical for 5 kg day-1 demo rover" }
|
| 123 |
+
payload_mass_kg: { value: 0.3, provenance: published, source: "ispace Mission 2 (HD camera + scoop sample demo)" }
|
| 124 |
+
|
| 125 |
+
- name: CADRE-unit
|
| 126 |
+
aliases: []
|
| 127 |
+
status: { value: design_target, provenance: published, source: "NASA/JPL CADRE (per-unit; no published surface-mission report at registry snapshot)" }
|
| 128 |
+
agency: { value: NASA/JPL, provenance: published, source: "Rothenbuchner et al. 2023 IEEE Aerospace #2300" }
|
| 129 |
+
launch_year: { value: 2025, provenance: imputed, source: "2024-2025 launch / deployment window (NASA/JPL CADRE)" }
|
| 130 |
+
landing_latitude_deg: { value: -85.0, provenance: imputed, source: "lunar south polar region target (design scenario)" }
|
| 131 |
+
landing_site: { value: "Lunar south pole region", provenance: published, source: "NASA/JPL CADRE project materials" }
|
| 132 |
+
mass_total_kg: { value: 2.0, provenance: published, source: "Rothenbuchner et al. 2023 IEEE Aerospace #2300 (per-unit ~2 kg)" }
|
| 133 |
+
n_wheels: { value: 4, provenance: published, source: "Rothenbuchner et al. 2023 IEEE Aerospace #2300" }
|
| 134 |
+
wheel_radius_m: { value: 0.08, provenance: published, source: "Rothenbuchner et al. 2023 IEEE Aerospace #2300" }
|
| 135 |
+
wheel_width_m: { value: 0.04, provenance: imputed, source: "class-typical aspect ratio for ultra-micro wire-spoke wheel" }
|
| 136 |
+
grouser_height_m: { value: 0.0, provenance: published, source: "smooth wire-spoke rims (JPL flotilla imagery)" }
|
| 137 |
+
grouser_count: { value: 0, provenance: published, source: "smooth wire-spoke rims (JPL flotilla imagery)" }
|
| 138 |
+
wheelbase_m: { value: 0.30, provenance: published, source: "Rothenbuchner et al. 2023 IEEE Aerospace #2300" }
|
| 139 |
+
solar_area_m2: { value: 0.10, provenance: published, source: "Rothenbuchner et al. 2023 IEEE Aerospace #2300 (small body-mounted array)" }
|
| 140 |
+
battery_capacity_wh: { value: 10.0, provenance: imputed, source: "power-budget back-solve (short coordinated drives)" }
|
| 141 |
+
payload_mass_kg: { value: 0.3, provenance: imputed, source: "stereo camera + small comms-ranging payload (estimated)" }
|
| 142 |
+
|
| 143 |
+
- name: MoonRanger
|
| 144 |
+
aliases: []
|
| 145 |
+
status: { value: design_target, provenance: published, source: "CMU/Astrobotic (in development)" }
|
| 146 |
+
agency: { value: CMU/Astrobotic, provenance: published, source: "Kumar et al. i-SAIRAS 2020 #5068" }
|
| 147 |
+
landing_latitude_deg: { value: -85.0, provenance: imputed, source: "south-polar demo target (design scenario)" }
|
| 148 |
+
landing_site: { value: "Lunar south pole region", provenance: published, source: "Kumar et al. i-SAIRAS 2020 #5068" }
|
| 149 |
+
mass_total_kg: { value: 13.0, provenance: published, source: "Kumar et al. i-SAIRAS 2020 #5068 (13 kg full-up)" }
|
| 150 |
+
n_wheels: { value: 4, provenance: published, source: "Kumar et al. i-SAIRAS 2020 #5068" }
|
| 151 |
+
wheel_radius_m: { value: 0.10, provenance: imputed, source: "class-match to Rashid-1" }
|
| 152 |
+
wheel_width_m: { value: 0.08, provenance: imputed, source: "class-match to Rashid-1" }
|
| 153 |
+
grouser_height_m: { value: 0.012, provenance: imputed, source: "class-typical for ~0.10 m radius lunar wheel" }
|
| 154 |
+
grouser_count: { value: 12, provenance: imputed, source: "class-typical" }
|
| 155 |
+
wheelbase_m: { value: 0.40, provenance: imputed, source: "body length ~0.65 m minus wheel diameter" }
|
| 156 |
+
solar_area_m2: { value: 0.30, provenance: imputed, source: "polar power-budget back-solve" }
|
| 157 |
+
battery_capacity_wh: { value: 100.0, provenance: imputed, source: "class-typical for 13 kg polar rover" }
|
| 158 |
+
|
| 159 |
+
- name: Sojourner
|
| 160 |
+
aliases: []
|
| 161 |
+
status: { value: flown_mars, provenance: published, source: "NASA Mars Pathfinder / Sojourner (Mars-gravity proxy, not lunar)" }
|
| 162 |
+
agency: { value: NASA/JPL, provenance: published, source: "Wilcox & Nguyen 1998" }
|
| 163 |
+
launch_year: { value: 1996, provenance: published, source: "Mars Pathfinder launched Dec 1996, landed Jul 1997" }
|
| 164 |
+
mass_total_kg: { value: 10.6, provenance: published, source: "Wilcox & Nguyen 1998" }
|
| 165 |
+
n_wheels: { value: 6, provenance: published, source: "Wilcox & Nguyen 1998" }
|
| 166 |
+
wheel_radius_m: { value: 0.065, provenance: published, source: "Wilcox & Nguyen 1998" }
|
| 167 |
+
wheel_width_m: { value: 0.08, provenance: imputed, source: "estimated" }
|
| 168 |
+
grouser_height_m: { value: 0.010, provenance: imputed, source: "estimated" }
|
| 169 |
+
grouser_count: { value: 12, provenance: imputed, source: "estimated" }
|
| 170 |
+
solar_area_m2: { value: 0.22, provenance: published, source: "Mars Pathfinder / Sojourner solar panel" }
|
| 171 |
+
battery_capacity_wh: { value: 40.0, provenance: imputed, source: "primary battery (estimated)" }
|
| 172 |
+
payload_mass_kg: { value: 1.5, provenance: published, source: "APXS (~0.55 kg) + 3 cameras + electronics" }
|
| 173 |
+
|
| 174 |
+
- name: ExoMy
|
| 175 |
+
aliases: []
|
| 176 |
+
status: { value: educational_concept, provenance: published, source: "ESA ExoMy open-hardware documentation" }
|
| 177 |
+
agency: { value: ESA/ESTEC, provenance: published, source: "ESA ExoMy open-hardware documentation" }
|
| 178 |
+
launch_year: { value: 2020, provenance: published, source: "ESA ExoMy open-hardware release" }
|
| 179 |
+
mass_total_kg: { value: 8.0, provenance: imputed, source: "approximate per ESA ExoMy docs (educational platform)" }
|
| 180 |
+
n_wheels: { value: 6, provenance: published, source: "ESA ExoMy open-hardware documentation" }
|
| 181 |
+
wheel_radius_m: { value: 0.055, provenance: published, source: "ESA ExoMy open-hardware documentation" }
|
| 182 |
+
wheel_width_m: { value: 0.06, provenance: imputed, source: "approximate per ESA ExoMy docs" }
|
| 183 |
+
grouser_height_m: { value: 0.008, provenance: imputed, source: "approximate per ESA ExoMy docs" }
|
| 184 |
+
grouser_count: { value: 12, provenance: imputed, source: "approximate per ESA ExoMy docs" }
|
| 185 |
+
solar_area_m2: { value: 0.10, provenance: imputed, source: "nominal (educational platform)" }
|
| 186 |
+
battery_capacity_wh: { value: 30.0, provenance: imputed, source: "nominal (educational platform)" }
|
| 187 |
+
payload_mass_kg: { value: 0.0, provenance: published, source: "educational platform, no dedicated science instrument" }
|
| 188 |
+
|
| 189 |
+
- name: MARSOKHOD-proto
|
| 190 |
+
aliases: []
|
| 191 |
+
status: { value: prototype, provenance: published, source: "Kemurjian et al. 1993 (historical reference)" }
|
| 192 |
+
agency: { value: IKI, provenance: published, source: "Kemurjian et al. 1993" }
|
| 193 |
+
launch_year: { value: 1992, provenance: published, source: "Kemurjian et al. 1993 (prototype)" }
|
| 194 |
+
mass_total_kg: { value: 70.0, provenance: published, source: "Kemurjian et al. 1993" }
|
| 195 |
+
n_wheels: { value: 6, provenance: published, source: "Kemurjian et al. 1993" }
|
| 196 |
+
wheel_radius_m: { value: 0.17, provenance: published, source: "Kemurjian et al. 1993" }
|
| 197 |
+
wheel_width_m: { value: 0.13, provenance: published, source: "Kemurjian et al. 1993" }
|
| 198 |
+
grouser_height_m: { value: 0.015, provenance: imputed, source: "estimated from prototype specs" }
|
| 199 |
+
grouser_count: { value: 12, provenance: imputed, source: "estimated" }
|
| 200 |
+
solar_area_m2: { value: 0.5, provenance: imputed, source: "estimated" }
|
| 201 |
+
battery_capacity_wh: { value: 100.0, provenance: imputed, source: "estimated" }
|
| 202 |
+
payload_mass_kg: { value: 8.0, provenance: imputed, source: "instrument mast + manipulator/sampling proto payload (estimated)" }
|
data/soil_simulants.csv
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
simulant,n,k_c_kN_per_m_n_plus_1,k_phi_kN_per_m_n_plus_2,cohesion_kPa,friction_angle_deg,density_kg_per_m3,citation,notes
|
| 2 |
+
FJS-1,1.0,1.37,820.0,0.2,38.0,1500,"Kanamori et al. 1998",Lunar highland simulant (Shimizu).
|
| 3 |
+
JSC-1A,1.0,1.4,820.0,1.0,45.0,1600,"Zeng et al. 2010, J Aerospace Engineering",Most widely used lunar mare simulant.
|
| 4 |
+
GRC-1,0.8,0.7,505.0,0.25,38.0,1620,"Oravec et al. 2010, JTerramechanics",Lunar regolith simulant (NASA Glenn).
|
| 5 |
+
GRC-3,0.9,1.0,700.0,0.4,42.0,1700,"He et al. 2013, J Aerospace Engineering",
|
| 6 |
+
Apollo_regolith_nominal,1.0,1.4,820.0,0.17,46.0,1660,"Heiken et al. 1991 (Lunar Sourcebook ch. 9)",Best-estimate nominal regolith; use for baseline runs.
|
| 7 |
+
Apollo_regolith_loose,1.0,0.5,400.0,0.1,30.0,1400,"Heiken et al. 1991, bounds",Soft / worst-case for slope studies.
|
| 8 |
+
Apollo_regolith_dense,1.2,2.0,1200.0,0.5,50.0,1900,"Heiken et al. 1991, bounds",Compacted / best-case.
|
| 9 |
+
Ding2011_planetary_simulant,1.10,15.6,2407.4,0.25,31.9,1605,"Ding et al. 2011, J. Terramechanics 48(1):27-45, Table 2","Planetary soil simulant used in Ding 2011 single-wheel tests; k_c/k_phi reported in kPa equal kN units here; Janosi K=9.7-13.1 mm (use ~0.011 m)."
|
| 10 |
+
KLS-1,1.2594,-44.0554,3581.8106,1.716,40.6,1600,"shear C/phi: Wang & Han 2016 J. Korean Geotech. Soc. 32(11) Table 2; pressure-sinkage n/k_c/k_phi: Lim et al. 2021 J. Astron. Space Sci. 38(4):237 bevameter fit","Korean lunar simulant; cohesion 1.716 kPa, friction 40.6 deg (direct shear, Wang & Han 2016). Bekker pressure-sinkage n=1.2594, k_c=-44.06, k_phi=3581.8 from a KICT/Pai Chai bevameter (Lim et al. 2021, Wong 1980 least-squares fit over three plate sizes); negative k_c is a normal least-squares fit artifact, k_eff = k_c/b + k_phi stays strongly positive. RD ~60%."
|
| 11 |
+
Hurrell2025_FJS1,1.0,1.37,820.0,2.4,38.0,1740,"Hurrell et al. 2025 Table 2 (cohesion 2.4 kPa via Ozaki et al. 2023; angle of repose 38 deg); n/k_c/k_phi proxied from catalogue FJS-1 (Kanamori 1998)","FJS-1 as characterised in Hurrell 2025 Rashid-1 tests, loose (~25% rel. density, bulk 1623-2100). Cohesion 2.4 kPa is far above the dry-catalogue FJS-1 value (0.2); pressure-sinkage params not reported -> FJS-1 proxy."
|
data/validation/README.md
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Validation data
|
| 2 |
+
|
| 3 |
+
Single-wheel testbed data digitised from published papers. Used **only** to
|
| 4 |
+
validate the evaluator; never used for training the surrogate.
|
| 5 |
+
|
| 6 |
+
## Active reference grids
|
| 7 |
+
|
| 8 |
+
- `wong_layer3_reference.csv` — Layer-3 BW-vs-published reference grid
|
| 9 |
+
exercised by `tests/test_terramechanics.py::test_layer3_published_reference_grid`.
|
| 10 |
+
Each row is one (wheel, soil, vertical load, slip) operating point with
|
| 11 |
+
per-quantity `[lo, hi]` tolerance bands. Three row kinds:
|
| 12 |
+
|
| 13 |
+
1. **`characterisation`** — Wong (2008) §4.2-style worked-example
|
| 14 |
+
fixture (JSC-1A canonical Bekker parameters; R=0.10 m, b=0.06 m;
|
| 15 |
+
50 N at slip ∈ {0.05, 0.20, 0.50}). Bounds pinned at the BW
|
| 16 |
+
kernel's verified outputs to within ±5 % so the test guards
|
| 17 |
+
against unintended kernel drift while staying inside the
|
| 18 |
+
±15-30 % BW model-form band reported in the literature.
|
| 19 |
+
2. **`published_rover_class`** — Apollo nominal regolith × a smooth
|
| 20 |
+
Pragyan-class wheel (R=0.135 m, b=0.10 m, W=70.2 N) and a
|
| 21 |
+
grousered Yutu-2-class wheel (R=0.165 m, b=0.150 m, h_g=0.012 m,
|
| 22 |
+
N_g=14, W=36.4 N). Bounds sized at the published Bekker-Wong
|
| 23 |
+
model-form error (Ishigami 2007; Ding et al. 2011).
|
| 24 |
+
3. **`closed_form_limit`** — kernel regression checks at analytic
|
| 25 |
+
limits (e.g. smooth wheel with N_g>0 ⇒ grouser lift factor ≡ 1;
|
| 26 |
+
bounds pinned at the v1 kernel output, not digitised experiments).
|
| 27 |
+
|
| 28 |
+
Appending rows is additive — the test reads the CSV with
|
| 29 |
+
`csv.DictReader` and parametrises one case per row.
|
| 30 |
+
|
| 31 |
+
- `single_wheel_experiments.csv` — **experiment-vs-model worksheet** for the
|
| 32 |
+
experimental anchor of Layer 3. Each row is one measured single-wheel
|
| 33 |
+
operating point: wheel geometry, vertical load, slip, the soil simulant
|
| 34 |
+
name (Bekker parameters resolved from `../soil_simulants.csv`), and the
|
| 35 |
+
Janosi-Hanamoto shear modulus `soil_shear_modulus_k_m`. The
|
| 36 |
+
`meas_drawbar_pull_n` / `meas_sinkage_m` / `meas_torque_nm` columns hold
|
| 37 |
+
point measurements traced from the source figures.
|
| 38 |
+
|
| 39 |
+
Consumed by `roverdevkit.validation.terramechanics_experiment`
|
| 40 |
+
(`compare_to_experiment`, `summarise`) and
|
| 41 |
+
`tests/test_terramechanics_experiment.py`. The harness runs the
|
| 42 |
+
analytical Bekker-Wong kernel at every operating point and reports
|
| 43 |
+
residuals + percentage errors against the measured columns.
|
| 44 |
+
`scripts/make_terramechanics_experiment_figure.py` renders the
|
| 45 |
+
terramechanics-experiment figure
|
| 46 |
+
(`reports/figures/fig_terramechanics_experiment.png`) from it.
|
| 47 |
+
|
| 48 |
+
## Sources
|
| 49 |
+
|
| 50 |
+
- **Ding et al. 2011**, *J. Terramechanics* 48(1):27-45 — rigid single-wheel
|
| 51 |
+
slip/sinkage/drawbar-pull experiments (R=135/157 mm, b=110/165 mm, lugs
|
| 52 |
+
0-15 mm, loads 30/80/150 N, slip 0-0.6). Digitised from Fig. 8/9 (Wh3
|
| 53 |
+
family: Wh34 smooth + Wh32 grousered at 80 N; soil Bekker params from
|
| 54 |
+
Table 2 → `Ding2011_planetary_simulant` in `../soil_simulants.csv`). BW
|
| 55 |
+
reproduces drawbar pull within the literature model-form band (~27 %
|
| 56 |
+
median |error|).
|
| 57 |
+
- **Wang & Han 2016**, *J. Korean Geotech. Soc.* 32(11):97-108 (open access) —
|
| 58 |
+
KLS-1 single-wheel testbed (R=85 mm, b=80 mm, 59 N), smooth vs grousered
|
| 59 |
+
(h=10 mm), slip 0.1-0.5. Digitised from Fig. 14. The paper publishes only
|
| 60 |
+
shear strength (Table 2: C=1.716 kPa, φ=40.6°); the pressure-sinkage moduli
|
| 61 |
+
in `KLS-1` (`../soil_simulants.csv`) are KLS-1's own bevameter-measured Bekker
|
| 62 |
+
values (Lim et al. 2021, *J. Astron. Space Sci.* 38(4):237; n=1.2594,
|
| 63 |
+
k_c=-44.06, k_phi=3581.8). This is a deliberate **stress case at the edge of
|
| 64 |
+
the rigid-wheel kernel's regime**: the smallest/most-lightly-loaded wheel on a
|
| 65 |
+
firm, dense, fines-rich simulant that barely sinks (1-14 mm), so BW's
|
| 66 |
+
force-balance sinkage solve over-predicts DP and sinkage (~135 % / ~385 %
|
| 67 |
+
median |error|) and cannot capture the measured DP collapse at s≈0.5.
|
| 68 |
+
- **Hurrell et al. 2025**, *Space Sci. Rev.* 221 art. 37 (open access, CC-BY) —
|
| 69 |
+
Rashid-1 micro-rover wheel (R=100 mm, b=80 mm, 14 grousers h=20 mm, 24.5 N)
|
| 70 |
+
on FJS-1, slip 0.1-0.5. Digitised from Figs. 5/6:
|
| 71 |
+
`meas_drawbar_pull_n` = traction coefficient F_x/F_z (Fig. 5) × 24.5 N;
|
| 72 |
+
`meas_sinkage_m` from Fig. 6; torque not reported. Soil = `Hurrell2025_FJS1`
|
| 73 |
+
(`../soil_simulants.csv`): cohesion 2.4 kPa (Ozaki et al. 2023) and AoR 38°,
|
| 74 |
+
pressure-sinkage proxied from catalogue FJS-1. **Most application-relevant
|
| 75 |
+
case** (in-scope micro-rover wheel + load); BW lands within band on both DP
|
| 76 |
+
(~24 % median) and sinkage (~28 %).
|
| 77 |
+
- **Iizuka & Kubota 2011** — grousered-wheel experiments motivating the
|
| 78 |
+
arc-density grouser correction (not digitised into this grid; empirical
|
| 79 |
+
grousered-wheel checks live in `single_wheel_experiments.csv`).
|
| 80 |
+
- **Wong** — datasets from *Theory of Ground Vehicles* (4th ed.) ch. 4.
|
| 81 |
+
|
| 82 |
+
Keep raw digitised traces in `raw/` (git-ignored, re-downloadable from the
|
| 83 |
+
papers); curated, checked-in reference rows live in
|
| 84 |
+
`wong_layer3_reference.csv` / `single_wheel_experiments.csv` at this level.
|
data/validation/raw/.gitkeep
ADDED
|
File without changes
|
data/validation/single_wheel_experiments.csv
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
source,case_id,wheel_radius_m,wheel_width_m,grouser_height_m,grouser_count,soil_simulant,soil_shear_modulus_k_m,vertical_load_n,slip,meas_drawbar_pull_n,meas_sinkage_m,meas_torque_nm,status,citation,notes
|
| 2 |
+
ding2011,ding2011_smooth_w80_s00,0.15735,0.165,0.0,0,Ding2011_planetary_simulant,0.011,80.0,0.0,-6.5,0.005,0.0,digitised_approx,"Ding et al. 2011, J. Terramechanics 48(1):27-45, single-wheel testbed (HIT)","Smooth rigid wheel Wh34 (R=157.35 mm, b=165 mm, h=0); DP/torque/sinkage vs slip from Fig 9 h=0 curve; soil Bekker params from paper Table 2."
|
| 3 |
+
ding2011,ding2011_smooth_w80_s10,0.15735,0.165,0.0,0,Ding2011_planetary_simulant,0.011,80.0,0.1,11.0,0.0055,3.0,digitised_approx,"Ding et al. 2011, J. Terramechanics 48(1):27-45, single-wheel testbed (HIT)","Smooth rigid wheel R=157.35 mm, b=165 mm."
|
| 4 |
+
ding2011,ding2011_smooth_w80_s20,0.15735,0.165,0.0,0,Ding2011_planetary_simulant,0.011,80.0,0.2,14.5,0.0075,4.2,digitised_approx,"Ding et al. 2011, J. Terramechanics 48(1):27-45, single-wheel testbed (HIT)","Smooth rigid wheel R=157.35 mm, b=165 mm."
|
| 5 |
+
ding2011,ding2011_smooth_w80_s30,0.15735,0.165,0.0,0,Ding2011_planetary_simulant,0.011,80.0,0.3,15.0,0.010,4.8,digitised_approx,"Ding et al. 2011, J. Terramechanics 48(1):27-45, single-wheel testbed (HIT)","Smooth rigid wheel R=157.35 mm, b=165 mm."
|
| 6 |
+
ding2011,ding2011_smooth_w80_s40,0.15735,0.165,0.0,0,Ding2011_planetary_simulant,0.011,80.0,0.4,15.0,0.0125,5.0,digitised_approx,"Ding et al. 2011, J. Terramechanics 48(1):27-45, single-wheel testbed (HIT)","Smooth rigid wheel R=157.35 mm, b=165 mm."
|
| 7 |
+
ding2011,ding2011_smooth_w80_s60,0.15735,0.165,0.0,0,Ding2011_planetary_simulant,0.011,80.0,0.6,15.0,0.0175,5.3,digitised_approx,"Ding et al. 2011, J. Terramechanics 48(1):27-45, single-wheel testbed (HIT)","Smooth rigid wheel R=157.35 mm, b=165 mm."
|
| 8 |
+
ding2011,ding2011_lug10_w80_s00,0.15735,0.165,0.010,30,Ding2011_planetary_simulant,0.011,80.0,0.0,-6.5,0.0045,0.0,digitised_approx,"Ding et al. 2011, J. Terramechanics 48(1):27-45, single-wheel testbed (HIT)","Lugged rigid wheel R=157.35 mm, b=165 mm, lug height 10 mm, 30 lugs (Wh32)."
|
| 9 |
+
ding2011,ding2011_lug10_w80_s10,0.15735,0.165,0.010,30,Ding2011_planetary_simulant,0.011,80.0,0.1,14.0,0.005,3.5,digitised_approx,"Ding et al. 2011, J. Terramechanics 48(1):27-45, single-wheel testbed (HIT)","Lugged rigid wheel R=157.35 mm, b=165 mm, lug height 10 mm, 30 lugs (Wh32)."
|
| 10 |
+
ding2011,ding2011_lug10_w80_s20,0.15735,0.165,0.010,30,Ding2011_planetary_simulant,0.011,80.0,0.2,20.0,0.0075,5.0,digitised_approx,"Ding et al. 2011, J. Terramechanics 48(1):27-45, single-wheel testbed (HIT)","Lugged rigid wheel R=157.35 mm, b=165 mm, lug height 10 mm, 30 lugs (Wh32)."
|
| 11 |
+
ding2011,ding2011_lug10_w80_s30,0.15735,0.165,0.010,30,Ding2011_planetary_simulant,0.011,80.0,0.3,22.0,0.0105,5.8,digitised_approx,"Ding et al. 2011, J. Terramechanics 48(1):27-45, single-wheel testbed (HIT)","Lugged rigid wheel R=157.35 mm, b=165 mm, lug height 10 mm, 30 lugs (Wh32)."
|
| 12 |
+
ding2011,ding2011_lug10_w80_s40,0.15735,0.165,0.010,30,Ding2011_planetary_simulant,0.011,80.0,0.4,23.0,0.014,6.3,digitised_approx,"Ding et al. 2011, J. Terramechanics 48(1):27-45, single-wheel testbed (HIT)","Lugged rigid wheel R=157.35 mm, b=165 mm, lug height 10 mm, 30 lugs (Wh32)."
|
| 13 |
+
ding2011,ding2011_lug10_w80_s60,0.15735,0.165,0.010,30,Ding2011_planetary_simulant,0.011,80.0,0.6,24.5,0.027,7.2,digitised_approx,"Ding et al. 2011, J. Terramechanics 48(1):27-45, single-wheel testbed (HIT)","Lugged rigid wheel R=157.35 mm, b=165 mm, lug height 10 mm, 30 lugs (Wh32)."
|
| 14 |
+
wang_han_2016_kls1,kls1_smooth_w59_s30,0.085,0.080,0.0,0,KLS-1,0.018,58.86,0.3,2.0,0.0045,1.9,digitised_approx,"Wang & Han 2016, J. Korean Geotech. Soc. 32(11):97-108 (open access), KICT single-wheel testbed","Smooth wheel d=170 mm, b=80 mm; KLS-1 simulant (PSD-matched to JSC-1/FJS-1), relative density 60%, load 6 kg, 10 mm/s. Measured DP/torque/sinkage in paper Fig. 14."
|
| 15 |
+
wang_han_2016_kls1,kls1_smooth_w59_s10,0.085,0.080,0.0,0,KLS-1,0.018,58.86,0.1,-0.5,0.001,1.3,digitised_approx,"Wang & Han 2016, J. Korean Geotech. Soc. 32(11):97-108 (open access), KICT single-wheel testbed","Smooth wheel d=170 mm, b=80 mm; KLS-1 simulant."
|
| 16 |
+
wang_han_2016_kls1,kls1_smooth_w59_s20,0.085,0.080,0.0,0,KLS-1,0.018,58.86,0.2,1.0,0.0043,1.55,digitised_approx,"Wang & Han 2016, J. Korean Geotech. Soc. 32(11):97-108 (open access), KICT single-wheel testbed","Smooth wheel d=170 mm, b=80 mm; KLS-1 simulant."
|
| 17 |
+
wang_han_2016_kls1,kls1_smooth_w59_s40,0.085,0.080,0.0,0,KLS-1,0.018,58.86,0.4,2.5,0.0057,2.05,digitised_approx,"Wang & Han 2016, J. Korean Geotech. Soc. 32(11):97-108 (open access), KICT single-wheel testbed","Smooth wheel d=170 mm, b=80 mm; KLS-1 simulant."
|
| 18 |
+
wang_han_2016_kls1,kls1_smooth_w59_s50,0.085,0.080,0.0,0,KLS-1,0.018,58.86,0.5,0.5,0.008,2.05,digitised_approx,"Wang & Han 2016, J. Korean Geotech. Soc. 32(11):97-108 (open access), KICT single-wheel testbed","Smooth wheel d=170 mm, b=80 mm; KLS-1 simulant."
|
| 19 |
+
wang_han_2016_kls1,kls1_lug10_w59_s30,0.085,0.080,0.010,16,KLS-1,0.018,58.86,0.3,10.0,0.0078,3.05,digitised_approx,"Wang & Han 2016, J. Korean Geotech. Soc. 32(11):97-108 (open access), KICT single-wheel testbed","Grousered wheel d=170 mm, b=80 mm, 16 grousers (10 mm, 36 deg spacing, 3 mm thick); KLS-1."
|
| 20 |
+
wang_han_2016_kls1,kls1_lug10_w59_s10,0.085,0.080,0.010,16,KLS-1,0.018,58.86,0.1,7.7,0.0022,2.85,digitised_approx,"Wang & Han 2016, J. Korean Geotech. Soc. 32(11):97-108 (open access), KICT single-wheel testbed","Grousered wheel d=170 mm, b=80 mm, 16 grousers; KLS-1."
|
| 21 |
+
wang_han_2016_kls1,kls1_lug10_w59_s20,0.085,0.080,0.010,16,KLS-1,0.018,58.86,0.2,9.0,0.0043,2.95,digitised_approx,"Wang & Han 2016, J. Korean Geotech. Soc. 32(11):97-108 (open access), KICT single-wheel testbed","Grousered wheel d=170 mm, b=80 mm, 16 grousers; KLS-1."
|
| 22 |
+
wang_han_2016_kls1,kls1_lug10_w59_s40,0.085,0.080,0.010,16,KLS-1,0.018,58.86,0.4,12.7,0.0096,3.1,digitised_approx,"Wang & Han 2016, J. Korean Geotech. Soc. 32(11):97-108 (open access), KICT single-wheel testbed","Grousered wheel d=170 mm, b=80 mm, 16 grousers; KLS-1."
|
| 23 |
+
wang_han_2016_kls1,kls1_lug10_w59_s50,0.085,0.080,0.010,16,KLS-1,0.018,58.86,0.5,6.4,0.0135,2.85,digitised_approx,"Wang & Han 2016, J. Korean Geotech. Soc. 32(11):97-108 (open access), KICT single-wheel testbed","Grousered wheel d=170 mm, b=80 mm, 16 grousers; KLS-1."
|
| 24 |
+
hurrell2025_rashid1,rashid1_fjs1_w24_s10,0.100,0.080,0.020,14,Hurrell2025_FJS1,0.018,24.5,0.1,8.1,0.006,,digitised_approx,"Hurrell et al. 2025, Space Sci. Rev. 221 art. 37 (open access, CC-BY), single-wheel testbed + DEM, Rashid-1 wheel","Rashid-1 micro-rover wheel R=100 mm, b=80 mm, 14 grousers (20 mm), load 24.5 N (Earth), FJS-1 loose (~25% rel. density). Source reports traction coefficient mu = DP/W (Fig 5) and dynamic sinkage (Fig 6); meas_drawbar_pull_n = mu * 24.5 N. FJS-1 measured AoR 38 deg matches catalogue phi."
|
| 25 |
+
hurrell2025_rashid1,rashid1_fjs1_w24_s20,0.100,0.080,0.020,14,Hurrell2025_FJS1,0.018,24.5,0.2,8.6,0.0095,,digitised_approx,"Hurrell et al. 2025, Space Sci. Rev. 221 art. 37 (open access, CC-BY), single-wheel testbed + DEM, Rashid-1 wheel","Rashid-1 wheel; meas_drawbar_pull_n = mu * 24.5 N from Fig 5."
|
| 26 |
+
hurrell2025_rashid1,rashid1_fjs1_w24_s30,0.100,0.080,0.020,14,Hurrell2025_FJS1,0.018,24.5,0.3,8.8,0.011,,digitised_approx,"Hurrell et al. 2025, Space Sci. Rev. 221 art. 37 (open access, CC-BY), single-wheel testbed + DEM, Rashid-1 wheel","Rashid-1 wheel; meas_drawbar_pull_n = mu * 24.5 N from Fig 5."
|
| 27 |
+
hurrell2025_rashid1,rashid1_fjs1_w24_s40,0.100,0.080,0.020,14,Hurrell2025_FJS1,0.018,24.5,0.4,9.6,0.016,,digitised_approx,"Hurrell et al. 2025, Space Sci. Rev. 221 art. 37 (open access, CC-BY), single-wheel testbed + DEM, Rashid-1 wheel","Rashid-1 wheel; meas_drawbar_pull_n = mu * 24.5 N from Fig 5."
|
| 28 |
+
hurrell2025_rashid1,rashid1_fjs1_w24_s50,0.100,0.080,0.020,14,Hurrell2025_FJS1,0.018,24.5,0.5,10.5,0.0195,,digitised_approx,"Hurrell et al. 2025, Space Sci. Rev. 221 art. 37 (open access, CC-BY), single-wheel testbed + DEM, Rashid-1 wheel","Rashid-1 wheel; meas_drawbar_pull_n = mu * 24.5 N from Fig 5."
|
data/validation/wong_layer3_reference.csv
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
case_id,kind,wheel_radius_m,wheel_width_m,grouser_height_m,grouser_count,soil_n,soil_k_c_kN,soil_k_phi_kN,soil_c_kPa,soil_phi_deg,vertical_load_n,slip,exp_drawbar_pull_n_lo,exp_drawbar_pull_n_hi,exp_sinkage_m_lo,exp_sinkage_m_hi,exp_torque_nm_lo,exp_torque_nm_hi,exp_dp_over_w_lo,exp_dp_over_w_hi,citation
|
| 2 |
+
wong_2008_ch4_fixture_s0_05,characterisation,0.10,0.06,0.0,0,1.0,1.4,820.0,1.0,45.0,50.0,0.05,-0.5,1.5,0.026,0.029,1.65,1.86,-0.01,0.03,Wong 2008 §4.2 worked-example fixture (JSC-1A canonical Bekker params); pinned at kernel v1.
|
| 3 |
+
wong_2008_ch4_fixture_s0_20,characterisation,0.10,0.06,0.0,0,1.0,1.4,820.0,1.0,45.0,50.0,0.20,5.5,7.7,0.026,0.029,2.30,2.55,0.10,0.16,Wong 2008 §4.2 worked-example fixture (JSC-1A canonical Bekker params); pinned at kernel v1.
|
| 4 |
+
wong_2008_ch4_fixture_s0_50,characterisation,0.10,0.06,0.0,0,1.0,1.4,820.0,1.0,45.0,50.0,0.50,12.5,14.9,0.026,0.029,3.05,3.30,0.24,0.31,Wong 2008 §4.2 worked-example fixture (JSC-1A canonical Bekker params); pinned at kernel v1.
|
| 5 |
+
pragyan_class_smooth_s0_20,published_rover_class,0.135,0.10,0.0,0,1.0,1.4,820.0,0.17,46.0,70.2,0.20,4.5,18.5,0.012,0.030,2.5,5.5,0.05,0.30,Apollo nominal regolith (Heiken et al. 1991) × Pragyan-class smooth wheel; DP/W and sinkage bounded by ±25% Bekker-Wong model-form error (Ishigami 2007; Ding 2011).
|
| 6 |
+
pragyan_class_smooth_s0_50,published_rover_class,0.135,0.10,0.0,0,1.0,1.4,820.0,0.17,46.0,70.2,0.50,15.0,30.0,0.012,0.030,4.5,7.0,0.20,0.45,Apollo nominal regolith × Pragyan-class smooth wheel at high slip; lunar-rover testbed (Ding 2011 Fig 8) reports DP/W in this band on JSC-1A / FJS-1.
|
| 7 |
+
yutu2_class_grousered_s0_20,published_rover_class,0.165,0.150,0.012,14,1.0,1.4,820.0,0.17,46.0,36.4,0.20,3.0,12.0,0.006,0.020,1.5,3.5,0.08,0.30,Apollo nominal regolith × Yutu-2-class grousered wheel at lunar per-wheel load 36.4 N (135 kg / 6 wheels / 1.62 m·s⁻²).
|
| 8 |
+
grouser_lift_unity_smooth_s0_60,closed_form_limit,0.10,0.10,0.0,14,1.0,1.4,820.0,0.17,46.0,40.0,0.6,8.7,12.7,0.012,0.020,2.05,2.65,0.20,0.32,Smooth-wheel limit (h_g=0 N_g=14): grouser shear factor collapses to unity; BW kernel regression check at slip=0.6; bounds pinned at v1 kernel output ±10%.
|
deploy/huggingface/README.md
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
title: RoverDevKit
|
| 3 |
+
emoji: 🛰️
|
| 4 |
+
colorFrom: gray
|
| 5 |
+
colorTo: blue
|
| 6 |
+
sdk: docker
|
| 7 |
+
app_port: 8000
|
| 8 |
+
pinned: false
|
| 9 |
+
license: mit
|
| 10 |
+
---
|
| 11 |
+
|
| 12 |
+
# RoverDevKit — hosted demo
|
| 13 |
+
|
| 14 |
+
Interactive tradespace explorer for conceptual design of lunar micro-rovers:
|
| 15 |
+
physics-based mission evaluator, calibrated surrogate predictions, parametric
|
| 16 |
+
sweeps, NSGA-II multi-objective optimization, and SHAP-style design
|
| 17 |
+
explanations.
|
| 18 |
+
|
| 19 |
+
- Source code: <https://github.com/Autonomous-Mission-Systems-Lab/roverdevkit>
|
| 20 |
+
- Paper preprint: <https://arxiv.org/abs/2606.21755>
|
| 21 |
+
|
| 22 |
+
This Space runs the single-container build from
|
| 23 |
+
[`webapp/Dockerfile`](https://github.com/Autonomous-Mission-Systems-Lab/roverdevkit/blob/main/webapp/Dockerfile):
|
| 24 |
+
one `uvicorn` process serves the FastAPI backend and the React single-page app
|
| 25 |
+
from the same origin on port 8000.
|
| 26 |
+
|
| 27 |
+
> This README (with its Spaces front matter) is generated for the hosted demo
|
| 28 |
+
> by `scripts/deploy_hf_space.sh` and is not the repository's main README.
|
environment.yml
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
name: roverdevkit
|
| 2 |
+
channels:
|
| 3 |
+
- conda-forge
|
| 4 |
+
dependencies:
|
| 5 |
+
- python=3.12
|
| 6 |
+
|
| 7 |
+
- pip
|
| 8 |
+
- numpy>=1.26
|
| 9 |
+
- scipy>=1.11
|
| 10 |
+
- pandas>=2.1
|
| 11 |
+
- scikit-learn>=1.4
|
| 12 |
+
- matplotlib>=3.8
|
| 13 |
+
- plotly>=5.18
|
| 14 |
+
- jupyterlab>=4.0
|
| 15 |
+
- pyyaml>=6.0
|
| 16 |
+
- tqdm>=4.66
|
| 17 |
+
- pip:
|
| 18 |
+
- xgboost>=2.0
|
| 19 |
+
- pymoo>=0.6.1
|
| 20 |
+
- shap>=0.44
|
| 21 |
+
- optuna>=3.5
|
| 22 |
+
- pydantic>=2.5
|
| 23 |
+
- torch>=2.2
|
| 24 |
+
- -e .
|
fig_system_architecture.png
ADDED
|
models/README.md
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Models
|
| 2 |
+
|
| 3 |
+
Shipped trained surrogate bundles used at runtime by the web app and
|
| 4 |
+
validation scripts.
|
| 5 |
+
|
| 6 |
+
| Path | Purpose |
|
| 7 |
+
| --- | --- |
|
| 8 |
+
| `surrogate_v9/quantile_bundles.joblib` | v9 quantile-XGB heads (calibrated 90% PIs) for the Current Design and Explain Design tabs |
|
| 9 |
+
|
| 10 |
+
Training-time metrics (`coverage.csv`, `median_sanity.csv`, etc.) are
|
| 11 |
+
written to `reports/surrogate_v9/` when you re-fit. A full calibration
|
| 12 |
+
run via `scripts/calibrate_intervals.py` publishes the runtime bundle
|
| 13 |
+
here automatically:
|
| 14 |
+
|
| 15 |
+
```bash
|
| 16 |
+
python scripts/calibrate_intervals.py \
|
| 17 |
+
--dataset data/analytical/lhs_v9.parquet \
|
| 18 |
+
--tuned-params reports/tuned_v9/tuned_best_params.json
|
| 19 |
+
```
|
| 20 |
+
|
| 21 |
+
Use `--no-publish-bundle` on smoke runs so partial calibrations do not
|
| 22 |
+
overwrite the shipped model. Override the runtime path with
|
| 23 |
+
`ROVERDEVKIT_QUANTILE_BUNDLES` if needed.
|
models/surrogate_v9/quantile_bundles.joblib
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:3ef01d20ce66ecc641df60287b71c8fee24ef16f56e11e880cfd849de80b4ec2
|
| 3 |
+
size 26786100
|
pyproject.toml
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[build-system]
|
| 2 |
+
requires = ["setuptools>=68", "wheel"]
|
| 3 |
+
build-backend = "setuptools.build_meta"
|
| 4 |
+
|
| 5 |
+
[project]
|
| 6 |
+
name = "roverdevkit"
|
| 7 |
+
version = "0.1.0"
|
| 8 |
+
description = "ML-accelerated co-design of mobility and power subsystems for lunar micro-rovers"
|
| 9 |
+
readme = "README.md"
|
| 10 |
+
license = { file = "LICENSE" }
|
| 11 |
+
requires-python = ">=3.11"
|
| 12 |
+
authors = [
|
| 13 |
+
{ name = "Autonomous Mission Systems Lab, Duke University" },
|
| 14 |
+
]
|
| 15 |
+
keywords = [
|
| 16 |
+
"lunar",
|
| 17 |
+
"rover",
|
| 18 |
+
"terramechanics",
|
| 19 |
+
"tradespace",
|
| 20 |
+
"surrogate",
|
| 21 |
+
"multi-objective-optimization",
|
| 22 |
+
"space-systems",
|
| 23 |
+
]
|
| 24 |
+
classifiers = [
|
| 25 |
+
"Development Status :: 2 - Pre-Alpha",
|
| 26 |
+
"Intended Audience :: Science/Research",
|
| 27 |
+
"License :: OSI Approved :: MIT License",
|
| 28 |
+
"Programming Language :: Python :: 3",
|
| 29 |
+
"Programming Language :: Python :: 3.11",
|
| 30 |
+
"Programming Language :: Python :: 3.12",
|
| 31 |
+
"Topic :: Scientific/Engineering",
|
| 32 |
+
]
|
| 33 |
+
dependencies = [
|
| 34 |
+
"numpy>=1.26",
|
| 35 |
+
"scipy>=1.11",
|
| 36 |
+
"pandas>=2.1",
|
| 37 |
+
"scikit-learn>=1.4",
|
| 38 |
+
"xgboost>=2.0",
|
| 39 |
+
"pymoo>=0.6.1",
|
| 40 |
+
"shap>=0.44",
|
| 41 |
+
"optuna>=3.5",
|
| 42 |
+
"matplotlib>=3.8",
|
| 43 |
+
"plotly>=5.18",
|
| 44 |
+
"pydantic>=2.5",
|
| 45 |
+
"pyyaml>=6.0",
|
| 46 |
+
"tqdm>=4.66",
|
| 47 |
+
"pyarrow>=14",
|
| 48 |
+
]
|
| 49 |
+
|
| 50 |
+
[project.optional-dependencies]
|
| 51 |
+
torch = ["torch>=2.2"]
|
| 52 |
+
webapp = [
|
| 53 |
+
# Browser-based tradespace tool backend.
|
| 54 |
+
# Frontend toolchain (Node 20 LTS + npm) is managed separately under webapp/frontend/.
|
| 55 |
+
"fastapi>=0.115",
|
| 56 |
+
"uvicorn[standard]>=0.30",
|
| 57 |
+
"sse-starlette>=2.1",
|
| 58 |
+
"httpx>=0.27",
|
| 59 |
+
"python-multipart>=0.0.9",
|
| 60 |
+
]
|
| 61 |
+
dev = [
|
| 62 |
+
"pytest>=8.0",
|
| 63 |
+
"pytest-cov>=4.1",
|
| 64 |
+
"ruff>=0.3",
|
| 65 |
+
"mypy>=1.8",
|
| 66 |
+
"jupyterlab>=4.0",
|
| 67 |
+
"nbstripout>=0.7",
|
| 68 |
+
]
|
| 69 |
+
|
| 70 |
+
[project.urls]
|
| 71 |
+
Repository = "https://github.com/Autonomous-Mission-Systems-Lab/roverdevkit"
|
| 72 |
+
|
| 73 |
+
[tool.setuptools.packages.find]
|
| 74 |
+
include = ["roverdevkit*"]
|
| 75 |
+
|
| 76 |
+
[tool.setuptools.package-data]
|
| 77 |
+
roverdevkit = ["py.typed"]
|
| 78 |
+
|
| 79 |
+
[tool.ruff]
|
| 80 |
+
line-length = 100
|
| 81 |
+
target-version = "py311"
|
| 82 |
+
|
| 83 |
+
[tool.ruff.lint]
|
| 84 |
+
select = ["E", "F", "W", "I", "N", "UP", "B", "SIM", "NPY"]
|
| 85 |
+
ignore = ["E501"]
|
| 86 |
+
|
| 87 |
+
[tool.ruff.lint.per-file-ignores]
|
| 88 |
+
# Allow the conventional sklearn naming (`X_train`, `Y_pred`, `X_feas`)
|
| 89 |
+
# in surrogate ML code; lowercase variants would obscure the standard
|
| 90 |
+
# convention.
|
| 91 |
+
"roverdevkit/surrogate/baselines.py" = ["N806"]
|
| 92 |
+
"roverdevkit/surrogate/tuning.py" = ["N803", "N806"]
|
| 93 |
+
"roverdevkit/surrogate/uncertainty.py" = ["N803", "N806"]
|
| 94 |
+
"scripts/tune_baselines.py" = ["N806"]
|
| 95 |
+
"scripts/calibrate_intervals.py" = ["N806"]
|
| 96 |
+
"tests/test_surrogate_tuning.py" = ["N806"]
|
| 97 |
+
"tests/test_surrogate_uncertainty.py" = ["N803", "N806"]
|
| 98 |
+
"webapp/backend/services/predict.py" = ["N803"]
|
| 99 |
+
"webapp/backend/routes/predict.py" = ["N806"]
|
| 100 |
+
|
| 101 |
+
[tool.ruff.format]
|
| 102 |
+
quote-style = "double"
|
| 103 |
+
|
| 104 |
+
[tool.mypy]
|
| 105 |
+
python_version = "3.11"
|
| 106 |
+
ignore_missing_imports = true
|
| 107 |
+
warn_unused_ignores = true
|
| 108 |
+
warn_redundant_casts = true
|
| 109 |
+
|
| 110 |
+
[tool.pytest.ini_options]
|
| 111 |
+
testpaths = ["tests", "webapp/backend/tests"]
|
| 112 |
+
# Repo root is added to sys.path so `import webapp.backend...` works
|
| 113 |
+
# without installing the webapp package. The webapp is a deployment
|
| 114 |
+
# artifact, not a Python distribution; it lives next to roverdevkit.
|
| 115 |
+
pythonpath = ["."]
|
| 116 |
+
addopts = "-ra --strict-markers"
|
| 117 |
+
markers = [
|
| 118 |
+
"slow: slow tests (>10s)",
|
| 119 |
+
"integration: full mission evaluator integration tests",
|
| 120 |
+
]
|
reports/pareto_fronts/front_crater_rim_survey.csv
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
scenario_name,wheel_radius_m,wheel_width_m,grouser_height_m,grouser_count,n_wheels,chassis_mass_kg,wheelbase_m,solar_area_m2,battery_capacity_wh,avionics_power_w,peak_wheel_torque_nm,range_km,energy_margin_raw_pct,slope_capability_deg,total_mass_kg,backend_used
|
| 2 |
+
crater_rim_survey,0.19996949336018777,0.03015591370191417,0.01999947745771378,24,4,0.5013295982314507,1.09239005542805,0.11074041652767178,6.5998976103874725,5.040316501944113,0.6816791635367268,25.0,64.75573128709165,32.39190332913843,9.147868153565007,evaluator
|
| 3 |
+
crater_rim_survey,0.19996475949140272,0.030046460113463763,0.01999810016708055,24,4,3.084264550682021,1.1412822471359005,0.10287281342060557,63.50697853914775,12.012126079302831,18.536282533125284,0.38006527396986056,0.5755216671889363,34.16605030168889,23.4666655556521,evaluator
|
| 4 |
+
crater_rim_survey,0.1999691605068453,0.03021341809861096,0.01999950890415926,24,4,32.0557930112859,0.8287957171902023,0.11064993132036935,38.423390020782065,5.807522040762394,5.084523565790135,25.0,17.447123653924336,34.78795237028887,54.900464902228116,evaluator
|
| 5 |
+
crater_rim_survey,0.19997541938513752,0.030210883832046093,0.01999480533631987,24,4,3.0067251103422388,1.0916940373390942,0.19029184457796658,7.862853303258829,26.050290075535422,4.484651162830488,6.1745592153218825,1.199240928328565,33.578144184942836,16.345451077945384,evaluator
|
| 6 |
+
crater_rim_survey,0.19999371313820274,0.0302101785843682,0.01999480533631987,24,4,4.9453598721917835,1.0040727343588634,0.20220891672271504,8.974146954879252,26.050290075535422,2.4390552811342747,25.0,3.8715615973877266,33.74026656337317,17.923333136178734,evaluator
|
| 7 |
+
crater_rim_survey,0.19992738362360304,0.030025907534493097,0.01999978200123614,24,4,11.625840702592752,1.1087973059317935,0.11801969071068683,78.35665817016414,12.375143296187218,17.724103777357353,13.51014492492579,5.296665042347408,34.60310527329037,34.89089614577678,evaluator
|
| 8 |
+
crater_rim_survey,0.199994593821754,0.030208110186515022,0.019998098535041933,24,4,4.200760509028835,0.9285303718390917,0.20287489830844252,194.93850095281107,26.58273228047366,4.567260654470145,22.156521162164832,3.07028830576107,33.939104232184405,20.21577098356393,evaluator
|
| 9 |
+
crater_rim_survey,0.1998949871214314,0.030226446195385434,0.019998440791125394,24,4,1.9444277499154028,1.138623951995295,0.11602906162333988,99.32372189273153,12.012126079302831,17.600191822065792,22.669379612387523,5.341743231664687,34.056770062505,21.86877883276512,evaluator
|
| 10 |
+
crater_rim_survey,0.1999857781965795,0.030219707500303873,0.019991902488848946,24,4,16.480098047845054,1.181846667401259,0.19734518866266185,17.888723619848363,25.758354168212,19.25454786667237,9.11510996273768,3.2350798403546905,34.72718815417143,42.83802367757407,evaluator
|
| 11 |
+
crater_rim_survey,0.19990870607176364,0.030005903631146944,0.019999782098889494,24,4,0.9780012293651135,1.1410654699732552,0.11602334313964643,13.263494286868664,13.572908448135099,19.376224060699947,6.521841063123421,2.062807291834961,33.97992105908711,20.635616391079257,evaluator
|
| 12 |
+
crater_rim_survey,0.19993754795265384,0.0302188258001639,0.019998828755911417,24,4,22.42907010712495,1.1331715897292536,0.19364059998966796,165.14780294750207,24.44552701676996,17.593253842836596,10.888515997861626,3.9840929686706517,34.78355858313113,51.59678768563304,evaluator
|
| 13 |
+
crater_rim_survey,0.19985603014556594,0.03022083184623974,0.019998809843019164,24,4,16.480098047845054,1.189590994985081,0.19754238550794442,98.18252251724451,25.218683255432175,19.033484831644756,12.003761067672333,3.8936653900425657,34.73564667948465,43.591329784582555,evaluator
|
| 14 |
+
crater_rim_survey,0.19998543176775221,0.030219707500303873,0.019991902488848946,24,4,16.90346789491681,1.181846667401259,0.19714559757258543,242.43737347732795,24.579601308626025,19.25454786667237,14.871717077408523,4.319745338819463,34.75464950864102,45.879642837033,evaluator
|
| 15 |
+
crater_rim_survey,0.19998288464547984,0.030212343262724605,0.019999872897597346,24,4,13.120265914448098,1.0921353308095618,0.20010149768516522,196.85947401952347,26.799532361513545,18.25226270422459,7.366092274882523,2.205636770687478,34.692170283016345,39.82978643388382,evaluator
|
| 16 |
+
crater_rim_survey,0.19992738362360304,0.030025907534493097,0.01999978200123614,24,4,13.850086445341361,1.1087973059317935,0.11801969071068683,77.56309429824228,12.375143296187218,17.724103777357353,12.386592773948287,5.296665042347408,34.66368605040657,37.908650738201494,evaluator
|
| 17 |
+
crater_rim_survey,0.1999998267581842,0.030224287458665244,0.019999888079936544,24,4,10.395015241185963,0.5447001379848206,0.11943090236622705,74.18031107617836,13.637844175820277,18.54953477077168,7.259576895060765,3.0249930938135954,34.571049672416926,33.72289459807108,evaluator
|
| 18 |
+
crater_rim_survey,0.19993106729572208,0.030025691595464102,0.01999981531032702,24,4,12.580518359002717,1.1046593742150552,0.12034701160859249,166.01080285732803,12.531714451309602,6.343881666037599,17.907045407026544,5.236896013614218,34.49780007091426,31.008123757677893,evaluator
|
| 19 |
+
crater_rim_survey,0.1999798793520878,0.03021613116781923,0.0199999007669067,24,4,0.7170518856952199,1.0916759879026448,0.12484608667360336,99.8841197552043,13.5702146463674,1.0098186363034296,25.0,5.309325282243869,32.85342043543148,11.310479876609676,evaluator
|
| 20 |
+
crater_rim_survey,0.19985603014556594,0.03022149988453427,0.019999783704246595,24,4,8.305982797643265,1.0063461590300977,0.1868290719919364,10.326856203447273,25.31491204627705,4.946013755270927,5.5710001803315725,1.4532239938067297,34.175827137340384,23.77378567037766,evaluator
|
| 21 |
+
crater_rim_survey,0.19996049402745086,0.030208672999496103,0.019998100204768634,24,4,3.449709457090443,1.1411500115891193,0.1156456209596583,200.49366664226574,12.012126079302831,18.877736200412205,19.84266616314643,4.705616618897112,34.28266042917022,25.75783322180167,evaluator
|
| 22 |
+
crater_rim_survey,0.19993969042263834,0.03021469824534104,0.01999947745771378,24,4,0.5013295982314507,1.1172947378577829,0.11074041652767178,68.54734302984869,5.040316501944113,0.6816791635367268,25.0,62.375091566859,32.55374098678155,9.854151422276866,evaluator
|
| 23 |
+
crater_rim_survey,0.19999371313820274,0.0302101785843682,0.019194677829063513,24,4,4.9453598721917835,1.0040727343588634,0.20220891672271504,8.974146954879252,26.050290075535422,2.4390552811342747,25.0,3.8715615973877266,33.42462260831035,17.906281275047576,evaluator
|
| 24 |
+
crater_rim_survey,0.19998288464547984,0.030212343262724605,0.019998098497353848,24,4,12.755009848307738,1.125500238982686,0.20034875217783665,196.78548849672512,26.799532361513545,17.85871112839597,7.733793012515015,2.255067532533772,34.680572893213636,39.11849233156485,evaluator
|
| 25 |
+
crater_rim_survey,0.19994659170934925,0.030225759225313672,0.019998474775986135,24,4,25.912964987324678,1.10935292429397,0.1212039704796571,6.548536080033266,12.959459668519187,2.4113643308563706,9.579072666008036,5.346733853199638,34.75173490655878,45.247952962638074,evaluator
|
| 26 |
+
crater_rim_survey,0.19989408551282212,0.030130993945979984,0.01999512793426261,24,4,6.629521847447889,0.7022225480133527,0.11816213322028937,62.621261457790624,12.737361809298234,17.30504052755663,14.215431729860589,4.646482139873441,34.371917413004155,27.71734901517685,evaluator
|
| 27 |
+
crater_rim_survey,0.19999828858438273,0.0301619482864442,0.01999991361391298,24,4,9.627334404382893,1.1721434753302948,0.12531789325058865,83.01067504182704,12.631405267602263,17.443294853148082,21.51054102614195,6.9337647081009885,34.528933692574675,32.12346883580072,evaluator
|
| 28 |
+
crater_rim_survey,0.1952066074556897,0.030208110186515022,0.019998098535041933,24,4,4.200760509028835,0.9285303718390917,0.24093368671451512,194.93850095281107,26.58273228047366,4.567260654470145,25.0,18.138874465159706,33.877416054371125,20.305673832827242,evaluator
|
| 29 |
+
crater_rim_survey,0.19997541938513752,0.030210600032861874,0.01999480533631987,24,4,0.8243151514801373,1.0916940373390942,0.12153592409393749,27.21004465383342,13.998247884409631,4.484651162830488,19.420976887332465,3.057372979463376,33.06697485822573,12.541076632781099,evaluator
|
| 30 |
+
crater_rim_survey,0.19999831477043908,0.030025601417783404,0.01999981499921321,24,4,12.566677732068761,1.1087973059317935,0.1194711049138735,74.31940606349654,11.733229145668608,17.73531146515037,18.63591192103689,7.090258141531174,34.63024752334344,36.09332952315394,evaluator
|
| 31 |
+
crater_rim_survey,0.19992738362360304,0.03003854921672812,0.01999978200123614,24,4,0.8940862451343436,1.1087973059317935,0.11801969071068683,77.56309429824228,13.95056805818217,4.430691369415018,10.063761931249484,1.8449038774055322,33.174612863955836,13.150317195886718,evaluator
|
| 32 |
+
crater_rim_survey,0.1999998267581842,0.030044158833755413,0.019999888079936544,24,4,6.521319554271298,0.5447001379848206,0.11943090236622705,74.18031107617836,13.637844175820277,18.54953477077168,8.672461738129524,3.0249930938135954,34.40747426368394,28.439171314791846,evaluator
|
| 33 |
+
crater_rim_survey,0.19996915094368273,0.03021124529636843,0.019999851980371464,24,4,1.4181397371099358,1.0108223196481498,0.1868290719919364,9.336019400593369,13.36001115638058,4.946013755270927,25.0,54.93100001380934,33.228393196922866,13.576392378046279,evaluator
|
| 34 |
+
crater_rim_survey,0.19998346056624744,0.030219711599665647,0.019999783704246595,24,4,11.513336098021568,1.0063461590300977,0.1868290719919364,10.326856203447273,25.31491204627705,5.180452589423311,4.653545848554822,1.4532239938067297,34.3945159669859,28.266892399852406,evaluator
|
| 35 |
+
crater_rim_survey,0.19996672942558297,0.030025908200769946,0.019997587275357605,24,4,12.826042313649488,1.1091042442285663,0.12155244879304188,98.3672578987437,14.793448257903915,18.101714223744423,2.0146495953268406,1.231207327039812,34.64936222079686,37.1334283234286,evaluator
|
| 36 |
+
crater_rim_survey,0.1999972589053633,0.030161993909901784,0.019997720223840953,24,4,27.43230645259139,0.47923049257521577,0.12079950503702129,182.5597417006075,5.847749380278069,2.876114908955059,25.0,15.367343631821818,34.77608578257913,49.07517029752097,evaluator
|
| 37 |
+
crater_rim_survey,0.1998503628815009,0.030211453382063477,0.01999399066062191,24,4,2.875649776832269,1.1055497642968386,0.1902836920482329,7.276332106252866,26.460483867131103,2.134751949511693,1.166746511598552,0.5918528066324912,33.40571628318376,14.908205440137719,evaluator
|
| 38 |
+
crater_rim_survey,0.1999453360698079,0.0302188144758805,0.019998614778199197,24,4,1.234189460708043,0.5027562960276755,0.12090449753720407,6.534935760589103,12.895912713147577,2.7952935952768327,25.0,5.624175762146862,32.95356172911064,11.868073539972091,evaluator
|
| 39 |
+
crater_rim_survey,0.19997505983140038,0.03001131130467081,0.019997593544333843,24,4,1.203028996067899,1.0948232403200435,0.11689308183718175,10.155484377260683,14.225659955951887,0.6694089854888206,2.916745322871573,0.8335843719001543,32.76137243310877,10.772338411573678,evaluator
|
| 40 |
+
crater_rim_survey,0.19996049402745086,0.030208672999496103,0.01999810016708055,24,4,3.084264550682021,1.1412822471359005,0.1156456209596583,200.41968111946736,12.012126079302831,18.536282533125284,20.40567580139513,4.705616618897112,34.24821431636473,25.07383673635179,evaluator
|
| 41 |
+
crater_rim_survey,0.1998935869093835,0.03011469097731069,0.019998792800966832,24,4,3.061583897423999,0.7232072925051825,0.1766777774406931,62.39608448419539,24.16924103447909,18.34887408862155,1.0080703174964334,0.6544225831419768,34.215579343120076,24.403572460978662,evaluator
|
| 42 |
+
crater_rim_survey,0.19995895850001952,0.030153428142367652,0.019991570018008036,24,4,8.083429534986386,0.5974179640374865,0.19851611329649577,194.37858518803256,25.411008777392365,1.0320005391055362,23.61486792806271,3.631400060266274,34.159167623367615,23.469895643478004,evaluator
|
| 43 |
+
crater_rim_survey,0.19994681067429088,0.030215425614192753,0.01999950890415926,24,4,32.14367050914465,0.8544890547835011,0.12123970470959237,5.720429072348487,6.266049326748184,4.605741185152444,25.0,18.935775344242035,34.78753528061201,54.45576419541737,evaluator
|
| 44 |
+
crater_rim_survey,0.19997541938513752,0.030214197093595848,0.01999480533631987,24,4,1.256673362201843,1.0937561032542502,0.11689308183718175,10.155484377260683,5.232187376949527,0.6766584919613376,25.0,68.54435966398616,32.63941387224673,10.251274243061342,evaluator
|
| 45 |
+
crater_rim_survey,0.1999302078193984,0.030025907534493097,0.01999978200123614,24,4,13.850086445341361,1.0116393663165382,0.1823339515046686,9.995989262321892,12.374546590079678,4.996055706599398,25.0,41.57925240511967,34.479072326151964,30.433088358004877,evaluator
|
| 46 |
+
crater_rim_survey,0.19982567550391447,0.0302188258001639,0.019998828755911417,24,4,22.42907010712495,1.1324298177762144,0.19284659557305986,82.37016760409517,24.44552701676996,17.593253842836596,10.23490561531125,3.969267927575843,34.779199243095405,50.65446313371426,evaluator
|
| 47 |
+
crater_rim_survey,0.19996915094368273,0.030212334662223245,0.019999873541036467,24,4,1.4181397371099358,1.0092034250635393,0.19933257379635824,183.34599492662016,27.24985783649888,15.777620264646071,7.604706374755984,1.5021196705386648,34.09622340578168,22.433203067983463,evaluator
|
| 48 |
+
crater_rim_survey,0.19991035441242636,0.03021404045288451,0.019998974136648747,24,4,1.0849572306739068,0.6907519778873832,0.17788556163701721,61.65024271909051,5.077339227965677,0.6816791635367268,25.0,152.9869710662552,32.753633480346835,10.800787092009811,evaluator
|
| 49 |
+
crater_rim_survey,0.19994604561822632,0.030225759225313672,0.019999135459863464,24,4,1.652882421929199,1.1077730078299493,0.12142218628500301,6.548536080033266,12.959459668519187,2.2233068460516243,25.0,5.569270383645587,32.99927610913862,12.133221098937998,evaluator
|
| 50 |
+
crater_rim_survey,0.1999715897464899,0.03000295868683525,0.019999771924460973,24,4,12.849906392433654,1.1043183840231139,0.12154572699696423,78.35665817016414,12.38654337768794,17.516181134539423,16.460037623691274,6.347591308681268,34.637646236539005,36.454983088793135,evaluator
|
| 51 |
+
crater_rim_survey,0.19999828858438273,0.030224148010901272,0.01999991361391298,24,4,9.627334404382893,1.1721434753302948,0.12531789325058865,77.42892437103957,12.631405267602263,18.54953477077168,21.138985125584643,6.9337647081009885,34.54275427379168,32.66660145616334,evaluator
|
reports/pareto_fronts/front_crater_rim_survey.metadata.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"scenario_name": "crater_rim_survey",
|
| 3 |
+
"backend": "evaluator",
|
| 4 |
+
"dataset_version": "v9",
|
| 5 |
+
"objectives": [
|
| 6 |
+
{
|
| 7 |
+
"target": "range_km",
|
| 8 |
+
"direction": "max"
|
| 9 |
+
},
|
| 10 |
+
{
|
| 11 |
+
"target": "total_mass_kg",
|
| 12 |
+
"direction": "min"
|
| 13 |
+
},
|
| 14 |
+
{
|
| 15 |
+
"target": "slope_capability_deg",
|
| 16 |
+
"direction": "max"
|
| 17 |
+
}
|
| 18 |
+
],
|
| 19 |
+
"constraints": [
|
| 20 |
+
{
|
| 21 |
+
"target": "range_km",
|
| 22 |
+
"sense": "min",
|
| 23 |
+
"value": 0.1
|
| 24 |
+
}
|
| 25 |
+
],
|
| 26 |
+
"traverse_distance_m": 25000.0,
|
| 27 |
+
"population_size": 50,
|
| 28 |
+
"generations": 60,
|
| 29 |
+
"seed": 12,
|
| 30 |
+
"panel_tilt_deg": 0.0,
|
| 31 |
+
"panel_azimuth_deg": 180.0,
|
| 32 |
+
"elapsed_s": 32.589310958981514,
|
| 33 |
+
"pareto_size": 50,
|
| 34 |
+
"front_csv": "reports/pareto_fronts/front_crater_rim_survey.csv"
|
| 35 |
+
}
|
reports/pareto_fronts/front_equatorial_mare_traverse.csv
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
scenario_name,wheel_radius_m,wheel_width_m,grouser_height_m,grouser_count,n_wheels,chassis_mass_kg,wheelbase_m,solar_area_m2,battery_capacity_wh,avionics_power_w,peak_wheel_torque_nm,range_km,energy_margin_raw_pct,slope_capability_deg,total_mass_kg,backend_used
|
| 2 |
+
equatorial_mare_traverse,0.17966028565146944,0.03039956760018241,0.008182229457302601,24,4,0.5380833748056679,0.4335806040369062,0.10165876619272624,8.997804783460904,5.356359047610721,0.3781790734474563,80.0,92.96340914451272,26.93473844847004,9.64490746552987,evaluator
|
| 3 |
+
equatorial_mare_traverse,0.19986128212195706,0.030292096427161217,0.019997073043771345,24,4,11.214649678956356,0.34573992106280527,0.150995512269261,29.873149349758204,29.34811017392639,11.920699578672703,0.8848064761851875,0.6385829045982598,34.54441144533367,32.907370502148154,evaluator
|
| 4 |
+
equatorial_mare_traverse,0.19997745599465494,0.030590948424177592,0.019999805514851414,24,4,25.768558209445658,0.9994731630133085,0.7597096755482708,11.240839905422256,12.554063797915965,14.310524706339239,80.0,438.32027744159217,34.788542594127875,54.75163640658672,evaluator
|
| 5 |
+
equatorial_mare_traverse,0.19367225817890393,0.03021385817426154,0.010130029878649197,24,4,0.5187433614190687,1.0181926341630547,0.10201064455895326,8.477782035487206,5.59173017600839,0.36078609602362616,80.0,88.1452358263669,28.342398331472445,9.76758401051974,evaluator
|
| 6 |
+
equatorial_mare_traverse,0.1935713769877956,0.03040622812539698,0.01867371181419051,22,4,0.533038056847511,1.0183338488033367,0.10210209288863026,9.038212012542584,5.583683998672508,0.36035876550515233,80.0,87.94761332050398,31.324903037607154,9.95353244985581,evaluator
|
| 7 |
+
equatorial_mare_traverse,0.1998128499767734,0.030055752455620845,0.019994827108702255,24,4,8.33812087873973,0.38532716546010376,0.1428739063393388,39.11452770746371,26.873862672526183,2.795460172362963,20.650998542349807,1.3882667214240019,34.18822447351292,23.918055580181665,evaluator
|
| 8 |
+
equatorial_mare_traverse,0.19986175521736724,0.03016560484862971,0.019972577000892826,24,4,12.101834660177074,0.32716952855232856,0.15074980107502692,487.5667435484656,26.593798194207228,16.232700999904782,57.976983664098285,2.592284850127033,34.70215365019747,41.45454780240283,evaluator
|
| 9 |
+
equatorial_mare_traverse,0.19994534055113247,0.030078855288681147,0.01999702870514257,24,4,25.54485632887412,1.006443364482119,0.10109684633712794,45.527385180641474,12.85157524204105,11.83971534500576,76.19583434819337,8.90737310012355,34.782453445946786,51.235195937509374,evaluator
|
| 10 |
+
equatorial_mare_traverse,0.19985376968358456,0.03007090038344987,0.019999763814230978,24,4,21.933767522829896,1.0708468804465745,0.1469879834079606,41.06803028889759,26.794861772185044,11.785105991640807,25.44096133600444,2.5325356307348654,34.766461825720114,47.3445123239427,evaluator
|
| 11 |
+
equatorial_mare_traverse,0.19999962921381634,0.0300427481864885,0.019993947750907595,24,4,15.388759224984664,0.9753088129443631,0.14831272139917662,10.275876201402788,26.55630181292349,11.939116010436774,40.91578906554164,3.298506593369386,34.666510080167996,38.16012698013008,evaluator
|
| 12 |
+
equatorial_mare_traverse,0.199857502461715,0.030285266221320408,0.019994931130989862,24,4,15.932169081616458,0.32660313476078584,0.7838793632444141,29.873149349758204,26.484977529325235,16.68279419246426,80.0,319.3303484555587,34.73613333061206,43.87679063334305,evaluator
|
| 13 |
+
equatorial_mare_traverse,0.19985807538289835,0.0300174549688136,0.019999194343062642,24,4,11.188699621510814,1.0494095592429245,0.14831272139917662,10.351990827401869,26.74077236547027,11.939116010436774,44.550540873027416,3.0736281681181556,34.53998009839563,32.455308099351626,evaluator
|
| 14 |
+
equatorial_mare_traverse,0.19985805142217997,0.030016756711533872,0.019995683385887503,24,4,15.744376191482553,1.0497694789002703,0.14831272139917662,477.8318325909422,27.189950740722384,15.664920217815917,29.85045207379336,1.7838777610712244,34.756872876820495,46.0143634495506,evaluator
|
| 15 |
+
equatorial_mare_traverse,0.19966907349277724,0.030039508757413364,0.01999307247729388,24,4,5.952954658210377,1.046062952056694,0.7578526886638339,478.38118535142047,13.201823888582929,2.6987140796568543,80.0,594.5632250208669,34.32961057797897,26.760520715195494,evaluator
|
| 16 |
+
equatorial_mare_traverse,0.1998515298002952,0.0302929146926154,0.019972577000892826,24,4,11.395558294570193,0.33527835326818295,0.14756155855198724,487.5667435484656,26.593798194207228,3.5619149115492164,53.39832356576964,2.085782404713479,34.55285139075893,33.5943089195924,evaluator
|
| 17 |
+
equatorial_mare_traverse,0.19974641540312468,0.030016789354282942,0.019995420244159165,24,4,9.356944952815827,1.0494095592429245,0.10360897055444673,14.680370919293011,12.792421677714248,13.057195999487226,80.0,10.167063802212015,34.443264949324245,29.518162795418633,evaluator
|
| 18 |
+
equatorial_mare_traverse,0.1935713769877956,0.030407530595858958,0.01934035775008197,24,4,0.5380833748056679,0.43422100296046434,0.10210209288863026,8.997804783460904,5.356335286024761,0.3781774863620438,80.0,91.12535658613218,32.22545628703486,10.001935148524822,evaluator
|
| 19 |
+
equatorial_mare_traverse,0.19358329655127376,0.03023763610346199,0.018572231014876296,24,4,0.5187433614190687,0.4491942308023737,0.10211767263101934,13.26248663503225,5.3643309468763105,0.378603229795105,80.0,90.9213139053419,31.93074417292202,9.997205457773376,evaluator
|
| 20 |
+
equatorial_mare_traverse,0.19997745599465494,0.03059269339327144,0.01999999882566245,24,4,25.768558209445658,1.0669904404357518,0.1817144976105955,11.240839905422256,12.554063797915965,13.779011643152165,80.0,43.835072948737704,34.785832143539004,52.49610788743395,evaluator
|
| 21 |
+
equatorial_mare_traverse,0.1935713769877956,0.03040622812539698,0.01934035775008197,22,4,0.5380833748056679,1.0183338488033367,0.10210209288863026,9.038212012542584,5.583683998672508,0.36035876550515233,80.0,87.89109854365401,31.576335059089573,9.973505879649348,evaluator
|
| 22 |
+
equatorial_mare_traverse,0.19966355239944847,0.0300446395164878,0.01999706064619622,24,4,5.952954658210377,1.041114969501095,0.1616174409348035,347.9614005277324,9.628382727098286,1.7640764459199434,80.0,85.59157557395393,34.102303446003596,22.501680950979612,evaluator
|
| 23 |
+
equatorial_mare_traverse,0.19985805142217997,0.03004037491195512,0.01999480882345419,24,4,12.349119305999292,1.0494095592429245,0.14834306164880967,10.011124389013247,26.488776858237145,15.659920068685839,44.96685131068038,3.388938297129835,34.624664334196964,36.040290827306876,evaluator
|
| 24 |
+
equatorial_mare_traverse,0.1933746459726256,0.03004001228993762,0.019986651538182895,24,4,0.5258967446130819,0.43422100296046434,0.10263326713930987,8.997804783460904,9.210509800400638,0.3781774863620438,80.0,47.29375568445724,32.5521141515573,10.236996823155255,evaluator
|
| 25 |
+
equatorial_mare_traverse,0.19994534055113247,0.03009193665409114,0.01999702870514257,24,4,6.903528773962815,0.9249366376279518,0.14702488780463763,45.68867788372448,12.85157524204105,3.2116874804077393,80.0,52.486537897478435,34.02581809580667,21.330645694424845,evaluator
|
| 26 |
+
equatorial_mare_traverse,0.19985847646075183,0.030016862079877728,0.019999995135702837,24,4,22.268500110648358,1.0734554446022797,0.14954803139300665,27.889456251127328,26.368472273951667,14.12003253841739,39.024241876707414,3.718717667055279,34.774705221345975,48.89754263948475,evaluator
|
| 27 |
+
equatorial_mare_traverse,0.19985807538289835,0.030016862079877728,0.019999194343062642,24,4,22.17579647692005,1.071451762183981,0.14954803139300665,8.799792375475512,26.74077236547027,14.12437886704558,32.956877518960795,3.3935299592864703,34.77299254617588,48.5825915873951,evaluator
|
| 28 |
+
equatorial_mare_traverse,0.19966119219019107,0.030044952808417978,0.01999706064619622,24,4,5.403081896575908,1.0412539193344732,0.1616174409348035,347.9614005277324,9.009317858790915,14.48442140465549,80.0,76.97735443086296,34.40891539926693,28.63523322358909,evaluator
|
| 29 |
+
equatorial_mare_traverse,0.19985805142217997,0.03001066404786041,0.019995683385887503,24,4,11.149556205329999,1.0494095592429245,0.14831272139917662,10.029767241614959,26.485506975304858,15.522273300235906,47.29892440150306,3.3850587930751184,34.58761364785491,34.33086196577899,evaluator
|
| 30 |
+
equatorial_mare_traverse,0.19985805142217997,0.030016676144910225,0.019995683385887503,24,4,16.028392890908183,0.9866980087447234,0.14831272139917662,10.011124389013247,26.488776858237145,15.664920217815917,38.994433231808735,3.381057911897182,34.708431071059,41.04805313014547,evaluator
|
| 31 |
+
equatorial_mare_traverse,0.1999583524211712,0.03033349152216959,0.01997018794400092,24,4,1.5398664290166044,1.014365608181424,0.15665320545287995,22.474954156559676,12.357787895460008,2.423970111799072,80.0,79.65846754167418,33.17686877614042,13.355073968528412,evaluator
|
| 32 |
+
equatorial_mare_traverse,0.1996695373567177,0.03006438152406502,0.01999307247729388,24,4,20.704781058302164,1.0452252084015694,0.14888821859776286,27.822697889754295,23.924731614151607,13.307073411160319,78.1415940892455,6.403448719515403,34.7533500308139,46.15941792774388,evaluator
|
| 33 |
+
equatorial_mare_traverse,0.19985807538289835,0.030015052311930544,0.01999542011268096,24,4,11.33756413877837,1.0494095592429245,0.14950151610999843,10.419531591569601,26.74077236547027,11.939116010436774,50.22323995266236,3.3815452819831853,34.54432297953303,32.66244793189408,evaluator
|
| 34 |
+
equatorial_mare_traverse,0.19964258777339933,0.030017418025845753,0.019998128751297695,24,4,13.233399834840787,1.0057904820957306,0.1451896681354153,28.050395950957103,26.150351986524512,15.318515395696526,37.04935403657662,2.885176801765543,34.646433001143485,37.22531901960305,evaluator
|
| 35 |
+
equatorial_mare_traverse,0.1999583524211712,0.0303339560382081,0.019999157594776645,24,4,0.5052566360827521,1.0396428840727354,0.16066278288365138,105.73909290015905,11.560411277399552,1.7693457716409773,80.0,91.93898557713827,33.05337390049307,12.495106123858172,evaluator
|
| 36 |
+
equatorial_mare_traverse,0.1999583524211712,0.030334607269122595,0.01999934290297604,24,4,0.5052566360827521,1.0396428840727354,0.7990788670969965,14.189059764706762,12.07537528311951,2.2724620517884784,80.0,801.6293705232293,33.272525778296924,13.937763452882253,evaluator
|
| 37 |
+
equatorial_mare_traverse,0.19971784842981258,0.030069158917998006,0.019995426062630075,24,4,11.368867547401816,1.0733692156293864,0.1498497541055819,11.757891612603204,26.68860759970813,14.20621993487694,51.21778072068148,3.5115851675165755,34.57471115815943,33.954456375216,evaluator
|
| 38 |
+
equatorial_mare_traverse,0.19997745599465494,0.030590948424177592,0.019999655485740328,24,4,1.2134545901091442,0.9985238224071419,0.1534391523521108,69.86899513361027,14.446645859539778,13.714484039162045,80.0,50.41660485632228,33.88376697711659,19.743693551648757,evaluator
|
| 39 |
+
equatorial_mare_traverse,0.19986838755162878,0.030039428190985724,0.01999307247729388,24,4,6.19588465451847,0.9829914819011443,0.15618450898803787,15.054508849986405,12.50065528735485,3.5520298360630997,80.0,66.20414614087082,33.94268454381091,20.20850212889211,evaluator
|
| 40 |
+
equatorial_mare_traverse,0.19994517613869897,0.030078855288681147,0.019999185848925443,24,4,25.218122418225008,1.04008329455604,0.10109684633712794,43.55701368606013,12.084463481396364,11.83971534500576,80.0,10.06993741825928,34.7819662394364,50.71608255386488,evaluator
|
| 41 |
+
equatorial_mare_traverse,0.19971470186150325,0.03003032999949951,0.019993081924397287,24,4,1.5682617332417674,1.0161668869061726,0.7309395961765106,386.93665335424157,9.334591124246263,1.1953343132698755,80.0,787.8628237372915,33.805659765506434,18.58358926501375,evaluator
|
| 42 |
+
equatorial_mare_traverse,0.19985691687481746,0.030016862079877728,0.019999194343062642,24,4,22.268500110648358,1.0667418817876002,0.14954803139300665,11.706344655679462,12.507685826610984,2.9051434016329756,80.0,32.48341338633476,34.717480946137556,41.666420023837205,evaluator
|
| 43 |
+
equatorial_mare_traverse,0.19989261816031834,0.030228370637315677,0.01999282288537965,24,4,1.5130409196645478,0.36766328183001656,0.10166392998974953,14.556523449331053,5.629633137351526,0.45723081822562905,80.0,81.10513612391884,32.8849907650051,11.506089083621763,evaluator
|
| 44 |
+
equatorial_mare_traverse,0.19969642451926337,0.03007090038344987,0.01999312655720344,24,4,21.262562159833283,1.0775949505575504,0.11875173698299846,27.65841864031782,12.690783798287889,13.215654674918527,80.0,13.357246541063084,34.75264824096287,46.00061079081805,evaluator
|
| 45 |
+
equatorial_mare_traverse,0.19985847646075183,0.03008347199737673,0.019999194343062642,24,4,22.268500110648358,1.0040060037648284,0.15005532889257034,7.0360279573693845,26.689341095544233,11.810728156931358,36.132833036869016,3.607056393338314,34.766720746235855,47.43218711507268,evaluator
|
| 46 |
+
equatorial_mare_traverse,0.19974641540312468,0.030016789354282942,0.019995420244159165,24,4,9.356944952815827,0.42977946305513054,0.140338279518992,15.652043507364441,12.7070141634327,12.169287616284453,80.0,37.66186001797165,34.43042214376875,29.165017253248095,evaluator
|
| 47 |
+
equatorial_mare_traverse,0.19985847646075183,0.030016862079877728,0.019999194343062642,24,4,22.268500110648358,1.0732663860256937,0.14954803139300665,11.706344655679462,26.689341095544233,14.12003253841739,33.75142447865225,3.4339290014019213,34.77368328020459,48.735841111783905,evaluator
|
| 48 |
+
equatorial_mare_traverse,0.19985805142217997,0.03001066404786041,0.019995683385887503,24,4,11.08574900800416,1.0494095592429245,0.1483076308151734,10.029767241614959,26.485506975304858,15.522273300235906,47.402902808659036,3.383736433686447,34.585516337064426,34.24401581349122,evaluator
|
| 49 |
+
equatorial_mare_traverse,0.19966907349277724,0.030296207050194892,0.019985003378199175,24,4,0.5157781225544853,1.039298098202913,0.1616174409348035,342.7997905737702,9.334591124246263,2.8189427606079764,80.0,106.25698034063112,33.481767291000224,15.615513453156433,evaluator
|
| 50 |
+
equatorial_mare_traverse,0.19999962921381634,0.030406004506259292,0.019993947750907595,24,4,0.6682030309261924,0.9762472994028375,0.14831272139917662,9.034927626034252,26.55630181292349,11.939116010436774,80.0,3.298506593369386,33.75048980231638,18.139323022562667,evaluator
|
| 51 |
+
equatorial_mare_traverse,0.19986838755162878,0.030039508757413364,0.01999307247729388,24,4,5.952954658210377,1.046062952056694,0.15618450898803787,478.38118535142047,13.201823888582929,3.5520298360630997,80.0,48.6842218318719,34.25684578398426,25.179760555145204,evaluator
|
reports/pareto_fronts/front_equatorial_mare_traverse.metadata.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"scenario_name": "equatorial_mare_traverse",
|
| 3 |
+
"backend": "evaluator",
|
| 4 |
+
"dataset_version": "v9",
|
| 5 |
+
"objectives": [
|
| 6 |
+
{
|
| 7 |
+
"target": "range_km",
|
| 8 |
+
"direction": "max"
|
| 9 |
+
},
|
| 10 |
+
{
|
| 11 |
+
"target": "total_mass_kg",
|
| 12 |
+
"direction": "min"
|
| 13 |
+
},
|
| 14 |
+
{
|
| 15 |
+
"target": "slope_capability_deg",
|
| 16 |
+
"direction": "max"
|
| 17 |
+
}
|
| 18 |
+
],
|
| 19 |
+
"constraints": [
|
| 20 |
+
{
|
| 21 |
+
"target": "range_km",
|
| 22 |
+
"sense": "min",
|
| 23 |
+
"value": 0.1
|
| 24 |
+
}
|
| 25 |
+
],
|
| 26 |
+
"traverse_distance_m": 80000.0,
|
| 27 |
+
"population_size": 50,
|
| 28 |
+
"generations": 60,
|
| 29 |
+
"seed": 13,
|
| 30 |
+
"panel_tilt_deg": 20.2,
|
| 31 |
+
"panel_azimuth_deg": 180.0,
|
| 32 |
+
"elapsed_s": 36.020679499953985,
|
| 33 |
+
"pareto_size": 50,
|
| 34 |
+
"front_csv": "reports/pareto_fronts/front_equatorial_mare_traverse.csv"
|
| 35 |
+
}
|
reports/pareto_fronts/front_highland_slope_capability.csv
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
scenario_name,wheel_radius_m,wheel_width_m,grouser_height_m,grouser_count,n_wheels,chassis_mass_kg,wheelbase_m,solar_area_m2,battery_capacity_wh,avionics_power_w,peak_wheel_torque_nm,range_km,energy_margin_raw_pct,slope_capability_deg,total_mass_kg,backend_used
|
| 2 |
+
highland_slope_capability,0.19999848849450302,0.030191849818629372,0.019503901234723633,24,4,0.5041814195273064,1.0339532362824977,0.18372809307203547,8.814768033990617,6.187941532527164,0.38264783264486746,48.169395364533685,285.38902015497695,19.358220439857405,8.332642941714546,evaluator
|
| 3 |
+
highland_slope_capability,0.19999784996301712,0.03825311376785551,0.01999807835169149,24,4,16.563387617513204,0.6591162728323452,0.374322644299933,242.77327514869162,9.960242557868893,2.5411140296122454,55.7123721743145,339.7824377276585,18.827464701217636,35.484425937053835,evaluator
|
| 4 |
+
highland_slope_capability,0.1999835438154197,0.03006780849686012,0.019999190913774154,24,4,0.5144281370934042,0.9468944568649806,0.3294369457766206,28.79990079853653,5.038143744898282,0.4754272153806184,52.541118043484495,665.3410489894707,19.526195803530094,9.043077395342834,evaluator
|
| 5 |
+
highland_slope_capability,0.19999835074990116,0.03044576666813941,0.01996011148891477,24,4,0.5041814195273064,1.0339532362824977,0.22461747600265922,67.99541052473444,5.036891183253616,0.3822386329974194,53.4576626275889,420.4578331795287,19.514393389934455,9.091502146875467,evaluator
|
| 6 |
+
highland_slope_capability,0.19998894837078124,0.03825311376785551,0.019998911527639163,24,4,15.356978994368717,0.6898335099413058,0.3286533656886073,236.63576080190361,5.508166295790697,1.4611285794677664,55.68940981812972,429.98378692054825,18.939054446700876,32.72693115224429,evaluator
|
| 7 |
+
highland_slope_capability,0.1999980045662266,0.031419879565108576,0.01999905400639963,24,4,12.36039479388276,0.9910410313276625,0.5922682236436743,236.63576080190361,9.902513664984559,2.2624638446300454,55.68903883747236,630.7762588539654,18.79651831977162,29.810948175599876,evaluator
|
| 8 |
+
highland_slope_capability,0.19996676140645783,0.0303384365525574,0.019991725260512457,24,4,0.5343027210561805,1.0239956207421692,0.2576369066630436,7.0074241260819115,5.105693858486826,0.46156729304200167,50.66701298662335,500.383525641933,19.523301460621557,8.594111000729022,evaluator
|
| 9 |
+
highland_slope_capability,0.1999980045662266,0.030538715527596853,0.01999905400639963,24,4,10.740125190250282,0.9910410313276625,0.5934680611965933,118.82840807769378,5.367391643685564,2.2624638446300454,55.655143237460656,945.9138338194754,18.951477536268168,25.905012798255246,evaluator
|
| 10 |
+
highland_slope_capability,0.19998916381541815,0.03044701010866504,0.01999946602876711,24,4,9.077770582133347,0.6709527621176541,0.8203114198329903,98.7530892529269,7.570862699148428,1.5962605279388438,55.60368344035622,1149.2191955652554,19.04225606029462,23.96786726815359,evaluator
|
| 11 |
+
highland_slope_capability,0.19857796534203842,0.030336490967733706,0.019987001874135026,24,4,0.5349910769796431,1.049227061847042,0.1908922885738742,5.705487679018481,5.151443096881851,0.46156729304200167,48.90737423527232,345.6119512671572,19.493589822633645,8.344569065563995,evaluator
|
| 12 |
+
highland_slope_capability,0.19999347496273354,0.03033043899949897,0.0199984185258365,24,4,4.980024417813447,0.623631608542796,0.7614646892287258,130.99452458737494,9.841438785384224,0.7594368566183693,55.290707488010575,955.308606031738,19.294965679979192,18.248055911381513,evaluator
|
| 13 |
+
highland_slope_capability,0.19999835074990116,0.03044576666813941,0.01999922235492463,24,4,0.5041814195273064,1.0339532362824977,0.3004569963923524,67.37948345580715,5.0369012412591605,0.3822386329974194,53.92521720648769,592.9600848489374,19.527268438617007,9.343364275048636,evaluator
|
| 14 |
+
highland_slope_capability,0.19993732733121755,0.030207138164324628,0.019987778068108725,24,4,0.5102718385157966,1.0221756910863005,0.302769991301532,10.445476161261759,5.185173397816671,0.46174803876281845,51.51909029689274,596.6423096064752,19.521615876686496,8.750083551891723,evaluator
|
| 15 |
+
highland_slope_capability,0.19996649876855524,0.030340425107676827,0.019503901234723633,24,4,0.5032973318270852,0.9447639155933812,0.2249674392538346,11.586887742807587,5.039055563382784,0.42069323538181075,50.044271582844274,429.3362027988452,19.358387410514634,8.455619432424527,evaluator
|
| 16 |
+
highland_slope_capability,0.19999835074990116,0.0304458708888056,0.01987247008493646,24,4,0.5041814195273064,1.0002117848272727,0.22461747600265922,14.285697236313268,5.036891183253616,0.3822386329974194,50.36390115979733,428.49102132151734,19.483455332288596,8.480558833243343,evaluator
|
| 17 |
+
highland_slope_capability,0.19996651173370084,0.03021808125803219,0.019998949931617296,24,4,0.5170130538330862,0.9468944568649806,0.29991927731405765,28.981142249400037,5.038143744898282,0.46051078696912895,52.25980019909972,598.1559640983224,19.526242660986977,8.950308334567735,evaluator
|
| 18 |
+
highland_slope_capability,0.1999291589241574,0.03000266064576263,0.01999807494502127,24,4,0.5170130538330862,1.0253577807898524,0.5087841584659651,28.981142249400037,5.038143744898282,0.9784760955537699,54.07347321808152,1062.7549604399037,19.520472930391573,9.92765517119426,evaluator
|
| 19 |
+
highland_slope_capability,0.1999939206924001,0.030493804802949578,0.019963921423231987,24,4,0.6358529057733895,0.668307388853824,0.6263552904405267,30.150383184154038,5.736997111214297,0.881425768432651,54.17150521235519,1220.2894440556825,19.507012230811625,10.530932966531829,evaluator
|
| 20 |
+
highland_slope_capability,0.19997527218483024,0.03030670638658543,0.0199996928919978,24,4,0.5871179443968604,0.8595883333536966,0.7816897306387341,128.37097130958628,8.167194461947945,2.175455036975377,54.665974063280686,1191.123169533209,19.47348893238036,12.964337575988248,evaluator
|
| 21 |
+
highland_slope_capability,0.199928310537739,0.030504621232126005,0.01999637573881543,24,4,3.5277885049333144,0.6926966270706141,0.5123919074047064,184.37673627662755,9.714433321415187,0.8748561329973845,55.05377972608475,631.8990272117136,19.3788747002025,16.095446112151297,evaluator
|
| 22 |
+
highland_slope_capability,0.19999835074990116,0.03044576666813941,0.01996011148891477,24,4,0.5041814195273064,0.9879669107502445,0.23330198548584838,67.99541052473444,5.154176815224884,0.3822386329974194,53.58614590274415,433.30357235332366,19.514372772015683,9.129026962536603,evaluator
|
| 23 |
+
highland_slope_capability,0.19996651173370084,0.03021808125803219,0.019996459388944107,24,4,0.5170130538330862,0.9781113097640339,0.29991927731405765,25.051634732743803,5.038143744898282,0.4617300956398033,51.92774710815905,599.1101168097003,19.5253739411876,8.906358322514944,evaluator
|
| 24 |
+
highland_slope_capability,0.19999800471187312,0.036957077491068316,0.0199977099302605,24,4,14.971442267880622,1.0329313216405829,0.3655008466601977,242.77327514869162,10.377888979922377,2.362663500472295,55.7021067261856,328.10277912517216,18.876365191575548,33.13016599547098,evaluator
|
| 25 |
+
highland_slope_capability,0.19998887972505716,0.03005930583668712,0.01999871079513833,24,4,5.771287224310623,1.0328274643887883,0.7036599749071026,129.7733760739555,8.758444076052633,1.476551439030848,55.38941132728368,936.172087943247,19.238651794636237,19.41226687835707,evaluator
|
| 26 |
+
highland_slope_capability,0.19998353702611302,0.030308149877947544,0.019997562963086364,24,4,1.6053186315392285,0.8071377877336818,0.7815868758436406,128.37097130958628,5.158058190856068,2.0873952633707273,54.836640176474155,1557.8959047555377,19.441976688723503,14.097003173172332,evaluator
|
| 27 |
+
highland_slope_capability,0.19999848849450302,0.030191849818629372,0.019503901234723633,24,4,0.5041814195273064,1.0339532362824977,0.22461747600265922,8.814768033990617,5.047349979907862,0.38264783264486746,49.719661766773115,428.82775138027114,19.358495047584135,8.394142773204312,evaluator
|
| 28 |
+
highland_slope_capability,0.19999745834160243,0.03006780849686012,0.01999670041992606,24,4,0.51348118320767,0.9468944568649806,0.22176515741688801,28.893266252190507,5.038143744898282,0.4766465240512927,51.11324921421623,419.5173565807165,19.525291713931367,8.677273459681214,evaluator
|
| 29 |
+
highland_slope_capability,0.19998353702611302,0.030308345502431264,0.01998770551512448,24,4,1.6062777934791779,0.8071377877336818,0.24596890590024587,128.37097130958628,5.158058190856068,0.5082680005961928,54.38297670618054,442.86436041020113,19.50239511500091,11.416388219948868,evaluator
|
| 30 |
+
highland_slope_capability,0.1999781396463912,0.030358115056512508,0.019999109660634652,24,4,8.176696793366695,0.9904428645363171,0.5959882860653999,130.2591787940024,5.379269833954202,2.2891797131848306,55.549544336112504,997.8699869828811,19.105658049305937,22.55765407229187,evaluator
|
| 31 |
+
highland_slope_capability,0.19999800471187312,0.030228340098058765,0.0199977099302605,24,4,10.516107483090398,1.0329313216405829,0.3655008466601977,242.77327514869162,10.377888979922377,2.362663500472295,55.66734274278879,351.71320493572864,18.902584723177814,26.6042233874943,evaluator
|
| 32 |
+
highland_slope_capability,0.19994192485228526,0.030336490967733706,0.019987001874135026,24,4,0.5349910769796431,1.0780475789850192,0.1908922885738742,5.705487679018481,5.070224403084438,0.46156729304200167,49.36312167198297,349.31667051467883,19.519961249568663,8.350364106724653,evaluator
|
| 33 |
+
highland_slope_capability,0.19856307657550615,0.030336490967733706,0.019997472965599603,24,4,0.5354748516662735,1.049227061847042,0.1908922885738742,5.68996773928722,5.151443096881851,0.46156729304200167,48.911641890313284,345.61134810073025,19.496841424645645,8.345151896998253,evaluator
|
| 34 |
+
highland_slope_capability,0.19999543959069935,0.030340726161044117,0.0199984185258365,24,4,0.533949883608984,0.6237522676033832,0.3211604273875452,10.791734151218982,5.122928281843378,0.4558279557766652,51.75119968606228,642.6096162878026,19.526840332499923,8.85123943377463,evaluator
|
| 35 |
+
highland_slope_capability,0.1999981476353354,0.030540996999403512,0.01999989758981069,24,4,12.89242534048866,1.0373736648140948,0.6041050134894371,102.44540365139258,5.059494632245982,2.245347548902042,55.685478191774,952.4097347741846,18.813652349705723,28.654176830259534,evaluator
|
| 36 |
+
highland_slope_capability,0.1999939206924001,0.030326044348926662,0.01999904997927752,24,4,2.803346545165649,0.6786221979522151,0.5908315689347444,135.36130308306215,7.582774222162994,1.6265654819804531,54.969096937592276,897.1579430910749,19.412611301713174,15.073088708702349,evaluator
|
| 37 |
+
highland_slope_capability,0.1999978400239634,0.038571337760413285,0.01999807835169149,24,4,16.11930059359123,1.0238416435753446,0.3207361417040406,242.77327514869162,6.088445310801983,2.5411140296122454,55.70709218523391,385.61471002122244,18.881151799565583,34.45627651523765,evaluator
|
| 38 |
+
highland_slope_capability,0.19996651173370084,0.03021808125803219,0.019959175892984946,24,4,0.5170130538330862,0.9468944568649806,0.22407975692449164,29.597070904134604,5.038133686892737,0.46051078696912895,51.118516819766725,424.7572410811491,19.512436389260017,8.698438369437387,evaluator
|
| 39 |
+
highland_slope_capability,0.1999869200252456,0.030332454319610888,0.019987001874135026,24,4,0.5349917718898918,1.0572011117715903,0.28752431434374337,311.7655352736915,6.4377447888494395,0.461232268609313,54.534924362605906,454.0034647377526,19.486415813752977,12.242787549857905,evaluator
|
| 40 |
+
highland_slope_capability,0.19997916056074375,0.03003972252071095,0.01999870664668745,24,4,5.767170306923489,0.8562849486849129,0.8078213001684713,138.5839005427178,7.659564102610683,2.7454229866017448,55.45415960633406,1171.9593414606932,19.19080934696055,20.475409312533728,evaluator
|
| 41 |
+
highland_slope_capability,0.1999980045662266,0.03054852252415752,0.019999295036762593,24,4,12.321995380153112,1.0312710673667833,0.6041050134894371,67.6904849735462,5.846595886623601,2.2247493887586836,55.67804865692019,902.818908962967,18.870947038311783,27.52666113628217,evaluator
|
| 42 |
+
highland_slope_capability,0.19998443712294817,0.03007674973345048,0.019995969361615892,24,4,3.5277885049333144,0.6926966270706141,0.5123919074047064,184.37673627662755,9.572160226445103,1.6199958466432522,55.13328882784802,636.00268253602,19.357659790399588,16.462372733033614,evaluator
|
| 43 |
+
highland_slope_capability,0.19998208145704682,0.030046789427068916,0.0199975480624849,24,4,0.5052931892563582,1.0053979007364457,0.19108112434974953,11.586887742807587,5.384414665979895,0.4621760686413018,49.57696317742857,334.23974845829474,19.524039781043754,8.379618419414982,evaluator
|
| 44 |
+
highland_slope_capability,0.19999834354304713,0.03044576666813941,0.019998994512111404,24,4,0.5041814195273064,1.0094228373816636,0.22540900480359904,240.85983073782552,5.036891183253616,0.4486276825492864,54.32523028163832,406.34581219739584,19.51291253023718,11.091449395391257,evaluator
|
| 45 |
+
highland_slope_capability,0.19997916056074375,0.03000742723386108,0.01999870664668745,24,4,5.767170306923489,0.8562849486849129,0.8078213001684713,138.5839005427178,7.659564102610683,4.671706812630349,55.51174178343114,1156.6338936287964,19.14182543021922,21.521701460536985,evaluator
|
| 46 |
+
highland_slope_capability,0.19999905408124183,0.030324789394730385,0.019998898692395094,24,4,6.104304315336263,0.9853129435691355,0.4613875789647047,255.81077317855085,8.655632413641351,2.362663500472295,55.47996471673478,573.0784561511423,19.17827922157098,20.964183704263785,evaluator
|
| 47 |
+
highland_slope_capability,0.1999655383938775,0.030218546909578682,0.0199984185258365,24,4,0.6264642390070607,0.9579454593986867,0.7614646892287258,131.01131049474748,9.841438785384224,0.7594368566183693,54.55834279556259,1019.9112385289977,19.487096072340748,12.315989409784025,evaluator
|
| 48 |
+
highland_slope_capability,0.19998353702611302,0.030308149877947544,0.01999754734592868,24,4,3.524729087508154,0.6879446531470726,0.7815868758436406,128.37097130958628,9.332512492728128,2.0873952633707273,55.177037069107755,1035.0931509967347,19.343336164796618,16.992966458450915,evaluator
|
| 49 |
+
highland_slope_capability,0.19999381837386723,0.0303111307044308,0.019999059183707233,24,4,2.465652916778426,1.0328274643887883,0.7036599749071026,129.75692862237716,7.55605068428146,1.4825867551879464,54.94214987169634,1093.2150622670245,19.4194944544425,14.852627959894514,evaluator
|
| 50 |
+
highland_slope_capability,0.1999980045662266,0.03140911008483446,0.019998575166492994,24,4,12.36039479388276,1.0258612695151046,0.5771143345702514,235.27356285379977,5.887982194369248,2.349280438557093,55.68803001665316,832.3426245504427,18.81044920434116,29.517302749642887,evaluator
|
| 51 |
+
highland_slope_capability,0.1999939206924001,0.030326044348926662,0.01999904997927752,24,4,4.415257370390839,0.6719854017060696,0.5908315689347444,133.66128840663598,7.477177731452813,1.8673372991981627,55.21771177379648,878.4824434094327,19.32996270717057,17.371170923558136,evaluator
|
reports/pareto_fronts/front_highland_slope_capability.metadata.json
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"scenario_name": "highland_slope_capability",
|
| 3 |
+
"backend": "evaluator",
|
| 4 |
+
"dataset_version": "v9",
|
| 5 |
+
"objectives": [
|
| 6 |
+
{
|
| 7 |
+
"target": "range_km",
|
| 8 |
+
"direction": "max"
|
| 9 |
+
},
|
| 10 |
+
{
|
| 11 |
+
"target": "total_mass_kg",
|
| 12 |
+
"direction": "min"
|
| 13 |
+
}
|
| 14 |
+
],
|
| 15 |
+
"constraints": [
|
| 16 |
+
{
|
| 17 |
+
"target": "range_km",
|
| 18 |
+
"sense": "min",
|
| 19 |
+
"value": 0.1
|
| 20 |
+
},
|
| 21 |
+
{
|
| 22 |
+
"target": "slope_capability_deg",
|
| 23 |
+
"sense": "min",
|
| 24 |
+
"value": 15.0
|
| 25 |
+
}
|
| 26 |
+
],
|
| 27 |
+
"traverse_distance_m": 120000.0,
|
| 28 |
+
"population_size": 50,
|
| 29 |
+
"generations": 60,
|
| 30 |
+
"seed": 14,
|
| 31 |
+
"panel_tilt_deg": 10.0,
|
| 32 |
+
"panel_azimuth_deg": 180.0,
|
| 33 |
+
"elapsed_s": 32.234466542024165,
|
| 34 |
+
"pareto_size": 50,
|
| 35 |
+
"front_csv": "reports/pareto_fronts/front_highland_slope_capability.csv"
|
| 36 |
+
}
|
reports/pareto_fronts/front_polar_prospecting.csv
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
scenario_name,wheel_radius_m,wheel_width_m,grouser_height_m,grouser_count,n_wheels,chassis_mass_kg,wheelbase_m,solar_area_m2,battery_capacity_wh,avionics_power_w,peak_wheel_torque_nm,range_km,energy_margin_raw_pct,slope_capability_deg,total_mass_kg,backend_used
|
| 2 |
+
polar_prospecting,0.19919232794559236,0.030016591051623878,0.019990618981594786,24,4,0.5245647253151042,0.5777976990573055,0.1923841802694848,71.66244047597367,5.098419236121115,0.730534243248785,30.0,54.670057128503935,33.01131914634105,12.209435410538148,evaluator
|
| 3 |
+
polar_prospecting,0.19971354003635836,0.030031478124273002,0.019992538887191644,24,4,14.067866682032632,0.6300021197272216,0.9858373638390915,188.23068988993924,37.37118138147521,19.2564842070138,30.0,127.49607323889296,34.75919697608552,46.94560084957061,evaluator
|
| 4 |
+
polar_prospecting,0.19970771015410305,0.03003654393706933,0.019992828042426577,24,4,8.871644856597744,0.6501104625959102,0.1812961201616805,185.82452300778394,11.262816109080399,2.334193635017587,3.749273378613546,0.8211066678378433,34.30083629900911,26.12299903996457,evaluator
|
| 5 |
+
polar_prospecting,0.19969889992196832,0.030585410546369755,0.019990565424487853,24,4,1.467746055786895,0.8698011431246389,0.18008603492148076,181.7623584365482,10.77418747538477,1.348962419524673,26.95211425437558,1.8943793797212167,33.450500163904536,15.465673704878702,evaluator
|
| 6 |
+
polar_prospecting,0.19919169278313936,0.030009602880722554,0.01999051351690844,24,4,1.323644885693695,0.6501104625959102,0.1801079033477888,269.65623896668046,11.082568950877992,2.3153604267328483,8.97773251654592,0.9680434013468255,33.62195786692515,16.769636756688715,evaluator
|
| 7 |
+
polar_prospecting,0.19969828769849654,0.030037092988265048,0.01999262919559681,24,4,14.842213854303811,0.8561204451358481,0.18008072645499432,185.24884923401976,10.323116416374006,18.729078889500872,18.545517257596693,3.258512703749742,34.72790853527953,43.09716929107084,evaluator
|
| 8 |
+
polar_prospecting,0.199216012911625,0.030112790938319266,0.019990720056646854,24,4,10.293030331406156,0.597935097826652,0.18009632045605373,188.90857525241023,10.32306628540407,1.745380210924138,29.381683369553954,3.256477161988716,34.3602313524069,27.704814029526375,evaluator
|
| 9 |
+
polar_prospecting,0.19919231892943542,0.030016591051623878,0.019990618971466582,24,4,0.7242088338470322,0.6359612441577706,0.1923841802694848,70.17452071048244,5.000299837486238,1.1714028302428345,30.0,55.680999663946054,33.09149561498588,12.697535574415827,evaluator
|
| 10 |
+
polar_prospecting,0.1991820931899969,0.030033972455268967,0.01999051351690844,24,4,12.557319998782047,0.663867761633789,0.18062311767339015,90.05615176350128,10.797527512413792,19.09620536992501,10.780298872064122,2.033631423136979,34.66841573436856,39.13785783678,evaluator
|
| 11 |
+
polar_prospecting,0.1998248974535649,0.030023031502208163,0.019990509253292325,24,4,3.5601440102148887,0.6371607077103119,0.18010912330198486,271.17442345946097,10.774315968286377,2.309743585301252,21.79841540839599,1.8624176969569861,33.91062218820201,19.812376805377593,evaluator
|
| 12 |
+
polar_prospecting,0.19923370061287007,0.03057358193216254,0.019990565298911456,24,4,0.7838632539813191,0.6889349058321569,0.18008603492148076,175.1666085842235,10.804775509697677,2.311094294086544,26.00734728238401,1.802470688107471,33.38460250460101,14.981334473374908,evaluator
|
| 13 |
+
polar_prospecting,0.19984311950575043,0.03012545640550927,0.019990735705662836,24,4,0.592158022459309,0.8559118089620925,0.18104986027579853,151.15446825261247,11.089465215561395,2.161871862298133,15.791336692551665,1.2609716679418796,33.338208422684936,14.3638680859347,evaluator
|
| 14 |
+
polar_prospecting,0.19970620375382786,0.030117433703774926,0.019995840331820253,24,4,0.693563850275746,0.6300017310998679,1.0395865293026232,73.52749899971654,37.57761993550009,2.3061321100647265,30.0,142.7507705146114,33.78765157366135,18.42151926266851,evaluator
|
| 15 |
+
polar_prospecting,0.19971342159322497,0.03003141446717941,0.019990818687721637,24,4,14.842213854303811,0.8553155443174655,0.18101550133420746,91.28333066825262,10.832640508375349,3.023967896209503,12.829624156275093,2.0512468281424163,34.56276809163969,33.520538060100954,evaluator
|
| 16 |
+
polar_prospecting,0.19919200218401562,0.030009602880722554,0.01999051351690844,24,4,4.986770174530046,0.6501444516575399,0.18108523775983357,269.65623896668046,11.082568950877992,2.3100360449115844,11.261430600791165,1.2650248519609526,34.04539401554572,21.754846914316456,evaluator
|
| 17 |
+
polar_prospecting,0.1996826595825958,0.030024133112202346,0.019996887081204465,24,4,14.842213854303811,0.6383236758679929,0.18008072645499432,151.82782253558352,10.299709196374497,4.3458006749572204,23.36287591838696,3.3595732573415944,34.598138176949355,34.88654713667193,evaluator
|
| 18 |
+
polar_prospecting,0.199216012911625,0.030112790938319266,0.019990720056646854,24,4,13.018372832705357,0.6492007757015484,0.18108523775983357,188.90857525241023,10.322239522410483,1.9852875870850326,28.596457848710774,3.5745064640916895,34.49765676066792,31.5473545319996,evaluator
|
| 19 |
+
polar_prospecting,0.19970964309217526,0.030041672471052834,0.019990731183361816,24,4,0.5431872460855484,0.6382007508613962,0.19173297576958848,152.4453499246995,5.124626233367495,0.8608044717195027,30.0,53.43064668482829,33.17954961526147,13.227306664991975,evaluator
|
| 20 |
+
polar_prospecting,0.199216012911625,0.03002244094012891,0.019990720056646854,24,4,5.168385777641856,0.6010437728322404,0.18009483915048427,178.49000619271686,10.32306628540407,1.745380210924138,30.0,3.2701551509087876,33.96352914786638,20.606847308695514,evaluator
|
| 21 |
+
polar_prospecting,0.19971342159322497,0.03003141446717941,0.01999262919559681,24,4,14.842213854303811,0.8561204451358481,0.18101550133420746,91.28333066825262,10.33883720152938,18.774779226573507,19.976903230047636,3.5943786056836196,34.71698418603195,42.06045966264026,evaluator
|
| 22 |
+
polar_prospecting,0.19919169278313936,0.03032334080128116,0.019990707366512185,24,4,12.641080917085677,0.6380666739211333,0.1806389722138684,33.110743075016565,10.805423924484087,2.2647429404066326,13.851322599369476,2.047831161986665,34.42150424405044,29.464902600055886,evaluator
|
| 23 |
+
polar_prospecting,0.19919169278313936,0.03032334080128116,0.019992517874387356,24,4,12.641080917085677,0.6380666739211333,0.1806389722138684,33.110743075016565,10.31162061763812,16.359085657751898,21.777895592908685,3.628389048333979,34.6286370999423,37.10317558000583,evaluator
|
| 24 |
+
polar_prospecting,0.19971354003635836,0.030316677784041127,0.019990722837143537,24,4,13.882434463441065,0.6300021197272216,0.9953029992348574,188.59397268192757,11.163937878342878,19.172420848403647,30.0,425.01296648805777,34.741220039228416,44.920255728911776,evaluator
|
| 25 |
+
polar_prospecting,0.199207320872731,0.030009602880722554,0.01999567448098733,24,4,0.9922545958377467,0.6382106524946465,0.1810734315152468,269.9661803758157,11.082568950877992,1.2725604708653215,15.55656604475901,1.2614460626376405,33.51198034388965,15.75810101675818,evaluator
|
| 26 |
+
polar_prospecting,0.19969938045502555,0.030041401601036216,0.019993218817141938,24,4,2.3322065408719563,0.6382007508613962,0.18008975112694933,152.4453499246995,11.056264168750458,4.133988091610977,9.288796294475821,1.0576101305107886,33.733029618267274,17.807391568879922,evaluator
|
| 27 |
+
polar_prospecting,0.19969828769849654,0.030040208928755033,0.019992635705520803,24,4,14.896967110082741,0.6642067351034092,0.18101550133420746,187.84283210132344,10.33309655444955,2.2647395988049093,25.82207237490948,3.5195129270500787,34.58111838740275,34.24329761133178,evaluator
|
| 28 |
+
polar_prospecting,0.19969828769849654,0.030037799998642003,0.01999074257851328,24,4,1.0433078212993552,0.6382007508613962,0.18008072645499432,153.19600555880967,10.81435781743055,4.147223640360962,23.47662526457484,1.781617828652949,33.54915887766028,16.05237595404288,evaluator
|
| 29 |
+
polar_prospecting,0.19970580017381542,0.030021342390539496,0.019990501546816763,24,4,4.300580763559699,0.6382010609369723,0.1818221561818048,193.82500881997433,11.253477694915782,1.1425957344529216,8.120573380536879,1.010136110821265,33.8707513876737,19.344854164646932,evaluator
|
| 30 |
+
polar_prospecting,0.19921056267964507,0.03001664974850319,0.01999051351690844,24,4,0.6832479485833767,0.6466378610037072,0.18130612734587512,78.92393369303892,11.262816109080399,1.9050523067827831,7.057469374422291,0.8356309158425392,33.21875923306655,13.528920123445811,evaluator
|
| 31 |
+
polar_prospecting,0.1997423726295979,0.030342246478011245,0.01999070336654011,24,4,6.7151539237418945,0.6380656151952937,0.18103019426311123,187.77771958611524,11.079291894569536,2.3191458109663836,10.129176609064636,1.2772973847641533,34.13234392262149,23.210270658691815,evaluator
|
| 32 |
+
polar_prospecting,0.19919169550492882,0.03011109689677441,0.01999051932866138,24,4,0.7397820859310764,0.6357297093144441,0.18102005989133635,91.41388194268204,11.082568950877992,2.26466645401143,16.076015171765846,1.287534899234842,33.27121966208532,13.936320175736316,evaluator
|
| 33 |
+
polar_prospecting,0.19969828769849654,0.03003805865823989,0.019990876019607102,24,4,9.997772513467199,0.6382562573361807,0.1810357740408716,149.9290022645877,11.089970197872585,4.984734609564864,7.745589060870799,1.2550419513269724,34.40890292471523,28.67846000686004,evaluator
|
| 34 |
+
polar_prospecting,0.19969828769849654,0.030347255481982666,0.019990735705662836,24,4,6.459805775371338,0.6382295061522196,0.1810357740408716,153.19600555880967,11.071835214684608,4.984734609564864,9.990315959128605,1.309100011294337,34.17330602922869,23.921060216786827,evaluator
|
| 35 |
+
polar_prospecting,0.19969828769849654,0.030037799998642003,0.01999074257851328,24,4,1.0433078212993552,0.6382007508613962,0.18008072645499432,153.19600555880967,10.805510176833634,4.147223640360962,23.96069743167954,1.8084006170285987,33.5490909050214,16.051773960576668,evaluator
|
| 36 |
+
polar_prospecting,0.19918615405504972,0.0303180114820083,0.019990698772475005,24,4,12.522247281187582,0.8355313917708629,0.18061081198933926,90.0551555342406,11.092555044625428,4.523901747273542,5.623519034250771,1.1259121359817825,34.48045538942091,31.197677616876717,evaluator
|
| 37 |
+
polar_prospecting,0.19970771015410305,0.030025478664323025,0.019992828042426577,24,4,8.871644856597744,0.6566882229209831,0.18062227095212882,88.28824432179823,10.784840240168444,2.334193635017587,17.495393152950268,2.0763689780380408,34.24473678525024,24.981363047038222,evaluator
|
| 38 |
+
polar_prospecting,0.1991788491685881,0.0303180114820083,0.01999051351690844,24,4,12.522247281187582,0.8355313917708629,0.18108523775983357,90.05615176350128,11.08830959795782,17.13317369096819,5.788401626072359,1.2909697320170777,34.6453658320887,38.0624283974201,evaluator
|
| 39 |
+
polar_prospecting,0.19919169278313936,0.03032334080128116,0.01999051351690844,24,4,12.641080917085677,0.6382010146305537,0.1806389722138684,91.53038088584388,10.812678060426776,2.2647429404066326,13.74609746785503,1.9920109180790118,34.44494036425441,30.127870717553176,evaluator
|
| 40 |
+
polar_prospecting,0.19920025879584385,0.030021707592354115,0.019993249643178113,24,4,4.963958690681447,0.8671175701590538,0.18173585996256186,90.59992255543604,10.324772606903702,1.4246735109996465,30.0,4.257672997946969,33.84930198362448,19.162995013296914,evaluator
|
| 41 |
+
polar_prospecting,0.19971724913733735,0.030012344873700036,0.019990520100549043,24,4,5.392629099188158,0.6382069263600166,0.18182501744003532,255.53492383987805,11.073059738076124,2.267315612662372,14.62964042805728,1.521462249664447,34.07787281949579,22.130121490094968,evaluator
|
| 42 |
+
polar_prospecting,0.19968816914944046,0.030013949457193278,0.019990618093049712,24,4,12.518413388701799,0.6431167243375115,0.18103893463203158,191.97501577503158,11.108313724751072,19.098021451833734,5.15602251283128,1.1926895837922364,34.69334279274973,40.26700720284735,evaluator
|
| 43 |
+
polar_prospecting,0.1991804527304546,0.03032334080128116,0.01999064234698691,24,4,8.532289880472714,0.6350248940340801,0.1806389722138684,91.53038088584388,10.798895952291817,2.2647429404066326,17.44558260605576,2.0344997648971805,34.2008907227876,24.535599641685433,evaluator
|
| 44 |
+
polar_prospecting,0.19918068199339525,0.030112006932992777,0.019992569131667207,24,4,7.258213434276835,0.6359214096122451,0.18102005989133635,91.53038088584388,11.076411718074075,2.2646950079213135,9.979696859160402,1.3062571351491186,34.109925404538444,22.80753449135939,evaluator
|
| 45 |
+
polar_prospecting,0.19919169278313936,0.030007924104700524,0.01999051351690844,24,4,1.781211073585153,0.6488340882918129,0.18102039842257645,269.1478548579722,11.082198355435638,2.263194985285352,13.82217855365255,1.2464336539181715,33.68281301153504,17.361096981928323,evaluator
|
| 46 |
+
polar_prospecting,0.19983652206335265,0.03009226090981498,0.019990497338213217,24,4,9.295856270217218,0.8732409135576399,0.18014318686335734,188.04436679526464,10.383908728976367,19.731562358024053,20.946847476129147,3.0913682016552033,34.623465202564915,36.13629289865456,evaluator
|
| 47 |
+
polar_prospecting,0.19971354003635836,0.030321622012370587,0.019995742757704105,24,4,13.389854957847453,0.6300021197272216,0.8576032079254274,205.11483895644412,36.98401333960562,19.2564842070138,30.0,99.74147067803864,34.750031056463136,45.77184803961729,evaluator
|
| 48 |
+
polar_prospecting,0.19919194118674907,0.03011262534427953,0.019991244508670545,24,4,13.945246008858074,0.6440080158382542,0.1800898641456323,178.00692364998406,11.08370047934501,2.2676882740527025,4.276994827206549,0.9723116713745091,34.536104025973025,32.88696015872462,evaluator
|
| 49 |
+
polar_prospecting,0.19970446607791795,0.030309854326076822,0.019992828042426577,24,4,8.836572139003389,0.828351853058061,0.18108439103857224,88.28824432179823,11.075622325712475,1.789899491787514,9.446858964729273,1.3312973122931218,34.21761609567688,24.678245283639615,evaluator
|
| 50 |
+
polar_prospecting,0.19969828769849654,0.030040208928755033,0.019992635705520803,24,4,9.426662825400259,0.6642067351034092,0.18101550133420746,187.91901578532259,10.37185588643266,2.2647395988049093,30.0,3.40063739586232,34.33173716873356,26.80280864866055,evaluator
|
| 51 |
+
polar_prospecting,0.1998248974535649,0.030023031502208163,0.019990620313485127,24,4,4.708175253080844,0.6382010609369723,0.17990633382425247,177.56171898074092,10.774315968286377,2.309743585301252,19.594354785924622,1.8376627457466181,33.94987627053331,20.31236211426689,evaluator
|
reports/pareto_fronts/front_polar_prospecting.metadata.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"scenario_name": "polar_prospecting",
|
| 3 |
+
"backend": "evaluator",
|
| 4 |
+
"dataset_version": "v9",
|
| 5 |
+
"objectives": [
|
| 6 |
+
{
|
| 7 |
+
"target": "range_km",
|
| 8 |
+
"direction": "max"
|
| 9 |
+
},
|
| 10 |
+
{
|
| 11 |
+
"target": "total_mass_kg",
|
| 12 |
+
"direction": "min"
|
| 13 |
+
},
|
| 14 |
+
{
|
| 15 |
+
"target": "slope_capability_deg",
|
| 16 |
+
"direction": "max"
|
| 17 |
+
}
|
| 18 |
+
],
|
| 19 |
+
"constraints": [
|
| 20 |
+
{
|
| 21 |
+
"target": "range_km",
|
| 22 |
+
"sense": "min",
|
| 23 |
+
"value": 0.1
|
| 24 |
+
}
|
| 25 |
+
],
|
| 26 |
+
"traverse_distance_m": 30000.0,
|
| 27 |
+
"population_size": 50,
|
| 28 |
+
"generations": 60,
|
| 29 |
+
"seed": 15,
|
| 30 |
+
"panel_tilt_deg": 80.0,
|
| 31 |
+
"panel_azimuth_deg": 0.0,
|
| 32 |
+
"elapsed_s": 41.1206740840571,
|
| 33 |
+
"pareto_size": 50,
|
| 34 |
+
"front_csv": "reports/pareto_fronts/front_polar_prospecting.csv"
|
| 35 |
+
}
|
reports/pareto_fronts/manifest.json
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[
|
| 2 |
+
{
|
| 3 |
+
"scenario_name": "crater_rim_survey",
|
| 4 |
+
"backend": "evaluator",
|
| 5 |
+
"dataset_version": "v9",
|
| 6 |
+
"objectives": [
|
| 7 |
+
{
|
| 8 |
+
"target": "range_km",
|
| 9 |
+
"direction": "max"
|
| 10 |
+
},
|
| 11 |
+
{
|
| 12 |
+
"target": "total_mass_kg",
|
| 13 |
+
"direction": "min"
|
| 14 |
+
},
|
| 15 |
+
{
|
| 16 |
+
"target": "slope_capability_deg",
|
| 17 |
+
"direction": "max"
|
| 18 |
+
}
|
| 19 |
+
],
|
| 20 |
+
"constraints": [
|
| 21 |
+
{
|
| 22 |
+
"target": "range_km",
|
| 23 |
+
"sense": "min",
|
| 24 |
+
"value": 0.1
|
| 25 |
+
}
|
| 26 |
+
],
|
| 27 |
+
"traverse_distance_m": 25000.0,
|
| 28 |
+
"population_size": 50,
|
| 29 |
+
"generations": 60,
|
| 30 |
+
"seed": 12,
|
| 31 |
+
"panel_tilt_deg": 0.0,
|
| 32 |
+
"panel_azimuth_deg": 180.0,
|
| 33 |
+
"elapsed_s": 32.589310958981514,
|
| 34 |
+
"pareto_size": 50,
|
| 35 |
+
"front_csv": "reports/pareto_fronts/front_crater_rim_survey.csv"
|
| 36 |
+
},
|
| 37 |
+
{
|
| 38 |
+
"scenario_name": "equatorial_mare_traverse",
|
| 39 |
+
"backend": "evaluator",
|
| 40 |
+
"dataset_version": "v9",
|
| 41 |
+
"objectives": [
|
| 42 |
+
{
|
| 43 |
+
"target": "range_km",
|
| 44 |
+
"direction": "max"
|
| 45 |
+
},
|
| 46 |
+
{
|
| 47 |
+
"target": "total_mass_kg",
|
| 48 |
+
"direction": "min"
|
| 49 |
+
},
|
| 50 |
+
{
|
| 51 |
+
"target": "slope_capability_deg",
|
| 52 |
+
"direction": "max"
|
| 53 |
+
}
|
| 54 |
+
],
|
| 55 |
+
"constraints": [
|
| 56 |
+
{
|
| 57 |
+
"target": "range_km",
|
| 58 |
+
"sense": "min",
|
| 59 |
+
"value": 0.1
|
| 60 |
+
}
|
| 61 |
+
],
|
| 62 |
+
"traverse_distance_m": 80000.0,
|
| 63 |
+
"population_size": 50,
|
| 64 |
+
"generations": 60,
|
| 65 |
+
"seed": 13,
|
| 66 |
+
"panel_tilt_deg": 20.2,
|
| 67 |
+
"panel_azimuth_deg": 180.0,
|
| 68 |
+
"elapsed_s": 36.020679499953985,
|
| 69 |
+
"pareto_size": 50,
|
| 70 |
+
"front_csv": "reports/pareto_fronts/front_equatorial_mare_traverse.csv"
|
| 71 |
+
},
|
| 72 |
+
{
|
| 73 |
+
"scenario_name": "highland_slope_capability",
|
| 74 |
+
"backend": "evaluator",
|
| 75 |
+
"dataset_version": "v9",
|
| 76 |
+
"objectives": [
|
| 77 |
+
{
|
| 78 |
+
"target": "range_km",
|
| 79 |
+
"direction": "max"
|
| 80 |
+
},
|
| 81 |
+
{
|
| 82 |
+
"target": "total_mass_kg",
|
| 83 |
+
"direction": "min"
|
| 84 |
+
}
|
| 85 |
+
],
|
| 86 |
+
"constraints": [
|
| 87 |
+
{
|
| 88 |
+
"target": "range_km",
|
| 89 |
+
"sense": "min",
|
| 90 |
+
"value": 0.1
|
| 91 |
+
},
|
| 92 |
+
{
|
| 93 |
+
"target": "slope_capability_deg",
|
| 94 |
+
"sense": "min",
|
| 95 |
+
"value": 15.0
|
| 96 |
+
}
|
| 97 |
+
],
|
| 98 |
+
"traverse_distance_m": 120000.0,
|
| 99 |
+
"population_size": 50,
|
| 100 |
+
"generations": 60,
|
| 101 |
+
"seed": 14,
|
| 102 |
+
"panel_tilt_deg": 10.0,
|
| 103 |
+
"panel_azimuth_deg": 180.0,
|
| 104 |
+
"elapsed_s": 32.234466542024165,
|
| 105 |
+
"pareto_size": 50,
|
| 106 |
+
"front_csv": "reports/pareto_fronts/front_highland_slope_capability.csv"
|
| 107 |
+
},
|
| 108 |
+
{
|
| 109 |
+
"scenario_name": "polar_prospecting",
|
| 110 |
+
"backend": "evaluator",
|
| 111 |
+
"dataset_version": "v9",
|
| 112 |
+
"objectives": [
|
| 113 |
+
{
|
| 114 |
+
"target": "range_km",
|
| 115 |
+
"direction": "max"
|
| 116 |
+
},
|
| 117 |
+
{
|
| 118 |
+
"target": "total_mass_kg",
|
| 119 |
+
"direction": "min"
|
| 120 |
+
},
|
| 121 |
+
{
|
| 122 |
+
"target": "slope_capability_deg",
|
| 123 |
+
"direction": "max"
|
| 124 |
+
}
|
| 125 |
+
],
|
| 126 |
+
"constraints": [
|
| 127 |
+
{
|
| 128 |
+
"target": "range_km",
|
| 129 |
+
"sense": "min",
|
| 130 |
+
"value": 0.1
|
| 131 |
+
}
|
| 132 |
+
],
|
| 133 |
+
"traverse_distance_m": 30000.0,
|
| 134 |
+
"population_size": 50,
|
| 135 |
+
"generations": 60,
|
| 136 |
+
"seed": 15,
|
| 137 |
+
"panel_tilt_deg": 80.0,
|
| 138 |
+
"panel_azimuth_deg": 0.0,
|
| 139 |
+
"elapsed_s": 41.1206740840571,
|
| 140 |
+
"pareto_size": 50,
|
| 141 |
+
"front_csv": "reports/pareto_fronts/front_polar_prospecting.csv"
|
| 142 |
+
}
|
| 143 |
+
]
|
roverdevkit/__init__.py
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""RoverDevKit — ML-accelerated co-design of lunar micro-rover mobility and power.
|
| 2 |
+
|
| 3 |
+
Top-level package. Most public API lives in submodules:
|
| 4 |
+
|
| 5 |
+
- :mod:`roverdevkit.schema` — shared dataclasses for design vectors, scenarios,
|
| 6 |
+
and mission metrics.
|
| 7 |
+
- :mod:`roverdevkit.terramechanics` — Bekker-Wong analytical terramechanics.
|
| 8 |
+
- :mod:`roverdevkit.power` — solar, battery, and thermal survival sub-models.
|
| 9 |
+
- :mod:`roverdevkit.mass` — parametric mass-estimating relationships.
|
| 10 |
+
- :mod:`roverdevkit.mission` — top-level mission evaluator, scenarios,
|
| 11 |
+
time-stepped traverse simulator.
|
| 12 |
+
- :mod:`roverdevkit.surrogate` — training, models, feature engineering, UQ.
|
| 13 |
+
- :mod:`roverdevkit.tradespace` — sweeps, NSGA-II optimization, SHAP rules.
|
| 14 |
+
- :mod:`roverdevkit.validation` — rover rediscovery, experimental comparison,
|
| 15 |
+
error budget.
|
| 16 |
+
"""
|
| 17 |
+
|
| 18 |
+
from __future__ import annotations
|
| 19 |
+
|
| 20 |
+
from roverdevkit.architecture import MobilityArchitecture
|
| 21 |
+
|
| 22 |
+
__version__ = "0.1.0"
|
| 23 |
+
|
| 24 |
+
__all__ = ["MobilityArchitecture", "__version__"]
|
roverdevkit/architecture.py
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Mobility-architecture proxy for obstacle negotiation and suspension mass.
|
| 2 |
+
|
| 3 |
+
This is an *architecture-level* model, not a kinematic rocker-bogie simulation.
|
| 4 |
+
``mobility_architecture`` selects between a four-wheel rigid/skid-steer proxy
|
| 5 |
+
and a six-wheel rocker-bogie proxy. Obstacle capability scales with wheel
|
| 6 |
+
radius through literature-motivated step-height factors; rocker-bogie carries
|
| 7 |
+
an explicit suspension mass penalty in the bottom-up mass model.
|
| 8 |
+
"""
|
| 9 |
+
|
| 10 |
+
from __future__ import annotations
|
| 11 |
+
|
| 12 |
+
from dataclasses import dataclass
|
| 13 |
+
from typing import Literal
|
| 14 |
+
|
| 15 |
+
MobilityArchitecture = Literal["rigid_4wheel", "rocker_bogie_6wheel"]
|
| 16 |
+
|
| 17 |
+
OBSTACLE_CAPABILITY_FACTOR: dict[MobilityArchitecture, float] = {
|
| 18 |
+
"rigid_4wheel": 0.5,
|
| 19 |
+
"rocker_bogie_6wheel": 1.25,
|
| 20 |
+
}
|
| 21 |
+
"""Max traversable obstacle height as a fraction of wheel radius R.
|
| 22 |
+
|
| 23 |
+
Conservative proxies for conceptual design: rigid four-wheel layouts are
|
| 24 |
+
limited to roughly half a wheel diameter; passive rocker-bogie suspension
|
| 25 |
+
can negotiate obstacles on the order of one wheel radius (MER/MSL class).
|
| 26 |
+
"""
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
def wheel_count_for_architecture(architecture: MobilityArchitecture) -> int:
|
| 30 |
+
"""Return the drive-wheel count implied by ``architecture``."""
|
| 31 |
+
return 6 if architecture == "rocker_bogie_6wheel" else 4
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
def architecture_for_wheel_count(n_wheels: int) -> MobilityArchitecture:
|
| 35 |
+
"""Map legacy ``n_wheels`` values to the closest architecture label."""
|
| 36 |
+
if n_wheels == 6:
|
| 37 |
+
return "rocker_bogie_6wheel"
|
| 38 |
+
if n_wheels == 4:
|
| 39 |
+
return "rigid_4wheel"
|
| 40 |
+
raise ValueError(f"n_wheels must be 4 or 6 (got {n_wheels}).")
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
def obstacle_capability_m(
|
| 44 |
+
architecture: MobilityArchitecture,
|
| 45 |
+
wheel_radius_m: float,
|
| 46 |
+
) -> float:
|
| 47 |
+
"""Estimated max traversable obstacle height, m."""
|
| 48 |
+
if wheel_radius_m <= 0.0:
|
| 49 |
+
raise ValueError("wheel_radius_m must be positive.")
|
| 50 |
+
return OBSTACLE_CAPABILITY_FACTOR[architecture] * wheel_radius_m
|
| 51 |
+
|
| 52 |
+
|
| 53 |
+
def obstacle_margin_m(
|
| 54 |
+
capability_m: float,
|
| 55 |
+
required_obstacle_height_m: float,
|
| 56 |
+
) -> float:
|
| 57 |
+
"""Capability minus the scenario requirement (m)."""
|
| 58 |
+
return capability_m - required_obstacle_height_m
|
| 59 |
+
|
| 60 |
+
|
| 61 |
+
def obstacle_requirement_met(
|
| 62 |
+
capability_m: float,
|
| 63 |
+
required_obstacle_height_m: float,
|
| 64 |
+
) -> bool:
|
| 65 |
+
return capability_m + 1e-12 >= required_obstacle_height_m
|
| 66 |
+
|
| 67 |
+
|
| 68 |
+
@dataclass(frozen=True)
|
| 69 |
+
class ArchitectureParams:
|
| 70 |
+
"""Mass penalty coefficients for the rocker-bogie proxy."""
|
| 71 |
+
|
| 72 |
+
rocker_bogie_fixed_mass_kg: float = 0.5
|
| 73 |
+
rocker_bogie_chassis_fraction: float = 0.08
|
| 74 |
+
|
| 75 |
+
|
| 76 |
+
def architecture_suspension_mass_kg(
|
| 77 |
+
architecture: MobilityArchitecture,
|
| 78 |
+
chassis_mass_kg: float,
|
| 79 |
+
*,
|
| 80 |
+
params: ArchitectureParams | None = None,
|
| 81 |
+
) -> float:
|
| 82 |
+
"""Suspension / linkage mass charged to rocker-bogie architectures only."""
|
| 83 |
+
if architecture == "rigid_4wheel":
|
| 84 |
+
return 0.0
|
| 85 |
+
p = params or ArchitectureParams()
|
| 86 |
+
if chassis_mass_kg <= 0.0:
|
| 87 |
+
raise ValueError("chassis_mass_kg must be positive.")
|
| 88 |
+
return p.rocker_bogie_fixed_mass_kg + p.rocker_bogie_chassis_fraction * chassis_mass_kg
|
roverdevkit/drivetrain/__init__.py
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Drivetrain modelling.
|
| 2 |
+
|
| 3 |
+
This package owns the motor + gearbox torque-speed envelope and the
|
| 4 |
+
helpers that derive cruise speed inside the mission evaluator. See
|
| 5 |
+
:mod:`roverdevkit.drivetrain.motor` for the public API.
|
| 6 |
+
"""
|
| 7 |
+
|
| 8 |
+
from roverdevkit.drivetrain.motor import (
|
| 9 |
+
DEFAULT_DRIVETRAIN_EFFICIENCY,
|
| 10 |
+
OMEGA_NO_LOAD_HUB_RAD_S,
|
| 11 |
+
CruiseResult,
|
| 12 |
+
cruise_speed,
|
| 13 |
+
effective_duty_cycle,
|
| 14 |
+
energy_balance_v_cruise,
|
| 15 |
+
kinematic_envelope_v_max,
|
| 16 |
+
sizing_peak_torque_anchor_nm,
|
| 17 |
+
)
|
| 18 |
+
|
| 19 |
+
__all__ = [
|
| 20 |
+
"DEFAULT_DRIVETRAIN_EFFICIENCY",
|
| 21 |
+
"OMEGA_NO_LOAD_HUB_RAD_S",
|
| 22 |
+
"CruiseResult",
|
| 23 |
+
"cruise_speed",
|
| 24 |
+
"effective_duty_cycle",
|
| 25 |
+
"energy_balance_v_cruise",
|
| 26 |
+
"kinematic_envelope_v_max",
|
| 27 |
+
"sizing_peak_torque_anchor_nm",
|
| 28 |
+
]
|
roverdevkit/drivetrain/motor.py
ADDED
|
@@ -0,0 +1,325 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Drivetrain torque-speed envelope and cruise-speed derivation.
|
| 2 |
+
|
| 3 |
+
Cruise speed is derived inside the evaluator rather than supplied as a
|
| 4 |
+
design input; see ``data/analytical/SCHEMA.md`` for the current schema.
|
| 5 |
+
Drive duty cycle is a single per-scenario ``operational_duty_cycle``:
|
| 6 |
+
the only role of a separate designed duty cycle was to upper-bound
|
| 7 |
+
``δ_eff``, which a user can equivalently express by lowering
|
| 8 |
+
``operational_duty_cycle``.
|
| 9 |
+
|
| 10 |
+
The pre-schema-v7 design vector exposed ``nominal_speed_mps`` and
|
| 11 |
+
``drive_duty_cycle`` as free design *inputs* and gated mobility on an
|
| 12 |
+
implicit, mass-derived torque ceiling inside the mass model. That made
|
| 13 |
+
``range_km`` close to a tautology of two design knobs and let the
|
| 14 |
+
optimiser pick rover speeds the drivetrain could not actually sustain
|
| 15 |
+
on the scenario soil and slope. This module implements the v6 fix:
|
| 16 |
+
|
| 17 |
+
1. **Slip-balance** is solved once by the traverse simulator (already
|
| 18 |
+
loop-invariant under the current scenario schema). It returns the
|
| 19 |
+
per-wheel hub torque demand ``T_req`` and equilibrium slip ``s_eq``
|
| 20 |
+
needed to develop the drawbar pull required to climb the scenario's
|
| 21 |
+
worst-case slope plus rolling resistance.
|
| 22 |
+
2. **Stall gate** (binary): ``stalled = T_req > peak_wheel_torque_nm``
|
| 23 |
+
or the slip solver failed (no slip in the bracket achieves the
|
| 24 |
+
required DP). Replaces the old implicit "even at slip 0.95 we can't
|
| 25 |
+
develop DP" gate with an explicit, design-controlled torque ceiling.
|
| 26 |
+
3. **Energy-balance steady-state cruise speed** ``v_eb``: the speed at
|
| 27 |
+
which avionics + ``δ_eff × P_mobility(v) ≤ P_solar_avg``. Solving
|
| 28 |
+
the equality gives a closed-form ``v_eb`` (no iteration). The
|
| 29 |
+
``δ_eff`` cancels in the achievable-range product
|
| 30 |
+
``v_eb × δ_eff × time``, so range in the energy-binding regime is
|
| 31 |
+
independent of duty cycle (only kinematic-bound regimes feel it).
|
| 32 |
+
4. **Kinematic envelope** ``v_kin = ω_no_load × R × (1 - s_eq)``. A
|
| 33 |
+
hygiene cap reflecting the conservative drivetrain archetype
|
| 34 |
+
assumption: motor + gearbox combined deliver ``peak_wheel_torque_nm``
|
| 35 |
+
at any hub speed up to ``ω_no_load_hub`` (5 rad/s ≈ 48 rpm). For
|
| 36 |
+
``R ∈ [0.05, 0.20] m`` and ``s ∈ [0, 0.3]`` this gate is an upper
|
| 37 |
+
bound rarely binding in the energy-binding regime where lunar
|
| 38 |
+
micro-rovers live; we expect to see it fire on < 1 % of LHS samples.
|
| 39 |
+
5. **Cruise speed** ``v_cruise = 0`` if stalled, else ``min(v_eb, v_kin)``.
|
| 40 |
+
|
| 41 |
+
API surface (importable from :mod:`roverdevkit.drivetrain`):
|
| 42 |
+
|
| 43 |
+
- :data:`OMEGA_NO_LOAD_HUB_RAD_S` — module-level constant for (4).
|
| 44 |
+
- :data:`DEFAULT_DRIVETRAIN_EFFICIENCY` — combined motor + gearbox
|
| 45 |
+
efficiency. Mirrors :data:`roverdevkit.mission.traverse_sim.DEFAULT_MOTOR_EFFICIENCY`.
|
| 46 |
+
- :func:`effective_duty_cycle` — clamp ``δ_ops`` into ``[0, 1]``
|
| 47 |
+
(kept as a thin helper for symmetry with the v6 API; the previous
|
| 48 |
+
``min(δ_des, δ_ops)`` semantics collapsed in v7 when
|
| 49 |
+
``designed_duty_cycle`` was removed from the design vector).
|
| 50 |
+
- :func:`kinematic_envelope_v_max` — step (4) closed form.
|
| 51 |
+
- :func:`energy_balance_v_cruise` — step (3) closed form.
|
| 52 |
+
- :func:`cruise_speed` — composes (2)–(5) into a :class:`CruiseResult`.
|
| 53 |
+
- :func:`sizing_peak_torque_anchor_nm` — pre-v6 implicit torque ceiling,
|
| 54 |
+
retained as the LHS prior anchor for the v6 dataset rebuild (so
|
| 55 |
+
``peak_wheel_torque_nm`` samples cluster around physically plausible
|
| 56 |
+
values for the rest of the design vector).
|
| 57 |
+
"""
|
| 58 |
+
|
| 59 |
+
from __future__ import annotations
|
| 60 |
+
|
| 61 |
+
from dataclasses import dataclass
|
| 62 |
+
|
| 63 |
+
OMEGA_NO_LOAD_HUB_RAD_S: float = 5.0
|
| 64 |
+
"""No-load hub angular speed of the constant-peak-torque drivetrain
|
| 65 |
+
archetype, in rad/s. Approximately 48 rpm at the wheel hub.
|
| 66 |
+
|
| 67 |
+
The micro-rover regime sits well inside this envelope: at ``R = 0.10 m``
|
| 68 |
+
and ``s = 0.10`` the kinematic cap is ``5 × 0.10 × 0.90 = 0.45 m/s``,
|
| 69 |
+
roughly an order of magnitude above any lunar-day average cruise. The
|
| 70 |
+
constant is exposed at module level (not promoted to a design variable)
|
| 71 |
+
because doing so would force the user to think in motor-internal terms
|
| 72 |
+
the rest of the design vector deliberately abstracts away. Revisit if
|
| 73 |
+
LHS sampling shows > 1 % of cells clamping at ``v_kin_max``."""
|
| 74 |
+
|
| 75 |
+
DEFAULT_DRIVETRAIN_EFFICIENCY: float = 0.8
|
| 76 |
+
"""Combined motor + gearbox efficiency, dimensionless. Mirrors
|
| 77 |
+
:data:`roverdevkit.mission.traverse_sim.DEFAULT_MOTOR_EFFICIENCY` so
|
| 78 |
+
that the cruise-speed solve and the per-step mobility power use the
|
| 79 |
+
same number; keeping a separate copy here would risk silent drift.
|
| 80 |
+
Value calibrated against Maxon EC-i + GP series datasheets (BLDC +
|
| 81 |
+
planetary gearbox at nominal load)."""
|
| 82 |
+
|
| 83 |
+
|
| 84 |
+
def effective_duty_cycle(operational_duty_cycle: float) -> float:
|
| 85 |
+
"""Return ``δ_eff = clamp(δ_ops, [0, 1])``.
|
| 86 |
+
|
| 87 |
+
Schema v7 collapsed the v6 ``min(δ_des, δ_ops)`` semantics into a
|
| 88 |
+
single per-scenario duty cycle: the design-side ``designed_duty_cycle``
|
| 89 |
+
field was removed from :class:`~roverdevkit.schema.DesignVector`
|
| 90 |
+
after it turned out to do no engineering work in the mass model.
|
| 91 |
+
This helper is retained as a thin wrapper so callers stay readable
|
| 92 |
+
and so the [0, 1] clamp lives in exactly one place.
|
| 93 |
+
"""
|
| 94 |
+
if operational_duty_cycle < 0.0:
|
| 95 |
+
raise ValueError(
|
| 96 |
+
"operational_duty_cycle must be non-negative "
|
| 97 |
+
f"(got {operational_duty_cycle})."
|
| 98 |
+
)
|
| 99 |
+
return min(1.0, operational_duty_cycle)
|
| 100 |
+
|
| 101 |
+
|
| 102 |
+
def kinematic_envelope_v_max(
|
| 103 |
+
omega_no_load_hub_rad_s: float,
|
| 104 |
+
wheel_radius_m: float,
|
| 105 |
+
slip_eq: float,
|
| 106 |
+
) -> float:
|
| 107 |
+
"""Kinematic cruise-speed cap from the constant-peak-torque envelope.
|
| 108 |
+
|
| 109 |
+
``v_kin = ω_no_load × R × (1 - s_eq)``. The slip term reduces the
|
| 110 |
+
forward speed for a given hub speed: a wheel spinning at ω with
|
| 111 |
+
equilibrium slip ``s`` advances at ``ω × R × (1 - s)``.
|
| 112 |
+
"""
|
| 113 |
+
if wheel_radius_m <= 0.0:
|
| 114 |
+
raise ValueError(f"wheel_radius_m must be positive (got {wheel_radius_m}).")
|
| 115 |
+
if omega_no_load_hub_rad_s <= 0.0:
|
| 116 |
+
raise ValueError(
|
| 117 |
+
f"omega_no_load_hub_rad_s must be positive (got {omega_no_load_hub_rad_s})."
|
| 118 |
+
)
|
| 119 |
+
return omega_no_load_hub_rad_s * wheel_radius_m * max(0.0, 1.0 - slip_eq)
|
| 120 |
+
|
| 121 |
+
|
| 122 |
+
def energy_balance_v_cruise(
|
| 123 |
+
*,
|
| 124 |
+
p_solar_avg_w: float,
|
| 125 |
+
p_avionics_w: float,
|
| 126 |
+
wheel_radius_m: float,
|
| 127 |
+
slip_eq: float,
|
| 128 |
+
motor_efficiency: float,
|
| 129 |
+
delta_eff: float,
|
| 130 |
+
n_wheels: int,
|
| 131 |
+
t_req_per_wheel_nm: float,
|
| 132 |
+
) -> float:
|
| 133 |
+
"""Closed-form energy-balance cruise speed.
|
| 134 |
+
|
| 135 |
+
Solves ``δ_eff × P_mobility(v) + P_avionics = P_solar_avg`` for ``v``.
|
| 136 |
+
With ``ω = v / (R × (1 - s_eq))`` and per-wheel mechanical power
|
| 137 |
+
``T_req × ω``, the per-wheel electrical draw at efficiency η is
|
| 138 |
+
``T_req × ω / η``; total mobility power is ``n_wheels`` times that.
|
| 139 |
+
Algebraic solve:
|
| 140 |
+
|
| 141 |
+
v_eb = (P_solar_avg - P_avionics) × R × (1 - s_eq) × η_motor
|
| 142 |
+
/ (δ_eff × n_wheels × T_req)
|
| 143 |
+
|
| 144 |
+
Returns 0.0 when net solar headroom is non-positive (rover cannot
|
| 145 |
+
even sustain avionics let alone mobility). Returns ``inf`` when
|
| 146 |
+
``T_req`` is effectively zero (flat ground, smooth wheels) so that
|
| 147 |
+
callers compose with ``min(v_eb, v_kin_max)`` cleanly.
|
| 148 |
+
"""
|
| 149 |
+
if delta_eff < 0.0 or n_wheels <= 0 or wheel_radius_m <= 0.0:
|
| 150 |
+
raise ValueError(
|
| 151 |
+
"delta_eff must be >= 0, n_wheels and wheel_radius_m must be "
|
| 152 |
+
f"positive (got delta_eff={delta_eff}, n_wheels={n_wheels}, "
|
| 153 |
+
f"wheel_radius_m={wheel_radius_m})."
|
| 154 |
+
)
|
| 155 |
+
if motor_efficiency <= 0.0:
|
| 156 |
+
raise ValueError(f"motor_efficiency must be positive (got {motor_efficiency}).")
|
| 157 |
+
|
| 158 |
+
p_net_avail = p_solar_avg_w - p_avionics_w
|
| 159 |
+
if p_net_avail <= 0.0:
|
| 160 |
+
return 0.0
|
| 161 |
+
|
| 162 |
+
# Effectively-zero torque demand: any speed is energy-feasible, so
|
| 163 |
+
# delegate the binding constraint to the kinematic cap.
|
| 164 |
+
if t_req_per_wheel_nm <= 1e-9:
|
| 165 |
+
return float("inf")
|
| 166 |
+
|
| 167 |
+
# delta_eff = 0 means the rover doesn't drive at all; the loop-side
|
| 168 |
+
# multiplier (dx_per_step ∝ δ_eff) zeroes out forward progress
|
| 169 |
+
# regardless of v_eb, but we'd divide by zero here. Return inf so
|
| 170 |
+
# the kinematic cap dominates and the caller gets a finite v_cruise.
|
| 171 |
+
if delta_eff <= 1e-12:
|
| 172 |
+
return float("inf")
|
| 173 |
+
|
| 174 |
+
factor = wheel_radius_m * max(1e-6, 1.0 - slip_eq) * motor_efficiency
|
| 175 |
+
return p_net_avail * factor / (delta_eff * n_wheels * t_req_per_wheel_nm)
|
| 176 |
+
|
| 177 |
+
|
| 178 |
+
@dataclass(frozen=True)
|
| 179 |
+
class CruiseResult:
|
| 180 |
+
"""Output of :func:`cruise_speed`.
|
| 181 |
+
|
| 182 |
+
Attributes
|
| 183 |
+
----------
|
| 184 |
+
stalled
|
| 185 |
+
``True`` iff the slip solver could not develop the required
|
| 186 |
+
drawbar pull, or the per-wheel torque demand exceeds the
|
| 187 |
+
design's ``peak_wheel_torque_nm``. When ``True``, ``v_cruise_mps``
|
| 188 |
+
is forced to 0.
|
| 189 |
+
v_cruise_mps
|
| 190 |
+
Final cruise speed used by the time loop, m/s.
|
| 191 |
+
v_eb_mps
|
| 192 |
+
Energy-balance solve output, m/s. Stored for diagnostics; can
|
| 193 |
+
be larger than ``v_cruise_mps`` when the kinematic cap binds.
|
| 194 |
+
``inf`` is possible when ``T_req`` is effectively zero (flat
|
| 195 |
+
ground, smooth wheels).
|
| 196 |
+
v_kin_max_mps
|
| 197 |
+
Kinematic envelope cap, m/s.
|
| 198 |
+
kinematic_clamped
|
| 199 |
+
``True`` iff ``v_eb`` exceeded ``v_kin_max`` and the cap bound.
|
| 200 |
+
Tracking this lets the LHS dataset builder verify the design
|
| 201 |
+
doc's "< 1 % of cells clamp" assumption.
|
| 202 |
+
delta_eff
|
| 203 |
+
Effective duty cycle the time loop should use.
|
| 204 |
+
"""
|
| 205 |
+
|
| 206 |
+
stalled: bool
|
| 207 |
+
v_cruise_mps: float
|
| 208 |
+
v_eb_mps: float
|
| 209 |
+
v_kin_max_mps: float
|
| 210 |
+
kinematic_clamped: bool
|
| 211 |
+
delta_eff: float
|
| 212 |
+
|
| 213 |
+
|
| 214 |
+
def cruise_speed(
|
| 215 |
+
*,
|
| 216 |
+
peak_wheel_torque_nm: float,
|
| 217 |
+
t_req_per_wheel_nm: float,
|
| 218 |
+
slip_eq: float,
|
| 219 |
+
slip_solver_failed: bool,
|
| 220 |
+
p_solar_avg_w: float,
|
| 221 |
+
p_avionics_w: float,
|
| 222 |
+
wheel_radius_m: float,
|
| 223 |
+
motor_efficiency: float,
|
| 224 |
+
delta_eff: float,
|
| 225 |
+
n_wheels: int,
|
| 226 |
+
omega_no_load_hub_rad_s: float = OMEGA_NO_LOAD_HUB_RAD_S,
|
| 227 |
+
) -> CruiseResult:
|
| 228 |
+
"""Compose the stall gate, energy-balance solve, and kinematic cap.
|
| 229 |
+
|
| 230 |
+
See module docstring for the physics. This is the canonical entry
|
| 231 |
+
point that :mod:`roverdevkit.mission.traverse_sim` calls on the
|
| 232 |
+
pre-loop wheel-force solve; tests hit it directly.
|
| 233 |
+
"""
|
| 234 |
+
if peak_wheel_torque_nm <= 0.0:
|
| 235 |
+
raise ValueError(f"peak_wheel_torque_nm must be positive (got {peak_wheel_torque_nm}).")
|
| 236 |
+
|
| 237 |
+
v_kin_max = kinematic_envelope_v_max(
|
| 238 |
+
omega_no_load_hub_rad_s, wheel_radius_m, slip_eq
|
| 239 |
+
)
|
| 240 |
+
|
| 241 |
+
stalled = bool(
|
| 242 |
+
slip_solver_failed
|
| 243 |
+
or t_req_per_wheel_nm > peak_wheel_torque_nm + 1e-9
|
| 244 |
+
)
|
| 245 |
+
if stalled:
|
| 246 |
+
return CruiseResult(
|
| 247 |
+
stalled=True,
|
| 248 |
+
v_cruise_mps=0.0,
|
| 249 |
+
v_eb_mps=0.0,
|
| 250 |
+
v_kin_max_mps=v_kin_max,
|
| 251 |
+
kinematic_clamped=False,
|
| 252 |
+
delta_eff=delta_eff,
|
| 253 |
+
)
|
| 254 |
+
|
| 255 |
+
v_eb = energy_balance_v_cruise(
|
| 256 |
+
p_solar_avg_w=p_solar_avg_w,
|
| 257 |
+
p_avionics_w=p_avionics_w,
|
| 258 |
+
wheel_radius_m=wheel_radius_m,
|
| 259 |
+
slip_eq=slip_eq,
|
| 260 |
+
motor_efficiency=motor_efficiency,
|
| 261 |
+
delta_eff=delta_eff,
|
| 262 |
+
n_wheels=n_wheels,
|
| 263 |
+
t_req_per_wheel_nm=t_req_per_wheel_nm,
|
| 264 |
+
)
|
| 265 |
+
|
| 266 |
+
if v_eb >= v_kin_max:
|
| 267 |
+
return CruiseResult(
|
| 268 |
+
stalled=False,
|
| 269 |
+
v_cruise_mps=v_kin_max,
|
| 270 |
+
v_eb_mps=v_eb,
|
| 271 |
+
v_kin_max_mps=v_kin_max,
|
| 272 |
+
kinematic_clamped=True,
|
| 273 |
+
delta_eff=delta_eff,
|
| 274 |
+
)
|
| 275 |
+
return CruiseResult(
|
| 276 |
+
stalled=False,
|
| 277 |
+
v_cruise_mps=max(0.0, v_eb),
|
| 278 |
+
v_eb_mps=v_eb,
|
| 279 |
+
v_kin_max_mps=v_kin_max,
|
| 280 |
+
kinematic_clamped=False,
|
| 281 |
+
delta_eff=delta_eff,
|
| 282 |
+
)
|
| 283 |
+
|
| 284 |
+
|
| 285 |
+
# ---------------------------------------------------------------------------
|
| 286 |
+
# Pre-v6 implicit torque ceiling (LHS prior anchor only)
|
| 287 |
+
# ---------------------------------------------------------------------------
|
| 288 |
+
|
| 289 |
+
|
| 290 |
+
def sizing_peak_torque_anchor_nm(
|
| 291 |
+
*,
|
| 292 |
+
total_mass_kg: float,
|
| 293 |
+
wheel_radius_m: float,
|
| 294 |
+
n_wheels: int,
|
| 295 |
+
motor_sizing_safety_factor: float = 2.0,
|
| 296 |
+
motor_peak_friction_coef: float = 0.7,
|
| 297 |
+
gravity_m_per_s2: float = 1.625,
|
| 298 |
+
) -> float:
|
| 299 |
+
"""Pre-v6 implicit per-wheel torque ceiling, retained as an LHS anchor.
|
| 300 |
+
|
| 301 |
+
``T_anchor = sf × μ × (m × g / N) × R``. In v5 the mass model
|
| 302 |
+
sized motor mass against this ceiling; in v6 ``peak_wheel_torque_nm``
|
| 303 |
+
is a first-class design variable, but the LHS sampler still draws
|
| 304 |
+
around this value (multiplied by a log-uniform tail) so the
|
| 305 |
+
surrogate spends data on physically realisable torque sizings.
|
| 306 |
+
|
| 307 |
+
Defaults match
|
| 308 |
+
:class:`roverdevkit.mass.parametric_mers.MassModelParams`. Live
|
| 309 |
+
here (rather than in mass) because the runtime mass model no
|
| 310 |
+
longer computes it; this function exists *only* for the
|
| 311 |
+
LHS prior in :mod:`roverdevkit.surrogate.sampling`.
|
| 312 |
+
"""
|
| 313 |
+
if total_mass_kg <= 0.0 or wheel_radius_m <= 0.0 or n_wheels <= 0:
|
| 314 |
+
raise ValueError(
|
| 315 |
+
"total_mass_kg, wheel_radius_m, n_wheels must be positive "
|
| 316 |
+
f"(got total_mass_kg={total_mass_kg}, "
|
| 317 |
+
f"wheel_radius_m={wheel_radius_m}, n_wheels={n_wheels})."
|
| 318 |
+
)
|
| 319 |
+
weight_per_wheel_n = total_mass_kg * gravity_m_per_s2 / n_wheels
|
| 320 |
+
return (
|
| 321 |
+
motor_sizing_safety_factor
|
| 322 |
+
* motor_peak_friction_coef
|
| 323 |
+
* weight_per_wheel_n
|
| 324 |
+
* wheel_radius_m
|
| 325 |
+
)
|
roverdevkit/mass/__init__.py
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Bottom-up parametric mass model for lunar micro-rovers.
|
| 2 |
+
|
| 3 |
+
See :mod:`.parametric_mers` for the :func:`estimate_mass` top-level
|
| 4 |
+
function and the :class:`MassModelParams` constants bag. See
|
| 5 |
+
:mod:`.validation` for the published-rover cross-check. The design choice
|
| 6 |
+
to go bottom-up (instead of fitting per-subsystem MERs on n~8 published
|
| 7 |
+
rovers) is documented inline in the validation helpers.
|
| 8 |
+
"""
|
| 9 |
+
|
| 10 |
+
from roverdevkit.mass.parametric_mers import (
|
| 11 |
+
MassBreakdown,
|
| 12 |
+
MassModelParams,
|
| 13 |
+
estimate_mass,
|
| 14 |
+
estimate_mass_from_design,
|
| 15 |
+
)
|
| 16 |
+
from roverdevkit.mass.validation import (
|
| 17 |
+
RoverValidationResult,
|
| 18 |
+
RoverValidationRow,
|
| 19 |
+
ValidationSummary,
|
| 20 |
+
format_report,
|
| 21 |
+
load_validation_set,
|
| 22 |
+
predict_row,
|
| 23 |
+
validate_against_published_rovers,
|
| 24 |
+
)
|
| 25 |
+
|
| 26 |
+
__all__ = [
|
| 27 |
+
"MassBreakdown",
|
| 28 |
+
"MassModelParams",
|
| 29 |
+
"RoverValidationResult",
|
| 30 |
+
"RoverValidationRow",
|
| 31 |
+
"ValidationSummary",
|
| 32 |
+
"estimate_mass",
|
| 33 |
+
"estimate_mass_from_design",
|
| 34 |
+
"format_report",
|
| 35 |
+
"load_validation_set",
|
| 36 |
+
"predict_row",
|
| 37 |
+
"validate_against_published_rovers",
|
| 38 |
+
]
|
roverdevkit/mass/parametric_mers.py
ADDED
|
@@ -0,0 +1,474 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Bottom-up parametric mass model for lunar micro-rovers.
|
| 2 |
+
|
| 3 |
+
Approach
|
| 4 |
+
--------
|
| 5 |
+
Each subsystem mass
|
| 6 |
+
is computed from a **physics-grounded specific mass or a standard
|
| 7 |
+
spacecraft-sizing fraction** with a cited source. The rows in
|
| 8 |
+
``data/mass_validation_set.csv`` are then used as a **validation set**
|
| 9 |
+
(see :mod:`roverdevkit.mass.validation`) - "does the bottom-up model
|
| 10 |
+
reproduce total mass within ~30 % for each real rover?".
|
| 11 |
+
|
| 12 |
+
The model is deliberately transparent: every coefficient is exposed as a
|
| 13 |
+
field of :class:`MassModelParams` so it can be overridden for sensitivity
|
| 14 |
+
studies from the surrogate / tradespace layer. Default values are chosen
|
| 15 |
+
from published space-hardware sources; see each field's docstring for the
|
| 16 |
+
citation.
|
| 17 |
+
|
| 18 |
+
Subsystem accounting (SMAD Ch. 11, Table 11-43 convention)::
|
| 19 |
+
|
| 20 |
+
m_subsystems = m_chassis + m_wheels + m_motors + m_solar + m_battery + m_avionics
|
| 21 |
+
m_harness = f_harness * m_subsystems
|
| 22 |
+
m_thermal = f_thermal * (m_subsystems + m_harness)
|
| 23 |
+
m_dry = m_subsystems + m_harness + m_thermal
|
| 24 |
+
m_margin = f_margin * m_dry
|
| 25 |
+
m_total = m_dry + m_margin + m_payload
|
| 26 |
+
|
| 27 |
+
Payload mass (schema v9). Scientific payload is a *mission
|
| 28 |
+
requirement* carried on :class:`roverdevkit.schema.MissionScenario`,
|
| 29 |
+
not a design variable. It enters the total as a top-level line item
|
| 30 |
+
**after** the AIAA S-120A dry-mass growth margin (``m_payload`` is a
|
| 31 |
+
known, specified mass, so the bus growth allowance does not apply to
|
| 32 |
+
it). This matches standard aerospace mass-budget practice (payload is
|
| 33 |
+
tracked separately from bus dry mass) and lets the bottom-up model
|
| 34 |
+
reproduce full-up published rover mass — e.g. Yutu-2's ~25 kg science
|
| 35 |
+
payload no longer has to be hidden inside ``chassis_mass_kg``.
|
| 36 |
+
|
| 37 |
+
Motor mass (schema v6, v6 schema update). The motor subsystem mass is now
|
| 38 |
+
computed directly from the design's
|
| 39 |
+
:attr:`roverdevkit.schema.DesignVector.peak_wheel_torque_nm` (a true
|
| 40 |
+
input), so the pre-v6 fixed-point loop over total mass is gone — this
|
| 41 |
+
function is now strictly bottom-up and converges in a single pass.
|
| 42 |
+
The pre-v6 implicit mass-derived torque ceiling lives on in
|
| 43 |
+
:func:`roverdevkit.drivetrain.motor.sizing_peak_torque_anchor_nm` only
|
| 44 |
+
as the LHS prior anchor.
|
| 45 |
+
|
| 46 |
+
Primary references
|
| 47 |
+
------------------
|
| 48 |
+
Larson, W. J. & Wertz, J. R. *Space Mission Analysis and Design (SMAD)*,
|
| 49 |
+
3rd ed., Microcosm/Springer, 1999.
|
| 50 |
+
Ch. 11 Table 11-43 - subsystem mass fractions.
|
| 51 |
+
Ch. 16 - C&DH MERs.
|
| 52 |
+
|
| 53 |
+
Larson, W. J. & Pranke, L. K. *Human Spaceflight: Mission Analysis and
|
| 54 |
+
Design*, McGraw-Hill, 2000. Surface-system sizing.
|
| 55 |
+
|
| 56 |
+
AIAA S-120A-2015 *Mass Properties Control for Space Systems*, dry-mass
|
| 57 |
+
growth allowances.
|
| 58 |
+
"""
|
| 59 |
+
|
| 60 |
+
from __future__ import annotations
|
| 61 |
+
|
| 62 |
+
import math
|
| 63 |
+
from dataclasses import dataclass, field
|
| 64 |
+
|
| 65 |
+
from roverdevkit.architecture import (
|
| 66 |
+
ArchitectureParams,
|
| 67 |
+
MobilityArchitecture,
|
| 68 |
+
architecture_suspension_mass_kg,
|
| 69 |
+
)
|
| 70 |
+
from roverdevkit.schema import DesignVector
|
| 71 |
+
|
| 72 |
+
# ---------------------------------------------------------------------------
|
| 73 |
+
# Model parameters
|
| 74 |
+
# ---------------------------------------------------------------------------
|
| 75 |
+
|
| 76 |
+
|
| 77 |
+
@dataclass(frozen=True)
|
| 78 |
+
class MassModelParams:
|
| 79 |
+
"""Specific-mass constants and sizing fractions for the bottom-up model.
|
| 80 |
+
|
| 81 |
+
All values are exposed so the tradespace layer can sweep them for
|
| 82 |
+
sensitivity analysis. Defaults are cited in-field.
|
| 83 |
+
"""
|
| 84 |
+
|
| 85 |
+
# -- Wheels -------------------------------------------------------------
|
| 86 |
+
wheel_structural_area_density_kg_per_m2: float = 8.0
|
| 87 |
+
"""Mass per unit of wheel-side area (2*pi*R*W), kg/m^2.
|
| 88 |
+
|
| 89 |
+
Covers rim, hub, spokes, and fastener hardware for aluminium/CFRP rigid
|
| 90 |
+
wheels in the 0.05-0.25 m radius class. Default chosen for the
|
| 91 |
+
micro-rover mass class where thin-gauge aluminium or composite wheels
|
| 92 |
+
dominate. Tune upward toward 15 kg/m^2 for MER/MSL-style stiff-rim wheels.
|
| 93 |
+
"""
|
| 94 |
+
|
| 95 |
+
grouser_plate_thickness_m: float = 0.002
|
| 96 |
+
"""Grouser-plate thickness, m. 2 mm aluminium is typical for
|
| 97 |
+
micro-rover traction fins (Bauer et al., i-SAIRAS 2005; MER grouser
|
| 98 |
+
geometry scaled to micro-rover class)."""
|
| 99 |
+
|
| 100 |
+
grouser_material_density_kg_per_m3: float = 2700.0
|
| 101 |
+
"""Grouser plate material density, kg/m^3. Default = 6061-T6 Al."""
|
| 102 |
+
|
| 103 |
+
# -- Motors and drives -------------------------------------------------
|
| 104 |
+
motor_base_mass_kg: float = 0.15
|
| 105 |
+
"""Irreducible motor + gearbox housing mass per wheel, kg.
|
| 106 |
+
Floor for small brushless motors (~20-50 W) paired with a compact
|
| 107 |
+
planetary or harmonic-drive reducer. Maxon EC-i 32 + GP 32 reaches
|
| 108 |
+
~0.12 kg; we round up to 0.15 kg to cover space-qualified bearings,
|
| 109 |
+
shaft seals, and a flight-heritage connector."""
|
| 110 |
+
|
| 111 |
+
motor_specific_torque_kg_per_nm: float = 0.10
|
| 112 |
+
"""Mass per unit of peak output (post-gearbox) torque, kg/(N*m).
|
| 113 |
+
|
| 114 |
+
Calibrated against vendor catalogues: Maxon EC-i 32 + GP 32 AR
|
| 115 |
+
planetary (100:1) = 0.325 kg at 4 N*m peak output -> 0.08 kg/(N*m);
|
| 116 |
+
Maxon EC-i 40 + GP 52 (80:1) = 1.15 kg at ~20 N*m peak output ->
|
| 117 |
+
0.06 kg/(N*m). We use 0.10 kg/(N*m) as a slightly conservative
|
| 118 |
+
centre of the 0.06-0.12 kg/(N*m) range. Applies to the output
|
| 119 |
+
torque; the motor itself produces a small fraction of this after
|
| 120 |
+
the gear reduction."""
|
| 121 |
+
|
| 122 |
+
motor_peak_friction_coef: float = 0.7
|
| 123 |
+
"""Peak tractive friction coefficient — schema v6 dead parameter.
|
| 124 |
+
|
| 125 |
+
Pre-v6 the mass model sized motor torque internally from this
|
| 126 |
+
coefficient and the rover's lunar weight. v6 makes
|
| 127 |
+
:attr:`roverdevkit.schema.DesignVector.peak_wheel_torque_nm` a
|
| 128 |
+
first-class design input, and this coefficient survives only as a
|
| 129 |
+
default in
|
| 130 |
+
:func:`roverdevkit.drivetrain.motor.sizing_peak_torque_anchor_nm`
|
| 131 |
+
(the LHS prior anchor for the v6 dataset rebuild). Kept on
|
| 132 |
+
:class:`MassModelParams` so existing callers / pickled fixtures
|
| 133 |
+
don't break; remove on the next mass-model bump."""
|
| 134 |
+
|
| 135 |
+
motor_sizing_safety_factor: float = 2.0
|
| 136 |
+
"""Schema v6 dead parameter — see :attr:`motor_peak_friction_coef`."""
|
| 137 |
+
|
| 138 |
+
# -- Solar panels ------------------------------------------------------
|
| 139 |
+
solar_specific_area_mass_kg_per_m2: float = 2.5
|
| 140 |
+
"""Areal mass density of a rigid body-mounted GaAs triple-junction solar
|
| 141 |
+
panel including CFRP substrate and cell-to-substrate bond, kg/m^2.
|
| 142 |
+
SMAD Table 11-43 gives 2.0-5.0 for body-mounted rigid panels;
|
| 143 |
+
Spectrolab/AzurSpace datasheets for UTJ/ZTJ cells on a thin CFRP
|
| 144 |
+
panel land near the lower bound."""
|
| 145 |
+
|
| 146 |
+
# -- Battery -----------------------------------------------------------
|
| 147 |
+
battery_pack_specific_energy_wh_per_kg: float = 120.0
|
| 148 |
+
"""Pack-level specific energy, Wh/kg. Li-ion cell-level ~200 Wh/kg
|
| 149 |
+
multiplied by a ~0.6 pack-integration factor (BMS, casing, harness,
|
| 150 |
+
thermal pads). NASA Glenn Battery Research Center tech reports;
|
| 151 |
+
SMAD Ch. 11 secondary-battery table."""
|
| 152 |
+
|
| 153 |
+
# -- Avionics and C&DH -------------------------------------------------
|
| 154 |
+
avionics_base_mass_kg: float = 0.3
|
| 155 |
+
"""Floor mass for the smallest flyable avionics box, kg.
|
| 156 |
+
Captures enclosure, backplane, and one CPU card. SMAD Ch. 16
|
| 157 |
+
CDH MER lower bound."""
|
| 158 |
+
|
| 159 |
+
avionics_specific_mass_kg_per_w: float = 0.05
|
| 160 |
+
"""Additional kg of structure / heat-sink per W of continuous avionics
|
| 161 |
+
power dissipation. Derived from rule-of-thumb PCB-and-chassis thermal
|
| 162 |
+
sizing at ~0.05 kg/W (SMAD Ch. 16)."""
|
| 163 |
+
|
| 164 |
+
# -- Housekeeping fractions -------------------------------------------
|
| 165 |
+
harness_fraction: float = 0.08
|
| 166 |
+
"""Harness mass as a fraction of the summed subsystem mass, SMAD
|
| 167 |
+
Table 11-43 mid-range (6-10 %)."""
|
| 168 |
+
|
| 169 |
+
thermal_fraction: float = 0.05
|
| 170 |
+
"""Thermal-control (MLI, heaters, straps) mass as a fraction of
|
| 171 |
+
(subsystems + harness). SMAD Table 11-43 small-spacecraft mid-range
|
| 172 |
+
(4-7 %)."""
|
| 173 |
+
|
| 174 |
+
margin_fraction: float = 0.20
|
| 175 |
+
"""Dry-mass growth allowance (margin) as a fraction of dry mass.
|
| 176 |
+
AIAA S-120A-2015 recommends 20 % at PDR maturity, dropping toward
|
| 177 |
+
launch. Tradespace-level work uses the PDR number."""
|
| 178 |
+
|
| 179 |
+
rocker_bogie_fixed_mass_kg: float = 0.5
|
| 180 |
+
"""Fixed rocker-bogie linkage / differential mass, kg."""
|
| 181 |
+
|
| 182 |
+
rocker_bogie_chassis_fraction: float = 0.08
|
| 183 |
+
"""Additional rocker-bogie suspension mass as a fraction of chassis mass."""
|
| 184 |
+
|
| 185 |
+
# -- Environment -------------------------------------------------------
|
| 186 |
+
gravity_moon_m_per_s2: float = 1.625
|
| 187 |
+
"""Surface gravity at the lunar equator, m/s^2."""
|
| 188 |
+
|
| 189 |
+
|
| 190 |
+
# ---------------------------------------------------------------------------
|
| 191 |
+
# Breakdown container
|
| 192 |
+
# ---------------------------------------------------------------------------
|
| 193 |
+
|
| 194 |
+
|
| 195 |
+
@dataclass(frozen=True)
|
| 196 |
+
class MassBreakdown:
|
| 197 |
+
"""Subsystem mass breakdown in kg. Sum of fields equals ``total_kg``."""
|
| 198 |
+
|
| 199 |
+
chassis_kg: float
|
| 200 |
+
wheels_kg: float
|
| 201 |
+
motors_and_drives_kg: float
|
| 202 |
+
solar_panels_kg: float
|
| 203 |
+
battery_kg: float
|
| 204 |
+
avionics_kg: float
|
| 205 |
+
harness_kg: float
|
| 206 |
+
thermal_kg: float
|
| 207 |
+
margin_kg: float
|
| 208 |
+
architecture_kg: float = 0.0
|
| 209 |
+
payload_kg: float = 0.0
|
| 210 |
+
"""Scientific-payload mass, kg (schema v9).
|
| 211 |
+
|
| 212 |
+
A mission requirement carried on
|
| 213 |
+
:class:`roverdevkit.schema.MissionScenario`, added to the total
|
| 214 |
+
*outside* the dry-mass growth margin. Defaults to 0.0 so pre-v9
|
| 215 |
+
callers (and the mass model's own subsystem-only sweeps) are
|
| 216 |
+
unaffected."""
|
| 217 |
+
n_iterations: int = field(default=0, compare=False)
|
| 218 |
+
"""Number of fixed-point iterations taken to converge motor mass.
|
| 219 |
+
|
| 220 |
+
Schema v6 (v6 schema update): always 1 — motor mass is a direct function
|
| 221 |
+
of :attr:`roverdevkit.schema.DesignVector.peak_wheel_torque_nm` so
|
| 222 |
+
the model converges in one pass. Field retained for back-compat
|
| 223 |
+
with pre-v6 fixtures and the validation harness."""
|
| 224 |
+
|
| 225 |
+
@property
|
| 226 |
+
def total_kg(self) -> float:
|
| 227 |
+
return (
|
| 228 |
+
self.chassis_kg
|
| 229 |
+
+ self.wheels_kg
|
| 230 |
+
+ self.motors_and_drives_kg
|
| 231 |
+
+ self.solar_panels_kg
|
| 232 |
+
+ self.battery_kg
|
| 233 |
+
+ self.avionics_kg
|
| 234 |
+
+ self.harness_kg
|
| 235 |
+
+ self.thermal_kg
|
| 236 |
+
+ self.margin_kg
|
| 237 |
+
+ self.architecture_kg
|
| 238 |
+
+ self.payload_kg
|
| 239 |
+
)
|
| 240 |
+
|
| 241 |
+
@property
|
| 242 |
+
def dry_kg(self) -> float:
|
| 243 |
+
"""Bus dry mass: excludes both the growth margin and the payload."""
|
| 244 |
+
return self.total_kg - self.margin_kg - self.payload_kg
|
| 245 |
+
|
| 246 |
+
|
| 247 |
+
# ---------------------------------------------------------------------------
|
| 248 |
+
# Per-subsystem helpers
|
| 249 |
+
# ---------------------------------------------------------------------------
|
| 250 |
+
|
| 251 |
+
|
| 252 |
+
def _wheels_mass(
|
| 253 |
+
wheel_radius_m: float,
|
| 254 |
+
wheel_width_m: float,
|
| 255 |
+
grouser_height_m: float,
|
| 256 |
+
grouser_count: int,
|
| 257 |
+
n_wheels: int,
|
| 258 |
+
params: MassModelParams,
|
| 259 |
+
) -> float:
|
| 260 |
+
"""Rim-and-hub + grouser mass for all drive wheels.
|
| 261 |
+
|
| 262 |
+
Structural term: ``rho_wheel_area * (2 * pi * R * W) * n_wheels``, where
|
| 263 |
+
the side-area factor captures the dominant scaling of a rim-and-hub
|
| 264 |
+
wheel with a thin cylindrical skin (calibrated to lunar-wheel
|
| 265 |
+
hardware, not derived from first-principles shell theory).
|
| 266 |
+
|
| 267 |
+
Grouser term: each grouser is modelled as a thin rectangular aluminium
|
| 268 |
+
plate of dimensions ``W x h_g x t``; mass is
|
| 269 |
+
``N_g * W * h_g * t * rho_Al``.
|
| 270 |
+
"""
|
| 271 |
+
if wheel_radius_m <= 0.0 or wheel_width_m <= 0.0 or n_wheels <= 0:
|
| 272 |
+
raise ValueError("wheel_radius_m, wheel_width_m and n_wheels must be positive.")
|
| 273 |
+
if grouser_height_m < 0.0 or grouser_count < 0:
|
| 274 |
+
raise ValueError("grouser_height_m and grouser_count must be non-negative.")
|
| 275 |
+
|
| 276 |
+
side_area_m2 = 2.0 * math.pi * wheel_radius_m * wheel_width_m
|
| 277 |
+
structural_kg = params.wheel_structural_area_density_kg_per_m2 * side_area_m2
|
| 278 |
+
|
| 279 |
+
grouser_volume_m3 = (
|
| 280 |
+
grouser_count * wheel_width_m * grouser_height_m * params.grouser_plate_thickness_m
|
| 281 |
+
)
|
| 282 |
+
grouser_kg = grouser_volume_m3 * params.grouser_material_density_kg_per_m3
|
| 283 |
+
|
| 284 |
+
return n_wheels * (structural_kg + grouser_kg)
|
| 285 |
+
|
| 286 |
+
|
| 287 |
+
def _motors_mass(
|
| 288 |
+
n_wheels: int,
|
| 289 |
+
peak_wheel_torque_nm: float,
|
| 290 |
+
params: MassModelParams,
|
| 291 |
+
) -> float:
|
| 292 |
+
"""Drive-motor + gearbox mass sized from the peak-wheel torque.
|
| 293 |
+
|
| 294 |
+
Schema v6 (v6 schema update): ``peak_wheel_torque_nm`` is now a direct
|
| 295 |
+
design input rather than something derived from the vehicle's
|
| 296 |
+
lunar weight inside the mass model. Per-motor mass remains
|
| 297 |
+
``m_0 + k_tau * tau_peak``; total summed over ``n_wheels``. The
|
| 298 |
+
pre-v6 mass-derived ceiling lives on in
|
| 299 |
+
:func:`roverdevkit.drivetrain.motor.sizing_peak_torque_anchor_nm`
|
| 300 |
+
only as an LHS prior anchor.
|
| 301 |
+
"""
|
| 302 |
+
if peak_wheel_torque_nm < 0.0:
|
| 303 |
+
raise ValueError("peak_wheel_torque_nm must be non-negative.")
|
| 304 |
+
if n_wheels <= 0:
|
| 305 |
+
raise ValueError("n_wheels must be positive.")
|
| 306 |
+
|
| 307 |
+
per_motor_kg = (
|
| 308 |
+
params.motor_base_mass_kg
|
| 309 |
+
+ params.motor_specific_torque_kg_per_nm * peak_wheel_torque_nm
|
| 310 |
+
)
|
| 311 |
+
return n_wheels * per_motor_kg
|
| 312 |
+
|
| 313 |
+
|
| 314 |
+
def _solar_panels_mass(solar_area_m2: float, params: MassModelParams) -> float:
|
| 315 |
+
if solar_area_m2 < 0.0:
|
| 316 |
+
raise ValueError("solar_area_m2 must be non-negative.")
|
| 317 |
+
return params.solar_specific_area_mass_kg_per_m2 * solar_area_m2
|
| 318 |
+
|
| 319 |
+
|
| 320 |
+
def _battery_mass(battery_capacity_wh: float, params: MassModelParams) -> float:
|
| 321 |
+
if battery_capacity_wh < 0.0:
|
| 322 |
+
raise ValueError("battery_capacity_wh must be non-negative.")
|
| 323 |
+
return battery_capacity_wh / params.battery_pack_specific_energy_wh_per_kg
|
| 324 |
+
|
| 325 |
+
|
| 326 |
+
def _avionics_mass(avionics_power_w: float, params: MassModelParams) -> float:
|
| 327 |
+
if avionics_power_w < 0.0:
|
| 328 |
+
raise ValueError("avionics_power_w must be non-negative.")
|
| 329 |
+
return params.avionics_base_mass_kg + params.avionics_specific_mass_kg_per_w * avionics_power_w
|
| 330 |
+
|
| 331 |
+
|
| 332 |
+
# ---------------------------------------------------------------------------
|
| 333 |
+
# Top-level entry point
|
| 334 |
+
# ---------------------------------------------------------------------------
|
| 335 |
+
|
| 336 |
+
|
| 337 |
+
def estimate_mass(
|
| 338 |
+
*,
|
| 339 |
+
wheel_radius_m: float,
|
| 340 |
+
wheel_width_m: float,
|
| 341 |
+
n_wheels: int,
|
| 342 |
+
chassis_mass_kg: float,
|
| 343 |
+
solar_area_m2: float,
|
| 344 |
+
battery_capacity_wh: float,
|
| 345 |
+
avionics_power_w: float,
|
| 346 |
+
peak_wheel_torque_nm: float,
|
| 347 |
+
grouser_height_m: float = 0.0,
|
| 348 |
+
grouser_count: int = 0,
|
| 349 |
+
payload_mass_kg: float = 0.0,
|
| 350 |
+
mobility_architecture: MobilityArchitecture = "rigid_4wheel",
|
| 351 |
+
params: MassModelParams | None = None,
|
| 352 |
+
) -> MassBreakdown:
|
| 353 |
+
"""Assemble a bottom-up subsystem mass breakdown for a rover design.
|
| 354 |
+
|
| 355 |
+
Schema v6 (v6 schema update). All subsystems are load-independent now
|
| 356 |
+
that ``peak_wheel_torque_nm`` is a true design input — the pre-v6
|
| 357 |
+
fixed-point iteration over total mass is gone, and this function
|
| 358 |
+
converges in a single pass. The ``n_iterations`` field on the
|
| 359 |
+
returned :class:`MassBreakdown` is kept for backward compatibility
|
| 360 |
+
but is always 1 in v6.
|
| 361 |
+
|
| 362 |
+
The keyword-only signature matches the design-variable names on
|
| 363 |
+
:class:`roverdevkit.schema.DesignVector`. See
|
| 364 |
+
:func:`estimate_mass_from_design` for a convenience wrapper.
|
| 365 |
+
|
| 366 |
+
Parameters
|
| 367 |
+
----------
|
| 368 |
+
wheel_radius_m, wheel_width_m
|
| 369 |
+
Wheel geometry, m.
|
| 370 |
+
n_wheels
|
| 371 |
+
Drive-wheel count (4 or 6 per :class:`DesignVector`).
|
| 372 |
+
chassis_mass_kg
|
| 373 |
+
Dry chassis structural mass, kg. A design-variable input.
|
| 374 |
+
solar_area_m2, battery_capacity_wh, avionics_power_w
|
| 375 |
+
Power-subsystem design variables.
|
| 376 |
+
peak_wheel_torque_nm
|
| 377 |
+
Peak per-wheel hub torque the drivetrain delivers, Nm. Sizes
|
| 378 |
+
motor mass directly via ``m_0 + k_tau * tau_peak``.
|
| 379 |
+
grouser_height_m, grouser_count
|
| 380 |
+
Grouser geometry, m and count. Defaults to 0.
|
| 381 |
+
payload_mass_kg
|
| 382 |
+
Scientific-payload mass, kg (schema v9). A mission requirement
|
| 383 |
+
from :attr:`roverdevkit.schema.MissionScenario.payload_mass_kg`.
|
| 384 |
+
Added to the total *after* the dry-mass growth margin (payload
|
| 385 |
+
is a known mass, not grown). Defaults to 0.0.
|
| 386 |
+
params
|
| 387 |
+
:class:`MassModelParams` override; defaults to the module defaults.
|
| 388 |
+
|
| 389 |
+
Returns
|
| 390 |
+
-------
|
| 391 |
+
MassBreakdown
|
| 392 |
+
Subsystem masses summing to the total vehicle mass.
|
| 393 |
+
|
| 394 |
+
Raises
|
| 395 |
+
------
|
| 396 |
+
ValueError
|
| 397 |
+
On any non-physical input (negative masses, non-positive
|
| 398 |
+
geometry).
|
| 399 |
+
"""
|
| 400 |
+
params = params or MassModelParams()
|
| 401 |
+
|
| 402 |
+
if payload_mass_kg < 0.0:
|
| 403 |
+
raise ValueError("payload_mass_kg must be non-negative.")
|
| 404 |
+
|
| 405 |
+
m_chassis = chassis_mass_kg
|
| 406 |
+
if m_chassis <= 0.0:
|
| 407 |
+
raise ValueError("chassis_mass_kg must be positive.")
|
| 408 |
+
m_wheels = _wheels_mass(
|
| 409 |
+
wheel_radius_m, wheel_width_m, grouser_height_m, grouser_count, n_wheels, params
|
| 410 |
+
)
|
| 411 |
+
m_solar = _solar_panels_mass(solar_area_m2, params)
|
| 412 |
+
m_battery = _battery_mass(battery_capacity_wh, params)
|
| 413 |
+
m_avionics = _avionics_mass(avionics_power_w, params)
|
| 414 |
+
m_motors = _motors_mass(n_wheels, peak_wheel_torque_nm, params)
|
| 415 |
+
|
| 416 |
+
m_subsystems = m_chassis + m_wheels + m_motors + m_solar + m_battery + m_avionics
|
| 417 |
+
m_architecture = architecture_suspension_mass_kg(
|
| 418 |
+
mobility_architecture,
|
| 419 |
+
m_chassis,
|
| 420 |
+
params=ArchitectureParams(
|
| 421 |
+
rocker_bogie_fixed_mass_kg=params.rocker_bogie_fixed_mass_kg,
|
| 422 |
+
rocker_bogie_chassis_fraction=params.rocker_bogie_chassis_fraction,
|
| 423 |
+
),
|
| 424 |
+
)
|
| 425 |
+
m_subsystems += m_architecture
|
| 426 |
+
m_harness = params.harness_fraction * m_subsystems
|
| 427 |
+
m_thermal = params.thermal_fraction * (m_subsystems + m_harness)
|
| 428 |
+
m_dry = m_subsystems + m_harness + m_thermal
|
| 429 |
+
m_margin = params.margin_fraction * m_dry
|
| 430 |
+
|
| 431 |
+
return MassBreakdown(
|
| 432 |
+
chassis_kg=m_chassis,
|
| 433 |
+
wheels_kg=m_wheels,
|
| 434 |
+
motors_and_drives_kg=m_motors,
|
| 435 |
+
solar_panels_kg=m_solar,
|
| 436 |
+
battery_kg=m_battery,
|
| 437 |
+
avionics_kg=m_avionics,
|
| 438 |
+
harness_kg=m_harness,
|
| 439 |
+
thermal_kg=m_thermal,
|
| 440 |
+
margin_kg=m_margin,
|
| 441 |
+
architecture_kg=m_architecture,
|
| 442 |
+
payload_kg=payload_mass_kg,
|
| 443 |
+
n_iterations=1,
|
| 444 |
+
)
|
| 445 |
+
|
| 446 |
+
|
| 447 |
+
def estimate_mass_from_design(
|
| 448 |
+
design: DesignVector,
|
| 449 |
+
params: MassModelParams | None = None,
|
| 450 |
+
*,
|
| 451 |
+
payload_mass_kg: float = 0.0,
|
| 452 |
+
) -> MassBreakdown:
|
| 453 |
+
"""Convenience wrapper that unpacks a :class:`DesignVector`.
|
| 454 |
+
|
| 455 |
+
``payload_mass_kg`` (schema v9) is a mission requirement that lives
|
| 456 |
+
on :class:`roverdevkit.schema.MissionScenario`, not on the design
|
| 457 |
+
vector, so it is passed in explicitly by the evaluator. Defaults to
|
| 458 |
+
0.0 for callers that only need the bus mass.
|
| 459 |
+
"""
|
| 460 |
+
return estimate_mass(
|
| 461 |
+
wheel_radius_m=design.wheel_radius_m,
|
| 462 |
+
wheel_width_m=design.wheel_width_m,
|
| 463 |
+
n_wheels=design.n_wheels,
|
| 464 |
+
chassis_mass_kg=design.chassis_mass_kg,
|
| 465 |
+
solar_area_m2=design.solar_area_m2,
|
| 466 |
+
battery_capacity_wh=design.battery_capacity_wh,
|
| 467 |
+
avionics_power_w=design.avionics_power_w,
|
| 468 |
+
peak_wheel_torque_nm=design.peak_wheel_torque_nm,
|
| 469 |
+
grouser_height_m=design.grouser_height_m,
|
| 470 |
+
grouser_count=design.grouser_count,
|
| 471 |
+
payload_mass_kg=payload_mass_kg,
|
| 472 |
+
mobility_architecture=design.mobility_architecture,
|
| 473 |
+
params=params,
|
| 474 |
+
)
|
roverdevkit/mass/validation.py
ADDED
|
@@ -0,0 +1,251 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Cross-check the bottom-up mass model against published rover total masses.
|
| 2 |
+
|
| 3 |
+
The validation set lives in ``data/mass_validation_set.csv``. Each row is a
|
| 4 |
+
best-effort full design vector for a published rover, with an
|
| 5 |
+
``imputation_notes`` column documenting every field that was not directly
|
| 6 |
+
published and how it was estimated.
|
| 7 |
+
|
| 8 |
+
The ``in_class`` flag (True/False) marks whether the rover is inside the
|
| 9 |
+
bottom-up mass model's specific-mass calibration regime (5-50 kg
|
| 10 |
+
lunar micro-rovers). At sub-5-kg total mass the bottom-up
|
| 11 |
+
model's fixed-cost terms (per-wheel motor base mass, avionics base
|
| 12 |
+
mass, harness / thermal / margin fractions) come to dominate, and the
|
| 13 |
+
model systematically over-predicts total mass relative to ultra-micro
|
| 14 |
+
hardware which uses mass-optimised custom motors and avionics that
|
| 15 |
+
the SMAD/AIAA/vendor-catalogue specific-mass constants do not reflect.
|
| 16 |
+
Updating the constants for the ultra-micro regime would invalidate
|
| 17 |
+
the model's calibration on the 5-50 kg class, so we keep the
|
| 18 |
+
calibration unchanged and explicitly mark sub-5-kg rovers as
|
| 19 |
+
``in_class=False``.
|
| 20 |
+
|
| 21 |
+
The primary validation statistic is **median absolute percent error on
|
| 22 |
+
in-class rovers**; the target is <= 30 % (plan §8). Out-of-regime
|
| 23 |
+
rovers (CADRE at 2 kg, Yutu-2 at 135 kg, etc.) are reported alongside
|
| 24 |
+
but excluded from the primary statistic.
|
| 25 |
+
"""
|
| 26 |
+
|
| 27 |
+
from __future__ import annotations
|
| 28 |
+
|
| 29 |
+
import csv
|
| 30 |
+
from dataclasses import dataclass
|
| 31 |
+
from pathlib import Path
|
| 32 |
+
from statistics import mean, median
|
| 33 |
+
|
| 34 |
+
from roverdevkit.architecture import architecture_for_wheel_count
|
| 35 |
+
from roverdevkit.drivetrain.motor import sizing_peak_torque_anchor_nm
|
| 36 |
+
from roverdevkit.mass.parametric_mers import (
|
| 37 |
+
MassBreakdown,
|
| 38 |
+
MassModelParams,
|
| 39 |
+
estimate_mass,
|
| 40 |
+
)
|
| 41 |
+
|
| 42 |
+
DEFAULT_VALIDATION_CSV: Path = (
|
| 43 |
+
Path(__file__).resolve().parents[2] / "data" / "mass_validation_set.csv"
|
| 44 |
+
)
|
| 45 |
+
|
| 46 |
+
|
| 47 |
+
@dataclass(frozen=True)
|
| 48 |
+
class RoverValidationRow:
|
| 49 |
+
"""One row of the validation set: a published rover plus imputations.
|
| 50 |
+
|
| 51 |
+
``in_class`` marks whether the rover sits inside the bottom-up
|
| 52 |
+
mass model's specific-mass calibration regime (5-50 kg lunar
|
| 53 |
+
micro-rovers). See the
|
| 54 |
+
module docstring for why the two diverged on 2026-05-27.
|
| 55 |
+
"""
|
| 56 |
+
|
| 57 |
+
rover_name: str
|
| 58 |
+
mass_total_kg: float
|
| 59 |
+
wheel_radius_m: float
|
| 60 |
+
wheel_width_m: float
|
| 61 |
+
n_wheels: int
|
| 62 |
+
chassis_mass_kg: float
|
| 63 |
+
solar_area_m2: float
|
| 64 |
+
battery_capacity_wh: float
|
| 65 |
+
avionics_power_w: float
|
| 66 |
+
grouser_height_m: float
|
| 67 |
+
grouser_count: int
|
| 68 |
+
payload_mass_kg: float
|
| 69 |
+
"""Scientific-payload mass, kg.
|
| 70 |
+
|
| 71 |
+
Separated out of the back-solved ``chassis_mass_kg`` bucket so the
|
| 72 |
+
bottom-up model sizes only the *bus* and adds payload as a flat,
|
| 73 |
+
ungrown line item — matching how payload enters the live evaluator.
|
| 74 |
+
See ``data/mass_validation_set.csv`` ``citation`` and
|
| 75 |
+
``imputation_notes`` for the per-rover literature source."""
|
| 76 |
+
in_class: bool
|
| 77 |
+
citation: str
|
| 78 |
+
imputation_notes: str
|
| 79 |
+
|
| 80 |
+
|
| 81 |
+
@dataclass(frozen=True)
|
| 82 |
+
class RoverValidationResult:
|
| 83 |
+
"""Outcome of running the bottom-up mass model on one rover.
|
| 84 |
+
|
| 85 |
+
``in_class`` mirrors :class:`RoverValidationRow.in_class`: True iff
|
| 86 |
+
the rover sits inside the mass-model calibration regime.
|
| 87 |
+
"""
|
| 88 |
+
|
| 89 |
+
rover_name: str
|
| 90 |
+
in_class: bool
|
| 91 |
+
mass_published_kg: float
|
| 92 |
+
mass_predicted_kg: float
|
| 93 |
+
breakdown: MassBreakdown
|
| 94 |
+
|
| 95 |
+
@property
|
| 96 |
+
def absolute_error_kg(self) -> float:
|
| 97 |
+
return self.mass_predicted_kg - self.mass_published_kg
|
| 98 |
+
|
| 99 |
+
@property
|
| 100 |
+
def percent_error(self) -> float:
|
| 101 |
+
return 100.0 * self.absolute_error_kg / self.mass_published_kg
|
| 102 |
+
|
| 103 |
+
|
| 104 |
+
@dataclass(frozen=True)
|
| 105 |
+
class ValidationSummary:
|
| 106 |
+
"""Aggregate statistics over a batch of validation rows."""
|
| 107 |
+
|
| 108 |
+
n_total: int
|
| 109 |
+
n_in_class: int
|
| 110 |
+
median_abs_percent_error_in_class: float
|
| 111 |
+
mean_abs_percent_error_in_class: float
|
| 112 |
+
worst_in_class: RoverValidationResult
|
| 113 |
+
per_rover: tuple[RoverValidationResult, ...]
|
| 114 |
+
|
| 115 |
+
|
| 116 |
+
# ---------------------------------------------------------------------------
|
| 117 |
+
# Loading
|
| 118 |
+
# ---------------------------------------------------------------------------
|
| 119 |
+
|
| 120 |
+
|
| 121 |
+
def _parse_bool(value: str) -> bool:
|
| 122 |
+
v = value.strip().lower()
|
| 123 |
+
if v in ("true", "1", "yes", "y"):
|
| 124 |
+
return True
|
| 125 |
+
if v in ("false", "0", "no", "n"):
|
| 126 |
+
return False
|
| 127 |
+
raise ValueError(f"unparseable boolean: {value!r}")
|
| 128 |
+
|
| 129 |
+
|
| 130 |
+
def load_validation_set(csv_path: Path | str | None = None) -> list[RoverValidationRow]:
|
| 131 |
+
"""Read ``data/mass_validation_set.csv`` into a list of dataclasses."""
|
| 132 |
+
path = Path(csv_path) if csv_path else DEFAULT_VALIDATION_CSV
|
| 133 |
+
rows: list[RoverValidationRow] = []
|
| 134 |
+
with path.open() as f:
|
| 135 |
+
reader = csv.DictReader(f)
|
| 136 |
+
for row in reader:
|
| 137 |
+
rows.append(
|
| 138 |
+
RoverValidationRow(
|
| 139 |
+
rover_name=row["rover_name"],
|
| 140 |
+
mass_total_kg=float(row["mass_total_kg"]),
|
| 141 |
+
wheel_radius_m=float(row["wheel_radius_m"]),
|
| 142 |
+
wheel_width_m=float(row["wheel_width_m"]),
|
| 143 |
+
n_wheels=int(row["n_wheels"]),
|
| 144 |
+
chassis_mass_kg=float(row["chassis_mass_kg"]),
|
| 145 |
+
solar_area_m2=float(row["solar_area_m2"]),
|
| 146 |
+
battery_capacity_wh=float(row["battery_capacity_wh"]),
|
| 147 |
+
avionics_power_w=float(row["avionics_power_w"]),
|
| 148 |
+
grouser_height_m=float(row["grouser_height_m"]),
|
| 149 |
+
grouser_count=int(row["grouser_count"]),
|
| 150 |
+
payload_mass_kg=float(row.get("payload_mass_kg", 0.0) or 0.0),
|
| 151 |
+
in_class=_parse_bool(row["in_class"]),
|
| 152 |
+
citation=row.get("citation", ""),
|
| 153 |
+
imputation_notes=row["imputation_notes"],
|
| 154 |
+
)
|
| 155 |
+
)
|
| 156 |
+
return rows
|
| 157 |
+
|
| 158 |
+
|
| 159 |
+
# ---------------------------------------------------------------------------
|
| 160 |
+
# Running the comparison
|
| 161 |
+
# ---------------------------------------------------------------------------
|
| 162 |
+
|
| 163 |
+
|
| 164 |
+
def predict_row(
|
| 165 |
+
row: RoverValidationRow,
|
| 166 |
+
params: MassModelParams | None = None,
|
| 167 |
+
) -> RoverValidationResult:
|
| 168 |
+
"""Run ``estimate_mass`` on a single validation row.
|
| 169 |
+
"""
|
| 170 |
+
peak_wheel_torque_nm = sizing_peak_torque_anchor_nm(
|
| 171 |
+
total_mass_kg=row.mass_total_kg,
|
| 172 |
+
wheel_radius_m=row.wheel_radius_m,
|
| 173 |
+
n_wheels=row.n_wheels,
|
| 174 |
+
)
|
| 175 |
+
breakdown = estimate_mass(
|
| 176 |
+
wheel_radius_m=row.wheel_radius_m,
|
| 177 |
+
wheel_width_m=row.wheel_width_m,
|
| 178 |
+
n_wheels=row.n_wheels,
|
| 179 |
+
chassis_mass_kg=row.chassis_mass_kg,
|
| 180 |
+
solar_area_m2=row.solar_area_m2,
|
| 181 |
+
battery_capacity_wh=row.battery_capacity_wh,
|
| 182 |
+
avionics_power_w=row.avionics_power_w,
|
| 183 |
+
peak_wheel_torque_nm=peak_wheel_torque_nm,
|
| 184 |
+
grouser_height_m=row.grouser_height_m,
|
| 185 |
+
grouser_count=row.grouser_count,
|
| 186 |
+
payload_mass_kg=row.payload_mass_kg,
|
| 187 |
+
mobility_architecture=architecture_for_wheel_count(row.n_wheels),
|
| 188 |
+
params=params,
|
| 189 |
+
)
|
| 190 |
+
return RoverValidationResult(
|
| 191 |
+
rover_name=row.rover_name,
|
| 192 |
+
in_class=row.in_class,
|
| 193 |
+
mass_published_kg=row.mass_total_kg,
|
| 194 |
+
mass_predicted_kg=breakdown.total_kg,
|
| 195 |
+
breakdown=breakdown,
|
| 196 |
+
)
|
| 197 |
+
|
| 198 |
+
|
| 199 |
+
def validate_against_published_rovers(
|
| 200 |
+
csv_path: Path | str | None = None,
|
| 201 |
+
params: MassModelParams | None = None,
|
| 202 |
+
) -> ValidationSummary:
|
| 203 |
+
"""Run the bottom-up mass model on the full validation set and summarise.
|
| 204 |
+
|
| 205 |
+
The primary statistic returned is the median absolute percent error on
|
| 206 |
+
in-class (5-50 kg) rovers. Out-of-class rovers (nano, medium, large)
|
| 207 |
+
are included in ``per_rover`` but excluded from the in-class
|
| 208 |
+
statistics, reflecting the 5-50 kg calibration range of the specific
|
| 209 |
+
mass constants in :class:`MassModelParams`.
|
| 210 |
+
"""
|
| 211 |
+
rows = load_validation_set(csv_path)
|
| 212 |
+
results = tuple(predict_row(r, params=params) for r in rows)
|
| 213 |
+
|
| 214 |
+
in_class_results = [r for r in results if r.in_class]
|
| 215 |
+
if not in_class_results:
|
| 216 |
+
raise ValueError("Validation set contains no in-class rovers.")
|
| 217 |
+
|
| 218 |
+
in_class_abs_errors = [abs(r.percent_error) for r in in_class_results]
|
| 219 |
+
worst = max(in_class_results, key=lambda r: abs(r.percent_error))
|
| 220 |
+
|
| 221 |
+
return ValidationSummary(
|
| 222 |
+
n_total=len(results),
|
| 223 |
+
n_in_class=len(in_class_results),
|
| 224 |
+
median_abs_percent_error_in_class=float(median(in_class_abs_errors)),
|
| 225 |
+
mean_abs_percent_error_in_class=float(mean(in_class_abs_errors)),
|
| 226 |
+
worst_in_class=worst,
|
| 227 |
+
per_rover=results,
|
| 228 |
+
)
|
| 229 |
+
|
| 230 |
+
|
| 231 |
+
def format_report(summary: ValidationSummary) -> str:
|
| 232 |
+
"""Human-readable table for notebooks and reports."""
|
| 233 |
+
lines = [
|
| 234 |
+
"Rover in_class published (kg) predicted (kg) err %",
|
| 235 |
+
"-" * 73,
|
| 236 |
+
]
|
| 237 |
+
for r in summary.per_rover:
|
| 238 |
+
flag = "yes" if r.in_class else "no "
|
| 239 |
+
lines.append(
|
| 240 |
+
f"{r.rover_name:20s} {flag:>8s} {r.mass_published_kg:14.2f} "
|
| 241 |
+
f"{r.mass_predicted_kg:14.2f} {r.percent_error:+7.1f}"
|
| 242 |
+
)
|
| 243 |
+
lines.append("-" * 73)
|
| 244 |
+
lines.append(
|
| 245 |
+
f"Aggregates on in-class rovers (n={summary.n_in_class}): "
|
| 246 |
+
f"median |err| = {summary.median_abs_percent_error_in_class:.1f} %, "
|
| 247 |
+
f"mean |err| = {summary.mean_abs_percent_error_in_class:.1f} %, "
|
| 248 |
+
f"worst = {summary.worst_in_class.rover_name} "
|
| 249 |
+
f"({summary.worst_in_class.percent_error:+.1f} %)."
|
| 250 |
+
)
|
| 251 |
+
return "\n".join(lines)
|
roverdevkit/mission/__init__.py
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Mission evaluator: the primary artifact of the project.
|
| 2 |
+
|
| 3 |
+
- :mod:`.evaluator` — top-level ``evaluate(design, scenario) → metrics``.
|
| 4 |
+
- :mod:`.scenarios` — the four canonical mission scenarios as configs.
|
| 5 |
+
- :mod:`.traverse_sim` — time-stepped traverse loop integrating
|
| 6 |
+
terramechanics, power, battery, mass, and thermal.
|
| 7 |
+
- :mod:`.capability` — static mobility capability metrics (max slope).
|
| 8 |
+
"""
|
| 9 |
+
|
| 10 |
+
from roverdevkit.mission.capability import max_climbable_slope_deg
|
| 11 |
+
from roverdevkit.mission.evaluator import evaluate
|
| 12 |
+
from roverdevkit.mission.scenarios import list_scenarios, load_scenario
|
| 13 |
+
from roverdevkit.mission.traverse_sim import TraverseLog, run_traverse
|
| 14 |
+
|
| 15 |
+
__all__ = [
|
| 16 |
+
"TraverseLog",
|
| 17 |
+
"evaluate",
|
| 18 |
+
"list_scenarios",
|
| 19 |
+
"load_scenario",
|
| 20 |
+
"max_climbable_slope_deg",
|
| 21 |
+
"run_traverse",
|
| 22 |
+
]
|
roverdevkit/mission/capability.py
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Derived mobility capabilities computed from the Bekker-Wong model.
|
| 2 |
+
|
| 3 |
+
Right now this contains one thing: the **maximum climbable slope** for a
|
| 4 |
+
rover design on a given soil. This is separate from the traverse sim
|
| 5 |
+
because it's a static capability metric (not time-resolved) and it
|
| 6 |
+
populates ``MissionMetrics.slope_capability_deg`` for the canonical mission scenarios
|
| 7 |
+
scenario 3).
|
| 8 |
+
|
| 9 |
+
Approach
|
| 10 |
+
--------
|
| 11 |
+
For a rover traversing a slope of inclination theta, at steady speed:
|
| 12 |
+
|
| 13 |
+
Tractive force required per wheel = m*g*sin(theta) / n_wheels
|
| 14 |
+
Normal load per wheel = m*g*cos(theta) / n_wheels
|
| 15 |
+
|
| 16 |
+
The Bekker-Wong model's ``drawbar_pull_n`` is the *net* horizontal force
|
| 17 |
+
a single wheel delivers beyond its own motion resistance, so DP must
|
| 18 |
+
balance the gradient term alone. At each candidate slope we evaluate
|
| 19 |
+
:func:`single_wheel_forces` at a reference high-slip point
|
| 20 |
+
(``max_slip``) to get the maximum available DP and look for the slope
|
| 21 |
+
at which available DP equals required DP.
|
| 22 |
+
|
| 23 |
+
We cap the search at 35 deg because (a) the :class:`MissionScenario`
|
| 24 |
+
schema allows ``max_slope_deg <= 35`` and (b) at that slope rover
|
| 25 |
+
stability (tip-over) starts to dominate over traction, which this model
|
| 26 |
+
ignores.
|
| 27 |
+
"""
|
| 28 |
+
|
| 29 |
+
from __future__ import annotations
|
| 30 |
+
|
| 31 |
+
import math
|
| 32 |
+
|
| 33 |
+
from scipy.optimize import brentq
|
| 34 |
+
|
| 35 |
+
from roverdevkit.terramechanics.bekker_wong import (
|
| 36 |
+
SoilParameters,
|
| 37 |
+
WheelGeometry,
|
| 38 |
+
single_wheel_forces,
|
| 39 |
+
)
|
| 40 |
+
|
| 41 |
+
DEFAULT_LUNAR_GRAVITY_M_PER_S2: float = 1.625
|
| 42 |
+
"""Reference lunar gravity; matches :data:`MassModelParams.gravity_moon_m_per_s2`."""
|
| 43 |
+
|
| 44 |
+
DEFAULT_MAX_SLIP_FOR_CAPABILITY: float = 0.6
|
| 45 |
+
"""Reference high-slip operating point for max-DP (Wong 2008 §4.2)."""
|
| 46 |
+
|
| 47 |
+
SLOPE_SEARCH_UPPER_DEG: float = 35.0
|
| 48 |
+
"""Upper bound of the brentq search, matching the scenario schema cap."""
|
| 49 |
+
|
| 50 |
+
|
| 51 |
+
def _dp_balance_residual(
|
| 52 |
+
slope_deg: float,
|
| 53 |
+
*,
|
| 54 |
+
wheel: WheelGeometry,
|
| 55 |
+
soil: SoilParameters,
|
| 56 |
+
total_mass_kg: float,
|
| 57 |
+
n_wheels: int,
|
| 58 |
+
gravity_m_per_s2: float,
|
| 59 |
+
max_slip: float,
|
| 60 |
+
) -> float:
|
| 61 |
+
"""Available minus required drawbar pull per wheel (in N).
|
| 62 |
+
|
| 63 |
+
Positive = rover can climb this slope with margin to spare;
|
| 64 |
+
negative = unclimbable.
|
| 65 |
+
"""
|
| 66 |
+
theta = math.radians(slope_deg)
|
| 67 |
+
weight_n = total_mass_kg * gravity_m_per_s2
|
| 68 |
+
load_per_wheel_n = weight_n * math.cos(theta) / n_wheels
|
| 69 |
+
required_dp_n = weight_n * math.sin(theta) / n_wheels
|
| 70 |
+
|
| 71 |
+
forces = single_wheel_forces(wheel, soil, load_per_wheel_n, slip=max_slip)
|
| 72 |
+
return forces.drawbar_pull_n - required_dp_n
|
| 73 |
+
|
| 74 |
+
|
| 75 |
+
def max_climbable_slope_deg(
|
| 76 |
+
wheel: WheelGeometry,
|
| 77 |
+
soil: SoilParameters,
|
| 78 |
+
total_mass_kg: float,
|
| 79 |
+
n_wheels: int,
|
| 80 |
+
*,
|
| 81 |
+
gravity_m_per_s2: float = DEFAULT_LUNAR_GRAVITY_M_PER_S2,
|
| 82 |
+
max_slip: float = DEFAULT_MAX_SLIP_FOR_CAPABILITY,
|
| 83 |
+
) -> float:
|
| 84 |
+
"""Largest slope (deg) this design can climb on this soil.
|
| 85 |
+
|
| 86 |
+
Parameters
|
| 87 |
+
----------
|
| 88 |
+
wheel, soil
|
| 89 |
+
Bekker-Wong geometry and soil parameters.
|
| 90 |
+
total_mass_kg
|
| 91 |
+
Vehicle mass (from the mass model).
|
| 92 |
+
n_wheels
|
| 93 |
+
Number of driven wheels.
|
| 94 |
+
gravity_m_per_s2
|
| 95 |
+
Surface gravity, default lunar.
|
| 96 |
+
max_slip
|
| 97 |
+
Slip ratio at which to evaluate max available DP. 0.6 is the
|
| 98 |
+
conventional choice for short-duration peak pull (Wong 2008).
|
| 99 |
+
|
| 100 |
+
Returns
|
| 101 |
+
-------
|
| 102 |
+
float
|
| 103 |
+
Slope in degrees, in ``[0, 35]``. Returns 35 if the rover can
|
| 104 |
+
climb at least that steep (the schema cap). Returns 0 if the
|
| 105 |
+
rover cannot move on flat ground.
|
| 106 |
+
"""
|
| 107 |
+
if total_mass_kg <= 0.0 or n_wheels <= 0:
|
| 108 |
+
raise ValueError("total_mass_kg and n_wheels must be positive.")
|
| 109 |
+
|
| 110 |
+
def residual(slope_deg: float) -> float:
|
| 111 |
+
return _dp_balance_residual(
|
| 112 |
+
slope_deg,
|
| 113 |
+
wheel=wheel,
|
| 114 |
+
soil=soil,
|
| 115 |
+
total_mass_kg=total_mass_kg,
|
| 116 |
+
n_wheels=n_wheels,
|
| 117 |
+
gravity_m_per_s2=gravity_m_per_s2,
|
| 118 |
+
max_slip=max_slip,
|
| 119 |
+
)
|
| 120 |
+
|
| 121 |
+
if residual(0.0) <= 0.0:
|
| 122 |
+
# Cannot move on flat ground (e.g. wheel is buried); return 0.
|
| 123 |
+
return 0.0
|
| 124 |
+
|
| 125 |
+
if residual(SLOPE_SEARCH_UPPER_DEG) >= 0.0:
|
| 126 |
+
# Rover can climb at least the schema cap.
|
| 127 |
+
return SLOPE_SEARCH_UPPER_DEG
|
| 128 |
+
|
| 129 |
+
return float(brentq(residual, 0.0, SLOPE_SEARCH_UPPER_DEG, xtol=1e-3, rtol=1e-4))
|
roverdevkit/mission/configs/cadre_polar_unit.yaml
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Validation scenario — NASA JPL CADRE flotilla single unit.
|
| 2 |
+
# Not a canonical tradespace scenario; used only by
|
| 3 |
+
# roverdevkit/validation/rover_registry.py for the design-target
|
| 4 |
+
# Layer-1 surrogate sanity check (CADRE is a multi-rover technology
|
| 5 |
+
# demonstration; this scenario describes one unit operating as a
|
| 6 |
+
# member of the flotilla).
|
| 7 |
+
#
|
| 8 |
+
# Source: Rothenbuchner et al. 2023 IEEE Aerospace #2300 "Cooperative
|
| 9 |
+
# Autonomous Distributed Robotic Exploration (CADRE)"; NASA/JPL CADRE
|
| 10 |
+
# project page; CADRE flotilla press materials (2024-2025 launch and
|
| 11 |
+
# deployment window onto a Commercial Lunar Payload Services lander).
|
| 12 |
+
# Each CADRE unit is a ~2 kg 4-wheel rover designed for multi-rover
|
| 13 |
+
# coordination demonstrations and Nokia/Bell Labs LTE communications
|
| 14 |
+
# trials at the lunar south pole region. As of the registry's snapshot
|
| 15 |
+
# the rovers had launched but ground-truth surface operations data was
|
| 16 |
+
# still propagating; entry is treated as `is_flown=False` design-target
|
| 17 |
+
# until the published surface-mission report is available.
|
| 18 |
+
#
|
| 19 |
+
# `traverse_distance_m` is non-binding: CADRE's mission demonstration
|
| 20 |
+
# is choreography over short distances (tens of metres), not range.
|
| 21 |
+
# Set as a soft cap an order of magnitude above the demonstration
|
| 22 |
+
# target.
|
| 23 |
+
name: cadre_polar_unit
|
| 24 |
+
latitude_deg: -85.0
|
| 25 |
+
traverse_distance_m: 200.0
|
| 26 |
+
terrain_class: polar_regolith
|
| 27 |
+
soil_simulant: Apollo_regolith_loose
|
| 28 |
+
mission_duration_earth_days: 14.0
|
| 29 |
+
max_slope_deg: 8.0
|
| 30 |
+
sun_geometry: polar_intermittent
|
| 31 |
+
# δ_ops calibrated for a coordinated-demonstration flotilla rover:
|
| 32 |
+
# slow ground-ops cadence, frequent stop-look-rendezvous moves rather
|
| 33 |
+
# than continuous drive. Class-typical for sub-class polar micro-rovers
|
| 34 |
+
# (between Pragyan 0.008 and MoonRanger 0.20).
|
| 35 |
+
operational_duty_cycle: 0.05
|
| 36 |
+
# Schema v9: CADRE per-unit payload = stereo-camera + LTE comms-ranging
|
| 37 |
+
# experiment payload, ~0.3 kg. payload_power_w held at 0 for this
|
| 38 |
+
# validation scenario (experiment payload off during the short
|
| 39 |
+
# coordinated drive moves).
|
| 40 |
+
payload_mass_kg: 0.3
|
| 41 |
+
payload_power_w: 0.0
|
roverdevkit/mission/configs/chandrayaan3_pragyan.yaml
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Validation scenario — Chandrayaan-3 Pragyan mission (ISRO, 2023).
|
| 2 |
+
# Not a canonical tradespace scenario; used only by
|
| 3 |
+
# roverdevkit/validation/rover_registry.py for the real-rover
|
| 4 |
+
# cross-check.
|
| 5 |
+
#
|
| 6 |
+
# Source: ISRO Chandrayaan-3 press materials (landing 23 Aug 2023) and
|
| 7 |
+
# Scientific Reports (Nature) 14:24178 (2024) for landing coordinates
|
| 8 |
+
# 69.37 S, 32.35 E. Pragyan operated through a single lunar day
|
| 9 |
+
# (~14 Earth days) and failed to reawaken after lunar night. Published
|
| 10 |
+
# in-mission traverse distance ~101.4 m over ~10 active Earth days.
|
| 11 |
+
name: chandrayaan3_pragyan
|
| 12 |
+
latitude_deg: -69.4
|
| 13 |
+
traverse_distance_m: 500.0 # soft cap; published actual ~101 m
|
| 14 |
+
terrain_class: polar_regolith
|
| 15 |
+
soil_simulant: Apollo_regolith_loose
|
| 16 |
+
mission_duration_earth_days: 14.0 # one lunar day, hot case
|
| 17 |
+
max_slope_deg: 5.0 # typical-ops mean; Pragyan stayed on near-flat terrain
|
| 18 |
+
|
| 19 |
+
sun_geometry: polar_intermittent
|
| 20 |
+
# Schema v6 (v6 schema update): δ_ops calibrated to Pragyan's published
|
| 21 |
+
# 101 m / ~10 active Earth days at ~6.6 cm/s nominal v_cruise.
|
| 22 |
+
# Implied δ_ops = (101/(10*86400)) / 0.066 ≈ 0.0018; we use 0.008
|
| 23 |
+
# (the design-doc historical-conservative anchor) which sits
|
| 24 |
+
# between Pragyan's own ops (~0.002) and a more aspirational
|
| 25 |
+
# polar concept. Used only by the registry-rover validation gate.
|
| 26 |
+
operational_duty_cycle: 0.008
|
| 27 |
+
# Schema v9: Pragyan science payload = APXS + LIBS spectrometers,
|
| 28 |
+
# ~3.5 kg (Chandrayaan-3 instrument suite). payload_power_w held at 0
|
| 29 |
+
# for this validation scenario: the published traverse / peak-solar /
|
| 30 |
+
# thermal truth was measured during mobility windows with the
|
| 31 |
+
# instruments powered down, so the mobility-validation gate sees mass
|
| 32 |
+
# (always carried) but not instrument standby draw.
|
| 33 |
+
payload_mass_kg: 3.5
|
| 34 |
+
payload_power_w: 0.0
|
roverdevkit/mission/configs/change4_yutu2_per_lunar_day.yaml
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Validation scenario — Chang'e-4 Yutu-2 single-lunar-day drive window.
|
| 2 |
+
# Not a canonical tradespace scenario; used only by
|
| 3 |
+
# roverdevkit/validation/rover_registry.py for the real-rover validation real-rover
|
| 4 |
+
# cross-check.
|
| 5 |
+
#
|
| 6 |
+
# Source: Di et al. 2020, Icarus; Ding et al. 2022, Acta Astronautica;
|
| 7 |
+
# CNSA mission dispatches. Yutu-2 landed 2019-01-03 at 45.5 S on the
|
| 8 |
+
# lunar far side (Von Karman crater) and has driven ~1.6 km cumulatively
|
| 9 |
+
# over 60+ lunar days. Published per-lunar-day drive distances from
|
| 10 |
+
# the first year range ~20-30 m; we treat that as the truth number the
|
| 11 |
+
# model must reproduce for *one* lunar-day active window.
|
| 12 |
+
#
|
| 13 |
+
# Rationale for the reduced mission_duration_earth_days: Yutu-2's drive
|
| 14 |
+
# operations are concentrated into a few Earth-day activity window per
|
| 15 |
+
# lunar day (not all 14). By setting the sim window to 5 days of active
|
| 16 |
+
# ops we approximate the drive schedule without needing a proper
|
| 17 |
+
# hibernation model (that is v2 work per traverse_sim.py docstring).
|
| 18 |
+
name: change4_yutu2_per_lunar_day
|
| 19 |
+
latitude_deg: -45.5
|
| 20 |
+
traverse_distance_m: 200.0
|
| 21 |
+
terrain_class: mare_nominal
|
| 22 |
+
soil_simulant: Apollo_regolith_nominal
|
| 23 |
+
mission_duration_earth_days: 5.0
|
| 24 |
+
max_slope_deg: 5.0 # typical-ops mean across Yutu-2's Von Karman traverse
|
| 25 |
+
sun_geometry: diurnal
|
| 26 |
+
# Schema v6 (v6 schema update): δ_ops calibrated to Yutu-2's
|
| 27 |
+
# historical-conservative ops (~0.001-0.0014 implied from total
|
| 28 |
+
# ~1.6 km / 60+ lunar days). Used only by the registry-rover
|
| 29 |
+
# validation gate; see data/analytical/SCHEMA.md.
|
| 30 |
+
operational_duty_cycle: 0.001
|
| 31 |
+
# Schema v9: Yutu-2 science payload = Lunar Penetrating Radar + VNIS +
|
| 32 |
+
# APXS + panoramic/navigation cameras, ~25 kg (Chang'e-4 payload
|
| 33 |
+
# manifest). payload_power_w held at 0 for this validation scenario:
|
| 34 |
+
# the published per-lunar-day drive distance was achieved with science
|
| 35 |
+
# instruments off during the short drive windows, so the
|
| 36 |
+
# mobility-validation gate (and especially the hot-case thermal check)
|
| 37 |
+
# sees payload mass but not instrument power.
|
| 38 |
+
payload_mass_kg: 25.0
|
| 39 |
+
payload_power_w: 0.0
|
roverdevkit/mission/configs/crater_rim_micro.yaml
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Class-generic crater-rim micro-rover scenario (rediscovery library).
|
| 2 |
+
#
|
| 3 |
+
# Used only by the Layer-5 rediscovery harness
|
| 4 |
+
# (`roverdevkit.validation.rover_rediscovery`). NOT a canonical
|
| 5 |
+
# tradespace scenario (the canonical crater-rim scenario lives in
|
| 6 |
+
# `crater_rim_survey.yaml` and is returned by `list_scenarios()`);
|
| 7 |
+
# this scenario is excluded from `list_scenarios()` and exposed by
|
| 8 |
+
# `list_class_generic_micro_scenarios()` instead.
|
| 9 |
+
#
|
| 10 |
+
# Leakage controls (why this exists as a separate file)
|
| 11 |
+
# -----------------------------------------------------
|
| 12 |
+
# The canonical `crater_rim_survey.yaml` pins
|
| 13 |
+
# `operational_duty_cycle: 0.20`, calibrated against MER-A / MER-B
|
| 14 |
+
# daily averages on uneven terrain. No registry rover is operating on
|
| 15 |
+
# this scenario family today, so the leakage risk is currently latent
|
| 16 |
+
# — but the class-generic library still pins δ_ops to a flat 0.10
|
| 17 |
+
# across all four scenarios for symmetry.
|
| 18 |
+
#
|
| 19 |
+
# Everything else (latitude, traverse-distance non-binding budget,
|
| 20 |
+
# duration, max_slope, sun_geometry, terrain_class, soil_simulant)
|
| 21 |
+
# inherits from the canonical scenario because those are environmental
|
| 22 |
+
# facts the rover does not choose.
|
| 23 |
+
name: crater_rim_micro
|
| 24 |
+
latitude_deg: 0.0
|
| 25 |
+
traverse_distance_m: 25000.0
|
| 26 |
+
terrain_class: mare_nominal
|
| 27 |
+
soil_simulant: Apollo_regolith_nominal
|
| 28 |
+
mission_duration_earth_days: 5.0
|
| 29 |
+
max_slope_deg: 18.0
|
| 30 |
+
sun_geometry: diurnal
|
| 31 |
+
operational_duty_cycle: 0.10
|
| 32 |
+
# Schema v9: payload left at 0 (class-neutral). The rediscovery harness
|
| 33 |
+
# forwards each target rover's published payload as a per-call override
|
| 34 |
+
# to both the rover re-evaluation and every NSGA-II candidate, so the
|
| 35 |
+
# scenario itself carries no per-rover payload label (same leakage
|
| 36 |
+
# logic as the flat δ_ops anchor above).
|
| 37 |
+
payload_mass_kg: 0.0
|
| 38 |
+
payload_power_w: 0.0
|
roverdevkit/mission/configs/crater_rim_survey.yaml
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Scenario 4 — crater rim survey, short traverse, lots of slope changes.
|
| 2 |
+
# Canonical crater-rim traverse scenario; optimizer objective is energy-optimal traverse.
|
| 3 |
+
#
|
| 4 |
+
# `traverse_distance_m` is non-binding (see equatorial_mare_traverse).
|
| 5 |
+
# 25 km is just below the 5-day theoretical reach at max speed / duty
|
| 6 |
+
# (~25.9 km); this keeps range energy-/duty-bound across the LHS sweep.
|
| 7 |
+
name: crater_rim_survey
|
| 8 |
+
latitude_deg: 0.0
|
| 9 |
+
traverse_distance_m: 25000.0
|
| 10 |
+
terrain_class: mare_nominal
|
| 11 |
+
soil_simulant: Apollo_regolith_nominal
|
| 12 |
+
mission_duration_earth_days: 5.0
|
| 13 |
+
max_slope_deg: 18.0
|
| 14 |
+
sun_geometry: diurnal
|
| 15 |
+
# Schema v6 (v6 schema update): per-scenario default ops duty cycle.
|
| 16 |
+
# Anchored on MER-A/B daily averages (~0.15-0.20 in typical drive
|
| 17 |
+
# sols on uneven terrain). See data/analytical/SCHEMA.md.
|
| 18 |
+
operational_duty_cycle: 0.20
|
| 19 |
+
# Schema v9: scientific-payload mission requirement. Crater-rim survey
|
| 20 |
+
# carries a mid-weight imaging + spectroscopy suite (~4 kg / 5 W).
|
| 21 |
+
# Surrogate LHS samples payload independently; webapp / evaluator
|
| 22 |
+
# default.
|
| 23 |
+
payload_mass_kg: 4.0
|
| 24 |
+
payload_power_w: 5.0
|
roverdevkit/mission/configs/equatorial_mare_traverse.yaml
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Scenario 1 — Apollo-17-like equatorial mare traverse, 14-day mission.
|
| 2 |
+
# Canonical Apollo-17-like equatorial mare traverse scenario.
|
| 3 |
+
#
|
| 4 |
+
# `traverse_distance_m` is set as a *non-binding budget* rather than a
|
| 5 |
+
# short mission assignment: 80 km is just above the 14-day theoretical
|
| 6 |
+
# reach at max speed (0.10 m/s) and max duty cycle (0.6), i.e.
|
| 7 |
+
# 14 * 86400 * 0.10 * 0.6 ≈ 72.6 km. This ensures the surrogate-training LHS sweep
|
| 8 |
+
# sees a range signal that responds to speed/duty/energy instead of
|
| 9 |
+
# saturating at an assigned distance cap.
|
| 10 |
+
name: equatorial_mare_traverse
|
| 11 |
+
latitude_deg: 20.2 # Apollo 17 landing-site latitude
|
| 12 |
+
traverse_distance_m: 80000.0
|
| 13 |
+
terrain_class: mare_nominal
|
| 14 |
+
soil_simulant: Apollo_regolith_nominal
|
| 15 |
+
mission_duration_earth_days: 14.0
|
| 16 |
+
max_slope_deg: 15.0
|
| 17 |
+
sun_geometry: diurnal
|
| 18 |
+
# Schema v6 (v6 schema update): per-scenario default ops duty cycle.
|
| 19 |
+
# Calibrated against Apollo-17 LRV (~0.5 manned EVA duty) and
|
| 20 |
+
# unmanned long-traverse references (~0.30). See
|
| 21 |
+
# the per-scenario calibration rationale.
|
| 22 |
+
operational_duty_cycle: 0.30
|
| 23 |
+
# Schema v9: scientific-payload mission requirement. Class-typical
|
| 24 |
+
# default for a mare science traverse (cameras + spectrometer +
|
| 25 |
+
# sample tools, ~5 kg / 5 W); the webapp Mission-Inputs panel and the
|
| 26 |
+
# evaluator use this unless the caller passes an override, and the
|
| 27 |
+
# surrogate LHS samples payload independently of this default.
|
| 28 |
+
payload_mass_kg: 5.0
|
| 29 |
+
payload_power_w: 5.0
|
roverdevkit/mission/configs/highland_micro.yaml
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Class-generic highland micro-rover scenario (rediscovery library).
|
| 2 |
+
#
|
| 3 |
+
# Used only by the Layer-5 rediscovery harness
|
| 4 |
+
# (`roverdevkit.validation.rover_rediscovery`). NOT a canonical
|
| 5 |
+
# tradespace scenario (the canonical highland scenario lives in
|
| 6 |
+
# `highland_slope_capability.yaml` and is returned by
|
| 7 |
+
# `list_scenarios()`); this scenario is excluded from
|
| 8 |
+
# `list_scenarios()` and exposed by
|
| 9 |
+
# `list_class_generic_micro_scenarios()` instead.
|
| 10 |
+
#
|
| 11 |
+
# Leakage controls (why this exists as a separate file)
|
| 12 |
+
# -----------------------------------------------------
|
| 13 |
+
# The canonical `highland_slope_capability.yaml` pins
|
| 14 |
+
# `operational_duty_cycle: 0.15`, calibrated against "slope-focused
|
| 15 |
+
# missions are systematically slower than nominal terrain missions".
|
| 16 |
+
# No registry rover is operating on highland_dense terrain today, so
|
| 17 |
+
# the leakage risk is currently latent rather than active — but the
|
| 18 |
+
# class-generic library still pins δ_ops to a flat 0.10 across all
|
| 19 |
+
# four scenarios so the leakage-control story is symmetric rather than
|
| 20 |
+
# "we pinned the three scenarios where we have flown rovers but kept
|
| 21 |
+
# the fourth at its calibrated value."
|
| 22 |
+
#
|
| 23 |
+
# Everything else (latitude, traverse-distance non-binding budget,
|
| 24 |
+
# duration, max_slope, sun_geometry, terrain_class, soil_simulant)
|
| 25 |
+
# inherits from the canonical scenario because those are environmental
|
| 26 |
+
# facts the rover does not choose.
|
| 27 |
+
name: highland_micro
|
| 28 |
+
latitude_deg: 10.0
|
| 29 |
+
traverse_distance_m: 20000.0
|
| 30 |
+
terrain_class: highland_dense
|
| 31 |
+
soil_simulant: Apollo_regolith_loose
|
| 32 |
+
mission_duration_earth_days: 7.0
|
| 33 |
+
max_slope_deg: 25.0
|
| 34 |
+
sun_geometry: diurnal
|
| 35 |
+
operational_duty_cycle: 0.10
|
| 36 |
+
# Schema v9: payload left at 0 (class-neutral). The rediscovery harness
|
| 37 |
+
# forwards each target rover's published payload as a per-call override
|
| 38 |
+
# to both the rover re-evaluation and every NSGA-II candidate, so the
|
| 39 |
+
# scenario itself carries no per-rover payload label (same leakage
|
| 40 |
+
# logic as the flat δ_ops anchor above).
|
| 41 |
+
payload_mass_kg: 0.0
|
| 42 |
+
payload_power_w: 0.0
|
roverdevkit/mission/configs/highland_slope_capability.yaml
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Scenario 3 — highland slope capability on loose regolith.
|
| 2 |
+
# Canonical highland slope-capability scenario; optimizer objective is usually min-mass
|
| 3 |
+
# subject to slope-capability ≥ max_slope_deg.
|
| 4 |
+
#
|
| 5 |
+
# On this scenario the slope-capability constraint dominates: the focus
|
| 6 |
+
# is whether a design can climb a sustained loose-regolith grade at all,
|
| 7 |
+
# not how far it travels. Treat ``range_km`` here as a feasibility floor
|
| 8 |
+
# (range > 0 means the rover can move) rather than an optimization objective.
|
| 9 |
+
#
|
| 10 |
+
# Target slope is 15°. This sits inside the validated Bekker-Wong slope
|
| 11 |
+
# envelope on loose Apollo regolith (the strongest micro-rover design in
|
| 12 |
+
# the search space tops out at ~19.6° under the analytical kernel), so the
|
| 13 |
+
# scenario yields a meaningful Pareto front of designs that genuinely clear
|
| 14 |
+
# the grade. A steeper 25° target is infeasible for every design in the
|
| 15 |
+
# space under pure BW physics (loose regolith friction limit), and would
|
| 16 |
+
# produce an empty front.
|
| 17 |
+
name: highland_slope_capability
|
| 18 |
+
latitude_deg: 10.0
|
| 19 |
+
traverse_distance_m: 20000.0
|
| 20 |
+
terrain_class: highland_dense
|
| 21 |
+
soil_simulant: Apollo_regolith_loose # worst-case soil for slope climbing
|
| 22 |
+
mission_duration_earth_days: 7.0
|
| 23 |
+
max_slope_deg: 15.0
|
| 24 |
+
sun_geometry: diurnal
|
| 25 |
+
# Schema v6 (v6 schema update): per-scenario default ops duty cycle.
|
| 26 |
+
# Slope-focused missions are systematically slower than nominal
|
| 27 |
+
# terrain missions (more careful traverse planning). See
|
| 28 |
+
# the per-scenario calibration rationale.
|
| 29 |
+
operational_duty_cycle: 0.15
|
| 30 |
+
# Schema v9: scientific-payload mission requirement. A slope-capability
|
| 31 |
+
# demonstrator carries a lighter payload (stereo cameras + IMU science,
|
| 32 |
+
# ~3 kg / 3 W) so mobility margin goes to the climb. Surrogate LHS
|
| 33 |
+
# samples payload independently; this is the webapp / evaluator default.
|
| 34 |
+
payload_mass_kg: 3.0
|
| 35 |
+
payload_power_w: 3.0
|