feat(actuators): Magic_Actuator class + planner integration - #80
Open
patrickmckeen wants to merge 1 commit into
Open
feat(actuators): Magic_Actuator class + planner integration#80patrickmckeen wants to merge 1 commit into
patrickmckeen wants to merge 1 commit into
Conversation
Adds the Magic actuator type back to Generalized_ADCS as a Python
``Magic_Actuator`` class wired through ``build_csat.py`` to the
existing C++ ``Satellite::add_magic`` API in OldPlanner.
The OldPlanner has had full magic-actuator support since forever
(``pysat.add_magic(axis, max_torq, cost)``, the ``magic_torq`` field
in ``Satellite::dist_torque``, the magic-cost block in ``stepcost``),
but Generalized_ADCS had no Python actuator class to drive it. The
companion regression in SALTRO -- where ``magic_control_weight`` is
a leftover cost setting but the actuator class itself was lost in
the OldPlanner -> SALTRO port -- is tracked separately and not
addressed by this PR.
Files changed
-------------
* ``ADCS/satellite_hardware/actuators/magic.py`` -- new
``Magic_Actuator(axis, max_torque, bias=None, noise=None,
estimate_bias=False)`` class. Direct body torque
``tau = a * (u + b) + n`` with no state or environment dependence.
Overrides ``torque``, ``dtorq__du``, ``dtorq__dbias``,
``jacobians``, ``hessians`` for the obvious-nonzero terms.
All other derivatives use the base Actuator zero defaults
(state-independent, momentum-storage-free torque -> almost
everything is zero).
* ``ADCS/satellite_hardware/actuators/__init__.py`` -- export
``Magic_Actuator``.
* ``ADCS/controller/plan_and_track/build_csat.py``:
- Updated ``get_cpp_to_python_control_permutation`` to handle the
third actuator class. C++ control vector ordering is
``[MTQs, RWs, magics]`` (from ``Satellite::stepcost``); the
permutation logic now mirrors that.
- Updated ``add_actuator`` to dispatch ``Magic_Actuator`` to
``csat.add_magic(axis, max_torq*scale, magic_control_weight)``.
- Fixed a small existing bug along the way: the error message in
the fallback ``raise ValueError`` used ``act.__name__`` (instances
don't have ``__name__``) instead of ``type(act).__name__``.
Tests
-----
* ``testing/test_actuators/test_actuator_magic.py`` -- 19 unit tests:
``torque`` returns ``a * u`` exactly (parametrized over axes);
independent of attitude and rate; bias adds to command; saturation
warning; ``storage_torque`` empty; ``dtorq__du = axis``;
``dtorq__dbasestate`` and ``dtorq__dh`` are zero; all 2nd
derivatives zero; ``jacobians()``/``hessians()`` API bundling
matches MTQ/RW; FD check on ``dtorq__du``.
* ``testing/test_controllers/test_planner_magic_actuator.py`` -- 5
integration tests:
- ``test_magic_actuator_routes_through_build_csat`` -- planner
plans successfully with 3 magic actuators + 3 dummy MTQs, control
rows in expected positions, MTQs unused (huge weight) and the
y-axis magic carries the slew.
- ``test_magic_actuator_eigenaxis_lqr_matches_riccati`` --
parametrized over omega0 in {0.005, 0.01, 0.02}: with a SINGLE
y-axis magic actuator plus 3 dummy MTQs, q0=identity, single-y
omega perturbation, cost-weight conversion (8x, 2x, 2x), the
planner provably reduces to the 2-state LQR Riccati to ~1e-5
absolute. Cleaner than the RW-based version of this test in
``test_planner_riccati_closed_form.py`` -- no ``J_eff = J - J_RW``
parallel-axis correction needed; the magic actuator applies
torque directly.
- ``test_magic_actuator_actuator_ordering`` -- mixed MTQ/RW/magic
fleet (interleaved in Python list) produces the expected control
layout after reordering back to Python ordering. Confirms the
permutation logic handles all three actuator types.
Local results
-------------
* ``test_actuator_magic.py``: 19 passed.
* ``test_planner_magic_actuator.py``: 5 passed.
* Fast subset of ``test_actuators/`` + ``test_controllers/``: 63
passed, no new failures.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds the Magic actuator type to Generalized_ADCS. The OldPlanner C++ (`pysat.Satellite::add_magic`) has always supported magic actuators; only the Python class was missing.
What
Tests
Local: 24 passed, 0 new failures in the controller + actuator suites.
Why this matters
The Magic actuator gives a clean body-torque commander with no MTQ rank-deficiency, no RW Newton-3rd back-reaction, and no wheel-momentum state. The closed-form Riccati test in this PR uses `J` directly (no `J_eff = J − J_RW` parallel-axis correction), giving a strictly simpler equivalence proof than the RW-based version in `test_planner_riccati_closed_form.py`.
The user's "magic actuators for test robustness" suggestion delivered. Future tests can now use this as the default direct-body-torque commander.
Related
🤖 Generated with Claude Code