Skip to content

Commit 7074ff8

Browse files
committed
fix unit test bug, and add test cases for ttc, drac.
1 parent afbbbb8 commit 7074ff8

4 files changed

Lines changed: 211 additions & 13 deletions

File tree

evaluator/autoware_planning_evaluator/include/autoware/planning_evaluator/obstacle_metrics_calculator.hpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ struct ObstacleTrajectoryPoint
108108
bool is_overlapping_with_ego_trajectory = false;
109109
bool is_collision_with_ego_trajectory = false;
110110
size_t first_overlapping_ego_trajectory_index = std::numeric_limits<size_t>::max();
111-
size_t last_overlapping_ego_trajectory_index = -1;
111+
size_t last_overlapping_ego_trajectory_index = 0;
112112

113113
double velocity_mps = 0.0;
114114
double time_from_start_s = 0.0;
@@ -128,7 +128,7 @@ struct ObstacleTrajectoryPoint
128128
this->is_overlapping_with_ego_trajectory = false;
129129
this->is_collision_with_ego_trajectory = false;
130130
this->first_overlapping_ego_trajectory_index = std::numeric_limits<size_t>::max();
131-
this->last_overlapping_ego_trajectory_index = -1;
131+
this->last_overlapping_ego_trajectory_index = 0;
132132
this->velocity_mps = velocity_mps;
133133
this->time_from_start_s = time_from_start_s;
134134
this->distance_from_start_m = distance_from_start_m;
@@ -147,7 +147,7 @@ struct ObstacleTrajectoryPoint
147147
this->is_overlapping_with_ego_trajectory = false;
148148
this->is_collision_with_ego_trajectory = false;
149149
this->first_overlapping_ego_trajectory_index = std::numeric_limits<size_t>::max();
150-
this->last_overlapping_ego_trajectory_index = -1;
150+
this->last_overlapping_ego_trajectory_index = 0;
151151

152152
this->velocity_mps = reference_point.velocity_mps;
153153
this->time_from_start_s = reference_point.time_from_start_s + time_from_reference_s;

evaluator/autoware_planning_evaluator/src/obstacle_metrics_calculator.cpp

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,26 @@ void ObstacleMetricsCalculator::ProcessObstaclesTrajectory()
389389
}
390390
}
391391

392+
// // Debug prints, uncomment to check trajectory points if needed
393+
// std::cerr << "Obstacle traj" << std::endl;
394+
// for (size_t i = 0; i < obstacle_trajectory_points_.size(); ++i) {
395+
// const auto & p = obstacle_trajectory_points_[i];
396+
// std::cerr << "[" << i << "] t: " << p.time_from_start_s << ", pos: (" << p.pose.position.x
397+
// << ", "
398+
// << p.pose.position.y << "), is_overlapping: " <<
399+
// p.is_overlapping_with_ego_trajectory << ", is_collision: "
400+
// << p.is_collision_with_ego_trajectory << ", first_overlapping_idx: " <<
401+
// p.first_overlapping_ego_trajectory_index << ", last_overlapping_idx: " <<
402+
// p.last_overlapping_ego_trajectory_index << std::endl;
403+
// }
404+
// std::cerr << "Ego traj" << std::endl;
405+
// for (size_t i = 0; i < ego_trajectory_points_.size(); ++i) {
406+
// const auto & p = ego_trajectory_points_[i];
407+
// std::cerr << "[" << i << "] t: " << p.time_from_start_s << ", pos: (" << p.pose.position.x
408+
// << ", "
409+
// << p.pose.position.y << ")" << std::endl;
410+
// }
411+
392412
// ------------------------------------------------------------------------------------------------
393413
// 6. get `obstacle_ttc` metrics
394414

@@ -413,8 +433,12 @@ void ObstacleMetricsCalculator::ProcessObstaclesTrajectory()
413433
if (obstacle_trajectory_point.is_overlapping_with_ego_trajectory) {
414434
const size_t ego_first_overlap_idx =
415435
obstacle_trajectory_point.first_overlapping_ego_trajectory_index;
416-
const double point_pet = obstacle_trajectory_point.time_from_start_s -
417-
ego_trajectory_points_[ego_first_overlap_idx].time_from_start_s;
436+
if (ego_first_overlap_idx == 0) { // skip if overlap at t=0 (pet = 0)
437+
continue;
438+
}
439+
const double point_pet =
440+
ego_trajectory_points_[ego_first_overlap_idx - 1].time_from_start_s -
441+
obstacle_trajectory_point.time_from_start_s;
418442
if (point_pet > 0) { // Only consider the case where obstacle leaves before ego arrives.
419443
obstacle_pet = std::min(obstacle_pet, point_pet);
420444
}
@@ -449,9 +473,10 @@ void ObstacleMetricsCalculator::ProcessObstaclesTrajectory()
449473
ego_end_vel =
450474
ego_start_vel >= 0.0 ? std::max(ego_end_vel, 0.0) : std::min(ego_end_vel, 0.0);
451475

452-
const double point_drac =
453-
(ego_start_vel - ego_end_vel) / ego_trajectory_point.time_from_start_s;
454-
obstacle_drac = std::max(obstacle_drac, point_drac);
476+
const double distance_to_collision = ego_trajectory_point.distance_from_start_m;
477+
const double point_drac = (ego_end_vel * ego_end_vel - ego_start_vel * ego_start_vel) /
478+
(2.0 * distance_to_collision + 1e-6);
479+
obstacle_drac = std::max(obstacle_drac, std::abs(point_drac));
455480
}
456481

457482
// Add to metric statistics if we found any valid DRAC value

evaluator/autoware_planning_evaluator/src/planning_evaluator_node.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -399,6 +399,7 @@ void PlanningEvaluatorNode::onTrajectory(
399399
}
400400
}
401401

402+
metrics_calculator_.setPreviousTrajectory(*traj_msg);
402403
auto runtime_trajectory = (now() - trajectory_start).seconds();
403404
RCLCPP_DEBUG(
404405
get_logger(), "Planning evaluation calculation time: %2.2f ms", runtime_trajectory * 1e3);

evaluator/autoware_planning_evaluator/test/test_planning_evaluator_node.cpp

Lines changed: 177 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -517,14 +517,15 @@ TEST_F(EvalTest, TestFrechet)
517517

518518
TEST_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

536537
TEST_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

557729
TEST_F(EvalTest, TestModifiedGoalLongitudinalDeviation)

0 commit comments

Comments
 (0)