|
| 1 | +General information about this repository, including legal information and known issues/limitations, are given in [README.md](../README.md) in the repository root. |
| 2 | + |
| 3 | +# The diagnostic_aggregator package |
| 4 | +This package contains the `aggregator_node`. |
| 5 | +It listens to the [`diagnostic_msgs/DiagnosticArray`](https://index.ros.org/p/diagnostic_msgs) messages on the `/diagnostics` topic and aggregates and published them on the `/diagnostics_agg` topic. |
| 6 | + |
| 7 | +One use case for this package is to aggregate the diagnostics of a robot. |
| 8 | +Aggregation means that the diagnostics of the robot are grouped by various aspects, like their location on the robot, their type, etc. |
| 9 | +This will allow you to easily see which part of the robot is causing problems. |
| 10 | + |
| 11 | +## Example |
| 12 | +In our example, we are looking at a robot with arms and legs. |
| 13 | +The robot has two of each, one on each side. |
| 14 | +The robot also 4 camera sensors, one left and one right and one in the front and one in the back. |
| 15 | +These are all the available diagnostic sources: |
| 16 | + |
| 17 | +``` |
| 18 | +/arms/left/motor |
| 19 | +/arms/right/motor |
| 20 | +/legs/left/motor |
| 21 | +/legs/right/motor |
| 22 | +/sensors/left/cam |
| 23 | +/sensors/right/cam |
| 24 | +/sensors/front/cam |
| 25 | +/sensors/rear/cam |
| 26 | +``` |
| 27 | + |
| 28 | +We want to group the diagnostics by |
| 29 | +- all sensors |
| 30 | +- all motors |
| 31 | +- left side of the robot |
| 32 | +- right side of the robot |
| 33 | + |
| 34 | +We can achieve that by creating a configuration file that looks like this (see [example_analyzers.yaml](example/example_analyzers.yaml)): |
| 35 | +``` yaml |
| 36 | +analyzers: |
| 37 | + ros__parameters: |
| 38 | + path: Aggregation |
| 39 | + arms: |
| 40 | + type: diagnostic_aggregator/GenericAnalyzer |
| 41 | + path: Arms |
| 42 | + startswith: [ '/arms' ] |
| 43 | + legs: |
| 44 | + type: diagnostic_aggregator/GenericAnalyzer |
| 45 | + path: Legs |
| 46 | + startswith: [ '/legs' ] |
| 47 | + sensors: |
| 48 | + type: diagnostic_aggregator/GenericAnalyzer |
| 49 | + path: Sensors |
| 50 | + startswith: [ '/sensors' ] |
| 51 | + motors: |
| 52 | + type: diagnostic_aggregator/GenericAnalyzer |
| 53 | + path: Motors |
| 54 | + contains: [ '/motor' ] |
| 55 | + topology: |
| 56 | + type: 'diagnostic_aggregator/AnalyzerGroup' |
| 57 | + path: Topology |
| 58 | + analyzers: |
| 59 | + left: |
| 60 | + type: diagnostic_aggregator/GenericAnalyzer |
| 61 | + path: Left |
| 62 | + contains: [ '/left' ] |
| 63 | + right: |
| 64 | + type: diagnostic_aggregator/GenericAnalyzer |
| 65 | + path: Right |
| 66 | + contains: [ '/right' ] |
| 67 | +``` |
| 68 | +
|
| 69 | +Based on this configuration, the [rqt_robot_monitor](https://index.ros.org/p/rqt_robot_monitor) will display the diagnostics information in a well-arranged manner as follows: |
| 70 | + |
| 71 | +
|
| 72 | +Note that it will also display the highest state per group to allow you to see at a glance which part of the robot is not working properly. |
| 73 | +For example in the above image, the left side of the robot is not working properly, because the left camera is in the `ERROR` state. |
| 74 | + |
| 75 | +# Analyzers |
| 76 | +The `aggregator_node` will load analyzers to process the diagnostics data. |
| 77 | +An analyzer is a plugin that inherits from the [`diagnostic_aggregator::Analyzer`](include/diagnostic_aggregator/analyzer.hpp) class. |
| 78 | +Analyzers must be implemented in packages that directly depend on [`pluginlib`](https://index.ros.org/p/pluginlib) and `diagnostic_aggregator`. |
| 79 | + |
| 80 | +The [`diagnostic_aggregator::Analyzer`](include/diagnostic_aggregator/analyzer.hpp) class is purely virtual and derived classes must implement the following methods: |
| 81 | + |
| 82 | +- `init()` - Analyzer is initialized with base path and namespace |
| 83 | +- `match()` - Returns true if the analyzer is interested in the status message |
| 84 | +- `analyze()` - Returns true if the analyzer will analyze the status message |
| 85 | +- `report()` - Returns results of analysis as vector of status messages |
| 86 | +- `getPath()` - Returns the prefix path of the analyzer (e.g., "/robot/motors/") |
| 87 | +- `getName()` - Returns the name of the analyzer (e.g., "Motors") |
| 88 | + |
| 89 | +Analyzers can choose the value of the error level of their output. |
| 90 | +Usually, the error level of the output is the highest error level of the input. |
| 91 | +The analyzers are responsible for setting the name of each item in the output correctly. |
| 92 | + |
| 93 | +# Using the aggregator_node |
| 94 | + |
| 95 | +## Configuration |
| 96 | + |
| 97 | +The `aggregator_node` can be configured at launch time like in this example: |
| 98 | +``` yaml |
| 99 | +pub_rate: 1.0 # Optional, defaults to 1.0 |
| 100 | +base_path: 'PRE' # Optional, defaults to "" |
| 101 | +analyzers: |
| 102 | + motors: |
| 103 | + type: PR2MotorsAnalyzer |
| 104 | + joints: |
| 105 | + type: GenericAnalyzer |
| 106 | + path: 'Joints' |
| 107 | + regex: 'Joint*' |
| 108 | +``` |
| 109 | + |
| 110 | +The `pub_rate` parameter is the rate at which the aggregated diagnostics will be published. |
| 111 | +The `base_path` parameter is the prefix that will be added to the name of each item in the output. |
| 112 | + |
| 113 | +Under the `analyzers` key, you can specify the analyzers that you want to use. |
| 114 | +Each analyzer must have a unique name. |
| 115 | +Under the name, you must specify the type of the analyzer. |
| 116 | +This must be the name of the class that implements the analyzer. |
| 117 | +Additional parameters depend on the type of the analyzer. |
| 118 | + |
| 119 | +Any diagnostic item that is not matched by any analyzer will be published by an "Other" analyzer. |
| 120 | +Items created by the "Other" analyzer will go stale after 5 seconds. |
| 121 | + |
| 122 | +## Launching |
| 123 | +You can launch the `aggregator_node` like this (see [example.launch.py.in](example/example.launch.py.in)): |
| 124 | +``` python |
| 125 | + aggregator = launch_ros.actions.Node( |
| 126 | + package='diagnostic_aggregator', |
| 127 | + executable='aggregator_node', |
| 128 | + output='screen', |
| 129 | + parameters=[analyzer_params_filepath]) |
| 130 | + return launch.LaunchDescription([ |
| 131 | + aggregator, |
| 132 | + ]) |
| 133 | +``` |
| 134 | + |
| 135 | +# Basic analyzers |
| 136 | +The `diagnostic_aggregator` package provides a few basic analyzers that you can use to aggregate your diagnostics. |
| 137 | + |
| 138 | +## GenericAnalyzer |
| 139 | +The [`diagnostic_aggregator::GenericAnalyzer`](include/diagnostic_aggregator/generic_analyzer.hpp) class is a basic analyzer that can be configured to match diagnostics based on their name. |
| 140 | +By defining a `path` parameter, you can specify the prefix that will be added to the name of each item in the output. |
| 141 | +This way you can group diagnostics by their location or other aspects as demonstrated in the [example](#example). |
| 142 | + |
| 143 | +## AnalyzerGroup |
| 144 | +The [`diagnostic_aggregator::AnalyzerGroup`](include/diagnostic_aggregator/analyzer_group.hpp) class is a basic analyzer that can be configured to group other analyzers. |
| 145 | +It has itself an `analyzers` parameter that can be filled with other analyzers to group them. |
| 146 | + |
| 147 | +An example of this is (see [example_analyzers.yaml](diagnostic_aggregator/example/example_analyzers.yaml)): |
| 148 | +``` yaml |
| 149 | + topology: |
| 150 | + type: 'diagnostic_aggregator/AnalyzerGroup' |
| 151 | + path: Topology |
| 152 | + analyzers: |
| 153 | + left: |
| 154 | + type: diagnostic_aggregator/GenericAnalyzer |
| 155 | + path: Left |
| 156 | + contains: [ '/left' ] |
| 157 | + right: |
| 158 | + type: diagnostic_aggregator/GenericAnalyzer |
| 159 | + path: Right |
| 160 | + contains: [ '/right' ] |
| 161 | +``` |
| 162 | + |
| 163 | +## DiscardAnalyzer |
| 164 | +The [`diagnostic_aggregator::DiscardAnalyzer`](include/diagnostic_aggregator/discard_analyzer.hpp) class is a basic analyzer that discards all diagnostics that match it. |
| 165 | +This can be useful if you want to ignore some diagnostics. |
| 166 | + |
| 167 | +## IgnoreAnalyzer |
| 168 | +The [`diagnostic_aggregator::IgnoreAnalyzer`](include/diagnostic_aggregator/ignore_analyzer.hpp) will ignore all parameters in its namespace and not match anything. |
| 169 | + |
| 170 | +The difference between the `DiscardAnalyzer` and the `IgnoreAnalyzer` is that the `DiscardAnalyzer` will match diagnostics and discard them, while the `IgnoreAnalyzer` will not match anything. |
| 171 | +This means that things that are ignored by the `IgnoreAnalyzer` will still be published in the "Other" analyzer, while things that are discarded by the `DiscardAnalyzer` will not be published at all. |
| 172 | + |
| 173 | +# ROS API |
| 174 | + ## `aggregator_node` |
| 175 | + |
| 176 | +### Subscribed Topics |
| 177 | +- `diagnostics` ([diagnostic_msgs/DiagnosticArray](https://index.ros.org/p/diagnostic_msgs)) - The diagnostics to be aggregated |
| 178 | + |
| 179 | +### Published Topics |
| 180 | +- `diagnostics_agg` ([diagnostic_msgs/DiagnosticArray](https://index.ros.org/p/diagnostic_msgs)) - The aggregated diagnostics |
| 181 | +- `diagnostics_toplevel_state` ([diagnostic_msgs/DiagnosticStatus](https://index.ros.org/p/diagnostic_msgs)) - The highest state of the aggregated diagnostics |
| 182 | + |
| 183 | +### Parameters |
| 184 | +- `pub_rate` (double, default: 1.0) - The rate at which the aggregated diagnostics will be published |
| 185 | +- `base_path` (string, default: "") - The prefix that will be added to the name of each item in the output |
| 186 | +- `analyzers` (map, default: {}) - The analyzers that will be used to aggregate the diagnostics |
| 187 | + |
| 188 | +# Tutorials |
| 189 | +TODO: Port tutorials #contributions-welcome |
0 commit comments