@@ -258,6 +258,8 @@ ReactionAnalyzerNode::ReactionAnalyzerNode(rclcpp::NodeOptions options)
258
258
}
259
259
260
260
node_params_.timer_period = get_parameter (" timer_period" ).as_double ();
261
+ node_params_.test_iteration = get_parameter (" test_iteration" ).as_int ();
262
+ node_params_.output_file_path = get_parameter (" output_file_path" ).as_string ();
261
263
node_params_.object_search_radius_offset =
262
264
get_parameter (" object_search_radius_offset" ).as_double ();
263
265
node_params_.spawn_time_after_init = get_parameter (" spawn_time_after_init" ).as_double ();
@@ -507,20 +509,12 @@ void ReactionAnalyzerNode::printResults(
507
509
const std::pair<std::string, rclcpp::Time> & a,
508
510
const std::pair<std::string, rclcpp::Time> & b) { return a.second < b.second ; });
509
511
510
- // first print spawn_cmd_time to first reaction
511
- // std::cout << "spawn_cmd_time to " << reaction_times.begin()->first << ": "
512
- // << createDurationMs(spawn_cmd_time_.value(), reaction_times.begin()->second)
513
- // << std::endl;
514
-
515
- // if (reaction_times.size() < 2) return;
516
-
517
- for (size_t i = 0 ; i < reaction_times.size (); i++) {
518
- const auto & first_reaction = reaction_times.at (i);
519
- // const auto & second_reaction = reaction_times.at(i + 1);
520
-
521
- std::cout << " spawn_cmd_time to " << first_reaction.first << " : "
522
- << createDurationMs (spawn_cmd_time_.value (), first_reaction.second ) << std::endl;
512
+ for (const auto & reaction_time : reaction_times) {
513
+ const double duration = createDurationMs (spawn_cmd_time_.value (), reaction_time.second );
514
+ test_results_[reaction_time.first ].emplace_back (duration);
515
+ std::cout << " spawn_cmd_time to " << reaction_time.first << " : " << duration << std::endl;
523
516
}
517
+ test_iteration_count_++;
524
518
is_output_printed_ = true ;
525
519
}
526
520
@@ -988,6 +982,11 @@ bool ReactionAnalyzerNode::searchDetectedObjectsNearEntity(const DetectedObjects
988
982
989
983
void ReactionAnalyzerNode::reset ()
990
984
{
985
+ if (test_iteration_count_ >= node_params_.test_iteration ) {
986
+ writeResultsToFile ();
987
+ RCLCPP_INFO (get_logger (), " Test finished. Node shutting down." );
988
+ rclcpp::shutdown ();
989
+ }
991
990
is_vehicle_initialized_ = false ;
992
991
is_route_set_ = false ;
993
992
test_environment_init_time_ = std::nullopt;
@@ -1004,6 +1003,79 @@ void ReactionAnalyzerNode::reset()
1004
1003
mutex_.unlock ();
1005
1004
}
1006
1005
1006
+ void ReactionAnalyzerNode::writeResultsToFile ()
1007
+ {
1008
+ // create csv file
1009
+ auto now = std::chrono::system_clock::now ();
1010
+ auto in_time_t = std::chrono::system_clock::to_time_t (now);
1011
+
1012
+ std::stringstream ss;
1013
+ ss << node_params_.output_file_path ;
1014
+ if (!node_params_.output_file_path .empty () && node_params_.output_file_path .back () != ' /' ) {
1015
+ ss << " /" ; // Ensure the path ends with a slash
1016
+ }
1017
+ if (node_running_mode_ == RunningMode::PlanningControl) {
1018
+ ss << " planning_control-" ;
1019
+ } else {
1020
+ ss << " perception_planning-" ;
1021
+ }
1022
+
1023
+ ss << std::put_time (std::localtime (&in_time_t ), " %Y-%m-%d-%H-%M-%S" );
1024
+ ss << " -reaction-results.csv" ;
1025
+
1026
+ // parse the results
1027
+ std::ofstream file (ss.str ());
1028
+ // Check if the file was opened successfully
1029
+ if (!file.is_open ()) {
1030
+ std::cerr << " Failed to open file: " << ss.str () << std::endl;
1031
+ return ;
1032
+ }
1033
+
1034
+ bool is_header_written = false ;
1035
+
1036
+ for (const auto & [node, durations] : test_results_) {
1037
+ if (!is_header_written) {
1038
+ file << " Node," ;
1039
+ const size_t num_durations = durations.size ();
1040
+ for (size_t i = 0 ; i < num_durations; ++i) {
1041
+ file << " Test - " << i + 1 << " ," ;
1042
+ }
1043
+ file << " Min,Max,Mean,Median,StdDev\n " ;
1044
+ is_header_written = true ;
1045
+ }
1046
+
1047
+ // parse test results
1048
+ file << node << " ," ;
1049
+ for (double i : durations) {
1050
+ file << i << " ," ;
1051
+ }
1052
+
1053
+ // calculate stats
1054
+ const double min = *std::min_element (durations.begin (), durations.end ());
1055
+ const double max = *std::max_element (durations.begin (), durations.end ());
1056
+
1057
+ std::vector<double > sorted_data = durations;
1058
+ std::sort (sorted_data.begin (), sorted_data.end ());
1059
+ const double median =
1060
+ sorted_data.size () % 2 == 0
1061
+ ? (sorted_data[sorted_data.size () / 2 - 1 ] + sorted_data[sorted_data.size () / 2 ]) / 2
1062
+ : sorted_data[sorted_data.size () / 2 ];
1063
+
1064
+ const double mean =
1065
+ std::accumulate (sorted_data.begin (), sorted_data.end (), 0.0 ) / sorted_data.size ();
1066
+ const double sq_sum = std::inner_product (
1067
+ sorted_data.begin (), sorted_data.end (), sorted_data.begin (), 0.0 , std::plus<>(),
1068
+ [&](double a, double b) { return (a - mean) * (b - mean); });
1069
+ double std_dev = std::sqrt (sq_sum / sorted_data.size ());
1070
+
1071
+ // parse stats
1072
+ file << min << " ," << max << " ," << mean << " ," << median << " ," << std_dev << " \n " ;
1073
+
1074
+ }
1075
+ file.close ();
1076
+ std::cout << " Results written to: " << ss.str () << std::endl;
1077
+ }
1078
+
1007
1079
} // namespace reaction_analyzer
1008
1080
1009
1081
#include < rclcpp_components/register_node_macro.hpp>
0 commit comments