Skip to content

Commit 9b63076

Browse files
saikishormergify[bot]
authored andcommitted
Publish controller manager statistics to better introspect the timings (#2449)
(cherry picked from commit ddb7fa9)
1 parent 7096fc1 commit 9b63076

File tree

9 files changed

+479
-83
lines changed

9 files changed

+479
-83
lines changed

controller_manager/include/controller_manager/controller_manager.hpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -674,6 +674,21 @@ class ControllerManager : public rclcpp::Node
674674
rclcpp::Subscription<std_msgs::msg::String>::SharedPtr robot_description_subscription_;
675675
rclcpp::TimerBase::SharedPtr robot_description_notification_timer_;
676676

677+
struct ControllerManagerExecutionTime
678+
{
679+
double read_time = 0.0;
680+
double update_time = 0.0;
681+
double write_time = 0.0;
682+
double switch_time = 0.0;
683+
double total_time = 0.0;
684+
double switch_chained_mode_time = 0.0;
685+
double switch_perform_mode_time = 0.0;
686+
double deactivation_time = 0.0;
687+
double activation_time = 0.0;
688+
};
689+
690+
ControllerManagerExecutionTime execution_time_;
691+
677692
controller_manager::MovingAverageStatistics periodicity_stats_;
678693

679694
struct SwitchParams

controller_manager/include/controller_manager/controller_spec.hpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,12 @@
2424
#include <vector>
2525
#include "controller_interface/controller_interface_base.hpp"
2626
#include "hardware_interface/controller_info.hpp"
27-
#include "libstatistics_collector/moving_average_statistics/moving_average.hpp"
27+
#include "hardware_interface/types/statistics_types.hpp"
2828

2929
namespace controller_manager
3030
{
3131

32-
using MovingAverageStatistics =
33-
libstatistics_collector::moving_average_statistics::MovingAverageStatistics;
32+
using MovingAverageStatistics = ros2_control::MovingAverageStatistics;
3433
/// Controller Specification
3534
/**
3635
* This struct contains both a pointer to a given controller, \ref c, as well

controller_manager/src/controller_manager.cpp

Lines changed: 195 additions & 29 deletions
Large diffs are not rendered by default.

controller_manager/test/test_controller_manager.cpp

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -912,21 +912,21 @@ TEST_P(TestControllerManagerWithUpdateRates, per_controller_equal_and_higher_upd
912912
testing::AllOf(testing::Ge(0.7 / cm_update_rate), testing::Lt((1.6 / cm_update_rate))));
913913
ASSERT_EQ(
914914
test_controller->internal_counter,
915-
cm_->get_loaded_controllers()[0].execution_time_statistics->GetCount());
915+
cm_->get_loaded_controllers()[0].execution_time_statistics->get_count());
916916
ASSERT_EQ(
917917
test_controller->internal_counter - 1,
918-
cm_->get_loaded_controllers()[0].periodicity_statistics->GetCount())
918+
cm_->get_loaded_controllers()[0].periodicity_statistics->get_count())
919919
<< "The first update is not counted in periodicity statistics";
920920
EXPECT_THAT(
921-
cm_->get_loaded_controllers()[0].periodicity_statistics->Average(),
921+
cm_->get_loaded_controllers()[0].periodicity_statistics->get_average(),
922922
testing::AllOf(
923923
testing::Ge(0.90 * cm_->get_update_rate()), testing::Lt((1.05 * cm_->get_update_rate()))));
924924
EXPECT_THAT(
925-
cm_->get_loaded_controllers()[0].periodicity_statistics->Min(),
925+
cm_->get_loaded_controllers()[0].periodicity_statistics->get_min(),
926926
testing::AllOf(
927927
testing::Ge(0.70 * cm_->get_update_rate()), testing::Lt((1.2 * cm_->get_update_rate()))));
928928
EXPECT_THAT(
929-
cm_->get_loaded_controllers()[0].periodicity_statistics->Max(),
929+
cm_->get_loaded_controllers()[0].periodicity_statistics->get_max(),
930930
testing::AllOf(
931931
testing::Ge(0.75 * cm_->get_update_rate()), testing::Lt((2.0 * cm_->get_update_rate()))));
932932
loop_rate.sleep();
@@ -1085,22 +1085,22 @@ TEST_P(TestControllerUpdateRates, check_the_controller_update_rate)
10851085
actual_counter, testing::AnyOf(testing::Ge(exp_counter - 1), testing::Le(exp_counter + 1)));
10861086
ASSERT_EQ(
10871087
test_controller->internal_counter,
1088-
cm_->get_loaded_controllers()[0].execution_time_statistics->GetCount());
1088+
cm_->get_loaded_controllers()[0].execution_time_statistics->get_count());
10891089
ASSERT_EQ(
10901090
test_controller->internal_counter - 1,
1091-
cm_->get_loaded_controllers()[0].periodicity_statistics->GetCount())
1091+
cm_->get_loaded_controllers()[0].periodicity_statistics->get_count())
10921092
<< "The first update is not counted in periodicity statistics";
10931093
EXPECT_THAT(
1094-
cm_->get_loaded_controllers()[0].periodicity_statistics->Average(),
1094+
cm_->get_loaded_controllers()[0].periodicity_statistics->get_average(),
10951095
testing::AllOf(testing::Ge(0.92 * exp_periodicity), testing::Lt((1.05 * exp_periodicity))));
10961096
EXPECT_THAT(
1097-
cm_->get_loaded_controllers()[0].periodicity_statistics->Min(),
1097+
cm_->get_loaded_controllers()[0].periodicity_statistics->get_min(),
10981098
testing::AllOf(testing::Ge(0.75 * exp_periodicity), testing::Lt((1.2 * exp_periodicity))));
10991099
EXPECT_THAT(
1100-
cm_->get_loaded_controllers()[0].periodicity_statistics->Max(),
1100+
cm_->get_loaded_controllers()[0].periodicity_statistics->get_max(),
11011101
testing::AllOf(testing::Ge(0.75 * exp_periodicity), testing::Lt((2.0 * exp_periodicity))));
11021102
EXPECT_LT(
1103-
cm_->get_loaded_controllers()[0].execution_time_statistics->Average(),
1103+
cm_->get_loaded_controllers()[0].execution_time_statistics->get_average(),
11041104
50.0); // 50 microseconds
11051105
}
11061106
}
@@ -1231,22 +1231,22 @@ TEST_F(TestAsyncControllerUpdateRates, check_the_async_controller_update_rate_an
12311231
EXPECT_THAT(
12321232
actual_counter, testing::AnyOf(testing::Ge(exp_counter - 1), testing::Le(exp_counter + 1)));
12331233
EXPECT_THAT(
1234-
cm_->get_loaded_controllers()[0].execution_time_statistics->GetCount(),
1234+
cm_->get_loaded_controllers()[0].execution_time_statistics->get_count(),
12351235
testing::AnyOf(testing::Ge(exp_counter - 1), testing::Le(exp_counter)));
12361236
EXPECT_THAT(
1237-
cm_->get_loaded_controllers()[0].periodicity_statistics->GetCount(),
1237+
cm_->get_loaded_controllers()[0].periodicity_statistics->get_count(),
12381238
testing::AnyOf(testing::Ge(exp_counter - 1), testing::Le(exp_counter)));
12391239
EXPECT_THAT(
1240-
cm_->get_loaded_controllers()[0].periodicity_statistics->Average(),
1240+
cm_->get_loaded_controllers()[0].periodicity_statistics->get_average(),
12411241
testing::AllOf(testing::Ge(0.9 * exp_periodicity), testing::Lt((1.1 * exp_periodicity))));
12421242
EXPECT_THAT(
1243-
cm_->get_loaded_controllers()[0].periodicity_statistics->Min(),
1243+
cm_->get_loaded_controllers()[0].periodicity_statistics->get_min(),
12441244
testing::AllOf(testing::Ge(0.5 * exp_periodicity), testing::Lt((1.2 * exp_periodicity))));
12451245
EXPECT_THAT(
1246-
cm_->get_loaded_controllers()[0].periodicity_statistics->Max(),
1246+
cm_->get_loaded_controllers()[0].periodicity_statistics->get_max(),
12471247
testing::AllOf(testing::Ge(0.75 * exp_periodicity), testing::Lt((2.0 * exp_periodicity))));
12481248
EXPECT_LT(
1249-
cm_->get_loaded_controllers()[0].execution_time_statistics->Average(),
1249+
cm_->get_loaded_controllers()[0].execution_time_statistics->get_average(),
12501250
12000); // more or less 12 milliseconds considering the waittime in the controller
12511251
}
12521252
}

doc/introspection.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,15 @@ The topic ``~/introspection_data/full`` can be used to integrate with your custo
1919
.. note::
2020
If you have a high frequency of data, it is recommended to use the ``~/introspection_data/names`` and ``~/introspection_data/values`` topic. So, that the data transferred and stored is minimized.
2121

22+
Along with the above introspection data, the ``controller_manager`` also publishes the statistics of the execution time and periodicity of the read and write cycles of the hardware components and the update cycle of the controllers. This is done by registering the statistics of these variables and publishing them on the ``~/statistics`` topic.
23+
24+
All the registered variables are published over 3 topics: ``~/statistics/full``, ``~/statistics/names``, and ``~/statistics/values``.
25+
- The ``~/statistics/full`` topic publishes the full introspection data along with names and values in a single message. This can be useful to track or view variables and information from command line.
26+
- The ``~/statistics/names`` topic publishes the names of the registered variables. This topic contains the names of the variables registered. This is only published every time a a variables is registered and unregistered.
27+
- The ``~/statistics/values`` topic publishes the values of the registered variables. This topic contains the values of the variables registered.
28+
29+
This topic is mainly used to introspect the behaviour of the realtime loops, this is very crucial for hardware that need to meet strict deadlines and also to understand the which component of the ecosystem is consuming more time in the realtime loop.
30+
2231
How to introspect internal variables of controllers and hardware components
2332
============================================================================
2433

doc/release_notes.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ controller_manager
9595
* The controller manager will use a monotonic clock for triggering read-update-write cycles, but when the ``use_sim_time`` parameter is set to true, it will use the ROS Clock for triggering. When monotonic clock is being used, all the hardware components will receive the monotonic time in their read and write method, instead the controllers will always receive the ROS time in their update method irrespective of the clock being used. (`#2046 <https://github.com/ros-controls/ros2_control/pull/2046>`_).
9696
* The default strictness of the ``switch_controllers`` can now we be chosen using ROS 2 parameters. The default behaviour is still left to ``BEST_EFFORT`` (`#2168 <https://github.com/ros-controls/ros2_control/pull/2168>`_).
9797
* Parameter ``shutdown_on_initial_state_failure`` was added to avoid shutting down on hardware initial state failure (`#2230 <https://github.com/ros-controls/ros2_control/pull/2230>`_).
98+
* The controller manager now publishes ``~/statistics/names`` and ``~/statistics/values`` topics to introspect the execution time and periodicity of the different entities running in the realtime loop (`#2449 <https://github.com/ros-controls/ros2_control/pull/2449>`_).
9899

99100
hardware_interface
100101
******************

hardware_interface/include/hardware_interface/introspection.hpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,37 @@
1717
#ifndef HARDWARE_INTERFACE__INTROSPECTION_HPP_
1818
#define HARDWARE_INTERFACE__INTROSPECTION_HPP_
1919

20+
#include <string>
21+
22+
#include "hardware_interface/types/statistics_types.hpp"
2023
#include "pal_statistics/pal_statistics_macros.hpp"
2124
#include "pal_statistics/pal_statistics_utils.hpp"
2225

26+
namespace pal_statistics
27+
{
28+
template <>
29+
inline IdType customRegister(
30+
StatisticsRegistry & registry, const std::string & name,
31+
const libstatistics_collector::moving_average_statistics::StatisticData * variable,
32+
RegistrationsRAII * bookkeeping, bool enabled)
33+
{
34+
registry.registerVariable(name + "/max", &variable->max, bookkeeping, enabled);
35+
registry.registerVariable(name + "/min", &variable->min, bookkeeping, enabled);
36+
registry.registerVariable(name + "/average", &variable->average, bookkeeping, enabled);
37+
registry.registerVariable(
38+
name + "/standard_deviation", &variable->standard_deviation, bookkeeping, enabled);
39+
std::function<double()> sample_func = [variable]
40+
{ return static_cast<double>(variable->sample_count); };
41+
return registry.registerFunction(name + "/sample_count", sample_func, bookkeeping, enabled);
42+
}
43+
} // namespace pal_statistics
44+
2345
namespace hardware_interface
2446
{
2547
constexpr char DEFAULT_REGISTRY_KEY[] = "ros2_control";
2648
constexpr char DEFAULT_INTROSPECTION_TOPIC[] = "~/introspection_data";
49+
constexpr char CM_STATISTICS_KEY[] = "cm_execution_statistics";
50+
constexpr char CM_STATISTICS_TOPIC[] = "~/statistics";
2751

2852
#define REGISTER_ROS2_CONTROL_INTROSPECTION(ID, ENTITY) \
2953
REGISTER_ENTITY( \

0 commit comments

Comments
 (0)