Skip to content

Commit 19b7630

Browse files
committed
Make diff_drive_controller a ChainableControllerInterface
1 parent 7ddaf83 commit 19b7630

File tree

4 files changed

+255
-71
lines changed

4 files changed

+255
-71
lines changed

diff_drive_controller/diff_drive_plugin.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<library path="diff_drive_controller">
2-
<class name="diff_drive_controller/DiffDriveController" type="diff_drive_controller::DiffDriveController" base_class_type="controller_interface::ControllerInterface">
2+
<class name="diff_drive_controller/DiffDriveController" type="diff_drive_controller::DiffDriveController" base_class_type="controller_interface::ChainableControllerInterface">
33
<description>
44
The differential drive controller transforms linear and angular velocity messages into signals for each wheel(s) for a differential drive robot.
55
</description>

diff_drive_controller/include/diff_drive_controller/diff_drive_controller.hpp

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,14 @@
2525
#include <string>
2626
#include <vector>
2727

28-
#include "controller_interface/controller_interface.hpp"
28+
#include "controller_interface/chainable_controller_interface.hpp"
2929
#include "diff_drive_controller/odometry.hpp"
3030
#include "diff_drive_controller/speed_limiter.hpp"
3131
#include "geometry_msgs/msg/twist_stamped.hpp"
3232
#include "nav_msgs/msg/odometry.hpp"
3333
#include "odometry.hpp"
3434
#include "rclcpp_lifecycle/state.hpp"
35-
#include "realtime_tools/realtime_box.hpp"
35+
#include "realtime_tools/realtime_buffer.hpp"
3636
#include "realtime_tools/realtime_publisher.hpp"
3737
#include "tf2_msgs/msg/tf_message.hpp"
3838

@@ -41,7 +41,7 @@
4141

4242
namespace diff_drive_controller
4343
{
44-
class DiffDriveController : public controller_interface::ControllerInterface
44+
class DiffDriveController : public controller_interface::ChainableControllerInterface
4545
{
4646
using TwistStamped = geometry_msgs::msg::TwistStamped;
4747

@@ -52,7 +52,11 @@ class DiffDriveController : public controller_interface::ControllerInterface
5252

5353
controller_interface::InterfaceConfiguration state_interface_configuration() const override;
5454

55-
controller_interface::return_type update(
55+
// Chainable controller replaces update() with the following two functions
56+
controller_interface::return_type update_reference_from_subscribers(
57+
const rclcpp::Time & time, const rclcpp::Duration & period) override;
58+
59+
controller_interface::return_type update_and_write_commands(
5660
const rclcpp::Time & time, const rclcpp::Duration & period) override;
5761

5862
controller_interface::CallbackReturn on_init() override;
@@ -76,6 +80,10 @@ class DiffDriveController : public controller_interface::ControllerInterface
7680
const rclcpp_lifecycle::State & previous_state) override;
7781

7882
protected:
83+
bool on_set_chained_mode(bool chained_mode) override;
84+
85+
std::vector<hardware_interface::CommandInterface> on_export_reference_interfaces() override;
86+
7987
struct WheelHandle
8088
{
8189
std::reference_wrapper<const hardware_interface::LoanedStateInterface> feedback;
@@ -103,7 +111,7 @@ class DiffDriveController : public controller_interface::ControllerInterface
103111
Odometry odometry_;
104112

105113
// Timeout to consider cmd_vel commands old
106-
std::chrono::milliseconds cmd_vel_timeout_{500};
114+
rclcpp::Duration cmd_vel_timeout_ = rclcpp::Duration::from_seconds(0.5);
107115

108116
std::shared_ptr<rclcpp::Publisher<nav_msgs::msg::Odometry>> odometry_publisher_ = nullptr;
109117
std::shared_ptr<realtime_tools::RealtimePublisher<nav_msgs::msg::Odometry>>
@@ -117,11 +125,9 @@ class DiffDriveController : public controller_interface::ControllerInterface
117125
bool subscriber_is_active_ = false;
118126
rclcpp::Subscription<TwistStamped>::SharedPtr velocity_command_subscriber_ = nullptr;
119127

120-
realtime_tools::RealtimeBox<std::shared_ptr<TwistStamped>> received_velocity_msg_ptr_{nullptr};
121-
std::shared_ptr<TwistStamped> last_command_msg_;
122-
123-
std::queue<TwistStamped> previous_commands_; // last two commands
128+
realtime_tools::RealtimeBuffer<std::shared_ptr<TwistStamped>> received_velocity_msg_ptr_{nullptr};
124129

130+
std::queue<std::array<double, 2>> previous_two_commands_;
125131
// speed limiters
126132
SpeedLimiter limiter_linear_;
127133
SpeedLimiter limiter_angular_;

diff_drive_controller/src/diff_drive_controller.cpp

Lines changed: 139 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,25 @@ constexpr auto DEFAULT_ODOMETRY_TOPIC = "~/odom";
3636
constexpr auto DEFAULT_TRANSFORM_TOPIC = "/tf";
3737
} // namespace
3838

39+
namespace
40+
{ // utility
41+
42+
// called from RT control loop
43+
void reset_controller_reference_msg(
44+
const std::shared_ptr<geometry_msgs::msg::TwistStamped> & msg,
45+
const std::shared_ptr<rclcpp_lifecycle::LifecycleNode> & node)
46+
{
47+
msg->header.stamp = node->now();
48+
msg->twist.linear.x = std::numeric_limits<double>::quiet_NaN();
49+
msg->twist.linear.y = std::numeric_limits<double>::quiet_NaN();
50+
msg->twist.linear.z = std::numeric_limits<double>::quiet_NaN();
51+
msg->twist.angular.x = std::numeric_limits<double>::quiet_NaN();
52+
msg->twist.angular.y = std::numeric_limits<double>::quiet_NaN();
53+
msg->twist.angular.z = std::numeric_limits<double>::quiet_NaN();
54+
}
55+
56+
} // namespace
57+
3958
namespace diff_drive_controller
4059
{
4160
using namespace std::chrono_literals;
@@ -46,11 +65,11 @@ using hardware_interface::HW_IF_VELOCITY;
4665
using lifecycle_msgs::msg::State;
4766

4867
DiffDriveController::DiffDriveController()
49-
: controller_interface::ControllerInterface(),
68+
: controller_interface::ChainableControllerInterface(),
5069
// dummy limiter, will be created in on_configure
5170
// could be done with shared_ptr instead -> but will break ABI
52-
limiter_angular_(std::numeric_limits<double>::quiet_NaN()),
53-
limiter_linear_(std::numeric_limits<double>::quiet_NaN())
71+
limiter_linear_(std::numeric_limits<double>::quiet_NaN()),
72+
limiter_angular_(std::numeric_limits<double>::quiet_NaN())
5473
{
5574
}
5675

@@ -104,8 +123,8 @@ InterfaceConfiguration DiffDriveController::state_interface_configuration() cons
104123
return {interface_configuration_type::INDIVIDUAL, conf_names};
105124
}
106125

107-
controller_interface::return_type DiffDriveController::update(
108-
const rclcpp::Time & time, const rclcpp::Duration & period)
126+
controller_interface::return_type DiffDriveController::update_reference_from_subscribers(
127+
const rclcpp::Time & time, const rclcpp::Duration & /*period*/)
109128
{
110129
auto logger = get_node()->get_logger();
111130
if (get_lifecycle_state().id() == State::PRIMARY_STATE_INACTIVE)
@@ -118,31 +137,60 @@ controller_interface::return_type DiffDriveController::update(
118137
return controller_interface::return_type::OK;
119138
}
120139

121-
// if the mutex is unable to lock, last_command_msg_ won't be updated
122-
received_velocity_msg_ptr_.try_get([this](const std::shared_ptr<TwistStamped> & msg)
123-
{ last_command_msg_ = msg; });
140+
const std::shared_ptr<TwistStamped> command_msg_ptr = *(received_velocity_msg_ptr_.readFromRT());
124141

125-
if (last_command_msg_ == nullptr)
142+
if (command_msg_ptr == nullptr)
126143
{
127144
RCLCPP_WARN(logger, "Velocity message received was a nullptr.");
128145
return controller_interface::return_type::ERROR;
129146
}
130147

131-
const auto age_of_last_command = time - last_command_msg_->header.stamp;
148+
const auto age_of_last_command = time - command_msg_ptr->header.stamp;
132149
// Brake if cmd_vel has timeout, override the stored command
133150
if (age_of_last_command > cmd_vel_timeout_)
134151
{
135-
last_command_msg_->twist.linear.x = 0.0;
136-
last_command_msg_->twist.angular.z = 0.0;
152+
reference_interfaces_[0] = 0.0;
153+
reference_interfaces_[1] = 0.0;
154+
}
155+
else if (!std::isnan(command_msg_ptr->twist.linear.x) && !std::isnan(command_msg_ptr->twist.angular.z))
156+
{
157+
reference_interfaces_[0] = command_msg_ptr->twist.linear.x;
158+
reference_interfaces_[1] = command_msg_ptr->twist.angular.z;
159+
}
160+
else
161+
{
162+
RCLCPP_WARN(logger, "Command message contains NaNs. Not updating reference interfaces.");
163+
}
164+
165+
previous_update_timestamp_ = time;
166+
167+
return controller_interface::return_type::OK;
168+
}
169+
170+
controller_interface::return_type DiffDriveController::update_and_write_commands(
171+
const rclcpp::Time & time, const rclcpp::Duration & period)
172+
{
173+
auto logger = get_node()->get_logger();
174+
if (get_lifecycle_state().id() == State::PRIMARY_STATE_INACTIVE)
175+
{
176+
if (!is_halted)
177+
{
178+
halt();
179+
is_halted = true;
180+
}
181+
return controller_interface::return_type::OK;
137182
}
138183

139184
// command may be limited further by SpeedLimit,
140185
// without affecting the stored twist command
141-
TwistStamped command = *last_command_msg_;
142-
double & linear_command = command.twist.linear.x;
143-
double & angular_command = command.twist.angular.z;
186+
double linear_command = reference_interfaces_[0];
187+
double angular_command = reference_interfaces_[1];
144188

145-
previous_update_timestamp_ = time;
189+
if(std::isnan(linear_command) || std::isnan(angular_command))
190+
{
191+
// NaNs occur when the controller is first initialized and the reference interfaces are not yet set
192+
return controller_interface::return_type::OK;
193+
}
146194

147195
// Apply (possibly new) multipliers:
148196
const double wheel_separation = params_.wheel_separation_multiplier * params_.wheel_separation;
@@ -239,22 +287,29 @@ controller_interface::return_type DiffDriveController::update(
239287
}
240288
}
241289

242-
auto & last_command = previous_commands_.back().twist;
243-
auto & second_to_last_command = previous_commands_.front().twist;
290+
double & last_linear = previous_two_commands_.back()[0];
291+
double & second_to_last_linear = previous_two_commands_.front()[0];
292+
double & last_angular = previous_two_commands_.back()[1];
293+
double & second_to_last_angular = previous_two_commands_.front()[1];
294+
244295
limiter_linear_.limit(
245-
linear_command, last_command.linear.x, second_to_last_command.linear.x, period.seconds());
296+
linear_command, last_linear, second_to_last_linear, period.seconds());
246297
limiter_angular_.limit(
247-
angular_command, last_command.angular.z, second_to_last_command.angular.z, period.seconds());
248-
249-
previous_commands_.pop();
250-
previous_commands_.emplace(command);
298+
angular_command, last_angular, second_to_last_angular, period.seconds());
299+
previous_two_commands_.pop();
300+
previous_two_commands_.push({{linear_command, angular_command}});
251301

252302
// Publish limited velocity
253303
if (publish_limited_velocity_ && realtime_limited_velocity_publisher_->trylock())
254304
{
255305
auto & limited_velocity_command = realtime_limited_velocity_publisher_->msg_;
256306
limited_velocity_command.header.stamp = time;
257-
limited_velocity_command.twist = command.twist;
307+
limited_velocity_command.twist.linear.x = linear_command;
308+
limited_velocity_command.twist.linear.y = 0.0;
309+
limited_velocity_command.twist.linear.z = 0.0;
310+
limited_velocity_command.twist.angular.x = 0.0;
311+
limited_velocity_command.twist.angular.y = 0.0;
312+
limited_velocity_command.twist.angular.z = angular_command;
258313
realtime_limited_velocity_publisher_->unlockAndPublish();
259314
}
260315

@@ -301,7 +356,7 @@ controller_interface::CallbackReturn DiffDriveController::on_configure(
301356
odometry_.setWheelParams(wheel_separation, left_wheel_radius, right_wheel_radius);
302357
odometry_.setVelocityRollingWindowSize(static_cast<size_t>(params_.velocity_rolling_window_size));
303358

304-
cmd_vel_timeout_ = std::chrono::milliseconds{static_cast<int>(params_.cmd_vel_timeout * 1000.0)};
359+
cmd_vel_timeout_ = rclcpp::Duration::from_seconds(params_.cmd_vel_timeout);
305360
publish_limited_velocity_ = params_.publish_limited_velocity;
306361

307362
// TODO(christophfroehlich) remove deprecated parameters
@@ -394,12 +449,15 @@ controller_interface::CallbackReturn DiffDriveController::on_configure(
394449
limited_velocity_publisher_);
395450
}
396451

397-
last_command_msg_ = std::make_shared<TwistStamped>();
398-
received_velocity_msg_ptr_.set([this](std::shared_ptr<TwistStamped> & stored_value)
399-
{ stored_value = last_command_msg_; });
400-
// Fill last two commands with default constructed commands
401-
previous_commands_.emplace(*last_command_msg_);
402-
previous_commands_.emplace(*last_command_msg_);
452+
const int nr_ref_itfs = 2;
453+
reference_interfaces_.resize(nr_ref_itfs, std::numeric_limits<double>::quiet_NaN());
454+
previous_two_commands_.push({{0.0, 0.0}}); //needs zeros (not NaN) to catch early accelerations
455+
previous_two_commands_.push({{0.0, 0.0}});
456+
457+
std::shared_ptr<TwistStamped> empty_msg_ptr = std::make_shared<TwistStamped>();
458+
reset_controller_reference_msg(empty_msg_ptr, get_node());
459+
received_velocity_msg_ptr_.reset();
460+
received_velocity_msg_ptr_.writeFromNonRT(empty_msg_ptr);
403461

404462
// initialize command subscriber
405463
velocity_command_subscriber_ = get_node()->create_subscription<TwistStamped>(
@@ -419,8 +477,22 @@ controller_interface::CallbackReturn DiffDriveController::on_configure(
419477
"time, this message will only be shown once");
420478
msg->header.stamp = get_node()->get_clock()->now();
421479
}
422-
received_velocity_msg_ptr_.set([msg](std::shared_ptr<TwistStamped> & stored_value)
423-
{ stored_value = std::move(msg); });
480+
481+
const auto current_time_diff = get_node()->now() - msg->header.stamp;
482+
483+
if (cmd_vel_timeout_ == rclcpp::Duration::from_seconds(0.0) || current_time_diff < cmd_vel_timeout_)
484+
{
485+
received_velocity_msg_ptr_.writeFromNonRT(msg);
486+
}
487+
else
488+
{
489+
RCLCPP_WARN(get_node()->get_logger(),
490+
"Ignoring the received message (timestamp %.10f) because it is older than "
491+
"the current time by %.10f seconds, which exceeds the allowed timeout (%.4f)",
492+
rclcpp::Time(msg->header.stamp).seconds(),
493+
current_time_diff.seconds(),
494+
cmd_vel_timeout_.seconds());
495+
}
424496
});
425497

426498
// initialize odometry publisher and message
@@ -498,6 +570,9 @@ controller_interface::CallbackReturn DiffDriveController::on_configure(
498570
controller_interface::CallbackReturn DiffDriveController::on_activate(
499571
const rclcpp_lifecycle::State &)
500572
{
573+
// Set default value in command
574+
reset_controller_reference_msg(*(received_velocity_msg_ptr_.readFromRT()), get_node());
575+
501576
const auto left_result =
502577
configure_side("left", params_.left_wheel_names, registered_left_wheel_handles_);
503578
const auto right_result =
@@ -546,6 +621,7 @@ controller_interface::CallbackReturn DiffDriveController::on_cleanup(
546621
{
547622
return controller_interface::CallbackReturn::ERROR;
548623
}
624+
received_velocity_msg_ptr_.reset();
549625

550626
return controller_interface::CallbackReturn::SUCCESS;
551627
}
@@ -564,16 +640,16 @@ bool DiffDriveController::reset()
564640
odometry_.resetOdometry();
565641

566642
// release the old queue
567-
std::queue<TwistStamped> empty;
568-
std::swap(previous_commands_, empty);
643+
std::queue<std::array<double, 2>> empty;
644+
std::swap(previous_two_commands_, empty);
569645

570646
registered_left_wheel_handles_.clear();
571647
registered_right_wheel_handles_.clear();
572648

573649
subscriber_is_active_ = false;
574650
velocity_command_subscriber_.reset();
575651

576-
received_velocity_msg_ptr_.set(nullptr);
652+
received_velocity_msg_ptr_.reset();
577653
is_halted = false;
578654
return true;
579655
}
@@ -649,9 +725,35 @@ controller_interface::CallbackReturn DiffDriveController::configure_side(
649725

650726
return controller_interface::CallbackReturn::SUCCESS;
651727
}
728+
729+
bool DiffDriveController::on_set_chained_mode(bool chained_mode)
730+
{
731+
// Always accept switch to/from chained mode
732+
return true || chained_mode;
733+
}
734+
735+
std::vector<hardware_interface::CommandInterface>
736+
DiffDriveController::on_export_reference_interfaces()
737+
{
738+
const int nr_ref_itfs = 2;
739+
reference_interfaces_.resize(nr_ref_itfs, std::numeric_limits<double>::quiet_NaN());
740+
std::vector<hardware_interface::CommandInterface> reference_interfaces;
741+
reference_interfaces.reserve(nr_ref_itfs);
742+
743+
reference_interfaces.push_back(hardware_interface::CommandInterface(
744+
get_node()->get_name(), std::string("linear/") + hardware_interface::HW_IF_VELOCITY,
745+
&reference_interfaces_[0]));
746+
747+
reference_interfaces.push_back(hardware_interface::CommandInterface(
748+
get_node()->get_name(), std::string("angular/") + hardware_interface::HW_IF_VELOCITY,
749+
&reference_interfaces_[1]));
750+
751+
return reference_interfaces;
752+
}
753+
652754
} // namespace diff_drive_controller
653755

654756
#include "class_loader/register_macro.hpp"
655757

656758
CLASS_LOADER_REGISTER_CLASS(
657-
diff_drive_controller::DiffDriveController, controller_interface::ControllerInterface)
759+
diff_drive_controller::DiffDriveController, controller_interface::ChainableControllerInterface)

0 commit comments

Comments
 (0)