Skip to content

Added Swerve Drive Controller Package#1694

Open
nitin2606 wants to merge 69 commits into
ros-controls:masterfrom
nitin2606:feature/swerve-drive-controller
Open

Added Swerve Drive Controller Package#1694
nitin2606 wants to merge 69 commits into
ros-controls:masterfrom
nitin2606:feature/swerve-drive-controller

Conversation

@nitin2606

Copy link
Copy Markdown
Contributor

Description
This PR introduces the swerve_drive_controller, a new controller for swerve drive robots with four independently steerable wheels, enabling omnidirectional motion in ROS 2. It complements controllers like diff_drive_controller by supporting advanced mobile robot platforms.

Features

Supports geometry_msgs/msg/Twist or TwistStamped velocity inputs (x, y linear; z angular).
Publishes raw nav_msgs/msg/Odometry for user-defined post-processing.
Publishes /tf transforms (optional, if enable_odom_tf=true).
Configurable via YAML for wheel geometry and kinematic constraints.
Changes
Added swerve_drive_controller package (src/, include/, test/).
Exported as a pluginlib plugin (swerve_drive_controller_plugin.xml).
Updated ros2_controllers CMakeLists.txt and package.xml.
Included gtest tests (test/test_swerve_drive_controller.cpp) and config (test/config/test_swerve_drive_controller.yaml).

@christophfroehlich christophfroehlich 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.

Thanks again for your contribution. Could you please fix the failing tests? A very quick first review below.

Do you mind add a brief section in the kinematics section of the docs, even in a new PR to get it merged faster? Thanks!

Comment thread swerve_drive_controller/CMakeLists.txt Outdated
Comment thread swerve_drive_controller/LICENSE
Comment thread swerve_drive_controller/doc/userdoc.rst
Comment thread swerve_drive_controller/package.xml Outdated
Comment thread swerve_drive_controller/src/swerve_drive_controller.cpp Outdated
Comment thread swerve_drive_controller/src/swerve_drive_kinematics.cpp Outdated
Comment thread swerve_drive_controller/test/test_swerve_drive_controller.hpp Outdated
Comment thread swerve_drive_controller/test/test_swerve_drive_controller.cpp Outdated
@nitin2606

Copy link
Copy Markdown
Contributor Author

I'll make changes and push.

nitin2606 and others added 2 commits May 25, 2025 22:21
@christophfroehlich

Copy link
Copy Markdown
Member

do you mind cherry-picking the changes to the docs in a separate PR? Splitting this up would help to reviewing things.

@nitin2606

Copy link
Copy Markdown
Contributor Author

do you mind cherry-picking the changes to the docs in a separate PR? Splitting this up would help to reviewing things.

I have raised a separate PR for changes in docs. PR #1712

Comment thread doc/controllers_index.rst Outdated

@Maverobot Maverobot 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.

Hi there, thanks for your contribution! I've started reviewing the changes and have left a few comments. I will try to find time to work through it and will follow up once I'm done.

Comment thread swerve_drive_controller/src/swerve_drive_kinematics.cpp Outdated
Comment thread swerve_drive_controller/src/swerve_drive_controller.cpp Outdated
Comment thread swerve_drive_controller/src/swerve_drive_controller.cpp Outdated
Comment thread swerve_drive_controller/src/swerve_drive_controller.cpp Outdated
Comment thread swerve_drive_controller/src/swerve_drive_controller.cpp Outdated
Comment thread doc/mobile_robot_kinematics.rst
Co-authored-by: Zheng Qu <quzhengrobot@gmail.com>
@mergify

mergify Bot commented Jun 24, 2025

Copy link
Copy Markdown
Contributor

This pull request is in conflict. Could you fix it @nitin2606?

Signed-off-by: nitin <nitinmaurya2606@gmail.com>
…ix CI duplicate errors

Signed-off-by: nitin2606 <nitinmaurya2606@gmail.com>
@nitin2606
nitin2606 force-pushed the feature/swerve-drive-controller branch 2 times, most recently from 6567bcb to c75660f Compare January 20, 2026 07:54
@christophfroehlich

Copy link
Copy Markdown
Member

@nitin2606 Please don't do force pushes if a PR is already under review. It will complicate re-reviews a lot. And we will squash the commits at the time of merging a PR anyways.

@nitin2606

Copy link
Copy Markdown
Contributor Author

@nitin2606 Please don't do force pushes if a PR is already under review. It will complicate re-reviews a lot. And we will squash the commits at the time of merging a PR anyways.

Thanks for the clarification
The force push was only to revert an accidental rebase and restore the branch to the last known CI-passing state. I won’t do force pushes on this PR going forward.

I’ll keep the history stable from here and only merge upstream changes if needed. Thanks for the guidance.

@nitin2606

Copy link
Copy Markdown
Contributor Author

@christophfroehlich, could you please help me with the failing CI checks?

@christophfroehlich

Copy link
Copy Markdown
Member

@christophfroehlich, could you please help me with the failing CI checks?

Failures of rolling binary build and windows build are not related.

Check Docs is failing because of:

/home/runner/work/ros2_controllers/ros2_controllers/doc/ros2_controllers/swerve_drive_controller/doc/userdoc.rst: WARNING: document isn't included in any toctree

You need to add it here https://github.com/ros-controls/ros2_controllers/blob/master/doc/controllers_index.rst#controllers-for-wheeled-mobile-robots

@nitin2606

Copy link
Copy Markdown
Contributor Author

@christophfroehlich, could you please help me with the failing CI checks?

Failures of rolling binary build and windows build are not related.

Check Docs is failing because of:

/home/runner/work/ros2_controllers/ros2_controllers/doc/ros2_controllers/swerve_drive_controller/doc/userdoc.rst: WARNING: document isn't included in any toctree

You need to add it here https://github.com/ros-controls/ros2_controllers/blob/master/doc/controllers_index.rst#controllers-for-wheeled-mobile-robots

Got it

Signed-off-by: nitin2606 <nitinmaurya2606@gmail.com>
@nitin2606

Copy link
Copy Markdown
Contributor Author

@christophfroehlich I added swerve_drive_controller to controllers_index.rst (see https://github.com/nitin2606/ros2_controllers/blob/feature/swerve-drive-controller/doc/controllers_index.rst
), but the CI checks are still failing. Could you please take a look and help me understand what might be missing?

jsupratman13 added a commit to sbgisen/ros2_controllers that referenced this pull request Apr 1, 2026
@github-actions

Copy link
Copy Markdown
Contributor

This PR is stale because it has been open for 45 days with no activity. Please tag a maintainer for help on completing this PR, or close it if you think it has become obsolete.

@github-actions github-actions Bot added the stale label Jun 23, 2026
@mergify

mergify Bot commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

This pull request is in conflict. Could you fix it @nitin2606?

@github-actions github-actions Bot removed the stale label Jun 24, 2026

@solonovamax solonovamax left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

just sort of skimmed through this on my phone (that's why each comment is only on a single line, sorry about that) and had some random comments

not super familiar with how things are done in this codebase however, so take some things with a grain of salt

auto logger = get_node()->get_logger();

wheel_handles_.resize(4);
for (std::size_t i = 0; i < 4; i++)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

is on_activate really the best place for this?
looking at a handful of other controllers, it seems like this type of thing is generally put into on_configure


for (std::size_t i = 0; i < wheel_handles_.size(); ++i)
{
if (!wheel_handles_[i])

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

is there a reason this (the validation part, not the resetting part) isn't in the same for loop that creates the wheel handles?

OdometryState odometry_;

// Topic Subscription
bool subscriber_is_active_ = false;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

it's not obvious to me why this is necessary?

other controllers seem to just blindly update the stored command value from the topic, even if deactivated, as there's not really any harm in that

rclcpp::Time previous_publish_timestamp_{0, 0, RCL_CLOCK_UNINITIALIZED};

// Timeout to consider cmd_vel commands old
std::chrono::milliseconds cmd_vel_timeout_{500};

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

shouldn't this be an rclcpp::Duration?

Params params_;

SwerveDriveKinematics swerveDriveKinematics_;
std::queue<TwistStamped> previous_commands_; // last two commands

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

seems to be unused?

regardless, storing a queue for two elements probably isn't the best idea
you could just have two fields with pointers and then swap them before overwriting the newer one, that way you avoid additional allocations


protected:
// Handles for four wheels and their axles
std::vector<std::optional<Wheel>> wheel_handles_;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

are these optional exclusively for the error handling?

imo it would be better to just make these not optional & have get_interface_object() return an optional, then in on_configure/on_activate if they cannot be found just erroring out

it avoids an necessary pointer dereference during normal operation (because optional is just a wrapper around a raw pointer + nullptr)
it also avoids a bunch of otherwise unecessary has_value() calls, because those can never happen.


if (wheel_radius <= 0.0)
{
std::cerr << "invalid wheel_radius <= 0.0\n";

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

should use the normal rclcpp logging

if (wheel_radius <= 0.0)
{
std::cerr << "invalid wheel_radius <= 0.0\n";
// fallthrough: compute but set angular velocities to 0 to avoid div-by-zero

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

wouldn't it be better to just completely error out?


wheel_commands[i].drive_velocity = linear_speed;

if (wheel_radius > 0.0)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

probably better to use a ternary here

double vx_sum = 0.0, vy_sum = 0.0, wz_sum = 0.0;
for (std::size_t i = 0; i < 4; i++)
{
double vx = wheel_velocities[i] * std::cos(steering_angles[i]);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

unsure if this is a GNU extension or not, but in math.h there's sincos which calculates the sine & cosine values simultaneously (though, the compiler might also optimize this for you, unsure)

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.

Add a controller for afour wheel independent steering / drive mobile base, aka a swerve drive

10 participants