[ROB-1464] fix(linear_rail): keep brake engaged after a single shutdown#56
Open
nu-jliu wants to merge 1 commit into
Open
[ROB-1464] fix(linear_rail): keep brake engaged after a single shutdown#56nu-jliu wants to merge 1 commit into
nu-jliu wants to merge 1 commit into
Conversation
- i2rt/utils/usb_gpio_driver.py: stop UsbGpioBackend.cleanup() from driving configured OUTPUT channels HIGH (released) on shutdown. The converter latches its last-driven level, so the brake now stays at its last commanded level (LOW = engaged) and the rail stays held. Rewrote the misleading comment. - i2rt/flow_base/linear_rail_controller.py: make LinearRailController.cleanup() idempotent (a 2nd call is a no-op, so the standalone double-close no longer reopens the serial port and resets the converter); isolate set_velocity(0.0) in its own try/except so a CAN error can't skip the brake engage; replace global GPIO.cleanup() with GPIO.cleanup((UPPER_LIMIT_GPIO, LOWER_LIMIT_GPIO)) so on the Pi the brake OUTPUT pin stays driven LOW instead of being floated. - i2rt/utils/tests/test_usb_gpio_driver.py: invert the cleanup test to assert the brake stays LOW and cleanup issues no HIGH to the brake channel; add a test that cleanup() accepts and ignores the limit-pin tuple the controller now passes. Co-Authored-By: Claude Opus 4.8 (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.
Summary
On the consolidated single-host LinearBot (x86; brake driven through the bestep USB-to-GPIO converter, not a Raspberry Pi), stopping the controller left the vertical rail brake RELEASED, so the rail could drift/drop. The standalone runner only appeared to work because it closes the vehicle twice; any caller that closes once (e.g. lab42) ended up with the brake released. A single
LinearRailController.cleanup()/LinearRailVehicle.close()now leaves the brake ENGAGED (GPIO LOW) on both x86 and Raspberry Pi, with no dependence on a second call and no serial-port reopen.Root cause:
cleanup()engaged the brake (drove it LOW), thenGPIO.cleanup()undid it — on x86UsbGpioBackend.cleanup()deliberately drove every configured OUTPUT channel HIGH (released), and on the Pi the globalRPi.GPIO.cleanup()floats all pins. Net result of one cleanup = brake released.Safety note: brake polarity is LOW = engaged/held (unchanged —
set_brake(engaged=True)already commanded LOW; this PR only stopscleanup()from overriding it back to HIGH). Steady state after a single close = the same latched LOW the standalone double-close already holds the rail in. Software cannot guarantee the level survives a hard kill / power loss / USB unplug — that depends on the brake being spring-engaged (power-to-release). Scope is brake-only; rail-motor / CAN-loop shutdown behavior is intentionally unchanged.Changes
i2rt/utils/usb_gpio_driver.py:UsbGpioBackend.cleanup()no longer drives configured OUTPUT channels HIGH (released) on shutdown. The converter latches its last-driven level across a port close, so the brake stays at its last commanded level (LOW = engaged) and the rail stays held. Rewrote the misleading comment.i2rt/flow_base/linear_rail_controller.py:LinearRailController.cleanup()is now idempotent (a 2nd call is a no-op, so the standalone double-close no longer reopens the serial port and resets the converter);set_velocity(0.0)is isolated in its owntry/exceptso a CAN error can't skip the safety-critical brake engage; replaced globalGPIO.cleanup()withGPIO.cleanup((UPPER_LIMIT_GPIO, LOWER_LIMIT_GPIO))so on the Pi the brake OUTPUT pin stays driven LOW instead of being floated. Added a_cleaned_upguard flag in__init__.i2rt/utils/tests/test_usb_gpio_driver.py: inverted the cleanup test (test_cleanup_keeps_brake_latched_low_not_released) to assert the brake stays LOW and cleanup issues no HIGH to the brake channel; addedtest_cleanup_accepts_pin_tuple_and_tears_downfor the limit-pin tuple the controller now passes.Test Plan
Unit tests (x86, no hardware — runs in CI)
uv run pytest i2rt/utils/tests/test_usb_gpio_driver.py -q— 21 pass, incl.test_cleanup_keeps_brake_latched_low_not_releasedandtest_cleanup_accepts_pin_tuple_and_tears_down.uv run pytest -n auto— full suite green (expected: 281 passed, 2 skipped).uv run ruff check .— clean.Manual on x86 (the reported failure)
LinearRailVehicle(enable_linear_rail=True, usb_gpio_device="/dev/ttyUSB0", ...)and callvehicle.close()exactly once.Standalone runner
python i2rt/flow_base/flow_base_controller.py --linear-rail --device /dev/ttyUSB0, then Ctrl-C.Regression / parity
enable_linear_rail=False— base-motor behavior unchanged.Related to ROB-1464
🤖 Generated with Claude Code