Repository navigation
Check the error field of setup and torque answers - #152
Conversation
set_torque, change_id, change_baudrate and the torque off and lock opening ahead of the last two went through write_register, which reads the motor's answer and drops its error field. A motor refusing a write (an id written with the torque still on, a value out of range) answers all the same, and the call reported success. Before rustypot, LeRobot raised on that field for exactly these writes. They now read it, and an answer reporting an error fails with a new MotorError, which carries the motor's id and its StatusError. set_torque lists it with the motors that failed and moves on to the next one. Retrying would get the same answer, so like a RegisterError it is not tried again.
|
Checked on hardware that healthy motors are not reported as failed now: two SO-101 arms, six STS3215 each (firmware 3.10).
I could not make a motor refuse a write on demand without moving the arm, so the |
pierre-rouanet
left a comment
There was a problem hiding this comment.
The code is clean and tests pass (cargo test 89 + 11 green, clippy count unchanged from develop, test packet checksums correct). One thing to change before merging, though: counting every non-zero status byte as a failure brings in a new partial-state bug.
"Any error bit means the write failed"
On Feetech and Dynamixel v1, the status byte also carries ongoing conditions: voltage 0x01, overheating 0x04, overload 0x20. On v2, Alert (0x80) stays set for as long as a hardware fault is latched. None of these mean the write was refused. Two problems follow:
-
set_torquenow leaves the lock out of step with the torque (src/bus.rs, theretryingclosure). When the torque write comes back with an error,?returns early and the lock write never happens. Before this PR both writes went through.- Feetech motor in an overload alarm,
set_torque(True): torque on, lock left open, so the EEPROM is unprotected. - Same motor,
set_torque(False): torque off, lock stays closed.
The new test uses exactly this case (0x20 = overload) and asserts the lock is left alone, so it locks the problem in rather than catching it.
- Feetech motor in an overload alarm,
-
change_id/change_baudraterefuse any motor with a latched alarm. The torque-off write inopen_eepromnow fails, so a motor reporting low voltage, or a v2 motor with Alert set, can't be given a new id or baud rate, which is arguably when you need it most.
I get the LeRobot-parity argument, but I think it's the wrong default for the library. Suggestions:
-
Minimum: in
set_torque, always do the lock write, then report the first error, so the lock always follows the torque. -
Better: fail only on bits that mean the instruction was refused, per protocol:
- v2:
v2_instruction_error() != 0, ignoring Alert; - Dynamixel v1: instruction (0x40), range (0x08), checksum (0x10);
- Feetech: probably nothing, since as far as I know its status byte only reports alarms (worth checking).
That keeps the useful case this PR is after: a Dynamixel refusing an id change with an access or range error.
- v2:
Minor
- Python callers only get a message string, so they can't read the id or status byte without parsing it. Fine for now; a dedicated exception class would fit with the typed-error question in #151.
MotorErroris broad for what is a status-packet error; something likeStatusPacketErrormight age better. Not a blocker.
A status byte also carries conditions the motor reports while it carries the write out: input voltage, temperature, load, and on protocol v2 the alert bit of a latched hardware fault. Counting them as failures left a Feetech lock out of step with the torque when the torque write came back with an alarm, and refused a new id or baud rate to any motor reporting one. ServoDefinition::refuses tells a refusal from an alarm: an error number on protocol v2, an instruction, checksum or range error on protocol v1, and never on a servo whose status byte only reports alarms, which the new status_alarms_only states for the Feetech servos. set_torque, change_id and change_baudrate fail only on a refusal, with MotorError renamed StatusPacketError.
…-setup-and-torque # Conflicts: # Changelog.md # src/lib.rs
|
@pierre-rouanet thanks, you were right: a status byte is not a verdict on the write. Changed in 53da259:
I also merged develop (fd4a455) for the Changelog, now under Unreleased since 1.12.0 is out. On the LeRobot side, read, write and sync read follow the same rule (huggingface/lerobot#4672). |
pierre-rouanet
left a comment
There was a problem hiding this comment.
Thanks, this addresses both points: an alarm no longer fails the write, the lock follows the torque, and a motor with a latched alarm can still be given a new id or baud rate. The Feetech SDK check settles the 0x08 question. On the PR head, tests pass (96 + 11) and clippy shows no new warnings. Orbita keeps the Dynamixel v1 rule, which is fine since its firmware runs an older rustypot.
A gap in #142 and #143, found in a review on the LeRobot side.
The problem
Bus::set_torque,change_id,change_baudrateand the torque off and lock opening ahead of the last two write throughwrite_register, which reads the motor's answer and drops its error field. A motor that refuses a write still answers: a Dynamixel asked to change its id with the torque on reports an access error, a value out of range a range error. The call then reports success, and inset_torquethe motor is not listed as failed.What changes
ServoDefinition::refuses(status)tells a refusal from an alarm: an error number on protocol v2 (the alert bit alone is a latched hardware fault), an instruction, checksum or range error on protocol v1, and never on a servo whose status byte only reports alarms. The newServoInfo.status_alarms_onlysays so for the Feetech servos (STS3215, SCS0009, SCS0043), whose status bits are input voltage, angle, overheating, over-current and overload, as in Feetech's SDK. Python reads it asServoDefinition.status_alarms_only.StatusPacketErrorcarrying the motor's id and itsStatusError(RuntimeErroron Python, with "motor 3 refused the instruction with status error 0x07"). An alarm does not fail them: a Feetech lock still follows the torque of an overloaded motor, and a motor in an alarm can still be given a new id or baud rate.set_torquelists a refusing motor with the ones that failed, and goes on with the next one.retryingleaves aStatusPacketErroralone, as it does aRegisterError.Tests
refuseson an STS3215 (0x08 and 0x20 are alarms), an AX (instruction, checksum and range errors refuse, voltage and overload do not) and an XL330 (an error number refuses, the alert bit alone does not).set_torqueon an STS3215 answering both writes with an overload alarm: nothing fails, and the lock follows the torque.set_torqueon an XL330 refusing the torque write with an access error: listed as failed with aStatusPacketError, written once withretries = 2, and the next motor still written.change_id: an STS3215 in an over-current alarm takes its new id; an XL330 refusing the id write fails withStatusPacketError { id: 9, .. }.cargo test(96 + 11), clippy with no warning, and the Python build. Not run on hardware: no motor here refuses these writes on demand.The other half of that review, typed errors for a timeout and for a port that is gone, is in #151 as a question, since nothing uses it yet; a Python exception class for
StatusPacketErrorbelongs there too.