1+ from typing import List
2+ from ..component import Component
3+ from ...state import AllState , VehicleIntent
4+ import time
5+ import math
6+
7+ class ParkingLogic (Component ):
8+ """
9+ Component that handles parking logic for GEM vehicles.
10+ This includes finding a parking spot and executing parking maneuvers.
11+ """
12+ # Intent codes
13+ INTENT_NORMAL_DRIVING = 0
14+ INTENT_SEARCHING = 1
15+ INTENT_PARKING = 2
16+ INTENT_PARKED = 3
17+ INTENT_EXITING = 4
18+
19+ # Parking state machine states
20+ STATE_IDLE = 0
21+ STATE_SEARCHING = 1
22+ STATE_APPROACH = 2
23+ STATE_ALIGNING = 3
24+ STATE_BACKING = 4
25+ STATE_ADJUSTING = 5
26+ STATE_PARKED = 6
27+ STATE_EXITING = 7
28+
29+ def rate (self ) -> float :
30+ """Returns the rate in Hz at which this component should be updated."""
31+ return 10.0 # Higher rate for more responsive parking
32+
33+ def state_inputs (self ) -> List [str ]:
34+ """Returns the list of AllState inputs this component requires."""
35+ return ['all' , 'perception' , 'localization' ]
36+
37+ def state_outputs (self ) -> List [str ]:
38+ """Returns the list of AllState outputs this component generates."""
39+ return ['intent' , 'trajectory' ]
40+
41+ def healthy (self ):
42+ """Returns True if the element is in a stable state."""
43+ return self .error_count < 5 # Consider unhealthy after multiple errors
44+
45+ def initialize (self ):
46+ """Initialize the component. This is called once before the first update."""
47+ self .parking_state = self .STATE_IDLE
48+ self .spot_found = False
49+ self .error_count = 0
50+ self .start_time = time .time ()
51+ self .last_state_change = time .time ()
52+
53+ # Parking spot characteristics
54+ self .min_spot_length = 5.0 # meters
55+ self .min_spot_width = 2.5 # meters
56+
57+ # Vehicle characteristics (example values)
58+ self .vehicle_length = 3.5 # meters
59+ self .vehicle_width = 1.5 # meters
60+
61+ # Parking maneuver parameters
62+ self .approach_distance = 1.0 # meters ahead of spot
63+ self .backing_speed = 0.5 # m/s
64+ self .forward_speed = 1.0 # m/s
65+
66+ # For detecting a spot
67+ self .last_obstacles = []
68+ self .potential_spots = []
69+
70+ # Parking spot once found
71+ self .target_spot = None
72+
73+ # Debug information
74+ self .debug ("initialized" , True )
75+ self .debug_event ("ParkingLogic initialized" )
76+
77+ def cleanup (self ):
78+ """Cleans up resources used by the component."""
79+ self .debug_event ("ParkingLogic cleanup" )
80+ pass
81+
82+ def update (self , state : AllState ):
83+ """Update the component based on current state."""
84+ try :
85+ if self .parking_state == self .STATE_IDLE :
86+ # Enter searching state
87+ self .parking_state = self .STATE_SEARCHING
88+ self .debug_event ("Starting to search for parking spot" )
89+ state .intent .intent = self .INTENT_SEARCHING
90+
91+ elif self .parking_state == self .STATE_SEARCHING :
92+ # Logic to search for a parking spot using perception data
93+ if self ._find_parking_spot (state ):
94+ self .parking_state = self .STATE_APPROACH
95+ self .debug_event (f"Parking spot found at { self .target_spot } " )
96+ state .intent .intent = self .INTENT_PARKING
97+ else :
98+ # Continue searching
99+ self ._maintain_search_pattern (state )
100+
101+ elif self .parking_state == self .STATE_APPROACH :
102+ # Approach the parking spot
103+ if self ._approach_parking_spot (state ):
104+ self .parking_state = self .STATE_ALIGNING
105+ self .debug_event ("Aligned with parking spot" )
106+
107+ elif self .parking_state == self .STATE_ALIGNING :
108+ # Align the vehicle parallel to the parking spot
109+ if self ._align_with_spot (state ):
110+ self .parking_state = self .STATE_BACKING
111+ self .debug_event ("Starting to back into spot" )
112+
113+ elif self .parking_state == self .STATE_BACKING :
114+ # Back into the parking spot
115+ if self ._back_into_spot (state ):
116+ self .parking_state = self .STATE_ADJUSTING
117+ self .debug_event ("Backed into spot, making final adjustments" )
118+
119+ elif self .parking_state == self .STATE_ADJUSTING :
120+ # Make final adjustments to center in the spot
121+ if self ._adjust_position (state ):
122+ self .parking_state = self .STATE_PARKED
123+ self .debug_event ("Successfully parked" )
124+ state .intent .intent = self .INTENT_PARKED
125+
126+ elif self .parking_state == self .STATE_PARKED :
127+ # Stay parked until requested to exit
128+ if self ._should_exit_parking (state ):
129+ self .parking_state = self .STATE_EXITING
130+ self .debug_event ("Starting to exit parking spot" )
131+ state .intent .intent = self .INTENT_EXITING
132+
133+ elif self .parking_state == self .STATE_EXITING :
134+ # Exit the parking spot
135+ if self ._exit_parking_spot (state ):
136+ self .parking_state = self .STATE_IDLE
137+ self .debug_event ("Exited parking spot, resuming normal operation" )
138+ state .intent .intent = self .INTENT_NORMAL_DRIVING
139+
140+ # Log current state for debugging
141+ self .debug ("parking_state" , self .parking_state )
142+ self .debug ("intent" , state .intent .intent )
143+
144+ except Exception as e :
145+ self .error_count += 1
146+ self .debug ("error" , str (e ))
147+ self .debug_event (f"Error in parking logic: { str (e )} " )
148+
149+ def _find_parking_spot (self , state : AllState ) -> bool :
150+ """
151+ Use perception data to find a suitable parking spot.
152+ Returns True if a spot is found, False otherwise.
153+ """
154+ # Example implementation using perception data
155+ # In a real implementation, you would use perception data to identify open spaces
156+
157+ if not hasattr (state , 'perception' ) or not hasattr (state .perception , 'obstacles' ):
158+ return False
159+
160+ current_obstacles = state .perception .obstacles
161+
162+ # Simple spot detection logic: look for gaps between obstacles
163+ # that are large enough for the vehicle
164+ potential_spots = []
165+
166+ # For parallel parking, look for gaps along the side of the road
167+ if hasattr (state , 'localization' ) and hasattr (state .localization , 'lane_position' ):
168+ # Get current lane information
169+ current_lane = state .localization .lane_position
170+
171+ # Example code to detect gaps - this would be much more sophisticated in reality
172+ # using advanced perception and mapping
173+ gaps = self ._detect_gaps (current_obstacles , current_lane )
174+
175+ for gap in gaps :
176+ if gap ['length' ] >= self .min_spot_length and gap ['width' ] >= self .min_spot_width :
177+ potential_spots .append (gap )
178+
179+ # If we found at least one spot, select the best one
180+ if potential_spots :
181+ # For this demo, just select the first valid spot
182+ self .target_spot = potential_spots [0 ]
183+ return True
184+
185+ return False
186+
187+ def _detect_gaps (self , obstacles , lane_info ):
188+ """
189+ Analyze obstacle data to find gaps that could be parking spots.
190+ This is a simplified implementation.
191+ """
192+ # In a real implementation, this would use sophisticated algorithms
193+ # to detect gaps between vehicles or in designated parking areas
194+
195+ # Placeholder example
196+ gaps = []
197+
198+ # Simulate finding a gap after a certain time for demo purposes
199+ if time .time () - self .start_time > 10.0 : # After 10 seconds, "find" a spot
200+ # Generate a simulated spot relative to current position
201+ example_gap = {
202+ 'length' : self .min_spot_length + 0.5 , # A bit larger than minimum
203+ 'width' : self .min_spot_width + 0.2 ,
204+ 'position' : {
205+ 'x' : lane_info .x + 5.0 , # 5 meters ahead
206+ 'y' : lane_info .y + 2.0 , # 2 meters to the right
207+ 'heading' : lane_info .heading
208+ },
209+ 'type' : 'parallel' # Could also be 'perpendicular'
210+ }
211+ gaps .append (example_gap )
212+
213+ return gaps
214+
215+ def _maintain_search_pattern (self , state : AllState ):
216+ """Maintain a driving pattern while searching for parking spots."""
217+ # In a real implementation, this would follow a search route
218+ # For the demo, just continue with normal driving
219+ state .intent .intent = self .INTENT_SEARCHING
220+
221+ def _approach_parking_spot (self , state : AllState ) -> bool :
222+ """
223+ Move the vehicle to the approach position for the target parking spot.
224+ Returns True when in position.
225+ """
226+ if not self .target_spot :
227+ return False
228+
229+ # Calculate approach position (would use proper path planning in real implementation)
230+ target_x = self .target_spot ['position' ]['x' ] - self .approach_distance
231+ target_y = self .target_spot ['position' ]['y' ]
232+
233+ # Check if we've reached the approach position
234+ current_x = state .localization .x
235+ current_y = state .localization .y
236+
237+ distance_to_approach = math .sqrt ((target_x - current_x )** 2 + (target_y - current_y )** 2 )
238+
239+ # Update trajectory to approach position
240+ if hasattr (state , 'trajectory' ):
241+ # Set trajectory to approach position
242+ # (This is a simplified placeholder - real implementation would
243+ # generate proper trajectory points)
244+ state .trajectory .target_speed = self .forward_speed
245+
246+ # Return True if we're close enough to approach position
247+ return distance_to_approach < 0.3 # meters
248+
249+ def _align_with_spot (self , state : AllState ) -> bool :
250+ """
251+ Align the vehicle parallel to the parking spot.
252+ Returns True when aligned.
253+ """
254+ if not self .target_spot :
255+ return False
256+
257+ target_heading = self .target_spot ['position' ]['heading' ]
258+ current_heading = state .localization .heading
259+
260+ # Calculate heading difference (accounting for circular values)
261+ heading_diff = abs ((target_heading - current_heading + 180 ) % 360 - 180 )
262+
263+ # Update trajectory for alignment
264+ if hasattr (state , 'trajectory' ):
265+ # Would set a trajectory that aligns with target heading
266+ state .trajectory .target_speed = 0.5 # Slow for precise alignment
267+
268+ # Return True if aligned within threshold
269+ return heading_diff < 5.0 # degrees
270+
271+ def _back_into_spot (self , state : AllState ) -> bool :
272+ """
273+ Execute backing maneuver into the parking spot.
274+ Returns True when in the spot.
275+ """
276+ if not self .target_spot :
277+ return False
278+
279+ # Calculate spot center
280+ spot_center_x = self .target_spot ['position' ]['x' ]
281+ spot_center_y = self .target_spot ['position' ]['y' ]
282+
283+ # Current position
284+ current_x = state .localization .x
285+ current_y = state .localization .y
286+
287+ # Distance to spot center
288+ distance_to_center = math .sqrt ((spot_center_x - current_x )** 2 + (spot_center_y - current_y )** 2 )
289+
290+ # Update trajectory for backing
291+ if hasattr (state , 'trajectory' ):
292+ # Would set reverse trajectory into spot
293+ state .trajectory .target_speed = - self .backing_speed # Negative for reverse
294+
295+ # Check if we've reached an appropriate position in the spot
296+ # In real implementation, would use more sophisticated positioning
297+ return distance_to_center < 0.5 # meters
298+
299+ def _adjust_position (self , state : AllState ) -> bool :
300+ """
301+ Make final adjustments to center in the parking spot.
302+ Returns True when properly centered.
303+ """
304+ # Simple implementation - in reality would use precise positioning
305+ # For demo, just assume adjustment is complete after a delay
306+ adjustment_time = 3.0 # seconds
307+
308+ if time .time () - self .last_state_change > adjustment_time :
309+ return True
310+
311+ # Would set small adjustment trajectories
312+ if hasattr (state , 'trajectory' ):
313+ state .trajectory .target_speed = 0.2 # Very slow for fine adjustments
314+
315+ return False
316+
317+ def _should_exit_parking (self , state : AllState ) -> bool :
318+ """
319+ Determine if the vehicle should exit the parking spot.
320+ In a real implementation, this could be triggered by user input
321+ or a scheduled departure time.
322+ """
323+ # For demo purposes, exit after 10 seconds of being parked
324+ parked_duration = time .time () - self .last_state_change
325+ return parked_duration > 10.0
326+
327+ def _exit_parking_spot (self , state : AllState ) -> bool :
328+ """
329+ Execute maneuver to exit the parking spot.
330+ Returns True when back on the road.
331+ """
332+ # Simple implementation - in reality would plan a path out
333+ # For demo, just assume exit is complete after a delay
334+ exit_time = 5.0 # seconds
335+
336+ if time .time () - self .last_state_change > exit_time :
337+ # Reset parking state
338+ self .target_spot = None
339+ return True
340+
341+ # Would set exit trajectory
342+ if hasattr (state , 'trajectory' ):
343+ state .trajectory .target_speed = self .forward_speed
344+
345+ return False
0 commit comments