diff --git a/control/control_performance_analysis/CMakeLists.txt b/control/control_performance_analysis/CMakeLists.txt
new file mode 100644
index 0000000000000..7adf34bea4eb5
--- /dev/null
+++ b/control/control_performance_analysis/CMakeLists.txt
@@ -0,0 +1,59 @@
+cmake_minimum_required(VERSION 3.5)
+project(control_performance_analysis)
+
+if(NOT CMAKE_CXX_STANDARD)
+ set(CMAKE_CXX_STANDARD 14)
+ set(CMAKE_CXX_STANDARD_REQUIRED ON)
+ set(CMAKE_CXX_EXTENSIONS OFF)
+endif()
+
+if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
+ add_compile_options(-Wall -Wextra -Wpedantic)
+endif()
+
+find_package(ament_cmake_auto REQUIRED)
+ament_auto_find_build_dependencies()
+
+find_package(Boost REQUIRED)
+
+find_package(eigen3_cmake_module REQUIRED)
+find_package(Eigen3 REQUIRED)
+
+rosidl_generate_interfaces(control_performance_analysis_msgs
+ msg/Error.msg
+ msg/ErrorStamped.msg
+ DEPENDENCIES
+ std_msgs
+)
+
+ament_auto_add_library(control_performance_analysis_core SHARED
+ src/control_performance_analysis_utils.cpp
+ src/control_performance_analysis_core.cpp
+)
+
+ament_auto_add_library(control_performance_analysis_node SHARED
+ src/control_performance_analysis_node.cpp
+)
+
+rosidl_target_interfaces(control_performance_analysis_node
+ control_performance_analysis_msgs "rosidl_typesupport_cpp")
+
+target_link_libraries(control_performance_analysis_node
+ control_performance_analysis_core
+)
+
+rclcpp_components_register_node(control_performance_analysis_node
+ PLUGIN "control_performance_analysis::ControlPerformanceAnalysisNode"
+ EXECUTABLE control_performance_analysis
+)
+
+if(BUILD_TESTING)
+ find_package(ament_lint_auto REQUIRED)
+ ament_lint_auto_find_test_dependencies()
+endif()
+
+ament_auto_package(
+ INSTALL_TO_SHARE
+ launch
+ config
+)
diff --git a/control/control_performance_analysis/README.md b/control/control_performance_analysis/README.md
new file mode 100644
index 0000000000000..dbb0405590b50
--- /dev/null
+++ b/control/control_performance_analysis/README.md
@@ -0,0 +1,28 @@
+# control_performance_analysis
+
+## Purpose
+
+`control_performance_analysis` is the package to analyze the tracking performance of a control module.
+
+This package is used as a tool to quantify the results of the control module.
+That's why it doesn't interfere with the core logic of autonomous driving.
+
+Based on the various input from planning, control, and vehicle, it publishes the result of analysis as `control_performance_analysis::msg::ErrorStamped` defined in this package.
+
+## Input / Output
+
+### Input topics
+
+| Name | Type | Description |
+| -------------------------------------------------- | -------------------------------------------------------- | --------------------------------------------------- |
+| `/planning/scenario_planning/trajectory` | autoware_auto_planning_msgs::msg::Trajectory | Output trajectory from planning module. |
+| `/control/trajectory_follower/lateral/control_cmd` | autoware_auto_control_msgs::msg::AckermannLateralCommand | Output lateral control command from control module. |
+| `/vehicle/status/steering_status` | autoware_auto_vehicle_msgs::msg::SteeringReport | Steering information from vehicle. |
+| `/localization/kinematic_state` | nav_msgs::msg::Odometry | Use twist from odometry. |
+| `/tf` | tf2_msgs::msg::TFMessage | Extract ego pose from tf. |
+
+### Output topics
+
+| Name | Type | Description |
+| --------------------------------------- | ----------------------------------------------- | --------------------------------------- |
+| `/control_performance/performance_vars` | control_performance_analysis::msg::ErrorStamped | The result of the performance analysis. |
diff --git a/control/control_performance_analysis/config/control_performance_analysis.param.yaml b/control/control_performance_analysis/config/control_performance_analysis.param.yaml
new file mode 100644
index 0000000000000..9867e170e9adb
--- /dev/null
+++ b/control/control_performance_analysis/config/control_performance_analysis.param.yaml
@@ -0,0 +1,5 @@
+/**:
+ ros__parameters:
+ # -- publishing period --
+ control_period: 0.033
+ double curvature_interval_length_: 5.0
diff --git a/control/control_performance_analysis/config/error_rqt_multiplot.xml b/control/control_performance_analysis/config/error_rqt_multiplot.xml
new file mode 100644
index 0000000000000..ceb9f2cb308e3
--- /dev/null
+++ b/control/control_performance_analysis/config/error_rqt_multiplot.xml
@@ -0,0 +1,853 @@
+
+
+
+ #000000
+ #fce94f
+ false
+ false
+
+
+
+
+
+
+ Untitled Axis
+ 0
+ true
+
+
+ Untitled Axis
+ 0
+ true
+
+
+
+
+
+
+
+ header/seq
+ 1
+
+ 1000
+ 0
+ 0
+ -1000
+ 0
+
+ /control_performance/performance_vars
+ control_performance_analysis/ErrorStamped
+
+
+ error/lateral_error
+ 0
+
+ 1000
+ 0
+ 0
+ -1000
+ 0
+
+ /control_performance/performance_vars
+ control_performance_analysis/ErrorStamped
+
+
+
+ #000000
+ 0
+
+
+ 1000
+ 10
+ 0
+
+
+ 100
+ Lateral Error
+
+
+
+ 30
+ Lateral Error Plot
+
+
+
+
+
+ Untitled Axis
+ 0
+ true
+
+
+ Untitled Axis
+ 0
+ true
+
+
+
+
+
+
+
+
+ 1
+
+ 1000
+ 0
+ 0
+ -1000
+ 0
+
+ /control_performance/performance_vars
+ control_performance_analysis/ErrorStamped
+
+
+ error/error_energy
+ 0
+
+ 1000
+ 0
+ 0
+ -1000
+ 0
+
+ /control_performance/performance_vars
+ control_performance_analysis/ErrorStamped
+
+
+
+ #000000
+ 0
+
+
+ 1000
+ 10
+ 0
+
+
+ 100
+ Error Energy
+
+
+
+ 30
+ Error Energy Plot
+
+
+
+
+
+ Untitled Axis
+ 0
+ true
+
+
+ Untitled Axis
+ 0
+ true
+
+
+
+
+
+
+
+
+ 1
+
+ 1000
+ 0
+ 0
+ -1000
+ 0
+
+ /control_performance/performance_vars
+ control_performance_analysis/ErrorStamped
+
+
+ error/lateral_error_velocity
+ 0
+
+ 1000
+ 0
+ 0
+ -1000
+ 0
+
+ /control_performance/performance_vars
+ control_performance_analysis/ErrorStamped
+
+
+
+ #000000
+ 0
+
+
+ 1000
+ 10
+ 0
+
+
+ 100
+ Lateral Error Speed
+
+
+
+ 30
+ Lateral Error Velocity Plot
+
+
+
+
+
+
+
+ Untitled Axis
+ 0
+ true
+
+
+ Untitled Axis
+ 0
+ true
+
+
+
+
+
+
+
+ error/heading_error
+ 1
+
+ 1000
+ 0
+ 0
+ -1000
+ 0
+
+ /control_performance/performance_vars
+ control_performance_analysis/ErrorStamped
+
+
+ error/heading_error
+ 0
+
+ 1000
+ 0
+ 0
+ -1000
+ 0
+
+ /control_performance/performance_vars
+ control_performance_analysis/ErrorStamped
+
+
+
+ #000000
+ 0
+
+
+ 1000
+ 10
+ 0
+
+
+ 100
+ Heading Angle Tracking Error
+
+
+
+ 30
+ Heading Error Plot
+
+
+
+
+
+ Untitled Axis
+ 0
+ true
+
+
+ Untitled Axis
+ 0
+ true
+
+
+
+
+
+
+
+
+ 1
+
+ 1000
+ 0
+ 0
+ -1000
+ 0
+
+ /control_performance/performance_vars
+ control_performance_analysis/ErrorStamped
+
+
+ error/control_effort_energy
+ 0
+
+ 1000
+ 0
+ 0
+ -1000
+ 0
+
+ /control_performance/performance_vars
+ control_performance_analysis/ErrorStamped
+
+
+
+ #000000
+ 0
+
+
+ 1000
+ 10
+ 0
+
+
+ 100
+ Steering Energy
+
+
+
+ 30
+ Control Energy Plot
+
+
+
+
+
+ Untitled Axis
+ 0
+ true
+
+
+ Untitled Axis
+ 0
+ true
+
+
+
+
+
+
+
+
+ 1
+
+ 1000
+ 0
+ 0
+ -1000
+ 0
+
+ /control_performance/performance_vars
+ control_performance_analysis/ErrorStamped
+
+
+ error/lateral_error_acceleration
+ 0
+
+ 1000
+ 0
+ 0
+ -1000
+ 0
+
+ /control_performance/performance_vars
+ control_performance_analysis/ErrorStamped
+
+
+
+ #000000
+ 0
+
+
+ 1000
+ 10
+ 0
+
+
+ 100
+ Lateral Error Acceleration Approximation
+
+
+
+ 30
+ Lateral Acceleration Plot
+
+
+
+
+
+
+
+ Untitled Axis
+ 0
+ true
+
+
+ Untitled Axis
+ 0
+ true
+
+
+
+
+
+
+
+
+ 1
+
+ 1000
+ 0
+ 0
+ -1000
+ 0
+
+ /control_performance/performance_vars
+ control_performance_analysis/ErrorStamped
+
+
+ error/value_approximation
+ 0
+
+ 1000
+ 0
+ 0
+ -1000
+ 0
+
+ /control_performance/performance_vars
+ control_performance_analysis/ErrorStamped
+
+
+
+ #000000
+ 0
+
+
+ 1000
+ 10
+ 0
+
+
+ 100
+ Value Function
+
+
+
+ 30
+ Approximate Value
+
+
+
+
+
+ Untitled Axis
+ 0
+ true
+
+
+ Untitled Axis
+ 0
+ true
+
+
+
+
+
+
+
+
+ 1
+
+ 1000
+ 0
+ 0
+ -1000
+ 0
+
+ /control_performance/performance_vars
+ control_performance_analysis/ErrorStamped
+
+
+ error/curvature_estimate
+ 0
+
+ 1000
+ 0
+ 0
+ -1000
+ 0
+
+ /control_performance/performance_vars
+ control_performance_analysis/ErrorStamped
+
+
+
+ #000000
+ 0
+
+
+ 1000
+ 10
+ 0
+
+
+ 100
+ Curvature
+
+
+
+ 30
+ Value Decay Rate Plot
+
+
+
+
+
+ Untitled Axis
+ 0
+ true
+
+
+ Untitled Axis
+ 0
+ true
+
+
+
+
+
+
+
+
+ 1
+
+ 1000
+ 0
+ 0
+ -1000
+ 0
+
+ /control/trajectory_follower/control_cmd
+ autoware_control_msgs/ControlCommandStamped
+
+
+ control/steering_angle
+ 0
+
+ 1000
+ 0
+ 0
+ -1000
+ 0
+
+ /control/trajectory_follower/control_cmd
+ autoware_control_msgs/ControlCommandStamped
+
+
+
+ #000000
+ 0
+
+
+ 1000
+ 10
+ 0
+
+
+ 100
+ Computed Steering
+
+
+
+
+
+ 1
+
+ 1000
+ 0
+ 0
+ -1000
+ 0
+
+ /vehicle/status/steering_status
+ autoware_vehicle_msgs/Steering
+
+
+ data
+ 0
+
+ 1000
+ 0
+ 0
+ -1000
+ 0
+
+ /vehicle/status/steering_status
+ autoware_vehicle_msgs/Steering
+
+
+
+ #000000
+ 0
+
+
+ 1000
+ 10
+ 0
+
+
+ 100
+ Measured Vehicle Steering
+
+
+
+ 30
+ Measured and Computed Steerings Plot
+
+
+
+
+
+
+
+ Untitled Axis
+ 0
+ true
+
+
+ Untitled Axis
+ 0
+ true
+
+
+
+
+
+
+
+
+ 1
+
+ 1000
+ 0
+ 0
+ -1000
+ 0
+
+ /vehicle/status/velocity_status
+ geometry_msgs/TwistStamped
+
+
+ twist/linear/x
+ 0
+
+ 1000
+ 0
+ 0
+ -1000
+ 0
+
+ /vehicle/status/velocity_status
+ geometry_msgs/TwistStamped
+
+
+
+ #000000
+ 0
+
+
+ 1000
+ 10
+ 0
+
+
+ 100
+ Velocity Curve
+
+
+
+ 30
+ Longitudinal Velocity Vx Plot
+
+
+
+
+
+ Untitled Axis
+ 0
+ true
+
+
+ Untitled Axis
+ 0
+ true
+
+
+
+
+ 30
+ Untitled Plot
+
+
+
+
+
+ Untitled Axis
+ 0
+ true
+
+
+ Untitled Axis
+ 0
+ true
+
+
+
+
+ 30
+ Untitled Plot
+
+
+
+ false
+
+
diff --git a/control/control_performance_analysis/include/control_performance_analysis/control_performance_analysis_core.hpp b/control/control_performance_analysis/include/control_performance_analysis/control_performance_analysis_core.hpp
new file mode 100644
index 0000000000000..4ffcc63f81473
--- /dev/null
+++ b/control/control_performance_analysis/include/control_performance_analysis/control_performance_analysis_core.hpp
@@ -0,0 +1,104 @@
+// 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.
+
+#ifndef CONTROL_PERFORMANCE_ANALYSIS__CONTROL_PERFORMANCE_ANALYSIS_CORE_HPP_
+#define CONTROL_PERFORMANCE_ANALYSIS__CONTROL_PERFORMANCE_ANALYSIS_CORE_HPP_
+
+#include "control_performance_analysis/control_performance_analysis_utils.hpp"
+
+#include
+
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+
+namespace control_performance_analysis
+{
+using autoware_auto_control_msgs::msg::AckermannLateralCommand;
+using autoware_auto_planning_msgs::msg::Trajectory;
+using geometry_msgs::msg::Pose;
+using geometry_msgs::msg::PoseArray;
+using geometry_msgs::msg::Twist;
+
+struct TargetPerformanceMsgVars
+{
+ double lateral_error;
+ double heading_error;
+ double control_effort_energy;
+ double error_energy;
+ double value_approximation;
+ double curvature_estimate;
+ double curvature_estimate_pp;
+ double lateral_error_velocity;
+ double lateral_error_acceleration;
+};
+
+class ControlPerformanceAnalysisCore
+{
+public:
+ // See https://eigen.tuxfamily.org/dox/group__TopicStructHavingEigenMembers.html
+ EIGEN_MAKE_ALIGNED_OPERATOR_NEW
+ ControlPerformanceAnalysisCore();
+ ControlPerformanceAnalysisCore(double wheelbase, double curvature_interval_length);
+
+ // Setters
+ void setCurrentPose(const Pose & msg);
+ void setCurrentWaypoints(const Trajectory & trajectory);
+ void setCurrentVelocities(const Twist & twist_msg);
+ void setCurrentControlValue(const AckermannLateralCommand & msg);
+ void setInterpolatedPose(Pose & interpolated_pose);
+
+ void findCurveRefIdx();
+ std::pair findClosestPrevWayPointIdx_path_direction();
+ double estimateCurvature();
+ double estimatePurePursuitCurvature();
+
+ // Getters
+ bool isDataReady() const;
+ std::pair getPerformanceVars();
+ Pose getPrevWPPose() const;
+ std::pair calculateClosestPose();
+
+private:
+ double wheelbase_;
+ double curvature_interval_length_;
+
+ // Variables Received Outside
+ std::shared_ptr current_waypoints_ptr_;
+ std::shared_ptr current_vec_pose_ptr_;
+ std::shared_ptr> current_velocities_ptr_; // [Vx, Heading rate]
+ std::shared_ptr current_control_ptr_;
+
+ // Variables computed
+ std::unique_ptr idx_prev_wp_; // the waypoint index, vehicle
+ std::unique_ptr idx_curve_ref_wp_; // index of waypoint corresponds to front axle center
+ std::unique_ptr idx_next_wp_; // the next waypoint index, vehicle heading to
+ std::unique_ptr prev_target_vars_{};
+ std::shared_ptr interpolated_pose_ptr_;
+ // V = xPx' ; Value function from DARE Lyap matrix P
+ Eigen::Matrix2d const lyap_P_ = (Eigen::MatrixXd(2, 2) << 2.342, 8.60, 8.60, 64.29).finished();
+ double const contR{10.0}; // Control weight in LQR
+
+ rclcpp::Logger logger_{rclcpp::get_logger("control_performance_analysis")};
+ rclcpp::Clock clock_{RCL_ROS_TIME};
+};
+} // namespace control_performance_analysis
+
+#endif // CONTROL_PERFORMANCE_ANALYSIS__CONTROL_PERFORMANCE_ANALYSIS_CORE_HPP_
diff --git a/control/control_performance_analysis/include/control_performance_analysis/control_performance_analysis_node.hpp b/control/control_performance_analysis/include/control_performance_analysis/control_performance_analysis_node.hpp
new file mode 100644
index 0000000000000..201d7b2c89b4b
--- /dev/null
+++ b/control/control_performance_analysis/include/control_performance_analysis/control_performance_analysis_node.hpp
@@ -0,0 +1,105 @@
+// 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.
+
+#ifndef CONTROL_PERFORMANCE_ANALYSIS__CONTROL_PERFORMANCE_ANALYSIS_NODE_HPP_
+#define CONTROL_PERFORMANCE_ANALYSIS__CONTROL_PERFORMANCE_ANALYSIS_NODE_HPP_
+
+#include "control_performance_analysis/control_performance_analysis_core.hpp"
+#include "control_performance_analysis/msg/error_stamped.hpp"
+
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+#include
+
+namespace control_performance_analysis
+{
+using autoware_auto_control_msgs::msg::AckermannLateralCommand;
+using autoware_auto_planning_msgs::msg::Trajectory;
+using autoware_auto_vehicle_msgs::msg::SteeringReport;
+using control_performance_analysis::msg::ErrorStamped;
+using geometry_msgs::msg::PoseStamped;
+using nav_msgs::msg::Odometry;
+
+// Parameters Struct
+struct Param
+{
+ // Global parameters
+ double wheel_base;
+ double curvature_interval_length;
+
+ // Control Method Parameters
+ double control_period;
+};
+
+class ControlPerformanceAnalysisNode : public rclcpp::Node
+{
+public:
+ explicit ControlPerformanceAnalysisNode(const rclcpp::NodeOptions & node_options);
+
+private:
+ // Subscribers and Local Variable Assignment
+ rclcpp::Subscription::SharedPtr sub_trajectory_; // subscribe to trajectory
+ rclcpp::Subscription::SharedPtr
+ sub_control_steering_; // subscribe to steering control value
+ rclcpp::Subscription::SharedPtr sub_velocity_; // subscribe to velocity
+ rclcpp::Subscription::SharedPtr sub_vehicle_steering_;
+
+ // Self Pose listener.
+ autoware_utils::SelfPoseListener self_pose_listener_{this}; // subscribe to pose listener.
+
+ // Publishers
+ rclcpp::Publisher::SharedPtr pub_error_msg_; // publish error message
+
+ // Node Methods
+ bool isDataReady() const; // check if data arrive
+ static bool isValidTrajectory(const Trajectory & traj);
+ boost::optional computeTargetPerformanceMsgVars() const;
+
+ // Callback Methods
+ void onTrajectory(const Trajectory::ConstSharedPtr msg);
+ void publishErrorMsg(const TargetPerformanceMsgVars & control_performance_vars);
+ void onControlRaw(const AckermannLateralCommand::ConstSharedPtr control_msg);
+ void onVecSteeringMeasured(const SteeringReport::ConstSharedPtr meas_steer_msg);
+ void onVelocity(const Odometry::ConstSharedPtr msg);
+
+ // Timer - To Publish In Control Period
+ rclcpp::TimerBase::SharedPtr timer_publish_;
+ void onTimer();
+
+ // Parameters
+ Param param_{}; // wheelbase, control period and feedback coefficients.
+ TargetPerformanceMsgVars target_error_vars_{};
+
+ // Subscriber Parameters
+ Trajectory::ConstSharedPtr current_trajectory_ptr_; // ConstPtr to local traj.
+ AckermannLateralCommand::ConstSharedPtr current_control_msg_ptr_;
+ SteeringReport::ConstSharedPtr current_vec_steering_msg_ptr_;
+ Odometry::ConstSharedPtr current_odom_ptr_;
+ PoseStamped::ConstSharedPtr current_pose_; // pose of the vehicle, x, y, heading
+
+ // Algorithm
+ std::unique_ptr control_performance_core_ptr_;
+};
+} // namespace control_performance_analysis
+
+#endif // CONTROL_PERFORMANCE_ANALYSIS__CONTROL_PERFORMANCE_ANALYSIS_NODE_HPP_
diff --git a/control/control_performance_analysis/include/control_performance_analysis/control_performance_analysis_utils.hpp b/control/control_performance_analysis/include/control_performance_analysis/control_performance_analysis_utils.hpp
new file mode 100644
index 0000000000000..de05ea3da63f3
--- /dev/null
+++ b/control/control_performance_analysis/include/control_performance_analysis/control_performance_analysis_utils.hpp
@@ -0,0 +1,106 @@
+// 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.
+
+#ifndef CONTROL_PERFORMANCE_ANALYSIS__CONTROL_PERFORMANCE_ANALYSIS_UTILS_HPP_
+#define CONTROL_PERFORMANCE_ANALYSIS__CONTROL_PERFORMANCE_ANALYSIS_UTILS_HPP_
+
+#include
+#include
+#include
+
+#include
+
+#include
+
+#include
+#include
+
+namespace control_performance_analysis
+{
+namespace utils
+{
+// Right hand sided tangent and normal vectors
+inline std::vector getTangentVector(double yaw_angle)
+{
+ return std::vector{cos(yaw_angle), sin(yaw_angle)};
+}
+
+inline std::vector getNormalVector(double yaw_angle)
+{
+ return std::vector{-sin(yaw_angle), cos(yaw_angle)};
+}
+
+inline double computeLateralError(
+ std::vector & closest_point_position, std::vector & vehicle_position,
+ double & yaw_angle)
+{
+ // Normal vector of vehicle direction
+ std::vector normal_vector = getNormalVector(yaw_angle);
+
+ // Vector to path point originating from the vehicle
+ std::vector vector_to_path_point{
+ closest_point_position[0] - vehicle_position[0],
+ closest_point_position[1] - vehicle_position[1]};
+
+ double lateral_error =
+ normal_vector[0] * vector_to_path_point[0] + normal_vector[1] * vector_to_path_point[1];
+
+ return lateral_error;
+}
+
+/*
+ * Shortest distance between two angles. As the angles are cyclic, interpolation between to
+ * angles must be carried out using the distance value instead of using the end values of
+ * two points.
+ * */
+inline double angleDistance(double & target_angle, double const & reference_angle)
+{
+ double diff = std::fmod(target_angle - reference_angle + M_PI_2, 2 * M_PI) - M_PI_2;
+ double diff_signed_correction = diff < -M_PI_2 ? diff + 2 * M_PI : diff; // Fix sign
+
+ return -1.0 * diff_signed_correction;
+}
+
+inline geometry_msgs::msg::Quaternion createOrientationMsgFromYaw(double yaw_angle)
+{
+ geometry_msgs::msg::Quaternion orientation_msg;
+ double roll_angle = 0.0;
+ double pitch_angle = 0.0;
+
+ tf2::Quaternion quaternion;
+ quaternion.setRPY(roll_angle, pitch_angle, yaw_angle);
+
+ orientation_msg.w = quaternion.getW();
+ orientation_msg.x = quaternion.getX();
+ orientation_msg.y = quaternion.getY();
+ orientation_msg.z = quaternion.getZ();
+
+ return orientation_msg;
+}
+
+// p-points a, b contains [x, y] coordinates.
+double determinant(std::array const & a, std::array const & b);
+
+double triangleArea(
+ std::array const & a, std::array const & b,
+ std::array const & c);
+
+double curvatureFromThreePoints(
+ std::array const & a, std::array const & b,
+ std::array const & c);
+
+} // namespace utils
+} // namespace control_performance_analysis
+
+#endif // CONTROL_PERFORMANCE_ANALYSIS__CONTROL_PERFORMANCE_ANALYSIS_UTILS_HPP_
diff --git a/control/control_performance_analysis/launch/controller_performance_analysis.launch.xml b/control/control_performance_analysis/launch/controller_performance_analysis.launch.xml
new file mode 100644
index 0000000000000..8a248c9e8745a
--- /dev/null
+++ b/control/control_performance_analysis/launch/controller_performance_analysis.launch.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/control/control_performance_analysis/msg/Error.msg b/control/control_performance_analysis/msg/Error.msg
new file mode 100644
index 0000000000000..f63ed2117ddce
--- /dev/null
+++ b/control/control_performance_analysis/msg/Error.msg
@@ -0,0 +1,9 @@
+float64 lateral_error
+float64 heading_error
+float64 control_effort_energy
+float64 error_energy
+float64 value_approximation
+float64 curvature_estimate
+float64 curvature_estimate_pp
+float64 lateral_error_velocity
+float64 lateral_error_acceleration
diff --git a/control/control_performance_analysis/msg/ErrorStamped.msg b/control/control_performance_analysis/msg/ErrorStamped.msg
new file mode 100644
index 0000000000000..8b0b37f653b40
--- /dev/null
+++ b/control/control_performance_analysis/msg/ErrorStamped.msg
@@ -0,0 +1,2 @@
+std_msgs/Header header
+control_performance_analysis/Error error
diff --git a/control/control_performance_analysis/package.xml b/control/control_performance_analysis/package.xml
new file mode 100644
index 0000000000000..bda3b7cce680b
--- /dev/null
+++ b/control/control_performance_analysis/package.xml
@@ -0,0 +1,39 @@
+
+
+
+ control_performance_analysis
+ 0.1.0
+ Controller Performance Evaluation
+ Ali Boyali
+ Apache License 2.0
+
+ ament_cmake_auto
+ rosidl_default_generators
+
+ autoware_auto_control_msgs
+ autoware_auto_planning_msgs
+ autoware_auto_vehicle_msgs
+ autoware_utils
+ geometry_msgs
+ libboost-dev
+ nav_msgs
+ rclcpp
+ sensor_msgs
+ std_msgs
+ tf2
+ tf2_eigen
+ tf2_ros
+ vehicle_info_util
+
+ autoware_global_parameter_loader
+ rosidl_default_runtime
+
+ ament_lint_auto
+ autoware_lint_common
+
+ rosidl_interface_packages
+
+
+ ament_cmake
+
+
diff --git a/control/control_performance_analysis/src/control_performance_analysis_core.cpp b/control/control_performance_analysis/src/control_performance_analysis_core.cpp
new file mode 100644
index 0000000000000..9847b874c8cb2
--- /dev/null
+++ b/control/control_performance_analysis/src/control_performance_analysis_core.cpp
@@ -0,0 +1,519 @@
+// 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 "control_performance_analysis/control_performance_analysis_core.hpp"
+
+#include
+#include
+#include
+#include
+#include
+
+namespace control_performance_analysis
+{
+using geometry_msgs::msg::Quaternion;
+
+ControlPerformanceAnalysisCore::ControlPerformanceAnalysisCore() : wheelbase_{2.74}
+{
+ prev_target_vars_ = std::make_unique(TargetPerformanceMsgVars{});
+ current_velocities_ptr_ = std::make_shared>(2, 0.0);
+}
+
+ControlPerformanceAnalysisCore::ControlPerformanceAnalysisCore(
+ double wheelbase, double curvature_interval_length)
+: wheelbase_{wheelbase}, curvature_interval_length_{curvature_interval_length}
+{
+ // prepare control performance struct
+ prev_target_vars_ = std::make_unique(TargetPerformanceMsgVars{});
+ current_velocities_ptr_ = std::make_shared>(2, 0.0);
+}
+
+void ControlPerformanceAnalysisCore::setCurrentWaypoints(const Trajectory & trajectory)
+{
+ current_waypoints_ptr_ = std::make_shared();
+
+ for (const auto & point : trajectory.points) {
+ current_waypoints_ptr_->poses.emplace_back(point.pose);
+ }
+}
+
+void ControlPerformanceAnalysisCore::setCurrentPose(const Pose & msg)
+{
+ current_vec_pose_ptr_ = std::make_shared(msg);
+}
+
+void ControlPerformanceAnalysisCore::setCurrentControlValue(const AckermannLateralCommand & msg)
+{
+ current_control_ptr_ = std::make_shared(msg);
+}
+
+std::pair ControlPerformanceAnalysisCore::findClosestPrevWayPointIdx_path_direction()
+{
+ if (!isDataReady()) {
+ return std::make_pair(false, std::numeric_limits::quiet_NaN());
+ }
+
+ // How far the next waypoint can be ahead of the vehicle direction.
+ double acceptable_min_distance = 2.0;
+
+ /*
+ * Create Vectors of Path Directions for each interval
+ * interval_vector_xy = {waypoint_1 - waypoint_0}_xy
+ * */
+
+ // Prepare vector of projection distance values; projection of vehicle vectors onto the intervals
+ std::vector projection_distances_ds; //
+
+ auto f_projection_dist = [this](auto pose_1, auto pose_0) {
+ // Vector of intervals.
+ std::vector int_vec{
+ pose_1.position.x - pose_0.position.x, pose_1.position.y - pose_0.position.y};
+
+ // Compute the magnitude of path interval vector
+ double ds_mag = std::hypot(int_vec[0], int_vec[1]);
+
+ // Vector to vehicle from the origin waypoints.
+ std::vector vehicle_vec{
+ this->current_vec_pose_ptr_->position.x - pose_0.position.x,
+ this->current_vec_pose_ptr_->position.y - pose_0.position.y};
+
+ double projection_distance_onto_interval;
+ projection_distance_onto_interval =
+ (int_vec[0] * vehicle_vec[0] + int_vec[1] * vehicle_vec[1]) / ds_mag;
+
+ return projection_distance_onto_interval;
+ };
+
+ // Fill the projection_distances vector.
+ std::transform(
+ current_waypoints_ptr_->poses.cbegin() + 1, current_waypoints_ptr_->poses.cend(),
+ current_waypoints_ptr_->poses.cbegin(), std::back_inserter(projection_distances_ds),
+ f_projection_dist);
+
+ // Lambda function to replace negative numbers with a large number.
+ auto fnc_check_if_negative = [](auto x) {
+ return x < 0 ? std::numeric_limits::max() : x;
+ };
+
+ std::vector projections_distances_all_positive;
+ std::transform(
+ projection_distances_ds.cbegin(), projection_distances_ds.cend(),
+ std::back_inserter(projections_distances_all_positive), fnc_check_if_negative);
+
+ // Minimum of all positive distances and the index of the next waypoint.
+ auto it = std::min_element(
+ projections_distances_all_positive.cbegin(), projections_distances_all_positive.cend());
+
+ // Extract the location of iterator idx and store in the class.
+ int32_t && temp_idx_prev_wp_ = std::distance(projections_distances_all_positive.cbegin(), it);
+ idx_prev_wp_ = std::make_unique(temp_idx_prev_wp_);
+
+ // Distance of next waypoint to the vehicle, for anomaly detection.
+ double min_distance_ds = projections_distances_all_positive[*idx_prev_wp_];
+ int32_t length_of_trajectory =
+ std::distance(current_waypoints_ptr_->poses.cbegin(), current_waypoints_ptr_->poses.cend());
+
+ // Find and set the waypoint L-wheelbase meters ahead of the current waypoint.
+ findCurveRefIdx();
+
+ return ((min_distance_ds <= acceptable_min_distance) & (*idx_prev_wp_ >= 0) &
+ (*idx_prev_wp_ <= length_of_trajectory))
+ ? std::make_pair(true, *idx_prev_wp_)
+ : std::make_pair(false, std::numeric_limits::quiet_NaN());
+}
+
+bool ControlPerformanceAnalysisCore::isDataReady() const
+{
+ rclcpp::Clock clock{RCL_ROS_TIME};
+ if (!current_vec_pose_ptr_) {
+ RCLCPP_WARN_THROTTLE(
+ logger_, clock, 1000, "cannot get current pose into control_performance algorithm");
+ return false;
+ }
+
+ if (!current_waypoints_ptr_) {
+ RCLCPP_WARN_THROTTLE(logger_, clock, 1000, "cannot get current trajectory waypoints ...");
+ return false;
+ }
+
+ if (!current_velocities_ptr_) {
+ RCLCPP_WARN_THROTTLE(logger_, clock, 1000, "waiting for current_velocity ...");
+ return false;
+ }
+
+ if (!current_control_ptr_) {
+ RCLCPP_WARN_THROTTLE(logger_, clock, 1000, "waiting for current_steering ...");
+ return false;
+ }
+
+ return true;
+}
+
+std::pair ControlPerformanceAnalysisCore::getPerformanceVars()
+{
+ constexpr double EPS = std::numeric_limits::epsilon();
+
+ // Check if data is ready.
+ if (!isDataReady() || !idx_prev_wp_) {
+ return std::make_pair(false, TargetPerformanceMsgVars{});
+ }
+
+ TargetPerformanceMsgVars target_vars{};
+
+ // Get the interpolated pose
+ std::pair pair_pose_interp_wp_ = calculateClosestPose();
+
+ if (!pair_pose_interp_wp_.first || !interpolated_pose_ptr_) {
+ RCLCPP_WARN_THROTTLE(
+ logger_, clock_, 1000, "Cannot get interpolated pose into control_performance algorithm");
+
+ return std::make_pair(false, TargetPerformanceMsgVars{});
+ }
+
+ auto && pose_interp_wp_ = pair_pose_interp_wp_.second;
+
+ // Create interpolated waypoint vector
+ std::vector interp_waypoint_xy{pose_interp_wp_.position.x, pose_interp_wp_.position.y};
+
+ // Create vehicle position vector
+ std::vector vehicle_position_xy{
+ current_vec_pose_ptr_->position.x, current_vec_pose_ptr_->position.y};
+
+ // Get Yaw angles of the reference waypoint and the vehicle
+ double const target_yaw = tf2::getYaw(pose_interp_wp_.orientation);
+ double vehicle_yaw_angle = tf2::getYaw(current_vec_pose_ptr_->orientation);
+
+ // Compute Curvature at the point where the front axle might follow
+ // get the waypoint corresponds to the front_axle center
+
+ if (!idx_curve_ref_wp_) {
+ RCLCPP_ERROR(logger_, "Cannot find index of curvature reference waypoint ");
+ return std::make_pair(false, TargetPerformanceMsgVars{});
+ }
+
+ double curvature_est = estimateCurvature(); // three point curvature
+ double curvature_est_pp = estimatePurePursuitCurvature(); // pure pursuit curvature
+
+ // Compute lateral error - projection of vector to waypoint from vehicle onto the vehicle normal.
+ double && lateral_error =
+ utils::computeLateralError(interp_waypoint_xy, vehicle_position_xy, vehicle_yaw_angle);
+
+ // Compute the yaw angle error.
+ double && heading_yaw_error = utils::angleDistance(vehicle_yaw_angle, target_yaw);
+
+ // Set the values of TargetPerformanceMsgVars.
+ target_vars.lateral_error = lateral_error;
+ target_vars.heading_error = heading_yaw_error;
+
+ double steering_val = current_control_ptr_->steering_tire_angle;
+ target_vars.control_effort_energy = contR * steering_val * steering_val; // u*R*u';
+
+ Eigen::Vector2d error_vec;
+ error_vec << lateral_error, heading_yaw_error;
+
+ target_vars.error_energy = error_vec.dot(error_vec);
+ target_vars.value_approximation = error_vec.transpose() * lyap_P_ * error_vec; // x'Px
+
+ double prev_value_approximation = prev_target_vars_->value_approximation;
+ double && value_decay = target_vars.value_approximation - prev_value_approximation;
+
+ target_vars.curvature_estimate = curvature_est;
+ target_vars.curvature_estimate_pp = curvature_est_pp;
+
+ double Vx = current_velocities_ptr_->at(0);
+ target_vars.lateral_error_velocity = Vx * sin(heading_yaw_error);
+ target_vars.lateral_error_acceleration = Vx * tan(steering_val) / wheelbase_ - curvature_est * Vx;
+
+ prev_target_vars_ = std::move(std::make_unique(target_vars));
+
+ return std::make_pair(true, target_vars);
+}
+
+Pose ControlPerformanceAnalysisCore::getPrevWPPose() const
+{
+ Pose pose_ref_waypoint_ = current_waypoints_ptr_->poses.at(*idx_prev_wp_);
+ return pose_ref_waypoint_;
+}
+void ControlPerformanceAnalysisCore::setCurrentVelocities(const Twist & twist_msg)
+{
+ current_velocities_ptr_->at(0) = twist_msg.linear.x;
+ current_velocities_ptr_->at(1) = twist_msg.angular.z;
+}
+void ControlPerformanceAnalysisCore::findCurveRefIdx()
+{
+ // Get the previous waypoint as the reference
+ if (!interpolated_pose_ptr_) {
+ RCLCPP_WARN_THROTTLE(
+ logger_, clock_, 1000, "Cannot set the curvature_idx, no valid interpolated pose ...");
+
+ return;
+ }
+
+ auto fun_distance_cond = [this](auto pose_t) {
+ double dist = std::hypot(
+ pose_t.position.x - this->interpolated_pose_ptr_->position.x,
+ pose_t.position.y - this->interpolated_pose_ptr_->position.y);
+
+ return dist > wheelbase_;
+ };
+
+ auto it = std::find_if(
+ current_waypoints_ptr_->poses.cbegin() + *idx_prev_wp_, current_waypoints_ptr_->poses.cend(),
+ fun_distance_cond);
+
+ if (it == current_waypoints_ptr_->poses.cend()) {
+ it = std::prev(it);
+ }
+
+ int32_t && temp_idx_curve_ref_wp = std::distance(current_waypoints_ptr_->poses.cbegin(), it);
+ idx_curve_ref_wp_ = std::make_unique(temp_idx_curve_ref_wp);
+}
+
+std::pair ControlPerformanceAnalysisCore::calculateClosestPose()
+{
+ Pose interpolated_pose;
+
+ // Get index of prev waypoint and sanity check.
+ if (!idx_prev_wp_) {
+ RCLCPP_ERROR(logger_, "Cannot find the next waypoint.");
+ return std::make_pair(false, interpolated_pose);
+ }
+
+ // Define the next waypoint - so that we can define an interval in which the car follow a line.
+ int32_t idx_next_wp_temp;
+ int32_t total_num_of_waypoints_in_traj =
+ std::distance(current_waypoints_ptr_->poses.cbegin(), current_waypoints_ptr_->poses.cend());
+
+ if (*idx_prev_wp_ < total_num_of_waypoints_in_traj - 1) {
+ idx_next_wp_temp = *idx_prev_wp_ + 1;
+
+ } else {
+ idx_next_wp_temp = total_num_of_waypoints_in_traj - 1;
+ }
+
+ idx_next_wp_ = std::make_unique(idx_next_wp_temp);
+
+ /*
+ * Create two vectors originating from the previous waypoints to the next waypoint and the
+ * vehicle position and find projection of vehicle vector on the the trajectory section,
+ *
+ * */
+
+ // First get te yaw angles of all three poses.
+ double prev_yaw = tf2::getYaw(current_waypoints_ptr_->poses.at(*idx_prev_wp_).orientation);
+ double next_yaw = tf2::getYaw(current_waypoints_ptr_->poses.at(*idx_next_wp_).orientation);
+
+ // Previous waypoint to next waypoint.
+ double dx_prev2next = current_waypoints_ptr_->poses.at(*idx_next_wp_).position.x -
+ current_waypoints_ptr_->poses.at(*idx_prev_wp_).position.x;
+
+ double dy_prev2next = current_waypoints_ptr_->poses.at(*idx_next_wp_).position.y -
+ current_waypoints_ptr_->poses.at(*idx_prev_wp_).position.y;
+
+ double delta_psi_prev2next = utils::angleDistance(next_yaw, prev_yaw);
+
+ // Create a vector from p0 (prev) --> p1 (to next wp)
+ std::vector v_prev2next_wp{dx_prev2next, dy_prev2next};
+
+ // Previous waypoint to the vehicle pose
+ /*
+ * p0:previous waypoint ----> p1 next waypoint
+ * vector = p1 - p0. We project vehicle vector on this interval to
+ *
+ * */
+
+ double dx_prev2vehicle =
+ current_vec_pose_ptr_->position.x - current_waypoints_ptr_->poses.at(*idx_prev_wp_).position.x;
+
+ double dy_prev2vehicle =
+ current_vec_pose_ptr_->position.y - current_waypoints_ptr_->poses.at(*idx_prev_wp_).position.y;
+
+ // Vector from p0 to p_vehicle
+ std::vector v_prev2vehicle{dx_prev2vehicle, dy_prev2vehicle};
+
+ // Compute the length of v_prev2next_wp : vector from p0 --> p1
+ double distance_p02p1 = std::hypot(dx_prev2next, dy_prev2next);
+
+ // Compute how far the car is away from p0 in p1 direction. p_interp is the location of the
+ // interpolated waypoint. This is the dot product normalized by the length of the interval.
+ // a.b = |a|.|b|.cos(alpha) -- > |a|.cos(alpha) = a.b / |b| where b is the path interval,
+
+ double distance_p02p_interp =
+ (dx_prev2next * dx_prev2vehicle + dy_prev2next * dy_prev2vehicle) / distance_p02p1;
+
+ /*
+ * We use the following linear interpolation
+ * pi = p0 + ratio_t * (p1 - p0)
+ * */
+
+ double ratio_t = distance_p02p_interp / distance_p02p1;
+
+ // Interpolate pose.position and pose.orientation
+ interpolated_pose.position.x =
+ current_waypoints_ptr_->poses.at(*idx_prev_wp_).position.x + ratio_t * dx_prev2next;
+
+ interpolated_pose.position.y =
+ current_waypoints_ptr_->poses.at(*idx_prev_wp_).position.y + ratio_t * dy_prev2next;
+
+ interpolated_pose.position.z = 0.0;
+
+ // Interpolate the yaw angle of pi : interpolated waypoint
+ double interp_yaw_angle = prev_yaw + ratio_t * delta_psi_prev2next;
+
+ Quaternion orient_msg = utils::createOrientationMsgFromYaw(interp_yaw_angle);
+ interpolated_pose.orientation = orient_msg;
+
+ setInterpolatedPose(interpolated_pose);
+
+ return std::make_pair(true, interpolated_pose);
+}
+
+// Sets interpolated waypoint_ptr_.
+void ControlPerformanceAnalysisCore::setInterpolatedPose(Pose & interpolated_pose)
+{
+ interpolated_pose_ptr_ = std::make_shared(interpolated_pose);
+}
+
+double ControlPerformanceAnalysisCore::estimateCurvature()
+{
+ // Get idx of front-axle center reference point on the trajectory.
+ // get the waypoint corresponds to the front_axle center.
+ Pose front_axleWP_pose = current_waypoints_ptr_->poses.at(*idx_curve_ref_wp_);
+
+ // for guarding -1 in finding previous waypoint for the front axle
+ int32_t idx_prev_waypoint = *idx_curve_ref_wp_ >= 1 ? *idx_curve_ref_wp_ - 1 : *idx_curve_ref_wp_;
+
+ Pose front_axleWP_pose_prev = current_waypoints_ptr_->poses.at(idx_prev_waypoint);
+
+ // Compute arc-length ds between 2 points.
+ double ds_arc_length = std::hypot(
+ front_axleWP_pose_prev.position.x - front_axleWP_pose.position.x,
+ front_axleWP_pose_prev.position.y - front_axleWP_pose.position.y);
+
+ auto distance_to_traj0 = idx_prev_waypoint * ds_arc_length;
+
+ // Define waypoints 10 meters behind the rear axle if exist.
+ // If not exist, we will take the first point of the
+ // curvature triangle as the start point of the trajectory.
+ auto && num_of_back_indices = std::round(curvature_interval_length_ / ds_arc_length);
+ int32_t loc_of_back_idx =
+ (*idx_curve_ref_wp_ - num_of_back_indices < 0) ? 0 : *idx_curve_ref_wp_ - num_of_back_indices;
+
+ // Define location of forward point 10 meters ahead of the front axle on curve.
+ uint32_t max_idx =
+ std::distance(current_waypoints_ptr_->poses.cbegin(), current_waypoints_ptr_->poses.cend());
+
+ auto num_of_forward_indices = num_of_back_indices;
+ int32_t loc_of_forward_idx = (*idx_curve_ref_wp_ + num_of_forward_indices > max_idx)
+ ? max_idx - 1
+ : *idx_curve_ref_wp_ + num_of_forward_indices - 1;
+
+ // We have three indices of the three trajectory poses.
+ // We compute a curvature estimate from these points.
+
+ std::array a_coord{
+ current_waypoints_ptr_->poses.at(loc_of_back_idx).position.x,
+ current_waypoints_ptr_->poses.at(loc_of_back_idx).position.y};
+
+ std::array b_coord{
+ current_waypoints_ptr_->poses.at(*idx_curve_ref_wp_).position.x,
+ current_waypoints_ptr_->poses.at(*idx_curve_ref_wp_).position.y};
+
+ std::array c_coord{
+ current_waypoints_ptr_->poses.at(loc_of_forward_idx).position.x,
+ current_waypoints_ptr_->poses.at(loc_of_forward_idx).position.y};
+
+ double estimated_curvature = utils::curvatureFromThreePoints(a_coord, b_coord, c_coord);
+
+ return estimated_curvature;
+}
+
+double ControlPerformanceAnalysisCore::estimatePurePursuitCurvature()
+{
+ if (!interpolated_pose_ptr_) {
+ RCLCPP_WARN_THROTTLE(
+ logger_, clock_, 1000,
+ "Cannot set pure pursuit_target_point_idx, no valid interpolated pose ...");
+
+ return 0;
+ }
+
+ double Vx = current_velocities_ptr_->at(0);
+ double look_ahead_distance_pp = std::max(wheelbase_, 2 * Vx);
+
+ auto fun_distance_cond = [this, &look_ahead_distance_pp](auto pose_t) {
+ double dist = std::hypot(
+ pose_t.position.x - this->interpolated_pose_ptr_->position.x,
+ pose_t.position.y - this->interpolated_pose_ptr_->position.y);
+
+ return dist > look_ahead_distance_pp;
+ };
+
+ auto it = std::find_if(
+ current_waypoints_ptr_->poses.cbegin() + *idx_prev_wp_, current_waypoints_ptr_->poses.cend(),
+ fun_distance_cond);
+
+ Pose target_pose_pp;
+
+ // If there is no waypoint left on the trajectory, interpolate one.
+ if (it == current_waypoints_ptr_->poses.cend()) {
+ // Interpolate a waypoint.
+ it = std::prev(it);
+ int32_t && temp_idx_pp = std::distance(current_waypoints_ptr_->poses.cbegin(), it);
+ Pose const & last_pose_on_traj = current_waypoints_ptr_->poses.at(temp_idx_pp);
+
+ // get the yaw angle of the last traj point.
+ double const && yaw_pp = tf2::getYaw(last_pose_on_traj.orientation);
+
+ // get unit tangent in this direction.
+ std::vector unit_tangent = utils::getTangentVector(yaw_pp);
+
+ target_pose_pp.position.z = 0;
+ target_pose_pp.position.x =
+ last_pose_on_traj.position.x + look_ahead_distance_pp * unit_tangent[0];
+ target_pose_pp.position.y =
+ last_pose_on_traj.position.y + look_ahead_distance_pp * unit_tangent[1];
+
+ target_pose_pp.orientation = last_pose_on_traj.orientation;
+ } else {
+ // idx of the last waypoint on the trajectory is
+ int32_t && temp_idx_pp = std::distance(current_waypoints_ptr_->poses.cbegin(), it);
+ Pose const & last_pose_on_traj = current_waypoints_ptr_->poses.at(temp_idx_pp);
+
+ target_pose_pp.position.z = last_pose_on_traj.position.z;
+ target_pose_pp.position.x = last_pose_on_traj.position.x;
+ target_pose_pp.position.y = last_pose_on_traj.position.y;
+ target_pose_pp.orientation = last_pose_on_traj.orientation;
+ }
+
+ // We have target pose for the pure pursuit.
+ // Find projection of target vector from vehicle.
+
+ std::vector vec_to_target{
+ target_pose_pp.position.x - current_vec_pose_ptr_->position.x,
+ target_pose_pp.position.y - current_vec_pose_ptr_->position.y};
+
+ double const && current_vec_yaw = tf2::getYaw(current_vec_pose_ptr_->orientation);
+ std::vector normal_vec = utils::getNormalVector(current_vec_yaw); // ClockWise
+
+ // Project this vector on the vehicle normal vector.
+ double x_pure_pursuit = vec_to_target[0] * normal_vec[0] + vec_to_target[1] * normal_vec[1];
+
+ // Pure pursuit curvature.
+ double curvature_pure_pursuit =
+ 2 * x_pure_pursuit / (look_ahead_distance_pp * look_ahead_distance_pp);
+
+ return curvature_pure_pursuit;
+}
+} // namespace control_performance_analysis
diff --git a/control/control_performance_analysis/src/control_performance_analysis_node.cpp b/control/control_performance_analysis/src/control_performance_analysis_node.cpp
new file mode 100644
index 0000000000000..2a48ca4971ce9
--- /dev/null
+++ b/control/control_performance_analysis/src/control_performance_analysis_node.cpp
@@ -0,0 +1,259 @@
+// 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 "control_performance_analysis/control_performance_analysis_node.hpp"
+
+#include "control_performance_analysis/msg/error_stamped.hpp"
+
+#include
+
+#include
+#include
+
+namespace
+{
+using control_performance_analysis::TargetPerformanceMsgVars;
+using control_performance_analysis::msg::ErrorStamped;
+
+ErrorStamped createPerformanceMsgVars(const TargetPerformanceMsgVars & target_performance_vars)
+{
+ ErrorStamped error_msgs{};
+
+ error_msgs.error.lateral_error = target_performance_vars.lateral_error;
+ error_msgs.error.heading_error = target_performance_vars.heading_error;
+ error_msgs.error.control_effort_energy = target_performance_vars.control_effort_energy;
+ error_msgs.error.error_energy = target_performance_vars.error_energy;
+ error_msgs.error.value_approximation = target_performance_vars.value_approximation;
+ error_msgs.error.curvature_estimate = target_performance_vars.curvature_estimate;
+ error_msgs.error.curvature_estimate_pp = target_performance_vars.curvature_estimate_pp;
+ error_msgs.error.lateral_error_velocity = target_performance_vars.lateral_error_velocity;
+ error_msgs.error.lateral_error_acceleration = target_performance_vars.lateral_error_acceleration;
+
+ return error_msgs;
+}
+} // namespace
+
+namespace control_performance_analysis
+{
+using vehicle_info_util::VehicleInfoUtil;
+
+ControlPerformanceAnalysisNode::ControlPerformanceAnalysisNode(
+ const rclcpp::NodeOptions & node_options)
+: Node("control_performance_analysis", node_options)
+{
+ using std::placeholders::_1;
+
+ // Implement Reading Global and Local Variables.
+ const auto vehicle_info = VehicleInfoUtil(*this).getVehicleInfo();
+ param_.wheel_base = vehicle_info.wheel_base_m;
+
+ // Node Parameters.
+ param_.control_period = declare_parameter("control_period", 0.033);
+ param_.curvature_interval_length = declare_parameter("curvature_interval_length", 10.0);
+
+ // Prepare error computation class with the wheelbase parameter.
+ control_performance_core_ptr_ = std::make_unique(
+ param_.wheel_base, param_.curvature_interval_length);
+
+ // Subscribers.
+ sub_trajectory_ = create_subscription(
+ "~/input/reference_trajectory", 1,
+ std::bind(&ControlPerformanceAnalysisNode::onTrajectory, this, _1));
+
+ sub_control_steering_ = create_subscription(
+ "~/input/control_raw", 1, std::bind(&ControlPerformanceAnalysisNode::onControlRaw, this, _1));
+
+ sub_vehicle_steering_ = create_subscription(
+ "~/input/measured_steering", 1,
+ std::bind(&ControlPerformanceAnalysisNode::onVecSteeringMeasured, this, _1));
+
+ sub_velocity_ = create_subscription(
+ "~/input/odometry", 1, std::bind(&ControlPerformanceAnalysisNode::onVelocity, this, _1));
+
+ // Publishers
+ pub_error_msg_ = create_publisher("~/output/error_stamped", 1);
+
+ // Timer
+ {
+ auto on_timer = std::bind(&ControlPerformanceAnalysisNode::onTimer, this);
+ auto period = std::chrono::duration_cast(
+ std::chrono::duration(param_.control_period));
+ timer_publish_ = std::make_shared>(
+ this->get_clock(), period, std::move(on_timer),
+ this->get_node_base_interface()->get_context());
+ this->get_node_timers_interface()->add_timer(timer_publish_, nullptr);
+ }
+
+ // Wait for first self pose
+ self_pose_listener_.waitForFirstPose();
+}
+
+void ControlPerformanceAnalysisNode::onTrajectory(const Trajectory::ConstSharedPtr msg)
+{
+ if (msg->points.size() < 3) {
+ RCLCPP_DEBUG(get_logger(), "received path size < 3, is not sufficient.");
+ return;
+ }
+
+ if (!isValidTrajectory(*msg)) {
+ RCLCPP_ERROR(get_logger(), "Trajectory is invalid!, stop computing.");
+ return;
+ }
+
+ current_trajectory_ptr_ = msg;
+}
+
+void ControlPerformanceAnalysisNode::onControlRaw(
+ const AckermannLateralCommand::ConstSharedPtr control_msg)
+{
+ if (!control_msg) {
+ RCLCPP_ERROR(get_logger(), "steering signal has not been received yet ...");
+ return;
+ }
+ current_control_msg_ptr_ = control_msg;
+}
+
+void ControlPerformanceAnalysisNode::onVecSteeringMeasured(
+ const SteeringReport::ConstSharedPtr meas_steer_msg)
+{
+ if (!meas_steer_msg) {
+ RCLCPP_WARN_THROTTLE(
+ get_logger(), *get_clock(), 1000, "waiting for vehicle measured steering message ...");
+ return;
+ }
+ current_vec_steering_msg_ptr_ = meas_steer_msg;
+}
+
+void ControlPerformanceAnalysisNode::onVelocity(const Odometry::ConstSharedPtr msg)
+{
+ current_odom_ptr_ = msg;
+}
+
+void ControlPerformanceAnalysisNode::onTimer()
+{
+ // Read and Update Current Pose updating var:current_pose_.
+ current_pose_ = self_pose_listener_.getCurrentPose();
+
+ // Check Data Stream
+ if (!isDataReady()) {
+ // Publish Here
+ return;
+ }
+
+ // Compute Control Performance Variables.
+ auto performanceVars = computeTargetPerformanceMsgVars();
+ if (!performanceVars) {
+ RCLCPP_ERROR(get_logger(), "steering signal has not been received yet ...");
+ return;
+ }
+
+ // If successful publish.
+ publishErrorMsg(*performanceVars);
+}
+
+void ControlPerformanceAnalysisNode::publishErrorMsg(
+ const TargetPerformanceMsgVars & control_performance_vars)
+{
+ control_performance_analysis::ErrorStamped error_msgs =
+ createPerformanceMsgVars(control_performance_vars);
+
+ pub_error_msg_->publish(error_msgs);
+}
+
+bool ControlPerformanceAnalysisNode::isDataReady() const
+{
+ rclcpp::Clock clock{RCL_ROS_TIME};
+ if (!current_pose_) {
+ RCLCPP_WARN_THROTTLE(get_logger(), clock, 1000, "waiting for current_pose ...");
+ return false;
+ }
+
+ if (!current_trajectory_ptr_) {
+ RCLCPP_WARN_THROTTLE(get_logger(), clock, 1000, "waiting for trajectory ... ");
+ return false;
+ }
+
+ if (!current_odom_ptr_) {
+ RCLCPP_WARN_THROTTLE(get_logger(), clock, 1000, "waiting for current_odom ...");
+ return false;
+ }
+
+ if (!current_control_msg_ptr_) {
+ RCLCPP_WARN_THROTTLE(get_logger(), clock, 1000, "waiting for current_control_steering_val ...");
+ return false;
+ }
+
+ return true;
+}
+
+/*
+ * - Pass trajectory and current pose to control_performance_analysis -> setCurrentPose()
+ * -> setWayPoints()
+ * -> findClosestPoint
+ * -> computePerformanceVars
+ * */
+
+boost::optional
+ControlPerformanceAnalysisNode::computeTargetPerformanceMsgVars() const
+{
+ // Set trajectory and current pose of controller_performance_core.
+ control_performance_core_ptr_->setCurrentWaypoints(*current_trajectory_ptr_);
+ control_performance_core_ptr_->setCurrentPose(current_pose_->pose);
+ control_performance_core_ptr_->setCurrentVelocities(current_odom_ptr_->twist.twist);
+ control_performance_core_ptr_->setCurrentControlValue(*current_control_msg_ptr_);
+
+ // Find the index of the next waypoint.
+ std::pair prev_closest_wp_pose_idx =
+ control_performance_core_ptr_->findClosestPrevWayPointIdx_path_direction();
+
+ if (!prev_closest_wp_pose_idx.first) {
+ RCLCPP_ERROR(get_logger(), "Cannot find closest waypoint");
+ return {};
+ }
+
+ // Compute control performance values.
+ const std::pair target_performance_vars =
+ control_performance_core_ptr_->getPerformanceVars();
+
+ if (!target_performance_vars.first) {
+ RCLCPP_ERROR(get_logger(), "Cannot compute control performance vars ...");
+ return {};
+ }
+
+ return target_performance_vars.second;
+}
+
+bool ControlPerformanceAnalysisNode::isValidTrajectory(const Trajectory & traj)
+{
+ bool check_condition = std::all_of(traj.points.cbegin(), traj.points.cend(), [](auto point) {
+ const auto & p = point.pose.position;
+ const auto & o = point.pose.orientation;
+ const auto & t = point.longitudinal_velocity_mps;
+ const auto & a = point.acceleration_mps2;
+
+ if (
+ !isfinite(p.x) || !isfinite(p.y) || !isfinite(p.z) || !isfinite(o.x) || !isfinite(o.y) ||
+ !isfinite(o.z) || !isfinite(o.w) || !isfinite(t) || !isfinite(a)) {
+ return false;
+ } else {
+ return true;
+ }
+ });
+
+ return check_condition;
+}
+} // namespace control_performance_analysis
+
+#include
+RCLCPP_COMPONENTS_REGISTER_NODE(control_performance_analysis::ControlPerformanceAnalysisNode)
diff --git a/control/control_performance_analysis/src/control_performance_analysis_utils.cpp b/control/control_performance_analysis/src/control_performance_analysis_utils.cpp
new file mode 100644
index 0000000000000..ba4ae2d194701
--- /dev/null
+++ b/control/control_performance_analysis/src/control_performance_analysis_utils.cpp
@@ -0,0 +1,52 @@
+// 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 "control_performance_analysis/control_performance_analysis_utils.hpp"
+
+#include
+
+namespace control_performance_analysis
+{
+namespace utils
+{
+double determinant(std::array const & a, std::array const & b)
+{
+ return a[0] * b[1] - b[0] * a[1];
+}
+
+double triangleArea(
+ std::array const & a, std::array const & b, std::array const & c)
+{
+ double m1 = determinant(a, b);
+ double m2 = determinant(b, c);
+ double m3 = determinant(c, a);
+
+ return 0.5 * (m1 + m2 + m3);
+}
+
+double curvatureFromThreePoints(
+ std::array const & a, std::array const & b, std::array const & c)
+{
+ double area = triangleArea(a, b, c);
+
+ double amag = std::hypot(a[0] - b[0], a[1] - b[1]); // magnitude of triangle edges
+ double bmag = std::hypot(b[0] - c[0], b[1] - c[1]);
+ double cmag = std::hypot(c[0] - a[0], c[1] - a[1]);
+
+ double curvature = 4 * area / std::max(amag * bmag * cmag, 1e-4);
+
+ return curvature;
+}
+} // namespace utils
+} // namespace control_performance_analysis