Skip to content

Commit e07c3d4

Browse files
kosuke55danielsanchezaran
authored andcommitted
feat(control_evaluator): add deviation metrics and queue for diagnostics (autowarefoundation#7484)
1 parent fdf3d47 commit e07c3d4

File tree

8 files changed

+255
-31
lines changed

8 files changed

+255
-31
lines changed

evaluator/control_evaluator/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ find_package(pluginlib REQUIRED)
88

99
ament_auto_add_library(${PROJECT_NAME}_node SHARED
1010
src/${PROJECT_NAME}_node.cpp
11+
src/metrics/deviation_metrics.cpp
1112
)
1213

1314
rclcpp_components_register_node(${PROJECT_NAME}_node

evaluator/control_evaluator/include/control_evaluator/control_evaluator_node.hpp

+40-7
Original file line numberDiff line numberDiff line change
@@ -15,21 +15,30 @@
1515
#ifndef CONTROL_EVALUATOR__CONTROL_EVALUATOR_NODE_HPP_
1616
#define CONTROL_EVALUATOR__CONTROL_EVALUATOR_NODE_HPP_
1717

18-
#include "rclcpp/rclcpp.hpp"
18+
#include "control_evaluator/metrics/deviation_metrics.hpp"
1919

20-
#include "diagnostic_msgs/msg/diagnostic_array.hpp"
20+
#include <rclcpp/rclcpp.hpp>
21+
#include <tier4_autoware_utils/ros/polling_subscriber.hpp>
22+
23+
#include <diagnostic_msgs/msg/diagnostic_array.hpp>
24+
#include <nav_msgs/msg/odometry.hpp>
2125

2226
#include <array>
2327
#include <deque>
2428
#include <memory>
2529
#include <string>
30+
#include <utility>
2631
#include <vector>
2732

2833
namespace control_diagnostics
2934
{
3035

36+
using autoware_planning_msgs::msg::Trajectory;
3137
using diagnostic_msgs::msg::DiagnosticArray;
3238
using diagnostic_msgs::msg::DiagnosticStatus;
39+
using geometry_msgs::msg::Point;
40+
using geometry_msgs::msg::Pose;
41+
using nav_msgs::msg::Odometry;
3342

3443
/**
3544
* @brief Node for control evaluation
@@ -38,22 +47,46 @@ class controlEvaluatorNode : public rclcpp::Node
3847
{
3948
public:
4049
explicit controlEvaluatorNode(const rclcpp::NodeOptions & node_options);
50+
void removeOldDiagnostics(const rclcpp::Time & stamp);
51+
void removeDiagnosticsByName(const std::string & name);
52+
void addDiagnostic(const DiagnosticStatus & diag, const rclcpp::Time & stamp);
53+
void updateDiagnosticQueue(
54+
const DiagnosticArray & input_diagnostics, const std::string & function,
55+
const rclcpp::Time & stamp);
56+
57+
DiagnosticStatus generateLateralDeviationDiagnosticStatus(
58+
const Trajectory & traj, const Point & ego_point);
59+
DiagnosticStatus generateYawDeviationDiagnosticStatus(
60+
const Trajectory & traj, const Pose & ego_pose);
61+
std::optional<DiagnosticStatus> generateStopDiagnosticStatus(
62+
const DiagnosticArray & diag, const std::string & function_name);
63+
64+
DiagnosticStatus generateAEBDiagnosticStatus(const DiagnosticStatus & diag);
4165

42-
/**
43-
* @brief publish the given metric statistic
44-
*/
45-
DiagnosticStatus generateDiagnosticStatus(const bool is_emergency_brake) const;
4666
void onDiagnostics(const DiagnosticArray::ConstSharedPtr diag_msg);
4767
void onTimer();
4868

4969
private:
70+
// The diagnostics cycle is faster than timer, and each node publishes diagnostic separately.
71+
// takeData() in onTimer() with a polling subscriber will miss a topic, so save all topics with
72+
// onDiagnostics().
5073
rclcpp::Subscription<DiagnosticArray>::SharedPtr control_diag_sub_;
74+
75+
tier4_autoware_utils::InterProcessPollingSubscriber<Odometry> odometry_sub_{
76+
this, "~/input/odometry"};
77+
tier4_autoware_utils::InterProcessPollingSubscriber<Trajectory> traj_sub_{
78+
this, "~/input/trajectory"};
79+
5180
rclcpp::Publisher<DiagnosticArray>::SharedPtr metrics_pub_;
5281

5382
// Calculator
5483
// Metrics
5584
std::deque<rclcpp::Time> stamps_;
56-
DiagnosticArray metrics_msg_;
85+
86+
// queue for diagnostics and time stamp
87+
std::deque<std::pair<DiagnosticStatus, rclcpp::Time>> diag_queue_;
88+
const std::vector<std::string> target_functions_ = {"autonomous_emergency_braking"};
89+
5790
rclcpp::TimerBase::SharedPtr timer_;
5891
};
5992
} // namespace control_diagnostics
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// Copyright 2024 TIER IV, Inc.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#ifndef CONTROL_EVALUATOR__METRICS__DEVIATION_METRICS_HPP_
16+
#define CONTROL_EVALUATOR__METRICS__DEVIATION_METRICS_HPP_
17+
18+
#include <autoware_planning_msgs/msg/trajectory.hpp>
19+
#include <autoware_planning_msgs/msg/trajectory_point.hpp>
20+
21+
namespace control_diagnostics
22+
{
23+
namespace metrics
24+
{
25+
using autoware_planning_msgs::msg::Trajectory;
26+
using geometry_msgs::msg::Point;
27+
using geometry_msgs::msg::Pose;
28+
29+
/**
30+
* @brief calculate lateral deviation of the given trajectory from the reference trajectory
31+
* @param [in] ref reference trajectory
32+
* @param [in] point input point
33+
* @return lateral deviation
34+
*/
35+
double calcLateralDeviation(const Trajectory & traj, const Point & point);
36+
37+
/**
38+
* @brief calculate yaw deviation of the given trajectory from the reference trajectory
39+
* @param [in] traj input trajectory
40+
* @param [in] pose input pose
41+
* @return yaw deviation
42+
*/
43+
double calcYawDeviation(const Trajectory & traj, const Pose & pose);
44+
45+
} // namespace metrics
46+
} // namespace control_diagnostics
47+
48+
#endif // CONTROL_EVALUATOR__METRICS__DEVIATION_METRICS_HPP_

evaluator/control_evaluator/launch/control_evaluator.launch.xml

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
<launch>
22
<arg name="input/diagnostics" default="/diagnostics"/>
3-
3+
<arg name="input/odometry" default="/localization/kinematic_state"/>
4+
<arg name="input/trajectory" default="/planning/scenario_planning/trajectory"/>
45
<!-- control evaluator -->
56
<group>
67
<node name="control_evaluator" exec="control_evaluator" pkg="control_evaluator">
78
<param from="$(find-pkg-share control_evaluator)/param/control_evaluator.defaults.yaml"/>
89
<remap from="~/input/diagnostics" to="$(var input/diagnostics)"/>
10+
<remap from="~/input/odometry" to="$(var input/odometry)"/>
11+
<remap from="~/input/trajectory" to="$(var input/trajectory)"/>
912
<remap from="~/metrics" to="/diagnostic/control_evaluator/metrics"/>
1013
</node>
1114
</group>

evaluator/control_evaluator/package.xml

+4
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,14 @@
1414
<buildtool_depend>ament_cmake_auto</buildtool_depend>
1515
<buildtool_depend>autoware_cmake</buildtool_depend>
1616

17+
<depend>autoware_planning_msgs</depend>
1718
<depend>diagnostic_msgs</depend>
19+
<depend>motion_utils</depend>
1820
<depend>pluginlib</depend>
1921
<depend>rclcpp</depend>
2022
<depend>rclcpp_components</depend>
23+
<depend>tier4_autoware_utils</depend>
24+
<!-- <depend>nav_msgs</depend> -->
2125

2226
<test_depend>ament_cmake_ros</test_depend>
2327
<test_depend>ament_lint_auto</test_depend>

evaluator/control_evaluator/src/control_evaluator_node.cpp

+116-22
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ controlEvaluatorNode::controlEvaluatorNode(const rclcpp::NodeOptions & node_opti
2828
: Node("control_evaluator", node_options)
2929
{
3030
using std::placeholders::_1;
31-
3231
control_diag_sub_ = create_subscription<DiagnosticArray>(
3332
"~/input/diagnostics", 1, std::bind(&controlEvaluatorNode::onDiagnostics, this, _1));
3433

@@ -41,45 +40,140 @@ controlEvaluatorNode::controlEvaluatorNode(const rclcpp::NodeOptions & node_opti
4140
rclcpp::create_timer(this, get_clock(), 100ms, std::bind(&controlEvaluatorNode::onTimer, this));
4241
}
4342

44-
DiagnosticStatus controlEvaluatorNode::generateDiagnosticStatus(const bool is_emergency_brake) const
43+
void controlEvaluatorNode::removeOldDiagnostics(const rclcpp::Time & stamp)
44+
{
45+
constexpr double KEEP_TIME = 1.0;
46+
diag_queue_.erase(
47+
std::remove_if(
48+
diag_queue_.begin(), diag_queue_.end(),
49+
[stamp](const std::pair<diagnostic_msgs::msg::DiagnosticStatus, rclcpp::Time> & p) {
50+
return (stamp - p.second).seconds() > KEEP_TIME;
51+
}),
52+
diag_queue_.end());
53+
}
54+
55+
void controlEvaluatorNode::removeDiagnosticsByName(const std::string & name)
56+
{
57+
diag_queue_.erase(
58+
std::remove_if(
59+
diag_queue_.begin(), diag_queue_.end(),
60+
[&name](const std::pair<diagnostic_msgs::msg::DiagnosticStatus, rclcpp::Time> & p) {
61+
return p.first.name.find(name) != std::string::npos;
62+
}),
63+
diag_queue_.end());
64+
}
65+
66+
void controlEvaluatorNode::addDiagnostic(
67+
const diagnostic_msgs::msg::DiagnosticStatus & diag, const rclcpp::Time & stamp)
68+
{
69+
diag_queue_.push_back(std::make_pair(diag, stamp));
70+
}
71+
72+
void controlEvaluatorNode::updateDiagnosticQueue(
73+
const DiagnosticArray & input_diagnostics, const std::string & function,
74+
const rclcpp::Time & stamp)
75+
{
76+
const auto it = std::find_if(
77+
input_diagnostics.status.begin(), input_diagnostics.status.end(),
78+
[&function](const diagnostic_msgs::msg::DiagnosticStatus & diag) {
79+
return diag.name.find(function) != std::string::npos;
80+
});
81+
if (it != input_diagnostics.status.end()) {
82+
removeDiagnosticsByName(it->name);
83+
addDiagnostic(*it, input_diagnostics.header.stamp);
84+
}
85+
86+
removeOldDiagnostics(stamp);
87+
}
88+
89+
void controlEvaluatorNode::onDiagnostics(const DiagnosticArray::ConstSharedPtr diag_msg)
90+
{
91+
// add target diagnostics to the queue and remove old ones
92+
for (const auto & function : target_functions_) {
93+
updateDiagnosticQueue(*diag_msg, function, now());
94+
}
95+
}
96+
97+
DiagnosticStatus controlEvaluatorNode::generateAEBDiagnosticStatus(const DiagnosticStatus & diag)
4598
{
4699
DiagnosticStatus status;
47100
status.level = status.OK;
48-
status.name = "autonomous_emergency_braking";
101+
status.name = diag.name;
49102
diagnostic_msgs::msg::KeyValue key_value;
50103
key_value.key = "decision";
104+
const bool is_emergency_brake = (diag.level == DiagnosticStatus::ERROR);
51105
key_value.value = (is_emergency_brake) ? "stop" : "none";
52106
status.values.push_back(key_value);
107+
53108
return status;
54109
}
55110

56-
void controlEvaluatorNode::onTimer()
111+
DiagnosticStatus controlEvaluatorNode::generateLateralDeviationDiagnosticStatus(
112+
const Trajectory & traj, const Point & ego_point)
57113
{
58-
if (!metrics_msg_.status.empty()) {
59-
metrics_pub_->publish(metrics_msg_);
60-
metrics_msg_.status.clear();
61-
}
114+
const double lateral_deviation = metrics::calcLateralDeviation(traj, ego_point);
115+
116+
DiagnosticStatus status;
117+
status.level = status.OK;
118+
status.name = "lateral_deviation";
119+
diagnostic_msgs::msg::KeyValue key_value;
120+
key_value.key = "metric_value";
121+
key_value.value = std::to_string(lateral_deviation);
122+
status.values.push_back(key_value);
123+
124+
return status;
62125
}
63126

64-
void controlEvaluatorNode::onDiagnostics(const DiagnosticArray::ConstSharedPtr diag_msg)
127+
DiagnosticStatus controlEvaluatorNode::generateYawDeviationDiagnosticStatus(
128+
const Trajectory & traj, const Pose & ego_pose)
65129
{
66-
const auto start = now();
67-
const auto aeb_status =
68-
std::find_if(diag_msg->status.begin(), diag_msg->status.end(), [](const auto & status) {
69-
const bool aeb_found = status.name.find("autonomous_emergency_braking") != std::string::npos;
70-
return aeb_found;
71-
});
130+
const double yaw_deviation = metrics::calcYawDeviation(traj, ego_pose);
72131

73-
if (aeb_status == diag_msg->status.end()) return;
74-
75-
const bool is_emergency_brake = (aeb_status->level == DiagnosticStatus::ERROR);
76-
metrics_msg_.header.stamp = now();
77-
metrics_msg_.status.emplace_back(generateDiagnosticStatus(is_emergency_brake));
132+
DiagnosticStatus status;
133+
status.level = status.OK;
134+
status.name = "yaw_deviation";
135+
diagnostic_msgs::msg::KeyValue key_value;
136+
key_value.key = "metric_value";
137+
key_value.value = std::to_string(yaw_deviation);
138+
status.values.push_back(key_value);
78139

79-
const auto runtime = (now() - start).seconds();
80-
RCLCPP_DEBUG(get_logger(), "control evaluation calculation time: %2.2f ms", runtime * 1e3);
140+
return status;
81141
}
82142

143+
void controlEvaluatorNode::onTimer()
144+
{
145+
DiagnosticArray metrics_msg;
146+
const auto traj = traj_sub_.takeData();
147+
const auto odom = odometry_sub_.takeData();
148+
149+
// generate decision diagnostics from input diagnostics
150+
for (const auto & function : target_functions_) {
151+
const auto it = std::find_if(
152+
diag_queue_.begin(), diag_queue_.end(),
153+
[&function](const std::pair<diagnostic_msgs::msg::DiagnosticStatus, rclcpp::Time> & p) {
154+
return p.first.name.find(function) != std::string::npos;
155+
});
156+
if (it == diag_queue_.end()) {
157+
continue;
158+
}
159+
// generate each decision diagnostics
160+
// - AEB decision
161+
if (it->first.name.find("autonomous_emergency_braking") != std::string::npos) {
162+
metrics_msg.status.push_back(generateAEBDiagnosticStatus(it->first));
163+
}
164+
}
165+
166+
// calculate deviation metrics
167+
if (odom && traj && !traj->points.empty()) {
168+
const Pose ego_pose = odom->pose.pose;
169+
metrics_msg.status.push_back(
170+
generateLateralDeviationDiagnosticStatus(*traj, ego_pose.position));
171+
metrics_msg.status.push_back(generateYawDeviationDiagnosticStatus(*traj, ego_pose));
172+
}
173+
174+
metrics_msg.header.stamp = now();
175+
metrics_pub_->publish(metrics_msg);
176+
}
83177
} // namespace control_diagnostics
84178

85179
#include "rclcpp_components/register_node_macro.hpp"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// Copyright 2024 TIER IV, Inc.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#include "control_evaluator/metrics/deviation_metrics.hpp"
16+
17+
#include "motion_utils/trajectory/trajectory.hpp"
18+
#include "tier4_autoware_utils/geometry/geometry.hpp"
19+
#include "tier4_autoware_utils/geometry/pose_deviation.hpp"
20+
21+
namespace control_diagnostics
22+
{
23+
namespace metrics
24+
{
25+
using autoware_planning_msgs::msg::Trajectory;
26+
27+
double calcLateralDeviation(const Trajectory & traj, const Point & point)
28+
{
29+
const size_t nearest_index = motion_utils::findNearestIndex(traj.points, point);
30+
return std::abs(
31+
tier4_autoware_utils::calcLateralDeviation(traj.points[nearest_index].pose, point));
32+
}
33+
34+
double calcYawDeviation(const Trajectory & traj, const Pose & pose)
35+
{
36+
const size_t nearest_index = motion_utils::findNearestIndex(traj.points, pose.position);
37+
return std::abs(tier4_autoware_utils::calcYawDeviation(traj.points[nearest_index].pose, pose));
38+
}
39+
40+
} // namespace metrics
41+
} // namespace control_diagnostics

launch/tier4_control_launch/launch/control.launch.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -370,7 +370,7 @@ def launch_setup(context, *args, **kwargs):
370370
("~/input/diagnostics", "/diagnostics"),
371371
("~/input/odometry", "/localization/kinematic_state"),
372372
("~/input/trajectory", "/planning/scenario_planning/trajectory"),
373-
("~/metrics", "/diagnostic/control_evaluator/metrics"),
373+
("~/output/metrics", "~/metrics"),
374374
],
375375
)
376376

0 commit comments

Comments
 (0)