diff --git a/planning/autoware_planning_validator/README.md b/planning/autoware_planning_validator/README.md index 92c9b86c8b31c..c7429134616f4 100644 --- a/planning/autoware_planning_validator/README.md +++ b/planning/autoware_planning_validator/README.md @@ -20,6 +20,7 @@ The following features are supported for trajectory validation and can have thre - **Distance deviation** : invalid if the ego is too far from the trajectory - **Longitudinal distance deviation** : invalid if the trajectory is too far from ego in longitudinal direction - **Forward trajectory length** : invalid if the trajectory length is not enough to stop within a given deceleration +- **Potential collision** : invalid if the planned trajectory is too close to the obstacle in the time horizon The following features are to be implemented. @@ -31,10 +32,11 @@ The following features are to be implemented. The `autoware_planning_validator` takes in the following inputs: -| Name | Type | Description | -| -------------------- | --------------------------------- | ---------------------------------------------- | -| `~/input/kinematics` | nav_msgs/Odometry | ego pose and twist | -| `~/input/trajectory` | autoware_planning_msgs/Trajectory | target trajectory to be validated in this node | +| Name | Type | Description | +| -------------------- | ----------------------------------------- | ---------------------------------------------- | +| `~/input/kinematics` | nav_msgs/Odometry | ego pose and twist | +| `~/input/trajectory` | autoware_planning_msgs/Trajectory | target trajectory to be validated in this node | +| `~/input/objects` | autoware_perception_msgs/PredictedObjects | obstacles in the environment | ### Outputs @@ -83,5 +85,10 @@ The input trajectory is detected as invalid if the index exceeds the following t For parameters used e.g. to calculate threshold. -| `parameters.forward_trajectory_length_acceleration` | double | This value is used to calculate required trajectory length. | -5.0 | -| `parameters.forward_trajectory_length_margin` | double | A margin of the required trajectory length not to raise an error when the ego slightly exceeds the trajectory end point. | 2.0 | +| Parameter Name | Type | Description | Default Value | +| ---------------------------------------------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------- | +| `parameters.forward_trajectory_length_acceleration` | double | This value is used to calculate required trajectory length. | -5.0 | +| `parameters.forward_trajectory_length_margin` | double | A margin of the required trajectory length not to raise an error when the ego slightly exceeds the trajectory end point. | 2.0 | +| `parameters.trajectory_to_object_distance_threshold` | double | A threshold for filtering objects to improve computational efficiency of collision checking. Only objects within this distance [m] from their nearest point on the trajectory are considered for collision checking. | 30.0 | +| `parameters.ego_to_object_distance_threshold` | double | A threshold for filtering objects to improve computational efficiency of collision checking. Only objects within this distance [m] from the current ego position are considered for collision checking. | 50.0 | +| `parameters.time_tolerance_threshold` | double | Time tolerance threshold [s] for collision detection between ego and object trajectories. Collision check is performed only when the time difference between ego and object predicted positions is within this threshold to detect temporal-spatial intersections. | 0.1 | diff --git a/planning/autoware_planning_validator/config/planning_validator.param.yaml b/planning/autoware_planning_validator/config/planning_validator.param.yaml index da337d70b1078..930e254625d25 100644 --- a/planning/autoware_planning_validator/config/planning_validator.param.yaml +++ b/planning/autoware_planning_validator/config/planning_validator.param.yaml @@ -37,3 +37,18 @@ # Setting it to 0 means an error will occur if even slightly exceeding the end of the path, # therefore, a certain margin is necessary. forward_trajectory_length_margin: 2.0 + + # Threshold to filter out objects that are far from the trajectory. + # Only objects within this distance [m] from their nearest point on the trajectory + # are considered for collision checking to improve computational efficiency. + trajectory_to_object_distance_threshold: 30.0 + + # Threshold to filter out objects that are far from the ego vehicle. + # Only objects within this distance [m] from the current ego position + # are considered for collision checking to improve computational efficiency. + ego_to_object_distance_threshold: 50.0 + + # Time tolerance threshold [s] for collision detection between ego and object trajectories. + # Collision check is performed only when the time difference between ego and object + # predicted positions is within this threshold to detect temporal-spatial intersections. + time_tolerance_threshold: 0.1 diff --git a/planning/autoware_planning_validator/image/planning_validator.drawio.svg b/planning/autoware_planning_validator/image/planning_validator.drawio.svg index 0a2267cba159e..9069f5c84b7eb 100644 --- a/planning/autoware_planning_validator/image/planning_validator.drawio.svg +++ b/planning/autoware_planning_validator/image/planning_validator.drawio.svg @@ -4,9 +4,9 @@ xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="496px" - height="171px" - viewBox="-0.5 -0.5 496 171" - content="<mxfile><diagram id="U03Aw2_6lbGUJ5V6ckVM" name="ページ1">5Vhbb9owGP01PA45CQnksaXtJk1Tq7JL91R5iZt4TWJmOwX26/c5scnF0FHK2CaKVOLj+3eO/Z0w8Kb58i3H8/QDi0k2cFG8HHgXA9edeD78V8CqBsKRBhJO4xpyGmBGfxINIo2WNCai01Aylkk674IRKwoSyQ6GOWeLbrMHlnVnneOEWMAswpmNfqGxTPW2fNTg7whNUjOzg3TNNxw9JpyVhZ6vYAWpa3JshtFNRYpjtmhB3uXAm3LGZP2UL6ckU1E1Eav7XW2pXS+Zk0Lu0mFUd3jCWal3/ZHj7xBNxld6hXJlAkJiiI8uVnvyzlOZZ1By4JEsqbxrPX+FZzR0JhNdvlC6QKawahVuCKc5kYRrrJ5YzbZ1VxoSrOSRbuWuwweCJAwGVFtAnGRY0qfuUFhLI1m3W3e9YRQmcZFWcTDRTGkR+4Z+M4TEPCFS92oiDQ+tZTRQFf/NXLgWF/MMFwUtknvAaIyBFIuTSmYk1pFbpFSS2RxXMVnAqexSpCcgXJLl86G146g7eH4vHkbJi+aIOAZL28cjQNtj34naMyHyXiTXIj5T1wCUogwLQaOeYAuY/c7oUBU6klVAo9mqtGqX9lWtHdpW6PwNkTPYK5Xsoh5z494Q9WGylGwNZElg7AzROGz+/tQBGVvsk4TdP9KCwL1KI3EwBbj+bwTwv3EdeOgwXFsDHY7d0GL3guKkYAKonUksyw387pGQam635qP9mT1wognQbpG2BhoF4bB1GMNJ97CG4XDi7SSGPTg01q1F4o3OYZ9NCjtFLnc0DS/k0hr2gETaZmTgBhlE7fyBVStsqAt+lMxUvBGVjT+DBg6aL5vKyqdnjHd6DFxvgtSnDQWJ+m7ndbD9ylWTej1KRWB49GpgI/WC6m6WrMDpyK6UhOTskUz1YrTUHmiW9SCYKSlU5gCxqCR/rnwThdeDM12R0zhW02x0XV1fNni98XLRuJd1tXdvGy9vQ0rp39j7+C7HNl7HlcMnoSiHdziu5JC2pEBZcXpacPpZ+ZhasN8Zj6uFz80dgGTrmjgxEfiO370QfOeIIvD/dn5QKUGAwe/dBJC6RZlJkzTqq0KshCR5Pe31e9jb5e3t9e3JKWZkWUzPUsz6zeHgign+CcWk8DZoZLGWCq5kZEAsKiHVuYYBqr5zFpcZAdt6VQrCxQlqJ+x5z8C+bcbBBu2MXqwdKDa/gNbmtfmB2bv8BQ==</diagram></mxfile>" + height="172px" + viewBox="-0.5 -0.5 496 172" + content="<mxfile><diagram id="U03Aw2_6lbGUJ5V6ckVM" name="ページ1">5Vnbcts2EP0aPdYDkiJNPvqWdCbTscdO0/Qpg5AwiRgiFAC0pH59FwQgXiDFisOqyciasYiD+56D3QU1i64W67cCL6s/eEHYLETFehZdz8JwngTwXwMbC0RzA5SCFgYKOuCB/kMsiCza0ILIQUPFOVN0OQRzXtckVwMMC8FXw2aPnA1nXeKSeMBDjpmP/kULVRk0jVGH/05oWbmZA2RrPuP8qRS8qe18Na+JqVlgN4xtKitc8FUPim5m0ZXgXJmnxfqKMG1VZzHT782e2u2SBanVIR0sI8+YNXbX7wX+AtbkYmNXqDbOIKQA+9hiu6foslILBqUAHsmaqo+957/hGZ0FaWrL11oXyBU2vcIdEXRBFBEW8zdh9yV5I3K7mNBAek29NnaXbwmHAfUWkCAMK/o85BRbaZTbdtuud5zCrCGyKk5Sy5QVcezod0MoLEqibK/O0vDQW0YHtfbfzUXocbFkuK5pXX4CjBYYSPE4aWVGCmu5VUUVeVji1kgrOJVDivYa9pkIRdbftKOtjeKRPZySV90RCRxW9Y9HgvbbfmC1b5go+i651sWFdgNQyhmWkuYjwdYw+0enQ10YSFYDnWbb0qZfOli1L0q0Z7p4h+Uc9oNKDtGIuXk2HMKcLk/J3kCeBObZGTrPur//6oCce+yTkn96ojUBv0pzOZkCwvgFAfxqXCcITcO1N9B07GYeu9cUlzWXQO2DwqrZwe8rApLhdm88ej2zEweaQy3tDTRPsrPeYczS4WHNsrM0OkgMr+DQpW49Eu9sDPvgQtgpcnlg0vCdXHrDTkikn4zMwoSBGS8febvCjrrka8NdxW+yTeMvoEGAluuuss3TGReDHrMwSpH+9KGk1N/9uA5pv86qiVmPVhEkPHY1sBGzINPNkxWkNWooJakEfyJXdjFWao+UsREEM5W1jhygHh3kL3WSROF6cGErFrQo9DQ7s65hXrZXiYcnXiE6Hx7k2FLUT7yiHSElmiDvCvzE67hy+FNqyuEOJ7Qcqp4UKK9PTwvBOFbMj6gF/854XC186HwAUj03cWIiiIN45BCCI4og/r/jgw4JEhL8kSeA0C0bplzQMK5CbqQiCzPt7TvY2839/e39ySlm7qWYkaeY7c1hcsUkP4ViKrgNOllspYJbGTkQy1ZIJtZwQPX3ghcNI5C2vmkkEfIEtZONcs/E9zbnyQ7tzCfQTupp5/az9vvTXfl/6KXP6D6xw9Q/zYuAA68ML78IiNHgpU86HHa61wKB/17guH7jnuS8rGEw7SO4k50JLwVIIVdmREblL5qLel7gIAHvuZfsdAwTpSFQ7H4aMVrqfnmKbv4F</diagram></mxfile>" > @@ -38,20 +38,20 @@ >
- autoware_planning_validator + planning_validator
- autoware_planning_validator + planning_validator
- - + + -
+
- Trajectory + Trajectory - - + + -
+
- ego_kinema... + ego_kinema... @@ -118,37 +118,34 @@ PlanningValidatorStatus - + -
+
- Trajectory to be validated + Trajectory to be validated
- Trajectory to be vali... + Trajectory to be vali... - + -
+
- Used for the validation + Used for the validation
- Used for the validati... + Used for the validati... @@ -161,7 +158,7 @@ >
- Validated trajectory + Validated trajectory
@@ -179,7 +176,7 @@ >
- To send validation result to the system: OK/ERROR + To send validation result to the system: OK/ERROR
@@ -197,7 +194,7 @@ >
- To show the result and the reason for other modules/users + To show the result and the reason for other modules/users
@@ -205,11 +202,47 @@ To show the result and the re... + + + + + +
+
+
+ Objects +
+
+
+
+ Objects +
+
+ + + + +
+
+
+ Recognized objects to detect collision +
+
+
+
+ Recognized objects to... +
+
- Viewer does not support full SVG 1.1 + Text is not SVG - cannot display diff --git a/planning/autoware_planning_validator/include/autoware/planning_validator/debug_marker.hpp b/planning/autoware_planning_validator/include/autoware/planning_validator/debug_marker.hpp index 5e8fd31734121..38d20eeb3f4fa 100644 --- a/planning/autoware_planning_validator/include/autoware/planning_validator/debug_marker.hpp +++ b/planning/autoware_planning_validator/include/autoware/planning_validator/debug_marker.hpp @@ -15,6 +15,8 @@ #ifndef AUTOWARE__PLANNING_VALIDATOR__DEBUG_MARKER_HPP_ #define AUTOWARE__PLANNING_VALIDATOR__DEBUG_MARKER_HPP_ +#include "autoware_vehicle_info_utils/vehicle_info_utils.hpp" + #include #include @@ -34,6 +36,9 @@ class PlanningValidatorDebugMarkerPublisher void pushPoseMarker( const autoware_planning_msgs::msg::TrajectoryPoint & p, const std::string & ns, int id = 0); void pushPoseMarker(const geometry_msgs::msg::Pose & pose, const std::string & ns, int id = 0); + void pushFootprintMarker( + const geometry_msgs::msg::Pose & pose, + const autoware::vehicle_info_utils::VehicleInfo & vehicle_info, const std::string & ns); void pushVirtualWall(const geometry_msgs::msg::Pose & pose); void pushWarningMsg(const geometry_msgs::msg::Pose & pose, const std::string & msg); void publish(); diff --git a/planning/autoware_planning_validator/include/autoware/planning_validator/planning_validator.hpp b/planning/autoware_planning_validator/include/autoware/planning_validator/planning_validator.hpp index e996855b9b4da..a8603db8f8951 100644 --- a/planning/autoware_planning_validator/include/autoware/planning_validator/planning_validator.hpp +++ b/planning/autoware_planning_validator/include/autoware/planning_validator/planning_validator.hpp @@ -16,6 +16,7 @@ #define AUTOWARE__PLANNING_VALIDATOR__PLANNING_VALIDATOR_HPP_ #include "autoware/planning_validator/debug_marker.hpp" +#include "autoware/universe_utils/geometry/boost_geometry.hpp" #include "autoware/universe_utils/ros/logger_level_configure.hpp" #include "autoware/universe_utils/ros/polling_subscriber.hpp" #include "autoware/universe_utils/system/stop_watch.hpp" @@ -26,6 +27,7 @@ #include #include +#include #include #include #include @@ -37,6 +39,8 @@ namespace autoware::planning_validator { using autoware::universe_utils::StopWatch; +using autoware::vehicle_info_utils::VehicleInfo; +using autoware_perception_msgs::msg::PredictedObjects; using autoware_planning_msgs::msg::Trajectory; using autoware_planning_msgs::msg::TrajectoryPoint; using autoware_planning_validator::msg::PlanningValidatorStatus; @@ -59,6 +63,9 @@ struct ValidationParams double velocity_deviation_threshold; double distance_deviation_threshold; double longitudinal_distance_deviation_threshold; + double trajectory_to_object_distance_threshold; + double ego_to_object_distance_threshold; + double time_tolerance_threshold; // parameters double forward_trajectory_length_acceleration; @@ -86,6 +93,7 @@ class PlanningValidator : public rclcpp::Node bool checkValidDistanceDeviation(const Trajectory & trajectory); bool checkValidLongitudinalDistanceDeviation(const Trajectory & trajectory); bool checkValidForwardTrajectoryLength(const Trajectory & trajectory); + bool checkValidTrajectoryCollision(const Trajectory & trajectory); private: void setupDiag(); @@ -105,6 +113,8 @@ class PlanningValidator : public rclcpp::Node autoware::universe_utils::InterProcessPollingSubscriber sub_kinematics_{ this, "~/input/kinematics"}; + autoware::universe_utils::InterProcessPollingSubscriber sub_obj_{ + this, "~/input/objects"}; rclcpp::Subscription::SharedPtr sub_traj_; rclcpp::Publisher::SharedPtr pub_traj_; rclcpp::Publisher::SharedPtr pub_status_; @@ -126,7 +136,7 @@ class PlanningValidator : public rclcpp::Node PlanningValidatorStatus validation_status_; ValidationParams validation_params_; // for thresholds - autoware::vehicle_info_utils::VehicleInfo vehicle_info_; + VehicleInfo vehicle_info_; bool isAllValid(const PlanningValidatorStatus & status) const; @@ -134,6 +144,7 @@ class PlanningValidator : public rclcpp::Node Trajectory::ConstSharedPtr previous_published_trajectory_; Odometry::ConstSharedPtr current_kinematics_; + PredictedObjects::ConstSharedPtr current_objects_; std::shared_ptr debug_pose_publisher_; diff --git a/planning/autoware_planning_validator/include/autoware/planning_validator/utils.hpp b/planning/autoware_planning_validator/include/autoware/planning_validator/utils.hpp index 7d8aca14aff77..b3a9be06322f6 100644 --- a/planning/autoware_planning_validator/include/autoware/planning_validator/utils.hpp +++ b/planning/autoware_planning_validator/include/autoware/planning_validator/utils.hpp @@ -15,18 +15,34 @@ #ifndef AUTOWARE__PLANNING_VALIDATOR__UTILS_HPP_ #define AUTOWARE__PLANNING_VALIDATOR__UTILS_HPP_ -#include +#include "autoware/universe_utils/geometry/boost_geometry.hpp" +#include "autoware_vehicle_info_utils/vehicle_info_utils.hpp" +#include #include -#include +#include +#include +#include +#include + #include #include namespace autoware::planning_validator { +using autoware::universe_utils::Polygon2d; +using autoware::vehicle_info_utils::VehicleInfo; +using autoware_perception_msgs::msg::PredictedObject; +using autoware_perception_msgs::msg::PredictedObjects; +using autoware_perception_msgs::msg::PredictedPath; +using autoware_perception_msgs::msg::Shape; using autoware_planning_msgs::msg::Trajectory; using autoware_planning_msgs::msg::TrajectoryPoint; +using Point = autoware::universe_utils::Point2d; +using Box = boost::geometry::model::box; +using BoxTimeIndexPair = std::pair>; +using Rtree = boost::geometry::index::rtree>; std::pair getAbsMaxValAndIdx(const std::vector & v); @@ -57,6 +73,31 @@ std::pair calcMaxSteeringAngles( std::pair calcMaxSteeringRates( const Trajectory & trajectory, const double wheelbase); +std::optional> check_collision( + const PredictedObjects & predicted_objects, const Trajectory & trajectory, + const geometry_msgs::msg::Point & current_ego_position, const VehicleInfo & vehicle_info, + const double trajectory_to_object_distance_threshold, + const double ego_to_object_distance_threshold, const double time_tolerance_threshold); + +Rtree make_ego_footprint_rtree( + const std::vector & trajectory, + const VehicleInfo & vehicle_info); + +std::optional filter_objects( + const PredictedObjects & objects, + const std::vector & trajectory, + const double trajectory_to_object_distance_threshold, + const double ego_to_object_distance_threshold); + +std::optional find_highest_confidence_path(const PredictedObject & object); + +void make_predicted_object_rtree( + const PredictedPath & highest_confidence_path, const Shape & object_shape, + const double predicted_time_step, std::vector & predicted_object_rtree_nodes); + +std::vector> detect_collisions( + const Rtree & ego_rtree, const Rtree & predicted_object_rtree, double time_tolerance); + bool checkFinite(const TrajectoryPoint & point); void shiftPose(geometry_msgs::msg::Pose & pose, double longitudinal); diff --git a/planning/autoware_planning_validator/launch/planning_validator.launch.xml b/planning/autoware_planning_validator/launch/planning_validator.launch.xml index aeb9c37d67cda..d695267c3d287 100644 --- a/planning/autoware_planning_validator/launch/planning_validator.launch.xml +++ b/planning/autoware_planning_validator/launch/planning_validator.launch.xml @@ -10,6 +10,7 @@ + diff --git a/planning/autoware_planning_validator/msg/PlanningValidatorStatus.msg b/planning/autoware_planning_validator/msg/PlanningValidatorStatus.msg index cf127347a6021..a52ec3cdbcfda 100644 --- a/planning/autoware_planning_validator/msg/PlanningValidatorStatus.msg +++ b/planning/autoware_planning_validator/msg/PlanningValidatorStatus.msg @@ -15,6 +15,7 @@ bool is_valid_velocity_deviation bool is_valid_distance_deviation bool is_valid_longitudinal_distance_deviation bool is_valid_forward_trajectory_length +bool is_valid_no_collision # values int64 trajectory_size diff --git a/planning/autoware_planning_validator/src/debug_marker.cpp b/planning/autoware_planning_validator/src/debug_marker.cpp index 7de23de8eda01..44dedaff69b38 100644 --- a/planning/autoware_planning_validator/src/debug_marker.cpp +++ b/planning/autoware_planning_validator/src/debug_marker.cpp @@ -14,6 +14,8 @@ #include "autoware/planning_validator/debug_marker.hpp" +#include "autoware/universe_utils/geometry/geometry.hpp" + #include #include @@ -73,6 +75,32 @@ void PlanningValidatorDebugMarkerPublisher::pushPoseMarker( marker_array_.markers.push_back(marker); } +void PlanningValidatorDebugMarkerPublisher::pushFootprintMarker( + const geometry_msgs::msg::Pose & pose, + const autoware::vehicle_info_utils::VehicleInfo & vehicle_info, const std::string & ns) +{ + using autoware::universe_utils::createMarkerColor; + Marker marker = autoware::universe_utils::createDefaultMarker( + "map", node_->get_clock()->now(), ns, getMarkerId(ns), Marker::LINE_STRIP, + autoware::universe_utils::createMarkerScale(0.1, 0.1, 0.1), + createMarkerColor(1.0, 0.0, 0.0, 0.999)); + const double half_width = vehicle_info.vehicle_width_m / 2.0; + const double base_to_front = vehicle_info.vehicle_length_m - vehicle_info.rear_overhang_m; + const double base_to_rear = vehicle_info.rear_overhang_m; + + marker.points.push_back( + autoware::universe_utils::calcOffsetPose(pose, base_to_front, -half_width, 0.0).position); + marker.points.push_back( + autoware::universe_utils::calcOffsetPose(pose, base_to_front, half_width, 0.0).position); + marker.points.push_back( + autoware::universe_utils::calcOffsetPose(pose, -base_to_rear, half_width, 0.0).position); + marker.points.push_back( + autoware::universe_utils::calcOffsetPose(pose, -base_to_rear, -half_width, 0.0).position); + marker.points.push_back(marker.points.front()); + marker.lifetime = rclcpp::Duration::from_seconds(0.2); + marker_array_.markers.push_back(marker); +} + void PlanningValidatorDebugMarkerPublisher::pushWarningMsg( const geometry_msgs::msg::Pose & pose, const std::string & msg) { diff --git a/planning/autoware_planning_validator/src/planning_validator.cpp b/planning/autoware_planning_validator/src/planning_validator.cpp index 29e8932c87842..fea015a6aec08 100644 --- a/planning/autoware_planning_validator/src/planning_validator.cpp +++ b/planning/autoware_planning_validator/src/planning_validator.cpp @@ -87,6 +87,11 @@ void PlanningValidator::setupParameters() declare_parameter(ps + "forward_trajectory_length_acceleration"); p.forward_trajectory_length_margin = declare_parameter(ps + "forward_trajectory_length_margin"); + p.trajectory_to_object_distance_threshold = + declare_parameter(ps + "trajectory_to_object_distance_threshold"); + p.ego_to_object_distance_threshold = + declare_parameter(ps + "ego_to_object_distance_threshold"); + p.time_tolerance_threshold = declare_parameter(ps + "time_tolerance_threshold"); } try { @@ -169,6 +174,9 @@ void PlanningValidator::setupDiag() stat, validation_status_.is_valid_forward_trajectory_length, "trajectory length is too short"); }); + d->add(ns + "trajectory_collision", [&](auto & stat) { + setStatus(stat, validation_status_.is_valid_no_collision, "collision is detected"); + }); } bool PlanningValidator::isDataReady() @@ -181,6 +189,9 @@ bool PlanningValidator::isDataReady() if (!current_kinematics_) { return waiting("current_kinematics_"); } + if (!current_objects_) { + return waiting("current_objects_"); + } if (!current_trajectory_) { return waiting("current_trajectory_"); } @@ -195,6 +206,7 @@ void PlanningValidator::onTrajectory(const Trajectory::ConstSharedPtr msg) // receive data current_kinematics_ = sub_kinematics_.takeData(); + current_objects_ = sub_obj_.takeData(); if (!isDataReady()) return; @@ -320,6 +332,7 @@ void PlanningValidator::validate(const Trajectory & trajectory) s.is_valid_lateral_acc = checkValidLateralAcceleration(resampled); s.is_valid_steering = checkValidSteering(resampled); s.is_valid_steering_rate = checkValidSteeringRate(resampled); + s.is_valid_no_collision = checkValidTrajectoryCollision(resampled); s.invalid_count = isAllValid(s) ? 0 : s.invalid_count + 1; } @@ -545,8 +558,36 @@ bool PlanningValidator::checkValidForwardTrajectoryLength(const Trajectory & tra return forward_length > forward_length_required; } +bool PlanningValidator::checkValidTrajectoryCollision(const Trajectory & trajectory) +{ + const auto ego_speed = std::abs(current_kinematics_->twist.twist.linear.x); + if (ego_speed < 1.0 / 3.6) { + return true; // Ego is almost stopped. + } + + const auto & collided_points = check_collision( + *current_objects_, trajectory, current_kinematics_->pose.pose.position, vehicle_info_, + validation_params_.trajectory_to_object_distance_threshold, + validation_params_.ego_to_object_distance_threshold, + validation_params_.time_tolerance_threshold); + + if (collided_points) { + for (const auto & p : *collided_points) { + debug_pose_publisher_->pushPoseMarker(p.pose, "collision", 0); + debug_pose_publisher_->pushFootprintMarker(p.pose, vehicle_info_, "collision"); + } + } + return !collided_points; +} + bool PlanningValidator::isAllValid(const PlanningValidatorStatus & s) const { + // TODO(Sugahara): Add s.is_valid_no_collision after verifying that: + // 1. The value of is_valid_no_collision correctly identifies path problems + // 2. Adding this check won't incorrectly invalidate otherwise valid paths + // + // want to avoid false negatives where good paths are marked invalid just because + // isAllValid becomes false return s.is_valid_size && s.is_valid_finite_value && s.is_valid_interval && s.is_valid_relative_angle && s.is_valid_curvature && s.is_valid_lateral_acc && s.is_valid_longitudinal_max_acc && s.is_valid_longitudinal_min_acc && @@ -583,6 +624,10 @@ void PlanningValidator::displayStatus() s.is_valid_longitudinal_distance_deviation, "planning trajectory is too far from ego in longitudinal direction!!"); warn(s.is_valid_forward_trajectory_length, "planning trajectory forward length is not enough!!"); + // warn( + // s.is_valid_no_collision, + // "planning trajectory has collision!! but this validation is not utilized for trajectory " + // "validation."); } } // namespace autoware::planning_validator diff --git a/planning/autoware_planning_validator/src/utils.cpp b/planning/autoware_planning_validator/src/utils.cpp index 0a70b4d415dcb..8b8b32cf289da 100644 --- a/planning/autoware_planning_validator/src/utils.cpp +++ b/planning/autoware_planning_validator/src/utils.cpp @@ -14,9 +14,13 @@ #include "autoware/planning_validator/utils.hpp" +#include "autoware/universe_utils/geometry/boost_polygon_utils.hpp" + #include #include +#include + #include #include #include @@ -289,6 +293,181 @@ std::pair calcMaxSteeringRates( return {max_steering_rate, max_index}; } +std::optional> check_collision( + const PredictedObjects & predicted_objects, const Trajectory & trajectory, + const geometry_msgs::msg::Point & current_ego_position, const VehicleInfo & vehicle_info, + const double trajectory_to_object_distance_threshold, + const double ego_to_object_distance_threshold, const double time_tolerance_threshold) +{ + std::vector filtered_trajectory; + + filtered_trajectory.reserve(trajectory.points.size()); + + for (size_t i = 0; i < trajectory.points.size(); ++i) { + const auto & point = trajectory.points[i]; + const double dist_to_point = autoware::motion_utils::calcSignedArcLength( + trajectory.points, current_ego_position, size_t(i)); + + // Only include points that are ahead of current position (positive distance) + if (dist_to_point > 0.0) { + filtered_trajectory.push_back(point); + } + } + if (filtered_trajectory.size() == 0) { + return std::nullopt; + } + // Calculate timestamps for each trajectory point + motion_utils::calculate_time_from_start(filtered_trajectory, current_ego_position); + + const auto & ego_rtree = make_ego_footprint_rtree(filtered_trajectory, vehicle_info); + + const auto filtered_objects = filter_objects( + predicted_objects, filtered_trajectory, trajectory_to_object_distance_threshold, + ego_to_object_distance_threshold); + if (!filtered_objects) { + return std::nullopt; + } + + std::vector predicted_object_rtree_nodes; + + // Check each predicted object for potential collisions + for (const auto & object : filtered_objects.value().objects) { + const auto & highest_confidence_path = find_highest_confidence_path(object); + if (!highest_confidence_path) { + continue; + } + + const double predicted_time_step = highest_confidence_path.value().time_step.sec + + highest_confidence_path.value().time_step.nanosec * 1e-9; + + make_predicted_object_rtree( + highest_confidence_path.value(), object.shape, predicted_time_step, + predicted_object_rtree_nodes); + } + + Rtree predicted_object_rtree( + predicted_object_rtree_nodes.begin(), predicted_object_rtree_nodes.end()); + + const auto & collision_index_set = + detect_collisions(ego_rtree, predicted_object_rtree, time_tolerance_threshold); + std::vector collision_points; + for (const auto & [ego_index, obj_index] : collision_index_set) { + collision_points.push_back(filtered_trajectory[ego_index]); + } + + return collision_points.empty() ? std::nullopt : std::make_optional(collision_points); +} + +Rtree make_ego_footprint_rtree( + const std::vector & trajectory, + const VehicleInfo & vehicle_info) +{ + autoware::universe_utils::MultiPolygon2d trajectory_footprints; + const double base_to_front = vehicle_info.wheel_base_m + vehicle_info.front_overhang_m; + const double base_to_rear = vehicle_info.rear_overhang_m; + + for (const auto & p : trajectory) + trajectory_footprints.push_back(autoware::universe_utils::toFootprint( + p.pose, base_to_front, base_to_rear, vehicle_info.vehicle_width_m)); + std::vector rtree_nodes; + + rtree_nodes.reserve(trajectory_footprints.size()); + for (auto i = 0UL; i < trajectory_footprints.size(); ++i) { + const auto box = + boost::geometry::return_envelope(trajectory_footprints[i]); + const double time = + trajectory[i].time_from_start.sec + trajectory[i].time_from_start.nanosec * 1e-9; + rtree_nodes.emplace_back(box, std::make_pair(time, i)); + } + return Rtree(rtree_nodes); +} + +std::optional filter_objects( + const PredictedObjects & objects, + const std::vector & trajectory, + const double trajectory_to_object_distance_threshold, + const double ego_to_object_distance_threshold) +{ + PredictedObjects filtered_objects; + + for (const auto & object : objects.objects) { + const auto & object_position = object.kinematics.initial_pose_with_covariance.pose.position; + const size_t nearest_index = + autoware::motion_utils::findNearestIndex(trajectory, object_position); + const double trajectory_to_object_distance = + autoware::universe_utils::calcDistance2d(trajectory[nearest_index], object_position); + const double ego_to_object_distance = + autoware::universe_utils::calcDistance2d(trajectory.front().pose.position, object_position); + + if ( + trajectory_to_object_distance < trajectory_to_object_distance_threshold && + ego_to_object_distance < ego_to_object_distance_threshold) { + filtered_objects.objects.push_back(object); + } + } + + return filtered_objects.objects.empty() ? std::nullopt : std::make_optional(filtered_objects); +} + +std::optional find_highest_confidence_path(const PredictedObject & object) +{ + const auto & paths = object.kinematics.predicted_paths; + + if (paths.empty()) { + return std::nullopt; + } + + const auto max_confidence_it = std::max_element( + paths.begin(), paths.end(), + [](const PredictedPath & a, const PredictedPath & b) { return a.confidence < b.confidence; }); + + return *max_confidence_it; +} + +void make_predicted_object_rtree( + const PredictedPath & highest_confidence_path, const Shape & object_shape, + const double predicted_time_step, std::vector & predicted_object_rtree_nodes) +{ + for (size_t j = 0; j < highest_confidence_path.path.size(); ++j) { + const auto & pose = highest_confidence_path.path[j]; + const double predicted_time = j * predicted_time_step; + + const auto predicted_polygon = autoware::universe_utils::toPolygon2d(pose, object_shape); + + const auto box = + boost::geometry::return_envelope(predicted_polygon); + + predicted_object_rtree_nodes.emplace_back(box, std::make_pair(predicted_time, j)); + } +} + +std::vector> detect_collisions( + const Rtree & ego_rtree, const Rtree & predicted_object_rtree, double time_tolerance) +{ + std::vector> collision_sets; + + for (const auto & ego_value : ego_rtree) { + const auto & ego_box = ego_value.first; + const double ego_time = ego_value.second.first; + + std::vector potential_collisions; + predicted_object_rtree.query( + boost::geometry::index::intersects(ego_box) && + boost::geometry::index::satisfies([&](const BoxTimeIndexPair & obj_value) { + return std::fabs(obj_value.second.first - ego_time) <= time_tolerance; + }), + std::back_inserter(potential_collisions)); + + for (const auto & obj_value : potential_collisions) { + if (boost::geometry::intersects(ego_box, obj_value.first)) { + collision_sets.emplace_back(ego_value.second.second, obj_value.second.second); + } + } + } + + return collision_sets; +} + bool checkFinite(const TrajectoryPoint & point) { const auto & p = point.pose.position;