@@ -517,14 +517,15 @@ TEST_F(EvalTest, TestFrechet)
517517
518518TEST_F (EvalTest, TestObstacleDistance)
519519{
520- setTargetMetric (planning_diagnostics::Metric::obstacle_distance, " /min " );
520+ setTargetMetric (planning_diagnostics::Metric::obstacle_distance, " /worst " );
521521 Objects objs;
522522 autoware_perception_msgs::msg::PredictedObject obj;
523523 obj.kinematics .initial_pose_with_covariance .pose .position .x = 4.0 ;
524524 obj.kinematics .initial_pose_with_covariance .pose .position .y = 0.0 ;
525525 objs.objects .push_back (obj);
526526 publishObjects (objs);
527527
528+ publishEgoPose (0.0 , 0.0 , 0.0 , 1.0 , 0.0 );
528529 Trajectory t = makeTrajectory ({{0.0 , 0.0 }, {1.0 , 0.0 }});
529530 EXPECT_LE (publishTrajectoryAndGetMetric (t), 3.0 ); // considering the vehicle and object shape, <
530531 // the distance is smaller than 4-1=3
@@ -535,8 +536,8 @@ TEST_F(EvalTest, TestObstacleDistance)
535536
536537TEST_F (EvalTest, TestObstacleTTC)
537538{
538- setTargetMetric (planning_diagnostics::Metric::obstacle_ttc);
539- publishEgoPose (0.001 , 0.0 , 0.0 , 1.0 , 0.0 ); // start nearly from the second traj point
539+ setTargetMetric (planning_diagnostics::Metric::obstacle_ttc, " /worst " );
540+ publishEgoPose (0.01 , 0.0 , 0.0 , 1.0 , 0.0 ); // start nearly from the second traj point
540541 Objects objs;
541542 autoware_perception_msgs::msg::PredictedObject obj;
542543 obj.kinematics .initial_pose_with_covariance .pose .position .x = 5.0 ;
@@ -548,10 +549,181 @@ TEST_F(EvalTest, TestObstacleTTC)
548549 for (TrajectoryPoint & p : t.points ) {
549550 p.longitudinal_velocity_mps = 1.0 ;
550551 }
551- EXPECT_DOUBLE_EQ (publishTrajectoryAndGetMetric (t), 5.0 );
552+ EXPECT_NEAR (publishTrajectoryAndGetMetric (t), 4.99 , epsilon); // from 0.01 to 5.0 at 1 m/s
552553
553554 t.points [2 ].pose .position .x = 4.0 ;
554- EXPECT_DOUBLE_EQ (publishTrajectoryAndGetMetric (t), 4.0 );
555+ EXPECT_NEAR (publishTrajectoryAndGetMetric (t), 3.99 , epsilon); // from 0.01 to 4.0 at 1 m/s
556+ }
557+
558+ TEST_F (EvalTest, TestObstaclePET)
559+ {
560+ // Create a scenario where ego vehicle approaches an intersection at (x,y)=(20,0)m from (0,0) at 5
561+ // m/s An obstacle (0.5m width, 2.0m length) is crossing the intersection from (20,-10) to (20,0)
562+ // at 10 m/s Ego vehicle dimensions
563+ const double ego_width = vehicle_info_.vehicle_width_m ;
564+ const double ego_baselink_to_front = vehicle_info_.max_longitudinal_offset_m ;
565+ const double ego_velocity = 5.0 ;
566+ const double ego_distance_to_intersection = 20.0 ;
567+ // Obstacle dimensions
568+ const double obj_width = 3.0 ;
569+ const double obj_length = 3.0 ;
570+ const double obj_velocity = 10.0 ;
571+ const double obj_distance_to_intersection = 10.0 ;
572+
573+ // object to leave
574+ const double object_leave_distance =
575+ obj_distance_to_intersection + ego_width / 2.0 + obj_length / 2.0 ;
576+ const double t_obstacle_leave = object_leave_distance / obj_velocity;
577+
578+ const double ego_arrive_distance =
579+ ego_distance_to_intersection - ego_baselink_to_front - obj_width / 2.0 ;
580+ const double t_ego_arrive = ego_arrive_distance / ego_velocity;
581+
582+ // Expected PET = t_ego_arrive - t_obstacle_leave
583+ const double expected_pet = t_ego_arrive - t_obstacle_leave;
584+
585+ // Create an obstacle that crosses ego's path from left to right
586+ Objects objs;
587+ autoware_perception_msgs::msg::PredictedObject obj;
588+ obj.kinematics .initial_pose_with_covariance .pose .position .x = ego_distance_to_intersection;
589+ obj.kinematics .initial_pose_with_covariance .pose .position .y = -obj_distance_to_intersection;
590+ tf2::Quaternion q;
591+ q.setRPY (0.0 , 0.0 , M_PI_2);
592+ obj.kinematics .initial_pose_with_covariance .pose .orientation .x = q.x ();
593+ obj.kinematics .initial_pose_with_covariance .pose .orientation .y = q.y ();
594+ obj.kinematics .initial_pose_with_covariance .pose .orientation .z = q.z ();
595+ obj.kinematics .initial_pose_with_covariance .pose .orientation .w = q.w ();
596+
597+ // Set obstacle shape
598+ obj.shape .type = autoware_perception_msgs::msg::Shape::BOUNDING_BOX;
599+ obj.shape .dimensions .x = obj_length;
600+ obj.shape .dimensions .y = obj_width;
601+ obj.shape .dimensions .z = 1.0 ;
602+
603+ obj.kinematics .initial_twist_with_covariance .twist .linear .x = 0.0 ;
604+ obj.kinematics .initial_twist_with_covariance .twist .linear .y = obj_velocity;
605+
606+ // Add predicted path for the obstacle
607+ autoware_perception_msgs::msg::PredictedPath path;
608+ for (double y = -obj_distance_to_intersection; y <= obj_distance_to_intersection; y += 0.5 ) {
609+ geometry_msgs::msg::Pose pose;
610+ pose.position .x = ego_distance_to_intersection;
611+ pose.position .y = y;
612+ pose.position .z = 0.0 ;
613+ pose.orientation = obj.kinematics .initial_pose_with_covariance .pose .orientation ;
614+ path.path .push_back (pose);
615+ }
616+ path.confidence = 1.0 ;
617+ obj.kinematics .predicted_paths .push_back (path);
618+
619+ objs.objects .push_back (obj);
620+
621+ // Create ego trajectory with dense sampling (0.1m spacing for better PET accuracy)
622+ Trajectory t;
623+ t.header .frame_id = " map" ;
624+ tf2::Quaternion q_ego;
625+ q_ego.setRPY (0.0 , 0.0 , 0.0 ); // heading along x-axis
626+ for (double x = 0.0 ; x <= ego_distance_to_intersection + 10 ; x += 0.1 ) { // Dense 0.2m spacing
627+ TrajectoryPoint p;
628+ p.pose .position .x = x;
629+ p.pose .position .y = 0.0 ;
630+ p.pose .orientation .x = q_ego.x ();
631+ p.pose .orientation .y = q_ego.y ();
632+ p.pose .orientation .z = q_ego.z ();
633+ p.pose .orientation .w = q_ego.w ();
634+ p.longitudinal_velocity_mps = ego_velocity;
635+ t.points .push_back (p);
636+ }
637+ setTargetMetric (planning_diagnostics::Metric::obstacle_pet, " /worst" );
638+ publishEgoPose (0.0 , 0.0 , 0.0 , ego_velocity, 0.0 );
639+ publishObjects (objs);
640+ EXPECT_NEAR (publishTrajectoryAndGetMetric (t), expected_pet, 0.05 );
641+ }
642+
643+ TEST_F (EvalTest, TestObstacleDRAC)
644+ {
645+ // Scenario: Both ego and obstacle moving in same direction, ego needs to decelerate to avoid
646+ // collision Initial setup
647+ const double ego_velocity = 10.0 ;
648+ const double obj_velocity = 5.0 ;
649+ const double ego_initial_x = 0.0 ;
650+ const double obj_initial_x = 20.0 ;
651+
652+ // ego/obj dimensions
653+ const double ego_baselink_to_front = vehicle_info_.max_longitudinal_offset_m ;
654+ const double obj_length = 1.0 ;
655+ const double obj_width = 1.0 ;
656+
657+ const double initial_gap =
658+ obj_initial_x - obj_length / 2.0 - ego_initial_x - ego_baselink_to_front;
659+ const double t_collision = initial_gap / (ego_velocity - obj_velocity);
660+
661+ // At collision, ego baselink would be at:
662+ const double ego_baselink_at_collision = ego_initial_x + ego_velocity * t_collision;
663+ const double ego_travel_distance = ego_baselink_at_collision - ego_initial_x;
664+
665+ const double expected_drac = std::abs (
666+ (obj_velocity * obj_velocity - ego_velocity * ego_velocity) / (2.0 * ego_travel_distance));
667+
668+ // Create a moving obstacle (same direction as ego, but slower)
669+ Objects objs;
670+ autoware_perception_msgs::msg::PredictedObject obj;
671+ obj.kinematics .initial_pose_with_covariance .pose .position .x = obj_initial_x;
672+ obj.kinematics .initial_pose_with_covariance .pose .position .y = 0.0 ;
673+ tf2::Quaternion q;
674+ q.setRPY (0.0 , 0.0 , 0.0 ); // Same direction as ego
675+ obj.kinematics .initial_pose_with_covariance .pose .orientation .x = q.x ();
676+ obj.kinematics .initial_pose_with_covariance .pose .orientation .y = q.y ();
677+ obj.kinematics .initial_pose_with_covariance .pose .orientation .z = q.z ();
678+ obj.kinematics .initial_pose_with_covariance .pose .orientation .w = q.w ();
679+
680+ obj.shape .type = autoware_perception_msgs::msg::Shape::BOUNDING_BOX;
681+ obj.shape .dimensions .x = obj_length;
682+ obj.shape .dimensions .y = obj_width;
683+ obj.shape .dimensions .z = 1.0 ;
684+
685+ obj.kinematics .initial_twist_with_covariance .twist .linear .x = obj_velocity;
686+ obj.kinematics .initial_twist_with_covariance .twist .linear .y = 0.0 ;
687+
688+ // Add predicted path for the moving obstacle
689+ autoware_perception_msgs::msg::PredictedPath path;
690+ const double path_duration = t_collision + 5.0 ;
691+ const double path_distance = obj_velocity * path_duration;
692+ for (double dist = 0.0 ; dist <= path_distance; dist += 0.5 ) {
693+ geometry_msgs::msg::Pose pose;
694+ pose.position .x = obj_initial_x + dist;
695+ pose.position .y = 0.0 ;
696+ pose.position .z = 0.0 ;
697+ pose.orientation = obj.kinematics .initial_pose_with_covariance .pose .orientation ;
698+ path.path .push_back (pose);
699+ }
700+ path.confidence = 1.0 ;
701+ obj.kinematics .predicted_paths .push_back (path);
702+
703+ objs.objects .push_back (obj);
704+
705+ // Create ego trajectory with dense sampling
706+ Trajectory t;
707+ t.header .frame_id = " map" ;
708+ tf2::Quaternion q_ego;
709+ q_ego.setRPY (0.0 , 0.0 , 0.0 ); // heading along x-axis
710+ const double trajectory_length = ego_baselink_at_collision + 10.0 ;
711+ for (double x = ego_initial_x; x <= trajectory_length; x += 0.1 ) {
712+ TrajectoryPoint p;
713+ p.pose .position .x = x;
714+ p.pose .position .y = 0.0 ;
715+ p.pose .orientation .x = q_ego.x ();
716+ p.pose .orientation .y = q_ego.y ();
717+ p.pose .orientation .z = q_ego.z ();
718+ p.pose .orientation .w = q_ego.w ();
719+ p.longitudinal_velocity_mps = ego_velocity;
720+ t.points .push_back (p);
721+ }
722+
723+ setTargetMetric (planning_diagnostics::Metric::obstacle_drac, " /worst" );
724+ publishEgoPose (ego_initial_x, 0.0 , 0.0 , ego_velocity, 0.0 );
725+ publishObjects (objs);
726+ EXPECT_NEAR (publishTrajectoryAndGetMetric (t), expected_drac, 0.1 );
555727}
556728
557729TEST_F (EvalTest, TestModifiedGoalLongitudinalDeviation)
0 commit comments