diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000000000..d1688ee2df9a9 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,159 @@ +### Copied from .github/CODEOWNERS-manual ### + +### Automatically generated from package.xml ### +control/external_cmd_selector/* kenji.miyake@tier4.jp +control/obstacle_collision_checker/* kenji.miyake@tier4.jp +control/vehicle_cmd_gate/* takamasa.horibe@tier4.jp +control/trajectory_follower/* takamasa.horibe@tier4.jp takayuki.murooka@tier4.jp +control/trajectory_follower_nodes/* takamasa.horibe@tier4.jp takayuki.murooka@tier4.jp +control/joy_controller/* kenji.miyake@tier4.jp +control/lane_departure_checker/* kyoichi.sugahara@tier4.jp +control/shift_decider/* takamasa.horibe@tier4.jp +control/pure_pursuit/* takamasa.horibe@tier4.jp +control/operation_mode_transition_manager/* takamasa.horibe@tier4.jp +control/control_performance_analysis/* ali.boyali@tier4.jp berkay@leodrive.ai +common/autoware_ad_api_msgs/* isamu.takagi@tier4.jp +common/motion_common/* christopherj.ho@gmail.com +common/component_interface_specs/* isamu.takagi@tier4.jp +common/motion_testing/* christopherj.ho@gmail.com +common/autoware_auto_perception_rviz_plugin/* opensource@apex.ai taichi.higashide@tier4.jp +common/autoware_auto_tf2/* jit.ray.c@gmail.com +common/signal_processing/* takayuki.murooka@tier4.jp +common/tier4_perception_rviz_plugin/* yukihiro.saito@tier4.jp +common/path_distance_calculator/* isamu.takagi@tier4.jp +common/kalman_filter/* yukihiro.saito@tier4.jp +common/tier4_traffic_light_rviz_plugin/* satoshi.ota@tier4.jp +common/vehicle_constants_manager/* mfc@leodrive.ai +common/component_interface_utils/* isamu.takagi@tier4.jp +common/tier4_planning_rviz_plugin/* yukihiro.saito@tier4.jp +common/perception_utils/* takayuki.murooka@tier4.jp +common/web_controller/* yukihiro.saito@tier4.jp +common/goal_distance_calculator/* taiki.tanaka@tier4.jp +common/motion_utils/* yutaka.shimizu@tier4.jp satoshi.ota@tier4.jp takayuki.murooka@tier4.jp +common/tier4_debug_tools/* kenji.miyake@tier4.jp +common/tier4_localization_rviz_plugin/* isamu.takagi@tier4.jp +common/autoware_point_types/* taichi.higashide@tier4.jp +common/tier4_screen_capture_rviz_plugin/* taiki.tanaka@tier4.jp +common/tier4_control_rviz_plugin/* taiki.tanaka@tier4.jp +common/osqp_interface/* maxime.clement@tier4.jp +common/tier4_simulated_clock_rviz_plugin/* maxime.clement@tier4.jp +common/neural_networks_provider/* ambroise.vincent@arm.com +common/global_parameter_loader/* kenji.miyake@tier4.jp +common/autoware_auto_common/* opensource@apex.ai +common/tvm_utility/* ambroise.vincent@arm.com +common/autoware_auto_geometry/* opensource@apex.ai +common/autoware_ad_api_specs/* isamu.takagi@tier4.jp +common/tier4_state_rviz_plugin/* hiroki.ota@tier4.jp +common/tier4_datetime_rviz_plugin/* isamu.takagi@tier4.jp +common/grid_map_utils/* maxime.clement@tier4.jp +common/tier4_autoware_utils/* takamasa.horibe@tier4.jp kenji.miyake@tier4.jp +common/fake_test_node/* opensource@apex.ai +common/autoware_testing/* adam.dabrowski@robotec.ai +common/tier4_api_utils/* isamu.takagi@tier4.jp +common/tier4_calibration_rviz_plugin/* tomoya.kimura@tier4.jp +common/interpolation/* fumiya.watanabe@tier4.jp takayuki.murooka@tier4.jp +common/time_utils/* christopherj.ho@gmail.com +common/polar_grid/* yukihiro.saito@tier4.jp +common/tier4_vehicle_rviz_plugin/* yukihiro.saito@tier4.jp +launch/tier4_vehicle_launch/* yukihiro.saito@tier4.jp +launch/tier4_system_launch/* kenji.miyake@tier4.jp +launch/tier4_map_launch/* ryohsuke.mitsudome@tier4.jp +launch/tier4_control_launch/* takamasa.horibe@tier4.jp +launch/tier4_sensing_launch/* yukihiro.saito@tier4.jp +launch/tier4_autoware_api_launch/* isamu.takagi@tier4.jp ryohsuke.mitsudome@tier4.jp +launch/tier4_perception_launch/* yukihiro.saito@tier4.jp +launch/tier4_planning_launch/* takamasa.horibe@tier4.jp +launch/tier4_localization_launch/* yamato.ando@tier4.jp +launch/tier4_simulator_launch/* keisuke.shima@tier4.jp +evaluator/localization_evaluator/* dominik.jargot@robotec.ai +evaluator/kinematic_evaluator/* dominik.jargot@robotec.ai +map/map_loader/* ryohsuke.mitsudome@tier4.jp kenji.miyake@tier4.jp +map/map_tf_generator/* azumi.suzuki@tier4.jp +map/util/lanelet2_map_preprocessor/* ryohsuke.mitsudome@tier4.jp +simulator/simple_planning_simulator/* takamasa.horibe@tier4.jp +simulator/dummy_perception_publisher/* yukihiro.saito@tier4.jp +simulator/fault_injection/* keisuke.shima@tier4.jp +tools/simulator_test/simulator_compatibility_test/* shpark@morai.ai +sensing/livox/livox_tag_filter/* kenji.miyake@tier4.jp +sensing/pointcloud_preprocessor/* abrahammonrroy@yahoo.com yukihiro.saito@tier4.jp +sensing/probabilistic_occupancy_grid_map/* yukihiro.saito@tier4.jp +sensing/imu_corrector/* yamato.ando@tier4.jp +sensing/geo_pos_conv/* yamato.ando@tier4.jp +sensing/image_transport_decompressor/* yukihiro.saito@tier4.jp +sensing/vehicle_velocity_converter/* ryu.yamamoto@tier4.jp +sensing/image_diagnostics/* dai.nguyen@tier4.jp +sensing/tier4_pcl_extensions/* ryu.yamamoto@tier4.jp +sensing/gnss_poser/* yamato.ando@tier4.jp +planning/freespace_planner/* takamasa.horibe@tier4.jp +planning/obstacle_cruise_planner/* takayuki.murooka@tier4.jp yutaka.shimizu@tier4.jp +planning/planning_error_monitor/* yutaka.shimizu@tier4.jp +planning/surround_obstacle_checker/* satoshi.ota@tier4.jp +planning/costmap_generator/* kenji.miyake@tier4.jp +planning/behavior_path_planner/* zulfaqar.azmi@tier4.jp kosuke.takeuchi@tier4.jp kosuke.takeuchi@tier4.jp fumiya.watanabe@tier4.jp takamasa.horibe@tier4.jp yutaka.shimizu@tier4.jp takayuki.murooka@tier4.jp +planning/obstacle_stop_planner/* satoshi.ota@tier4.jp +planning/behavior_velocity_planner/* mamoru.sobue@tier4.jp satoshi.ota@tier4.jp kyoichi.sugahara@tier4.jp taiki.tanaka@tier4.jp kosuke.takeuchi@tier4.jp satoshi.ota@tier4.jp mamoru.sobue@tier4.jp yutaka.shimizu@tier4.jp kosuke.takeuchi@tier4.jp tomohito.ando@tier4.jp +planning/rtc_auto_approver/* fumiya.watanabe@tier4.jp +planning/scenario_selector/* kenji.miyake@tier4.jp +planning/planning_evaluator/* maxime.clement@tier4.jp +planning/rtc_interface/* fumiya.watanabe@tier4.jp +planning/planning_debug_tools/* takamasa.horibe@tier4.jp +planning/external_velocity_limit_selector/* satoshi.ota@tier4.jp +planning/freespace_planning_algorithms/* takamasa.horibe@tier4.jp +planning/obstacle_avoidance_planner/* takayuki.murooka@tier4.jp +planning/motion_velocity_smoother/* fumiya.watanabe@tier4.jp +planning/route_handler/* fumiya.watanabe@tier4.jp +planning/mission_planner/* ryohsuke.mitsudome@tier4.jp +perception/shape_estimation/* yukihiro.saito@tier4.jp +perception/front_vehicle_velocity_estimator/* satoshi.tanaka@tier4.jp +perception/traffic_light_visualization/* yukihiro.saito@tier4.jp +perception/detection_by_tracker/* yukihiro.saito@tier4.jp +perception/object_range_splitter/* yukihiro.saito@tier4.jp +perception/heatmap_visualizer/* kotaro.uetake@tier4.jp +perception/detected_object_feature_remover/* tomoya.kimura@tier4.jp +perception/crosswalk_traffic_light_estimator/* satoshi.ota@tier4.jp +perception/euclidean_cluster/* yukihiro.saito@tier4.jp +perception/image_projection_based_fusion/* yukihiro.saito@tier4.jp yusuke.muramatsu@tier4.jp +perception/traffic_light_ssd_fine_detector/* daisuke.nishimatsu@tier4.jp +perception/lidar_centerpoint/* yusuke.muramatsu@tier4.jp +perception/map_based_prediction/* yutaka.shimizu@tier4.jp +perception/multi_object_tracker/* yukihiro.saito@tier4.jp jilada.eccleston@tier4.jp +perception/object_merger/* yukihiro.saito@tier4.jp +perception/elevation_map_loader/* kosuke.takeuchi@tier4.jp taichi.higashide@tier4.jp +perception/traffic_light_map_based_detector/* yukihiro.saito@tier4.jp +perception/tensorrt_yolo/* daisuke.nishimatsu@tier4.jp +perception/lidar_apollo_instance_segmentation/* yukihiro.saito@tier4.jp +perception/radar_fusion_to_detected_object/* satoshi.tanaka@tier4.jp +perception/radar_tracks_msgs_converter/* satoshi.tanaka@tier4.jp +perception/traffic_light_classifier/* yukihiro.saito@tier4.jp +perception/detected_object_validation/* yukihiro.saito@tier4.jp +perception/ground_segmentation/* abrahammonrroy@yahoo.com yukihiro.saito@tier4.jp +perception/occupancy_grid_map_outlier_filter/* abrahammonrroy@yahoo.com yukihiro.saito@tier4.jp +perception/compare_map_segmentation/* abrahammonrroy@yahoo.com yukihiro.saito@tier4.jp +vehicle/steer_offset_estimator/* taiki.tanaka@tier4.jp +vehicle/raw_vehicle_cmd_converter/* takamasa.horibe@tier4.jp +vehicle/vehicle_info_util/* yamato.ando@tier4.jp +vehicle/accel_brake_map_calibrator/accel_brake_map_calibrator/* tomoya.kimura@tier4.jp +vehicle/external_cmd_converter/* takamasa.horibe@tier4.jp +localization/pose_initializer/* yamato.ando@tier4.jp +localization/ndt_pcl_modified/* yamato.ando@tier4.jp +localization/pose2twist/* yamato.ando@tier4.jp +localization/ndt_scan_matcher/* yamato.ando@tier4.jp +localization/gyro_odometer/* yamato.ando@tier4.jp +localization/localization_error_monitor/* yamato.ando@tier4.jp +localization/initial_pose_button_panel/* yamato.ando@tier4.jp +localization/twist2accel/* koji.minoda@tier4.jp yamato.ando@tier4.jp +localization/ndt/* yamato.ando@tier4.jp +localization/ekf_localizer/* takamasa.horibe@tier4.jp koji.minoda@tier4.jp yamato.ando@tier4.jp +localization/stop_filter/* koji.minoda@tier4.jp yamato.ando@tier4.jp +system/default_ad_api_helpers/ad_api_adaptors/* isamu.takagi@tier4.jp +system/default_ad_api_helpers/automatic_pose_initializer/* isamu.takagi@tier4.jp +system/velodyne_monitor/* fumihito.ito@tier4.jp +system/dummy_infrastructure/* kenji.miyake@tier4.jp +system/topic_state_monitor/* kenji.miyake@tier4.jp +system/bluetooth_monitor/* fumihito.ito@tier4.jp +system/ad_service_state_monitor/* kenji.miyake@tier4.jp +system/system_monitor/* fumihito.ito@tier4.jp +system/emergency_handler/* kenji.miyake@tier4.jp +system/dummy_diag_publisher/* kenji.miyake@tier4.jp +system/system_error_monitor/* kenji.miyake@tier4.jp +system/default_ad_api/* isamu.takagi@tier4.jp diff --git a/.github/CODEOWNERS-manual b/.github/CODEOWNERS-manual new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/.github/sync-files.yaml b/.github/sync-files.yaml index 31f869b6c8912..be25169b1604e 100644 --- a/.github/sync-files.yaml +++ b/.github/sync-files.yaml @@ -12,6 +12,7 @@ - source: .github/PULL_REQUEST_TEMPLATE/standard-change.md - source: .github/dependabot.yaml - source: .github/stale.yml + - source: .github/workflows/cancel-previous-workflows.yaml - source: .github/workflows/github-release.yaml - source: .github/workflows/pre-commit.yaml - source: .github/workflows/pre-commit-optional.yaml @@ -81,8 +82,8 @@ include:" {source} - source: .github/workflows/check-build-depends.yaml pre-commands: | - sd "container: ros:(\w+)" "container: ghcr.io/autowarefoundation/autoware-universe:\$1-latest" {source} sd -f ms '(rosdistro: humble.*?build-depends-repos): build_depends.repos' '$1: build_depends.humble.repos' {source} + - source: .github/workflows/update-codeowners-from-packages.yaml - source: codecov.yaml - repository: autowarefoundation/autoware-documentation diff --git a/.github/workflows/build-and-test-differential.yaml b/.github/workflows/build-and-test-differential.yaml index 3181aafbbc7c7..248dbb09b61a4 100644 --- a/.github/workflows/build-and-test-differential.yaml +++ b/.github/workflows/build-and-test-differential.yaml @@ -23,9 +23,6 @@ jobs: container: ghcr.io/autowarefoundation/autoware-universe:humble-latest build-depends-repos: build_depends.humble.repos steps: - - name: Cancel previous runs - uses: styfle/cancel-workflow-action@0.10.0 - - name: Check out repository uses: actions/checkout@v3 with: @@ -81,11 +78,20 @@ jobs: id: get-modified-packages uses: autowarefoundation/autoware-github-actions/get-modified-packages@v1 + - name: Get modified files + id: get-modified-files + uses: tj-actions/changed-files@v28 + with: + files: | + **/*.cpp + **/*.hpp + - name: Run clang-tidy - if: ${{ steps.get-modified-packages.outputs.modified-packages != '' }} + if: ${{ steps.get-modified-files.outputs.all_changed_files != '' }} uses: autowarefoundation/autoware-github-actions/clang-tidy@v1 with: rosdistro: humble target-packages: ${{ steps.get-modified-packages.outputs.modified-packages }} + target-files: ${{ steps.get-modified-files.outputs.all_changed_files }} clang-tidy-config-url: https://raw.githubusercontent.com/autowarefoundation/autoware/main/.clang-tidy build-depends-repos: build_depends.humble.repos diff --git a/.github/workflows/cancel-previous-workflows.yaml b/.github/workflows/cancel-previous-workflows.yaml new file mode 100644 index 0000000000000..aeb34c6a03dcf --- /dev/null +++ b/.github/workflows/cancel-previous-workflows.yaml @@ -0,0 +1,14 @@ +name: cancel-previous-workflows + +on: + pull_request_target: + +jobs: + cancel-previous-workflows: + runs-on: ubuntu-latest + steps: + - name: Cancel previous runs + uses: styfle/cancel-workflow-action@0.10.0 + with: + workflow_id: all + all_but_latest: true diff --git a/.github/workflows/check-build-depends.yaml b/.github/workflows/check-build-depends.yaml index 6c82d2bf0b950..600eaa3c5dcb4 100644 --- a/.github/workflows/check-build-depends.yaml +++ b/.github/workflows/check-build-depends.yaml @@ -3,16 +3,26 @@ name: check-build-depends on: pull_request: paths: - - build_depends.repos + - build_depends*.repos jobs: check-build-depends: runs-on: ubuntu-latest - container: ghcr.io/autowarefoundation/autoware-universe:galactic-latest + container: ${{ matrix.container }} + strategy: + fail-fast: false + matrix: + rosdistro: + - galactic + - humble + include: + - rosdistro: galactic + container: ros:galactic + build-depends-repos: build_depends.repos + - rosdistro: humble + container: ros:humble + build-depends-repos: build_depends.humble.repos steps: - - name: Cancel previous runs - uses: styfle/cancel-workflow-action@0.10.0 - - name: Check out repository uses: actions/checkout@v3 @@ -26,6 +36,6 @@ jobs: - name: Build uses: autowarefoundation/autoware-github-actions/colcon-build@v1 with: - rosdistro: galactic + rosdistro: ${{ matrix.rosdistro }} target-packages: ${{ steps.get-self-packages.outputs.self-packages }} - build-depends-repos: build_depends.repos + build-depends-repos: ${{ matrix.build-depends-repos }} diff --git a/.github/workflows/update-codeowners-from-packages.yaml b/.github/workflows/update-codeowners-from-packages.yaml new file mode 100644 index 0000000000000..055d2b02a799a --- /dev/null +++ b/.github/workflows/update-codeowners-from-packages.yaml @@ -0,0 +1,33 @@ +name: update-codeowners-from-packages + +on: + schedule: + - cron: 0 0 * * * + workflow_dispatch: + +jobs: + check-secret: + uses: autowarefoundation/autoware-github-actions/.github/workflows/check-secret.yaml@v1 + secrets: + secret: ${{ secrets.APP_ID }} + + update-codeowners-from-packages: + needs: check-secret + if: ${{ needs.check-secret.outputs.set == 'true' }} + runs-on: ubuntu-latest + steps: + - name: Generate token + id: generate-token + uses: tibdex/github-app-token@v1 + with: + app_id: ${{ secrets.APP_ID }} + private_key: ${{ secrets.PRIVATE_KEY }} + + - name: Run update-codeowners-from-packages + uses: autowarefoundation/autoware-github-actions/update-codeowners-from-packages@v1 + with: + token: ${{ steps.generate-token.outputs.token }} + pr-labels: | + bot + update-codeowners-from-packages + auto-merge-method: squash diff --git a/.yamllint.yaml b/.yamllint.yaml index 6228c70f02da9..2c7bd088e2648 100644 --- a/.yamllint.yaml +++ b/.yamllint.yaml @@ -1,7 +1,6 @@ extends: default ignore: | - .clang-tidy *.param.yaml rules: diff --git a/NOTICE b/NOTICE index 981e257f9bd21..0ac1d22befa35 100644 --- a/NOTICE +++ b/NOTICE @@ -6,3 +6,9 @@ The Autoware Foundation (https://www.autoware.org/). This product includes code developed by TIER IV. Copyright 2017 TIER IV, Inc. + +This product includes code developed by AutoCore. +Copyright 2022 AutoCore Technology (Nanjing) Co., Ltd. + +This product includes code developed by Leo Drive. +Copyright 2022 Leo Drive Teknoloji A.Ş. diff --git a/common/bag_time_manager_rviz_plugin/CMakeLists.txt b/common/bag_time_manager_rviz_plugin/CMakeLists.txt new file mode 100644 index 0000000000000..ccf9961fc55cc --- /dev/null +++ b/common/bag_time_manager_rviz_plugin/CMakeLists.txt @@ -0,0 +1,28 @@ +cmake_minimum_required(VERSION 3.14) +project(bag_time_manager_rviz_plugin) + +find_package(autoware_cmake REQUIRED) +autoware_package() + +find_package(Qt5 ${rviz_QT_VERSION} EXACT REQUIRED Core Widgets) +set(QT_LIBRARIES Qt5::Widgets) +set(CMAKE_AUTOMOC ON) +set(CMAKE_INCLUDE_CURRENT_DIR ON) +add_definitions(-DQT_NO_KEYWORDS) + +ament_auto_add_library(${PROJECT_NAME} SHARED + src/bag_time_manager_panel.cpp +) + +target_link_libraries(${PROJECT_NAME} + ${QT_LIBRARIES} +) + +# Export the plugin to be imported by rviz2 +pluginlib_export_plugin_description_file(rviz_common plugins/plugin_description.xml) + +ament_auto_package( + INSTALL_TO_SHARE + icons + plugins +) diff --git a/common/bag_time_manager_rviz_plugin/README.md b/common/bag_time_manager_rviz_plugin/README.md new file mode 100644 index 0000000000000..2fad6b2204bb9 --- /dev/null +++ b/common/bag_time_manager_rviz_plugin/README.md @@ -0,0 +1,26 @@ +# bag_time_manager_rviz_plugin + +## Purpose + +This plugin allows publishing and controlling the ros bag time. + +## Output + +tbd. + +## HowToUse + +1. Start rviz and select panels/Add new panel. + + ![select_panel](./images/select_panels.png) + +2. Select BagTimeManagerPanel and press OK. + + ![select_manager_plugin](./images/add_bag_time_manager_panel.png) + +3. See bag_time_manager_rviz_plugin/BagTimeManagerPanel is added. + + ![manager_plugin](./images/bag_time_manager_panel.png) + +- Pause/Resume: pause/resume the clock. +- ApplyRate: apply rate of the clock. diff --git a/common/bag_time_manager_rviz_plugin/icons/classes/BagTimeManagerPanel.png b/common/bag_time_manager_rviz_plugin/icons/classes/BagTimeManagerPanel.png new file mode 100644 index 0000000000000..9431c2d422363 Binary files /dev/null and b/common/bag_time_manager_rviz_plugin/icons/classes/BagTimeManagerPanel.png differ diff --git a/common/bag_time_manager_rviz_plugin/images/add_bag_time_manager_panel.png b/common/bag_time_manager_rviz_plugin/images/add_bag_time_manager_panel.png new file mode 100644 index 0000000000000..8078c0ccbf34c Binary files /dev/null and b/common/bag_time_manager_rviz_plugin/images/add_bag_time_manager_panel.png differ diff --git a/common/bag_time_manager_rviz_plugin/images/bag_time_manager_panel.png b/common/bag_time_manager_rviz_plugin/images/bag_time_manager_panel.png new file mode 100644 index 0000000000000..47ec05d5539c8 Binary files /dev/null and b/common/bag_time_manager_rviz_plugin/images/bag_time_manager_panel.png differ diff --git a/common/bag_time_manager_rviz_plugin/images/select_panels.png b/common/bag_time_manager_rviz_plugin/images/select_panels.png new file mode 100644 index 0000000000000..a691602c42c3c Binary files /dev/null and b/common/bag_time_manager_rviz_plugin/images/select_panels.png differ diff --git a/common/bag_time_manager_rviz_plugin/package.xml b/common/bag_time_manager_rviz_plugin/package.xml new file mode 100644 index 0000000000000..b12c24b728cde --- /dev/null +++ b/common/bag_time_manager_rviz_plugin/package.xml @@ -0,0 +1,28 @@ + + + + bag_time_manager_rviz_plugin + 0.0.1 + Rviz plugin to publish and control the ros bag + Taiki Tanaka + Apache License 2.0 + + ament_cmake_auto + + autoware_cmake + libqt5-core + libqt5-gui + libqt5-widgets + qtbase5-dev + rclcpp + rosbag2_interfaces + rviz_common + + ament_lint_auto + autoware_lint_common + + + ament_cmake + + + diff --git a/common/bag_time_manager_rviz_plugin/plugins/plugin_description.xml b/common/bag_time_manager_rviz_plugin/plugins/plugin_description.xml new file mode 100644 index 0000000000000..2a3bbf667b715 --- /dev/null +++ b/common/bag_time_manager_rviz_plugin/plugins/plugin_description.xml @@ -0,0 +1,9 @@ + + + + Panel that publishes a service to modify its speed. + + + diff --git a/common/bag_time_manager_rviz_plugin/src/bag_time_manager_panel.cpp b/common/bag_time_manager_rviz_plugin/src/bag_time_manager_panel.cpp new file mode 100644 index 0000000000000..dde3f10e9b554 --- /dev/null +++ b/common/bag_time_manager_rviz_plugin/src/bag_time_manager_panel.cpp @@ -0,0 +1,117 @@ +// +// Copyright 2022 Tier IV, Inc. All rights reserved. +// +// 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 "bag_time_manager_panel.hpp" + +#include +#include +#include +#include + +namespace rviz_plugins +{ +BagTimeManagerPanel::BagTimeManagerPanel(QWidget * parent) : rviz_common::Panel(parent) +{ + // pause / resume + { + pause_button_ = new QPushButton("Pause"); + pause_button_->setToolTip("Pause/Resume ROS time."); + pause_button_->setStyleSheet("background-color: #00FF00;"); + pause_button_->setCheckable(true); + } + + // apply + { + apply_rate_button_ = new QPushButton("ApplyRate"); + apply_rate_button_->setToolTip("control ROS time rate."); + } + + // combo + { + rate_label_ = new QLabel("Rate:"); + rate_label_->setAlignment(Qt::AlignCenter); + rate_combo_ = new QComboBox(); + rate_combo_->addItems({"0.01", "0.1", "0.5", "1.0", "2.0", "5.0", "10.0"}); + rate_combo_->setCurrentText(QString("1.0")); + time_label_ = new QLabel("X real time "); + rate_label_->setAlignment(Qt::AlignCenter); + } + + auto * layout = new QHBoxLayout(); + layout->addWidget(pause_button_); + layout->addWidget(apply_rate_button_); + layout->addWidget(rate_label_); + layout->addWidget(rate_combo_); + layout->addWidget(time_label_); + setLayout(layout); + + connect(pause_button_, SIGNAL(clicked()), this, SLOT(onPauseClicked())); + connect(apply_rate_button_, SIGNAL(clicked()), this, SLOT(onApplyRateClicked())); + connect(rate_combo_, SIGNAL(currentIndexChanged(int)), this, SLOT(onRateChanged())); +} + +void BagTimeManagerPanel::onInitialize() +{ + raw_node_ = this->getDisplayContext()->getRosNodeAbstraction().lock()->get_raw_node(); + client_pause_ = + raw_node_->create_client("/rosbag2_player/pause", rmw_qos_profile_services_default); + client_resume_ = + raw_node_->create_client("/rosbag2_player/resume", rmw_qos_profile_services_default); + client_set_rate_ = + raw_node_->create_client("/rosbag2_player/set_rate", rmw_qos_profile_services_default); +} + +void BagTimeManagerPanel::onPauseClicked() +{ + if (current_state_ == STATE::PAUSE) { + // do resume + current_state_ = STATE::RESUME; + pause_button_->setText(QString::fromStdString("Resume")); + // green + pause_button_->setStyleSheet("background-color: #00FF00;"); + auto req = std::make_shared(); + client_resume_->async_send_request( + req, [this]([[maybe_unused]] rclcpp::Client::SharedFuture result) {}); + } else { + // do pause + current_state_ = STATE::PAUSE; + pause_button_->setText(QString::fromStdString("Pause")); + // red + pause_button_->setStyleSheet("background-color: #FF0000;"); + auto req = std::make_shared(); + client_pause_->async_send_request( + req, [this]([[maybe_unused]] rclcpp::Client::SharedFuture result) {}); + } +} + +void BagTimeManagerPanel::onApplyRateClicked() +{ + auto request = std::make_shared(); + request->rate = std::stod(rate_combo_->currentText().toStdString()); + client_set_rate_->async_send_request( + request, [this, request](rclcpp::Client::SharedFuture result) { + const auto & response = result.get(); + if (response->success) { + RCLCPP_INFO(raw_node_->get_logger(), "set ros bag rate %f x real time", request->rate); + } else { + RCLCPP_WARN(raw_node_->get_logger(), "service failed"); + } + }); +} +} // namespace rviz_plugins + +#include +PLUGINLIB_EXPORT_CLASS(rviz_plugins::BagTimeManagerPanel, rviz_common::Panel) diff --git a/common/bag_time_manager_rviz_plugin/src/bag_time_manager_panel.hpp b/common/bag_time_manager_rviz_plugin/src/bag_time_manager_panel.hpp new file mode 100644 index 0000000000000..2cc4b98a6dabf --- /dev/null +++ b/common/bag_time_manager_rviz_plugin/src/bag_time_manager_panel.hpp @@ -0,0 +1,72 @@ +// +// Copyright 2022 Tier IV, Inc. All rights reserved. +// +// 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 BAG_TIME_MANAGER_PANEL_HPP_ +#define BAG_TIME_MANAGER_PANEL_HPP_ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace rviz_plugins +{ +using rosbag2_interfaces::srv::Pause; +using rosbag2_interfaces::srv::Resume; +using rosbag2_interfaces::srv::SetRate; +class BagTimeManagerPanel : public rviz_common::Panel +{ + Q_OBJECT +public: + explicit BagTimeManagerPanel(QWidget * parent = nullptr); + void onInitialize() override; + +protected Q_SLOTS: + /// @brief callback for when the publishing rate is changed + void onRateChanged() {} + /// @brief callback for when the step button is clicked + void onPauseClicked(); + void onApplyRateClicked(); + +protected: + // ROS + rclcpp::Node::SharedPtr raw_node_; + rclcpp::Client::SharedPtr client_pause_; + rclcpp::Client::SharedPtr client_resume_; + rclcpp::Client::SharedPtr client_set_rate_; + + // GUI + QPushButton * pause_button_; + QPushButton * apply_rate_button_; + QLabel * rate_label_; + QLabel * time_label_; + QComboBox * rate_combo_; + +private: + enum STATE { PAUSE, RESUME }; + STATE current_state_{RESUME}; +}; + +} // namespace rviz_plugins + +#endif // BAG_TIME_MANAGER_PANEL_HPP_ diff --git a/common/goal_distance_calculator/include/goal_distance_calculator/goal_distance_calculator.hpp b/common/goal_distance_calculator/include/goal_distance_calculator/goal_distance_calculator.hpp index 2f59543e2442c..b4a98d90c74e4 100644 --- a/common/goal_distance_calculator/include/goal_distance_calculator/goal_distance_calculator.hpp +++ b/common/goal_distance_calculator/include/goal_distance_calculator/goal_distance_calculator.hpp @@ -45,7 +45,7 @@ struct Output class GoalDistanceCalculator { public: - Output update(const Input & input); + static Output update(const Input & input); void setParam(const Param & param) { param_ = param; } diff --git a/common/goal_distance_calculator/include/goal_distance_calculator/goal_distance_calculator_node.hpp b/common/goal_distance_calculator/include/goal_distance_calculator/goal_distance_calculator_node.hpp index 10aba5872b66a..50051d928b6c1 100644 --- a/common/goal_distance_calculator/include/goal_distance_calculator/goal_distance_calculator_node.hpp +++ b/common/goal_distance_calculator/include/goal_distance_calculator/goal_distance_calculator_node.hpp @@ -33,8 +33,8 @@ namespace goal_distance_calculator { struct NodeParam { - double update_rate; - bool oneshot; + double update_rate{0.0}; + bool oneshot{false}; }; class GoalDistanceCalculatorNode : public rclcpp::Node diff --git a/common/interpolation/CMakeLists.txt b/common/interpolation/CMakeLists.txt index 7afed6fe57c41..cc91bed012432 100644 --- a/common/interpolation/CMakeLists.txt +++ b/common/interpolation/CMakeLists.txt @@ -8,6 +8,7 @@ ament_auto_add_library(interpolation SHARED src/linear_interpolation.cpp src/spline_interpolation.cpp src/spline_interpolation_points_2d.cpp + src/spherical_linear_interpolation.cpp ) if(BUILD_TESTING) diff --git a/common/interpolation/README.md b/common/interpolation/README.md index a56af1e864e32..5801c06dcb717 100644 --- a/common/interpolation/README.md +++ b/common/interpolation/README.md @@ -12,7 +12,7 @@ Then it calculates interpolated values on y-axis for `query_keys` on x-axis. ## Spline Interpolation -`slerp(base_keys, base_values, query_keys)` (for vector interpolation) applies spline regression to each two continuous points whose x values are`base_keys` and whose y values are `base_values`. +`spline(base_keys, base_values, query_keys)` (for vector interpolation) applies spline regression to each two continuous points whose x values are`base_keys` and whose y values are `base_values`. Then it calculates interpolated values on y-axis for `query_keys` on x-axis. ### Evaluation of calculation cost diff --git a/common/interpolation/include/interpolation/interpolation_utils.hpp b/common/interpolation/include/interpolation/interpolation_utils.hpp index d170007685bbf..ed381a3108410 100644 --- a/common/interpolation/include/interpolation/interpolation_utils.hpp +++ b/common/interpolation/include/interpolation/interpolation_utils.hpp @@ -76,8 +76,9 @@ inline void validateKeys( } } -inline void validateKeysAndValues( - const std::vector & base_keys, const std::vector & base_values) +template +void validateKeysAndValues( + const std::vector & base_keys, const std::vector & base_values) { // when vectors are empty if (base_keys.empty() || base_values.empty()) { diff --git a/common/interpolation/include/interpolation/spherical_linear_interpolation.hpp b/common/interpolation/include/interpolation/spherical_linear_interpolation.hpp new file mode 100644 index 0000000000000..516b3ab09e8b7 --- /dev/null +++ b/common/interpolation/include/interpolation/spherical_linear_interpolation.hpp @@ -0,0 +1,44 @@ +// Copyright 2022 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 INTERPOLATION__SPHERICAL_LINEAR_INTERPOLATION_HPP_ +#define INTERPOLATION__SPHERICAL_LINEAR_INTERPOLATION_HPP_ + +#include "interpolation/interpolation_utils.hpp" + +#include + +#include + +#ifdef ROS_DISTRO_GALACTIC +#include +#else +#include +#endif + +#include + +namespace interpolation +{ +geometry_msgs::msg::Quaternion slerp( + const geometry_msgs::msg::Quaternion & src_quat, const geometry_msgs::msg::Quaternion & dst_quat, + const double ratio); + +std::vector slerp( + const std::vector & base_keys, + const std::vector & base_values, + const std::vector & query_keys); +} // namespace interpolation + +#endif // INTERPOLATION__SPHERICAL_LINEAR_INTERPOLATION_HPP_ diff --git a/common/interpolation/include/interpolation/spline_interpolation.hpp b/common/interpolation/include/interpolation/spline_interpolation.hpp index c56498249e91f..9640bcd6c5120 100644 --- a/common/interpolation/include/interpolation/spline_interpolation.hpp +++ b/common/interpolation/include/interpolation/spline_interpolation.hpp @@ -46,10 +46,10 @@ struct MultiSplineCoef }; // static spline interpolation functions -std::vector slerp( +std::vector spline( const std::vector & base_keys, const std::vector & base_values, const std::vector & query_keys); -std::vector slerpByAkima( +std::vector splineByAkima( const std::vector & base_keys, const std::vector & base_values, const std::vector & query_keys); } // namespace interpolation diff --git a/common/interpolation/include/interpolation/spline_interpolation_points_2d.hpp b/common/interpolation/include/interpolation/spline_interpolation_points_2d.hpp index c1f08a6d937ae..d44d16d88dd04 100644 --- a/common/interpolation/include/interpolation/spline_interpolation_points_2d.hpp +++ b/common/interpolation/include/interpolation/spline_interpolation_points_2d.hpp @@ -21,8 +21,12 @@ namespace interpolation { +std::array, 3> slerp2dFromXY( + const std::vector & base_keys, const std::vector & base_x_values, + const std::vector & base_y_values, const std::vector & query_keys); + template -std::vector slerpYawFromPoints(const std::vector & points); +std::vector splineYawFromPoints(const std::vector & points); } // namespace interpolation // non-static points spline interpolation @@ -66,8 +70,8 @@ class SplineInterpolationPoints2d private: void calcSplineCoefficientsInner(const std::vector & points); - SplineInterpolation slerp_x_; - SplineInterpolation slerp_y_; + SplineInterpolation spline_x_; + SplineInterpolation spline_y_; std::vector base_s_vec_; }; diff --git a/common/interpolation/include/interpolation/zero_order_hold.hpp b/common/interpolation/include/interpolation/zero_order_hold.hpp index 7de6743ed9d16..e48da814c5740 100644 --- a/common/interpolation/include/interpolation/zero_order_hold.hpp +++ b/common/interpolation/include/interpolation/zero_order_hold.hpp @@ -28,23 +28,7 @@ std::vector zero_order_hold( { // throw exception for invalid arguments interpolation_utils::validateKeys(base_keys, query_keys); - - // when vectors are empty - if (base_keys.empty() || base_values.empty()) { - throw std::invalid_argument("Points is empty."); - } - - // when size of vectors are less than 2 - if (base_keys.size() < 2 || base_values.size() < 2) { - throw std::invalid_argument( - "The size of points is less than 2. base_keys.size() = " + std::to_string(base_keys.size()) + - ", base_values.size() = " + std::to_string(base_values.size())); - } - - // when sizes of indices and values are not same - if (base_keys.size() != base_values.size()) { - throw std::invalid_argument("The size of base_keys and base_values are not the same."); - } + interpolation_utils::validateKeysAndValues(base_keys, base_values); std::vector query_values; size_t closest_segment_idx = 0; diff --git a/common/interpolation/src/spherical_linear_interpolation.cpp b/common/interpolation/src/spherical_linear_interpolation.cpp new file mode 100644 index 0000000000000..014e9011e2a61 --- /dev/null +++ b/common/interpolation/src/spherical_linear_interpolation.cpp @@ -0,0 +1,60 @@ +// Copyright 2022 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 "interpolation/spherical_linear_interpolation.hpp" + +namespace interpolation +{ +geometry_msgs::msg::Quaternion slerp( + const geometry_msgs::msg::Quaternion & src_quat, const geometry_msgs::msg::Quaternion & dst_quat, + const double ratio) +{ + tf2::Quaternion src_tf; + tf2::Quaternion dst_tf; + tf2::fromMsg(src_quat, src_tf); + tf2::fromMsg(dst_quat, dst_tf); + const auto interpolated_quat = tf2::slerp(src_tf, dst_tf, ratio); + return tf2::toMsg(interpolated_quat); +} + +std::vector slerp( + const std::vector & base_keys, + const std::vector & base_values, + const std::vector & query_keys) +{ + // throw exception for invalid arguments + interpolation_utils::validateKeys(base_keys, query_keys); + interpolation_utils::validateKeysAndValues(base_keys, base_values); + + // calculate linear interpolation + std::vector query_values; + size_t key_index = 0; + for (const auto query_key : query_keys) { + while (base_keys.at(key_index + 1) < query_key) { + ++key_index; + } + + const auto src_quat = base_values.at(key_index); + const auto dst_quat = base_values.at(key_index + 1); + const double ratio = (query_key - base_keys.at(key_index)) / + (base_keys.at(key_index + 1) - base_keys.at(key_index)); + + const auto interpolated_quat = slerp(src_quat, dst_quat, ratio); + query_values.push_back(interpolated_quat); + } + + return query_values; +} + +} // namespace interpolation diff --git a/common/interpolation/src/spline_interpolation.cpp b/common/interpolation/src/spline_interpolation.cpp index ebf7c1ddb18bd..cf00452f1d850 100644 --- a/common/interpolation/src/spline_interpolation.cpp +++ b/common/interpolation/src/spline_interpolation.cpp @@ -81,7 +81,7 @@ inline std::vector solveTridiagonalMatrixAlgorithm(const TDMACoef & tdma namespace interpolation { -std::vector slerp( +std::vector spline( const std::vector & base_keys, const std::vector & base_values, const std::vector & query_keys) { @@ -94,7 +94,7 @@ std::vector slerp( return interpolator.getSplineInterpolatedValues(query_keys); } -std::vector slerpByAkima( +std::vector splineByAkima( const std::vector & base_keys, const std::vector & base_values, const std::vector & query_keys) { diff --git a/common/interpolation/src/spline_interpolation_points_2d.cpp b/common/interpolation/src/spline_interpolation_points_2d.cpp index 2a34e53fc884e..71e2629044f11 100644 --- a/common/interpolation/src/spline_interpolation_points_2d.cpp +++ b/common/interpolation/src/spline_interpolation_points_2d.cpp @@ -68,8 +68,31 @@ std::array, 3> getBaseValues( namespace interpolation { + +std::array, 3> slerp2dFromXY( + const std::vector & base_keys, const std::vector & base_x_values, + const std::vector & base_y_values, const std::vector & query_keys) +{ + SplineInterpolation interpolator_x, interpolator_y; + std::vector yaw_vec; + + // calculate spline coefficients + interpolator_x.calcSplineCoefficients(base_keys, base_x_values); + interpolator_y.calcSplineCoefficients(base_keys, base_y_values); + const auto diff_x = interpolator_x.getSplineInterpolatedDiffValues(query_keys); + const auto diff_y = interpolator_y.getSplineInterpolatedDiffValues(query_keys); + for (size_t i = 0; i < diff_x.size(); i++) { + double yaw = std::atan2(diff_y[i], diff_x[i]); + yaw_vec.push_back(yaw); + } + // interpolate base_keys at query_keys + return { + interpolator_x.getSplineInterpolatedValues(query_keys), + interpolator_y.getSplineInterpolatedValues(query_keys), yaw_vec}; +} + template -std::vector slerpYawFromPoints(const std::vector & points) +std::vector splineYawFromPoints(const std::vector & points) { SplineInterpolationPoints2d interpolator; @@ -84,8 +107,9 @@ std::vector slerpYawFromPoints(const std::vector & points) } return yaw_vec; } -template std::vector slerpYawFromPoints( +template std::vector splineYawFromPoints( const std::vector & points); + } // namespace interpolation geometry_msgs::msg::Point SplineInterpolationPoints2d::getSplineInterpolatedPoint( @@ -103,8 +127,8 @@ geometry_msgs::msg::Point SplineInterpolationPoints2d::getSplineInterpolatedPoin whole_s = base_s_vec_.back(); } - const double x = slerp_x_.getSplineInterpolatedValues({whole_s}).at(0); - const double y = slerp_y_.getSplineInterpolatedValues({whole_s}).at(0); + const double x = spline_x_.getSplineInterpolatedValues({whole_s}).at(0); + const double y = spline_y_.getSplineInterpolatedValues({whole_s}).at(0); geometry_msgs::msg::Point geom_point; geom_point.x = x; @@ -126,8 +150,8 @@ double SplineInterpolationPoints2d::getSplineInterpolatedYaw(const size_t idx, c whole_s = base_s_vec_.back(); } - const double diff_x = slerp_x_.getSplineInterpolatedDiffValues({whole_s}).at(0); - const double diff_y = slerp_y_.getSplineInterpolatedDiffValues({whole_s}).at(0); + const double diff_x = spline_x_.getSplineInterpolatedDiffValues({whole_s}).at(0); + const double diff_y = spline_y_.getSplineInterpolatedDiffValues({whole_s}).at(0); return std::atan2(diff_y, diff_x); } @@ -150,6 +174,6 @@ void SplineInterpolationPoints2d::calcSplineCoefficientsInner( const auto & base_y_vec = base.at(2); // calculate spline coefficients - slerp_x_.calcSplineCoefficients(base_s_vec_, base_x_vec); - slerp_y_.calcSplineCoefficients(base_s_vec_, base_y_vec); + spline_x_.calcSplineCoefficients(base_s_vec_, base_x_vec); + spline_y_.calcSplineCoefficients(base_s_vec_, base_y_vec); } diff --git a/common/interpolation/test/src/test_spherical_linear_interpolation.cpp b/common/interpolation/test/src/test_spherical_linear_interpolation.cpp new file mode 100644 index 0000000000000..0bb9ba8ef2ce8 --- /dev/null +++ b/common/interpolation/test/src/test_spherical_linear_interpolation.cpp @@ -0,0 +1,137 @@ +// Copyright 2021 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 "interpolation/spherical_linear_interpolation.hpp" + +#include + +#include +#include + +constexpr double epsilon = 1e-6; + +namespace +{ +inline geometry_msgs::msg::Quaternion createQuaternionFromRPY( + const double roll, const double pitch, const double yaw) +{ + tf2::Quaternion q; + q.setRPY(roll, pitch, yaw); + return tf2::toMsg(q); +} +} // namespace + +TEST(slerp, spline_scalar) +{ + using interpolation::slerp; + + // Same value + { + const double src_yaw = 0.0; + const double dst_yaw = 0.0; + const auto src_quat = createQuaternionFromRPY(0.0, 0.0, src_yaw); + const auto dst_quat = createQuaternionFromRPY(0.0, 0.0, dst_yaw); + + const auto ans_quat = createQuaternionFromRPY(0.0, 0.0, 0.0); + + for (double ratio = -2.0; ratio < 2.0 + epsilon; ratio += 0.1) { + const auto interpolated_quat = slerp(src_quat, dst_quat, ratio); + EXPECT_NEAR(ans_quat.x, interpolated_quat.x, epsilon); + EXPECT_NEAR(ans_quat.y, interpolated_quat.y, epsilon); + EXPECT_NEAR(ans_quat.z, interpolated_quat.z, epsilon); + EXPECT_NEAR(ans_quat.w, interpolated_quat.w, epsilon); + } + } + + // Random Value + { + const double src_yaw = 0.0; + const double dst_yaw = M_PI; + const auto src_quat = createQuaternionFromRPY(0.0, 0.0, src_yaw); + const auto dst_quat = createQuaternionFromRPY(0.0, 0.0, dst_yaw); + + for (double ratio = -2.0; ratio < 2.0 + epsilon; ratio += 0.1) { + const auto interpolated_quat = slerp(src_quat, dst_quat, ratio); + + const double ans_yaw = M_PI * ratio; + tf2::Quaternion ans; + ans.setRPY(0, 0, ans_yaw); + const geometry_msgs::msg::Quaternion ans_quat = tf2::toMsg(ans); + + EXPECT_NEAR(ans_quat.x, interpolated_quat.x, epsilon); + EXPECT_NEAR(ans_quat.y, interpolated_quat.y, epsilon); + EXPECT_NEAR(ans_quat.z, interpolated_quat.z, epsilon); + EXPECT_NEAR(ans_quat.w, interpolated_quat.w, epsilon); + } + } +} + +TEST(slerp, spline_vector) +{ + using interpolation::slerp; + + // query keys are same as base keys + { + const std::vector base_keys{0.0, 1.0, 2.0, 3.0, 4.0}; + std::vector base_values; + for (size_t i = 0; i < 5; ++i) { + const auto quat = createQuaternionFromRPY(0.0, 0.0, i * M_PI / 5.0); + base_values.push_back(quat); + } + const std::vector query_keys = base_keys; + const auto ans = base_values; + + const auto results = slerp(base_keys, base_values, query_keys); + + for (size_t i = 0; i < results.size(); ++i) { + const auto interpolated_quat = results.at(i); + const auto ans_quat = ans.at(i); + + EXPECT_NEAR(ans_quat.x, interpolated_quat.x, epsilon); + EXPECT_NEAR(ans_quat.y, interpolated_quat.y, epsilon); + EXPECT_NEAR(ans_quat.z, interpolated_quat.z, epsilon); + EXPECT_NEAR(ans_quat.w, interpolated_quat.w, epsilon); + } + } + + // random + { + const std::vector base_keys{0.0, 1.0, 2.0, 3.0, 4.0}; + std::vector base_values; + for (size_t i = 0; i < 5; ++i) { + const auto quat = createQuaternionFromRPY(0.0, 0.0, i * M_PI / 5.0); + base_values.push_back(quat); + } + const std::vector query_keys = {0.0, 0.1, 1.5, 2.6, 3.1, 3.8}; + std::vector ans(query_keys.size()); + ans.at(0) = createQuaternionFromRPY(0.0, 0.0, 0.0); + ans.at(1) = createQuaternionFromRPY(0.0, 0.0, 0.1 * M_PI / 5.0); + ans.at(2) = createQuaternionFromRPY(0.0, 0.0, 0.5 * M_PI / 5.0 + M_PI / 5.0); + ans.at(3) = createQuaternionFromRPY(0.0, 0.0, 0.6 * M_PI / 5.0 + 2.0 * M_PI / 5.0); + ans.at(4) = createQuaternionFromRPY(0.0, 0.0, 0.1 * M_PI / 5.0 + 3.0 * M_PI / 5.0); + ans.at(5) = createQuaternionFromRPY(0.0, 0.0, 0.8 * M_PI / 5.0 + 3.0 * M_PI / 5.0); + + const auto results = slerp(base_keys, base_values, query_keys); + + for (size_t i = 0; i < results.size(); ++i) { + const auto interpolated_quat = results.at(i); + const auto ans_quat = ans.at(i); + + EXPECT_NEAR(ans_quat.x, interpolated_quat.x, epsilon); + EXPECT_NEAR(ans_quat.y, interpolated_quat.y, epsilon); + EXPECT_NEAR(ans_quat.z, interpolated_quat.z, epsilon); + EXPECT_NEAR(ans_quat.w, interpolated_quat.w, epsilon); + } + } +} diff --git a/common/interpolation/test/src/test_spline_interpolation.cpp b/common/interpolation/test/src/test_spline_interpolation.cpp index 132b0100ff3b5..c2bb8eb177ba1 100644 --- a/common/interpolation/test/src/test_spline_interpolation.cpp +++ b/common/interpolation/test/src/test_spline_interpolation.cpp @@ -23,7 +23,7 @@ constexpr double epsilon = 1e-6; -TEST(spline_interpolation, slerp) +TEST(spline_interpolation, spline) { { // straight: query_keys is same as base_keys const std::vector base_keys{0.0, 1.0, 2.0, 3.0, 4.0}; @@ -31,7 +31,7 @@ TEST(spline_interpolation, slerp) const std::vector query_keys = base_keys; const std::vector ans = base_values; - const auto query_values = interpolation::slerp(base_keys, base_values, query_keys); + const auto query_values = interpolation::spline(base_keys, base_values, query_keys); for (size_t i = 0; i < query_values.size(); ++i) { EXPECT_NEAR(query_values.at(i), ans.at(i), epsilon); } @@ -43,7 +43,7 @@ TEST(spline_interpolation, slerp) const std::vector query_keys{0.0, 0.7, 1.9, 4.0}; const std::vector ans{0.0, 1.05, 2.85, 6.0}; - const auto query_values = interpolation::slerp(base_keys, base_values, query_keys); + const auto query_values = interpolation::spline(base_keys, base_values, query_keys); for (size_t i = 0; i < query_values.size(); ++i) { EXPECT_NEAR(query_values.at(i), ans.at(i), epsilon); } @@ -55,7 +55,7 @@ TEST(spline_interpolation, slerp) const std::vector query_keys = base_keys; const std::vector ans = base_values; - const auto query_values = interpolation::slerp(base_keys, base_values, query_keys); + const auto query_values = interpolation::spline(base_keys, base_values, query_keys); for (size_t i = 0; i < query_values.size(); ++i) { EXPECT_NEAR(query_values.at(i), ans.at(i), epsilon); } @@ -67,7 +67,7 @@ TEST(spline_interpolation, slerp) const std::vector query_keys{0.0, 8.0, 18.0}; const std::vector ans{-0.075611, 0.997242, 1.573258}; - const auto query_values = interpolation::slerp(base_keys, base_values, query_keys); + const auto query_values = interpolation::spline(base_keys, base_values, query_keys); for (size_t i = 0; i < query_values.size(); ++i) { EXPECT_NEAR(query_values.at(i), ans.at(i), epsilon); } @@ -79,7 +79,7 @@ TEST(spline_interpolation, slerp) const std::vector query_keys = base_keys; const std::vector ans = base_values; - const auto query_values = interpolation::slerp(base_keys, base_values, query_keys); + const auto query_values = interpolation::spline(base_keys, base_values, query_keys); for (size_t i = 0; i < query_values.size(); ++i) { EXPECT_NEAR(query_values.at(i), ans.at(i), epsilon); } @@ -91,7 +91,7 @@ TEST(spline_interpolation, slerp) const std::vector query_keys = base_keys; const std::vector ans = base_values; - const auto query_values = interpolation::slerp(base_keys, base_values, query_keys); + const auto query_values = interpolation::spline(base_keys, base_values, query_keys); for (size_t i = 0; i < query_values.size(); ++i) { EXPECT_NEAR(query_values.at(i), ans.at(i), epsilon); } @@ -103,7 +103,7 @@ TEST(spline_interpolation, slerp) const std::vector query_keys{-1.0, 0.0, 4.0}; const std::vector ans{-0.808769, -0.077539, 1.035096}; - const auto query_values = interpolation::slerp(base_keys, base_values, query_keys); + const auto query_values = interpolation::spline(base_keys, base_values, query_keys); for (size_t i = 0; i < query_values.size(); ++i) { EXPECT_NEAR(query_values.at(i), ans.at(i), epsilon); } @@ -115,14 +115,14 @@ TEST(spline_interpolation, slerp) const std::vector query_keys = {0.0, 1.0, 1.5, 2.0, 3.0, 4.0}; const std::vector ans = {0.0, 0.0, 137.591789, 0.1, 0.1, 0.1}; - const auto query_values = interpolation::slerp(base_keys, base_values, query_keys); + const auto query_values = interpolation::spline(base_keys, base_values, query_keys); for (size_t i = 0; i < query_values.size(); ++i) { EXPECT_NEAR(query_values.at(i), ans.at(i), epsilon); } } } -TEST(spline_interpolation, slerpByAkima) +TEST(spline_interpolation, splineByAkima) { { // straight: query_keys is same as base_keys const std::vector base_keys{0.0, 1.0, 2.0, 3.0, 4.0}; @@ -130,7 +130,7 @@ TEST(spline_interpolation, slerpByAkima) const std::vector query_keys = base_keys; const std::vector ans = base_values; - const auto query_values = interpolation::slerpByAkima(base_keys, base_values, query_keys); + const auto query_values = interpolation::splineByAkima(base_keys, base_values, query_keys); for (size_t i = 0; i < query_values.size(); ++i) { EXPECT_NEAR(query_values.at(i), ans.at(i), epsilon); } @@ -142,7 +142,7 @@ TEST(spline_interpolation, slerpByAkima) const std::vector query_keys{0.0, 0.7, 1.9, 4.0}; const std::vector ans{0.0, 1.05, 2.85, 6.0}; - const auto query_values = interpolation::slerpByAkima(base_keys, base_values, query_keys); + const auto query_values = interpolation::splineByAkima(base_keys, base_values, query_keys); for (size_t i = 0; i < query_values.size(); ++i) { EXPECT_NEAR(query_values.at(i), ans.at(i), epsilon); } @@ -154,7 +154,7 @@ TEST(spline_interpolation, slerpByAkima) const std::vector query_keys = base_keys; const std::vector ans = base_values; - const auto query_values = interpolation::slerpByAkima(base_keys, base_values, query_keys); + const auto query_values = interpolation::splineByAkima(base_keys, base_values, query_keys); for (size_t i = 0; i < query_values.size(); ++i) { EXPECT_NEAR(query_values.at(i), ans.at(i), epsilon); } @@ -166,7 +166,7 @@ TEST(spline_interpolation, slerpByAkima) const std::vector query_keys{0.0, 8.0, 18.0}; const std::vector ans{-0.0801, 1.110749, 1.4864}; - const auto query_values = interpolation::slerpByAkima(base_keys, base_values, query_keys); + const auto query_values = interpolation::splineByAkima(base_keys, base_values, query_keys); for (size_t i = 0; i < query_values.size(); ++i) { EXPECT_NEAR(query_values.at(i), ans.at(i), epsilon); } @@ -178,7 +178,7 @@ TEST(spline_interpolation, slerpByAkima) const std::vector query_keys = base_keys; const std::vector ans = base_values; - const auto query_values = interpolation::slerpByAkima(base_keys, base_values, query_keys); + const auto query_values = interpolation::splineByAkima(base_keys, base_values, query_keys); for (size_t i = 0; i < query_values.size(); ++i) { EXPECT_NEAR(query_values.at(i), ans.at(i), epsilon); } @@ -190,7 +190,7 @@ TEST(spline_interpolation, slerpByAkima) const std::vector query_keys = base_keys; const std::vector ans = base_values; - const auto query_values = interpolation::slerpByAkima(base_keys, base_values, query_keys); + const auto query_values = interpolation::splineByAkima(base_keys, base_values, query_keys); for (size_t i = 0; i < query_values.size(); ++i) { EXPECT_NEAR(query_values.at(i), ans.at(i), epsilon); } @@ -202,7 +202,7 @@ TEST(spline_interpolation, slerpByAkima) const std::vector query_keys{-1.0, 0.0, 4.0}; const std::vector ans{-0.8378, -0.0801, 0.927031}; - const auto query_values = interpolation::slerpByAkima(base_keys, base_values, query_keys); + const auto query_values = interpolation::splineByAkima(base_keys, base_values, query_keys); for (size_t i = 0; i < query_values.size(); ++i) { EXPECT_NEAR(query_values.at(i), ans.at(i), epsilon); } @@ -214,14 +214,14 @@ TEST(spline_interpolation, slerpByAkima) const std::vector query_keys = {0.0, 1.0, 1.5, 2.0, 3.0, 4.0}; const std::vector ans = {0.0, 0.0, 0.1, 0.1, 0.1, 0.1}; - const auto query_values = interpolation::slerpByAkima(base_keys, base_values, query_keys); + const auto query_values = interpolation::splineByAkima(base_keys, base_values, query_keys); for (size_t i = 0; i < query_values.size(); ++i) { EXPECT_NEAR(query_values.at(i), ans.at(i), epsilon); } } } -TEST(spline_interpolation, slerpYawFromPoints) +TEST(spline_interpolation, splineYawFromPoints) { using tier4_autoware_utils::createPoint; @@ -235,7 +235,7 @@ TEST(spline_interpolation, slerpYawFromPoints) const std::vector ans{0.9827937, 0.9827937, 0.9827937, 0.9827937, 0.9827937}; - const auto yaws = interpolation::slerpYawFromPoints(points); + const auto yaws = interpolation::splineYawFromPoints(points); for (size_t i = 0; i < yaws.size(); ++i) { EXPECT_NEAR(yaws.at(i), ans.at(i), epsilon); } @@ -251,7 +251,7 @@ TEST(spline_interpolation, slerpYawFromPoints) const std::vector ans{1.3593746, 0.9813541, 1.0419655, 0.8935115, 0.2932783}; - const auto yaws = interpolation::slerpYawFromPoints(points); + const auto yaws = interpolation::splineYawFromPoints(points); for (size_t i = 0; i < yaws.size(); ++i) { EXPECT_NEAR(yaws.at(i), ans.at(i), epsilon); } @@ -261,7 +261,7 @@ TEST(spline_interpolation, slerpYawFromPoints) std::vector points; points.push_back(createPoint(1.0, 0.0, 0.0)); - EXPECT_THROW(interpolation::slerpYawFromPoints(points), std::logic_error); + EXPECT_THROW(interpolation::splineYawFromPoints(points), std::logic_error); } { // straight: size of base_keys is 2 (edge case in the implementation) @@ -271,7 +271,7 @@ TEST(spline_interpolation, slerpYawFromPoints) const std::vector ans{0.9827937, 0.9827937}; - const auto yaws = interpolation::slerpYawFromPoints(points); + const auto yaws = interpolation::splineYawFromPoints(points); for (size_t i = 0; i < yaws.size(); ++i) { EXPECT_NEAR(yaws.at(i), ans.at(i), epsilon); } @@ -285,7 +285,7 @@ TEST(spline_interpolation, slerpYawFromPoints) const std::vector ans{0.9827937, 0.9827937, 0.9827937}; - const auto yaws = interpolation::slerpYawFromPoints(points); + const auto yaws = interpolation::splineYawFromPoints(points); for (size_t i = 0; i < yaws.size(); ++i) { EXPECT_NEAR(yaws.at(i), ans.at(i), epsilon); } diff --git a/common/kalman_filter/package.xml b/common/kalman_filter/package.xml index 6771e3c1a3238..84887dc4e5804 100644 --- a/common/kalman_filter/package.xml +++ b/common/kalman_filter/package.xml @@ -4,7 +4,7 @@ kalman_filter 0.1.0 The kalman filter package - Yukihiro Saito Horibe + Yukihiro Saito Apache License 2.0 Takamasa Horibe diff --git a/common/motion_common/include/motion_common/motion_common.hpp b/common/motion_common/include/motion_common/motion_common.hpp index e73cff3ed6e3f..2742b1c073bda 100644 --- a/common/motion_common/include/motion_common/motion_common.hpp +++ b/common/motion_common/include/motion_common/motion_common.hpp @@ -159,11 +159,11 @@ T interpolate(T a, T b, RealT t) } /// Spherical linear interpolation -MOTION_COMMON_PUBLIC Orientation slerp(const Orientation & a, const Orientation & b, const Real t); +MOTION_COMMON_PUBLIC Orientation spline(const Orientation & a, const Orientation & b, const Real t); /// Trajectory point interpolation template -Point interpolate(Point a, Point b, Real t, SlerpF slerp_fn) +Point interpolate(Point a, Point b, Real t, SlerpF spline_fn) { Point ret{rosidl_runtime_cpp::MessageInitialization::ALL}; { @@ -173,7 +173,7 @@ Point interpolate(Point a, Point b, Real t, SlerpF slerp_fn) } ret.pose.position.x = interpolate(a.pose.position.x, b.pose.position.x, t); ret.pose.position.y = interpolate(a.pose.position.y, b.pose.position.y, t); - ret.pose.orientation = slerp_fn(a.pose.orientation, b.pose.orientation, t); + ret.pose.orientation = spline_fn(a.pose.orientation, b.pose.orientation, t); ret.longitudinal_velocity_mps = interpolate(a.longitudinal_velocity_mps, b.longitudinal_velocity_mps, t); ret.lateral_velocity_mps = interpolate(a.lateral_velocity_mps, b.lateral_velocity_mps, t); @@ -191,7 +191,7 @@ MOTION_COMMON_PUBLIC Point interpolate(Point a, Point b, Real t); /// Sample a trajectory using interpolation; does not extrapolate template void sample( - const Trajectory & in, Trajectory & out, std::chrono::nanoseconds period, SlerpF slerp_fn) + const Trajectory & in, Trajectory & out, std::chrono::nanoseconds period, SlerpF spline_fn) { out.points.clear(); out.header = in.header; @@ -230,7 +230,7 @@ void sample( const auto dt_ = std::chrono::duration_cast>( ref_duration - from_message(curr_pt.time_from_start)); const auto t = dt_.count() / dt.count(); - out.points.push_back(interpolate(curr_pt, next_pt, t, slerp_fn)); + out.points.push_back(interpolate(curr_pt, next_pt, t, spline_fn)); } ref_duration += period; } diff --git a/common/motion_common/src/motion_common/motion_common.cpp b/common/motion_common/src/motion_common/motion_common.cpp index aaedabfd83b9a..7ad71096c53f8 100644 --- a/common/motion_common/src/motion_common/motion_common.cpp +++ b/common/motion_common/src/motion_common/motion_common.cpp @@ -153,7 +153,7 @@ Heading nlerp(Heading a, Heading b, Real t) } //////////////////////////////////////////////////////////////////////////////// -Orientation slerp(const Orientation & a, const Orientation & b, const Real t) +Orientation spline(const Orientation & a, const Orientation & b, const Real t) { tf2::Quaternion quat_a; tf2::Quaternion quat_b; @@ -163,12 +163,12 @@ Orientation slerp(const Orientation & a, const Orientation & b, const Real t) } //////////////////////////////////////////////////////////////////////////////// -Point interpolate(Point a, Point b, Real t) { return interpolate(a, b, t, slerp); } +Point interpolate(Point a, Point b, Real t) { return interpolate(a, b, t, spline); } //////////////////////////////////////////////////////////////////////////////// void sample(const Trajectory & in, Trajectory & out, std::chrono::nanoseconds period) { - sample(in, out, period, slerp); + sample(in, out, period, spline); } //////////////////////////////////////////////////////////////////////////////// diff --git a/common/motion_common/test/interpolation.cpp b/common/motion_common/test/interpolation.cpp index e59d9e0af9346..ae57e5ea76ba6 100644 --- a/common/motion_common/test/interpolation.cpp +++ b/common/motion_common/test/interpolation.cpp @@ -109,8 +109,8 @@ TEST(Interpolation, Slerp2d) }; // Plain check const auto test_case = [=](Orientation a, Orientation b, Real t, Orientation res, Real tol) { - using motion::motion_common::slerp; - const auto ret = slerp(a, b, t); + using motion::motion_common::spline; + const auto ret = spline(a, b, t); EXPECT_LT(std::fabs(to_angle(ret) - to_angle(res)), tol) << to_angle(ret) << ", " << to_angle(res); }; @@ -123,10 +123,10 @@ TEST(Interpolation, Slerp2d) const auto t_ = motion::motion_common::clamp(t, 0.0F, 1.0F); const auto th_t = th_a + (t_ * ab); const auto res_th = from_angle(th_t); - using motion::motion_common::slerp; + using motion::motion_common::spline; test_case(a, b, t, res_th, tol); if (HasFailure()) { - const auto ret = slerp(a, b, t); + const auto ret = spline(a, b, t); std::cout << "Angles: " << th_a << ", " << th_b << "; " << th_t << ", " << to_angle(ret) << "\n"; } diff --git a/common/motion_utils/CMakeLists.txt b/common/motion_utils/CMakeLists.txt index 59e43d2ec0347..941855eb62528 100644 --- a/common/motion_utils/CMakeLists.txt +++ b/common/motion_utils/CMakeLists.txt @@ -10,6 +10,7 @@ ament_auto_add_library(motion_utils SHARED src/motion_utils.cpp src/marker/marker_helper.cpp src/resample/resample.cpp + src/trajectory/interpolation.cpp src/vehicle/vehicle_state_checker.cpp ) diff --git a/common/motion_utils/include/motion_utils/trajectory/interpolation.hpp b/common/motion_utils/include/motion_utils/trajectory/interpolation.hpp new file mode 100644 index 0000000000000..b05a5334b7328 --- /dev/null +++ b/common/motion_utils/include/motion_utils/trajectory/interpolation.hpp @@ -0,0 +1,49 @@ +// Copyright 2022 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 MOTION_UTILS__TRAJECTORY__INTERPOLATION_HPP_ +#define MOTION_UTILS__TRAJECTORY__INTERPOLATION_HPP_ + +#include "tier4_autoware_utils/geometry/geometry.hpp" +#include "tier4_autoware_utils/math/constants.hpp" + +#include "autoware_auto_planning_msgs/msg/trajectory.hpp" + +#include + +#include +#include +#include +#include + +namespace motion_utils +{ +/** + * @brief An interpolation function that finds the closest interpolated point on the trajectory from + * the given pose + * @param trajectory input trajectory + * @param target_pose target_pose + * @param use_zero_order_for_twist flag to decide wether to use zero order hold interpolation for + * twist information + * @return resampled path(poses) + */ +autoware_auto_planning_msgs::msg::TrajectoryPoint calcInterpolatedPoint( + const autoware_auto_planning_msgs::msg::Trajectory & trajectory, + const geometry_msgs::msg::Pose & target_pose, const bool use_zero_order_hold_for_twist = false, + const double dist_threshold = std::numeric_limits::max(), + const double yaw_threshold = std::numeric_limits::max()); + +} // namespace motion_utils + +#endif // MOTION_UTILS__TRAJECTORY__INTERPOLATION_HPP_ diff --git a/common/motion_utils/include/motion_utils/trajectory/path_with_lane_id.hpp b/common/motion_utils/include/motion_utils/trajectory/path_with_lane_id.hpp index a10db47a377a2..a4a3842d1047d 100644 --- a/common/motion_utils/include/motion_utils/trajectory/path_with_lane_id.hpp +++ b/common/motion_utils/include/motion_utils/trajectory/path_with_lane_id.hpp @@ -20,13 +20,14 @@ #include +#include #include #include namespace motion_utils { inline boost::optional> getPathIndexRangeWithLaneId( - const autoware_auto_planning_msgs::msg::PathWithLaneId & path, const int64_t lane_id) + const autoware_auto_planning_msgs::msg::PathWithLaneId & path, const int64_t target_lane_id) { size_t start_idx = 0; // NOTE: to prevent from maybe-uninitialized error size_t end_idx = 0; // NOTE: to prevent from maybe-uninitialized error @@ -35,7 +36,7 @@ inline boost::optional> getPathIndexRangeWithLaneId( for (size_t i = 0; i < path.points.size(); ++i) { const auto & p = path.points.at(i); for (const auto & id : p.lane_ids) { - if (id == lane_id) { + if (id == target_lane_id) { if (!found_first_idx) { start_idx = i; found_first_idx = true; @@ -46,6 +47,10 @@ inline boost::optional> getPathIndexRangeWithLaneId( } if (found_first_idx) { + // NOTE: In order to fully cover lanes with target_lane_id, start_idx and end_idx are expanded. + start_idx = static_cast(std::max(0, static_cast(start_idx) - 1)); + end_idx = std::min(path.points.size() - 1, end_idx + 1); + return std::make_pair(start_idx, end_idx); } diff --git a/common/motion_utils/src/resample/resample.cpp b/common/motion_utils/src/resample/resample.cpp index affe832f6b221..7f050a4454ffe 100644 --- a/common/motion_utils/src/resample/resample.cpp +++ b/common/motion_utils/src/resample/resample.cpp @@ -66,13 +66,13 @@ std::vector resamplePath( const auto lerp = [&](const auto & input) { return interpolation::lerp(input_arclength, input, resampled_arclength); }; - const auto slerp = [&](const auto & input) { - return interpolation::slerp(input_arclength, input, resampled_arclength); + const auto spline = [&](const auto & input) { + return interpolation::spline(input_arclength, input, resampled_arclength); }; - const auto interpolated_x = use_lerp_for_xy ? lerp(x) : slerp(x); - const auto interpolated_y = use_lerp_for_xy ? lerp(y) : slerp(y); - const auto interpolated_z = use_lerp_for_z ? lerp(z) : slerp(z); + const auto interpolated_x = use_lerp_for_xy ? lerp(x) : spline(x); + const auto interpolated_y = use_lerp_for_xy ? lerp(y) : spline(y); + const auto interpolated_z = use_lerp_for_z ? lerp(z) : spline(z); std::vector resampled_points; resampled_points.resize(interpolated_x.size()); diff --git a/common/motion_utils/src/trajectory/interpolation.cpp b/common/motion_utils/src/trajectory/interpolation.cpp new file mode 100644 index 0000000000000..cd603676ddd78 --- /dev/null +++ b/common/motion_utils/src/trajectory/interpolation.cpp @@ -0,0 +1,92 @@ +// Copyright 2022 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 "motion_utils/trajectory/interpolation.hpp" + +#include "interpolation/linear_interpolation.hpp" +#include "interpolation/zero_order_hold.hpp" +#include "motion_utils/trajectory/trajectory.hpp" + +using autoware_auto_planning_msgs::msg::Trajectory; +using autoware_auto_planning_msgs::msg::TrajectoryPoint; + +namespace motion_utils +{ +TrajectoryPoint calcInterpolatedPoint( + const Trajectory & trajectory, const geometry_msgs::msg::Pose & target_pose, + const bool use_zero_order_hold_for_twist, const double dist_threshold, const double yaw_threshold) +{ + if (trajectory.points.empty()) { + TrajectoryPoint interpolated_point{}; + interpolated_point.pose = target_pose; + return interpolated_point; + } else if (trajectory.points.size() == 1) { + return trajectory.points.front(); + } + + const size_t segment_idx = motion_utils::findFirstNearestSegmentIndexWithSoftConstraints( + trajectory.points, target_pose, dist_threshold, yaw_threshold); + + // Calculate interpolation ratio + const auto & curr_pt = trajectory.points.at(segment_idx); + const auto & next_pt = trajectory.points.at(segment_idx + 1); + const auto v1 = tier4_autoware_utils::point2tfVector(curr_pt, next_pt); + const auto v2 = tier4_autoware_utils::point2tfVector(curr_pt, target_pose); + if (v1.length2() < 1e-3) { + return curr_pt; + } + + const double ratio = v1.dot(v2) / v1.length2(); + const double clamped_ratio = std::clamp(ratio, 0.0, 1.0); + + // Interpolate + TrajectoryPoint interpolated_point{}; + + // pose interpolation + interpolated_point.pose = + tier4_autoware_utils::calcInterpolatedPose(curr_pt, next_pt, clamped_ratio); + + // twist interpolation + if (use_zero_order_hold_for_twist) { + interpolated_point.longitudinal_velocity_mps = curr_pt.longitudinal_velocity_mps; + interpolated_point.lateral_velocity_mps = curr_pt.lateral_velocity_mps; + interpolated_point.acceleration_mps2 = curr_pt.acceleration_mps2; + } else { + interpolated_point.longitudinal_velocity_mps = interpolation::lerp( + curr_pt.longitudinal_velocity_mps, next_pt.longitudinal_velocity_mps, clamped_ratio); + interpolated_point.lateral_velocity_mps = interpolation::lerp( + curr_pt.lateral_velocity_mps, next_pt.lateral_velocity_mps, clamped_ratio); + interpolated_point.acceleration_mps2 = + interpolation::lerp(curr_pt.acceleration_mps2, next_pt.acceleration_mps2, clamped_ratio); + } + + // heading rate interpolation + interpolated_point.heading_rate_rps = + interpolation::lerp(curr_pt.heading_rate_rps, next_pt.heading_rate_rps, clamped_ratio); + + // wheel interpolation + interpolated_point.front_wheel_angle_rad = interpolation::lerp( + curr_pt.front_wheel_angle_rad, next_pt.front_wheel_angle_rad, clamped_ratio); + interpolated_point.rear_wheel_angle_rad = + interpolation::lerp(curr_pt.rear_wheel_angle_rad, next_pt.rear_wheel_angle_rad, clamped_ratio); + + // time interpolation + const double interpolated_time = interpolation::lerp( + rclcpp::Duration(curr_pt.time_from_start).seconds(), + rclcpp::Duration(next_pt.time_from_start).seconds(), clamped_ratio); + interpolated_point.time_from_start = rclcpp::Duration::from_seconds(interpolated_time); + + return interpolated_point; +} +} // namespace motion_utils diff --git a/common/motion_utils/test/src/trajectory/test_interpolation.cpp b/common/motion_utils/test/src/trajectory/test_interpolation.cpp new file mode 100644 index 0000000000000..f0e45179d05b6 --- /dev/null +++ b/common/motion_utils/test/src/trajectory/test_interpolation.cpp @@ -0,0 +1,309 @@ +// Copyright 2022 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 "motion_utils/trajectory/interpolation.hpp" +#include "motion_utils/trajectory/tmp_conversion.hpp" +#include "motion_utils/trajectory/trajectory.hpp" +#include "tier4_autoware_utils/geometry/boost_geometry.hpp" +#include "tier4_autoware_utils/math/unit_conversion.hpp" + +#include +#include +#include + +#include +#include + +namespace +{ +using autoware_auto_planning_msgs::msg::Trajectory; +using autoware_auto_planning_msgs::msg::TrajectoryPoint; +using tier4_autoware_utils::createPoint; +using tier4_autoware_utils::createQuaternionFromRPY; +using tier4_autoware_utils::transformPoint; + +constexpr double epsilon = 1e-6; + +geometry_msgs::msg::Pose createPose( + double x, double y, double z, double roll, double pitch, double yaw) +{ + geometry_msgs::msg::Pose p; + p.position = createPoint(x, y, z); + p.orientation = createQuaternionFromRPY(roll, pitch, yaw); + return p; +} + +TrajectoryPoint generateTestTrajectoryPoint( + const double x, const double y, const double z, const double theta = 0.0, + const double vel_lon = 0.0, const double vel_lat = 0.0, const double heading_rate = 0.0, + const double acc = 0.0) +{ + TrajectoryPoint p; + p.pose = createPose(x, y, z, 0.0, 0.0, theta); + p.longitudinal_velocity_mps = vel_lon; + p.lateral_velocity_mps = vel_lat; + p.heading_rate_rps = heading_rate; + p.acceleration_mps2 = acc; + return p; +} + +template +T generateTestTrajectory( + const size_t num_points, const double point_interval, const double vel_lon = 0.0, + const double vel_lat = 0.0, const double heading_rate_rps = 0.0, const double acc = 0.0, + const double init_theta = 0.0, const double delta_theta = 0.0) +{ + using Point = typename T::_points_type::value_type; + + T traj; + for (size_t i = 0; i < num_points; ++i) { + const double theta = init_theta + i * delta_theta; + const double x = i * point_interval * std::cos(theta); + const double y = i * point_interval * std::sin(theta); + + Point p; + p.pose = createPose(x, y, 0.0, 0.0, 0.0, theta); + p.longitudinal_velocity_mps = vel_lon; + p.lateral_velocity_mps = vel_lat; + p.heading_rate_rps = heading_rate_rps; + p.acceleration_mps2 = acc; + traj.points.push_back(p); + } + return traj; +} +} // namespace + +TEST(Interpolation, interpolate_path_for_trajectory) +{ + using motion_utils::calcInterpolatedPoint; + + { + autoware_auto_planning_msgs::msg::Trajectory traj; + traj.points.resize(10); + for (size_t i = 0; i < 10; ++i) { + traj.points.at(i) = + generateTestTrajectoryPoint(i * 1.0, 0.0, 0.0, 0.0, i * 1.0, i * 0.5, i * 0.1, i * 0.05); + } + + // Same points as the trajectory point + { + const auto target_pose = createPose(3.0, 0.0, 0.0, 0.0, 0.0, 0.0); + const auto result = calcInterpolatedPoint(traj, target_pose); + + EXPECT_NEAR(result.pose.position.x, 3.0, epsilon); + EXPECT_NEAR(result.pose.position.y, 0.0, epsilon); + EXPECT_NEAR(result.pose.position.z, 0.0, epsilon); + EXPECT_NEAR(result.pose.orientation.x, 0.0, epsilon); + EXPECT_NEAR(result.pose.orientation.y, 0.0, epsilon); + EXPECT_NEAR(result.pose.orientation.z, 0.0, epsilon); + EXPECT_NEAR(result.pose.orientation.w, 1.0, epsilon); + EXPECT_NEAR(result.longitudinal_velocity_mps, 3.0, epsilon); + EXPECT_NEAR(result.lateral_velocity_mps, 1.5, epsilon); + EXPECT_NEAR(result.heading_rate_rps, 0.3, epsilon); + EXPECT_NEAR(result.acceleration_mps2, 0.15, epsilon); + EXPECT_NEAR(result.front_wheel_angle_rad, 0.0, epsilon); + EXPECT_NEAR(result.rear_wheel_angle_rad, 0.0, epsilon); + } + + // Random Point + { + const auto target_pose = createPose(2.5, 5.0, 0.0, 0.0, 0.0, 0.0); + const auto result = calcInterpolatedPoint(traj, target_pose); + + EXPECT_NEAR(result.pose.position.x, 2.5, epsilon); + EXPECT_NEAR(result.pose.position.y, 0.0, epsilon); + EXPECT_NEAR(result.pose.position.z, 0.0, epsilon); + EXPECT_NEAR(result.pose.orientation.x, 0.0, epsilon); + EXPECT_NEAR(result.pose.orientation.y, 0.0, epsilon); + EXPECT_NEAR(result.pose.orientation.z, 0.0, epsilon); + EXPECT_NEAR(result.pose.orientation.w, 1.0, epsilon); + EXPECT_NEAR(result.longitudinal_velocity_mps, 2.5, epsilon); + EXPECT_NEAR(result.lateral_velocity_mps, 1.25, epsilon); + EXPECT_NEAR(result.heading_rate_rps, 0.25, epsilon); + EXPECT_NEAR(result.acceleration_mps2, 0.125, epsilon); + EXPECT_NEAR(result.front_wheel_angle_rad, 0.0, epsilon); + EXPECT_NEAR(result.rear_wheel_angle_rad, 0.0, epsilon); + } + + // Random Point with zero order hold + { + const auto target_pose = createPose(2.5, 5.0, 0.0, 0.0, 0.0, 0.0); + const auto result = calcInterpolatedPoint(traj, target_pose, true); + + EXPECT_NEAR(result.pose.position.x, 2.5, epsilon); + EXPECT_NEAR(result.pose.position.y, 0.0, epsilon); + EXPECT_NEAR(result.pose.position.z, 0.0, epsilon); + EXPECT_NEAR(result.pose.orientation.x, 0.0, epsilon); + EXPECT_NEAR(result.pose.orientation.y, 0.0, epsilon); + EXPECT_NEAR(result.pose.orientation.z, 0.0, epsilon); + EXPECT_NEAR(result.pose.orientation.w, 1.0, epsilon); + EXPECT_NEAR(result.longitudinal_velocity_mps, 2.0, epsilon); + EXPECT_NEAR(result.lateral_velocity_mps, 1.0, epsilon); + EXPECT_NEAR(result.heading_rate_rps, 0.25, epsilon); + EXPECT_NEAR(result.acceleration_mps2, 0.1, epsilon); + EXPECT_NEAR(result.front_wheel_angle_rad, 0.0, epsilon); + EXPECT_NEAR(result.rear_wheel_angle_rad, 0.0, epsilon); + } + + // Initial Point + { + const auto target_pose = createPose(0.0, 0.0, 0.0, 0.0, 0.0, 0.0); + const auto result = calcInterpolatedPoint(traj, target_pose); + + EXPECT_NEAR(result.pose.position.x, 0.0, epsilon); + EXPECT_NEAR(result.pose.position.y, 0.0, epsilon); + EXPECT_NEAR(result.pose.position.z, 0.0, epsilon); + EXPECT_NEAR(result.pose.orientation.x, 0.0, epsilon); + EXPECT_NEAR(result.pose.orientation.y, 0.0, epsilon); + EXPECT_NEAR(result.pose.orientation.z, 0.0, epsilon); + EXPECT_NEAR(result.pose.orientation.w, 1.0, epsilon); + EXPECT_NEAR(result.longitudinal_velocity_mps, 0.0, epsilon); + EXPECT_NEAR(result.lateral_velocity_mps, 0.0, epsilon); + EXPECT_NEAR(result.heading_rate_rps, 0.0, epsilon); + EXPECT_NEAR(result.acceleration_mps2, 0.0, epsilon); + EXPECT_NEAR(result.front_wheel_angle_rad, 0.0, epsilon); + EXPECT_NEAR(result.rear_wheel_angle_rad, 0.0, epsilon); + } + + // Terminal Point + { + const auto target_pose = createPose(9.0, 0.0, 0.0, 0.0, 0.0, 0.0); + const auto result = calcInterpolatedPoint(traj, target_pose); + + EXPECT_NEAR(result.pose.position.x, 9.0, epsilon); + EXPECT_NEAR(result.pose.position.y, 0.0, epsilon); + EXPECT_NEAR(result.pose.position.z, 0.0, epsilon); + EXPECT_NEAR(result.pose.orientation.x, 0.0, epsilon); + EXPECT_NEAR(result.pose.orientation.y, 0.0, epsilon); + EXPECT_NEAR(result.pose.orientation.z, 0.0, epsilon); + EXPECT_NEAR(result.pose.orientation.w, 1.0, epsilon); + EXPECT_NEAR(result.longitudinal_velocity_mps, 9.0, epsilon); + EXPECT_NEAR(result.lateral_velocity_mps, 4.5, epsilon); + EXPECT_NEAR(result.heading_rate_rps, 0.9, epsilon); + EXPECT_NEAR(result.acceleration_mps2, 0.45, epsilon); + EXPECT_NEAR(result.front_wheel_angle_rad, 0.0, epsilon); + EXPECT_NEAR(result.rear_wheel_angle_rad, 0.0, epsilon); + } + + // Outside of initial point + { + const auto target_pose = createPose(-2.0, -9.0, 0.0, 0.0, 0.0, 0.0); + const auto result = calcInterpolatedPoint(traj, target_pose); + + EXPECT_NEAR(result.pose.position.x, 0.0, epsilon); + EXPECT_NEAR(result.pose.position.y, 0.0, epsilon); + EXPECT_NEAR(result.pose.position.z, 0.0, epsilon); + EXPECT_NEAR(result.pose.orientation.x, 0.0, epsilon); + EXPECT_NEAR(result.pose.orientation.y, 0.0, epsilon); + EXPECT_NEAR(result.pose.orientation.z, 0.0, epsilon); + EXPECT_NEAR(result.pose.orientation.w, 1.0, epsilon); + EXPECT_NEAR(result.longitudinal_velocity_mps, 0.0, epsilon); + EXPECT_NEAR(result.lateral_velocity_mps, 0.0, epsilon); + EXPECT_NEAR(result.heading_rate_rps, 0.0, epsilon); + EXPECT_NEAR(result.acceleration_mps2, 0.0, epsilon); + EXPECT_NEAR(result.front_wheel_angle_rad, 0.0, epsilon); + EXPECT_NEAR(result.rear_wheel_angle_rad, 0.0, epsilon); + } + + // Outside of terminal point + { + const auto target_pose = createPose(10.0, 5.0, 0.0, 0.0, 0.0, 0.0); + const auto result = calcInterpolatedPoint(traj, target_pose); + + EXPECT_NEAR(result.pose.position.x, 9.0, epsilon); + EXPECT_NEAR(result.pose.position.y, 0.0, epsilon); + EXPECT_NEAR(result.pose.position.z, 0.0, epsilon); + EXPECT_NEAR(result.pose.orientation.x, 0.0, epsilon); + EXPECT_NEAR(result.pose.orientation.y, 0.0, epsilon); + EXPECT_NEAR(result.pose.orientation.z, 0.0, epsilon); + EXPECT_NEAR(result.pose.orientation.w, 1.0, epsilon); + EXPECT_NEAR(result.longitudinal_velocity_mps, 9.0, epsilon); + EXPECT_NEAR(result.lateral_velocity_mps, 4.5, epsilon); + EXPECT_NEAR(result.heading_rate_rps, 0.9, epsilon); + EXPECT_NEAR(result.acceleration_mps2, 0.45, epsilon); + EXPECT_NEAR(result.front_wheel_angle_rad, 0.0, epsilon); + EXPECT_NEAR(result.rear_wheel_angle_rad, 0.0, epsilon); + } + } + + // Empty Point + { + const Trajectory traj; + const auto target_pose = createPose(3.0, 5.0, 0.0, 0.0, 0.0, 0.0); + const auto result = calcInterpolatedPoint(traj, target_pose); + + EXPECT_NEAR(result.pose.position.x, 3.0, epsilon); + EXPECT_NEAR(result.pose.position.y, 5.0, epsilon); + EXPECT_NEAR(result.pose.position.z, 0.0, epsilon); + EXPECT_NEAR(result.pose.orientation.x, 0.0, epsilon); + EXPECT_NEAR(result.pose.orientation.y, 0.0, epsilon); + EXPECT_NEAR(result.pose.orientation.z, 0.0, epsilon); + EXPECT_NEAR(result.pose.orientation.w, 1.0, epsilon); + EXPECT_NEAR(result.longitudinal_velocity_mps, 0.0, epsilon); + EXPECT_NEAR(result.lateral_velocity_mps, 0.0, epsilon); + EXPECT_NEAR(result.heading_rate_rps, 0.0, epsilon); + EXPECT_NEAR(result.acceleration_mps2, 0.0, epsilon); + EXPECT_NEAR(result.front_wheel_angle_rad, 0.0, epsilon); + EXPECT_NEAR(result.rear_wheel_angle_rad, 0.0, epsilon); + } + + // One point + { + const auto traj = generateTestTrajectory(1, 1.0, 1.0, 0.5, 0.1, 0.05); + const auto target_pose = createPose(3.0, 5.0, 0.0, 0.0, 0.0, 0.0); + const auto result = calcInterpolatedPoint(traj, target_pose); + + EXPECT_NEAR(result.pose.position.x, 0.0, epsilon); + EXPECT_NEAR(result.pose.position.y, 0.0, epsilon); + EXPECT_NEAR(result.pose.position.z, 0.0, epsilon); + EXPECT_NEAR(result.pose.orientation.x, 0.0, epsilon); + EXPECT_NEAR(result.pose.orientation.y, 0.0, epsilon); + EXPECT_NEAR(result.pose.orientation.z, 0.0, epsilon); + EXPECT_NEAR(result.pose.orientation.w, 1.0, epsilon); + EXPECT_NEAR(result.longitudinal_velocity_mps, 1.0, epsilon); + EXPECT_NEAR(result.lateral_velocity_mps, 0.5, epsilon); + EXPECT_NEAR(result.heading_rate_rps, 0.1, epsilon); + EXPECT_NEAR(result.acceleration_mps2, 0.05, epsilon); + EXPECT_NEAR(result.front_wheel_angle_rad, 0.0, epsilon); + EXPECT_NEAR(result.rear_wheel_angle_rad, 0.0, epsilon); + } + + // Duplicated Points + { + autoware_auto_planning_msgs::msg::Trajectory traj; + traj.points.resize(10); + for (size_t i = 0; i < 10; ++i) { + traj.points.at(i) = + generateTestTrajectoryPoint(i * 1.0, 0.0, 0.0, 0.0, i * 1.0, i * 0.5, i * 0.1, i * 0.05); + } + traj.points.at(4) = traj.points.at(3); + + const auto target_pose = createPose(3.2, 5.0, 0.0, 0.0, 0.0, 0.0); + const auto result = calcInterpolatedPoint(traj, target_pose); + + EXPECT_NEAR(result.pose.position.x, 3.0, epsilon); + EXPECT_NEAR(result.pose.position.y, 0.0, epsilon); + EXPECT_NEAR(result.pose.position.z, 0.0, epsilon); + EXPECT_NEAR(result.pose.orientation.x, 0.0, epsilon); + EXPECT_NEAR(result.pose.orientation.y, 0.0, epsilon); + EXPECT_NEAR(result.pose.orientation.z, 0.0, epsilon); + EXPECT_NEAR(result.pose.orientation.w, 1.0, epsilon); + EXPECT_NEAR(result.longitudinal_velocity_mps, 3.0, epsilon); + EXPECT_NEAR(result.lateral_velocity_mps, 1.5, epsilon); + EXPECT_NEAR(result.heading_rate_rps, 0.3, epsilon); + EXPECT_NEAR(result.acceleration_mps2, 0.15, epsilon); + EXPECT_NEAR(result.front_wheel_angle_rad, 0.0, epsilon); + EXPECT_NEAR(result.rear_wheel_angle_rad, 0.0, epsilon); + } +} diff --git a/common/motion_utils/test/src/trajectory/test_path_with_lane_id.cpp b/common/motion_utils/test/src/trajectory/test_path_with_lane_id.cpp index 1eadf7105d087..d3e2565c99f1b 100644 --- a/common/motion_utils/test/src/trajectory/test_path_with_lane_id.cpp +++ b/common/motion_utils/test/src/trajectory/test_path_with_lane_id.cpp @@ -72,16 +72,16 @@ TEST(path_with_lane_id, getPathIndexRangeWithLaneId) { const auto res = getPathIndexRangeWithLaneId(points, 3); EXPECT_EQ(res->first, 0U); - EXPECT_EQ(res->second, 1U); + EXPECT_EQ(res->second, 2U); } { const auto res = getPathIndexRangeWithLaneId(points, 1); - EXPECT_EQ(res->first, 2U); - EXPECT_EQ(res->second, 2U); + EXPECT_EQ(res->first, 1U); + EXPECT_EQ(res->second, 3U); } { const auto res = getPathIndexRangeWithLaneId(points, 2); - EXPECT_EQ(res->first, 3U); + EXPECT_EQ(res->first, 2U); EXPECT_EQ(res->second, 5U); } { @@ -142,7 +142,7 @@ TEST(path_with_lane_id, findNearestIndexFromLaneId) for (size_t i = 3; i < 9; ++i) { modified_path.points.at(i).lane_ids = {100}; } - EXPECT_EQ(findNearestIndexFromLaneId(modified_path, createPoint(2.4, 1.3, 0.0), 100), 3U); + EXPECT_EQ(findNearestIndexFromLaneId(modified_path, createPoint(2.4, 1.3, 0.0), 100), 2U); EXPECT_EQ( findNearestSegmentIndexFromLaneId(modified_path, createPoint(2.4, 1.3, 0.0), 100), 2U); } diff --git a/common/osqp_interface/design/osqp_interface-design.md b/common/osqp_interface/design/osqp_interface-design.md index 8fd6ab28a659d..e9ff4a2a3bc3a 100644 --- a/common/osqp_interface/design/osqp_interface-design.md +++ b/common/osqp_interface/design/osqp_interface-design.md @@ -68,5 +68,3 @@ The interface can be used in several ways: ## Related issues - -- This package was introduced as a dependency of the MPC-based lateral controller: diff --git a/common/perception_utils/CMakeLists.txt b/common/perception_utils/CMakeLists.txt index 46f7ae74774e2..b6d9376a790ae 100644 --- a/common/perception_utils/CMakeLists.txt +++ b/common/perception_utils/CMakeLists.txt @@ -8,6 +8,7 @@ find_package(Boost REQUIRED) ament_auto_add_library(perception_utils SHARED src/perception_utils.cpp + src/predicted_path_utils.cpp ) if(BUILD_TESTING) diff --git a/common/perception_utils/include/perception_utils/predicted_path_utils.hpp b/common/perception_utils/include/perception_utils/predicted_path_utils.hpp index 314318ca16b12..4e5593bff3412 100644 --- a/common/perception_utils/include/perception_utils/predicted_path_utils.hpp +++ b/common/perception_utils/include/perception_utils/predicted_path_utils.hpp @@ -16,6 +16,7 @@ #define PERCEPTION_UTILS__PREDICTED_PATH_UTILS_HPP_ #include "interpolation/linear_interpolation.hpp" +#include "interpolation/spherical_linear_interpolation.hpp" #include "interpolation/spline_interpolation.hpp" #include "perception_utils/geometry.hpp" #include "tier4_autoware_utils/geometry/geometry.hpp" @@ -39,28 +40,8 @@ namespace perception_utils * time_step*(num_of_path_points)] * @return interpolated pose */ -inline boost::optional calcInterpolatedPose( - const autoware_auto_perception_msgs::msg::PredictedPath & path, const double relative_time) -{ - // Check if relative time is in the valid range - if (path.path.empty() || relative_time < 0.0) { - return boost::none; - } - - constexpr double epsilon = 1e-6; - const double & time_step = rclcpp::Duration(path.time_step).seconds(); - for (size_t path_idx = 1; path_idx < path.path.size(); ++path_idx) { - const auto & pt = path.path.at(path_idx); - const auto & prev_pt = path.path.at(path_idx - 1); - if (relative_time - epsilon < time_step * path_idx) { - const double offset = relative_time - time_step * (path_idx - 1); - const double ratio = std::clamp(offset / time_step, 0.0, 1.0); - return tier4_autoware_utils::calcInterpolatedPose(prev_pt, pt, ratio); - } - } - - return boost::none; -} +boost::optional calcInterpolatedPose( + const autoware_auto_perception_msgs::msg::PredictedPath & path, const double relative_time); /** * @brief Resampling predicted path by time step vector. Note this function does not substitute @@ -70,63 +51,10 @@ inline boost::optional calcInterpolatedPose( * time_step*(num_of_path_points)] * @return resampled path */ -inline autoware_auto_perception_msgs::msg::PredictedPath resamplePredictedPath( +autoware_auto_perception_msgs::msg::PredictedPath resamplePredictedPath( const autoware_auto_perception_msgs::msg::PredictedPath & path, const std::vector & resampled_time, const bool use_spline_for_xy = true, - const bool use_spline_for_z = false) -{ - if (path.path.empty() || resampled_time.empty()) { - throw std::invalid_argument("input path or resampled_time is empty"); - } - - const double & time_step = rclcpp::Duration(path.time_step).seconds(); - std::vector input_time(path.path.size()); - std::vector x(path.path.size()); - std::vector y(path.path.size()); - std::vector z(path.path.size()); - for (size_t i = 0; i < path.path.size(); ++i) { - input_time.at(i) = time_step * i; - x.at(i) = path.path.at(i).position.x; - y.at(i) = path.path.at(i).position.y; - z.at(i) = path.path.at(i).position.z; - } - - const auto lerp = [&](const auto & input) { - return interpolation::lerp(input_time, input, resampled_time); - }; - const auto slerp = [&](const auto & input) { - return interpolation::slerp(input_time, input, resampled_time); - }; - - const auto interpolated_x = use_spline_for_xy ? slerp(x) : lerp(x); - const auto interpolated_y = use_spline_for_xy ? slerp(y) : lerp(y); - const auto interpolated_z = use_spline_for_z ? slerp(z) : lerp(z); - - autoware_auto_perception_msgs::msg::PredictedPath resampled_path; - const auto resampled_size = std::min(resampled_path.path.max_size(), resampled_time.size()); - resampled_path.confidence = path.confidence; - resampled_path.path.resize(resampled_size); - - // Set Position - for (size_t i = 0; i < resampled_size; ++i) { - const auto p = tier4_autoware_utils::createPoint( - interpolated_x.at(i), interpolated_y.at(i), interpolated_z.at(i)); - resampled_path.path.at(i).position = p; - } - - // Set Quaternion - for (size_t i = 0; i < resampled_size - 1; ++i) { - const auto & src_point = resampled_path.path.at(i).position; - const auto & dst_point = resampled_path.path.at(i + 1).position; - const double pitch = tier4_autoware_utils::calcElevationAngle(src_point, dst_point); - const double yaw = tier4_autoware_utils::calcAzimuthAngle(src_point, dst_point); - resampled_path.path.at(i).orientation = - tier4_autoware_utils::createQuaternionFromRPY(0.0, pitch, yaw); - } - resampled_path.path.back().orientation = resampled_path.path.at(resampled_size - 2).orientation; - - return resampled_path; -} + const bool use_spline_for_z = false); /** * @brief Resampling predicted path by sampling time interval. Note that this function samples @@ -136,37 +64,10 @@ inline autoware_auto_perception_msgs::msg::PredictedPath resamplePredictedPath( * @param sampling_horizon sampling time horizon * @return resampled path */ -inline autoware_auto_perception_msgs::msg::PredictedPath resamplePredictedPath( +autoware_auto_perception_msgs::msg::PredictedPath resamplePredictedPath( const autoware_auto_perception_msgs::msg::PredictedPath & path, const double sampling_time_interval, const double sampling_horizon, - const bool use_spline_for_xy = true, const bool use_spline_for_z = false) -{ - if (path.path.empty()) { - throw std::invalid_argument("Predicted Path is empty"); - } - - if (sampling_time_interval <= 0.0 || sampling_horizon <= 0.0) { - throw std::invalid_argument("sampling time interval or sampling time horizon is negative"); - } - - // Calculate Horizon - const double predicted_horizon = - rclcpp::Duration(path.time_step).seconds() * static_cast(path.path.size() - 1); - const double horizon = std::min(predicted_horizon, sampling_horizon); - - // Get sampling time vector - constexpr double epsilon = 1e-6; - std::vector sampling_time_vector; - for (double t = 0.0; t < horizon + epsilon; t += sampling_time_interval) { - sampling_time_vector.push_back(t); - } - - // Resample and substitute time interval - auto resampled_path = - resamplePredictedPath(path, sampling_time_vector, use_spline_for_xy, use_spline_for_z); - resampled_path.time_step = rclcpp::Duration::from_seconds(sampling_time_interval); - return resampled_path; -} + const bool use_spline_for_xy = true, const bool use_spline_for_z = false); } // namespace perception_utils #endif // PERCEPTION_UTILS__PREDICTED_PATH_UTILS_HPP_ diff --git a/common/perception_utils/src/predicted_path_utils.cpp b/common/perception_utils/src/predicted_path_utils.cpp new file mode 100644 index 0000000000000..96ca74d2cb234 --- /dev/null +++ b/common/perception_utils/src/predicted_path_utils.cpp @@ -0,0 +1,127 @@ +// Copyright 2022 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 "perception_utils/predicted_path_utils.hpp" + +namespace perception_utils +{ +boost::optional calcInterpolatedPose( + const autoware_auto_perception_msgs::msg::PredictedPath & path, const double relative_time) +{ + // Check if relative time is in the valid range + if (path.path.empty() || relative_time < 0.0) { + return boost::none; + } + + constexpr double epsilon = 1e-6; + const double & time_step = rclcpp::Duration(path.time_step).seconds(); + for (size_t path_idx = 1; path_idx < path.path.size(); ++path_idx) { + const auto & pt = path.path.at(path_idx); + const auto & prev_pt = path.path.at(path_idx - 1); + if (relative_time - epsilon < time_step * path_idx) { + const double offset = relative_time - time_step * (path_idx - 1); + const double ratio = std::clamp(offset / time_step, 0.0, 1.0); + return tier4_autoware_utils::calcInterpolatedPose(prev_pt, pt, ratio, false); + } + } + + return boost::none; +} + +autoware_auto_perception_msgs::msg::PredictedPath resamplePredictedPath( + const autoware_auto_perception_msgs::msg::PredictedPath & path, + const std::vector & resampled_time, const bool use_spline_for_xy, + const bool use_spline_for_z) +{ + if (path.path.empty() || resampled_time.empty()) { + throw std::invalid_argument("input path or resampled_time is empty"); + } + + const double & time_step = rclcpp::Duration(path.time_step).seconds(); + std::vector input_time(path.path.size()); + std::vector x(path.path.size()); + std::vector y(path.path.size()); + std::vector z(path.path.size()); + std::vector quat(path.path.size()); + for (size_t i = 0; i < path.path.size(); ++i) { + input_time.at(i) = time_step * i; + x.at(i) = path.path.at(i).position.x; + y.at(i) = path.path.at(i).position.y; + z.at(i) = path.path.at(i).position.z; + quat.at(i) = path.path.at(i).orientation; + } + + const auto lerp = [&](const auto & input) { + return interpolation::lerp(input_time, input, resampled_time); + }; + const auto spline = [&](const auto & input) { + return interpolation::spline(input_time, input, resampled_time); + }; + const auto slerp = [&](const auto & input) { + return interpolation::slerp(input_time, input, resampled_time); + }; + + const auto interpolated_x = use_spline_for_xy ? spline(x) : lerp(x); + const auto interpolated_y = use_spline_for_xy ? spline(y) : lerp(y); + const auto interpolated_z = use_spline_for_z ? spline(z) : lerp(z); + const auto interpolated_quat = slerp(quat); + + autoware_auto_perception_msgs::msg::PredictedPath resampled_path; + const auto resampled_size = std::min(resampled_path.path.max_size(), resampled_time.size()); + resampled_path.confidence = path.confidence; + resampled_path.path.resize(resampled_size); + + // Set Position + for (size_t i = 0; i < resampled_size; ++i) { + const auto p = tier4_autoware_utils::createPoint( + interpolated_x.at(i), interpolated_y.at(i), interpolated_z.at(i)); + resampled_path.path.at(i).position = p; + resampled_path.path.at(i).orientation = interpolated_quat.at(i); + } + + return resampled_path; +} + +autoware_auto_perception_msgs::msg::PredictedPath resamplePredictedPath( + const autoware_auto_perception_msgs::msg::PredictedPath & path, + const double sampling_time_interval, const double sampling_horizon, const bool use_spline_for_xy, + const bool use_spline_for_z) +{ + if (path.path.empty()) { + throw std::invalid_argument("Predicted Path is empty"); + } + + if (sampling_time_interval <= 0.0 || sampling_horizon <= 0.0) { + throw std::invalid_argument("sampling time interval or sampling time horizon is negative"); + } + + // Calculate Horizon + const double predicted_horizon = + rclcpp::Duration(path.time_step).seconds() * static_cast(path.path.size() - 1); + const double horizon = std::min(predicted_horizon, sampling_horizon); + + // Get sampling time vector + constexpr double epsilon = 1e-6; + std::vector sampling_time_vector; + for (double t = 0.0; t < horizon + epsilon; t += sampling_time_interval) { + sampling_time_vector.push_back(t); + } + + // Resample and substitute time interval + auto resampled_path = + resamplePredictedPath(path, sampling_time_vector, use_spline_for_xy, use_spline_for_z); + resampled_path.time_step = rclcpp::Duration::from_seconds(sampling_time_interval); + return resampled_path; +} +} // namespace perception_utils diff --git a/common/tier4_autoware_utils/include/tier4_autoware_utils/geometry/boost_polygon_utils.hpp b/common/tier4_autoware_utils/include/tier4_autoware_utils/geometry/boost_polygon_utils.hpp index 835f0c37d3031..8bbfe681fa3ae 100644 --- a/common/tier4_autoware_utils/include/tier4_autoware_utils/geometry/boost_polygon_utils.hpp +++ b/common/tier4_autoware_utils/include/tier4_autoware_utils/geometry/boost_polygon_utils.hpp @@ -18,7 +18,6 @@ #include "tier4_autoware_utils/geometry/boost_geometry.hpp" #include "autoware_auto_perception_msgs/msg/predicted_object.hpp" -// #include "geometry_msgs/msg/point32.hpp" #include "geometry_msgs/msg/pose.hpp" #include @@ -35,6 +34,7 @@ geometry_msgs::msg::Polygon rotatePolygon( const geometry_msgs::msg::Polygon & polygon, const double & angle); Polygon2d toPolygon2d( const geometry_msgs::msg::Pose & pose, const autoware_auto_perception_msgs::msg::Shape & shape); +Polygon2d toPolygon2d(const autoware_auto_perception_msgs::msg::PredictedObject & object); double getArea(const autoware_auto_perception_msgs::msg::Shape & shape); } // namespace tier4_autoware_utils diff --git a/common/tier4_autoware_utils/include/tier4_autoware_utils/geometry/geometry.hpp b/common/tier4_autoware_utils/include/tier4_autoware_utils/geometry/geometry.hpp index 115b544726bdf..2ba61fe321cbe 100644 --- a/common/tier4_autoware_utils/include/tier4_autoware_utils/geometry/geometry.hpp +++ b/common/tier4_autoware_utils/include/tier4_autoware_utils/geometry/geometry.hpp @@ -430,6 +430,18 @@ inline geometry_msgs::msg::TransformStamped pose2transform( return transform; } +template +tf2::Vector3 point2tfVector(const Point1 & src, const Point2 & dst) +{ + const auto src_p = getPoint(src); + const auto dst_p = getPoint(dst); + + double dx = dst_p.x - src_p.x; + double dy = dst_p.y - src_p.y; + double dz = dst_p.z - src_p.z; + return tf2::Vector3(dx, dy, dz); +} + inline Point3d transformPoint( const Point3d & point, const geometry_msgs::msg::Transform & transform) { diff --git a/common/tier4_autoware_utils/package.xml b/common/tier4_autoware_utils/package.xml index 193cf657ea830..1e9acb6101935 100644 --- a/common/tier4_autoware_utils/package.xml +++ b/common/tier4_autoware_utils/package.xml @@ -4,7 +4,7 @@ tier4_autoware_utils 0.1.0 The tier4_autoware_utils package - Takamasa Horibe + Takamasa Horibe Kenji Miyake Apache License 2.0 diff --git a/common/tier4_autoware_utils/src/geometry/boost_polygon_utils.cpp b/common/tier4_autoware_utils/src/geometry/boost_polygon_utils.cpp index f9ba0f537f07f..b54f888ad0ae1 100644 --- a/common/tier4_autoware_utils/src/geometry/boost_polygon_utils.cpp +++ b/common/tier4_autoware_utils/src/geometry/boost_polygon_utils.cpp @@ -167,6 +167,13 @@ Polygon2d toPolygon2d( return isClockwise(polygon) ? polygon : inverseClockwise(polygon); } +tier4_autoware_utils::Polygon2d toPolygon2d( + const autoware_auto_perception_msgs::msg::PredictedObject & object) +{ + return tier4_autoware_utils::toPolygon2d( + object.kinematics.initial_pose_with_covariance.pose, object.shape); +} + double getArea(const autoware_auto_perception_msgs::msg::Shape & shape) { if (shape.type == autoware_auto_perception_msgs::msg::Shape::BOUNDING_BOX) { diff --git a/common/tier4_autoware_utils/test/src/geometry/test_geometry.cpp b/common/tier4_autoware_utils/test/src/geometry/test_geometry.cpp index 6b8d886b5196a..70ebb8ac72dca 100644 --- a/common/tier4_autoware_utils/test/src/geometry/test_geometry.cpp +++ b/common/tier4_autoware_utils/test/src/geometry/test_geometry.cpp @@ -752,6 +752,73 @@ TEST(geometry, pose2transform) } } +TEST(geometry, point2tfVector) +{ + using tier4_autoware_utils::createQuaternionFromRPY; + using tier4_autoware_utils::deg2rad; + using tier4_autoware_utils::point2tfVector; + + // Point + { + geometry_msgs::msg::Point src; + src.x = 1.0; + src.y = 2.0; + src.z = 3.0; + + geometry_msgs::msg::Point dst; + dst.x = 10.0; + dst.y = 5.0; + dst.z = -5.0; + + const auto vec = point2tfVector(src, dst); + + EXPECT_DOUBLE_EQ(vec.x(), 9.0); + EXPECT_DOUBLE_EQ(vec.y(), 3.0); + EXPECT_DOUBLE_EQ(vec.z(), -8.0); + } + + // Pose + { + geometry_msgs::msg::Pose src; + src.position.x = 1.0; + src.position.y = 2.0; + src.position.z = 3.0; + src.orientation = createQuaternionFromRPY(deg2rad(30), deg2rad(30), deg2rad(30)); + + geometry_msgs::msg::Pose dst; + dst.position.x = 10.0; + dst.position.y = 5.0; + dst.position.z = -5.0; + dst.orientation = createQuaternionFromRPY(deg2rad(10), deg2rad(10), deg2rad(10)); + + const auto vec = point2tfVector(src, dst); + + EXPECT_DOUBLE_EQ(vec.x(), 9.0); + EXPECT_DOUBLE_EQ(vec.y(), 3.0); + EXPECT_DOUBLE_EQ(vec.z(), -8.0); + } + + // Point and Pose + { + geometry_msgs::msg::Point src; + src.x = 1.0; + src.y = 2.0; + src.z = 3.0; + + geometry_msgs::msg::Pose dst; + dst.position.x = 10.0; + dst.position.y = 5.0; + dst.position.z = -5.0; + dst.orientation = createQuaternionFromRPY(deg2rad(10), deg2rad(10), deg2rad(10)); + + const auto vec = point2tfVector(src, dst); + + EXPECT_DOUBLE_EQ(vec.x(), 9.0); + EXPECT_DOUBLE_EQ(vec.y(), 3.0); + EXPECT_DOUBLE_EQ(vec.z(), -8.0); + } +} + TEST(geometry, transformPoint) { using tier4_autoware_utils::createQuaternionFromRPY; diff --git a/common/tier4_planning_rviz_plugin/include/path_with_lane_id/display.hpp b/common/tier4_planning_rviz_plugin/include/path_with_lane_id/display.hpp index 53f54202a9cce..df01ffc73c466 100644 --- a/common/tier4_planning_rviz_plugin/include/path_with_lane_id/display.hpp +++ b/common/tier4_planning_rviz_plugin/include/path_with_lane_id/display.hpp @@ -24,6 +24,7 @@ #include #include #include +#include #include @@ -35,6 +36,7 @@ #include #include +#include namespace rviz_plugins { @@ -62,6 +64,8 @@ private Q_SLOTS: const QColor & color_min, const QColor & color_max, const double ratio); Ogre::ManualObject * path_manual_object_; Ogre::ManualObject * velocity_manual_object_; + std::vector velocity_texts_; + std::vector velocity_text_nodes_; rviz_common::properties::BoolProperty * property_path_view_; rviz_common::properties::BoolProperty * property_velocity_view_; rviz_common::properties::FloatProperty * property_path_width_; @@ -70,6 +74,8 @@ private Q_SLOTS: rviz_common::properties::FloatProperty * property_path_alpha_; rviz_common::properties::FloatProperty * property_velocity_alpha_; rviz_common::properties::FloatProperty * property_velocity_scale_; + rviz_common::properties::BoolProperty * property_velocity_text_view_; + rviz_common::properties::FloatProperty * property_velocity_text_scale_; rviz_common::properties::BoolProperty * property_path_color_view_; rviz_common::properties::BoolProperty * property_velocity_color_view_; rviz_common::properties::FloatProperty * property_vel_max_; diff --git a/common/tier4_planning_rviz_plugin/src/path_with_lane_id/display.cpp b/common/tier4_planning_rviz_plugin/src/path_with_lane_id/display.cpp index 590c7ac9e0320..0ae03ffecce28 100644 --- a/common/tier4_planning_rviz_plugin/src/path_with_lane_id/display.cpp +++ b/common/tier4_planning_rviz_plugin/src/path_with_lane_id/display.cpp @@ -85,7 +85,10 @@ AutowarePathWithLaneIdDisplay::AutowarePathWithLaneIdDisplay() "Constant Color", false, "", property_velocity_view_, SLOT(updateVisualization()), this); property_velocity_color_ = new rviz_common::properties::ColorProperty( "Color", Qt::black, "", property_velocity_view_, SLOT(updateVisualization()), this); - + property_velocity_text_view_ = new rviz_common::properties::BoolProperty( + "View Text Velocity", false, "", this, SLOT(updateVisualization()), this); + property_velocity_text_scale_ = new rviz_common::properties::FloatProperty( + "Scale", 0.3, "", property_velocity_text_view_, SLOT(updateVisualization()), this); property_vel_max_ = new rviz_common::properties::FloatProperty( "Color Border Vel Max", 3.0, "[m/s]", this, SLOT(updateVisualization()), this); property_vel_max_->setMin(0.0); @@ -96,6 +99,12 @@ AutowarePathWithLaneIdDisplay::~AutowarePathWithLaneIdDisplay() if (initialized()) { scene_manager_->destroyManualObject(path_manual_object_); scene_manager_->destroyManualObject(velocity_manual_object_); + for (size_t i = 0; i < velocity_text_nodes_.size(); i++) { + Ogre::SceneNode * node = velocity_text_nodes_.at(i); + node->removeAndDestroyAllChildren(); + node->detachAllObjects(); + scene_manager_->destroySceneNode(node); + } } } @@ -168,6 +177,29 @@ void AutowarePathWithLaneIdDisplay::processMessage( // path_manual_object_->begin("BaseWhiteNoLighting", Ogre::RenderOperation::OT_TRIANGLE_STRIP); velocity_manual_object_->begin("BaseWhiteNoLighting", Ogre::RenderOperation::OT_LINE_STRIP); + if (msg_ptr->points.size() > velocity_texts_.size()) { + for (size_t i = velocity_texts_.size(); i < msg_ptr->points.size(); i++) { + Ogre::SceneNode * node = scene_node_->createChildSceneNode(); + rviz_rendering::MovableText * text = + new rviz_rendering::MovableText("not initialized", "Liberation Sans", 0.1); + text->setVisible(false); + text->setTextAlignment( + rviz_rendering::MovableText::H_CENTER, rviz_rendering::MovableText::V_ABOVE); + node->attachObject(text); + velocity_texts_.push_back(text); + velocity_text_nodes_.push_back(node); + } + } else if (msg_ptr->points.size() < velocity_texts_.size()) { + for (size_t i = velocity_texts_.size() - 1; i >= msg_ptr->points.size(); i--) { + Ogre::SceneNode * node = velocity_text_nodes_.at(i); + node->detachAllObjects(); + node->removeAndDestroyAllChildren(); + scene_manager_->destroySceneNode(node); + } + velocity_texts_.resize(msg_ptr->points.size()); + velocity_text_nodes_.resize(msg_ptr->points.size()); + } + for (size_t point_idx = 0; point_idx < msg_ptr->points.size(); point_idx++) { const auto & e = msg_ptr->points.at(point_idx); /* @@ -236,6 +268,29 @@ void AutowarePathWithLaneIdDisplay::processMessage( e.point.longitudinal_velocity_mps * property_velocity_scale_->getFloat()); velocity_manual_object_->colour(color); } + + /* + * Velocity Text + */ + if (property_velocity_text_view_->getBool()) { + Ogre::Vector3 position; + position.x = e.point.pose.position.x; + position.y = e.point.pose.position.y; + position.z = e.point.pose.position.z; + Ogre::SceneNode * node = velocity_text_nodes_.at(point_idx); + node->setPosition(position); + + rviz_rendering::MovableText * text = velocity_texts_.at(point_idx); + double vel = e.point.longitudinal_velocity_mps; + text->setCaption( + std::to_string(static_cast(std::floor(vel))) + "." + + std::to_string(static_cast(std::floor(vel * 100)))); + text->setCharacterHeight(property_velocity_text_scale_->getFloat()); + text->setVisible(true); + } else { + rviz_rendering::MovableText * text = velocity_texts_.at(point_idx); + text->setVisible(false); + } } path_manual_object_->end(); diff --git a/common/vehicle_constants_manager/design/vehicle_constants_manager-design.md b/common/vehicle_constants_manager/design/vehicle_constants_manager-design.md index 88ba349675659..1a83c48a34aed 100644 --- a/common/vehicle_constants_manager/design/vehicle_constants_manager-design.md +++ b/common/vehicle_constants_manager/design/vehicle_constants_manager-design.md @@ -147,5 +147,3 @@ Not Available. ## Related issues - - diff --git a/control/control_performance_analysis/package.xml b/control/control_performance_analysis/package.xml index 67626d8de41cb..17f1b35129347 100644 --- a/control/control_performance_analysis/package.xml +++ b/control/control_performance_analysis/package.xml @@ -9,6 +9,9 @@ Apache License 2.0 + Ali Boyali + Berkay Karaman + ament_cmake_auto rosidl_default_generators diff --git a/control/joy_controller/package.xml b/control/joy_controller/package.xml index 11d47f8afa3d9..ad992590adc7b 100644 --- a/control/joy_controller/package.xml +++ b/control/joy_controller/package.xml @@ -7,6 +7,8 @@ Kenji Miyake Apache License 2.0 + Kenji Miyake + ament_cmake_auto autoware_cmake diff --git a/control/lane_departure_checker/package.xml b/control/lane_departure_checker/package.xml index 6658dcc61bd57..b73105c6b5ee0 100644 --- a/control/lane_departure_checker/package.xml +++ b/control/lane_departure_checker/package.xml @@ -4,9 +4,11 @@ lane_departure_checker 0.1.0 The lane_departure_checker package - Kenji Miyake + Kyoichi Sugahara Apache License 2.0 + Kenji Miyake + ament_cmake_auto autoware_cmake diff --git a/control/obstacle_collision_checker/package.xml b/control/obstacle_collision_checker/package.xml index 2c5f980b899be..c49dcf646c105 100644 --- a/control/obstacle_collision_checker/package.xml +++ b/control/obstacle_collision_checker/package.xml @@ -7,6 +7,7 @@ Kenji Miyake Apache License 2.0 + Kenji Miyake Satoshi Tanaka ament_cmake diff --git a/control/operation_mode_transition_manager/package.xml b/control/operation_mode_transition_manager/package.xml index d469750163547..9db703b6ea7db 100644 --- a/control/operation_mode_transition_manager/package.xml +++ b/control/operation_mode_transition_manager/package.xml @@ -2,9 +2,11 @@ operation_mode_transition_manager 0.1.0 The vehicle_cmd_gate package - Takamasa Horibe + Takamasa Horibe Apache License 2.0 + Takamasa Horibe + autoware_cmake rosidl_default_generators diff --git a/control/pure_pursuit/package.xml b/control/pure_pursuit/package.xml index b780835bff3df..0cd68d0749ee0 100644 --- a/control/pure_pursuit/package.xml +++ b/control/pure_pursuit/package.xml @@ -4,10 +4,12 @@ pure_pursuit 0.1.0 The pure_pursuit package - Kenji Miyake - Takamasa Horibe + Takamasa Horibe Apache License 2.0 + Kenji Miyake + Takamasa Horibe + ament_cmake_auto autoware_cmake diff --git a/control/shift_decider/package.xml b/control/shift_decider/package.xml index 29033dcf960bf..a8c347b061472 100644 --- a/control/shift_decider/package.xml +++ b/control/shift_decider/package.xml @@ -4,10 +4,10 @@ shift_decider 0.1.0 The shift_decider package - Takamasa Horibe + Takamasa Horibe Apache License 2.0 - Takamasa Horibe + Takamasa Horibe ament_cmake diff --git a/control/trajectory_follower_nodes/design/media/BrakeKeeping.drawio.svg b/control/trajectory_follower/design/media/BrakeKeeping.drawio.svg similarity index 100% rename from control/trajectory_follower_nodes/design/media/BrakeKeeping.drawio.svg rename to control/trajectory_follower/design/media/BrakeKeeping.drawio.svg diff --git a/control/trajectory_follower_nodes/design/media/LongitudinalControllerDiagram.drawio.svg b/control/trajectory_follower/design/media/LongitudinalControllerDiagram.drawio.svg similarity index 100% rename from control/trajectory_follower_nodes/design/media/LongitudinalControllerDiagram.drawio.svg rename to control/trajectory_follower/design/media/LongitudinalControllerDiagram.drawio.svg diff --git a/control/trajectory_follower_nodes/design/media/LongitudinalControllerStateTransition.drawio.svg b/control/trajectory_follower/design/media/LongitudinalControllerStateTransition.drawio.svg similarity index 100% rename from control/trajectory_follower_nodes/design/media/LongitudinalControllerStateTransition.drawio.svg rename to control/trajectory_follower/design/media/LongitudinalControllerStateTransition.drawio.svg diff --git a/control/trajectory_follower_nodes/design/lateral_controller-design.md b/control/trajectory_follower/design/mpc_lateral_controller-design.md similarity index 81% rename from control/trajectory_follower_nodes/design/lateral_controller-design.md rename to control/trajectory_follower/design/mpc_lateral_controller-design.md index 450e7d04707d3..ee79cc88f206b 100644 --- a/control/trajectory_follower_nodes/design/lateral_controller-design.md +++ b/control/trajectory_follower/design/mpc_lateral_controller-design.md @@ -1,4 +1,4 @@ -# Lateral Controller +# MPC Lateral Controller This is the design document for the lateral controller node in the `trajectory_follower_nodes` package. @@ -22,29 +22,62 @@ The node uses an implementation of linear model predictive control (MPC) for acc The MPC uses a model of the vehicle to simulate the trajectory resulting from the control command. The optimization of the control command is formulated as a Quadratic Program (QP). -These functionalities are implemented in the `trajectory_follower` package -(see [trajectory_follower-mpc-design](../../trajectory_follower/design/trajectory_follower-mpc-design.md#mpc-trajectory-follower)) +Different vehicle models are implemented: -### Assumptions / Known limits +- kinematics : bicycle kinematics model with steering 1st-order delay. +- kinematics_no_delay : bicycle kinematics model without steering delay. +- dynamics : bicycle dynamics model considering slip angle. + The kinematics model is being used by default. Please see the reference [1] for more details. + +For the optimization, a Quadratic Programming (QP) solver is used and two options are currently implemented: + +- unconstraint_fast : use least square method to solve unconstraint QP with eigen. +- [osqp](https://osqp.org/): run the [following ADMM](https://web.stanford.edu/~boyd/papers/admm_distr_stats.html) algorithm (for more details see the related papers at the [Citing OSQP](https://web.stanford.edu/~boyd/papers/admm_distr_stats.html) section): + +### Filtering + +Filtering is required for good noise reduction. +A [Butterworth filter](https://en.wikipedia.org/wiki/Butterworth_filter) is used for the yaw and lateral errors used as input of the MPC as well as for +the output steering angle. +Other filtering methods can be considered as long as the noise reduction performances are good +enough. +The moving average filter for example is not suited and can yield worse results than without any +filtering. + +## Assumptions / Known limits The tracking is not accurate if the first point of the reference trajectory is at or in front of the current ego pose. -- Issue to add points behind ego: - -### Inputs / Outputs / API +## Inputs / Outputs / API -Inputs +### Inputs + +Set the following from the [controller_node](../../trajectory_follower_nodes/design/trajectory_follower-design.md) + +- `autoware_auto_planning_msgs/Trajectory` : reference trajectory to follow. +- `nav_msgs/Odometry`: current odometry +- `autoware_auto_vehicle_msgs/SteeringReport` current steering -- `input/reference_trajectory` : reference trajectory to follow. -- `input/current_kinematic_state`: current state of the vehicle (position, velocity, etc). - Output -- `output/lateral_control_cmd`: generated lateral control command. +### Outputs + +Return LateralOutput which contains the following to [controller_node](../../trajectory_follower_nodes/design/trajectory_follower-design.md) + +- `autoware_auto_control_msgs/AckermannLateralCommand` +- LateralSyncData + - steer angle convergence + +### MPC class + +The `MPC` class (defined in `mpc.hpp`) provides the interface with the MPC algorithm. +Once a vehicle model, a QP solver, and the reference trajectory to follow have been set +(using `setVehicleModel()`, `setQPSolver()`, `setReferenceTrajectory()`), a lateral control command +can be calculated by providing the current steer, velocity, and pose to function `calculateMPC()`. ### Parameter description @@ -66,7 +99,7 @@ AutonomouStuff Lexus RX 450h for under 40 km/h driving. | stop_state_entry_ego_speed \*1 | double | threshold value of the ego vehicle speed used to the stop state entry condition | 0.0 | | stop_state_entry_target_speed \*1 | double | threshold value of the target speed used to the stop state entry condition | 0.0 | | converged_steer_rad | double | threshold value of the steer convergence | 0.1 | -| keep_steer_control_until_converged | bool | keep steer control until steer is converged | false | +| keep_steer_control_until_converged | bool | keep steer control until steer is converged | true | | new_traj_duration_time | double | threshold value of the time to be considered as new trajectory | 1.0 | | new_traj_end_dist | double | threshold value of the distance between trajectory ends to be considered as new trajectory | 0.3 | @@ -132,8 +165,13 @@ AutonomouStuff Lexus RX 450h for under 40 km/h driving. - `weight_terminal_lat_error`: Preferable to set a higher value than normal lateral weight `weight_lat_error` for stability. - `weight_terminal_heading_error`: Preferable to set a higher value than normal heading weight `weight_heading_error` for stability. +## References / External links + + + +- [1] Jarrod M. Snider, "Automatic Steering Methods for Autonomous Automobile Path Tracking", + Robotics Institute, Carnegie Mellon University, February 2009. + ## Related issues - -- diff --git a/control/trajectory_follower_nodes/design/longitudinal_controller-design.md b/control/trajectory_follower/design/pid_longitudinal_controller-design.md similarity index 87% rename from control/trajectory_follower_nodes/design/longitudinal_controller-design.md rename to control/trajectory_follower/design/pid_longitudinal_controller-design.md index 207a9755d7117..0cca6803490cd 100644 --- a/control/trajectory_follower_nodes/design/longitudinal_controller-design.md +++ b/control/trajectory_follower/design/pid_longitudinal_controller-design.md @@ -1,4 +1,4 @@ -# Longitudinal Controller +# PID Longitudinal Controller ## Purpose / Use cases @@ -9,56 +9,7 @@ It is assumed that the target acceleration calculated here will be properly real Note that the use of this module is not mandatory for Autoware if the vehicle supports the "target speed" interface. -## Separated lateral (steering) and longitudinal (velocity) controls - -This longitudinal controller assumes that the roles of lateral and longitudinal control are separated as follows. - -- Lateral control computes a target steering to keep the vehicle on the trajectory, assuming perfect velocity tracking. -- Longitudinal control computes a target velocity/acceleration to keep the vehicle velocity on the trajectory speed, assuming perfect trajectory tracking. - -Ideally, dealing with the lateral and longitudinal control as a single mixed problem can achieve high performance. In contrast, there are two reasons to provide velocity controller as a stand-alone function, described below. - -### Complex requirements for longitudinal motion - -The longitudinal vehicle behavior that humans expect is difficult to express in a single logic. For example, the expected behavior just before stopping differs depending on whether the ego-position is ahead/behind of the stop line, or whether the current speed is higher/lower than the target speed to achieve a human-like movement. - -In addition, some vehicles have difficulty measuring the ego-speed at extremely low speeds. In such cases, a configuration that can improve the functionality of the longitudinal control without affecting the lateral control is important. - -There are many characteristics and needs that are unique to longitudinal control. Designing them separately from the lateral control keeps the modules less coupled and improves maintainability. - -### Nonlinear coupling of lateral and longitudinal motion - -The lat-lon mixed control problem is very complex and uses nonlinear optimization to achieve high performance. Since it is difficult to guarantee the convergence of the nonlinear optimization, a simple control logic is also necessary for development. - -Also, the benefits of simultaneous longitudinal and lateral control are small if the vehicle doesn't move at high speed. - -## Design - -## Assumptions / Known limits - -1. Smoothed target velocity and its acceleration shall be set in the trajectory - 1. The velocity command is not smoothed inside the controller (only noise may be removed). - 2. For step-like target signal, tracking is performed as fast as possible. -2. The vehicle velocity must be an appropriate value - 1. The ego-velocity must be a signed-value corresponding to the forward/backward direction - 2. The ego-velocity should be given with appropriate noise processing. - 3. If there is a large amount of noise in the ego-velocity, the tracking performance will be significantly reduced. -3. The output of this controller must be achieved by later modules (e.g. vehicle interface). - 1. If the vehicle interface does not have the target velocity or acceleration interface (e.g., the vehicle only has a gas pedal and brake interface), an appropriate conversion must be done after this controller. - -## Inputs / Outputs / API - -### Output - -- control_cmd [`autoware_auto_msgs/LongitudinalCommand`] : command to control the longitudinal motion of the vehicle. It contains the target velocity and target acceleration. -- debug_values [`autoware_auto_msgs/Float32MultiArrayDiagnostic`] : debug values used for the control command generation (e.g. the contributions of each P-I-D terms). - -### Input - -- current_state [`autoware_auto_msgs/VehicleKinematicState`] : Current ego state including the current pose and velocity. -- current_trajectory [`autoware_auto_msgs/Trajectory`] : Current target trajectory for the desired velocity on the each trajectory points. - -## Inner-workings / Algorithms +## Design / Inner-workings / Algorithms ### States @@ -141,7 +92,59 @@ Depending on the actuating principle of the vehicle, the mechanism that physical In this controller, the predicted ego-velocity and the target velocity after the delay time are calculated and used for the feedback to address the time delay problem. -### Parameter description +### Slope compensation + +Based on the slope information, a compensation term is added to the target acceleration. + +There are two sources of the slope information, which can be switched by a parameter. + +- Pitch of the estimated ego-pose (default) + - Calculates the current slope from the pitch angle of the estimated ego-pose + - Pros: Easily available + - Cons: Cannot extract accurate slope information due to the influence of vehicle vibration. +- Z coordinate on the trajectory + - Calculates the road slope from the difference of z-coordinates between the front and rear wheel positions in the target trajectory + - Pros: More accurate than pitch information, if the z-coordinates of the route are properly maintained + - Pros: Can be used in combination with delay compensation (not yet implemented) + - Cons: z-coordinates of high-precision map is needed. + - Cons: Does not support free space planning (for now) + +## Assumptions / Known limits + +1. Smoothed target velocity and its acceleration shall be set in the trajectory + 1. The velocity command is not smoothed inside the controller (only noise may be removed). + 2. For step-like target signal, tracking is performed as fast as possible. +2. The vehicle velocity must be an appropriate value + 1. The ego-velocity must be a signed-value corresponding to the forward/backward direction + 2. The ego-velocity should be given with appropriate noise processing. + 3. If there is a large amount of noise in the ego-velocity, the tracking performance will be significantly reduced. +3. The output of this controller must be achieved by later modules (e.g. vehicle interface). + 1. If the vehicle interface does not have the target velocity or acceleration interface (e.g., the vehicle only has a gas pedal and brake interface), an appropriate conversion must be done after this controller. + +## Inputs / Outputs / API + +### Input + +Set the following from the [controller_node](../../trajectory_follower_nodes/design/trajectory_follower-design.md) + +- `autoware_auto_planning_msgs/Trajectory` : reference trajectory to follow. +- `nav_msgs/Odometry`: current odometry + +### Output + +Return LongitudinalOutput which contains the following to [controller_node](../../trajectory_follower_nodes/design/trajectory_follower-design.md) + +- `autoware_auto_control_msgs/LongitudinalCommand`: command to control the longitudinal motion of the vehicle. It contains the target velocity and target acceleration. +- LongitudinalSyncData + - velocity convergence(currently not used) + +### PIDController class + +The `PIDController` class is straightforward to use. +First, gains and limits must be set (using `setGains()` and `setLimits()`) for the proportional (P), integral (I), and derivative (D) components. +Then, the velocity can be calculated by providing the current error and time step duration to the `calculate()` function. + +## Parameter description The default parameters defined in `param/lateral_controller_defaults.param.yaml` are adjusted to the AutonomouStuff Lexus RX 450h for under 40 km/h driving. @@ -154,7 +157,7 @@ AutonomouStuff Lexus RX 450h for under 40 km/h driving. | enable_large_tracking_error_emergency | bool | flag to enable transition to EMERGENCY when the closest trajectory point search is failed due to a large deviation between trajectory and ego pose. | true | | enable_slope_compensation | bool | flag to modify output acceleration for slope compensation. The source of the slope angle can be selected from ego-pose or trajectory angle. See `use_trajectory_for_pitch_calculation`. | true | | enable_brake_keeping_before_stop | bool | flag to keep a certain acceleration during DRIVE state before the ego stops. See [Brake keeping](#brake-keeping). | false | -| enable_keep_stopped_until_steer_convergence | bool | flag to keep stopped condition until until the steer converges. | false | +| enable_keep_stopped_until_steer_convergence | bool | flag to keep stopped condition until until the steer converges. | true | | max_acc | double | max value of output acceleration [m/s^2] | 3.0 | | min_acc | double | min value of output acceleration [m/s^2] | -5.0 | | max_jerk | double | max value of jerk of output acceleration [m/s^3] | 2.0 | @@ -164,7 +167,7 @@ AutonomouStuff Lexus RX 450h for under 40 km/h driving. | max_pitch_rad | double | max value of estimated pitch [rad] | 0.1 | | min_pitch_rad | double | min value of estimated pitch [rad] | -0.1 | -#### State transition +### State transition | Name | Type | Description | Default value | | :---------------------------------- | :----- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------ | @@ -177,7 +180,7 @@ AutonomouStuff Lexus RX 450h for under 40 km/h driving. | emergency_state_traj_trans_dev | double | If the ego's position is `emergency_state_traj_tran_dev` meter away from the nearest trajectory point, the state will transit to EMERGENCY. [m] | 3.0 | | emergency_state_traj_rot_dev | double | If the ego's orientation is `emergency_state_traj_rot_dev` rad away from the nearest trajectory point orientation, the state will transit to EMERGENCY. [rad] | 0.784 | -#### DRIVE Parameter +### DRIVE Parameter | Name | Type | Description | Default value | | :------------------------------------ | :----- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------ | @@ -196,7 +199,7 @@ AutonomouStuff Lexus RX 450h for under 40 km/h driving. | current_vel_threshold_pid_integration | double | Velocity error is integrated for I-term only when the absolute value of current velocity is larger than this parameter. [m/s] | 0.5 | | brake_keeping_acc | double | If `enable_brake_keeping_before_stop` is true, a certain acceleration is kept during DRIVE state before the ego stops [m/s^2] See [Brake keeping](#brake-keeping). | 0.2 | -#### STOPPING Parameter (smooth stop) +### STOPPING Parameter (smooth stop) Smooth stop is enabled if `enable_smooth_stop` is true. In smooth stop, strong acceleration (`strong_acc`) will be output first to decrease the ego velocity. @@ -218,7 +221,7 @@ If the ego is still running, strong acceleration (`strong_stop_acc`) to stop rig | smooth_stop_weak_stop_dist | double | Weak acceleration will be output when the ego is `smooth_stop_weak_stop_dist`-meter before the stop point. [m] | -0.3 | | smooth_stop_strong_stop_dist | double | Strong acceleration will be output when the ego is `smooth_stop_strong_stop_dist`-meter over the stop point. [m] | -0.5 | -#### STOPPED Parameter +### STOPPED Parameter | Name | Type | Description | Default value | | :----------- | :----- | :------------------------------------------- | :------------ | @@ -226,7 +229,7 @@ If the ego is still running, strong acceleration (`strong_stop_acc`) to stop rig | stopped_acc | double | target acceleration in STOPPED state [m/s^2] | -3.4 | | stopped_jerk | double | target jerk in STOPPED state [m/s^3] | -5.0 | -#### EMERGENCY Parameter +### EMERGENCY Parameter | Name | Type | Description | Default value | | :------------- | :----- | :------------------------------------------------ | :------------ | @@ -239,6 +242,3 @@ If the ego is still running, strong acceleration (`strong_stop_acc`) to stop rig ## Future extensions / Unimplemented parts ## Related issues - -- -- diff --git a/control/trajectory_follower/design/trajectory_follower-design.md b/control/trajectory_follower/design/trajectory_follower-design.md index 2ade485f1a641..46358abe91090 100644 --- a/control/trajectory_follower/design/trajectory_follower-design.md +++ b/control/trajectory_follower/design/trajectory_follower-design.md @@ -12,13 +12,49 @@ This package provides the library code used by the nodes of the `trajectory_foll Mainly, it implements two algorithms: - Model-Predictive Control (MPC) for the computation of lateral steering commands. - - [trajectory_follower-mpc-design](trajectory_follower-mpc-design.md) + - [trajectory_follower-mpc-design](mpc_lateral_controller-design.md) - PID control for the computation of velocity and acceleration commands. - - [trajectory_follower-pid-design](trajectory_follower-pid-design.md) + - [trajectory_follower-pid-design](pid_longitudinal_controller-design.md) + +## Design + +![Controller](../../trajectory_follower_nodes/design/media/Controller.drawio.svg) + +There are lateral and longitudinal base interface classes and each algorithm inherits from this class to implement. +The interface class has the following base functions. + +- `setInputData()`: Input the data subscribed in [Trajectory Follower Nodes](../../trajectory_follower_nodes/design/trajectory_follower-design.md). This must be implemented with the inherited algorithm and the used data must be selected. +- `run()`: Compute control commands and return to [Trajectory Follower Nodes](../../trajectory_follower_nodes/design/trajectory_follower-design.md). This must be implemented by inherited algorithms. +- `syncData()`: Input the result of running the other controller. + - steer angle convergence + - allow keeping stopped until steer is converged. + - velocity convergence(currently not used) + +See [the Design of Trajectory Follower Nodes](../../trajectory_follower_nodes/design/trajectory_follower-design.md#Design) for how these functions work in the node. + +## Separated lateral (steering) and longitudinal (velocity) controls + +This longitudinal controller assumes that the roles of lateral and longitudinal control are separated as follows. + +- Lateral control computes a target steering to keep the vehicle on the trajectory, assuming perfect velocity tracking. +- Longitudinal control computes a target velocity/acceleration to keep the vehicle velocity on the trajectory speed, assuming perfect trajectory tracking. + +Ideally, dealing with the lateral and longitudinal control as a single mixed problem can achieve high performance. In contrast, there are two reasons to provide velocity controller as a stand-alone function, described below. + +### Complex requirements for longitudinal motion + +The longitudinal vehicle behavior that humans expect is difficult to express in a single logic. For example, the expected behavior just before stopping differs depending on whether the ego-position is ahead/behind of the stop line, or whether the current speed is higher/lower than the target speed to achieve a human-like movement. + +In addition, some vehicles have difficulty measuring the ego-speed at extremely low speeds. In such cases, a configuration that can improve the functionality of the longitudinal control without affecting the lateral control is important. + +There are many characteristics and needs that are unique to longitudinal control. Designing them separately from the lateral control keeps the modules less coupled and improves maintainability. + +### Nonlinear coupling of lateral and longitudinal motion + +The lat-lon mixed control problem is very complex and uses nonlinear optimization to achieve high performance. Since it is difficult to guarantee the convergence of the nonlinear optimization, a simple control logic is also necessary for development. + +Also, the benefits of simultaneous longitudinal and lateral control are small if the vehicle doesn't move at high speed. ## Related issues - -- -- diff --git a/control/trajectory_follower/design/trajectory_follower-mpc-design.md b/control/trajectory_follower/design/trajectory_follower-mpc-design.md deleted file mode 100644 index 5dbdf4169651b..0000000000000 --- a/control/trajectory_follower/design/trajectory_follower-mpc-design.md +++ /dev/null @@ -1,76 +0,0 @@ -# MPC (Trajectory Follower) - -This is the design document for the MPC implemented in the `trajectory_follower` package. - -## Purpose / Use cases - - - - -Model Predictive Control (MPC) is used by the `trajectory_follower` -to calculate the lateral commands corresponding to a steering angle and a steering rate. - -This implementation differs from the one in the `mpc_controller` package in several aspects. - -- This is a linear MPC that only computes the steering command whereas - the `mpc_controller` uses a non-linear MPC which calculates coupled steering and velocity commands. -- The optimization problem solved by the linear MPC is simpler, making it less likely to fail. -- Tuning of the linear MPC is easier. - -## Design - - - - -MPC uses predictions of the vehicle's motion to optimize the immediate control command. - -Different vehicle models are implemented: - -- `kinematics` : bicycle kinematics model with steering 1st-order delay. -- `kinematics_no_delay` : bicycle kinematics model without steering delay. -- `dynamics` : bicycle dynamics model considering slip angle. - -The `kinematics` model is being used by default. Please see the reference [1] for more details. - -For the optimization, a Quadratic Programming (QP) solver is used -with two options are currently implemented: - -- `unconstraint` : use least square method to solve unconstraint QP with eigen. -- `unconstraint_fast` : similar to unconstraint. This is faster, but lower accuracy for optimization. - -## Filtering - -Filtering is required for good noise reduction. -A [Butterworth filter](https://en.wikipedia.org/wiki/Butterworth_filter) is used for the yaw and lateral errors used as input of the MPC as well as for -the output steering angle. -Other filtering methods can be considered as long as the noise reduction performances are good -enough. -The moving average filter for example is not suited and can yield worse results than without any -filtering. - -## Inputs / Outputs / API - - - - -The `MPC` class (defined in `mpc.hpp`) provides the interface with the MPC algorithm. -Once a vehicle model, a QP solver, and the reference trajectory to follow have been set -(using `setVehicleModel()`, `setQPSolver()`, `setReferenceTrajectory()`), a lateral control command -can be calculated by providing the current steer, velocity, and pose to function `calculateMPC()`. - -## References / External links - - - -- [1] Jarrod M. Snider, "Automatic Steering Methods for Autonomous Automobile Path Tracking", - Robotics Institute, Carnegie Mellon University, February 2009. - -## Related issues - - - -- -- diff --git a/control/trajectory_follower/design/trajectory_follower-pid-design.md b/control/trajectory_follower/design/trajectory_follower-pid-design.md deleted file mode 100644 index ef94ffcc1eeda..0000000000000 --- a/control/trajectory_follower/design/trajectory_follower-pid-design.md +++ /dev/null @@ -1,95 +0,0 @@ -# PID (Trajectory Follower) - -This is the design document for the PID implemented in the `trajectory_follower` package. - -## Purpose / Use cases - - - - -PID control is used by the `trajectory_follower` -to calculate longitudinal commands corresponding to a velocity and an acceleration. - -## Design - - - - -This PID control calculates the target acceleration from the deviation between the current ego-velocity and the target velocity. - -This PID logic has a maximum value for the output of each term. This is to prevent the following: - -- Large integral terms may cause unintended behavior by users. -- Unintended noise may cause the output of the derivative term to be very large. - -Also, the integral term is not accumulated when the vehicle is stopped. This is to prevent unintended accumulation of the integral term in cases such as "Autoware assumes that the vehicle is engaged, but an external system has locked the vehicle to start. -On the other hand, if the vehicle gets stuck in a depression in the road surface when starting, the vehicle will not start forever, which is currently being addressed by developers. - -At present, PID control is implemented from the viewpoint of trade-off between development/maintenance cost and performance. -This may be replaced by a higher performance controller (adaptive control or robust control) in future development. - -@image html images/trajectory_follower-pid-diagram.svg "PID controller diagram" - -### States - -This module has four state transitions as shown below in order to handle special processing in a specific situation. - -- **DRIVE** - - Executes target velocity tracking by PID control. - - It also applies the delay compensation and slope compensation. -- **STOPPING** - - Controls the motion just before stopping. - - Special sequence is performed to achieve accurate and smooth stopping. -- **STOPPED** - - Performs operations in the stopped state (e.g. brake hold) -- **EMERGENCY**. - - Enters an emergency state when certain conditions are met (e.g., when the vehicle has crossed a certain distance of a stop line). - - The recovery condition (whether or not to keep emergency state until the vehicle completely stops) or the deceleration in the emergency state are defined by parameters. - -The state transition diagram is shown below. - -@image html images/trajectory_follower-pid-states-diagram.svg "State Transitions" - -### Time delay compensation - -At high speeds, the delay of actuator systems such as gas pedals and brakes has a significant impact on driving accuracy. -Depending on the actuating principle of the vehicle, -the mechanism that physically controls the gas pedal and brake typically has a delay of about a hundred millisecond. - -In this controller, -the predicted ego-velocity and the target velocity after the delay time are calculated and used for the feedback to address the time delay problem. - -### Slope compensation - -Based on the slope information, a compensation term is added to the target acceleration. - -There are two sources of the slope information, which can be switched by a parameter. - -- Pitch of the estimated ego-pose (default) - - Calculates the current slope from the pitch angle of the estimated ego-pose - - Pros: Easily available - - Cons: Cannot extract accurate slope information due to the influence of vehicle vibration. -- Z coordinate on the trajectory - - Calculates the road slope from the difference of z-coordinates between the front and rear wheel positions in the target trajectory - - Pros: More accurate than pitch information, if the z-coordinates of the route are properly maintained - - Pros: Can be used in combination with delay compensation (not yet implemented) - - Cons: z-coordinates of high-precision map is needed. - - Cons: Does not support free space planning (for now) - -### Inputs / Outputs / API - - - - -The `PIDController` class is straightforward to use. -First, gains and limits must be set (using `setGains()` and `setLimits()`) for the proportional (P), integral (I), and derivative (D) components. -Then, the velocity can be calculated by providing the current error and time step duration to the `calculate()` function. - -## Related issues - - - -- diff --git a/control/trajectory_follower/package.xml b/control/trajectory_follower/package.xml index 296c7f7f6c59a..559d79cc4ace6 100644 --- a/control/trajectory_follower/package.xml +++ b/control/trajectory_follower/package.xml @@ -4,9 +4,18 @@ trajectory_follower 1.0.0 Library for generating lateral and longitudinal controls following a trajectory - Maxime CLEMENT + + + Takamasa Horibe + + Takayuki Murooka + Apache 2.0 + Takamasa Horibe + Maxime CLEMENT + Takayuki Murooka + ament_cmake_auto autoware_cmake diff --git a/control/trajectory_follower/src/mpc_utils.cpp b/control/trajectory_follower/src/mpc_utils.cpp index a56dc36a007de..6fce85d88827c 100644 --- a/control/trajectory_follower/src/mpc_utils.cpp +++ b/control/trajectory_follower/src/mpc_utils.cpp @@ -100,13 +100,13 @@ bool8_t resampleMPCTrajectoryByDistance( std::vector input_yaw = input.yaw; convertEulerAngleToMonotonic(&input_yaw); - output->x = interpolation::slerp(input_arclength, input.x, output_arclength); - output->y = interpolation::slerp(input_arclength, input.y, output_arclength); - output->z = interpolation::slerp(input_arclength, input.z, output_arclength); - output->yaw = interpolation::slerp(input_arclength, input.yaw, output_arclength); + output->x = interpolation::spline(input_arclength, input.x, output_arclength); + output->y = interpolation::spline(input_arclength, input.y, output_arclength); + output->z = interpolation::spline(input_arclength, input.z, output_arclength); + output->yaw = interpolation::spline(input_arclength, input.yaw, output_arclength); output->vx = interpolation::lerp(input_arclength, input.vx, output_arclength); - output->k = interpolation::slerp(input_arclength, input.k, output_arclength); - output->smooth_k = interpolation::slerp(input_arclength, input.smooth_k, output_arclength); + output->k = interpolation::spline(input_arclength, input.k, output_arclength); + output->smooth_k = interpolation::spline(input_arclength, input.smooth_k, output_arclength); output->relative_time = interpolation::lerp(input_arclength, input.relative_time, output_arclength); diff --git a/control/trajectory_follower_nodes/design/latlon_muxer-design.md b/control/trajectory_follower_nodes/design/latlon_muxer-design.md deleted file mode 100644 index aa5ea71f2bfcb..0000000000000 --- a/control/trajectory_follower_nodes/design/latlon_muxer-design.md +++ /dev/null @@ -1,40 +0,0 @@ -# Lateral/Longitudinal Control Muxer - -## Purpose - -When using controllers that independently compute lateral and longitudinal commands, -this node combines the resulting messages into a single control command message. - -## Design - -Inputs. - -- `AckermannLateralCommand`: lateral command. -- `LongitudinalCommand`: longitudinal command. - -Output. - -- `AckermannControlCommand`: message containing both lateral and longitudinal commands. - -Parameter. - -- `timeout_thr_sec`: duration in second after which input messages are discarded. - -Each time the node receives an input message it publishes an `AckermannControlCommand` -if the following two conditions are met. - -1. Both inputs have been received. -2. The last received input messages are not older than defined by `timeout_thr_sec`. - -## Implementation Details - -Callbacks `latCtrlCmdCallback` and `lonCtrlCmdCallback` are defined for each input message. -Upon reception, the message is stored and function `publishCmd` is called. - -Function `publishCmd` first checks that both messages have been received -and that the stored messages are not older than the timeout. -If both conditions are true, the combined control message is built and published. - -`checkTimeout` is used to check that the stored messages are not too old -by comparing the timeout parameter `timeout_thr_sec` -with the difference between `rclcpp::Time stamp` and the current time `now()`. diff --git a/control/trajectory_follower_nodes/design/media/Controller.drawio.svg b/control/trajectory_follower_nodes/design/media/Controller.drawio.svg new file mode 100644 index 0000000000000..e5c3517233f3f --- /dev/null +++ b/control/trajectory_follower_nodes/design/media/Controller.drawio.svg @@ -0,0 +1,204 @@ + + + + + + + + + Controller + + + + + + + + + + + LongitudinalControllerBase: longitudinal_controller_ + + + + + + LateralControllerBase: lateral_controller_ + + + + + + + + + + LongitudinalControllerBase: longitudinal_c... + + + + + + + + + + LongitudinalControllerBase + + + + + + + + + + PidLongitudinalController + + + + + + + + + + LateralControllerBase + + + + + + + + + + MpcLateralController + + + + + + + + + + PurePursuitLateralController + + + + + + + + + trajectory_follower_nodes/controller_node + + + + + + trajectory_follower_nodes/controller_node + + + + + + + + + + trajectory_follower/ + + lateral_controller_base + + + + + + trajectory_follower/... + + + + + + + + + + trajectory_follower/ + + longitudinal_controller_base + + + + + + trajectory_follower/... + + + + + + + + + + trajectory_follower/ + + mpc_lateral_controller + + + + + + trajectory_follower/... + + + + + + + + + + trajectory_follower/ + + pure_pursuit_lateral_controller + + + + + + trajectory_follower/... + + + + + + + + + + trajectory_follower/ + + pid_longitudinal_controller + + + + + + trajectory_follower/... + + + + + + + + + Viewer does not support full SVG 1.1 + + + + diff --git a/control/trajectory_follower_nodes/design/trajectory_follower-design.md b/control/trajectory_follower_nodes/design/trajectory_follower-design.md index 3a1c8ab2457a5..a7b6361cff856 100644 --- a/control/trajectory_follower_nodes/design/trajectory_follower-design.md +++ b/control/trajectory_follower_nodes/design/trajectory_follower-design.md @@ -6,16 +6,49 @@ Generate control commands to follow a given Trajectory. ## Design -This functionality is decomposed into three nodes. +This is a node of the functionalities implemented in [trajectory_follower](../../trajectory_follower/design/trajectory_follower-design.md#trajectory-follower) package. It has instances of those functionalities, gives them input data to perform calculations, and publishes control commands. -- [lateral-controller-design](lateral_controller-design.md) : generates lateral control messages. -- [longitudinal-controller-design](longitudinal_controller-design.md) : generates longitudinal control messages. -- [latlon-muxer-design](latlon_muxer-design.md) : combines the lateral and longitudinal control commands - into a single control command. +![Controller](media/Controller.drawio.svg) -Core functionalities are implemented in the [trajectory_follower](../../trajectory_follower/design/trajectory_follower-design.md#trajectory-follower) package. +The process flow is as follows. -@image html images/trajectory_follower-diagram.png "Overview of the Trajectory Follower package" +1. Set input data to the lateral controller +2. Compute lateral commands. +3. Sync the result of the lateral control to the longitudinal controller. + - steer angle convergence +4. Set input data to the longitudinal controller +5. Compute longitudinal commands. +6. Sync the result of the longitudinal control to the longitudinal controller. + - velocity convergence(currently not used) + +Giving the longitudinal controller information about steer convergence allows it to control steer when stopped if following parameters are `true` + +- lateral controller + - `keep_steer_control_until_converged` +- longitudinal controller + - `enable_keep_stopped_until_steer_convergence` + +### Inputs / Outputs / API + +#### Inputs + +- `autoware_auto_planning_msgs/Trajectory` : reference trajectory to follow. +- `nav_msgs/Odometry`: current odometry +- `autoware_auto_vehicle_msgs/SteeringReport` current steering + +#### Outputs + +- `autoware_auto_control_msgs/AckermannControlCommand`: message containing both lateral and longitudinal commands. + +#### Parameter + +- `ctrl_period`: control commands publishing period +- `timeout_thr_sec`: duration in second after which input messages are discarded. + - Each time the node receives lateral and longitudinal commands from each controller, it publishes an `AckermannControlCommand` if the following two conditions are met. + 1. Both commands have been received. + 2. The last received commands are not older than defined by `timeout_thr_sec`. +- `lateral_controller_mode`: `mpc_follower` or `pure_pursuit` + - (currently there is only `PID` for longitudinal controller) ## Debugging diff --git a/control/trajectory_follower_nodes/package.xml b/control/trajectory_follower_nodes/package.xml index b3a982b0a6b9b..de184228e5f87 100644 --- a/control/trajectory_follower_nodes/package.xml +++ b/control/trajectory_follower_nodes/package.xml @@ -4,9 +4,18 @@ trajectory_follower_nodes 1.0.0 Nodes to follow a trajectory by generating control commands separated into lateral and longitudinal commands - Maxime CLEMENT + + + Takamasa Horibe + + Takayuki Murooka + Apache License 2.0 + Takamasa Horibe + Maxime CLEMENT + Takayuki Murooka + ament_cmake_auto autoware_cmake diff --git a/control/trajectory_follower_nodes/param/lateral_controller_defaults.param.yaml b/control/trajectory_follower_nodes/param/lateral_controller_defaults.param.yaml index f21ef8f81fa6c..a6d98fa682d74 100644 --- a/control/trajectory_follower_nodes/param/lateral_controller_defaults.param.yaml +++ b/control/trajectory_follower_nodes/param/lateral_controller_defaults.param.yaml @@ -56,7 +56,7 @@ stop_state_entry_ego_speed: 0.001 stop_state_entry_target_speed: 0.001 converged_steer_rad: 0.1 - keep_steer_control_until_converged: false + keep_steer_control_until_converged: true new_traj_duration_time: 1.0 new_traj_end_dist: 0.3 diff --git a/control/trajectory_follower_nodes/param/longitudinal_controller_defaults.param.yaml b/control/trajectory_follower_nodes/param/longitudinal_controller_defaults.param.yaml index e5e82ec101be2..eb2ef443c4576 100644 --- a/control/trajectory_follower_nodes/param/longitudinal_controller_defaults.param.yaml +++ b/control/trajectory_follower_nodes/param/longitudinal_controller_defaults.param.yaml @@ -6,7 +6,7 @@ enable_overshoot_emergency: true enable_large_tracking_error_emergency: true enable_slope_compensation: false - enable_keep_stopped_until_steer_convergence: false + enable_keep_stopped_until_steer_convergence: true # state transition drive_state_stop_dist: 0.5 diff --git a/control/trajectory_follower_nodes/test/test_latlon_muxer_node.cpp b/control/trajectory_follower_nodes/test/test_latlon_muxer_node.cpp deleted file mode 100644 index bb1fd9d3b0bc6..0000000000000 --- a/control/trajectory_follower_nodes/test/test_latlon_muxer_node.cpp +++ /dev/null @@ -1,192 +0,0 @@ -// Copyright 2021 The Autoware Foundation -// -// 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 "fake_test_node/fake_test_node.hpp" -#include "gtest/gtest.h" -#include "rclcpp/rclcpp.hpp" -#include "rclcpp/time.hpp" -#include "trajectory_follower_test_utils.hpp" - -#include - -#include "autoware_auto_control_msgs/msg/ackermann_control_command.hpp" -#include "autoware_auto_control_msgs/msg/ackermann_lateral_command.hpp" -#include "autoware_auto_control_msgs/msg/longitudinal_command.hpp" - -#include -#include -#include - -using LatLonMuxer = autoware::motion::control::trajectory_follower_nodes::LatLonMuxer; -using LateralCommand = autoware_auto_control_msgs::msg::AckermannLateralCommand; -using LongitudinalCommand = autoware_auto_control_msgs::msg::LongitudinalCommand; -using ControlCommand = autoware_auto_control_msgs::msg::AckermannControlCommand; - -using FakeNodeFixture = autoware::tools::testing::FakeTestNode; - -const rclcpp::Duration one_second(1, 0); - -TEST_F(FakeNodeFixture, TestCorrectOutput) -{ - // Data to test - ControlCommand::SharedPtr cmd_msg; - bool received_combined_command = false; - // Node - rclcpp::NodeOptions node_options; - node_options.append_parameter_override("timeout_thr_sec", 0.5); - std::shared_ptr node = std::make_shared(node_options); - // Publisher/Subscribers - rclcpp::Publisher::SharedPtr lat_pub = - this->create_publisher("latlon_muxer/input/lateral/control_cmd"); - rclcpp::Publisher::SharedPtr lon_pub = - this->create_publisher("latlon_muxer/input/longitudinal/control_cmd"); - rclcpp::Subscription::SharedPtr cmd_sub = - this->create_subscription( - "latlon_muxer/output/control_cmd", *node, - [&cmd_msg, &received_combined_command](const ControlCommand::SharedPtr msg) { - cmd_msg = msg; - received_combined_command = true; - }); - // Publish messages - LateralCommand lat_msg; - lat_msg.steering_tire_angle = 1.5; - lat_msg.steering_tire_rotation_rate = 0.2f; - LongitudinalCommand lon_msg; - lon_msg.speed = 5.0; - lon_msg.acceleration = -1.0; - lon_msg.jerk = 0.25; - lat_msg.stamp = node->now(); - lon_msg.stamp = node->now(); - lat_pub->publish(lat_msg); - lon_pub->publish(lon_msg); - - test_utils::waitForMessage(node, this, received_combined_command); - // Ensure the combined control command was published and contains correct data - ASSERT_TRUE(received_combined_command); - EXPECT_EQ(cmd_msg->lateral.steering_tire_angle, lat_msg.steering_tire_angle); - EXPECT_EQ(cmd_msg->lateral.steering_tire_rotation_rate, lat_msg.steering_tire_rotation_rate); - EXPECT_EQ(cmd_msg->longitudinal.speed, lon_msg.speed); - EXPECT_EQ(cmd_msg->longitudinal.acceleration, lon_msg.acceleration); - EXPECT_EQ(cmd_msg->longitudinal.jerk, lon_msg.jerk); - EXPECT_GT(rclcpp::Time(cmd_msg->stamp), rclcpp::Time(lat_msg.stamp)); - EXPECT_GT(rclcpp::Time(cmd_msg->stamp), rclcpp::Time(lon_msg.stamp)); -} - -TEST_F(FakeNodeFixture, TestLateralTimeout) -{ - // Data to test - ControlCommand::SharedPtr cmd_msg; - bool received_combined_command = false; - // Node - rclcpp::NodeOptions node_options; - node_options.append_parameter_override("timeout_thr_sec", 0.5); - std::shared_ptr node = std::make_shared(node_options); - // Publisher/Subscribers - rclcpp::Publisher::SharedPtr lat_pub = - this->create_publisher("latlon_muxer/input/lateral/control_cmd"); - rclcpp::Publisher::SharedPtr lon_pub = - this->create_publisher("latlon_muxer/input/longitudinal/control_cmd"); - rclcpp::Subscription::SharedPtr cmd_sub = - this->create_subscription( - "latlon_muxer/output/control_cmd", *node, - [&cmd_msg, &received_combined_command](const ControlCommand::SharedPtr msg) { - cmd_msg = msg; - received_combined_command = true; - }); - // Publish messages - LateralCommand lat_msg; - LongitudinalCommand lon_msg; - // Generate a timeout of the lateral message - lat_msg.stamp = node->now() - one_second; - lon_msg.stamp = node->now(); - lat_pub->publish(lat_msg); - lon_pub->publish(lon_msg); - - test_utils::waitForMessage( - node, this, received_combined_command, std::chrono::seconds{1LL}, false); - // Ensure combined command was not published - ASSERT_FALSE(received_combined_command); -} - -TEST_F(FakeNodeFixture, TestLongitudinalTimeout) -{ - // Data to test - ControlCommand::SharedPtr cmd_msg; - bool received_combined_command = false; - // Node - rclcpp::NodeOptions node_options; - node_options.append_parameter_override("timeout_thr_sec", 0.5); - std::shared_ptr node = std::make_shared(node_options); - // Publisher/Subscribers - rclcpp::Publisher::SharedPtr lat_pub = - this->create_publisher("latlon_muxer/input/lateral/control_cmd"); - rclcpp::Publisher::SharedPtr lon_pub = - this->create_publisher("latlon_muxer/input/longitudinal/control_cmd"); - rclcpp::Subscription::SharedPtr cmd_sub = - this->create_subscription( - "latlon_muxer/output/control_cmd", *node, - [&cmd_msg, &received_combined_command](const ControlCommand::SharedPtr msg) { - cmd_msg = msg; - received_combined_command = true; - }); - // Publish messages - LateralCommand lat_msg; - LongitudinalCommand lon_msg; - // Generate a timeout of the longitudinal message - lat_msg.stamp = node->now(); - lon_msg.stamp = node->now() - one_second; - lat_pub->publish(lat_msg); - lon_pub->publish(lon_msg); - - test_utils::waitForMessage( - node, this, received_combined_command, std::chrono::seconds{1LL}, false); - // Ensure combined command was not published - ASSERT_FALSE(received_combined_command); -} - -TEST_F(FakeNodeFixture, TestLatlonTimeout) -{ - // Data to test - ControlCommand::SharedPtr cmd_msg; - bool received_combined_command = false; - // Node - rclcpp::NodeOptions node_options; - node_options.append_parameter_override("timeout_thr_sec", 0.5); - std::shared_ptr node = std::make_shared(node_options); - // Publisher/Subscribers - rclcpp::Publisher::SharedPtr lat_pub = - this->create_publisher("latlon_muxer/input/lateral/control_cmd"); - rclcpp::Publisher::SharedPtr lon_pub = - this->create_publisher