Skip to content

Distinguish an unreachable move_p target from a motion timeout #13

Description

@X-F-R

Context

Arm.move_p() (src/litearm/arm.py:1968) sends a Cartesian target and lets the firmware solve it:

self._write_cmd(P.CMD_MOVE_P, payload)
a.expect(P.RSP_ACK, 1.0, "move_p", echo_cmd=P.CMD_MOVE_P)
end = time.monotonic() + self.move_timeout
while time.monotonic() < end:
    st = self.get_state(refresh=True, timeout=0.1).value
    if st is not None and st.faulted:
        raise MotorFaultError(f"move_p: FAULT {st.fault_detail}")
    tcp = self.get_tcp().value
    if st is not None and tcp is not None and self._pose_near(tcp, pose, pos_tol, rpy_tol):
        return st
    time.sleep(0.02)
raise MotionTimeoutError(f"move_p 未到目标位姿, 超时 {self.move_timeout}s")

The only ways out of the loop are "arrived" and "faulted". Whether the firmware could solve the target at all is not one of them.

Expected

A target the firmware cannot solve should be reported as such. The firmware decides this immediately and asynchronously, and the SDK's own comments elsewhere in the module are explicit that an immediate rejection and a motion that never completes are different events, with different remedies.

Actual

The 0x02 command is accepted, then the firmware fails to solve it and sends nothing further. The loop polls get_tcp() until move_timeout expires and raises:

MotionTimeoutError: move_p 未到目标位姿, 超时 15.0s

The message names a motion that did not reach its target. The situation is a target that was never solvable, and the difference matters: "not arrived" suggests torque, obstruction, a lost frame or a slipping arm, while the actual remedy is to choose a different target.

This is worst at the home pose. After homing, the arm is fully extended vertically and the pose is a kinematic singularity, so any target along the arm axis is unsolvable while lateral targets are fine. A user following the obvious instinct — move a little further along the direction the arm already points — gets a 15-second wait and an error pointing at the wrong subsystem.

Reproduce

Observed on hardware, firmware Litearm1.8.0-7J, via the C++ port. With the arm homed (TCP z ≈ 0.783 m, pointing up):

  • ik() with z + 0.02 m (further along the arm axis): no solution;
  • ik() with z - 0.02 m and z - 0.05 m: no solution;
  • ik() with x ± 0.05 m and y ± 0.05 m: solutions, and reachable.

move_p to the first of those targets waits out the full move_timeout and raises MotionTimeoutError, which is where the misattribution was first seen on real hardware.

ik() reports the truth immediately because it is a pure computation; the failure only becomes ambiguous on the asynchronous move_p path.

Suggested direction

The firmware already knows the answer at the moment it refuses to plan, so the cleanest fix is a reply the SDK can key on — an ERR for the 0x02, or a status bit meaning "no solution". If the firmware cannot be changed, the SDK can call ik(pose) before sending and raise a distinct error when it returns no solution; that costs one round trip and removes the ambiguity for the singular and unreachable cases, though it cannot see obstacle or limit rejections.

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