Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Support ExtendedPostiion and Current control; Implement on_configure#101

Open
sebtiburzio wants to merge 10 commits into
dynamixel-community:rollingdynamixel-community/dynamixel_hardware:rollingfrom
sebtiburzio:rollingsebtiburzio/dynamixel_hardware:rollingCopy head branch name to clipboard
Open

Support ExtendedPostiion and Current control; Implement on_configure#101
sebtiburzio wants to merge 10 commits into
dynamixel-community:rollingdynamixel-community/dynamixel_hardware:rollingfrom
sebtiburzio:rollingsebtiburzio/dynamixel_hardware:rollingCopy head branch name to clipboard

Conversation

@sebtiburzio

Copy link
Copy Markdown
Contributor

Hello, last year I worked on a project using dynamixel_hardware, during which I came across some problems and made some changes:

Couple of things to note:

  • The hardware interface implementation doesn't handle the different control modes very elegantly and will switch between them when receiving different command types. So I would recommend ensuring only one controller is active on startup and providing ROS services to switch between them.
  • According to the ROS2_control diagram linked above, powering the motors (enable_torque(true)) should be done in the on_activate callback. However I found that after on_configure completes the real-time read/write calls interfere with the dynamixel workbench functions, so I left it in on_configure. I believe similar issues are discussed here: Delayed Read Write Calls till after on_activate() is completed #89

Feel 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.

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>
@youtalk

youtalk commented Jul 25, 2026

Copy link
Copy Markdown
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!

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

Morty Proxy This is a proxified and sanitized view of the page, visit original site.