Skip to content

Add ROS 2 control mock hardware mode - #56

Open
Daniiiil1 wants to merge 1 commit into
odriverobotics:mainfrom
Daniiiil1:feature/mock-hardware
Open

Daniiiil1 wants to merge 1 commit into
odriverobotics:mainfrom
Daniiiil1:feature/mock-hardware

Conversation

@Daniiiil1

Copy link
Copy Markdown

Summary

Add a use_mock_hardware parameter to the odrive_ros2_control system interface so controllers can be developed and tested without SocketCAN or physical ODrives.

Behavior

  • skips CAN and node-ID requirements in mock mode
  • initializes deterministic zero state instead of NaN
  • mirrors position commands into position state
  • integrates velocity commands into position using the ROS control period
  • models effort-only control as a locked axis with zero velocity
  • preserves velocity and effort feed-forward state when those interfaces accompany a primary command
  • keeps the existing real-hardware path unchanged

The README documents the parameter and model. This implements the behavior outlined by the maintainer in #24.

Validation

  • git diff --check
  • reviewed every lifecycle path to ensure mock configure, cleanup, activation, deactivation, read, write, and mode switching avoid SocketCAN access

A full build requires the repository ROS 2 development container and is left to project CI.

Closes #24

@CLAassistant

CLAassistant commented Aug 8, 2026 •

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@samuelsadok samuelsadok left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's one thing I missed when writing the comment in #24 (comment): There is already a vendor independent ROS2 way to do this: mock_components/GenericSystem.

This repo already showcases it in the odrive_botwheel_explorer usage example, in diffbot.ros2_control.xacro.

With this in mind, do you still have a use case where mock support directly in odrive_hardware_interface.cpp would be important? (please describe)

Still added some review comments while I was at it.

return_type ODriveHardwareInterface::read(const rclcpp::Time& timestamp, const rclcpp::Duration&) {
return_type ODriveHardwareInterface::read(const rclcpp::Time& timestamp, const rclcpp::Duration& period) {
if (use_mock_hardware_) {
for (auto& axis : axes_) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the body of this for loop should be in a separate function, e.g. Axis::update_mock()

can_intf_name_ = info_.hardware_parameters["can"];
auto mock_parameter = info_.hardware_parameters.find("use_mock_hardware");
use_mock_hardware_ = mock_parameter != info_.hardware_parameters.end() &&
(mock_parameter->second == "true" || mock_parameter->second == "1");

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hardware_interface::parse_bool() seems to be the standard way to do this.


for (auto& joint : info_.joints) {
axes_.emplace_back(&can_intf_, std::stoi(joint.parameters.at("node_id")));
if (!use_mock_hardware_) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This adds more complexity than necessary to the init logic. Note that we can just construct the Axis objects with {nullptr, 0} if use_mock_hardware_ is true. Could be as simple as:

use_mock_hardware_ = // load from params
if (use_mock_hardware_) {
    // fill axes_ array with Axis(nullptr, 0)
} else {
    // old instantiation logic
}

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.

ROS2 Control Mock Hardware

3 participants