1- from ...utils import settings
21from ..component import Component
32import rospy
43from gazebo_msgs .msg import ContactsState
5- from datetime import datetime
6- import os
4+ from collections import deque
5+ import time
76
87class GazeboCollisionLogger (Component ):
9- """Component that logs collision data from Gazebo simulation."""
8+ """Logs collision data from Gazebo simulation."""
109
1110 def __init__ (self , vehicle_interface ):
1211 super ().__init__ ()
1312 self .vehicle_interface = vehicle_interface
14- self .log_file = None
15- self .initialized = False
13+ self .collision_messages = deque (maxlen = 100 )
14+ self .last_collision_time = None
15+ self .last_collision_pair = None
16+
1617
1718 def initialize (self ):
18- """Initialize the collision logger component."""
19- if not self .initialized :
20- # Get log folder from settings
21- log_settings = settings .get ('run.log' , {})
22- topfolder = log_settings .get ('folder' , 'logs' )
23- prefix = log_settings .get ('prefix' , '' )
24- suffix = log_settings .get ('suffix' , datetime .now ().strftime ('%Y-%m-%d_%H-%M-%S' ))
25- logfolder = os .path .join (topfolder , prefix + suffix )
26-
27- # Create collision log file
28- self .log_file = os .path .join (logfolder , 'collision_log.txt' )
29- os .makedirs (os .path .dirname (self .log_file ), exist_ok = True )
30-
31- # Subscribe to contact sensor topic
32- rospy .Subscriber ('/contact_sensor' , ContactsState , self .contact_callback )
33-
34- self .initialized = True
35- rospy .loginfo ("Collision logger initialized. Logging to: %s" , self .log_file )
36- rospy .loginfo ("Waiting for collision events..." )
19+ """Initialize the collision logger."""
20+ self .contact_sub = rospy .Subscriber ('/contact_sensor' , ContactsState , self .contact_callback )
21+ rospy .loginfo ("Gazebo collision logger initialized" )
3722
3823 def simplify_collision_name (self , name ):
3924 """Simplify collision names to be more readable."""
@@ -49,60 +34,43 @@ def simplify_collision_name(self, name):
4934
5035 def contact_callback (self , msg ):
5136 """Callback for processing collision messages."""
52- if not msg .states : # If no contacts
37+ if not msg .states :
5338 return
5439
55- timestamp = datetime .now ().strftime ('%Y-%m-%d %H:%M:%S.%f' )[:- 3 ]
56-
57- # Create a dictionary to store unique collisions
58- unique_collisions = {}
40+ current_time = time .time ()
5941
6042 # Process all collision states
6143 for state in msg .states :
62- collision_key = tuple (sorted ([state .collision1_name , state .collision2_name ]))
63- if collision_key not in unique_collisions or state .depths [0 ] > unique_collisions [collision_key ].depths [0 ]:
64- unique_collisions [collision_key ] = state
65-
66- # Log collision information
67- rospy .loginfo ("\n === Collision Detected at %s ===" , timestamp )
68-
69- with open (self .log_file , 'a' ) as f :
70- f .write (f"\n === Collision Detected at { timestamp } ===\n " )
44+ collision1 = self .simplify_collision_name (state .collision1_name )
45+ collision2 = self .simplify_collision_name (state .collision2_name )
7146
72- for i , (collision_key , state ) in enumerate (unique_collisions .items (), 1 ):
73- collision1 = self .simplify_collision_name (state .collision1_name )
74- collision2 = self .simplify_collision_name (state .collision2_name )
75-
76- # Reorder collisions to put vehicle parts first
77- if "vehicle" in collision2 :
78- collision1 , collision2 = collision2 , collision1
79-
80- # Log collision details
81- rospy .loginfo ("\n Contact %d:" , i )
82- rospy .loginfo ("Collision between: %s and %s" , collision1 , collision2 )
47+ # Reorder collisions to put vehicle parts first
48+ if "vehicle" in collision2 :
49+ collision1 , collision2 = collision2 , collision1
50+
51+ collision_pair = tuple (sorted ([collision1 , collision2 ]))
52+
53+ # Only log if it's a new collision or enough time has passed
54+ if (self .last_collision_time is None or
55+ current_time - self .last_collision_time > 0.25 or # Different time
56+ collision_pair != self .last_collision_pair ): # Different collision pair
8357
8458 pos = state .contact_positions [0 ]
85- pos_str = f"Contact Position: x={ pos .x :.3f} , y={ pos .y :.3f} , z={ pos .z :.3f} "
86- rospy .loginfo (pos_str )
87-
8859 normal = state .contact_normals [0 ]
89- normal_str = f"Contact Normal: x={ normal .x :.3f} , y={ normal .y :.3f} , z={ normal .z :.3f} "
90- rospy .loginfo (normal_str )
9160
92- depth_str = f"Contact Depth: { state .depths [0 ]:.3f} "
93- rospy .loginfo (depth_str )
61+ message = (
62+ f"Collision between: { collision1 } and { collision2 } \n "
63+ f"Contact Position: x={ pos .x :.3f} , y={ pos .y :.3f} , z={ pos .z :.3f} \n "
64+ f"Contact Normal: x={ normal .x :.3f} , y={ normal .y :.3f} , z={ normal .z :.3f} \n "
65+ f"Contact Depth: { state .depths [0 ]:.3f} \n "
66+ f"{ '-' * 50 } "
67+ )
9468
95- # Write to file
96- f .write (f"\n Contact { i } :\n " )
97- f .write (f"Collision between: { collision1 } and { collision2 } \n " )
98- f .write (f"{ pos_str } \n " )
99- f .write (f"{ normal_str } \n " )
100- f .write (f"{ depth_str } \n " )
101- f .write ("-" * 50 + "\n " )
102-
103- f .write ("\n " )
104- rospy .loginfo ("-" * 50 )
69+ self .collision_messages .append (message )
70+ self .last_collision_time = current_time
71+ self .last_collision_pair = collision_pair
10572
10673 def update (self ):
107- """Update method required by Component base class."""
108- pass # All work is done in the ROS callback
74+ """Output collision messages to the console/log."""
75+ while self .collision_messages :
76+ print (self .collision_messages .popleft ())
0 commit comments