11#!/usr/bin/python3
2+ # Licensed under GPLv2. See LICENSE file. Copyright Carologistics.
3+ from decimal import Decimal
24
5+ import matplotlib .pyplot as plt
6+ import pandas as pd
37import rclpy
4- from rclpy .node import Node
5- from nav_msgs .msg import Path
6- from tf2_msgs .msg import TFMessage
78from rcl_interfaces .msg import Log
8- from decimal import Decimal
9- from builtin_interfaces .msg import Time
10- import pandas as pd
11- import matplotlib .pyplot as plt
9+ from rclpy .node import Node
1210from tf2_ros import TransformException
1311from tf2_ros .buffer import Buffer
1412from tf2_ros .transform_listener import TransformListener
15- from PIL import Image
13+
1614
1715class DataExtractorNode (Node ):
1816 def __init__ (self ):
19- super ().__init__ (' data_extractor' )
17+ super ().__init__ (" data_extractor" )
2018 self .declare_parameter ("namespace" , "robotinobase1" )
2119 self .namespace = self .get_parameter ("namespace" ).get_parameter_value ().string_value
2220 self .goal_start_time = None
2321 self .goal_end_time = None
2422
2523 # Subscribe to rosout topic
26- self .rosout_sub = self .create_subscription (Log , ' /rosbag_rosout' , self .rosout_callback , 10 )
24+ self .rosout_sub = self .create_subscription (Log , " /rosbag_rosout" , self .rosout_callback , 10 )
2725
2826 # Subscribe to namespace plan topic
29- #self.plan_sub = self.create_subscription(Path, self.namespace+'/plan', self.plan_callback, 10)
27+ # self.plan_sub = self.create_subscription(Path, self.namespace+'/plan', self.plan_callback, 10)
3028
3129 # Call on_timer function every second
3230 self .timer = self .create_timer (0.1 , self .on_timer )
3331
3432 # Declare and acquire `target_frame` parameter
35- self .target_frame = self .namespace + ' /base_link'
33+ self .target_frame = self .namespace + " /base_link"
3634 self .tf_buffer = Buffer ()
3735 self .tf_listener = TransformListener (self .tf_buffer , self )
3836
3937 # Define the lookup table for the poses
4038 self .machine_pose = {
4139 "C-BS-I" : (5.50 , 2.50 , 90 ),
4240 "C-BS-O" : (5.50 , 4.50 , 270 ),
43- "C-RS2-I" : (3.50 , 5.50 ,180 ),
44- "C-RS2-O" : (5.50 , 5.50 ,0 ),
45- "C-CS1-I" : (0.50 , 5.50 ,180 ),
46- "C-CS1-O" : (2.50 , 5.50 ,0 ),
47- "C-CS2-I" : (0.50 , 1.50 ,180 ),
48- "C-CS2-O" : (2.50 , 1.50 ,0 ),
41+ "C-RS2-I" : (3.50 , 5.50 , 180 ),
42+ "C-RS2-O" : (5.50 , 5.50 , 0 ),
43+ "C-CS1-I" : (0.50 , 5.50 , 180 ),
44+ "C-CS1-O" : (2.50 , 5.50 , 0 ),
45+ "C-CS2-I" : (0.50 , 1.50 , 180 ),
46+ "C-CS2-O" : (2.50 , 1.50 , 0 ),
4947 "C-DS-I" : (0.50 , 0.50 , 180 ),
5048 "C-DS-O" : (2.50 , 0.50 , 0 ),
5149 "C-RS1-I" : (1.50 , 4.50 , 270 ),
@@ -54,12 +52,12 @@ def __init__(self):
5452 "C-SS-O" : (4.50 , 3.50 , 270 ),
5553 "M-BS-I" : (- 5.50 , 2.50 , 90 ),
5654 "M-BS-O" : (- 5.50 , 4.50 , 270 ),
57- "M-RS2-I" : (- 3.50 , 5.50 ,0 ),
58- "M-RS2-O" : (- 5.50 , 5.50 ,180 ),
59- "M-CS1-I" : (- 0.50 , 5.50 ,0 ),
60- "M-CS1-O" : (- 2.50 , 5.50 ,180 ),
61- "M-CS2-I" : (- 0.50 , 1.50 ,0 ),
62- "M-CS2-O" : (- 2.50 , 1.50 ,180 ),
55+ "M-RS2-I" : (- 3.50 , 5.50 , 0 ),
56+ "M-RS2-O" : (- 5.50 , 5.50 , 180 ),
57+ "M-CS1-I" : (- 0.50 , 5.50 , 0 ),
58+ "M-CS1-O" : (- 2.50 , 5.50 , 180 ),
59+ "M-CS2-I" : (- 0.50 , 1.50 , 0 ),
60+ "M-CS2-O" : (- 2.50 , 1.50 , 180 ),
6361 "M-DS-I" : (- 0.50 , 0.50 , 0 ),
6462 "M-DS-O" : (- 2.50 , 0.50 , 180 ),
6563 "M-RS1-I" : (- 1.50 , 4.50 , 270 ),
@@ -68,7 +66,17 @@ def __init__(self):
6866 "M-SS-O" : (- 4.50 , 3.50 , 270 ),
6967 }
7068
71- self .columns = ["pose_start_x" , "pose_start_y" , "pose_end_x" , "pose_end_y" , "machine_start" , "machine_end" , "time_start" , "time_end" , "time_diff" ]
69+ self .columns = [
70+ "pose_start_x" ,
71+ "pose_start_y" ,
72+ "pose_end_x" ,
73+ "pose_end_y" ,
74+ "machine_start" ,
75+ "machine_end" ,
76+ "time_start" ,
77+ "time_end" ,
78+ "time_diff" ,
79+ ]
7280 self .table = pd .DataFrame (columns = self .columns )
7381
7482 self .columns_ = ["pose_x" , "pose_y" ]
@@ -85,7 +93,7 @@ def __init__(self):
8593 self .record_data = False
8694
8795 def rosout_callback (self , msg ):
88- if msg .name == self .namespace + ' .bt_navigator' :
96+ if msg .name == self .namespace + " .bt_navigator" :
8997 if self .msg_check (msg .msg , "Begin navigating" ):
9098 self .goal_start_time = self .get_time (msg )
9199 self .pose_data = self .machine_pose_check (msg .msg )
@@ -98,14 +106,14 @@ def rosout_callback(self, msg):
98106 "pose_end_y" : [self .pose_data [3 ]],
99107 "machine_start" : [self .machine_start ],
100108 "machine_end" : [self .machine_end ],
101- "time_start" : [self .goal_start_time ]
109+ "time_start" : [self .goal_start_time ],
102110 }
103111 self .table = pd .concat ([self .table , pd .DataFrame (data_row )], ignore_index = True )
104112
105113 if msg .msg == "Goal succeeded" :
106114 self .goal_end_time = self .get_time (msg )
107115 self .table .at [self .counter , "time_end" ] = self .goal_end_time
108- #self.table.at[self.counter, "time_diff"] = self.goal_end_time - self.goal_start_time
116+ # self.table.at[self.counter, "time_diff"] = self.goal_end_time - self.goal_start_time
109117 self .machine_start = self .machine_end
110118 self .counter += 1
111119 if self .counter in [1 , 5 , 9 , 13 , 17 ]:
@@ -117,10 +125,9 @@ def rosout_callback(self, msg):
117125 # self.table.to_csv(self.namespace+"_pose_data.csv", index=False)
118126 # self.get_logger().info(f'Pose data saved, check rosbag status and shutdown the node')
119127
120-
121128 def msg_check (self , message , check ):
122129 words = message .split ()
123- first_two = ' ' .join (words [:2 ])
130+ first_two = " " .join (words [:2 ])
124131 return first_two == check
125132
126133 def machine_pose_check (self , message ):
@@ -132,7 +139,7 @@ def machine_pose_check(self, message):
132139 for key , value in self .machine_pose .items ():
133140 if end_pose_x == value [0 ] and end_pose_y == value [1 ]:
134141 machine_end = key
135- self .get_logger ().info (f' machine end: { machine_end } ' )
142+ self .get_logger ().info (f" machine end: { machine_end } " )
136143 return [start_pose_x , start_pose_y , end_pose_x , end_pose_y , machine_end ]
137144
138145 def get_time (self , msg ):
@@ -141,47 +148,44 @@ def get_time(self, msg):
141148 def on_timer (self ):
142149 if self .record_data :
143150 try :
144- t = self .tf_buffer .lookup_transform (' map' , self .target_frame , rclpy .time .Time ())
151+ t = self .tf_buffer .lookup_transform (" map" , self .target_frame , rclpy .time .Time ())
145152
146153 except TransformException as ex :
147- self .get_logger ().info (
148- f'Could not transform { self .target_frame } to map: { ex } ' )
154+ self .get_logger ().info (f"Could not transform { self .target_frame } to map: { ex } " )
149155 return
150156 self .robot_pose_x .append (t .transform .translation .x )
151157 self .robot_pose_y .append (t .transform .translation .y )
152158
153- data_row = {
154- "pose_x" : [t .transform .translation .x ],
155- "pose_y" : [t .transform .translation .y ]
156- }
159+ data_row = {"pose_x" : [t .transform .translation .x ], "pose_y" : [t .transform .translation .y ]}
157160 self .table_ = pd .concat ([self .table_ , pd .DataFrame (data_row )], ignore_index = True )
158161
159-
160162 if self .plot_data :
161163 quo , rem = divmod (self .counter , 4 )
162164
163165 plt .figure (quo )
164166
165167 plt .clf ()
166- plt .plot (self .robot_pose_x , self .robot_pose_y ,'-b' )
167- plt .xlabel (' X_Position' )
168- plt .ylabel (' Y_Position' )
169- plt .title (self .namespace + ' _iteration' + str (quo )+ ' _followed_Path' )
168+ plt .plot (self .robot_pose_x , self .robot_pose_y , "-b" )
169+ plt .xlabel (" X_Position" )
170+ plt .ylabel (" Y_Position" )
171+ plt .title (self .namespace + " _iteration" + str (quo ) + " _followed_Path" )
170172 plt .grid (True )
171173 plt .xlim (- 6 , 6 )
172174 plt .ylim (0 , 6 )
173- plt .gca ().set_aspect (' equal' )
175+ plt .gca ().set_aspect (" equal" )
174176
175- plt .text (self .robot_pose_x [0 ], self .robot_pose_y [0 ], ' Start' , fontsize = 12 , color = ' green' , ha = ' right' )
176- plt .text (self .robot_pose_x [- 1 ], self .robot_pose_y [- 1 ], ' End' , fontsize = 12 , color = ' red' , ha = ' right' )
177+ plt .text (self .robot_pose_x [0 ], self .robot_pose_y [0 ], " Start" , fontsize = 12 , color = " green" , ha = " right" )
178+ plt .text (self .robot_pose_x [- 1 ], self .robot_pose_y [- 1 ], " End" , fontsize = 12 , color = " red" , ha = " right" )
177179
178- plt .savefig (self .namespace + '_iteration' + str (quo )+ '_followed_path' + '.png' ) # Save the plot as PNG file
179- self .table_ .to_csv (self .namespace + '_iteration' + str (quo )+ '_pose_data.csv' , index = False )
180+ plt .savefig (
181+ self .namespace + "_iteration" + str (quo ) + "_followed_path" + ".png"
182+ ) # Save the plot as PNG file
183+ self .table_ .to_csv (self .namespace + "_iteration" + str (quo ) + "_pose_data.csv" , index = False )
180184
181185 self .columns_ = ["pose_x" , "pose_y" ]
182186 self .table_ = pd .DataFrame (columns = self .columns_ )
183187
184- self .get_logger ().info (f' Plotting followed path data for iteration { quo } ' )
188+ self .get_logger ().info (f" Plotting followed path data for iteration { quo } " )
185189
186190 plt .close ()
187191
@@ -221,16 +225,18 @@ def on_timer(self):
221225 # self.plan_pose_x = []
222226 # self.plan_pose_y = []
223227
228+
224229def main (args = None ):
225230 rclpy .init (args = args )
226231 node = DataExtractorNode ()
227232 try :
228- node .get_logger ().info (' Beginning data extraction node, shut down with CTRL-C' )
233+ node .get_logger ().info (" Beginning data extraction node, shut down with CTRL-C" )
229234 rclpy .spin (node )
230235 except KeyboardInterrupt :
231- node .get_logger ().info (' Keyboard interrupt, shutting down.\n ' )
236+ node .get_logger ().info (" Keyboard interrupt, shutting down.\n " )
232237 node .destroy_node ()
233238 rclpy .shutdown ()
234239
235- if __name__ == '__main__' :
240+
241+ if __name__ == "__main__" :
236242 main ()
0 commit comments