From 6ff212147b2159e67462c0e90d80c3b1905e5bb7 Mon Sep 17 00:00:00 2001 From: Maxime CLEMENT <78338830+maxime-clem@users.noreply.github.com> Date: Sun, 20 Oct 2024 21:29:03 +0900 Subject: [PATCH] test(detection_area): refactor and add unit tests (#9087) Signed-off-by: Maxime CLEMENT --- .../CMakeLists.txt | 13 +- .../package.xml | 1 + .../src/scene.cpp | 169 ++-------------- .../src/scene.hpp | 12 +- .../src/utils.cpp | 165 ++++++++++++++++ .../src/utils.hpp | 71 +++++++ .../test/test_utils.cpp | 183 ++++++++++++++++++ 7 files changed, 448 insertions(+), 166 deletions(-) create mode 100644 planning/behavior_velocity_planner/autoware_behavior_velocity_detection_area_module/src/utils.cpp create mode 100644 planning/behavior_velocity_planner/autoware_behavior_velocity_detection_area_module/src/utils.hpp create mode 100644 planning/behavior_velocity_planner/autoware_behavior_velocity_detection_area_module/test/test_utils.cpp diff --git a/planning/behavior_velocity_planner/autoware_behavior_velocity_detection_area_module/CMakeLists.txt b/planning/behavior_velocity_planner/autoware_behavior_velocity_detection_area_module/CMakeLists.txt index 57eb826927663..3aff4a524ffdd 100644 --- a/planning/behavior_velocity_planner/autoware_behavior_velocity_detection_area_module/CMakeLists.txt +++ b/planning/behavior_velocity_planner/autoware_behavior_velocity_detection_area_module/CMakeLists.txt @@ -6,9 +6,16 @@ autoware_package() pluginlib_export_plugin_description_file(autoware_behavior_velocity_planner plugins.xml) ament_auto_add_library(${PROJECT_NAME} SHARED - src/debug.cpp - src/manager.cpp - src/scene.cpp + DIRECTORY src ) +if(BUILD_TESTING) + find_package(ament_lint_auto REQUIRED) + ament_lint_auto_find_test_dependencies() + ament_add_ros_isolated_gtest(test_${PROJECT_NAME} + test/test_utils.cpp + ) + target_link_libraries(test_${PROJECT_NAME} ${PROJECT_NAME}) +endif() + ament_auto_package(INSTALL_TO_SHARE config) diff --git a/planning/behavior_velocity_planner/autoware_behavior_velocity_detection_area_module/package.xml b/planning/behavior_velocity_planner/autoware_behavior_velocity_detection_area_module/package.xml index c707e2fbfdbde..8669aa16b7252 100644 --- a/planning/behavior_velocity_planner/autoware_behavior_velocity_detection_area_module/package.xml +++ b/planning/behavior_velocity_planner/autoware_behavior_velocity_detection_area_module/package.xml @@ -33,6 +33,7 @@ tf2_geometry_msgs visualization_msgs + ament_cmake_ros ament_lint_auto autoware_lint_common diff --git a/planning/behavior_velocity_planner/autoware_behavior_velocity_detection_area_module/src/scene.cpp b/planning/behavior_velocity_planner/autoware_behavior_velocity_detection_area_module/src/scene.cpp index d6a1747259962..ad3ba986733e1 100644 --- a/planning/behavior_velocity_planner/autoware_behavior_velocity_detection_area_module/src/scene.cpp +++ b/planning/behavior_velocity_planner/autoware_behavior_velocity_detection_area_module/src/scene.cpp @@ -14,32 +14,28 @@ #include "scene.hpp" +#include "utils.hpp" + #include #include #include -#ifdef ROS_DISTRO_GALACTIC -#include -#else #include -#endif #include -#include #include #include #include namespace autoware::behavior_velocity_planner { -namespace bg = boost::geometry; using autoware::motion_utils::calcLongitudinalOffsetPose; using autoware::motion_utils::calcSignedArcLength; DetectionAreaModule::DetectionAreaModule( const int64_t module_id, const int64_t lane_id, const lanelet::autoware::DetectionArea & detection_area_reg_elem, - const PlannerParam & planner_param, const rclcpp::Logger logger, + const PlannerParam & planner_param, const rclcpp::Logger & logger, const rclcpp::Clock::SharedPtr clock) : SceneModuleInterface(module_id, logger, clock), lane_id_(lane_id), @@ -51,13 +47,6 @@ DetectionAreaModule::DetectionAreaModule( velocity_factor_.init(PlanningBehavior::USER_DEFINED_DETECTION_AREA); } -LineString2d DetectionAreaModule::getStopLineGeometry2d() const -{ - const lanelet::ConstLineString3d stop_line = detection_area_reg_elem_.stopLine(); - return planning_utils::extendLine( - stop_line[0], stop_line[1], planner_data_->stop_line_extend_length); -} - bool DetectionAreaModule::modifyPathVelocity(PathWithLaneId * path, StopReason * stop_reason) { // Store original path @@ -69,14 +58,16 @@ bool DetectionAreaModule::modifyPathVelocity(PathWithLaneId * path, StopReason * *stop_reason = planning_utils::initializeStopReason(StopReason::DETECTION_AREA); // Find obstacles in detection area - const auto obstacle_points = getObstaclePoints(); + const auto obstacle_points = detection_area::get_obstacle_points( + detection_area_reg_elem_.detectionAreas(), *planner_data_->no_ground_pointcloud); debug_data_.obstacle_points = obstacle_points; if (!obstacle_points.empty()) { last_obstacle_found_time_ = std::make_shared(clock_->now()); } // Get stop line geometry - const auto stop_line = getStopLineGeometry2d(); + const auto stop_line = detection_area::get_stop_line_geometry2d( + detection_area_reg_elem_, planner_data_->stop_line_extend_length); // Get self pose const auto & self_pose = planner_data_->current_odometry->pose; @@ -118,7 +109,8 @@ bool DetectionAreaModule::modifyPathVelocity(PathWithLaneId * path, StopReason * setDistance(stop_dist); // Check state - setSafe(canClearStopState()); + setSafe(detection_area::can_clear_stop_state( + last_obstacle_found_time_, clock_->now(), planner_param_.state_clear_time)); if (isActivated()) { state_ = State::GO; last_obstacle_found_time_ = {}; @@ -165,7 +157,14 @@ bool DetectionAreaModule::modifyPathVelocity(PathWithLaneId * path, StopReason * // Ignore objects if braking distance is not enough if (planner_param_.use_pass_judge_line) { - if (state_ != State::STOP && !hasEnoughBrakingDistance(self_pose, stop_point->second)) { + const auto current_velocity = planner_data_->current_velocity->twist.linear.x; + const double pass_judge_line_distance = planning_utils::calcJudgeLineDistWithAccLimit( + current_velocity, planner_data_->current_acceleration->accel.accel.linear.x, + planner_data_->delay_response_time); + if ( + state_ != State::STOP && + !detection_area::has_enough_braking_distance( + self_pose, stop_point->second, pass_judge_line_distance, current_velocity)) { RCLCPP_WARN_THROTTLE( logger_, *clock_, std::chrono::milliseconds(1000).count(), "[detection_area] vehicle is over stop border"); @@ -206,138 +205,4 @@ bool DetectionAreaModule::modifyPathVelocity(PathWithLaneId * path, StopReason * return true; } - -// calc smallest enclosing circle with average O(N) algorithm -// reference: -// https://erickimphotography.com/blog/wp-content/uploads/2018/09/Computational-Geometry-Algorithms-and-Applications-3rd-Ed.pdf -std::pair calcSmallestEnclosingCircle( - const lanelet::ConstPolygon2d & poly) -{ - // The `eps` is used to avoid precision bugs in circle inclusion checks. - // If the value of `eps` is too small, this function doesn't work well. More than 1e-10 is - // recommended. - const double eps = 1e-5; - lanelet::BasicPoint2d center(0.0, 0.0); - double radius_squared = 0.0; - - auto cross = [](const lanelet::BasicPoint2d & p1, const lanelet::BasicPoint2d & p2) -> double { - return p1.x() * p2.y() - p1.y() * p2.x(); - }; - - auto make_circle_3 = [&]( - const lanelet::BasicPoint2d & p1, const lanelet::BasicPoint2d & p2, - const lanelet::BasicPoint2d & p3) -> void { - // reference for circumcenter vector https://en.wikipedia.org/wiki/Circumscribed_circle - const double A = (p2 - p3).squaredNorm(); - const double B = (p3 - p1).squaredNorm(); - const double C = (p1 - p2).squaredNorm(); - const double S = cross(p2 - p1, p3 - p1); - if (std::abs(S) < eps) return; - center = (A * (B + C - A) * p1 + B * (C + A - B) * p2 + C * (A + B - C) * p3) / (4 * S * S); - radius_squared = (center - p1).squaredNorm() + eps; - }; - - auto make_circle_2 = - [&](const lanelet::BasicPoint2d & p1, const lanelet::BasicPoint2d & p2) -> void { - center = (p1 + p2) * 0.5; - radius_squared = (center - p1).squaredNorm() + eps; - }; - - auto in_circle = [&](const lanelet::BasicPoint2d & p) -> bool { - return (center - p).squaredNorm() <= radius_squared; - }; - - // mini disc - for (size_t i = 1; i < poly.size(); i++) { - const auto p1 = poly[i].basicPoint2d(); - if (in_circle(p1)) continue; - - // mini disc with point - const auto p0 = poly[0].basicPoint2d(); - make_circle_2(p0, p1); - for (size_t j = 0; j < i; j++) { - const auto p2 = poly[j].basicPoint2d(); - if (in_circle(p2)) continue; - - // mini disc with two points - make_circle_2(p1, p2); - for (size_t k = 0; k < j; k++) { - const auto p3 = poly[k].basicPoint2d(); - if (in_circle(p3)) continue; - - // mini disc with tree points - make_circle_3(p1, p2, p3); - } - } - } - - return std::make_pair(center, radius_squared); -} - -std::vector DetectionAreaModule::getObstaclePoints() const -{ - std::vector obstacle_points; - - const auto detection_areas = detection_area_reg_elem_.detectionAreas(); - const auto & points = *(planner_data_->no_ground_pointcloud); - - for (const auto & detection_area : detection_areas) { - const auto poly = lanelet::utils::to2D(detection_area); - const auto circle = calcSmallestEnclosingCircle(poly); - for (const auto p : points) { - const double squared_dist = (circle.first.x() - p.x) * (circle.first.x() - p.x) + - (circle.first.y() - p.y) * (circle.first.y() - p.y); - if (squared_dist <= circle.second) { - if (bg::within(Point2d{p.x, p.y}, poly.basicPolygon())) { - obstacle_points.push_back(autoware::universe_utils::createPoint(p.x, p.y, p.z)); - // get all obstacle point becomes high computation cost so skip if any point is found - break; - } - } - } - } - - return obstacle_points; -} - -bool DetectionAreaModule::canClearStopState() const -{ - // vehicle can clear stop state if the obstacle has never appeared in detection area - if (!last_obstacle_found_time_) { - return true; - } - - // vehicle can clear stop state if the certain time has passed since the obstacle disappeared - const auto elapsed_time = clock_->now() - *last_obstacle_found_time_; - if (elapsed_time.seconds() >= planner_param_.state_clear_time) { - return true; - } - - // rollback in simulation mode - if (elapsed_time.seconds() < 0.0) { - return true; - } - - return false; -} - -bool DetectionAreaModule::hasEnoughBrakingDistance( - const geometry_msgs::msg::Pose & self_pose, const geometry_msgs::msg::Pose & line_pose) const -{ - // get vehicle info and compute pass_judge_line_distance - const auto current_velocity = planner_data_->current_velocity->twist.linear.x; - const double max_acc = planner_data_->max_stop_acceleration_threshold; - const double delay_response_time = planner_data_->delay_response_time; - const double pass_judge_line_distance = - planning_utils::calcJudgeLineDistWithAccLimit(current_velocity, max_acc, delay_response_time); - - // prevent from being judged as not having enough distance when the current velocity is zero - // and the vehicle crosses the stop line - if (current_velocity < 1e-3) { - return true; - } - - return arc_lane_utils::calcSignedDistance(self_pose, line_pose.position) > - pass_judge_line_distance; -} } // namespace autoware::behavior_velocity_planner diff --git a/planning/behavior_velocity_planner/autoware_behavior_velocity_detection_area_module/src/scene.hpp b/planning/behavior_velocity_planner/autoware_behavior_velocity_detection_area_module/src/scene.hpp index efc1c1809302a..d756ea39a7f92 100644 --- a/planning/behavior_velocity_planner/autoware_behavior_velocity_detection_area_module/src/scene.hpp +++ b/planning/behavior_velocity_planner/autoware_behavior_velocity_detection_area_module/src/scene.hpp @@ -63,11 +63,10 @@ class DetectionAreaModule : public SceneModuleInterface double distance_to_judge_over_stop_line; }; -public: DetectionAreaModule( const int64_t module_id, const int64_t lane_id, const lanelet::autoware::DetectionArea & detection_area_reg_elem, - const PlannerParam & planner_param, const rclcpp::Logger logger, + const PlannerParam & planner_param, const rclcpp::Logger & logger, const rclcpp::Clock::SharedPtr clock); bool modifyPathVelocity(PathWithLaneId * path, StopReason * stop_reason) override; @@ -76,15 +75,6 @@ class DetectionAreaModule : public SceneModuleInterface autoware::motion_utils::VirtualWalls createVirtualWalls() override; private: - LineString2d getStopLineGeometry2d() const; - - std::vector getObstaclePoints() const; - - bool canClearStopState() const; - - bool hasEnoughBrakingDistance( - const geometry_msgs::msg::Pose & self_pose, const geometry_msgs::msg::Pose & line_pose) const; - // Lane id int64_t lane_id_; diff --git a/planning/behavior_velocity_planner/autoware_behavior_velocity_detection_area_module/src/utils.cpp b/planning/behavior_velocity_planner/autoware_behavior_velocity_detection_area_module/src/utils.cpp new file mode 100644 index 0000000000000..6b3bf416e6d99 --- /dev/null +++ b/planning/behavior_velocity_planner/autoware_behavior_velocity_detection_area_module/src/utils.cpp @@ -0,0 +1,165 @@ +// Copyright 2024 Tier IV, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "utils.hpp" + +#include +#include +#include +#include + +#include +#include + +#include + +namespace +{ +// calc smallest enclosing circle with average O(N) algorithm +// reference: +// https://erickimphotography.com/blog/wp-content/uploads/2018/09/Computational-Geometry-Algorithms-and-Applications-3rd-Ed.pdf +std::pair get_smallest_enclosing_circle( + const lanelet::ConstPolygon2d & poly) +{ + // The `eps` is used to avoid precision bugs in circle inclusion checks. + // If the value of `eps` is too small, this function doesn't work well. More than 1e-10 is + // recommended. + const double eps = 1e-5; + lanelet::BasicPoint2d center(0.0, 0.0); + double radius_squared = 0.0; + + auto cross = [](const lanelet::BasicPoint2d & p1, const lanelet::BasicPoint2d & p2) -> double { + return p1.x() * p2.y() - p1.y() * p2.x(); + }; + + auto make_circle_3 = [&]( + const lanelet::BasicPoint2d & p1, const lanelet::BasicPoint2d & p2, + const lanelet::BasicPoint2d & p3) -> void { + // reference for circumcenter vector https://en.wikipedia.org/wiki/Circumscribed_circle + const double a = (p2 - p3).squaredNorm(); + const double b = (p3 - p1).squaredNorm(); + const double c = (p1 - p2).squaredNorm(); + const double s = cross(p2 - p1, p3 - p1); + if (std::abs(s) < eps) return; + center = (a * (b + c - a) * p1 + b * (c + a - b) * p2 + c * (a + b - c) * p3) / (4 * s * s); + radius_squared = (center - p1).squaredNorm() + eps; + }; + + auto make_circle_2 = + [&](const lanelet::BasicPoint2d & p1, const lanelet::BasicPoint2d & p2) -> void { + center = (p1 + p2) * 0.5; + radius_squared = (center - p1).squaredNorm() + eps; + }; + + auto in_circle = [&](const lanelet::BasicPoint2d & p) -> bool { + return (center - p).squaredNorm() <= radius_squared; + }; + + // mini disc + for (size_t i = 1; i < poly.size(); i++) { + const auto p1 = poly[i].basicPoint2d(); + if (in_circle(p1)) continue; + + // mini disc with point + const auto p0 = poly[0].basicPoint2d(); + make_circle_2(p0, p1); + for (size_t j = 0; j < i; j++) { + const auto p2 = poly[j].basicPoint2d(); + if (in_circle(p2)) continue; + + // mini disc with two points + make_circle_2(p1, p2); + for (size_t k = 0; k < j; k++) { + const auto p3 = poly[k].basicPoint2d(); + if (in_circle(p3)) continue; + + // mini disc with tree points + make_circle_3(p1, p2, p3); + } + } + } + + return std::make_pair(center, radius_squared); +} +} // namespace + +namespace autoware::behavior_velocity_planner::detection_area +{ +universe_utils::LineString2d get_stop_line_geometry2d( + const lanelet::autoware::DetectionArea & detection_area, const double extend_length) +{ + const auto stop_line = detection_area.stopLine(); + return planning_utils::extendLine(stop_line[0], stop_line[1], extend_length); +} + +std::vector get_obstacle_points( + const lanelet::ConstPolygons3d & detection_areas, const pcl::PointCloud & points) +{ + std::vector obstacle_points; + for (const auto & detection_area : detection_areas) { + const auto poly = lanelet::utils::to2D(detection_area); + const auto circle = get_smallest_enclosing_circle(poly); + for (const auto p : points) { + const double squared_dist = (circle.first.x() - p.x) * (circle.first.x() - p.x) + + (circle.first.y() - p.y) * (circle.first.y() - p.y); + if (squared_dist <= circle.second) { + if (boost::geometry::within(Point2d{p.x, p.y}, poly.basicPolygon())) { + obstacle_points.push_back(autoware::universe_utils::createPoint(p.x, p.y, p.z)); + // get all obstacle point becomes high computation cost so skip if any point is found + break; + } + } + } + } + return obstacle_points; +} + +bool can_clear_stop_state( + const std::shared_ptr & last_obstacle_found_time, const rclcpp::Time & now, + const double state_clear_time) +{ + // vehicle can clear stop state if the obstacle has never appeared in detection area + if (!last_obstacle_found_time) { + return true; + } + + // vehicle can clear stop state if the certain time has passed since the obstacle disappeared + const auto elapsed_time = now - *last_obstacle_found_time; + if (elapsed_time.seconds() >= state_clear_time) { + return true; + } + + // rollback in simulation mode + if (elapsed_time.seconds() < 0.0) { + return true; + } + + return false; +} + +bool has_enough_braking_distance( + const geometry_msgs::msg::Pose & self_pose, const geometry_msgs::msg::Pose & line_pose, + const double pass_judge_line_distance, const double current_velocity) +{ + // prevent from being judged as not having enough distance when the current velocity is zero + // and the vehicle crosses the stop line + if (current_velocity < 1e-3) { + return true; + } + + return arc_lane_utils::calcSignedDistance(self_pose, line_pose.position) > + pass_judge_line_distance; +} + +} // namespace autoware::behavior_velocity_planner::detection_area diff --git a/planning/behavior_velocity_planner/autoware_behavior_velocity_detection_area_module/src/utils.hpp b/planning/behavior_velocity_planner/autoware_behavior_velocity_detection_area_module/src/utils.hpp new file mode 100644 index 0000000000000..3a0ba0172cbd1 --- /dev/null +++ b/planning/behavior_velocity_planner/autoware_behavior_velocity_detection_area_module/src/utils.hpp @@ -0,0 +1,71 @@ +// Copyright 2024 Tier IV, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef UTILS_HPP_ +#define UTILS_HPP_ + +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include +#include + +namespace autoware::behavior_velocity_planner::detection_area +{ + +/// @brief get the extended stop line of the given detection area +/// @param [in] detection_area detection area +/// @param [in] extend_length [m] extension length to add on each edge of the stop line +/// @return extended stop line +universe_utils::LineString2d get_stop_line_geometry2d( + const lanelet::autoware::DetectionArea & detection_area, const double extend_length); + +/// @brief get the obstacle points found inside a detection area +/// @param [in] detection_areas detection area polygons +/// @param [in] points obstacle points +/// @return the first obstacle point found in each detection area +std::vector get_obstacle_points( + const lanelet::ConstPolygons3d & detection_areas, const pcl::PointCloud & points); + +/// @brief return true if the stop state can be cleared +/// @details can be cleared if enough time passed since last detecting an obstacle +/// @param [in] last_obstacle_found_time pointer to the time when an obstacle was last detected +/// @param [in] now current time +/// @param [in] state_clear_time [s] minimum duration since last obstacle detection to clear the +/// stop state +/// @return true if the stop state can be cleared +bool can_clear_stop_state( + const std::shared_ptr & last_obstacle_found_time, const rclcpp::Time & now, + const double state_clear_time); + +/// @brief return true if distance to brake is enough +/// @param self_pose current ego pose +/// @param line_pose stop pose +/// @param pass_judge_line_distance braking distance +/// @param current_velocity current ego velocity +/// @return true if the distance to brake is enough +bool has_enough_braking_distance( + const geometry_msgs::msg::Pose & self_pose, const geometry_msgs::msg::Pose & line_pose, + const double pass_judge_line_distance, const double current_velocity); +} // namespace autoware::behavior_velocity_planner::detection_area + +#endif // UTILS_HPP_ diff --git a/planning/behavior_velocity_planner/autoware_behavior_velocity_detection_area_module/test/test_utils.cpp b/planning/behavior_velocity_planner/autoware_behavior_velocity_detection_area_module/test/test_utils.cpp new file mode 100644 index 0000000000000..9b4dc5193f655 --- /dev/null +++ b/planning/behavior_velocity_planner/autoware_behavior_velocity_detection_area_module/test/test_utils.cpp @@ -0,0 +1,183 @@ +// Copyright 2024 Tier IV, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "../src/utils.hpp" + +#include +#include +#include +#include + +#include +#include + +TEST(TestUtils, getStopLine) +{ + using autoware::behavior_velocity_planner::detection_area::get_stop_line_geometry2d; + lanelet::LineString3d line; + line.push_back(lanelet::Point3d(lanelet::InvalId, 0.0, -1.0)); + line.push_back(lanelet::Point3d(lanelet::InvalId, 0.0, 1.0)); + lanelet::Polygons3d detection_areas; + lanelet::Polygon3d area; + area.push_back(lanelet::Point3d(lanelet::InvalId, 1.0, -1.0)); + area.push_back(lanelet::Point3d(lanelet::InvalId, 1.0, 1.0)); + area.push_back(lanelet::Point3d(lanelet::InvalId, 3.0, 1.0)); + area.push_back(lanelet::Point3d(lanelet::InvalId, 3.0, -1.0)); + detection_areas.push_back(area); + auto detection_area = + lanelet::autoware::DetectionArea::make(lanelet::InvalId, {}, detection_areas, line); + { + const double extend_length = 0.0; + const auto stop_line = get_stop_line_geometry2d(*detection_area, extend_length); + ASSERT_EQ(stop_line.size(), 2UL); + EXPECT_EQ(stop_line[0].x(), line[0].x()); + EXPECT_EQ(stop_line[0].y(), line[0].y()); + EXPECT_EQ(stop_line[1].x(), line[1].x()); + EXPECT_EQ(stop_line[1].y(), line[1].y()); + } + // extended line + for (auto extend_length = -2.0; extend_length < 2.0; extend_length += 0.1) { + const auto stop_line = get_stop_line_geometry2d(*detection_area, extend_length); + ASSERT_EQ(stop_line.size(), 2UL); + EXPECT_EQ(stop_line[0].x(), line[0].x()); + EXPECT_EQ(stop_line[0].y(), line[0].y() - extend_length); + EXPECT_EQ(stop_line[1].x(), line[1].x()); + EXPECT_EQ(stop_line[1].y(), line[1].y() + extend_length); + } +} + +TEST(TestUtils, getObstaclePoints) +{ + using autoware::behavior_velocity_planner::detection_area::get_obstacle_points; + lanelet::ConstPolygons3d detection_areas; + lanelet::Polygon3d area; + area.push_back(lanelet::Point3d(lanelet::InvalId, 1.0, -1.0)); + area.push_back(lanelet::Point3d(lanelet::InvalId, 1.0, 1.0)); + area.push_back(lanelet::Point3d(lanelet::InvalId, 3.0, 1.0)); + area.push_back(lanelet::Point3d(lanelet::InvalId, 3.0, -1.0)); + detection_areas.push_back(area); + pcl::PointCloud points; + // empty points + { + const auto obstacle_points = get_obstacle_points(detection_areas, points); + EXPECT_TRUE(obstacle_points.empty()); + } + // add points outside the detection area + points.emplace_back(0.0, 0.0, 0.0); + points.emplace_back(4.0, 4.0, 0.0); + { + const auto obstacle_points = get_obstacle_points(detection_areas, points); + EXPECT_TRUE(obstacle_points.empty()); + } + // add point on the edge of the detection area (will not be found) + points.emplace_back(1.0, 1.0, 0.0); + { + const auto obstacle_points = get_obstacle_points(detection_areas, points); + EXPECT_TRUE(obstacle_points.empty()); + } + // add point inside the detection area (will be found) + points.emplace_back(2.0, 0.0, 0.0); + { + const auto obstacle_points = get_obstacle_points(detection_areas, points); + ASSERT_EQ(obstacle_points.size(), 1UL); + EXPECT_EQ(obstacle_points[0].x, points[3].x); + EXPECT_EQ(obstacle_points[0].y, points[3].y); + } + // add a detection area that covers all points + lanelet::Polygon3d full_area; + full_area.push_back(lanelet::Point3d(lanelet::InvalId, -10.0, -10.0)); + full_area.push_back(lanelet::Point3d(lanelet::InvalId, -10.0, 10.0)); + full_area.push_back(lanelet::Point3d(lanelet::InvalId, 10.0, 10.0)); + full_area.push_back(lanelet::Point3d(lanelet::InvalId, 10.0, -10.0)); + detection_areas.push_back(full_area); + { + const auto obstacle_points = get_obstacle_points(detection_areas, points); + ASSERT_EQ(obstacle_points.size(), 2UL); // only the 1st point found for each area are returned + EXPECT_EQ(obstacle_points[0].x, points[3].x); + EXPECT_EQ(obstacle_points[0].y, points[3].y); + EXPECT_EQ(obstacle_points[1].x, points[0].x); + EXPECT_EQ(obstacle_points[1].y, points[0].y); + } +} + +TEST(TestUtils, canClearStopState) +{ + using autoware::behavior_velocity_planner::detection_area::can_clear_stop_state; + std::shared_ptr last_obstacle_found_time = nullptr; + // can clear if we never found an obstacle + for (auto now_s = 0; now_s <= 10; now_s += 1) { + for (auto now_ns = 0; now_ns <= 1e9; now_ns += 1e8) { + for (double state_clear_time = 0.0; state_clear_time <= 10.0; state_clear_time += 0.1) { + const auto can_clear = can_clear_stop_state( + last_obstacle_found_time, rclcpp::Time(now_s, now_ns, RCL_CLOCK_UNINITIALIZED), + state_clear_time); + EXPECT_TRUE(can_clear); + } + } + } + last_obstacle_found_time = std::make_shared(1.0, 0.0); + const auto state_clear_time = 1.0; + // special case for negative time difference which may occur with simulated time + EXPECT_TRUE(can_clear_stop_state(last_obstacle_found_time, rclcpp::Time(0, 0), state_clear_time)); + EXPECT_TRUE( + can_clear_stop_state(last_obstacle_found_time, rclcpp::Time(0, 9 * 1e8), state_clear_time)); + // cannot clear before the time has passed since the obstacle disappeared + EXPECT_FALSE( + can_clear_stop_state(last_obstacle_found_time, rclcpp::Time(1, 1), state_clear_time)); + EXPECT_FALSE( + can_clear_stop_state(last_obstacle_found_time, rclcpp::Time(1, 1e9 - 1), state_clear_time)); + // can clear after the time has passed + EXPECT_TRUE(can_clear_stop_state(last_obstacle_found_time, rclcpp::Time(2, 1), state_clear_time)); + EXPECT_TRUE( + can_clear_stop_state(last_obstacle_found_time, rclcpp::Time(100, 0), state_clear_time)); + // negative time + const auto negative_state_clear_time = -1.0; + EXPECT_TRUE( + can_clear_stop_state(last_obstacle_found_time, rclcpp::Time(0, 0), negative_state_clear_time)); + EXPECT_TRUE( + can_clear_stop_state(last_obstacle_found_time, rclcpp::Time(1, 0), negative_state_clear_time)); + EXPECT_TRUE( + can_clear_stop_state(last_obstacle_found_time, rclcpp::Time(2, 0), negative_state_clear_time)); +} + +TEST(TestUtils, hasEnoughBrakingDistance) +{ + using autoware::behavior_velocity_planner::detection_area::has_enough_braking_distance; + // prepare a stop pose 10m away from the self pose + geometry_msgs::msg::Pose self_pose; + self_pose.position.x = 0.0; + self_pose.position.y = 0.0; + geometry_msgs::msg::Pose line_pose; + line_pose.position.x = 10.0; + line_pose.position.y = 0.0; + // can always brake at zero velocity + for (auto pass_judge_line_distance = 0.0; pass_judge_line_distance <= 20.0; + pass_judge_line_distance += 0.1) { + double current_velocity = 0.0; + EXPECT_TRUE(has_enough_braking_distance( + self_pose, line_pose, pass_judge_line_distance, current_velocity)); + } + // if velocity is not zero, can brake if the pass judge line distance is lower than 10m + const double current_velocity = 5.0; + for (auto pass_judge_line_distance = 0.0; pass_judge_line_distance < 10.0; + pass_judge_line_distance += 0.1) { + EXPECT_TRUE(has_enough_braking_distance( + self_pose, line_pose, pass_judge_line_distance, current_velocity)); + } + for (auto pass_judge_line_distance = 10.0; pass_judge_line_distance <= 20.0; + pass_judge_line_distance += 0.1) { + EXPECT_FALSE(has_enough_braking_distance( + self_pose, line_pose, pass_judge_line_distance, current_velocity)); + } +}