@@ -36,6 +36,25 @@ constexpr auto DEFAULT_ODOMETRY_TOPIC = "~/odom";
36
36
constexpr auto DEFAULT_TRANSFORM_TOPIC = " /tf" ;
37
37
} // namespace
38
38
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
+
39
58
namespace diff_drive_controller
40
59
{
41
60
using namespace std ::chrono_literals;
@@ -46,11 +65,11 @@ using hardware_interface::HW_IF_VELOCITY;
46
65
using lifecycle_msgs::msg::State;
47
66
48
67
DiffDriveController::DiffDriveController ()
49
- : controller_interface::ControllerInterface (),
68
+ : controller_interface::ChainableControllerInterface (),
50
69
// dummy limiter, will be created in on_configure
51
70
// 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())
54
73
{
55
74
}
56
75
@@ -104,8 +123,8 @@ InterfaceConfiguration DiffDriveController::state_interface_configuration() cons
104
123
return {interface_configuration_type::INDIVIDUAL, conf_names};
105
124
}
106
125
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*/ )
109
128
{
110
129
auto logger = get_node ()->get_logger ();
111
130
if (get_lifecycle_state ().id () == State::PRIMARY_STATE_INACTIVE)
@@ -118,31 +137,60 @@ controller_interface::return_type DiffDriveController::update(
118
137
return controller_interface::return_type::OK;
119
138
}
120
139
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 ());
124
141
125
- if (last_command_msg_ == nullptr )
142
+ if (command_msg_ptr == nullptr )
126
143
{
127
144
RCLCPP_WARN (logger, " Velocity message received was a nullptr." );
128
145
return controller_interface::return_type::ERROR;
129
146
}
130
147
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 ;
132
149
// Brake if cmd_vel has timeout, override the stored command
133
150
if (age_of_last_command > cmd_vel_timeout_)
134
151
{
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;
137
182
}
138
183
139
184
// command may be limited further by SpeedLimit,
140
185
// 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 ];
144
188
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
+ }
146
194
147
195
// Apply (possibly new) multipliers:
148
196
const double wheel_separation = params_.wheel_separation_multiplier * params_.wheel_separation ;
@@ -239,22 +287,29 @@ controller_interface::return_type DiffDriveController::update(
239
287
}
240
288
}
241
289
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
+
244
295
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 ());
246
297
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}});
251
301
252
302
// Publish limited velocity
253
303
if (publish_limited_velocity_ && realtime_limited_velocity_publisher_->trylock ())
254
304
{
255
305
auto & limited_velocity_command = realtime_limited_velocity_publisher_->msg_ ;
256
306
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;
258
313
realtime_limited_velocity_publisher_->unlockAndPublish ();
259
314
}
260
315
@@ -301,7 +356,7 @@ controller_interface::CallbackReturn DiffDriveController::on_configure(
301
356
odometry_.setWheelParams (wheel_separation, left_wheel_radius, right_wheel_radius);
302
357
odometry_.setVelocityRollingWindowSize (static_cast <size_t >(params_.velocity_rolling_window_size ));
303
358
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 ) ;
305
360
publish_limited_velocity_ = params_.publish_limited_velocity ;
306
361
307
362
// TODO(christophfroehlich) remove deprecated parameters
@@ -394,12 +449,15 @@ controller_interface::CallbackReturn DiffDriveController::on_configure(
394
449
limited_velocity_publisher_);
395
450
}
396
451
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);
403
461
404
462
// initialize command subscriber
405
463
velocity_command_subscriber_ = get_node ()->create_subscription <TwistStamped>(
@@ -419,8 +477,22 @@ controller_interface::CallbackReturn DiffDriveController::on_configure(
419
477
" time, this message will only be shown once" );
420
478
msg->header .stamp = get_node ()->get_clock ()->now ();
421
479
}
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
+ }
424
496
});
425
497
426
498
// initialize odometry publisher and message
@@ -498,6 +570,9 @@ controller_interface::CallbackReturn DiffDriveController::on_configure(
498
570
controller_interface::CallbackReturn DiffDriveController::on_activate (
499
571
const rclcpp_lifecycle::State &)
500
572
{
573
+ // Set default value in command
574
+ reset_controller_reference_msg (*(received_velocity_msg_ptr_.readFromRT ()), get_node ());
575
+
501
576
const auto left_result =
502
577
configure_side (" left" , params_.left_wheel_names , registered_left_wheel_handles_);
503
578
const auto right_result =
@@ -546,6 +621,7 @@ controller_interface::CallbackReturn DiffDriveController::on_cleanup(
546
621
{
547
622
return controller_interface::CallbackReturn::ERROR;
548
623
}
624
+ received_velocity_msg_ptr_.reset ();
549
625
550
626
return controller_interface::CallbackReturn::SUCCESS;
551
627
}
@@ -564,16 +640,16 @@ bool DiffDriveController::reset()
564
640
odometry_.resetOdometry ();
565
641
566
642
// 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);
569
645
570
646
registered_left_wheel_handles_.clear ();
571
647
registered_right_wheel_handles_.clear ();
572
648
573
649
subscriber_is_active_ = false ;
574
650
velocity_command_subscriber_.reset ();
575
651
576
- received_velocity_msg_ptr_.set ( nullptr );
652
+ received_velocity_msg_ptr_.reset ( );
577
653
is_halted = false ;
578
654
return true ;
579
655
}
@@ -649,9 +725,35 @@ controller_interface::CallbackReturn DiffDriveController::configure_side(
649
725
650
726
return controller_interface::CallbackReturn::SUCCESS;
651
727
}
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
+
652
754
} // namespace diff_drive_controller
653
755
654
756
#include " class_loader/register_macro.hpp"
655
757
656
758
CLASS_LOADER_REGISTER_CLASS (
657
- diff_drive_controller::DiffDriveController, controller_interface::ControllerInterface )
759
+ diff_drive_controller::DiffDriveController, controller_interface::ChainableControllerInterface )
0 commit comments