orbit_com_path — the CoM inspection orbit: build it, track it, roll it forward
Concept
The system’s center of mass follows a pole-to-pole spherical helix that sweeps the target for full
coverage. Three files own three stages of that one trajectory — build the geometry (utils/orbit.py),
track it per step as a desired(p_c, v_c, a_c)(GNC/com_guidance.py), and roll it forward
dynamics-free as a coverage oracle (guidance_rollout.py). No single file holds the whole pipeline, so
the per-file pages document each stage and this documents how they chain.
What this spans
utils/orbit.py(orbit) — generates the spherical helix and exposes it two ways: an analytic
closed-form sampler and a uniform-arclength polyline with tangents + curvature.GNC/com_guidance.py(com_guidance) — turns the static orbit into a live per-step CoM reference:
monotonic progress tracking, a lookahead velocity field, a startup speed ramp, optional smoothing.GNC/guidance/guidance_rollout.py(guidance_rollout) — walks the same orbit on a pure arclength
clock without the controller/dynamics, to study selection + coverage in isolation.- Thesis: the inspection orbit is a single geometric object (a spherical helix) that is generated once,
tracked dynamically by the controller path, and swept open-loop by the oracle path — and the second
and third readers must agree on the first’s geometry (curvature, tangents, arclength) or the coverage
number is meaningless.
Constituent pages
Down-links to the per-file pages that hold each part’s contract:
- orbit —
OrbitGenerator: builds the helix, the uniform-arclength polyline, and the analytic sampler;
the sole owner of the geometry. - com_guidance —
COMGuidance: the root of the guidance tower; the live per-step CoM desired with
progress tracking + startup ramp. - guidance_rollout —
build_guidance_only_rollout: the dynamics-free coverage/selection oracle that
walks the orbit on an arclength clock.
Mechanism (where it lives in code)
The end-to-end path from helix coefficients to a tracked (or swept) CoM trajectory:
Stage 1 — build the geometry (once, cached):
- Sample the analytic spherical helix on the time grid; cache its
(R, θ_rate, φ0, φ_rate)coefficients —utils/orbit.py:75 - Re-interpolate to
Npoints uniform in arclength (the addressable polyline) —utils/orbit.py:97 - Build the cached path + the progress→
uparameter table for analytic sampling —utils/orbit.py:27 - Polyline tangents + Frenet curvature vectors (open-path differences, run at construction) —
utils/orbit.py:150 - Analytic curvature magnitude at axial coordinate
s(closed form, no FD ripple) —utils/orbit.py:119 - Runtime closed-form sampler: position / unit-tangent / curvature vector at a continuous progress index —
utils/orbit.py:46
Stage 2 — track it per step (the live controller path):
- Startup speed ramp
exp_scale(t), gated oncontroller.com.startup.enable—GNC/com_guidance.py:39 - Live per-step entry: ramped speed → desired → optional smoothing —
GNC/com_guidance.py:89 - The velocity-field core: monotonic progress (local window) → lookahead tangent →
(p_c, v_c, a_c)—GNC/com_guidance.py:170 - t=0 seed that caches
a_c(0)for the analytic-feedforward warm-start —GNC/com_guidance.py:111 - Curvature-limited arc-time oracle (
v ≤ √(a_ff_max/κ)) used to size run duration —GNC/com_guidance.py:134
Stage 3 — roll it forward dynamics-free (the oracle path):
- The loop: resolve windows → arclength clock
s = t·desired_speed→ sample CoM/base/EE goal → mark FOV every stride → log —GNC/guidance/guidance_rollout.py:90 - Sample the nominal CoM from arclength progress:
v_c = speed·t̂,a_c = speed²·κ—GNC/guidance/guidance_rollout.py:45 - Fake plant: desired base motion carrying the previous desired EE command (no dynamics solve) —
GNC/guidance/guidance_rollout.py:60 - FOV-marking stride (cost lever, not resolution) —
GNC/guidance/guidance_rollout.py:13
Evidence
Purely structural — the chain is established by who owns the geometry and who reads it, not by a single
run. OrbitGenerator is instantiated by exactly one owner, COMGuidance (GNC/com_guidance.py:34), and
the rollout reads the same cached fields (traj.orbit.path / .tangents / .curvature,
GNC/guidance/guidance_rollout.py:45) so both stages sweep one geometry. The two readers do agree on the
acceleration form (a_c = ‖v‖²·κ): the centripetal feedforward in desired_at_window
(GNC/com_guidance.py:170) and a_c = speed²·κ in _sample_nominal_com (GNC/guidance/guidance_rollout.py:45)
are the same §5.3 term. The durable rationale (closed-form vs polyline curvature, open-path differences,
coverage-as-traversal) is recorded in utils/INSIGHTS.md (orbit [math]) and GNC/INSIGHTS.md
([guidance], guidance_rollout, Coverage marking); numbers live in the dated CLAIMS.md, never here.
Footguns
Two curvature paths, two sample shapes — never cross-wire them
orbit exposes curvature twice:
sample_analytic(utils/orbit.py:46) returns the closed-form
curvature vector to dodge the polyline finite-difference ripple at segment crossings, while the cached
self.curvaturefield (utils/orbit.py:150) is the polyline Frenet form for windowed lookups. The live
tracker reaches for the analytic sampler; the rollout reads the cached polyline window. Feeding one where
the other is expected silently changes the reference signal. (utils/INSIGHTS.mdorbit [math])
Progress is strictly monotonic — the tracker never walks backward
COMGuidancetakesprogress = max(progress_prev, progress_geom)and re-centers a cheap local search
window each step (GNC/com_guidance.py:170); the full-orbit polyline scan runs once per run. The rollout
path has no such tracker — it drives an open-loop arclength clocks = t·desired_speed
(GNC/guidance/guidance_rollout.py:90). Don’t expect the rollout’s progress to behave like the tracker’s;
they sweep the same curve but advance it differently. (GNC/INSIGHTS.md[guidance])
The rollout's motion state is faked — it is NOT a controller validation
guidance_fake_motion_state(GNC/guidance/guidance_rollout.py:60) copies the previous desired EE command
instead of integrating dynamics. This path measures coverage and selection along the orbit, and says
nothing about tracking error, the derate stack, or whether the controller can actually fly the helix.
Coverage here is a traversal artifact: a short run only sweeps the southern cap (~0.47); the full
helix reaches 1.0, so size runs by progress-to-completion, not step count.
(GNC/INSIGHTS.mdguidance_rollout, Coverage marking)
Open path — never wrap terminal onto initial
The helix is an open pole-to-pole curve. Tangents and
dt/dsuse one-sided differences at the endpoints
(utils/orbit.py:150); wrapping the last sample onto the first would inject a spurious jump into both the
reference and the curvature. (utils/INSIGHTS.mdorbit [math])
Equations & references
Key equations mirrored from current_sota — the math source of truth; see there for derivations.
Spherical-helix position (analytic, , ) — §5.2, eq (5.4):
Startup speed ramp — §5.1, eq (5.1):
Centripetal acceleration feedforward — §5.2, eq (5.3):
References:
- CoM orbit reference + startup ramp (§5.1, eq 5.1): current_sota > 5 ·
GNC/com_guidance.py:89. - Analytic spherical-helix tangent & curvature (§5.2): current_sota > 5 ·
utils/orbit.py:46. - Centripetal acceleration feedforward
a_cd = ‖v_cd‖²κ(§5.2–5.3): current_sota > 5 ·GNC/com_guidance.py:170/GNC/guidance/guidance_rollout.py:45. - eq↔code cross-check:
generated_reports/GNC/cross_check.md.
Related
com_vs_base · target_finding_coverage · guidance_modes · orbit · com_guidance · guidance_rollout · base_guidance · ee_guidance · terminology