Skip to content

Validate motion arguments on the client before sending #12

Description

@X-F-R

Context

The motion entry points pack their arguments and send them without validating what the caller passed. Three gaps, all on the client side of the wire, all in src/litearm/arm.py:

  1. No non-finite check anywhere. grep -rn 'isfinite\|isnan\|isinf' src/litearm/ returns nothing. movej validates len(q) and speed but not the values (src/litearm/arm.py:1926).
  2. No soft-limit check. Nothing compares a target against the firmware's q_min/q_max, even though params().all_joint_params() exposes exactly those values (src/litearm/params.py:28).
  3. Speed is validated on two of six entry points. movej (src/litearm/arm.py:1930) and movej_sync (:1959) both raise InvalidCommandError("speed 需 0..1"). move_p (:1968), move_l (:1792), move_c (:1816) and move_path (:1832) do not check it at all.

Expected

An argument the client can see is unusable should be refused locally, naming the argument, before a frame goes out. That is what movej already does for speed, and what set_joint_limits does for q_min < q_max (src/litearm/params.py:74).

Actual

Each gap has its own consequence.

Non-finite targets. The value is packed as f32 and sent. The arrival check then compares the arm's state against the target, and every comparison against a NaN is false, so the pose can never be judged reached. The call runs to the end of move_timeout and then raises a timeout that names the motion, not the argument. What the firmware does with a NaN target before that is not something I verified.

Soft-limit violations. The frame goes out and the firmware clamps it silently, so the arm stops at the limit while the caller is told it arrived. A caller asking for q = 3.5 on an axis limited to 2.809547 gets a successful return and an arm that is somewhere else. This is the failure mode that is hardest to notice, because nothing raises.

Unvalidated speed. move_l(pose, speed=5.0) is accepted while movej(q, speed=5.0) is refused, for the same argument with the same meaning, in the same module.

Reproduce

By inspection, with the greps above; all three are visible in src/litearm/arm.py without running anything.

For the speed gap specifically:

arm.movej([0.0] * 7, speed=5.0)     # raises InvalidCommandError: speed 需 0..1
arm.move_l(pose, speed=5.0)         # accepted, frame sent

For the soft-limit gap, read the limits, then ask for something outside them:

print([jp.q_min for jp in arm.params().all_joint_params()])
arm.movej([10.0] * 7)               # accepted; firmware clamps; reports arrival

I found these while porting the client to C++, where all three are prechecked before the frame is built. I have not run the Python examples above against this implementation.

Suggested direction

Precheck at the shared entry, not in each method, so the six entry points cannot drift apart again. The values a non-finite or soft-limit check needs are already available: n from the session, and q_min/q_max from the joint parameters. Reading the limits once when the session is established keeps the check local and costs no round trip per command.

Note that the port found one exemption worth keeping: home() targets zeros and must not be prechecked, because an arm that has drifted outside its soft limits is precisely when you need home() to be reachable. Its own docstring already explains the equivalent reasoning at src/litearm/arm.py:2152.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions