Skip to content

Commit 453431d

Browse files
authored
Update HWStatus draft from Soham (#68)
1 parent 170a0f1 commit 453431d

2 files changed

Lines changed: 123 additions & 65 deletions

File tree

design_drafts/hardware_status.md

Lines changed: 123 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
This proposal is an attempt at a standardized way for hardware components in `ros2_control` to report their status.
66

7-
Right now, if you're writing a hardware interface, how you report things like health, errors, or connectivity is pretty much up to you. This usually means everyone rolls their own custom messages. While that works for a single project, it makes it really tough to build generic, reusable tools on top of `ros2_control` (and even internally!). This proposal is a first-pass attempt at defining a generic `HardwareStatus` message. The main goal is to find a good balance between a structured, predictable format that tools can rely on, and the flexibility needed to report all the weird, wonderful, and specific details of different hardware.
7+
Right now, if you're writing a hardware component, how you report things like health, errors, or connectivity is pretty much up to you. This usually means everyone rolls their own custom messages. While that works for a single project, it makes it really tough to build generic, reusable tools on top of `ros2_control` (and even internally!). This proposal is a first-pass attempt at defining a generic `HardwareStatus` message. The main goal is to find a good balance between a structured, predictable format that tools can rely on, and the flexibility needed to report all the weird, wonderful, and specific details of different hardware.
88

99
This is very much a draft to get the conversation started, not a final solution!
1010

@@ -14,17 +14,24 @@ Here's a diagram I put together to visualize it:
1414

1515
Let's discuss this in slightly more detail.
1616

17+
## 0. Note on Terminology
18+
- Hardware Component: A Hardware Interface written for a Single Component, currently can be of form `System`, `Actuator` or `Sensor`, and have multiple sub-components.
19+
- Ex - "pal_arm"
20+
- Device: A sub-component of a Hardware Component.
21+
- Ex - "base_motor"
22+
1723
## 1. The Idea: Structured vs. Unstructured
1824

1925
The core idea is to split status reporting into two complementary parts.
2026

21-
1. **Structured Status:**
22-
- A fixed set of fields covering \~80% of common hardware needs—machine-readable, reliable, and directly consumable by controllers, watchdogs, automation tools and even for us internally.
23-
- A compact, general-purpose block of enums and identifiers. If a device can’t fill one of these fields, it simply reports `UNKNOWN`.
27+
1. **Structured, Standards-Based Status:**
28+
- A fixed set of fields covering \~80% of common hardware needs - machine-readable, reliable, and directly consumable by controllers, watchdogs, automation tools and even for us internally.
29+
- A collection of status messages, where each message type corresponds to a specific industry standard (e.g., `CANopenState`), providing a machine-readable and reliable format.
30+
- A device in a hardware component populates only the status blocks relevant to it within a single, device-specific message(`HardwareDeviceStatus`), aggregates it into one message(`HardwareStatus`) which containes all the devices in the hardware, covering the common hardware needs for controllers, watchdogs, and automation tools.
2431

25-
2. **Unstructured Status:**
26-
- A free-form array of key/value diagnostics for everything elsegeared toward logs, dashboards, and human inspection only.
27-
- A slower, richer stream of `diagnostic_msgs/KeyValue[]`, strictly for debugging and UI, ideally not parsed by control loops.
32+
2. **Unstructured Status:**
33+
- A free-form array of key/value diagnostics for everything else-geared toward logs, dashboards, and human inspection only.
34+
- A slower, richer stream of `diagnostic_msgs/KeyValue[]`, strictly for debugging and UI, ideally not parsed by control loops.
2835

2936
## 2. Example Message Topology
3037

@@ -37,37 +44,68 @@ We separate **real-time status** (fast, small) from **detailed diagnostics** (bu
3744

3845
## 3. Structured Status: `HardwareStatus`
3946

47+
The foundation of this approach is the `HardwareStatus` message. A single publisher per hardware component would publish `HardwareStatus` messages on the `/hardware_status` topic , each message is an array of `HardwareDeviceStatus` messages which contain the standard separated messages of a single device in the hardware component.
48+
4049
```
4150
# control_msgs/msg/HardwareStatus
4251
4352
std_msgs/Header header # timestamp + frame_id (optional)
44-
string hardware_id # unique per‐instance, e.g. "left_wheel/driver"
53+
string hardware_id # unique per‐hardware-component, ideally the name of the hardware derived from HardwareInfo e.g. "pal_arm"
54+
55+
# --- Device Status Aggregation ---------------------------------
56+
# An array containing the status of individual devices in the hardware component
57+
HardwareDeviceStatus[] hardware_device_states
58+
```
59+
```
60+
# control_msgs/msg/HardwareDeviceStatus
61+
string device_id # unique per-device, e.g. "base_motor"
62+
63+
# --- Standard-Specific States --------------------------------------
64+
# States populated based on the standards relevant to this device.
65+
# A device will only fill the arrays for the standards it implements, rest will be empty
66+
GenericState[] generic_hardware_status
67+
CANopenState[] canopen_states
68+
EtherCATState[] ethercat_states
69+
VDA5050State[] vda5050_states
70+
```
71+
72+
### 3.1. Standardized State Messages
73+
74+
Below are the proposed initial standard-specific messages, based on widely used industrial standards. Additions and opinions here would be really appreciated!
75+
76+
---
77+
78+
**`ros2_control` Generic State**
79+
80+
This message encapsulates the general-purpose status fields, serving as a baseline for any hardware component.
4581

46-
# ——— Health & Error ——————————————————————————————————————————————
82+
```
83+
# control_msgs/msg/GenericState
84+
85+
# --- Health & Error ----------------------------------------------
4786
uint8 health_status # see HealthStatus enum
48-
uint8[] error_domain # Array of device errors, because hardware can throw more than one, see ErrorDomain enum
87+
uint8[] error_domain # Array of device errors, see ErrorDomain enum
4988
50-
# ——— Operational State ———————————————————————————————————————————
89+
# --- Operational State -------------------------------------------
5190
uint8 operational_mode # see ModeStatus enum
5291
uint8 power_state # see PowerState enum
5392
uint8 connectivity_status # see ConnectivityStatus enum
5493
55-
# ——— Vendor & Version Info ————————————————————————————————————————
94+
# --- Vendor & Version Info ----------------------------------------
5695
string manufacturer # e.g. "Bosch"
5796
string model # e.g. "Lidar-XYZ-v2"
5897
string firmware_version # e.g. "1.2.3"
5998
60-
# ——— Optional Details for Context —————————————————————————————————
99+
# --- Optional Details for Context ---------------------------------
61100
# Provides specific quantitative values related to the enums above.
62101
# e.g., for power_state, could have {key: "voltage", value: "24.1"}
63102
# e.g., for connectivity, could have {key: "signal_strength", value: "-55dBm"}
64103
diagnostic_msgs/KeyValue[] state_details
65104
```
66105

67-
### 3.1. Enums
68-
106+
#### `ROS2ControlState` Enums
69107
```
70-
# control_msgs/msg/HardwareStatus (continued)
108+
# control_msgs/msg/GenericState (enums)
71109
72110
# High-level health
73111
uint8 HEALTH_UNKNOWN=0
@@ -90,10 +128,7 @@ uint8 EMERGENCY_STOP_HW # state of the emergency stop hardware (i.e. e-stop butt
90128
uint8 EMERGENCY_STOP_SW # state of the emergency stop software system (over travel, pinch point)
91129
uint8 PROTECTIVE_STOP_HW # state of the protective stop hardware (i.e. safety field state)
92130
uint8 PROTECTIVE_STOP_SW # state of the software protective stop
93-
# Some protective stop errors need to be acknowledged before the hardware can reactivate
94-
# see https://docs.universal-robots.com/Universal_Robots_ROS2_Documentation/doc/ur_robot_driver/ur_robot_driver/doc/dashboard_client.html#unlock-protective-stop-std-srvs-trigger
95131
uint8 SAFETY_STOP
96-
# Some hardware requires calibration on startup (for example a linear rail or quadruped)
97132
unit8 CALIBRATION_REQUIRED
98133
99134
@@ -133,27 +168,72 @@ uint8 CONNECT_FAILURE =3
133168
uint8 CONNECTION_SLOW # to tell the controlling system it is struggling to communicate at rate
134169
```
135170

136-
#### 3.2. A Note on a Future Addition
171+
---
137172

138-
A potential limitation of the single-value enums above is that a component can only report one state per category at a time. Consider the `error_domain`: what happens if a hardware fault (`ERROR_HW`) immediately causes a communication failure (`ERROR_COMM`)? With the current design, the hardware driver must choose to (or is limited to) report only one.
139-
That or return an array of errors and let mission control sort out the correct action to recover.
173+
**CANopen State**
140174

141-
A potential solution for this in a future iteration would be to define some enums as **bitfields**. This would involve assigning values as powers of 2, allowing multiple states to be combined using a bitwise `OR` operation.
175+
Reports state according to CiA 301 and CiA 402, common for motor drives and I/O.
176+
- **Source:** [CAN in Automation (CiA)](https://www.can-cia.org/) - CiA 301 & 402 specifications.
142177

143-
For example, the `ErrorDomain` enum could be redefined as a bitmask:
144178
```
145-
# Example ErrorDomain as a bitfield (why only an 8 bit number?)
146-
uint8 ERROR_NONE = 0 # 0b00000000
147-
uint8 ERROR_HW = 1 # 0b00000001
148-
uint8 ERROR_FW = 2 # 0b00000010
149-
uint8 ERROR_COMM = 4 # 0b00000100
150-
uint8 ERROR_POWER = 8 # 0b00001000
151-
# ... up to 4 more flags
179+
# control_msgs/msg/CANopenState
180+
181+
uint8 node_id # The CANopen node ID of the device
182+
183+
# --- CiA 301 State -------------------------------------------------
184+
uint8 nmt_state # Network Management state (e.g., OPERATIONAL)
185+
186+
# --- CiA 402 State (for drives) ------------------------------------
187+
uint8 dsp_402_state # Drive state machine state (e.g., OPERATION_ENABLED)
188+
189+
# --- Error Reporting -----------------------------------------------
190+
uint32 last_emcy_code # Last Emergency (EMCY) error code received
152191
```
153192

154-
A publisher could then report both a hardware and power fault simultaneously by setting the value to `ERROR_HW | ERROR_POWER` (which is `9`, or `0b00001001`). A subscriber could then check for a specific error using a bitwise `AND` (e.g., `if (status.error_domain & ERROR_HW)`).
193+
---
194+
195+
**EtherCAT State**
196+
197+
Reports the EtherCAT slave state according to the EtherCAT State Machine (ESM).
198+
- **Source:** [EtherCAT Technology Group (ETG)](https://www.ethercat.org/en/downloads.html) - ETG.1000.4 EtherCAT Protocol Specifications.
199+
200+
```
201+
# control_msgs/msg/EtherCATState
155202
156-
The primary trade-off is that we would be limited by the size of the enum's underlying type. A `uint8` allows for exactly 8 unique flags. While this may be sufficient for now, it's a constraint to keep in mind as we finalize this design. We can add this if we hear from the community that this is needed.
203+
uint16 slave_position # Position of the slave on the bus (0, 1, 2...)
204+
string vendor_id # Unique vendor identifier
205+
string product_code # Unique product code for the device
206+
207+
# --- EtherCAT State Machine (ESM) ----------------------------------
208+
uint8 al_state # Application Layer state (INIT, PREOP, SAFEOP, OP)
209+
bool has_error # True if the slave is in an error state
210+
uint16 al_status_code # AL Status Code indicating the reason for an error
211+
```
212+
213+
---
214+
215+
**VDA5050 State**
216+
217+
For AGVs and AMRs compliant with VDA5050, this provides a snapshot of the vehicle's high-level status.
218+
- **Source:** [Verband der Automobilindustrie (VDA)](https://github.com/VDA5050/VDA5050) - VDA 5050 Specification.
219+
220+
```
221+
# control_msgs/msg/VDA5050State
222+
223+
# --- Order and Action Status ---------------------------------------
224+
string order_id # ID of the currently executed order
225+
string action_status # e.g., RUNNING, PAUSED, FINISHED, FAILED
226+
uint32 last_node_id # ID of the last reached node in the topology
227+
228+
# --- Vehicle State -------------------------------------------------
229+
bool driving # True if the vehicle's drives are active
230+
float64 battery_charge # Current battery charge in percent
231+
string operating_mode # e.g., MANUAL, AUTOMATIC, SERVICE
232+
233+
# --- Error Reporting -----------------------------------------------
234+
string error_type
235+
string error_description
236+
```
157237

158238
## 4. Unstructured Diagnostics: `HardwareDiagnostics`
159239

@@ -178,60 +258,38 @@ KeyValue[] entries # diagnostic_msgs/KeyValue[]
178258
> ```
179259
180260
## 5. Open Questions & Discussion
261+
1. Is the current list of standardized state messages (`CANopen`, `EtherCAT`, `VDA5050`, `ISO10218`) a good starting point? Are there other non-proprietary standards that are critical to include?
262+
2. Is this whole approach overly complicated? It would be good to avoid that pitfall.
181263
182-
1. Could we reuse `lifecycle_msgs/State` for `operational_mode`, or is a dedicated enum preferable for clarity?
183-
2. I left some question marks in the diagrams, any categories we are missing?
184-
3. Should `HardwareStatus` include a short `string error_message`, or strictly push error details into diagnostics only?
185-
4. Also another thing, maybe we use one big message (`control_msgs/HardwareStatus`) to make it simpler rather than publish structured vs. unstructured data on separate topics (`/hardware_status` and `/hardware_diagnostics`)?
186-
5. And the main questions that I have, Is this whole approach overly complicated, let's avoid that pitfall.
264+
## 6. Alternative Publishing Strategies
187265
188-
Looking forward to hearing what everyone thinks!
266+
While this proposal centers on a single topic with an array of device statuses, it's worth discussing the trade-offs of other possible architectures. How else could we structure the flow of status information?
189267
190-
## Hardware Status Interface
191-
What does configuring the Hardware Status (per hardware because a mobile_base is likely different than the arm mounted on top of it) look like?
192-
Should we have blocks of state (i.e. standardized messages) that can be added together if the hardware offers X, Y and Z features?
193-
(For example my robot arm has a `safety interface` for e-stop/p-stop and a `hardware_status` interface to report power, operating mode and ...)
194-
What does it look like at the interface level? Is there a separate read (and maybe write) method for status reporting and reconfiguration?
195-
For example standard safety status (E-stop, P-stop), operating mode, [battery state](https://docs.ros2.org/foxy/api/sensor_msgs/msg/BatteryState.html).
196-
197-
JointState has been the standard ROS2 control works with. What about GPIO, SafetyStatus, BatteryState, .... these are interfaces that hardware frequently provides.
198-
What if ros2_control made a set of messages to standardize it's interfaces for each subcategory?
199-
[SensorMsgs](https://docs.ros2.org/foxy/api/sensor_msgs/index-msg.html) is a start of what we need.
200-
For example the UR controller exposes lots of interfaces via [GPIO](https://github.com/UniversalRobots/Universal_Robots_ROS2_Description/blob/85d2ad8d1526ee6c0f21dca94e1e697c83706b71/urdf/ur.ros2_control.xacro#L294-L311) but not in a standardized way so if someone wanted to control it and then switch robots their codebase would likely need to change to handle auxiliary control and monitoring.
201-
202-
Some errors or states will be set as the hardware stops functioning.
203-
Should the status broadcaster hold and continue to publish last known state?
204-
Should the status broadcaster offer statistics on hardware DEACTIVATE/ERROR and ACTIVATIONS?
205-
Lots of industrial applications would like to know how many e-stops, number of controller errors/faults, ____ per shift, week or some period of time
206-
Could this open the option for custom or standard controllers to monitor and keep the system healthy? i.e. automatic arm fault reset controller,
268+
- **Per Device Messages**
269+
- One issue I see with the current aggregated status message approach is that it seems a tad bit complicated for simple systems, what if a hardware component has only 1 actuator?
270+
- Then what if, instead of a single aggregated topic, each device in a hardware component published its own `HardwareDeviceStatus` message on the same `/hardware_status` topic which will now be of the type `HardwareDeviceStatus`
271+
- Then receivers just listen to the same `/hardware_status` topic as before, but just have to parse the `device_id` to see if the data is relevant, and similarly, publishers have to also only fill in the `HardwareDeviceStatus` message and send it without need of aggregation
207272
273+
## References
208274
Links of hardware interfaces and their attempt to convey hardware status and support other control modes
209-
210275
### UR
211276
[hardware_interface](https://github.com/UniversalRobots/Universal_Robots_ROS2_Driver/blob/868f240bc8578ebfa1d19b94f8a6a1ad62fa0bd1/ur_robot_driver/src/hardware_interface.cpp#L266-L270)
212277
[SafetyMode.msg](https://github.com/UniversalRobots/Universal_Robots_ROS2_Driver/blob/main/ur_dashboard_msgs/msg/SafetyMode.msg)
213278
[RobotMode.msg](https://github.com/UniversalRobots/Universal_Robots_ROS2_Driver/blob/main/ur_dashboard_msgs/msg/RobotMode.msg)
214279
[control.xacro](https://github.com/UniversalRobots/Universal_Robots_ROS2_Description/blob/85d2ad8d1526ee6c0f21dca94e1e697c83706b71/urdf/ur.ros2_control.xacro#L294-L311)
215-
216280
### Kuka
217281
[hardware_interface](https://github.com/lbr-stack/lbr_fri_ros2_stack/blob/f2784b86e5975eddc9b5eab901baaca329306653/lbr_ros2_control/include/lbr_ros2_control/system_interface_type_values.hpp#L8-L27)
218-
219282
### Kinova
220283
[fault_reset controller](https://github.com/Kinovarobotics/ros2_kortex/blob/main/kortex_description/arms/gen3/7dof/config/ros2_controllers.yaml#L17-L18) to report and reset faults.
221284
[twist_controller](https://github.com/Kinovarobotics/ros2_kortex/blob/309f9c9d4a277970e542e5ac1fe260ced0630f65/kortex_description/arms/gen3/7dof/config/ros2_controllers.yaml#L11-L12)
222-
223285
### Dynamixel
224286
[hardware_interface](https://github.com/ROBOTIS-GIT/dynamixel_hardware_interface/blob/02841dd2ae422676e5dc0fea37057bdec3be8cc1/include/dynamixel_hardware_interface/dynamixel_hardware_interface.hpp#L53-L91)
225-
226287
### Robotiq
227288
[hardware_interface](https://github.com/PickNikRobotics/ros2_robotiq_gripper/blob/12e623212e6891a5fcc9af94d67b07e640916394/robotiq_driver/include/robotiq_driver/driver.hpp#L41-L66)
228289
[acrivation_controller](https://github.com/PickNikRobotics/ros2_robotiq_gripper/blob/main/robotiq_controllers/src/robotiq_activation_controller.cpp)
229-
230290
### Ethercat
231291
[hardware_interface](https://github.com/ICube-Robotics/ethercat_driver_ros2/blob/52be2c2ed163bab25d46c402ddb4e7216c0a0ec3/ethercat_generic_plugins/ethercat_generic_cia402_drive/include/ethercat_generic_plugins/cia402_common_defs.hpp#L31-L56)
232-
233292
### ROS2 canopen driver
234293
https://github.com/ros-industrial/ros2_canopen/tree/master
235-
236294
### Picknik Twist & Fault controllers
237-
https://github.com/PickNikRobotics/picknik_controllers
295+
https://github.com/PickNikRobotics/picknik_controllers
-2.12 MB
Loading

0 commit comments

Comments
 (0)