Skip to content

Commit 0a0dae2

Browse files
authored
Feature/diagnostics (#156)
* Generate params for new diagnostics package * Link sim rates to config objects or match number with todo note * Added firmware version check * Disable MCU diagnostics for A200
1 parent 8510287 commit 0a0dae2

File tree

2 files changed

+142
-5
lines changed

2 files changed

+142
-5
lines changed

clearpath_generator_common/clearpath_generator_common/description/sensors.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ def __init__(self, sensor: BaseLidar2D) -> None:
101101
self.MAXIMUM_ANGLE: sensor.max_angle,
102102
self.MINIMUM_RANGE: 0.05,
103103
self.MAXIMUM_RANGE: 25.0,
104-
self.UPDATE_RATE: 50
104+
self.UPDATE_RATE: 40 # TODO: link to clearpath_config property
105105
})
106106

107107
class Lidar3dDescription(BaseDescription):
@@ -127,7 +127,7 @@ def __init__(self, sensor: BaseLidar3D) -> None:
127127
self.MAXIMUM_ANGLE_V: 0.261799,
128128
self.MINIMUM_RANGE: 0.9,
129129
self.MAXIMUM_RANGE: 130.0,
130-
self.UPDATE_RATE: 50
130+
self.UPDATE_RATE: 20 # TODO: link to clearpath_config property
131131
})
132132

133133
class ImuDescription(BaseDescription):
@@ -137,7 +137,7 @@ def __init__(self, sensor: BaseIMU) -> None:
137137
super().__init__(sensor)
138138

139139
self.parameters.update({
140-
self.UPDATE_RATE: 100
140+
self.UPDATE_RATE: sensor.update_rate
141141
})
142142

143143
class CameraDescription(BaseDescription):
@@ -152,14 +152,12 @@ def __init__(self, sensor: BaseCamera) -> None:
152152

153153
class AxisCameraDescription(CameraDescription):
154154
MODEL = 'model'
155-
UPDATE_RATE = 'update_rate'
156155

157156
def __init__(self, sensor: AxisCamera) -> None:
158157
super().__init__(sensor)
159158

160159
self.parameters.update({
161160
self.MODEL: sensor.device_type,
162-
self.UPDATE_RATE: 15,
163161
})
164162

165163
class IntelRealsenseDescription(CameraDescription):

clearpath_generator_common/clearpath_generator_common/param/platform.py

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,23 +31,38 @@
3131
# of Clearpath Robotics.
3232
import os
3333

34+
from apt import Cache
35+
3436
from clearpath_config.clearpath_config import ClearpathConfig
3537
from clearpath_config.common.types.platform import Platform
3638
from clearpath_config.common.utils.dictionary import merge_dict, replace_dict_items
39+
from clearpath_config.sensors.types.cameras import BaseCamera, IntelRealsense
40+
from clearpath_config.sensors.types.gps import BaseGPS
41+
from clearpath_config.sensors.types.imu import BaseIMU, PhidgetsSpatial
42+
from clearpath_config.sensors.types.lidars_2d import BaseLidar2D
43+
from clearpath_config.sensors.types.lidars_3d import BaseLidar3D
44+
from clearpath_config.sensors.types.sensor import BaseSensor
3745
from clearpath_generator_common.common import Package, ParamFile
3846
from clearpath_generator_common.param.writer import ParamWriter
47+
from clearpath_generator_common.ros import ROS_DISTRO
3948

4049

4150
class PlatformParam():
4251
CONTROL = 'control'
52+
DIAGNOSTIC_AGGREGATOR = 'diagnostic_aggregator'
53+
DIAGNOSTIC_UPDATER = 'diagnostic_updater'
4354
IMU_FILTER = 'imu_filter'
4455
LOCALIZATION = 'localization'
4556
TELEOP_INTERACTIVE_MARKERS = 'teleop_interactive_markers'
4657
TELEOP_JOY = 'teleop_joy'
4758
TWIST_MUX = 'twist_mux'
4859

60+
NOT_APPLICABLE = 'not_applicable'
61+
4962
PARAMETERS = [
5063
CONTROL,
64+
DIAGNOSTIC_AGGREGATOR,
65+
DIAGNOSTIC_UPDATER,
5166
IMU_FILTER,
5267
LOCALIZATION,
5368
TELEOP_INTERACTIVE_MARKERS,
@@ -57,6 +72,7 @@ class PlatformParam():
5772

5873
class BaseParam():
5974
CLEARPATH_CONTROL = 'clearpath_control'
75+
CLEARPATH_DIAGNOSTICS = 'clearpath_diagnostics'
6076

6177
def __init__(self,
6278
parameter: str,
@@ -169,6 +185,127 @@ def __init__(self,
169185
self.default_parameter_file_package = self.clearpath_control_package
170186
self.default_parameter_file_path = 'config'
171187

188+
class DiagnosticsAggregatorParam(BaseParam):
189+
"""Parameter file that decides the aggregation of the diagnostics data for display."""
190+
191+
def __init__(self,
192+
parameter: str,
193+
clearpath_config: ClearpathConfig,
194+
param_path: str) -> None:
195+
super().__init__(parameter, clearpath_config, param_path)
196+
self.default_parameter_file_package = Package(self.CLEARPATH_DIAGNOSTICS)
197+
self.default_parameter_file_path = 'config'
198+
199+
class DiagnosticsUpdaterParam(BaseParam):
200+
"""Parameter file for Clearpath Diagnostics indicating which topics to monitor."""
201+
202+
DIAGNOSTIC_UPDATER_NODE = 'clearpath_diagnostic_updater'
203+
204+
def __init__(self,
205+
parameter: str,
206+
clearpath_config: ClearpathConfig,
207+
param_path: str) -> None:
208+
super().__init__(parameter, clearpath_config, param_path)
209+
self.default_parameter_file_package = Package(self.CLEARPATH_DIAGNOSTICS)
210+
self.default_parameter_file_path = 'config'
211+
self.diag_dict = {}
212+
213+
def generate_parameters(self, use_sim_time: bool = False) -> None:
214+
super().generate_parameters(use_sim_time)
215+
216+
# Read the default parameter file
217+
self.default_param_file = ParamFile(
218+
name=self.default_parameter,
219+
package=self.default_parameter_file_package,
220+
path=self.default_parameter_file_path,
221+
parameters={}
222+
)
223+
self.default_param_file.read()
224+
225+
# Initialize parameters with the default parameters
226+
self.param_file.parameters = self.default_param_file.parameters
227+
228+
# Update parameters based on the robot.yaml
229+
platform_model = self.clearpath_config.get_platform_model()
230+
self.param_file.update({self.DIAGNOSTIC_UPDATER_NODE: {
231+
'serial_number': self.clearpath_config.get_serial_number(),
232+
'platform_model': platform_model}})
233+
234+
if use_sim_time:
235+
latest_apt_firmware_version = 'simulated'
236+
installed_apt_firmware_version = 'simulated'
237+
elif platform_model == Platform.A200:
238+
latest_apt_firmware_version = PlatformParam.NOT_APPLICABLE
239+
installed_apt_firmware_version = PlatformParam.NOT_APPLICABLE
240+
else:
241+
# Check latest firmware version available and save it in the config
242+
cache = Cache()
243+
latest_apt_firmware_version = 'not_found'
244+
installed_apt_firmware_version = 'none'
245+
try:
246+
pkg = cache[f'ros-{ROS_DISTRO}-clearpath-firmware']
247+
latest_apt_firmware_version = pkg.versions[0].version.split('-')[0]
248+
if (pkg.is_installed):
249+
installed_apt_firmware_version = pkg.installed.version.split('-')[0]
250+
except KeyError:
251+
print(f'\033[93mWarning: ros-{ROS_DISTRO}-clearpath-firmware'
252+
' package not found\033[0m')
253+
254+
self.param_file.update({self.DIAGNOSTIC_UPDATER_NODE: {
255+
'ros_distro': ROS_DISTRO,
256+
'latest_apt_firmware_version': latest_apt_firmware_version,
257+
'installed_apt_firmware_version': installed_apt_firmware_version}})
258+
259+
# List all topics to be monitored from each launched sensor
260+
for sensor in self.clearpath_config.sensors.get_all_sensors():
261+
262+
if not sensor.launch_enabled:
263+
continue
264+
265+
match sensor:
266+
case IntelRealsense():
267+
if sensor.color_enabled:
268+
self.add_topic(sensor, sensor.TOPICS.COLOR_IMAGE)
269+
if sensor.depth_enabled:
270+
self.add_topic(sensor, sensor.TOPICS.DEPTH_IMAGE)
271+
if sensor.pointcloud_enabled:
272+
self.add_topic(sensor, sensor.TOPICS.POINTCLOUD)
273+
274+
case BaseCamera():
275+
self.add_topic(sensor, sensor.TOPICS.COLOR_IMAGE)
276+
277+
case BaseLidar2D():
278+
self.add_topic(sensor, sensor.TOPICS.SCAN)
279+
280+
case BaseLidar3D():
281+
self.add_topic(sensor, sensor.TOPICS.SCAN)
282+
self.add_topic(sensor, sensor.TOPICS.POINTS)
283+
284+
case PhidgetsSpatial():
285+
self.add_topic(sensor, sensor.TOPICS.RAW_DATA),
286+
self.add_topic(sensor, sensor.TOPICS.MAG),
287+
288+
case BaseIMU():
289+
self.add_topic(sensor, sensor.TOPICS.DATA)
290+
self.add_topic(sensor, sensor.TOPICS.MAG)
291+
292+
case BaseGPS():
293+
self.add_topic(sensor, sensor.TOPICS.FIX)
294+
295+
# Output the list of topics into the parameter file
296+
self.param_file.update({self.DIAGNOSTIC_UPDATER_NODE: {'topics': self.diag_dict}})
297+
298+
def add_topic(self, sensor: BaseSensor, topic_key: str) -> None:
299+
"""
300+
Add a sensor topic to the dictionary using the topic key string.
301+
302+
:param sensor: The sensor object from which the topic info will be gotten
303+
:param topic_key: The key used to identify the topic to be monitored
304+
"""
305+
self.diag_dict[sensor.get_topic_name(topic_key, local=True)] = {
306+
'type': sensor.get_topic_type(topic_key),
307+
'rate': float(sensor.get_topic_rate(topic_key))}
308+
172309
class LocalizationParam(BaseParam):
173310
EKF_NODE = 'ekf_node'
174311
imu_config = [False, False, False,
@@ -247,6 +384,8 @@ def __init__(self,
247384

248385
PARAMETER = {
249386
IMU_FILTER: ImuFilterParam,
387+
DIAGNOSTIC_AGGREGATOR: DiagnosticsAggregatorParam,
388+
DIAGNOSTIC_UPDATER: DiagnosticsUpdaterParam,
250389
LOCALIZATION: LocalizationParam,
251390
TELEOP_JOY: TeleopJoyParam,
252391
TWIST_MUX: TwistMuxParam,

0 commit comments

Comments
 (0)