GNC/guidance/ee_guidance.py — EE camera guidance

EEGuidance turns a scheduled surface aim point xsurf(s)\bm x_\text{surf}(s) into the desired EE pose (ped,zed)(\bm p_{ed},\bm z_{ed}) and twist νed\bm\nu_{ed} fed to the controller each step.

Standoff construction (§5.4)

Standoff vector from CoM to surface aim point:

r=xsurfpc,ρ=r,r^=r/ρ\bm r = \bm x_\text{surf} - \bm p_c, \quad \rho = \lVert\bm r\rVert, \quad \hat{\bm r} = \bm r/\rho

Desired EE position and look axis (eq. 5.7):

ped=pc+rcamr^,zed=r^\bm p_{ed} = \bm p_c + r_\text{cam}\hat{\bm r}, \qquad \bm z_{ed} = \hat{\bm r}

Key simplification: the whole desired pose is governed by the single direction r^\hat{\bm r}; the look axis equals the aim direction.

POSE vs ANCHOR

Two OrbitPathMode values route the pose builder.

POSE (adopted default):

ped,zedpath(saim)(eq. 5.7, deterministic)\bm p_{ed},\bm z_{ed} \leftarrow \text{path}(s_\text{aim}) \quad \text{(eq. 5.7, deterministic)}

Scorer never runs. Complete, scheduled coverage guaranteed.

ANCHOR (retained for risk-aware planning):

ped,zedEETargetFinder.choose_goal(panchor)\bm p_{ed},\bm z_{ed} \leftarrow \text{EETargetFinder.choose\_goal}(\bm p_\text{anchor})

Vantage panchor\bm p_\text{anchor} is a CoM-orbit point, not xsurf\bm x_\text{surf} (placing cameras at the surface would put them inside the target). Scoring: Ω=iσiwi\Omega = \prod_i \sigma_i^{w_i} (eq. 7.2); inert in POSE.

State machine: INITIAL → HOLD

The GuidanceMode IntEnum governs four states; the machine runs once per step in set_ee_target.

INITIAL (startup latch, steps 0nsu0\ldots n_\text{su}):

pe=pc+Rbpce(0),Re=RbRbe(0)\bm p_e = \bm p_c + \bm R_b \bm p_{ce}^{(0)}, \quad \bm R_e = \bm R_b \bm R_{be}^{(0)}

pce(0),Rbe(0)\bm p_{ce}^{(0)},\bm R_{be}^{(0)} latched on the first call; cleared only by reset_runtime_state. If aim_during_initial, steps 1nsu1\ldots n_\text{su} already run orbit_path_pose under the INITIAL tag.

After initial_phase_complete (stepnsu\text{step} \geq n_\text{su}), POSE mode short-circuits to orbit_path_pose every step; HOLD/TARGETING/FALLBACK are inert in the adopted default.

TARGETING is a one-step reselect event (ANCHOR only): accept_target margin gate → latch new pce\bm p_{ce} → immediately re-enters HOLD. The EE tracks in HOLD, not TARGETING.

The aim clock

The arclength argument saims_\text{aim} passed to orbit_path.eval:

saim={sΔλprogress-synced (closed-loop, default)kΔtvdesopen-loop (step k)s_\text{aim} = \begin{cases} s \cdot \Delta\lambda & \text{progress-synced (closed-loop, default)} \\ k \Delta t v_\text{des} & \text{open-loop (step } k\text{)} \end{cases}

ss = achieved progress index along the discrete helix Λ\Lambda; Δλ\Delta\lambda = arc sample spacing; vdesv_\text{des} = constant cruise speed.

Closed-loop: aim driven by where the vehicle actually is, not wall time. Optional knobs (default OFF, bit-identical): floor rate s˙floor\dot s_\text{floor} breaks the dwell fixed point; lead cap sleads_\text{lead} bounds open-loop excursion.

Analytic desired twist (§5.5 → §5.6)

Unit-vector rate (tangential projection):

r^˙=1ρ(Ir^r^)r˙,r˙=x˙surfvcd\dot{\hat{\bm r}} = \frac{1}{\rho}\bigl(\mathbb{I} - \hat{\bm r}\hat{\bm r}^\top\bigr)\dot{\bm r}, \qquad \dot{\bm r} = \dot{\bm x}_\text{surf} - \bm v_{cd}

World-frame desired twist (eq. 5.14):

νed=[vcd+rcamr^˙r^×r^˙]\bm\nu_{ed} = \begin{bmatrix}\bm v_{cd} + r_\text{cam}\dot{\hat{\bm r}} \\ \hat{\bm r}\times\dot{\hat{\bm r}}\end{bmatrix}

Angular velocity is roll-free by construction: r^ωed=0\hat{\bm r}\cdot\bm\omega_{ed}=0.

Converted to circumcentroidal ()(\oplus) frame (eq. 5.17):

νed=νedlocalGvcvcd+Gωbωbd\bm\nu_{ed}^\oplus = \bm\nu_{ed}^\text{local} - \bm G_{v_c}\bm v_{cd} + \bm G_{\omega_b}\bm\omega_{bd}

Analytic feedforward valid in POSE only; ANCHOR falls back to finite-difference.

Finalization and handoff to controller

Every raw pose passes through consistent_finalize_pose before reaching Desired:

raw ped,zedpathfilter(1α)pe,prev+αpe,rawlow-pass + rate clamppcerreachDesired.(pe,Re,ze,νe)\underbrace{\text{raw } \bm p_{ed},\bm z_{ed}}_{\text{path}} \xrightarrow{\text{filter}} \underbrace{(1-\alpha)\bm p_{e,\text{prev}} + \alpha \bm p_{e,\text{raw}}}_{\text{low-pass + rate clamp}} \xrightarrow{\lVert\bm p_{ce}\rVert \leq r_\text{reach}} \text{Desired}.(\bm p_e,\bm R_e,\bm z_e,\bm\nu_e)

The controller reads des.nu_e directly. In POSE mode, analytic_desired_nu_e_feedforward writes the closed-form νedlocal\bm\nu_{ed}^\text{local} (and νed,ν˙ed\bm\nu_{ed}^\oplus,\dot{\bm\nu}_{ed}^\oplus for the acceleration FF into the RHS, eq. 5.18). The EE twist may additionally be scaled by the conditioning ramp γ(σG)\gamma(\sigma_G) via derate_twist_by_gamma.