@@ -36,6 +36,25 @@ constexpr auto DEFAULT_ODOMETRY_TOPIC = "~/odom";
3636constexpr 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+
3958namespace diff_drive_controller
4059{
4160using namespace std ::chrono_literals;
@@ -46,11 +65,11 @@ using hardware_interface::HW_IF_VELOCITY;
4665using lifecycle_msgs::msg::State;
4766
4867DiffDriveController::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(
498570controller_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
656758CLASS_LOADER_REGISTER_CLASS (
657- diff_drive_controller::DiffDriveController, controller_interface::ControllerInterface )
759+ diff_drive_controller::DiffDriveController, controller_interface::ChainableControllerInterface )
0 commit comments