Support ExtendedPostiion and Current control; Implement on_configure#101
Open
sebtiburzio wants to merge 10 commits into
Open
Support ExtendedPostiion and Current control; Implement on_configure#101sebtiburzio wants to merge 10 commits into
sebtiburzio wants to merge 10 commits into
Conversation
Swap Position to ExtendedPosition mode
…ntrol_mode Add current control to hardware interface
…f_to_dummy Add velocity and effort update to dummy mode
Seb/startup fixes
youtalk
added a commit
that referenced
this pull request
Jul 25, 2026
…#111) * feat: add DynamixelDriver interface and multi-distro compat header Introduce the abstract dynamixel_hardware::DynamixelDriver interface that all servo I/O will go through, and compat.hpp which concentrates every HARDWARE_INTERFACE_VERSION_GTE gate behind DXL_HAS_* macros (params-based on_init >= 4.34.0, ConstSharedPtr on_export_* >= 4.19.0 — 4.18.x used SharedPtr signatures — component logger >= 4.13.0, test-side ResourceManager ctor >= 4.13.0). The ControlMode enum moves into dynamixel_driver.hpp; the misspelled Currrent enumerator is fixed to Current in the move. Signed-off-by: Yutaka Kondo <yutaka.kondo@youtalk.jp> * feat: add DummyDriver and gmock test infrastructure DummyDriver is the in-memory DynamixelDriver implementation that will back the use_dummy hardware parameter: position commands reflect into the emulated state and velocity commands are integrated over the write-cycle period via tick(). This fixes the basic case of the dummy velocity-control bug (refs #71); mode-equivalent emulation of all eight operating modes follows in the control-mode rework. Adds ament_cmake_gmock / ros2_control_test_assets test dependencies and the first gmock test target. Signed-off-by: Yutaka Kondo <yutaka.kondo@youtalk.jp> * feat: add WorkbenchDriver wrapping DynamixelWorkbench Move every DynamixelWorkbench call site (port init, ping, control-table item lookup with the Goal_Velocity->Moving_Speed, Present_Velocity->Present_Speed and Present_Current->Present_Load fallback chains, sync write handlers 0/1, the sync read window with the historical +2 gap, torque, position/velocity mode setters, value conversion, itemWrite) into WorkbenchDriver behind the DynamixelDriver interface. Workbench log pointers are captured into last_error(). The sync-read window math is extracted into the pure static helper compute_read_window() and locked in by the new test_workbench_driver unit tests; the serial-facing rest is verified at compile level here and in the on-hardware session. Modes other than Position/Velocity fail with a clear message until the control-mode rework. The plugin still holds its own workbench member; it is removed in the follow-up lifecycle commit. Signed-off-by: Yutaka Kondo <yutaka.kondo@youtalk.jp> * fix: never destroy an uninitialized DynamixelWorkbench WorkbenchDriver held a DynamixelWorkbench by value, but the toolbox leaves its port and packet handler pointers uninitialized until init() runs and dereferences them in its destructor. Constructing a driver without connecting therefore segfaulted on teardown -- reachable from production, because the plugin builds its driver in on_init and a component that is loaded but never configured is destroyed without on_configure ever running. Own the workbench through a unique_ptr allocated in connect() and released in disconnect(), and fail the remaining calls with a "not connected" last_error() while it is absent. Signed-off-by: Yutaka Kondo <yutaka.kondo@youtalk.jp> * refactor: normalize lifecycle and route plugin through DynamixelDriver DynamixelHardware no longer touches DynamixelWorkbench directly: it owns a std::unique_ptr<DynamixelDriver> selected in on_init (DummyDriver for use_dummy, WorkbenchDriver otherwise) with a public set_driver_for_testing() injection hook. Lifecycle normalization: on_init parses and validates parameters only (std::stoi/.at failures are caught and reported as CallbackReturn::ERROR instead of crashing), on_configure connects/pings/sets up handlers and writes extra joint parameters, on_activate performs the first read, reset_command and torque on, on_deactivate turns torque off, on_cleanup/on_shutdown disconnect, on_error best-effort torque off and disconnect. The plugin now loads through the ResourceManager with real serial parameters and no hardware attached. Multi-distro support via compat.hpp: params-based on_init and description-based on_export_* on jazzy and newer, HardwareInfo on_init and by-value exports on humble, one logger() accessor for both. The canonical hardware parameter is port_name; usb_port still works with a deprecation warning. The legacy write() heuristic mode switching, torque off/mode/torque on sequencing and reset_command semantics are preserved exactly and are now locked in by MockDriver unit tests plus ResourceManager load smoke tests. Refs #89, #101, #71. Co-authored-by: soham2560 <soham2560@users.noreply.github.com> Co-authored-by: sebtiburzio <sebtiburzio@users.noreply.github.com> Signed-off-by: Yutaka Kondo <yutaka.kondo@youtalk.jp> * test: cover the init_impl driver-injection-order contract set_driver_for_testing() before on_init must survive on_init: the init_impl `if (!driver_)` guard only constructs a driver when none is set, and M3/M4 test fixtures rely on that. Every other test injects the mock after on_init, exercising only the opposite branch, so a regression that made init_impl unconditionally construct a WorkbenchDriver would leave all of them green while later fixtures silently started opening /dev/ttyUSB0. injected_driver_survives_on_init injects the mock first and asserts on_configure() drives that same injected instance. Signed-off-by: Yutaka Kondo <yutaka.kondo@youtalk.jp> * fix: guard on_activate and WorkbenchDriver against stale state on_activate() ignored read()'s return value, so a failed initial sync-read left every joint's NaN seed from init_impl() in place. reset_command() then copied that NaN into command.position, and because NaN != NaN, write()'s change detection treated it as a real command and sync-wrote NaN to servos with torque enabled. Refuse to activate when any joint's state is still NaN after the read. WorkbenchDriver::disconnect() reset the DynamixelWorkbench but left control_items_ populated with pointers owned by that now-destroyed object, and a failed re-setup() could leave stale entries in place too. Add an ensure_setup() guard (used by read_states/write_positions /write_velocities) that fails once control_items_ is empty, and clear control_items_ in both disconnect() and at the start of setup(). Also document read_states()'s implicit sizing postcondition on the DynamixelDriver interface. Signed-off-by: Yutaka Kondo <yutaka.kondo@youtalk.jp> * docs: document port_name and the on_deactivate torque change README still taught the deprecated usb_port parameter name in its setup instructions and copy-paste <hardware> XML block; switch both to port_name and note that usb_port still works but logs a deprecation warning. Also note that on_deactivate now disables torque (it used to be a no-op), so deactivating the hardware component or the controller_manager makes the joints go limp. Signed-off-by: Yutaka Kondo <yutaka.kondo@youtalk.jp> --------- Signed-off-by: Yutaka Kondo <yutaka.kondo@youtalk.jp> Co-authored-by: soham2560 <soham2560@users.noreply.github.com> Co-authored-by: sebtiburzio <sebtiburzio@users.noreply.github.com>
Collaborator
|
The current-control part of this PR has been re-implemented in #112 with a Co-authored-by credit (the on_configure lifecycle part was absorbed by the driver-abstraction PR). Thank you! |
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.
Hello, last year I worked on a project using
dynamixel_hardware, during which I came across some problems and made some changes:on_configurecallback for initial communication with motors (instead ofon_init, to align with this diagram: https://control.ros.org/rolling/_images/hardware_interface_lifecycle.png)Couple of things to note:
ROS2_controldiagram linked above, powering the motors (enable_torque(true)) should be done in theon_activatecallback. However I found that afteron_configurecompletes the real-timeread/writecalls interfere with the dynamixel workbench functions, so I left it inon_configure. I believe similar issues are discussed here: Delayed Read Write Calls till afteron_activate()is completed #89Feel free to review the changes and see if they can be included. I am no longer working on the project (and don't have access to the hardware) but can make some updates if needed. I also noted there has been some other PRs made recently, not sure what the compatibility is with my changes.
Cheers.