@@ -61,7 +61,7 @@ def waypoint_generate(vehicle_state, cones, cone_idx):
6161 target_heading = car_heading
6262
6363 # ===== Parameters =====
64- u_turn_radius = 11.5 # Radius for U-turn
64+ u_turn_radius = 10.0 # Radius for U-turn
6565 offset = 2.0 # Offset for left/right pass
6666 lookahead_distance = 10.0 # Distance ahead for fixed point
6767 # ======================
@@ -455,7 +455,7 @@ def no_cone_planning(vehicle_dict):
455455 vehicle_x , vehicle_y = vehicle_dict ['position' ][0 ], vehicle_dict ['position' ][1 ]
456456 vehicle_heading = vehicle_dict ['heading' ]
457457 vehicle_velocity = vehicle_dict ['velocity' ]
458- step_size = 0.5 * ( max ( 1 , vehicle_velocity ))
458+ step_size = 0.5
459459 for i in range (10 ):
460460 temp_points .append ([vehicle_x + i * step_size * np .cos (vehicle_heading ),
461461 vehicle_y + i * step_size * np .sin (vehicle_heading )])
@@ -485,8 +485,11 @@ def got_new_cone(current_cones, prev_cones):
485485class SlalomTrajectoryPlanner (Component ):
486486 def __init__ (self , ** kwargs ):
487487 # You can accept args here if needed
488+ self .prev_vehicle_position = None
488489 self .trajectory = None
489490 self .prev_cones = None
491+ self .travelled_distance = 0.0
492+ self .cones = []
490493 # ----------------------------
491494 # Predifined-Cones Simulation
492495 # self.run_fake_plan = True
@@ -517,26 +520,40 @@ def update(self, agents: Dict[str, AgentState], vehicle: VehicleState):
517520 for id , agent in agents .items ():
518521 if agent .type == AgentEnum .CONE :
519522 # ===== RUNNING ONBOARD =====
520- cones .append ({
521- 'id' : id ,
522- 'x' : agent .pose .x ,
523- 'y' : agent .pose .y ,
524- 'orientation' : agent .activity
525- })
526- # ===== TESTING ONBOARD in BASIC SIM =====
527- # if n % 2 == 0:
528- # curr_activity = 'LEFT'
529- # elif n % 2 == 1:
530- # curr_activity = 'RIGHT'
531- # else:
532- # curr_activity = 'STANDING'
533523 # cones.append({
534524 # 'id': id,
535525 # 'x': agent.pose.x,
536526 # 'y': agent.pose.y,
537- # 'orientation': curr_activity
527+ # 'orientation': agent.activity
538528 # })
539- # n = n + 1
529+ # ===== TESTING ONBOARD in BASIC SIM =====
530+ if n > 3 :
531+ break
532+ if n % 4 == 0 :
533+ curr_activity = 'LEFT'
534+ elif n % 4 == 1 :
535+ curr_activity = 'RIGHT'
536+ elif n % 4 == 2 :
537+ curr_activity = 'LEFT'
538+ else :
539+ curr_activity = 'STANDING'
540+ c = {
541+ 'id' : id ,
542+ 'x' : agent .pose .x ,
543+ 'y' : agent .pose .y ,
544+ 'orientation' : curr_activity
545+ }
546+ n = n + 1
547+ if c ['id' ] not in {cone ['id' ] for cone in self .cones }:
548+ self .cones .append (c )
549+
550+ curr_pos = np .array ([vehicle .pose .x , vehicle .pose .y ])
551+ if self .prev_vehicle_position is None :
552+ distance_increment = 0.0
553+ else :
554+ distance_increment = np .linalg .norm (curr_pos - self .prev_vehicle_position )
555+
556+ self .prev_vehicle_position = curr_pos
540557
541558 vehicle_dict = {
542559 'position' : [vehicle .pose .x , vehicle .pose .y ],
@@ -546,19 +563,23 @@ def update(self, agents: Dict[str, AgentState], vehicle: VehicleState):
546563 if self .DEBUG_MODE :
547564 print ("===================== STATES =====================" )
548565 print (f"Vehicle State: { vehicle_dict } " )
549- print (f"Detected Cones: { cones } " )
566+ print (f"Detected Cones: { self . cones } " )
550567 print ("===================== ====== =====================" )
568+
569+ print ("cones when planning: " , self .cones )
570+ self .trajectory = self .online_trajectory_planning (vehicle_dict , self .cones , distance_increment )
571+
551572 # If no cones detected, drive forward
552- if len (cones ) == 0 :
573+ if len (self . cones ) == 0 :
553574 self .trajectory = no_cone_planning (vehicle_dict )
554- # Otherwise, plan trajectory
555- elif got_new_cone (cones , self .prev_cones ):
556- # Replan only if new cones are detected
557- self .trajectory = plan_full_slalom_trajectory (vehicle_dict , cones )
558- self .prev_cones = cones
559- else :
560- # No need to update the plan if the same cones are detected
561- self .prev_cones = cones
575+ # # Otherwise, plan trajectory
576+ # elif got_new_cone(cones, self.prev_cones):
577+ # # Replan only if new cones are detected
578+ # self.trajectory = plan_full_slalom_trajectory(vehicle_dict, cones)
579+ # self.prev_cones = cones
580+ # else:
581+ # # No need to update the plan if the same cones are detected
582+ # self.prev_cones = cones
562583
563584 # Testing with predefined fake generated cone positions
564585 elif self .run_fake_plan :
@@ -610,13 +631,192 @@ def update(self, agents: Dict[str, AgentState], vehicle: VehicleState):
610631 # Update output
611632 return self .trajectory
612633
634+ def online_trajectory_planning (self , vehicle_state , cones , distance_increment , replan_threshold = 100.0 ):
635+ print ("planing......" )
636+ if not hasattr (self , 'prev_cones' ):
637+ self .prev_cones = None
638+
639+ if not hasattr (self , 'no_cone_ahead' ):
640+ self .no_cone_ahead = False
641+
642+ if not hasattr (self , 'visited_cone_ids' ):
643+ self .visited_cone_ids = set ()
644+
645+ stitch_idx = - 1
646+
647+ def got_new_cone (current , prev ):
648+ if prev is None :
649+ return True
650+ prev_ids = {c ['id' ] for c in prev }
651+ return any (c ['id' ] not in prev_ids for c in current )
652+
653+ self .travelled_distance += distance_increment
654+ new_cone_detected = got_new_cone (self .cones , self .prev_cones )
655+
656+ # Plan at the beginning or when new cones detected or after threshold distance
657+ if self .trajectory is None or new_cone_detected or not self .no_cone_ahead or True :
658+ self .prev_cones = self .cones
659+
660+ if self .trajectory is None :
661+ current_position = vehicle_state ['position' ]
662+ init_state = {
663+ 'x' : current_position [0 ],
664+ 'y' : current_position [1 ],
665+ 'psi' : vehicle_state ['heading' ],
666+ 'c' : 0.0 ,
667+ 'v' : vehicle_state ['velocity' ]
668+ }
669+ else :
670+ stitch_idx , init_point , heading = self .get_future_point_on_trajectory (self .trajectory , vehicle_state ['position' ], lookahead_distance = 500.0 )
671+ init_state = {
672+ 'x' : init_point [0 ],
673+ 'y' : init_point [1 ],
674+ 'psi' : heading ,
675+ 'c' : 0.0 ,
676+ 'v' : vehicle_state ['velocity' ]
677+ }
678+
679+ print ("all cones: " , self .cones )
680+ current_cone_idx , updated_cones = self .get_current_cone_idx (self .cones , init_state )
681+ self .cones = updated_cones
682+ print ("updated cones are here: " , self .cones )
683+ print ("init state: " , init_state )
684+ print ("current cone: " , current_cone_idx )
685+
686+ # No cone ahead
687+ if current_cone_idx == - 1 :
688+ self .no_cone_ahead = True
689+ return self .trajectory
690+ else :
691+ self .no_cone_ahead = False
692+
693+ # No need to plan if there is no new cone detected and no cone ahead
694+ if not new_cone_detected and self .no_cone_ahead :
695+ return self .trajectory
696+
697+ self .visited_cone_ids .add (self .cones [current_cone_idx ]['id' ])
698+ scenario , flex_wps , fixed_wp , target_heading = waypoint_generate (vehicle_state , self .cones , current_cone_idx )
613699
700+ if flex_wps and fixed_wp is not None :
701+ final_state = {
702+ 'x' : fixed_wp [0 ], 'y' : fixed_wp [1 ], 'psi' : target_heading , 'c' : 0.0
703+ }
614704
705+ # Stitch from current vehicle position to new plan start
706+ if self .trajectory is not None :
707+ # 1. Plan new trajectory from init_state onward
708+ x_new , y_new , _ , _ , v_new , _ , _ = trajectory_generation (init_state , final_state , waypoints = flex_wps )
615709
710+ # 2. Cut old trajectory up to init_state (e.g., index `stitch_idx`)
711+ old_points = self .trajectory .points [:stitch_idx ]
712+ old_x = [p [0 ] for p in old_points ]
713+ old_y = [p [1 ] for p in old_points ]
714+ old_v = [vehicle_state ['velocity' ]] * len (old_x ) # or extract from old trajectory if available
616715
716+ # 3. Combine old + new
717+ x_full = np .concatenate ([old_x , x_new ])
718+ y_full = np .concatenate ([old_y , y_new ])
719+ v_full = np .concatenate ([old_v , v_new ])
617720
721+ # 4. Create trajectory
722+ self .trajectory = to_gemstack_trajectory (x_full , y_full , v_full )
723+ else :
724+ x , y , _ , _ , v , _ , _ = trajectory_generation (init_state , final_state , waypoints = flex_wps )
725+ self .trajectory = to_gemstack_trajectory (x , y , v )
618726
727+ self .travelled_distance = 0.0
619728
729+ return self .trajectory
730+
731+ @staticmethod
732+ def get_future_point_on_trajectory (trajectory , vehicle_position , lookahead_distance = 80.0 ):
733+ """
734+ Finds a point `lookahead_distance` ahead of the current vehicle position along the trajectory.
735+ """
736+ traj_points = trajectory .points
737+ current_pos = np .array (vehicle_position )
738+
739+ # Step 1: Find the closest point on trajectory
740+ dists = [np .linalg .norm (current_pos - np .array ([p [0 ], p [1 ]])) for p in traj_points ]
741+ closest_idx = np .argmin (dists )
742+
743+ # Step 2: Accumulate distance from closest_idx forward
744+ accumulated = 0.0
745+ heading = 0
746+ for i in range (closest_idx + 1 , len (traj_points )):
747+ p1 = np .array ([traj_points [i - 1 ][0 ], traj_points [i - 1 ][1 ]])
748+ p2 = np .array ([traj_points [i ][0 ], traj_points [i ][1 ]])
749+ heading = np .arctan2 (p2 [1 ] - p1 [1 ], p2 [0 ] - p1 [0 ])
750+ segment = np .linalg .norm (p2 - p1 )
751+ accumulated += segment
752+ if accumulated >= lookahead_distance :
753+ return i , traj_points [i ], heading # Return future point
754+
755+ p1 = np .array ([traj_points [- 2 ][0 ], traj_points [- 2 ][1 ]])
756+ p2 = np .array ([traj_points [- 1 ][0 ], traj_points [- 1 ][1 ]])
757+ heading = np .arctan2 (p2 [1 ] - p1 [1 ], p2 [0 ] - p1 [0 ])
758+ # If not enough length, return the last point
759+ return - 1 , traj_points [- 1 ], heading
760+
761+ def get_current_cone_idx (self , cones , init_state , forward_dist = 50.0 , angle_thresh = np .pi ):
762+ """
763+ Get the index of the nearest cone in front of the init_state.
764+ If a 'STANDING' cone is found, previous cones are flipped and returned with the index of the standing cone.
765+
766+ Args:
767+ cones: List of cones.
768+ init_state: Dict with keys 'x', 'y', 'psi'.
769+ forward_dist: Max distance to search for cones ahead.
770+ angle_thresh: Angle threshold to filter cones roughly in front.
771+
772+ Returns:
773+ idx: Index of the cone in front (after STANDING logic if needed).
774+ updated_cones: Possibly updated cones with flipped orientations.
775+ """
776+ pos = np .array ([init_state ['x' ], init_state ['y' ]])
777+ heading = init_state ['psi' ]
778+ heading_vec = np .array ([np .cos (heading ), np .sin (heading )])
779+
780+ best_idx = None
781+ min_dist = float ('inf' )
782+
783+ # Search in the list of cones, which one is nearest ahead of init_state
784+ for i , cone in enumerate (cones ):
785+ if cone ['id' ] in self .visited_cone_ids :
786+ continue # Skip already visited cones
787+ cone_pos = np .array ([cone ['x' ], cone ['y' ]])
788+ vec_to_cone = cone_pos - pos
789+ dist = np .linalg .norm (vec_to_cone )
790+ if dist > forward_dist :
791+ continue
792+ angle = np .arccos (np .clip (np .dot (heading_vec , vec_to_cone / (dist + 1e-8 )), - 1.0 , 1.0 ))
793+ if angle < angle_thresh and dist < min_dist :
794+ best_idx = i
795+ min_dist = dist
796+
797+ # If STANDING cone is ahead, flip previous cone directions
798+ if best_idx is not None and cones [best_idx ]['orientation' ] == 'STANDING' :
799+ updated_cones = cones [:best_idx + 1 ] + [
800+ self .flip_cone_orientation (c ) for c in cones [:best_idx ][::- 1 ]
801+ ] + cones [best_idx + 1 :]
802+ print ("updated cones: " , updated_cones )
803+ return best_idx , updated_cones
804+ else :
805+ # -1 means no available cone is ahead
806+ return best_idx if best_idx is not None else - 1 , cones
807+
808+ @staticmethod
809+ def flip_cone_orientation (cone ):
810+ """
811+ Flip cone orientation LEFT↔RIGHT
812+ """
813+ flipped = cone .copy ()
814+ flipped ['id' ] = cone ['id' ] + 'flipped'
815+ if cone ['orientation' ] == 'LEFT' :
816+ flipped ['orientation' ] = 'RIGHT'
817+ elif cone ['orientation' ] == 'RIGHT' :
818+ flipped ['orientation' ] = 'LEFT'
819+ return flipped
620820
621821########################################################################################################################
622822########################################################################################################################
0 commit comments