Formula Student Autonomous Systems
The code for the main driverless system
Loading...
Searching...
No Matches
evaluator.py
Go to the documentation of this file.
1import rclpy
2from rclpy.node import Node
3from custom_interfaces.msg import (
4 ConeArray,
5 VehicleState,
6 PathPointArray,
7 PathPoint,
8 VehicleState,
9 Velocities,
10 Pose,
11)
12from geometry_msgs.msg import TwistWithCovarianceStamped, TransformStamped
13from evaluator.adapter import Adapter
14from evaluator.metrics import (
15 get_mean_squared_difference,
16 get_average_difference,
17 get_inter_cones_distance,
18 compute_distance,
19 get_false_positives,
20 get_duplicates,
21 compute_closest_distances,
22 get_average_error,
23 get_mean_squared_error,
24)
25from evaluator.formats import (
26 format_vehicle_state_msg,
27 format_point2d_msg,
28 find_closest_elements,
29)
30import tf2_ros
31import sys
32from tf2_ros.transform_listener import TransformListener
33from evaluator.adapter_maps import (
34 ADAPTER_CONSTRUCTOR_DICTINARY,
35 ADAPTER_POINT_CLOUD_TOPIC_DICTIONARY,
36 ADAPTER_POINT_CLOUD_TOPIC_QOS_DICTIONARY,
37)
38import message_filters
39import numpy as np
40import rclpy.subscription
41from std_msgs.msg import Float32, Float32MultiArray, MultiArrayDimension, Int32, Float64
42from sensor_msgs.msg import PointCloud2
43import datetime
44from math import sqrt
45from evaluator.formats import format_path_point_array_msg
46from message_filters import TimeSynchronizer
47from std_msgs.msg import Float32, Float64MultiArray
48
49import csv
50import signal
51import yaml
52import os
53from ament_index_python.packages import get_package_prefix
54
55
56class Evaluator(Node):
57 """!
58 A ROS2 node for computing and publishing the system's real-time metrics
59 """
60
61 def __init__(self) -> None:
62 """
63 Initializes the Evaluator Node.
64 This function sets up the necessary parameters, subscriptions, and publishers for the Evaluator Node.
65 It also initializes variables for storing metrics over time.
66 Parameters:
67 None
68 Returns:
69 None
70 """
71 super().__init__("evaluator")
72 self.get_logger().info("Evaluator Node has started")
73
74 # Load configuration from YAML file
75 self.load_config()
76
77 if (self._adapter_name_ == "fsds") and (self.use_simulated_perception_):
78 self.get_logger().error(
79 "Simulated perception is not supported for FSDS adapter"
80 )
81 sys.exit(1)
82
83 # Subscriptions
84 self.perception_subscription_ = message_filters.Subscriber(
85 self, ConeArray, "/perception/cones"
86 )
87 self.map_subscription_ = message_filters.Subscriber(
88 self, ConeArray, "/state_estimation/map"
89 )
90 self.velocities_subscription_ = message_filters.Subscriber(
91 self, Velocities, "/state_estimation/velocities"
92 )
93 self.vehicle_pose_subscription_ = message_filters.Subscriber(
94 self, Pose, "/state_estimation/vehicle_pose"
95 )
96 self.planning_subscription_ = message_filters.Subscriber(
97 self, PathPointArray, "/path_planning/path"
98 )
99
100 if not self.use_simulated_planning_:
101 self.planning_subscription_.registerCallback(
102 lambda msg: setattr(
103 self,
104 "path_points",
105 [[p.x, p.y, p.v] for p in msg.pathpoint_array],
106 )
107 )
108
109 if not self.use_simulated_velocities_:
110 self.velocities_subscription_.registerCallback(
111 lambda msg: setattr(
112 self,
113 "absolute_velocity",
114 sqrt(msg.velocity_x**2 + msg.velocity_y**2),
115 )
116 )
117
118 if not self.use_simulated_se_:
119 self.vehicle_pose_subscription_.registerCallback(
120 lambda msg: setattr(
121 self,
122 "position",
123 [msg.x, msg.y],
124 )
125 )
126
127 self.transform_buffer_ = tf2_ros.Buffer()
128 self._transforms_listener_ = TransformListener(self.transform_buffer_, self)
129 self.planning_gt_subscription_ = message_filters.Subscriber(
130 self, PathPointArray, "/path_planning/mock_path"
131 )
133 self.planning_gt_subscription_.registerCallback(
134 lambda msg: setattr(
135 self,
136 "path_points",
137 [[p.x, p.y, p.v] for p in msg.pathpoint_array],
138 )
139 )
140
141 self._correction_step_time_subscription_ = self.create_subscription(
142 Float64,
143 "/state_estimation/execution_time/correction_step",
145 10,
146 )
147 self._prediction_step_time_subscription_ = self.create_subscription(
148 Float64,
149 "/state_estimation/execution_time/prediction_step",
151 10,
152 )
153 self._planning_execution_time_subscription_ = self.create_subscription(
154 Float64,
155 "/path_planning/execution_time",
157 10,
158 )
159 self._perception_execution_time_subscription_ = self.create_subscription(
160 Float64,
161 "/perception/execution_time",
163 10,
164 )
165 self._control_execution_time_subscription_ = self.create_subscription(
166 Float64MultiArray,
167 "/control/execution_time",
169 10,
170 )
171
172 self.path_points = []
173 self.position = [0.0, 0.0]
175
176 # Publishers for perception metrics
177 self._perception_mean_difference_ = self.create_publisher(
178 Float32, "/evaluator/perception/mean_difference", 10
179 )
180 self._perception_mean_squared_difference_ = self.create_publisher(
181 Float32, "/evaluator/perception/mean_squared_difference", 10
182 )
183 self._perception_root_mean_squared_difference_ = self.create_publisher(
184 Float32, "/evaluator/perception/root_mean_squared_difference", 10
185 )
186 self._perception_inter_cones_distance_ = self.create_publisher(
187 Float32, "/evaluator/perception/inter_cones_distance", 10
188 )
189 self._perception_false_positives_ = self.create_publisher(
190 Int32, "/evaluator/perception/false_positives", 10
191 )
192 self._perception_precision_ = self.create_publisher(
193 Float32, "/evaluator/perception/precision", 10
194 )
195 self._perception_recall_ = self.create_publisher(
196 Float32, "/evaluator/perception/recall", 10
197 )
198 self._perception_number_duplicates = self.create_publisher(
199 Int32, "/evaluator/perception/number_duplicates", 10
200 )
201
202 # Publishers for state estimation metrics
203 self._velocities_difference_ = self.create_publisher(
204 Float32MultiArray,
205 "/evaluator/state_estimation/velocities_difference",
206 10,
207 )
208 self._vehicle_pose_difference_ = self.create_publisher(
209 Float32MultiArray,
210 "/evaluator/state_estimation/vehicle_pose_difference",
211 10,
212 )
213 self._map_mean_squared_difference_ = self.create_publisher(
214 Float32, "/evaluator/state_estimation/map_mean_squared_difference", 10
215 )
216 self._map_mean_difference_ = self.create_publisher(
217 Float32, "/evaluator/state_estimation/map_mean_difference", 10
218 )
219 self._map_root_mean_squared_difference_ = self.create_publisher(
220 Float32, "/evaluator/state_estimation/map_root_mean_squared_difference", 10
221 )
222
223 self._se_false_positives_ = self.create_publisher(
224 Int32, "/evaluator/state_estimation/false_positives", 10
225 )
226 self._se_number_duplicates = self.create_publisher(
227 Int32, "/evaluator/state_estimation/number_duplicates", 10
228 )
229 self._se_difference_with_map_ = self.create_publisher(
230 Int32, "/evaluator/state_estimation/difference_with_map", 10
231 )
232
233 # Publisher for path planning metrics
234 self._planning_mean_difference_to_gt = self.create_publisher(
235 Float32, "/evaluator/planning/gt_me", 10
236 )
237 self._planning_mean_squared_difference_to_gt = self.create_publisher(
238 Float32, "/evaluator/planning/gt_mse", 10
239 )
241 Float32, "/evaluator/planning/gt_rmse", 10
242 )
243
244 self._planning_mean_difference_to_left_cones = self.create_publisher(
245 Float32, "/evaluator/planning/left_cones_me", 10
246 )
248 Float32, "/evaluator/planning/left_cones_mse", 10
249 )
251 self.create_publisher(Float32, "/evaluator/planning/left_cones_rmse", 10)
252 )
253
254 self._planning_mean_difference_to_right_cones = self.create_publisher(
255 Float32, "/evaluator/planning/right_cones_me", 10
256 )
258 Float32, "/evaluator/planning/right_cones_mse", 10
259 )
261 self.create_publisher(Float32, "/evaluator/planning/right_cones_rmse", 10)
262 )
263
264 self._planning_mean_difference_to_cones = self.create_publisher(
265 Float32, "/evaluator/planning/cones_me", 10
266 )
267 self._planning_mean_squared_difference_to_cones = self.create_publisher(
268 Float32, "/evaluator/planning/cones_mse", 10
269 )
271 Float32, "/evaluator/planning/cones_rmse", 10
272 )
273
274 # Publisher for control metrics
275 self._control_position_error_ = self.create_publisher(
276 Float32, "/evaluator/control/position/error", 10
277 )
278 self._control_velocity_error_ = self.create_publisher(
279 Float32, "/evaluator/control/velocity/error", 10
280 )
281
282 # Replace spaces with underscores in csv_suffix
283 self.csv_suffix = self.csv_suffix.replace(" ", "_")
284
285 # Metrics over time
287 self.map_metrics = []
288 self.pose_metrics = []
297
303
310
314 self._map_count_ = 0
316 self._ve_count_ = 0
317
318 self._sum_velocities_error = Float32MultiArray()
319 self._sum_squared_velocities_error = Float32MultiArray()
320 self._sum_velocities_error.layout.dim = [MultiArrayDimension()]
321 self._sum_squared_velocities_error.layout.dim = [MultiArrayDimension()]
322 self._sum_velocities_error.layout.dim[0].size = 3
323 self._sum_squared_velocities_error.layout.dim[0].size = 3
324 self._sum_velocities_error.data = [0.0] * 3
325 self._sum_squared_velocities_error.data = [0.0] * 3
326 self._sum_vehicle_pose_error = Float32MultiArray()
327 self._sum_squared_vehicle_pose_error = Float32MultiArray()
328 self._sum_vehicle_pose_error.layout.dim = [MultiArrayDimension()]
329 self._sum_squared_vehicle_pose_error.layout.dim = [MultiArrayDimension()]
330 self._sum_vehicle_pose_error.layout.dim[0].size = 3
331 self._sum_squared_vehicle_pose_error.layout.dim[0].size = 3
332 self._sum_vehicle_pose_error.data = [0.0] * 3
333 self._sum_squared_vehicle_pose_error.data = [0.0] * 3
338
339 # Perception Metrics Over time
340 self._perception_mean_mean_error = self.create_publisher(
341 Float32, "/evaluator/perception/mean_mean_error", 10
342 )
343
344 self._perception_mean_mean_squared_error = self.create_publisher(
345 Float32, "/evaluator/perception/mean_mean_squared_error", 10
346 )
347
348 self._perception_mean_mean_root_squared_error = self.create_publisher(
349 Float32, "/evaluator/perception/mean_mean_root_squared_error", 10
350 )
351
352 self._perception_mean_precision = self.create_publisher(
353 Float32, "/evaluator/perception/mean_precision", 10
354 )
355
356 self._perception_mean_recall = self.create_publisher(
357 Float32, "/evaluator/perception/mean_recall", 10
358 )
359
360 # State estimation metrics over time
361 self._map_mean_mean_error = self.create_publisher(
362 Float32, "/evaluator/state_estimation/map_mean_mean_difference", 10
363 )
364
365 self._map_mean_mean_squared_error = self.create_publisher(
366 Float32, "/evaluator/state_estimation/map_mean_mean_squared_difference", 10
367 )
368
369 self._map_mean_mean_root_squared_error = self.create_publisher(
370 Float32,
371 "/evaluator/state_estimation/map_mean_mean_root_squared_difference",
372 10,
373 )
374
375 self._state_estimation_mean_vehicle_pose_error = self.create_publisher(
376 Float32MultiArray,
377 "/evaluator/state_estimation/vehicle_pose_mean_difference",
378 10,
379 )
380
382 Float32MultiArray,
383 "/evaluator/state_estimation/vehicle_pose_mean_squared_difference",
384 10,
385 )
386
388 self.create_publisher(
389 Float32MultiArray,
390 "/evaluator/state_estimation/vehicle_pose_mean_root_squared_difference",
391 10,
392 )
393 )
394
395 self._state_estimation_mean_velocities_error = self.create_publisher(
396 Float32MultiArray,
397 "/evaluator/state_estimation/velocities_mean_difference",
398 10,
399 )
400
402 Float32MultiArray,
403 "/evaluator/state_estimation/velocities_mean_squared_difference",
404 10,
405 )
406
408 self.create_publisher(
409 Float32MultiArray,
410 "/evaluator/state_estimation/velocities_mean_root_squared_difference",
411 10,
412 )
413 )
414
415 # Planning metrics over time
416 self._planning_mean_mean_error = self.create_publisher(
417 Float32, "/evaluator/planning/mean_mean_difference", 10
418 )
419
420 self._planning_mean_mean_squared_error = self.create_publisher(
421 Float32, "/evaluator/planning/mean_mean_squared_difference", 10
422 )
423
424 self._planning_mean_mean_root_squared_error = self.create_publisher(
425 Float32, "/evaluator/planning/mean_mean_root_squared_difference", 10
426 )
427
428 # Control metrics over time
429 self._control_position_mean_error_ = self.create_publisher(
430 Float32, "/evaluator/control/position/mean_error", 10
431 )
432 self._control_position_mean_squared_error_ = self.create_publisher(
433 Float32, "/evaluator/control/position/mean_squared_error", 10
434 )
435 self._control_position_root_mean_squared_error_ = self.create_publisher(
436 Float32, "/evaluator/control/position/root_mean_squared_error", 10
437 )
438
439 self._control_velocity_mean_error_ = self.create_publisher(
440 Float32, "/evaluator/control/velocity/mean_error", 10
441 )
442 self._control_velocity_mean_squared_error_ = self.create_publisher(
443 Float32,
444 "/evaluator/control/velocity/mean_squared_error",
445 10,
446 )
447 self._control_velocity_root_mean_squared_error_ = self.create_publisher(
448 Float32,
449 "/evaluator/control/velocity/root_mean_squared_error",
450 10,
451 )
452
453 # Adapter selection
454 self._adapter_: Adapter = ADAPTER_CONSTRUCTOR_DICTINARY[self._adapter_name_](
455 self
456 )
457
458 signal.signal(signal.SIGINT, self.signal_handlersignal_handler)
459
460 def get_config_yaml_path(self, package_name, dir, filename):
461 package_share_directory = get_package_prefix(package_name)
462 config_path = os.path.join(
463 package_share_directory, "..", "..", "config", dir, f"{filename}.yaml"
464 )
465 return config_path
466
467 def load_config(self):
468 """Load configuration from YAML file."""
469 global_config_path = self.get_config_yaml_path(
470 "evaluator", "global", "global_config"
471 )
472 self.get_logger().debug(f"Loading global config from: {global_config_path}")
473 with open(global_config_path, "r") as file:
474 global_config = yaml.safe_load(file)
475
476 adapter = global_config["global"]["adapter"]
477 self._adapter_name_ = adapter
478 self.use_simulated_perception_ = global_config["global"][
479 "use_simulated_perception"
480 ]
481 self.use_simulated_se_ = global_config["global"]["use_simulated_se"]
482 self.use_simulated_velocities_ = global_config["global"][
483 "use_simulated_velocities"
484 ]
485 self.use_simulated_planning_ = global_config["global"]["use_simulated_planning"]
486
487 specific_config_path = self.get_config_yaml_path(
488 "evaluator", "evaluator", adapter
489 )
490 self.get_logger().debug(f"Loading specific config from: {specific_config_path}")
491 with open(specific_config_path, "r") as file:
492 specific_config = yaml.safe_load(file)
493
494 self.generate_csv = specific_config["evaluator"]["generate_csv"]
495 self.csv_suffix = specific_config["evaluator"]["csv_suffix"].replace(" ", "_")
496
497 def signal_handler(self, sig: int, frame) -> None:
498 """!
499 Writes metrics to csv and exits when Ctrl+C is pressed.
500 This function is triggered when the program receives a termination signal (e.g., SIGINT or SIGTERM)
501 and it saves the collected metrics to CSV files, then exits the program with a status code of 0.
502 The metrics are saved in separate CSV files based on their category.
503
504 Args:
505 sig (int): The signal number. (not used in this function)
506 frame (frame): The current stack frame. (not used in this function)
507 Note:
508 - Metrics are saved in the "performance/evaluator_metrics" directory as "<metric_name>_<timestamp>.csv".
509 - If a category has no metrics, no CSV file will be created for that category.
510 Example:
511 # Create an instance of the Evaluator class
512 evaluator = Evaluator()
513 # Register the signal handler function
514 signal.signal(signal.SIGINT, evaluator.signal_handler)
515 # Start the program
516 evaluator.run()
517 """
518 if self.generate_csv:
519 finish_time = datetime.datetime.now().strftime("%Y-%m-%d_%H:%M:%S")
520 metrics_dict = {
521 "perception": self.perception_metrics,
522 "map": self.map_metrics,
523 "pose": self.pose_metrics,
524 "planning": self.planning_metrics,
525 "control": self.control_metrics,
526 "se_correction_execution_time": self._se_correction_execution_time_,
527 "se_prediction_execution_time": self._se_prediction_execution_time_,
528 "control_execution_time": self._control_execution_time_,
529 "planning_execution_time": self._planning_execution_time_,
530 "perception_execution_time": self._perception_execution_time_,
531 }
532 for filename, metrics in metrics_dict.items():
533 if metrics:
534 datetime_filename = f"{filename}_{self._adapter_name_}_{finish_time}_{self.csv_suffix}.csv"
535 self.metrics_to_csv(
536 metrics, "performance/evaluator_metrics/" + datetime_filename
537 )
538 sys.exit(0)
539
540 def metrics_to_csv(self, metrics: list, filename: str) -> None:
541 """
542 Converts metrics to csv and writes them to a file.
543 Args:
544 metrics (list): A list of dictionaries representing the metrics.
545 filename (str): The name of the CSV file to write the metrics to.
546 Returns:
547 None
548 """
549
550 # Add 'time' key to each metric
551 start_time = metrics[0]["timestamp"]
552 for metric in metrics:
553 elapsed_time = (metric["timestamp"] - start_time).total_seconds()
554 metric["time"] = elapsed_time # Add/Update 'time' key with elapsed time
555
556 # Write metrics to csv
557 with open(filename, "w", newline="") as csvfile:
558 fieldnames = metrics[0].keys()
559 writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
560 writer.writeheader()
561 writer.writerows(metrics)
562
563 def correction_step_time_callback(self, msg: Float64) -> None:
564 """!
565 Callback function to store the execution time of the correction step.
566
567 Args:
568 msg (Float64): Message containing the correction step execution time.
569 Returns:
570 None
571 """
573 {"timestamp": datetime.datetime.now(), "execution_time": msg.data}
574 )
575
576 def prediction_step_time_callback(self, msg: Float64) -> None:
577 """
578 Callback function to store the execution time of the prediction step.
579 Args:
580 msg (Float64): Message containing the prediction step execution time.
581 Returns:
582 None
583 """
585 {"timestamp": datetime.datetime.now(), "execution_time": msg.data}
586 )
587
588 def perception_execution_time_callback(self, msg: Float64) -> None:
589 """!
590 Callback function to store the perception execution time.
591
592 Args:
593 msg (Float64): Message containing the Perception execution time.
594 Returns:
595 None
596 """
597 self._perception_execution_time_.append(
598 {"timestamp": datetime.datetime.now(), "execution_time": msg.data}
599 )
600
601 def planning_execution_time_callback(self, msg: Float64) -> None:
602 """!
603 Callback function to store the planning execution time.
604
605 Args:
606 msg (Float64): Message containing the planning execution time.
607 Returns:
608 None
609 """
610 self._planning_execution_time_.append(
611 {"timestamp": datetime.datetime.now(), "execution_time": msg.data}
612 )
613
615 self,
616 pose: np.ndarray,
617 groundtruth_pose: np.ndarray,
618 ) -> None:
619 """!
620 Computes state estimation metrics and publishes them.
621 Args:
622 pose (np.ndarray): Vehicle state estimation data. [x,y,theta]
623 groundtruth_pose (np.ndarray): Ground truth vehicle state data. [x,y,theta]
624 """
625
626 self.get_logger().debug("Received pose")
627
628 # Compute instantaneous vehicle pose metrics
629 vehicle_pose_error = Float32MultiArray()
630 vehicle_pose_error.layout.dim = [MultiArrayDimension()]
631 vehicle_pose_error.layout.dim[0].size = 3
632 vehicle_pose_error.layout.dim[0].label = "Vehicle Pose Error: [x, y, theta]"
633 vehicle_pose_error.data = [0.0] * 3
634 if groundtruth_pose != []:
635 vehicle_pose_error.data[0] = abs(pose[0] - groundtruth_pose[0])
636 vehicle_pose_error.data[1] = abs(pose[1] - groundtruth_pose[1])
637 vehicle_pose_error.data[2] = abs(pose[2] - groundtruth_pose[2]) % (
638 2 * np.pi
639 )
640
641 self.get_logger().debug(
642 "Computed slam metrics:\n \
643 Vehicle state error: {}".format(
644 vehicle_pose_error,
645 )
646 )
647
648 # Publishes computed state estimation metrics
649 self._vehicle_pose_difference_.publish(vehicle_pose_error)
650
651 # Compute vehicle pose metrics over time
652 mean_vehicle_pose_error = Float32MultiArray()
653 mean_squared_vehicle_pose_error = Float32MultiArray()
654 mean_root_squared_vehicle_pose_error = Float32MultiArray()
655 mean_vehicle_pose_error.layout.dim = [MultiArrayDimension()]
656 mean_squared_vehicle_pose_error.layout.dim = [MultiArrayDimension()]
657 mean_root_squared_vehicle_pose_error.layout.dim = [MultiArrayDimension()]
658 mean_vehicle_pose_error.layout.dim[0].size = 3
659 mean_squared_vehicle_pose_error.layout.dim[0].size = 3
660 mean_root_squared_vehicle_pose_error.layout.dim[0].size = 3
661 mean_vehicle_pose_error.data = [0.0] * 3
662 mean_squared_vehicle_pose_error.data = [0.0] * 3
663 mean_root_squared_vehicle_pose_error.data = [0.0] * 3
664 self._pose_count_ += 1
665 for i in range(3):
666 self._sum_vehicle_pose_error.data[i] += vehicle_pose_error.data[i]
667 self._sum_squared_vehicle_pose_error.data[i] += (
668 vehicle_pose_error.data[i] ** 2
669 )
670 mean_vehicle_pose_error.data[i] = (
671 self._sum_vehicle_pose_error.data[i] / self._pose_count_
672 )
673 mean_squared_vehicle_pose_error.data[i] = (
675 )
676 mean_root_squared_vehicle_pose_error.data[i] = (
677 sqrt(self._sum_squared_vehicle_pose_error.data[i]) / self._pose_count_
678 )
679
680 # Publish vehicle state errors over time
681 self._state_estimation_mean_vehicle_pose_error.publish(mean_vehicle_pose_error)
683 mean_squared_vehicle_pose_error
684 )
686 mean_root_squared_vehicle_pose_error
687 )
688
689 # For exporting metrics to csv
690 metrics = {
691 "timestamp": datetime.datetime.now(),
692 "vehicle_pose_error_x": vehicle_pose_error.data[0],
693 "vehicle_pose_error_y": vehicle_pose_error.data[1],
694 "vehicle_pose_error_theta": vehicle_pose_error.data[2],
695 "mean_vehicle_pose_error_x": mean_vehicle_pose_error.data[0],
696 "mean_vehicle_pose_error_y": mean_vehicle_pose_error.data[1],
697 "mean_vehicle_pose_error_theta": mean_vehicle_pose_error.data[2],
698 "mean_squared_vehicle_pose_error_x": mean_squared_vehicle_pose_error.data[
699 0
700 ],
701 "mean_squared_vehicle_pose_error_y": mean_squared_vehicle_pose_error.data[
702 1
703 ],
704 "mean_squared_vehicle_pose_error_theta": mean_squared_vehicle_pose_error.data[
705 2
706 ],
707 "mean_root_squared_vehicle_pose_error_x": mean_root_squared_vehicle_pose_error.data[
708 0
709 ],
710 "mean_root_squared_vehicle_pose_error_y": mean_root_squared_vehicle_pose_error.data[
711 1
712 ],
713 "mean_root_squared_vehicle_pose_error_theta": mean_root_squared_vehicle_pose_error.data[
714 2
715 ],
716 }
717 self.pose_metrics.append(metrics)
718
720 self,
721 map: np.ndarray,
722 groundtruth_map: np.ndarray,
723 ) -> None:
724 """!
725 Computes state estimation metrics and publishes them.
726
727 Args:
728 pose (np.ndarray): Vehicle state estimation data. [x,y,theta]
729 groundtruth_pose (np.ndarray): Ground truth vehicle state data. [x,y,theta]
730 map (np.ndarray): Map data. [[x,y,color,confidence]]
731 groundtruth_map (np.ndarray): Ground truth map data. [[x,y,color,confidence]]
732 """
733 if map.size == 0 or groundtruth_map.size == 0:
734 return
735
736 self.get_logger().debug("Received map")
737
738 # Compute instantaneous map metrics
739 cone_positions = map[:, :2]
740 groundtruth_cone_positions = groundtruth_map[:, :2]
741 mean_difference = Float32()
742 mean_difference.data = get_average_difference(
743 cone_positions, groundtruth_cone_positions
744 )
745
746 mean_squared_difference = Float32()
747 mean_squared_difference.data = get_mean_squared_difference(
748 cone_positions, groundtruth_cone_positions
749 )
750
751 num_duplicates = Int32()
752 num_duplicates.data = int(get_duplicates(cone_positions, 0.1))
753
754 root_mean_squared_difference = Float32()
755 root_mean_squared_difference.data = sqrt(
756 get_mean_squared_difference(cone_positions, groundtruth_cone_positions)
757 )
758
759 false_positives = Int32()
760 false_positives.data = int(
761 get_false_positives(cone_positions, groundtruth_cone_positions, 2)
762 )
763
764 difference_with_map = Int32()
765 difference_with_map.data = cone_positions.size - groundtruth_cone_positions.size
766
767 self.get_logger().debug(
768 "Computed slam metrics:\n \
769 Mean difference: {}\n \
770 Mean squared difference: {}\n \
771 Root mean squared difference: {}".format(
772 mean_difference,
773 mean_squared_difference,
774 root_mean_squared_difference,
775 )
776 )
777
778 # Publishes computed state estimation metrics
779 self._map_mean_difference_.publish(mean_difference)
780 self._map_mean_squared_difference_.publish(mean_squared_difference)
781 self._map_root_mean_squared_difference_.publish(root_mean_squared_difference)
782 self._se_number_duplicates.publish(num_duplicates)
783 self._se_false_positives_.publish(false_positives)
784 self._se_difference_with_map_.publish(difference_with_map)
785
786 # Compute map metrics over time
787 self._se_map_sum_error += get_average_difference(
788 cone_positions, groundtruth_cone_positions
789 )
790 self._se_map_squared_sum_error += get_mean_squared_difference(
791 cone_positions, groundtruth_cone_positions
792 )
793 self._se_map_mean_root_squared_sum_error += get_mean_squared_difference(
794 cone_positions, groundtruth_cone_positions
795 ) ** (1 / 2)
796 self._map_count_ += 1
797 mean_mean_error = Float32()
798 mean_mean_error.data = self._se_map_sum_error / self._map_count_
799 mean_mean_squared_error = Float32()
800 mean_mean_squared_error.data = self._se_map_squared_sum_error / self._map_count_
801 mean_mean_root_squared_error = Float32()
802 mean_mean_root_squared_error.data = (
804 )
805
806 # Publish map metrics over time
807 self._map_mean_mean_error.publish(mean_mean_error)
808 self._map_mean_mean_squared_error.publish(mean_mean_squared_error)
809 self._map_mean_mean_root_squared_error.publish(mean_mean_root_squared_error)
810
811 # For exporting metrics to csv
812 metrics = {
813 "timestamp": datetime.datetime.now(),
814 "mean_difference": mean_difference.data,
815 "mean_squared_difference": mean_squared_difference.data,
816 "root_mean_squared_difference": root_mean_squared_difference.data,
817 "mean_mean_error": mean_mean_error.data,
818 "mean_mean_squared_error": mean_mean_squared_error.data,
819 "mean_mean_root_squared_error": mean_mean_root_squared_error.data,
820 "false_positives": false_positives.data,
821 "difference_with_map": difference_with_map.data,
822 "num_duplicates": num_duplicates.data,
823 }
824 self.map_metrics.append(metrics)
825
827 self,
828 velocities: np.ndarray,
829 groundtruth_velocities: np.ndarray,
830 ) -> None:
831 """!
832 Computes state estimation metrics and publishes them.
833
834 Args:
835 velocities (np.ndarray): Vehicle state velocities. [vx, vy, w]
836 groundtruth_velocities (np.ndarray): Ground truth vehicle state velocities. [vx, vy, w]
837 """
838
839 self.get_logger().debug("Received vehicle speed estimation")
840
841 # Compute instantaneous vehicle state metrics
842 velocities_error = Float32MultiArray()
843 velocities_error.layout.dim = [MultiArrayDimension()]
844 velocities_error.layout.dim[0].size = 3
845 velocities_error.layout.dim[0].label = "vehicle state error: [vx, vy, w]"
846 velocities_error.data = [0.0] * 3
847 if groundtruth_velocities != []:
848 velocities_error.data[0] = abs(velocities[0] - groundtruth_velocities[0])
849 velocities_error.data[1] = abs(velocities[1] - groundtruth_velocities[1])
850 velocities_error.data[2] = abs(velocities[2] - groundtruth_velocities[2])
851
852 self.get_logger().debug(
853 "Computed state estimation metrics:\n \
854 Vehicle velocities error: {}".format(
855 velocities_error,
856 )
857 )
858
859 # Publishes computed state estimation metrics
860 self._velocities_difference_.publish(velocities_error)
861
862 # Compute vehicle velocity metrics over time
863 self._ve_count_ += 1
864 mean_velocities_error = Float32MultiArray()
865 mean_squared_velocities_error = Float32MultiArray()
866 mean_root_squared_velocities_error = Float32MultiArray()
867 mean_velocities_error.layout.dim = [MultiArrayDimension()]
868 mean_squared_velocities_error.layout.dim = [MultiArrayDimension()]
869 mean_root_squared_velocities_error.layout.dim = [MultiArrayDimension()]
870 mean_velocities_error.layout.dim[0].size = 3
871 mean_squared_velocities_error.layout.dim[0].size = 3
872 mean_root_squared_velocities_error.layout.dim[0].size = 3
873 mean_velocities_error.data = [0.0] * 3
874 mean_squared_velocities_error.data = [0.0] * 3
875 mean_root_squared_velocities_error.data = [0.0] * 3
876 for i in range(3):
877 self._sum_velocities_error.data[i] += velocities_error.data[i]
878 self._sum_squared_velocities_error.data[i] += velocities_error.data[i] ** 2
879 mean_velocities_error.data[i] = (
880 self._sum_velocities_error.data[i] / self._ve_count_
881 )
882 mean_squared_velocities_error.data[i] = (
883 self._sum_squared_velocities_error.data[i] / self._ve_count_
884 )
885 mean_root_squared_velocities_error.data[i] = (
886 sqrt(self._sum_squared_velocities_error.data[i]) / self._ve_count_
887 )
888
889 # Publish vehicle state errors over time
890 self._state_estimation_mean_velocities_error.publish(mean_velocities_error)
892 mean_squared_velocities_error
893 )
895 mean_root_squared_velocities_error
896 )
897
898 # For exporting metrics to csv
899 metrics = {
900 "timestamp": datetime.datetime.now(),
901 "velocities_error_v1": velocities_error.data[0],
902 "velocities_error_v2": velocities_error.data[1],
903 "velocities_error_w": velocities_error.data[2],
904 "mean_velocities_error_v1": mean_velocities_error.data[0],
905 "mean_velocities_error_v2": mean_velocities_error.data[1],
906 "mean_velocities_error_w": mean_velocities_error.data[2],
907 "mean_squared_velocities_error_v1": mean_squared_velocities_error.data[0],
908 "mean_squared_velocities_error_v2": mean_squared_velocities_error.data[1],
909 "mean_squared_velocities_error_w": mean_squared_velocities_error.data[2],
910 "mean_root_squared_velocities_error_v1": mean_root_squared_velocities_error.data[
911 0
912 ],
913 "mean_root_squared_velocities_error_v2": mean_root_squared_velocities_error.data[
914 1
915 ],
916 "mean_root_squared_velocities_error_w": mean_root_squared_velocities_error.data[
917 2
918 ],
919 }
920 self.vel_estimation_metrics.append(metrics)
921
923 self, perception_output: np.ndarray, perception_ground_truth: np.ndarray
924 ) -> None:
925 """!
926 Computes perception metrics and publishes them.
927
928 Args:
929 perception_output (np.ndarray): Perceived cones.
930 perception_ground_truth (np.ndarray): Ground truth cones.
931 """
932
933 if len(perception_output) == 0 or len(perception_ground_truth) == 0:
934 self.get_logger().debug("Perception, ground truth or cones info missing")
935 return
936
937 # Compute instantaneous perception metrics
938
939 cone_positions = perception_output[:, :2]
940 groundtruth_cone_positions = perception_ground_truth[:, :2]
941 mean_difference = Float32()
942 mean_difference.data = get_average_difference(
943 cone_positions, groundtruth_cone_positions
944 )
945 mean_squared_error = Float32()
946 mean_squared_error.data = get_mean_squared_difference(
947 cone_positions, groundtruth_cone_positions
948 )
949 inter_cones_distance = Float32()
950 inter_cones_distance.data = float(get_inter_cones_distance(cone_positions))
951 root_mean_squared_difference = Float32()
952 root_mean_squared_difference.data = sqrt(mean_squared_error.data)
953
954 false_positives = Int32()
955 false_positives.data = int(
956 get_false_positives(cone_positions, groundtruth_cone_positions, 0.1)
957 )
958 precision = Float32()
959 precision.data = float(
960 (len(cone_positions) - false_positives.data) / len(cone_positions)
961 )
962 recall = Float32()
963 recall.data = float(
964 (len(cone_positions) - false_positives.data)
965 / len(groundtruth_cone_positions)
966 )
967
968 num_duplicates = Int32()
969 num_duplicates.data = int(get_duplicates(perception_output, 0.1))
970
971 self.get_logger().debug(
972 "Computed perception metrics:\n \
973 Mean difference: {}\n \
974 Inter cones distance: {}\n \
975 Mean squared difference: {}\n \
976 Root mean squared difference: {}\n \
977 False positives: {}\n \
978 Precision: {}\n \
979 Recall: {}\n \
980 Duplicates: {}".format(
981 mean_difference,
982 inter_cones_distance,
983 mean_squared_error,
984 root_mean_squared_difference,
985 false_positives,
986 precision,
987 recall,
988 num_duplicates,
989 )
990 )
991
992 # Publishes computed perception metrics
993 self._perception_mean_difference_.publish(mean_difference)
994 self._perception_inter_cones_distance_.publish(inter_cones_distance)
995 self._perception_mean_squared_difference_.publish(mean_squared_error)
997 root_mean_squared_difference
998 )
999 self._perception_false_positives_.publish(false_positives)
1000 self._perception_precision_.publish(precision)
1001 self._perception_recall_.publish(recall)
1002 self._perception_number_duplicates.publish(num_duplicates)
1003
1004 # Compute perception metrics over time
1005 self._perception_sum_error += get_average_difference(
1006 cone_positions, groundtruth_cone_positions
1007 )
1008 self._perception_squared_sum_error += get_mean_squared_difference(
1009 cone_positions, groundtruth_cone_positions
1010 )
1011 self._perception_root_squared_sum_error += get_mean_squared_difference(
1012 cone_positions, groundtruth_cone_positions
1013 ) ** (1 / 2)
1014 self._perception_precision_sum += float(
1015 (
1016 len(cone_positions)
1017 - get_false_positives(cone_positions, groundtruth_cone_positions, 0.1)
1018 )
1019 / len(cone_positions)
1020 )
1021 self._perception_recall_sum += float(
1022 (
1023 len(cone_positions)
1024 - get_false_positives(cone_positions, groundtruth_cone_positions, 0.1)
1025 )
1026 / len(groundtruth_cone_positions)
1027 )
1028 self._perception_count += 1
1029 mean_mean_error = Float32()
1030 mean_mean_error.data = self._perception_sum_error / self._perception_count
1031 mean_mean_squared_error = Float32()
1032 mean_mean_squared_error.data = (
1034 )
1035 mean_mean_root_squared_error = Float32()
1036 mean_mean_root_squared_error.data = (
1038 )
1039 mean_precision = Float32()
1040 mean_precision.data = self._perception_precision_sum / self._perception_count
1041 mean_recall = Float32()
1042 mean_recall.data = self._perception_recall_sum / self._perception_count
1043
1044 # Publish perception metrics over time
1045 self._perception_mean_mean_error.publish(mean_mean_error)
1046 self._perception_mean_mean_squared_error.publish(mean_mean_squared_error)
1048 mean_mean_root_squared_error
1049 )
1050 self._perception_mean_precision.publish(mean_precision)
1051 self._perception_mean_recall.publish(mean_recall)
1052
1053 # For exporting metrics to csv
1054 metrics = {
1055 "timestamp": datetime.datetime.now(),
1056 "mean_difference": mean_difference.data,
1057 "inter_cones_distance": inter_cones_distance.data,
1058 "mean_squared_difference": mean_squared_error.data,
1059 "root_mean_squared_difference": root_mean_squared_difference.data,
1060 "mean_mean_error": mean_mean_error.data,
1061 "mean_mean_squared_error": mean_mean_squared_error.data,
1062 "mean_mean_root_squared_error": mean_mean_root_squared_error.data,
1063 "false_positives": false_positives.data,
1064 "precision": precision.data,
1065 "recall": recall.data,
1066 "mean_precision": mean_precision.data,
1067 "mean_recall": mean_recall.data,
1068 "duplicates": num_duplicates.data,
1069 }
1070 self.perception_metrics.append(metrics)
1071
1073 self,
1074 path: np.ndarray,
1075 path_gt: np.ndarray,
1076 left_cones_gt: np.ndarray,
1077 right_cones_gt: np.ndarray,
1078 ):
1079 """!
1080 Computes planning metrics and publishes them.
1081
1082 Args:
1083 path (np.ndarray): Path computed by the planner.
1084 left_cones_gt (np.ndarray): Ground truth of the left cones.
1085 right_cones_gt (np.ndarray): Ground truth of the right cones.
1086 """
1087 self.get_logger().debug("Received planning")
1088
1089 # Compute instantaneous planning metrics
1090
1091 if (
1092 len(path) == 0
1093 or len(path_gt) == 0
1094 or len(left_cones_gt) == 0
1095 or len(right_cones_gt) == 0
1096 ):
1097 self.get_logger().debug(
1098 "Path, path ground truth or map ground truth info missing"
1099 )
1100 return
1101
1102 # Metric 1: compute distances between cones and closes point in the path
1103 blue_cones = left_cones_gt
1104 yellow_cones = right_cones_gt
1105
1106 useful_blue_cones = find_closest_elements(path, blue_cones)
1107 useful_yellow_cones = find_closest_elements(path, yellow_cones)
1108 distance_to_cones_left = compute_closest_distances(path, useful_blue_cones)
1109 distance_to_cones_right = compute_closest_distances(path, useful_yellow_cones)
1110 general_distance_to_cones = np.concatenate(
1111 [distance_to_cones_left, distance_to_cones_right]
1112 )
1113
1114 mean_cones_difference_left = get_average_error(distance_to_cones_left)
1115 mean_squared_cones_difference_left = get_mean_squared_error(
1116 distance_to_cones_left
1117 )
1118 root_mean_squared_cones_difference_left = (
1119 mean_squared_cones_difference_left ** (1 / 2)
1120 )
1121 mean_cones_difference_right = get_average_error(distance_to_cones_right)
1122 mean_squared_cones_difference_right = get_mean_squared_error(
1123 distance_to_cones_right
1124 )
1125 root_mean_squared_cones_difference_right = (
1126 mean_squared_cones_difference_right ** (1 / 2)
1127 )
1128 mean_cones_difference = get_average_error(general_distance_to_cones)
1129 # if mean_cones_difference > 4.0:
1130 # self.get_logger().info(
1131 # "blue_cones: {}\n yellow_cones: {}\n useful_blue_cones: {}\n useful_yellow_cones: {}\n path: {}".format(
1132 # blue_cones,
1133 # yellow_cones,
1134 # useful_blue_cones,
1135 # useful_yellow_cones,
1136 # path,
1137 # )
1138 # )
1139 mean_squared_cones_difference = get_mean_squared_error(
1140 general_distance_to_cones
1141 )
1142 root_mean_squared_cones_difference = mean_squared_cones_difference ** (1 / 2)
1143 # Metric 2: compute distance between ground truth and closest point in the path
1144 useful_gt_points = find_closest_elements(path, path_gt)
1145 distance_to_gt = compute_closest_distances(path, useful_gt_points)
1146
1147 mean_gt_difference = get_average_error(distance_to_gt)
1148 mean_squared_gt_difference = get_mean_squared_error(distance_to_gt)
1149 root_mean_squared_gt_difference = mean_squared_gt_difference ** (1 / 2)
1150
1151 # Log some computed metrics
1152 self.get_logger().debug(
1153 "Computed planning metrics:\n \
1154 Mean difference to cones: {}\n \
1155 Mean squared difference to cones: {}\n \
1156 Mean difference to ground truth: {}\n \
1157 Mean squared difference to ground truth: {}".format(
1158 mean_cones_difference,
1159 mean_squared_cones_difference,
1160 mean_gt_difference,
1161 mean_squared_gt_difference,
1162 )
1163 )
1164
1165 # Publish planning metrics
1166 self._planning_mean_difference_to_gt.publish(Float32(data=mean_gt_difference))
1168 Float32(data=mean_squared_gt_difference)
1169 )
1171 Float32(data=root_mean_squared_gt_difference)
1172 )
1173
1175 Float32(data=mean_cones_difference_left)
1176 )
1178 Float32(data=mean_squared_cones_difference_left)
1179 )
1181 Float32(data=root_mean_squared_cones_difference_left)
1182 )
1183
1185 Float32(data=mean_cones_difference_right)
1186 )
1188 Float32(data=mean_squared_cones_difference_right)
1189 )
1191 Float32(data=root_mean_squared_cones_difference_right)
1192 )
1193
1195 Float32(data=mean_cones_difference)
1196 )
1198 Float32(data=mean_squared_cones_difference)
1199 )
1201 Float32(data=root_mean_squared_cones_difference)
1202 )
1203
1204 # Compute planning metrics over time
1205 # self._planning_sum_error += get_average_difference(path, path_gt)
1206 # self._planning_squared_sum_error += get_mean_squared_difference(path, path_gt)
1207 # self._planning_mean_root_squared_sum_error += get_mean_squared_difference(
1208 # path, path_gt
1209 # ) ** (1 / 2)
1210 # self._planning_count += 1
1211 # mean_mean_error = Float32()
1212 # mean_mean_error.data = self._planning_sum_error / self._planning_count
1213 # mean_mean_squared_error = Float32()
1214 # mean_mean_squared_error.data = (
1215 # self._planning_squared_sum_error / self._planning_count
1216 # )
1217 # mean_mean_root_squared_error = Float32()
1218 # mean_mean_root_squared_error.data = (
1219 # self._planning_mean_root_squared_sum_error / self._planning_count
1220 # )
1221
1222 # Publish planning metrics over time
1223 # self._planning_mean_mean_error.publish(mean_mean_error)
1224 # self._planning_mean_mean_squared_error.publish(mean_mean_squared_error)
1225 # self._planning_mean_mean_root_squared_error.publish(
1226 # mean_mean_root_squared_error
1227 # )
1228
1229 # For exporting metrics to csv
1230 metrics = {
1231 "timestamp": datetime.datetime.now(),
1232 "mean_difference_to_gt": mean_gt_difference.data,
1233 "mean_squared_difference_to_gt": mean_squared_gt_difference.data,
1234 "root_mean_squared_difference_to_gt": root_mean_squared_gt_difference.data,
1235 "mean_difference_left_cones": mean_cones_difference_left.data,
1236 "mean_squared_difference_left_cones": mean_squared_cones_difference_left.data,
1237 "root_mean_squared_difference_left_cones": root_mean_squared_cones_difference_left.data,
1238 "mean_difference_right_cones": mean_cones_difference_right.data,
1239 "mean_squared_difference_right_cones": mean_squared_cones_difference_right.data,
1240 "root_mean_squared_difference_right_cones": root_mean_squared_cones_difference_right.data,
1241 "mean_difference_to_cones": mean_cones_difference.data,
1242 "mean_squared_difference_to_cones": mean_squared_cones_difference.data,
1243 "root_mean_squared_difference_to_cones": root_mean_squared_cones_difference.data,
1244 }
1245 self.planning_metrics.append(metrics)
1246
1247 def compute_and_publish_control(self, msg: Float64):
1248 """!
1249 Computes control metrics and publishes them.
1250 Args:
1251 msg (Float64): Control execution time message.
1252 """
1253 self.get_logger().debug("Received control")
1254
1255 # Compute closest point
1256 closest_point_distance = float("inf")
1257 closest_point = None
1258 for path_point in self.path_points:
1259 dist = compute_distance(path_point[:2], self.position)
1260 if dist < closest_point_distance:
1261 closest_point_distance = dist
1262 closest_point = path_point
1263
1264 if closest_point is None:
1265 self.get_logger().warn("No closest point found.")
1266 return
1267
1268 velocity_error_value = self.velocity - self.closest_point_velocity
1269
1270 position_error = Float32()
1271 position_error.data = closest_point_distance
1272
1273 velocity_error = Float32()
1274 velocity_error.data = float(velocity_error_value)
1275
1276 self.get_logger().debug(
1277 "Computed control metrics:\n \
1278 Pose Difference: {}\n \
1279 Velocity Difference: {}\n ".format(
1280 position_error,
1281 velocity_error,
1282 )
1283 )
1284
1285 # Publish instantaneous control metrics
1286 self._control_position_error_.publish(position_error)
1287 self._control_velocity_error_.publish(velocity_error)
1288
1289 # =================== Compute Control metrics over time ===================
1290 self._control_position_error_sum += position_error.data
1291 self._control_position_squared_error_sum += position_error.data**2
1292
1293 self._control_count += 1
1294 self._control_velocity_error_sum += float(velocity_error)
1295 self._control_velocity_squared_error_sum += float(velocity_error) ** 2
1296
1297 # Velocity ME
1298 control_velocity_mean_error = Float32()
1299 control_velocity_mean_error.data = float(
1301 )
1302 # Velocity MSE
1303 control_velocity_mean_squared_error = Float32()
1304 control_velocity_mean_squared_error.data = float(
1306 )
1307 # Velocity RMSE
1308 control_velocity_root_mean_squared_error = Float32()
1309 control_velocity_root_mean_squared_error.data = sqrt(
1311 )
1312
1313 # Position ME
1314 control_position_mean_error = Float32()
1315 control_position_mean_error.data = float(
1317 )
1318 # Position MSE
1319 control_position_mean_squared_error = Float32()
1320 control_position_mean_squared_error.data = float(
1322 )
1323 # Position RMSE
1324 control_position_root_mean_squared_error = Float32()
1325 control_position_root_mean_squared_error.data = sqrt(
1327 )
1328
1329 # =============== Publish control metrics over time ================
1330 self._control_position_mean_error_.publish(control_position_mean_error)
1332 control_position_mean_squared_error
1333 )
1335 control_position_root_mean_squared_error
1336 )
1337
1338 self._control_velocity_mean_error_.publish(control_velocity_mean_error)
1340 control_velocity_mean_squared_error
1341 )
1343 control_velocity_root_mean_squared_error
1344 )
1345
1346 self._control_execution_time_.append(
1347 {"timestamp": datetime.datetime.now(), "execution_time": msg.data[0]}
1348 )
1349
1350 # For exporting metrics to csv
1351 metrics = {
1352 "timestamp": datetime.datetime.now(),
1353 "position_error": position_error.data,
1354 "position_mean_error": control_position_mean_error.data,
1355 "position_mean_squared_error": control_position_mean_squared_error.data,
1356 "position_root_mean_squared_error": control_position_root_mean_squared_error.data,
1357 "velocity_error": velocity_error.data,
1358 "velocity_mean_error": control_velocity_mean_error.data,
1359 "velocity_mean_squared_error": control_velocity_mean_squared_error.data,
1360 "velocity_root_mean_squared_error": control_velocity_root_mean_squared_error.data,
1361 }
1362 self.control_metrics.append(metrics)
1363
1364
1365def main(args=None):
1366 """
1367 Main function for the evaluator module.
1368 Args:
1369 args (list): Optional command-line arguments.
1370 """
1371 rclpy.init(args=args)
1372 node = Evaluator()
1373 rclpy.spin(node)
1374 rclpy.shutdown()
1375
1376
1377if __name__ == "__main__":
1378 main()
A ROS2 node for computing and publishing the system's real-time metrics.
Definition evaluator.py:56
None compute_and_publish_perception(self, np.ndarray perception_output, np.ndarray perception_ground_truth)
Computes perception metrics and publishes them.
Definition evaluator.py:924
get_config_yaml_path(self, package_name, dir, filename)
Definition evaluator.py:460
None compute_and_publish_velocities(self, np.ndarray velocities, np.ndarray groundtruth_velocities)
Computes state estimation metrics and publishes them.
Definition evaluator.py:830
None planning_execution_time_callback(self, Float64 msg)
Callback function to store the planning execution time.
Definition evaluator.py:601
None compute_and_publish_pose(self, np.ndarray pose, np.ndarray groundtruth_pose)
Computes state estimation metrics and publishes them.
Definition evaluator.py:618
None prediction_step_time_callback(self, Float64 msg)
Definition evaluator.py:576
None correction_step_time_callback(self, Float64 msg)
Callback function to store the execution time of the correction step.
Definition evaluator.py:563
None signal_handler(self, int sig, frame)
Writes metrics to csv and exits when Ctrl+C is pressed.
Definition evaluator.py:497
compute_and_publish_planning(self, np.ndarray path, np.ndarray path_gt, np.ndarray left_cones_gt, np.ndarray right_cones_gt)
Computes planning metrics and publishes them.
None compute_and_publish_map(self, np.ndarray map, np.ndarray groundtruth_map)
Computes state estimation metrics and publishes them.
Definition evaluator.py:723
None metrics_to_csv(self, list metrics, str filename)
Definition evaluator.py:540
None perception_execution_time_callback(self, Float64 msg)
Callback function to store the perception execution time.
Definition evaluator.py:588
compute_and_publish_control(self, Float64 msg)
Computes control metrics and publishes them.
Definition main.py:1