Skip to content

Commit 3d14b42

Browse files
Make diff_drive_controller a ChainableControllerInterface (#1485)
1 parent cd6d5f8 commit 3d14b42

File tree

4 files changed

+545
-75
lines changed

4 files changed

+545
-75
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: 18 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;
@@ -73,6 +77,10 @@ class DiffDriveController : public controller_interface::ControllerInterface
7377
const rclcpp_lifecycle::State & previous_state) override;
7478

7579
protected:
80+
bool on_set_chained_mode(bool chained_mode) override;
81+
82+
std::vector<hardware_interface::CommandInterface> on_export_reference_interfaces() override;
83+
7684
struct WheelHandle
7785
{
7886
std::reference_wrapper<const hardware_interface::LoanedStateInterface> feedback;
@@ -100,7 +108,7 @@ class DiffDriveController : public controller_interface::ControllerInterface
100108
Odometry odometry_;
101109

102110
// Timeout to consider cmd_vel commands old
103-
std::chrono::milliseconds cmd_vel_timeout_{500};
111+
rclcpp::Duration cmd_vel_timeout_ = rclcpp::Duration::from_seconds(0.5);
104112

105113
std::shared_ptr<rclcpp::Publisher<nav_msgs::msg::Odometry>> odometry_publisher_ = nullptr;
106114
std::shared_ptr<realtime_tools::RealtimePublisher<nav_msgs::msg::Odometry>>
@@ -114,11 +122,9 @@ class DiffDriveController : public controller_interface::ControllerInterface
114122
bool subscriber_is_active_ = false;
115123
rclcpp::Subscription<TwistStamped>::SharedPtr velocity_command_subscriber_ = nullptr;
116124

117-
realtime_tools::RealtimeBox<std::shared_ptr<TwistStamped>> received_velocity_msg_ptr_{nullptr};
118-
std::shared_ptr<TwistStamped> last_command_msg_;
119-
120-
std::queue<TwistStamped> previous_commands_; // last two commands
125+
realtime_tools::RealtimeBuffer<std::shared_ptr<TwistStamped>> received_velocity_msg_ptr_{nullptr};
121126

127+
std::queue<std::array<double, 2>> previous_two_commands_;
122128
// speed limiters
123129
std::unique_ptr<SpeedLimiter> limiter_linear_;
124130
std::unique_ptr<SpeedLimiter> limiter_angular_;
@@ -139,6 +145,9 @@ class DiffDriveController : public controller_interface::ControllerInterface
139145

140146
bool reset();
141147
void halt();
148+
149+
private:
150+
void reset_buffers();
142151
};
143152
} // namespace diff_drive_controller
144153
#endif // DIFF_DRIVE_CONTROLLER__DIFF_DRIVE_CONTROLLER_HPP_

diff_drive_controller/src/diff_drive_controller.cpp

Lines changed: 133 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ using hardware_interface::HW_IF_POSITION;
4545
using hardware_interface::HW_IF_VELOCITY;
4646
using lifecycle_msgs::msg::State;
4747

48-
DiffDriveController::DiffDriveController() : controller_interface::ControllerInterface() {}
48+
DiffDriveController::DiffDriveController() : controller_interface::ChainableControllerInterface() {}
4949

5050
const char * DiffDriveController::feedback_type() const
5151
{
@@ -97,8 +97,8 @@ InterfaceConfiguration DiffDriveController::state_interface_configuration() cons
9797
return {interface_configuration_type::INDIVIDUAL, conf_names};
9898
}
9999

100-
controller_interface::return_type DiffDriveController::update(
101-
const rclcpp::Time & time, const rclcpp::Duration & period)
100+
controller_interface::return_type DiffDriveController::update_reference_from_subscribers(
101+
const rclcpp::Time & time, const rclcpp::Duration & /*period*/)
102102
{
103103
auto logger = get_node()->get_logger();
104104
if (get_lifecycle_state().id() == State::PRIMARY_STATE_INACTIVE)
@@ -111,31 +111,64 @@ controller_interface::return_type DiffDriveController::update(
111111
return controller_interface::return_type::OK;
112112
}
113113

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

118-
if (last_command_msg_ == nullptr)
116+
if (command_msg_ptr == nullptr)
119117
{
120118
RCLCPP_WARN(logger, "Velocity message received was a nullptr.");
121119
return controller_interface::return_type::ERROR;
122120
}
123121

124-
const auto age_of_last_command = time - last_command_msg_->header.stamp;
122+
const auto age_of_last_command = time - command_msg_ptr->header.stamp;
125123
// Brake if cmd_vel has timeout, override the stored command
126124
if (age_of_last_command > cmd_vel_timeout_)
127125
{
128-
last_command_msg_->twist.linear.x = 0.0;
129-
last_command_msg_->twist.angular.z = 0.0;
126+
reference_interfaces_[0] = 0.0;
127+
reference_interfaces_[1] = 0.0;
128+
}
129+
else if (
130+
std::isfinite(command_msg_ptr->twist.linear.x) &&
131+
std::isfinite(command_msg_ptr->twist.angular.z))
132+
{
133+
reference_interfaces_[0] = command_msg_ptr->twist.linear.x;
134+
reference_interfaces_[1] = command_msg_ptr->twist.angular.z;
135+
}
136+
else
137+
{
138+
RCLCPP_WARN_SKIPFIRST_THROTTLE(
139+
logger, *get_node()->get_clock(), cmd_vel_timeout_.seconds() * 1000,
140+
"Command message contains NaNs. Not updating reference interfaces.");
141+
}
142+
143+
previous_update_timestamp_ = time;
144+
145+
return controller_interface::return_type::OK;
146+
}
147+
148+
controller_interface::return_type DiffDriveController::update_and_write_commands(
149+
const rclcpp::Time & time, const rclcpp::Duration & period)
150+
{
151+
auto logger = get_node()->get_logger();
152+
if (get_lifecycle_state().id() == State::PRIMARY_STATE_INACTIVE)
153+
{
154+
if (!is_halted)
155+
{
156+
halt();
157+
is_halted = true;
158+
}
159+
return controller_interface::return_type::OK;
130160
}
131161

132162
// command may be limited further by SpeedLimit,
133163
// without affecting the stored twist command
134-
TwistStamped command = *last_command_msg_;
135-
double & linear_command = command.twist.linear.x;
136-
double & angular_command = command.twist.angular.z;
164+
double linear_command = reference_interfaces_[0];
165+
double angular_command = reference_interfaces_[1];
137166

138-
previous_update_timestamp_ = time;
167+
if (!std::isfinite(linear_command) || !std::isfinite(angular_command))
168+
{
169+
// NaNs occur on initialization when the reference interfaces are not yet set
170+
return controller_interface::return_type::OK;
171+
}
139172

140173
// Apply (possibly new) multipliers:
141174
const double wheel_separation = params_.wheel_separation_multiplier * params_.wheel_separation;
@@ -232,22 +265,27 @@ controller_interface::return_type DiffDriveController::update(
232265
}
233266
}
234267

235-
auto & last_command = previous_commands_.back().twist;
236-
auto & second_to_last_command = previous_commands_.front().twist;
237-
limiter_linear_->limit(
238-
linear_command, last_command.linear.x, second_to_last_command.linear.x, period.seconds());
239-
limiter_angular_->limit(
240-
angular_command, last_command.angular.z, second_to_last_command.angular.z, period.seconds());
268+
double & last_linear = previous_two_commands_.back()[0];
269+
double & second_to_last_linear = previous_two_commands_.front()[0];
270+
double & last_angular = previous_two_commands_.back()[1];
271+
double & second_to_last_angular = previous_two_commands_.front()[1];
241272

242-
previous_commands_.pop();
243-
previous_commands_.emplace(command);
273+
limiter_linear_->limit(linear_command, last_linear, second_to_last_linear, period.seconds());
274+
limiter_angular_->limit(angular_command, last_angular, second_to_last_angular, period.seconds());
275+
previous_two_commands_.pop();
276+
previous_two_commands_.push({{linear_command, angular_command}});
244277

245278
// Publish limited velocity
246279
if (publish_limited_velocity_ && realtime_limited_velocity_publisher_->trylock())
247280
{
248281
auto & limited_velocity_command = realtime_limited_velocity_publisher_->msg_;
249282
limited_velocity_command.header.stamp = time;
250-
limited_velocity_command.twist = command.twist;
283+
limited_velocity_command.twist.linear.x = linear_command;
284+
limited_velocity_command.twist.linear.y = 0.0;
285+
limited_velocity_command.twist.linear.z = 0.0;
286+
limited_velocity_command.twist.angular.x = 0.0;
287+
limited_velocity_command.twist.angular.y = 0.0;
288+
limited_velocity_command.twist.angular.z = angular_command;
251289
realtime_limited_velocity_publisher_->unlockAndPublish();
252290
}
253291

@@ -294,7 +332,7 @@ controller_interface::CallbackReturn DiffDriveController::on_configure(
294332
odometry_.setWheelParams(wheel_separation, left_wheel_radius, right_wheel_radius);
295333
odometry_.setVelocityRollingWindowSize(static_cast<size_t>(params_.velocity_rolling_window_size));
296334

297-
cmd_vel_timeout_ = std::chrono::milliseconds{static_cast<int>(params_.cmd_vel_timeout * 1000.0)};
335+
cmd_vel_timeout_ = rclcpp::Duration::from_seconds(params_.cmd_vel_timeout);
298336
publish_limited_velocity_ = params_.publish_limited_velocity;
299337

300338
// TODO(christophfroehlich) remove deprecated parameters
@@ -387,13 +425,6 @@ controller_interface::CallbackReturn DiffDriveController::on_configure(
387425
limited_velocity_publisher_);
388426
}
389427

390-
last_command_msg_ = std::make_shared<TwistStamped>();
391-
received_velocity_msg_ptr_.set([this](std::shared_ptr<TwistStamped> & stored_value)
392-
{ stored_value = last_command_msg_; });
393-
// Fill last two commands with default constructed commands
394-
previous_commands_.emplace(*last_command_msg_);
395-
previous_commands_.emplace(*last_command_msg_);
396-
397428
// initialize command subscriber
398429
velocity_command_subscriber_ = get_node()->create_subscription<TwistStamped>(
399430
DEFAULT_COMMAND_TOPIC, rclcpp::SystemDefaultsQoS(),
@@ -410,10 +441,26 @@ controller_interface::CallbackReturn DiffDriveController::on_configure(
410441
get_node()->get_logger(),
411442
"Received TwistStamped with zero timestamp, setting it to current "
412443
"time, this message will only be shown once");
413-
msg->header.stamp = get_node()->get_clock()->now();
444+
msg->header.stamp = get_node()->now();
445+
}
446+
447+
const auto current_time_diff = get_node()->now() - msg->header.stamp;
448+
449+
if (
450+
cmd_vel_timeout_ == rclcpp::Duration::from_seconds(0.0) ||
451+
current_time_diff < cmd_vel_timeout_)
452+
{
453+
received_velocity_msg_ptr_.writeFromNonRT(msg);
454+
}
455+
else
456+
{
457+
RCLCPP_WARN(
458+
get_node()->get_logger(),
459+
"Ignoring the received message (timestamp %.10f) because it is older than "
460+
"the current time by %.10f seconds, which exceeds the allowed timeout (%.4f)",
461+
rclcpp::Time(msg->header.stamp).seconds(), current_time_diff.seconds(),
462+
cmd_vel_timeout_.seconds());
414463
}
415-
received_velocity_msg_ptr_.set([msg](std::shared_ptr<TwistStamped> & stored_value)
416-
{ stored_value = std::move(msg); });
417464
});
418465

419466
// initialize odometry publisher and message
@@ -527,6 +574,7 @@ controller_interface::CallbackReturn DiffDriveController::on_deactivate(
527574
halt();
528575
is_halted = true;
529576
}
577+
reset_buffers();
530578
registered_left_wheel_handles_.clear();
531579
registered_right_wheel_handles_.clear();
532580
return controller_interface::CallbackReturn::SUCCESS;
@@ -556,21 +604,41 @@ bool DiffDriveController::reset()
556604
{
557605
odometry_.resetOdometry();
558606

559-
// release the old queue
560-
std::queue<TwistStamped> empty;
561-
std::swap(previous_commands_, empty);
607+
reset_buffers();
562608

563609
registered_left_wheel_handles_.clear();
564610
registered_right_wheel_handles_.clear();
565611

566612
subscriber_is_active_ = false;
567613
velocity_command_subscriber_.reset();
568614

569-
received_velocity_msg_ptr_.set(nullptr);
570615
is_halted = false;
571616
return true;
572617
}
573618

619+
void DiffDriveController::reset_buffers()
620+
{
621+
reference_interfaces_ = std::vector<double>(2, std::numeric_limits<double>::quiet_NaN());
622+
// Empty out the old queue. Fill with zeros (not NaN) to catch early accelerations.
623+
std::queue<std::array<double, 2>> empty;
624+
std::swap(previous_two_commands_, empty);
625+
previous_two_commands_.push({{0.0, 0.0}});
626+
previous_two_commands_.push({{0.0, 0.0}});
627+
628+
// Fill RealtimeBuffer with NaNs so it will contain a known value
629+
// but still indicate that no command has yet been sent.
630+
received_velocity_msg_ptr_.reset();
631+
std::shared_ptr<TwistStamped> empty_msg_ptr = std::make_shared<TwistStamped>();
632+
empty_msg_ptr->header.stamp = get_node()->now();
633+
empty_msg_ptr->twist.linear.x = std::numeric_limits<double>::quiet_NaN();
634+
empty_msg_ptr->twist.linear.y = std::numeric_limits<double>::quiet_NaN();
635+
empty_msg_ptr->twist.linear.z = std::numeric_limits<double>::quiet_NaN();
636+
empty_msg_ptr->twist.angular.x = std::numeric_limits<double>::quiet_NaN();
637+
empty_msg_ptr->twist.angular.y = std::numeric_limits<double>::quiet_NaN();
638+
empty_msg_ptr->twist.angular.z = std::numeric_limits<double>::quiet_NaN();
639+
received_velocity_msg_ptr_.writeFromNonRT(empty_msg_ptr);
640+
}
641+
574642
void DiffDriveController::halt()
575643
{
576644
const auto halt_wheels = [](auto & wheel_handles)
@@ -636,9 +704,35 @@ controller_interface::CallbackReturn DiffDriveController::configure_side(
636704

637705
return controller_interface::CallbackReturn::SUCCESS;
638706
}
707+
708+
bool DiffDriveController::on_set_chained_mode(bool chained_mode)
709+
{
710+
// Always accept switch to/from chained mode (without linting type-cast error)
711+
return true || chained_mode;
712+
}
713+
714+
std::vector<hardware_interface::CommandInterface>
715+
DiffDriveController::on_export_reference_interfaces()
716+
{
717+
const int nr_ref_itfs = 2;
718+
reference_interfaces_.resize(nr_ref_itfs, std::numeric_limits<double>::quiet_NaN());
719+
std::vector<hardware_interface::CommandInterface> reference_interfaces;
720+
reference_interfaces.reserve(nr_ref_itfs);
721+
722+
reference_interfaces.push_back(hardware_interface::CommandInterface(
723+
get_node()->get_name(), std::string("linear/") + hardware_interface::HW_IF_VELOCITY,
724+
&reference_interfaces_[0]));
725+
726+
reference_interfaces.push_back(hardware_interface::CommandInterface(
727+
get_node()->get_name(), std::string("angular/") + hardware_interface::HW_IF_VELOCITY,
728+
&reference_interfaces_[1]));
729+
730+
return reference_interfaces;
731+
}
732+
639733
} // namespace diff_drive_controller
640734

641735
#include "class_loader/register_macro.hpp"
642736

643737
CLASS_LOADER_REGISTER_CLASS(
644-
diff_drive_controller::DiffDriveController, controller_interface::ControllerInterface)
738+
diff_drive_controller::DiffDriveController, controller_interface::ChainableControllerInterface)

0 commit comments

Comments
 (0)