Doctoral Research · Space Robotics Inspection with a Free-Flying Space Manipulator
A Doctoral Research Journal Aerospace Engineering

Breve Controller — Plan to Nail Down the Base Spacecraft

Generated 2026-06-06 (TASK_2). Report only — no code modified.

Inspected: GNC/BreveController.py, GNC/base_guidance.py, and (for context only) GNC/com_controller.py, GNC/guidance/ee_guidance.py, GNC/guidance/guidance_classes.py, analysis/runner.py.

Plan in brief

Findings — the architecture

Two parallel inheritance chains, each with COM at the root:

Layer Controller Guidance
COM (proven) CC_Controller (com_controller.py:22) COMGuidance
+ base attitude BaseController (BreveController.py:31) BaseGuidance (base_guidance.py:36)
+ end effector EEController (BreveController.py:224) EEGuidance (GuidanceMode state machine + target_finder)

The repetition to streamline

Duplicated method Base copy EE copy What actually differs in the EE copy
RHS34b :120 :449 arm_scale on the arm D/K block + an accel feedforward term
x_tilde :84 :296 folds real x_e_tilde + integral + axis-only projection
v_breve_damping_error :97 :327 real EE twist error + roll-damping scale
reconstruct_generalized_velocity com_controller.py:207 BreveController.py:206 Tikhonov-regularized solve vs plain lstsq
integral update _update_com_integral_state com_controller.py:272 update_x_e_integral BreveController.py:263 same gate/leak/clamp shape, different names

The RHS34b block is the big one: ~30 lines of C_breve/D_breve/K_breve/ COM_coupling/posture assembly is copied verbatim, and only the two EE additions are genuinely new.

Answers to the task’s questions

Step-1 — “Can you stop here, or do you need the rest of the guidance?” Stop here. Nothing in the base path reaches into ee_guidance.py; the EE error terms are already zeroed cleanly. The base is the easy win and is largely implemented — it just needs the streamline + a validation pin, exactly as COM did.

Step-2 — “How much EE guidance should we pull in?” Recommend Option 1 (minimum): take GuidanceMode.INITIAL as the EE baseline. It is deterministic and low-error, needs no target_finder, and exercises the full EE control path so we can trust it before adding the targeting state machine. Option 2 (keep separate) perpetuates the duplication you want gone; Option 3 (import everything) risks overload before the base is nailed. Defer this to the EE phase — Task 4 below.

Recommendation — streamline via hooks (behavior-preserving)

Hoist the shared skeleton up to BaseController and reduce EEController to small overrides. Two new hooks default to no-ops in the base, so the base path is unchanged:

# BaseController — defaults make the base path identical to today.
def arm_gain_scale(self):          # EE overrides with gamma conditioning
    return 1.0
def rhs_feedforward(self, st, des):  # EE overrides with accel feedforward
    return 0.0

def RHS34b(self, st, des):
    # ... single copy of the C_breve / D_breve / K_breve / COM_coupling / posture
    # assembly, using self.arm_gain_scale() on the arm D/K block ...
    return sanitize_column(rhs_total) + self.rhs_feedforward(st, des)

x_tilde / v_breve_damping_error already dispatch through x_e_tilde / J_xe (zero in the base), so a single parent version works once the EE-specific axis-only projection and integral folding move into x_e_tilde — its natural owner. Keep reconstruct_generalized_velocity’s regularized override as-is (it is a real behavior difference, gated by config), but drop the duplicated logic from the EE layer where it only re-derives the base version.

This respects the COM-first layering, removes the copy-paste, and stays behavior-preserving — each step validated by a saved-log A/B.


Tasks

TASK_A — Streamline RHS34b via no-op hooks

Goal. Remove the duplicated RHS34b. One copy lives in BaseController, parameterized by arm_gain_scale() and rhs_feedforward() hooks that default to 1.0 / 0.0 so the base path is byte-identical and EEController overrides only the two hooks.

Modify - GNC/BreveController.py — add the two hooks to BaseController; collapse EEController.RHS34b (:449) into the parent.

Validation - Saved-log A/B: run the base and arm configs before and after; assert the logged forces/state match to numerical tolerance (one script, identical config, monkeypatch only RHS34b). py_compile the module.

TASK_B — Converge x_tilde and v_breve_damping_error

Goal. Collapse the duplicated error/damping assembly to single parent methods driven by the x_e_tilde / J_xe hooks. Move the EE-only axis-only projection and integral folding into x_e_tilde so the parent assembly is task-agnostic.

Modify - GNC/BreveController.pyx_tilde (:84 / :296), v_breve_damping_error (:97 / :327), and EEController.x_e_tilde.

Validation - Same saved-log A/B for base + arm; confirm x_tilde / damping vectors match pre-refactor. py_compile.

TASK_C — Pin a base-controller validation baseline

Goal. Give the streamline tasks a fixed yardstick: a short, saved reference log for the base controller at a matched config, plus a tiny A/B comparator, so behavior-preservation is checkable in seconds without a full sim.

Modify - A focused validation script under the existing test/validation location (reuse the COM A/B pattern; do not add a new framework).

Validation - The comparator reports max abs diff ≈ 0 against the pinned baseline on an unmodified tree.

TASK_D — (Next phase) EE Phase-1 with GuidanceMode.INITIAL only

Goal. After the base is solid, exercise the EE control path end-to-end using only GuidanceMode.INITIAL — the deterministic, low-error baseline pose — with the target_finder / targeting state machine left out.

Modify - GNC/guidance/ee_guidance.py / GNC/BreveController.py — wire EEController to drive only the INITIAL pose path; gate the targeting machinery off.

Validation - Short smoke: EE pose tracks the INITIAL baseline with low error and bounded control effort over a few seconds of sim.