Skip to content

Commit 2907429

Browse files
feat(planning_data_analyzer): add open-loop history comfort metric (#379)
* feat(planning_data_analyzer): add open-loop history comfort metric * chore(planning_data_analyzer): drop local implementation note from PR * refactor(planning_data_analyzer): extract history comfort metric utility * style(planning_data_analyzer): apply clang-format to history comfort utility * fix(planning_data_analyzer): address history comfort review comments
1 parent a5e322e commit 2907429

11 files changed

Lines changed: 388 additions & 54 deletions

planning/autoware_planning_data_analyzer/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ ament_auto_add_library(${PROJECT_NAME} SHARED
1111
src/or_event_extractor.cpp
1212
src/or_scene_evaluator.cpp
1313
src/metrics/deviation_metrics.cpp
14+
src/metrics/history_comfort.cpp
1415
src/metrics/trajectory_metrics.cpp
1516
src/utils/bag_utils.cpp
1617
src/utils/json_utils.cpp

planning/autoware_planning_data_analyzer/config/planning_data_analyzer.param.yaml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,16 @@
3434
gt_source_mode: "kinematic_state"
3535
gt_trajectory_topic: "/planning/ground_truth_trajectory"
3636
gt_sync_tolerance_ms: 200.0
37+
hc:
38+
# NAVSIM-aligned default thresholds for the History Comfort (HC) subscore.
39+
finite_difference_epsilon: 0.001
40+
max_longitudinal_acceleration: 2.40
41+
min_longitudinal_acceleration: -4.05
42+
max_lateral_acceleration: 4.89
43+
max_jerk_magnitude: 8.37
44+
max_longitudinal_jerk: 4.13
45+
max_yaw_rate: 0.95
46+
max_yaw_acceleration: 1.93
3747
metric_variant: ""
3848
# DLR result horizon checkpoints in seconds.
3949
# ADE/FDE are reported at these horizons plus "full" (entire trajectory).

planning/autoware_planning_data_analyzer/src/autoware_planning_data_analyzer_node.cpp

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,22 @@ AutowarePlanningDataAnalyzerNode::AutowarePlanningDataAnalyzerNode(
108108
gt_trajectory_topic_name_ =
109109
get_or_declare_parameter<std::string>(*this, "open_loop.gt_trajectory_topic");
110110
gt_sync_tolerance_ms_ = get_or_declare_parameter<double>(*this, "open_loop.gt_sync_tolerance_ms");
111+
history_comfort_params_.finite_difference_epsilon =
112+
get_or_declare_parameter<double>(*this, "open_loop.hc.finite_difference_epsilon");
113+
history_comfort_params_.max_longitudinal_acceleration =
114+
get_or_declare_parameter<double>(*this, "open_loop.hc.max_longitudinal_acceleration");
115+
history_comfort_params_.min_longitudinal_acceleration =
116+
get_or_declare_parameter<double>(*this, "open_loop.hc.min_longitudinal_acceleration");
117+
history_comfort_params_.max_lateral_acceleration =
118+
get_or_declare_parameter<double>(*this, "open_loop.hc.max_lateral_acceleration");
119+
history_comfort_params_.max_jerk_magnitude =
120+
get_or_declare_parameter<double>(*this, "open_loop.hc.max_jerk_magnitude");
121+
history_comfort_params_.max_longitudinal_jerk =
122+
get_or_declare_parameter<double>(*this, "open_loop.hc.max_longitudinal_jerk");
123+
history_comfort_params_.max_yaw_rate =
124+
get_or_declare_parameter<double>(*this, "open_loop.hc.max_yaw_rate");
125+
history_comfort_params_.max_yaw_acceleration =
126+
get_or_declare_parameter<double>(*this, "open_loop.hc.max_yaw_acceleration");
111127
objects_topic_name_ = get_or_declare_parameter<std::string>(*this, "objects_topic");
112128
tf_topic_name_ = get_or_declare_parameter<std::string>(*this, "tf_topic");
113129
acceleration_topic_name_ = get_or_declare_parameter<std::string>(*this, "acceleration_topic");
@@ -451,7 +467,8 @@ void AutowarePlanningDataAnalyzerNode::run_evaluation()
451467
}
452468
const auto evaluation_horizons =
453469
get_or_declare_parameter<std::vector<double>>(*this, "open_loop.evaluation_horizons");
454-
OpenLoopEvaluator evaluator(get_logger(), route_handler_, gt_mode, gt_sync_tolerance_ms_);
470+
OpenLoopEvaluator evaluator(
471+
get_logger(), route_handler_, gt_mode, gt_sync_tolerance_ms_, history_comfort_params_);
455472
evaluator.set_json_output_dir(output_dir_path.string());
456473
evaluator.set_metric_variant(open_loop_metric_variant);
457474
evaluator.set_evaluation_horizons(evaluation_horizons);

planning/autoware_planning_data_analyzer/src/autoware_planning_data_analyzer_node.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#define AUTOWARE_PLANNING_DATA_ANALYZER_NODE_HPP_
1717

1818
#include "bag_handler.hpp"
19+
#include "metrics/trajectory_metrics.hpp"
1920
#include "rosbag2_cpp/reader.hpp"
2021
#include "rosbag2_cpp/writer.hpp"
2122
#include "serialized_bag_message.hpp"
@@ -84,6 +85,7 @@ class AutowarePlanningDataAnalyzerNode : public rclcpp::Node
8485
std::string gt_source_mode_;
8586
std::string gt_trajectory_topic_name_;
8687
double gt_sync_tolerance_ms_ = 200.0;
88+
metrics::HistoryComfortParameters history_comfort_params_;
8789
std::string objects_topic_name_;
8890
std::string tf_topic_name_;
8991
std::string acceleration_topic_name_;
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
// Copyright 2026 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 "history_comfort.hpp"
16+
17+
#include <autoware_utils_math/normalization.hpp>
18+
19+
#include <tf2_geometry_msgs/tf2_geometry_msgs.hpp>
20+
21+
#include <tf2/utils.h>
22+
23+
#include <algorithm>
24+
#include <cmath>
25+
#include <vector>
26+
27+
namespace autoware::planning_data_analyzer::metrics
28+
{
29+
30+
namespace
31+
{
32+
33+
double calculate_time_resolution(
34+
const autoware_planning_msgs::msg::TrajectoryPoint & point,
35+
const autoware_planning_msgs::msg::TrajectoryPoint & next_point, const double epsilon)
36+
{
37+
const double time_diff = rclcpp::Duration(next_point.time_from_start).seconds() -
38+
rclcpp::Duration(point.time_from_start).seconds();
39+
return time_diff > epsilon ? time_diff : epsilon;
40+
}
41+
42+
void backfill_last_value_from_previous(std::vector<double> & values)
43+
{
44+
if (values.size() > 1U) {
45+
values.back() = values[values.size() - 2];
46+
}
47+
}
48+
49+
bool is_within(const std::vector<double> & values, const double min_value, const double max_value)
50+
{
51+
return std::all_of(values.begin(), values.end(), [min_value, max_value](const double value) {
52+
return min_value < value && value < max_value;
53+
});
54+
}
55+
56+
bool is_abs_within(const std::vector<double> & values, const double max_abs_value)
57+
{
58+
return std::all_of(values.begin(), values.end(), [max_abs_value](const double value) {
59+
return std::abs(value) < max_abs_value;
60+
});
61+
}
62+
63+
} // namespace
64+
65+
void calculate_history_comfort_metrics(
66+
const autoware_planning_msgs::msg::Trajectory & trajectory,
67+
const HistoryComfortParameters & history_comfort_params, TrajectoryPointMetrics & metrics)
68+
{
69+
const size_t num_points = trajectory.points.size();
70+
if (num_points == 0U) {
71+
return;
72+
}
73+
74+
metrics.longitudinal_accelerations.resize(num_points, 0.0);
75+
metrics.lateral_accelerations.resize(num_points, 0.0);
76+
metrics.lateral_jerks.resize(num_points, 0.0);
77+
metrics.jerk_magnitudes.resize(num_points, 0.0);
78+
metrics.longitudinal_jerks.resize(num_points, 0.0);
79+
metrics.yaw_rates.resize(num_points, 0.0);
80+
metrics.yaw_accelerations.resize(num_points, 0.0);
81+
82+
for (size_t i = 0; i < num_points - 1; ++i) {
83+
const double time_resolution = calculate_time_resolution(
84+
trajectory.points[i], trajectory.points[i + 1],
85+
history_comfort_params.finite_difference_epsilon);
86+
87+
const double yaw_delta = autoware_utils_math::normalize_radian(
88+
tf2::getYaw(trajectory.points[i + 1].pose.orientation) -
89+
tf2::getYaw(trajectory.points[i].pose.orientation));
90+
metrics.yaw_rates[i] = yaw_delta / time_resolution;
91+
metrics.longitudinal_accelerations[i] = (trajectory.points[i + 1].longitudinal_velocity_mps -
92+
trajectory.points[i].longitudinal_velocity_mps) /
93+
time_resolution;
94+
metrics.lateral_accelerations[i] =
95+
trajectory.points[i].longitudinal_velocity_mps * metrics.yaw_rates[i];
96+
}
97+
backfill_last_value_from_previous(metrics.longitudinal_accelerations);
98+
backfill_last_value_from_previous(metrics.lateral_accelerations);
99+
backfill_last_value_from_previous(metrics.yaw_rates);
100+
101+
for (size_t i = 0; i < num_points - 1; ++i) {
102+
const double time_resolution = calculate_time_resolution(
103+
trajectory.points[i], trajectory.points[i + 1],
104+
history_comfort_params.finite_difference_epsilon);
105+
106+
metrics.longitudinal_jerks[i] =
107+
(metrics.longitudinal_accelerations[i + 1] - metrics.longitudinal_accelerations[i]) /
108+
time_resolution;
109+
metrics.lateral_jerks[i] =
110+
(metrics.lateral_accelerations[i + 1] - metrics.lateral_accelerations[i]) / time_resolution;
111+
metrics.jerk_magnitudes[i] =
112+
std::hypot(metrics.longitudinal_jerks[i], metrics.lateral_jerks[i]);
113+
metrics.yaw_accelerations[i] =
114+
(metrics.yaw_rates[i + 1] - metrics.yaw_rates[i]) / time_resolution;
115+
}
116+
backfill_last_value_from_previous(metrics.longitudinal_jerks);
117+
backfill_last_value_from_previous(metrics.lateral_jerks);
118+
backfill_last_value_from_previous(metrics.jerk_magnitudes);
119+
backfill_last_value_from_previous(metrics.yaw_accelerations);
120+
121+
const bool longitudinal_acceleration_ok = is_within(
122+
metrics.longitudinal_accelerations, history_comfort_params.min_longitudinal_acceleration,
123+
history_comfort_params.max_longitudinal_acceleration);
124+
const bool lateral_acceleration_ok =
125+
is_abs_within(metrics.lateral_accelerations, history_comfort_params.max_lateral_acceleration);
126+
const bool jerk_magnitude_ok =
127+
is_abs_within(metrics.jerk_magnitudes, history_comfort_params.max_jerk_magnitude);
128+
const bool longitudinal_jerk_ok =
129+
is_abs_within(metrics.longitudinal_jerks, history_comfort_params.max_longitudinal_jerk);
130+
const bool yaw_rate_ok = is_abs_within(metrics.yaw_rates, history_comfort_params.max_yaw_rate);
131+
const bool yaw_acceleration_ok =
132+
is_abs_within(metrics.yaw_accelerations, history_comfort_params.max_yaw_acceleration);
133+
134+
metrics.history_comfort = longitudinal_acceleration_ok && lateral_acceleration_ok &&
135+
jerk_magnitude_ok && longitudinal_jerk_ok && yaw_rate_ok &&
136+
yaw_acceleration_ok
137+
? 1.0
138+
: 0.0;
139+
}
140+
141+
} // namespace autoware::planning_data_analyzer::metrics
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// Copyright 2026 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 METRICS__HISTORY_COMFORT_HPP_
16+
#define METRICS__HISTORY_COMFORT_HPP_
17+
18+
#include "trajectory_metrics.hpp"
19+
20+
#include <autoware_planning_msgs/msg/trajectory.hpp>
21+
22+
namespace autoware::planning_data_analyzer::metrics
23+
{
24+
25+
void calculate_history_comfort_metrics(
26+
const autoware_planning_msgs::msg::Trajectory & trajectory,
27+
const HistoryComfortParameters & history_comfort_params, TrajectoryPointMetrics & metrics);
28+
29+
} // namespace autoware::planning_data_analyzer::metrics
30+
31+
#endif // METRICS__HISTORY_COMFORT_HPP_

planning/autoware_planning_data_analyzer/src/metrics/trajectory_metrics.cpp

Lines changed: 7 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,14 @@
1414

1515
#include "trajectory_metrics.hpp"
1616

17+
#include "history_comfort.hpp"
18+
1719
#include <autoware/lanelet2_utils/geometry.hpp>
1820
#include <autoware/motion_utils/trajectory/trajectory.hpp>
1921
#include <autoware_lanelet2_extension/utility/utilities.hpp>
2022
#include <autoware_utils_geometry/geometry.hpp>
2123
#include <tf2/LinearMath/Vector3.hpp>
2224

23-
#include <tf2/utils.h>
24-
2525
#include <algorithm>
2626
#include <cmath>
2727
#include <limits>
@@ -191,7 +191,8 @@ double calculate_time_to_collision(
191191

192192
TrajectoryPointMetrics calculate_trajectory_point_metrics(
193193
const std::shared_ptr<SynchronizedData> & sync_data,
194-
const std::shared_ptr<autoware::route_handler::RouteHandler> & route_handler)
194+
const std::shared_ptr<autoware::route_handler::RouteHandler> & route_handler,
195+
const HistoryComfortParameters & history_comfort_params)
195196
{
196197
TrajectoryPointMetrics metrics;
197198

@@ -203,56 +204,15 @@ TrajectoryPointMetrics calculate_trajectory_point_metrics(
203204
const size_t num_points = trajectory.points.size();
204205

205206
// Initialize vectors
206-
metrics.lateral_accelerations.resize(num_points, 0.0);
207-
metrics.longitudinal_jerks.resize(num_points, 0.0);
208207
metrics.ttc_values.resize(num_points, std::numeric_limits<double>::max());
209208
metrics.lateral_deviations.resize(num_points, 0.0);
210209
metrics.travel_distances.resize(num_points, 0.0);
211210

212-
// Calculate lateral acceleration from lateral velocity difference
213-
constexpr double epsilon = 1.0e-3;
214-
for (size_t i = 0; i < num_points - 1; ++i) {
215-
const double time_diff = rclcpp::Duration(trajectory.points[i + 1].time_from_start).seconds() -
216-
rclcpp::Duration(trajectory.points[i].time_from_start).seconds();
217-
const double time_resolution = time_diff > epsilon ? time_diff : epsilon;
218-
219-
const double lateral_acc =
220-
(trajectory.points[i + 1].lateral_velocity_mps - trajectory.points[i].lateral_velocity_mps) /
221-
time_resolution;
222-
metrics.lateral_accelerations[i] = lateral_acc;
223-
}
224-
if (num_points > 0) {
225-
metrics.lateral_accelerations[num_points - 1] = metrics.lateral_accelerations[num_points - 2];
226-
}
227-
228-
// Calculate longitudinal jerk from velocity differences
229-
std::vector<double> longitudinal_accelerations(num_points, 0.0);
230-
for (size_t i = 0; i < num_points - 1; ++i) {
231-
const double time_diff = rclcpp::Duration(trajectory.points[i + 1].time_from_start).seconds() -
232-
rclcpp::Duration(trajectory.points[i].time_from_start).seconds();
233-
const double time_resolution = time_diff > epsilon ? time_diff : epsilon;
234-
235-
longitudinal_accelerations[i] = (trajectory.points[i + 1].longitudinal_velocity_mps -
236-
trajectory.points[i].longitudinal_velocity_mps) /
237-
time_resolution;
238-
}
239-
if (num_points > 0) {
240-
longitudinal_accelerations[num_points - 1] = longitudinal_accelerations[num_points - 2];
211+
if (num_points == 0U) {
212+
return metrics;
241213
}
242214

243-
// Calculate jerk from acceleration differences
244-
for (size_t i = 0; i < num_points - 1; ++i) {
245-
const double time_diff = rclcpp::Duration(trajectory.points[i + 1].time_from_start).seconds() -
246-
rclcpp::Duration(trajectory.points[i].time_from_start).seconds();
247-
const double time_resolution = time_diff > epsilon ? time_diff : epsilon;
248-
249-
const double jerk =
250-
(longitudinal_accelerations[i + 1] - longitudinal_accelerations[i]) / time_resolution;
251-
metrics.longitudinal_jerks[i] = jerk;
252-
}
253-
if (num_points > 0) {
254-
metrics.longitudinal_jerks[num_points - 1] = metrics.longitudinal_jerks[num_points - 2];
255-
}
215+
calculate_history_comfort_metrics(trajectory, history_comfort_params, metrics);
256216

257217
// Calculate TTC for each point (based on autoware_trajectory_ranker implementation)
258218
constexpr double max_ttc_value = 10.0; // Maximum TTC value in seconds

planning/autoware_planning_data_analyzer/src/metrics/trajectory_metrics.hpp

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,32 @@
2525
namespace autoware::planning_data_analyzer::metrics
2626
{
2727

28+
struct HistoryComfortParameters
29+
{
30+
double finite_difference_epsilon{1.0e-3};
31+
double max_longitudinal_acceleration{2.40};
32+
double min_longitudinal_acceleration{-4.05};
33+
double max_lateral_acceleration{4.89};
34+
double max_jerk_magnitude{8.37};
35+
double max_longitudinal_jerk{4.13};
36+
double max_yaw_rate{0.95};
37+
double max_yaw_acceleration{1.93};
38+
};
39+
2840
// Structure for trajectory point-wise metrics
2941
struct TrajectoryPointMetrics
3042
{
43+
std::vector<double> longitudinal_accelerations;
3144
std::vector<double> lateral_accelerations;
45+
std::vector<double> lateral_jerks;
46+
std::vector<double> jerk_magnitudes;
3247
std::vector<double> longitudinal_jerks;
48+
std::vector<double> yaw_rates;
49+
std::vector<double> yaw_accelerations;
3350
std::vector<double> ttc_values;
3451
std::vector<double> lateral_deviations;
3552
std::vector<double> travel_distances;
53+
double history_comfort{0.0};
3654
};
3755

3856
/**
@@ -43,7 +61,8 @@ struct TrajectoryPointMetrics
4361
*/
4462
TrajectoryPointMetrics calculate_trajectory_point_metrics(
4563
const std::shared_ptr<SynchronizedData> & sync_data,
46-
const std::shared_ptr<autoware::route_handler::RouteHandler> & route_handler = nullptr);
64+
const std::shared_ptr<autoware::route_handler::RouteHandler> & route_handler = nullptr,
65+
const HistoryComfortParameters & history_comfort_params = HistoryComfortParameters{});
4766

4867
} // namespace autoware::planning_data_analyzer::metrics
4968

0 commit comments

Comments
 (0)