1+ from GEMstack .state .obstacle import ObstacleMaterialEnum , ObstacleState , ObstacleStateEnum
12from .gem import *
23from ...utils import settings
34import math
@@ -53,7 +54,13 @@ class GNSSReading:
5354 'car' : 'car' ,
5455 'vehicle' : 'car' ,
5556 'truck' : 'medium_truck' ,
56- 'large_truck' : 'large_truck'
57+ 'large_truck' : 'large_truck' ,
58+ 'cone' : 'traffic_cone'
59+ }
60+
61+ # Map model prefixes to obstacle types
62+ MODEL_PREFIX_TO_OBSTACLE_TYPE = {
63+ 'cone' : 'traffic_cone'
5764}
5865
5966class GEMGazeboInterface (GEMInterface ):
@@ -93,11 +100,15 @@ def __init__(self):
93100 self .model_states_sub = None
94101 self .tracked_model_prefixes = settings .get ('simulator.agent_tracker.model_prefixes' ,
95102 ['pedestrian' , 'bicycle' , 'car' ])
103+ self .tracked_obstacle_prefixes = settings .get ('simulator.obstacle_tracker.model_prefixes' ,
104+ ['cone' ])
96105 self .agent_detector_callback = None
106+ self .obstacle_detector_callback = None
97107 self .last_agent_positions = {}
98108 self .last_agent_velocities = {}
99109 self .last_model_states_time = 0.0
100110 self .agent_detection_rate = settings .get ('simulator.agent_tracker.rate' , 10.0 ) # Hz
111+ self .obstacle_detection_rate = settings .get ('simulator.obstacle_tracker.rate' , 50.0 ) # Hz
101112
102113 # Frame transformation variables
103114 self .start_pose_abs = None # Initial vehicle pose in GLOBAL frame
@@ -141,15 +152,15 @@ def model_states_callback(self, msg: ModelStates):
141152 current_time = self .time ()
142153
143154 # Check if we should process this update (rate limiting)
144- if current_time - self .last_model_states_time < 1.0 / self .agent_detection_rate :
155+ if (( current_time - self .last_model_states_time < 1.0 / self .agent_detection_rate ) and ( current_time - self . last_model_states_time < 1.0 / self . obstacle_detection_rate )) :
145156 return
146157
147158 # Calculate time delta since last update
148159 dt = current_time - self .last_model_states_time
149160 self .last_model_states_time = current_time
150161
151162 # Skip if no callback is registered
152- if self .agent_detector_callback is None :
163+ if self .agent_detector_callback is None and self . obstacle_detector_callback is None :
153164 return
154165
155166 # Find vehicle in model states
@@ -203,16 +214,24 @@ def model_states_callback(self, msg: ModelStates):
203214 agent_type = None
204215 for prefix in self .tracked_model_prefixes :
205216 if model_name .lower ().startswith (prefix .lower ()):
206- # Find the appropriate agent type from the prefix
207217 for key , value in MODEL_PREFIX_TO_AGENT_TYPE .items ():
208218 if prefix .lower ().startswith (key .lower ()):
209219 agent_type = value
210220 break
211221 break
222+
223+ obstacle_type = None
224+ for prefix in self .tracked_obstacle_prefixes :
225+ if model_name .lower ().startswith (prefix .lower ()):
226+ for key , value in MODEL_PREFIX_TO_OBSTACLE_TYPE .items ():
227+ if prefix .lower ().startswith (key .lower ()):
228+ obstacle_type = value
229+ break
230+ break
212231
213- if agent_type is None :
232+ if agent_type is None and obstacle_type is None :
214233 continue # Not an agent we're tracking
215-
234+
216235 # Get position and orientation from model states
217236 position = msg .pose [i ].position
218237 orientation = msg .pose [i ].orientation
@@ -224,7 +243,6 @@ def model_states_callback(self, msg: ModelStates):
224243 # Convert orientation quaternion to euler angles
225244 quaternion = (orientation .x , orientation .y , orientation .z , orientation .w )
226245 roll , pitch , yaw = euler_from_quaternion (quaternion )
227-
228246 # Create agent pose in ABSOLUTE_CARTESIAN frame (Gazebo's native frame)
229247 agent_global_pose = ObjectPose (
230248 frame = ObjectFrameEnum .ABSOLUTE_CARTESIAN ,
@@ -269,61 +287,72 @@ def model_states_callback(self, msg: ModelStates):
269287 # If transformation not initialized yet, just use the model_states pose
270288 agent_pose = agent_global_pose
271289
272- # Calculate velocity manually if twist data is zero or missing
273- velocity = (linear_vel .x , linear_vel .y , linear_vel .z )
274- velocity_is_zero = abs (linear_vel .x ) < 1e-6 and abs (linear_vel .y ) < 1e-6 and abs (linear_vel .z ) < 1e-6
275-
276- if velocity_is_zero and model_name in self .last_agent_positions and dt > 0 :
277- # Calculate velocity from position difference
278- prev_pos = self .last_agent_positions [model_name ]
279- dx = position .x - prev_pos [0 ]
280- dy = position .y - prev_pos [1 ]
281- dz = position .z - prev_pos [2 ]
290+ if agent_type is not None :
291+ # Calculate velocity manually if twist data is zero or missing
292+ velocity = (linear_vel .x , linear_vel .y , linear_vel .z )
293+ velocity_is_zero = abs (linear_vel .x ) < 1e-6 and abs (linear_vel .y ) < 1e-6 and abs (linear_vel .z ) < 1e-6
282294
283- # Calculate velocity (position change / time)
284- calculated_vel = (dx / dt , dy / dt , dz / dt )
295+ if velocity_is_zero and model_name in self .last_agent_positions and dt > 0 :
296+ # Calculate velocity from position difference
297+ prev_pos = self .last_agent_positions [model_name ]
298+ dx = position .x - prev_pos [0 ]
299+ dy = position .y - prev_pos [1 ]
300+ dz = position .z - prev_pos [2 ]
301+
302+ # Calculate velocity (position change / time)
303+ calculated_vel = (dx / dt , dy / dt , dz / dt )
304+
305+ # Apply some smoothing with the previous velocity if available
306+ if model_name in self .last_agent_velocities :
307+ prev_vel = self .last_agent_velocities [model_name ]
308+ # Apply exponential smoothing (0.7 current + 0.3 previous)
309+ velocity = (
310+ 0.7 * calculated_vel [0 ] + 0.3 * prev_vel [0 ],
311+ 0.7 * calculated_vel [1 ] + 0.3 * prev_vel [1 ],
312+ 0.7 * calculated_vel [2 ] + 0.3 * prev_vel [2 ]
313+ )
314+ else :
315+ velocity = calculated_vel
285316
286- # Apply some smoothing with the previous velocity if available
287- if model_name in self .last_agent_velocities :
288- prev_vel = self .last_agent_velocities [model_name ]
289- # Apply exponential smoothing (0.7 current + 0.3 previous)
290- velocity = (
291- 0.7 * calculated_vel [0 ] + 0.3 * prev_vel [0 ],
292- 0.7 * calculated_vel [1 ] + 0.3 * prev_vel [1 ],
293- 0.7 * calculated_vel [2 ] + 0.3 * prev_vel [2 ]
294- )
317+ # Determine activity state based on velocity magnitude
318+ velocity_magnitude = np .linalg .norm (velocity )
319+ if velocity_magnitude < 0.1 :
320+ activity = AgentActivityEnum .STOPPED
321+ elif velocity_magnitude > 5.0 : # Arbitrary threshold for "fast"
322+ activity = AgentActivityEnum .FAST
295323 else :
296- velocity = calculated_vel
324+ activity = AgentActivityEnum .MOVING
325+
326+ # Get agent dimensions
327+ dimensions = AGENT_DIMENSIONS .get (agent_type , (1.0 , 1.0 , 1.0 )) # Default if unknown
297328
298- # Determine activity state based on velocity magnitude
299- velocity_magnitude = np .linalg .norm (velocity )
300- if velocity_magnitude < 0.1 :
301- activity = AgentActivityEnum .STOPPED
302- elif velocity_magnitude > 5.0 : # Arbitrary threshold for "fast"
303- activity = AgentActivityEnum .FAST
304- else :
305- activity = AgentActivityEnum .MOVING
329+ # Create agent state
330+ agent_state = AgentState (
331+ pose = agent_pose , # Using START frame pose
332+ dimensions = dimensions ,
333+ outline = None ,
334+ type = getattr (AgentEnum , agent_type .upper ()),
335+ activity = activity ,
336+ velocity = velocity ,
337+ yaw_rate = angular_vel .z
338+ )
306339
307- # Get agent dimensions
308- dimensions = AGENT_DIMENSIONS .get (agent_type , (1.0 , 1.0 , 1.0 )) # Default if unknown
309-
310- # Create agent state
311- agent_state = AgentState (
312- pose = agent_pose , # Using START frame pose
313- dimensions = dimensions ,
314- outline = None ,
315- type = getattr (AgentEnum , agent_type .upper ()),
316- activity = activity ,
317- velocity = velocity ,
318- yaw_rate = angular_vel .z
319- )
320-
321- # Store current position for next velocity calculation (using raw positions)
322- self .last_agent_positions [model_name ] = (position .x , position .y , position .z )
323- self .last_agent_velocities [model_name ] = velocity
324-
325- # Call the callback with the agent state
326- self .agent_detector_callback (model_name , agent_state )
340+ # Store current position for next velocity calculation (using raw positions)
341+ self .last_agent_positions [model_name ] = (position .x , position .y , position .z )
342+ self .last_agent_velocities [model_name ] = velocity
343+ # Call the callback with the agent state
344+ self .agent_detector_callback (model_name , agent_state )
345+
346+ if obstacle_type is not None :
347+ # Create obstacle state
348+ obstacle_state = ObstacleState (
349+ dimensions = (0 ,0 ,0 ),
350+ outline = None ,
351+ pose = agent_pose ,
352+ type = getattr (ObstacleMaterialEnum , obstacle_type .upper ()),
353+ activity = ObstacleStateEnum .STANDING ,
354+ )
355+ self .obstacle_detector_callback (model_name , obstacle_state )
327356
328357 def subscribe_sensor (self , name , callback , type = None ):
329358 if name == 'gnss' :
@@ -424,6 +453,11 @@ def callback_with_cv2(msg: Image):
424453 raise ValueError ("GEMGazeboInterface only supports AgentState for agent_detector" )
425454 self .agent_detector_callback = callback
426455
456+ elif name == 'obstacle_detector' :
457+ if type is not None and type is not ObstacleState :
458+ raise ValueError ("GEMGazeboInterface only supports ObstacleState for obstacle_detector" )
459+ self .obstacle_detector_callback = callback
460+
427461 def hardware_faults (self ) -> List [str ]:
428462 # In simulation, we don't have real hardware faults
429463 return self .faults
@@ -459,10 +493,12 @@ def send_command(self, command : GEMVehicleCommand):
459493
460494 # Calculate acceleration from pedal positions
461495 acceleration = pedal_positions_to_acceleration (accelerator_pedal_position , brake_pedal_position , v , 0 , 1 )
462-
496+ print ( "acceleration before" , acceleration )
463497 # Apply reasonable limits to acceleration
464498 max_accel = settings .get ('vehicle.limits.max_acceleration' , 1.0 )
465- max_decel = settings .get ('vehicle.limits.max_deceleration' , - 2.0 )
499+ max_decel = - 1 * settings .get ('vehicle.limits.max_deceleration' , 2.0 ) # cuz ackermann expects neg but pure pursiut wants positive decel val
500+ print ("max_accel" , max_accel )
501+ print ("max_decel" , max_decel )
466502 acceleration = np .clip (acceleration , max_decel , max_accel )
467503
468504 # Convert wheel angle to steering angle (front wheel angle)
@@ -477,14 +513,15 @@ def send_command(self, command : GEMVehicleCommand):
477513 # Don't use infinite speed, instead calculate a reasonable target speed
478514 current_speed = v
479515 target_speed = current_speed
480-
516+ print ( "acceleration " , acceleration )
481517 if acceleration > 0 :
482518 # Accelerating - set target speed to current speed plus some increment
483519 # This is more realistic than infinite speed
484520 max_speed = settings .get ('vehicle.limits.max_speed' , 10.0 )
485521 target_speed = min (current_speed + acceleration * 0.5 , max_speed )
486522 elif acceleration < 0 :
487523 # Braking - set target speed to zero if deceleration is significant
524+ print ("braking " , acceleration )
488525 if brake_pedal_position > 0.1 :
489526 target_speed = 0.0
490527
@@ -499,4 +536,4 @@ def send_command(self, command : GEMVehicleCommand):
499536 print (f"[ACKERMANN] Speed: { msg .speed :.2f} , Accel: { msg .acceleration :.2f} , Steer: { msg .steering_angle :.2f} " )
500537
501538 self .ackermann_pub .publish (msg )
502- self .last_command = command
539+ self .last_command = command
0 commit comments