Skip to content

fix(actuators): read an actuator's phase from the position it is put in - #63

Merged
HugoFara merged 1 commit into
mainfrom
fix/actuator-phase-from-position
Sep 18, 2026
Merged

HugoFara merged 1 commit into
mainfrom
fix/actuator-phase-from-position

Conversation

@HugoFara

Copy link
Copy Markdown
Owner

The pre-existing limitation noted in the review of #62.

set_coords() put the joints back but not the actuators: an ArcCrank kept _angle/_direction, a LinearActuator _extension/_direction, a Mechanism DriverLink/ArcDriverLink its current_angle. Only a Crank re-derives its angle from x/y. So in an optimizer, a sensitivity analysis or a simulation() context, each run started with the actuator wherever the previous one had stopped it — two evaluations of the same candidate returned different loci.

Fix: coordinates are now the whole state, for every actuator, as they already were for Crank.

  • ArcCrank.set_coord() reads the angle from the anchor→position direction, brought onto the arc (an arc like [0, 3π/2] reaches past atan2's range; off the arc, the nearer end), and restarts the sweep forward as a new crank does. reload() writes x/y directly, so stepping is unaffected.
  • LinearActuator.set_coord() reads the extension as the projection onto its axis, held within [0, stroke], and restarts extending.
  • DriverLink.read_angle() / ArcDriverLink.read_angle() do the same from the motor and output joints; Mechanism.set_joint_positions() — hence set_coords(), rebuild(), set_completely() — calls them.

Tests: repeated kinematic_default_test evaluations give identical loci for Crank, ArcCrank and LinearActuator (and equal a fresh linkage's run); Mechanism runs repeat after set_coords; unit tests for each set_coord/read_angle including the arc wrap-around and the unplaced-joint case. 11 of them fail on main. Full suite (2650) and docs tests green. Also drops the now-stale "cannot be rewound" wording from #62's comment and changelog.

__all__ untouched: read_angle is a method, the arc helper is private in geometry.core.

An ArcCrank kept its angle and sweep direction, a LinearActuator its
extension and direction, and a Mechanism DriverLink / ArcDriverLink its
current_angle, as internal state that set_coords() left alone; only a
Crank reads its angle from its coordinates. So the second evaluation of
a candidate by kinematic_minimization, a sensitivity analysis or a
simulation() context started with the actuator wherever the previous
run had stopped it, and two evaluations of the same candidate returned
different loci.

ArcCrank.set_coord() and LinearActuator.set_coord() now read the angle
or the extension from the position they are given — the angle brought
onto the arc, since an arc can reach past the range of atan2, the
extension held within the stroke — and restart the sweep in the
positive direction, as a new actuator does. reload() writes x and y
directly, so it is unaffected. Mechanism.set_joint_positions(), hence
set_coords(), rebuild() and set_completely(), calls the new
DriverLink.read_angle() / ArcDriverLink.read_angle(), which do the same
from the motor and output joints.

Noted in the review of #62.
@HugoFara
HugoFara merged commit bfaec1f into main Sep 18, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant