Skip to content

Commit fe35797

Browse files
authored
feat(intersection): rectify initial accel/velocity profile in ego velocity profile (#5496)
Signed-off-by: Mamoru Sobue <mamoru.sobue@tier4.jp>
1 parent 0df1d8f commit fe35797

File tree

6 files changed

+94
-40
lines changed

6 files changed

+94
-40
lines changed

planning/behavior_velocity_intersection_module/README.md

+8
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,14 @@ The parameters `collision_detection.collision_start_margin_time` and `collision_
8686

8787
If collision is detected, the state transits to "STOP" immediately. On the other hand, the state does not transit to "GO" unless safe judgement continues for a certain period `collision_detection.state_transit_margin` to prevent the chattering of decisions.
8888

89+
Currently, the intersection module uses `motion_velocity_smoother` feature to precisely calculate ego vehicle velocity profile along the intersection lane under longitudinal/lateral constraints. If the flag `collision_detection.use_upstream_velocity` is true, the target velocity profile of the original path is used. Otherwise the target velocity is set to `common.intersection_velocity`. In the trajectory smoothing process the target velocity at/before ego trajectory points are set to ego current velocity. The smoothed trajectory is then converted to an array of (time, distance) which indicates the arrival time to each trajectory point on the path from current ego position. You can visualize this array by adding the lane id to `debug.ttc` and running
90+
91+
```bash
92+
ros2 run behavior_velocity_intersection_module ttc.py --lane_id <lane_id>
93+
```
94+
95+
![ego ttc profile](./docs/ttc.gif)
96+
8997
#### Stop Line Automatic Generation
9098

9199
If a stopline is associated with the intersection lane on the map, that line is used as the stopline for collision detection. Otherwise the path is interpolated at a certain intervals (=`common.path_interpolation_ds`), and the point which is `stop_line_margin` meters behind the attention area is defined as the position of the stop line for the vehicle front.

planning/behavior_velocity_intersection_module/config/intersection.param.yaml

+7-7
Original file line numberDiff line numberDiff line change
@@ -35,24 +35,24 @@
3535
distance_thr: 1.0 # [m]
3636

3737
collision_detection:
38-
state_transit_margin_time: 1.0
38+
state_transit_margin_time: 0.5
3939
min_predicted_path_confidence: 0.05
4040
minimum_ego_predicted_velocity: 1.388 # [m/s]
4141
fully_prioritized:
4242
collision_start_margin_time: 2.0
4343
collision_end_margin_time: 0.0
4444
partially_prioritized:
45-
collision_start_margin_time: 2.0
45+
collision_start_margin_time: 3.0
4646
collision_end_margin_time: 2.0
4747
not_prioritized:
48-
collision_start_margin_time: 4.0 # [s] this + state_transit_margin_time should be higher to account for collision with fast/accelerating object
49-
collision_end_margin_time: 6.0 # [s] this + state_transit_margin_time should be higher to account for collision with slow/decelerating object
48+
collision_start_margin_time: 3.0 # [s] this + state_transit_margin_time should be higher to account for collision with fast/accelerating object
49+
collision_end_margin_time: 2.0 # [s] this + state_transit_margin_time should be higher to account for collision with slow/decelerating object
5050
keep_detection_vel_thr: 0.833 # == 3.0km/h. keep detection if ego is ego.vel < keep_detection_vel_thr
5151
use_upstream_velocity: true # flag to use the planned velocity profile from the upstream module
5252
minimum_upstream_velocity: 0.01 # [m/s] minimum velocity to avoid null division for the stop line from the upstream velocity
5353
yield_on_green_traffic_light:
54-
distance_to_assigned_lanelet_start: 5.0
55-
duration: 2.0
54+
distance_to_assigned_lanelet_start: 10.0
55+
duration: 3.0
5656
object_dist_to_stopline: 10.0 # [m]
5757
ignore_on_amber_traffic_light:
5858
object_expected_deceleration: 2.0 # [m/ss]
@@ -81,7 +81,7 @@
8181
maximum_peeking_distance: 6.0 # [m]
8282
attention_lane_crop_curvature_threshold: 0.25
8383
attention_lane_curvature_calculation_ds: 0.5
84-
static_occlusion_with_traffic_light_timeout: 3.5
84+
static_occlusion_with_traffic_light_timeout: 0.5
8585

8686
debug:
8787
ttc: [0]
Loading

planning/behavior_velocity_intersection_module/scripts/ttc.py

+8-5
Original file line numberDiff line numberDiff line change
@@ -126,8 +126,11 @@ def plot_ttc(self):
126126
self.ttc_ax.set_xlabel("ego time")
127127
self.ttc_ax.set_ylabel("ego dist")
128128
time_dist_plot = self.ttc_ax.plot(ego_ttc_time, ego_ttc_dist, label="time-dist", c="orange")
129-
self.ttc_ax.set_xlim(min(ego_ttc_time) - 2.0, max(ego_ttc_time) + 3.0)
130-
self.ttc_ax.set_ylim(min(ego_ttc_dist) - 2.0, max(ego_ttc_dist) + 3.0)
129+
self.ttc_ax.set_xlim(
130+
min(ego_ttc_time) - 2.0,
131+
min(max(ego_ttc_time) + 3.0, self.args.max_time),
132+
)
133+
# self.ttc_ax.set_ylim(min(ego_ttc_dist) - 2.0, max(ego_ttc_dist) + 3.0)
131134
for npc, color in zip(self.npc_vehicles, cycle(self.color_list)):
132135
t0, t1 = npc.collision_start_time, npc.collision_end_time
133136
d0, d1 = npc.collision_start_dist, npc.collision_end_dist
@@ -137,15 +140,13 @@ def plot_ttc(self):
137140
c=color,
138141
alpha=0.2,
139142
)
140-
141143
dd = [d1 - d0 for d0, d1 in zip(ego_ttc_dist, ego_ttc_dist[1:])]
142144
dt = [t1 - t0 for t0, t1 in zip(ego_ttc_time, ego_ttc_time[1:])]
143145
v = [d / t for d, t in zip(dd, dt)]
144146
self.ttc_vel_ax.yaxis.set_label_position("right")
145147
self.ttc_vel_ax.set_ylabel("ego velocity")
146-
self.ttc_vel_ax.set_ylim(0.0, max(v) + 1.0)
148+
# self.ttc_vel_ax.set_ylim(0.0, max(v) + 1.0)
147149
time_velocity_plot = self.ttc_vel_ax.plot(ego_ttc_time[1:], v, label="time-v", c="red")
148-
149150
lines = time_dist_plot + time_velocity_plot
150151
labels = [line.get_label() for line in lines]
151152
self.ttc_ax.legend(lines, labels, loc="upper left")
@@ -218,6 +219,7 @@ def cleanup(self):
218219
if self.args.save:
219220
kwargs_write = {"fps": self.args.fps, "quantizer": "nq"}
220221
imageio.mimsave("./" + self.args.gif + ".gif", self.images, **kwargs_write)
222+
rclpy.shutdown()
221223

222224
def on_plot_timer(self):
223225
with self.lock:
@@ -277,6 +279,7 @@ def on_object_ttc(self, msg):
277279
default=60,
278280
help="detect range for drawing",
279281
)
282+
parser.add_argument("--max_time", type=float, default=100, help="max plot limit for time")
280283
parser.add_argument("-s", "--save", action="store_true", help="flag to save gif")
281284
parser.add_argument("--gif", type=str, default="ttc", help="filename of gif file")
282285
parser.add_argument("--fps", type=float, default=5, help="fps of gif")

planning/behavior_velocity_intersection_module/src/util.cpp

+63-23
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636

3737
#include <algorithm>
3838
#include <cmath>
39+
#include <limits>
3940
#include <list>
4041
#include <memory>
4142
#include <set>
@@ -1304,14 +1305,27 @@ TimeDistanceArray calcIntersectionPassingTime(
13041305
const bool use_upstream_velocity, const double minimum_upstream_velocity,
13051306
tier4_debug_msgs::msg::Float64MultiArrayStamped * debug_ttc_array)
13061307
{
1307-
double dist_sum = 0.0;
1308+
const double current_velocity = planner_data->current_velocity->twist.linear.x;
1309+
13081310
int assigned_lane_found = false;
13091311

13101312
// crop intersection part of the path, and set the reference velocity to intersection_velocity
13111313
// for ego's ttc
13121314
PathWithLaneId reference_path;
1313-
for (size_t i = closest_idx; i < path.points.size(); ++i) {
1315+
std::optional<size_t> upstream_stop_line{std::nullopt};
1316+
for (size_t i = 0; i < path.points.size() - 1; ++i) {
13141317
auto reference_point = path.points.at(i);
1318+
// assume backward velocity is current ego velocity
1319+
if (i < closest_idx) {
1320+
reference_point.point.longitudinal_velocity_mps = current_velocity;
1321+
}
1322+
if (
1323+
i > last_intersection_stop_line_candidate_idx &&
1324+
std::fabs(reference_point.point.longitudinal_velocity_mps) <
1325+
std::numeric_limits<double>::epsilon() &&
1326+
!upstream_stop_line) {
1327+
upstream_stop_line = i;
1328+
}
13151329
if (!use_upstream_velocity) {
13161330
reference_point.point.longitudinal_velocity_mps = intersection_velocity;
13171331
}
@@ -1326,41 +1340,63 @@ TimeDistanceArray calcIntersectionPassingTime(
13261340
return {{0.0, 0.0}}; // has already passed the intersection.
13271341
}
13281342

1343+
std::vector<std::pair<double, double>> original_path_xy;
1344+
for (size_t i = 0; i < reference_path.points.size(); ++i) {
1345+
const auto & p = reference_path.points.at(i).point.pose.position;
1346+
original_path_xy.emplace_back(p.x, p.y);
1347+
}
1348+
13291349
// apply smoother to reference velocity
13301350
PathWithLaneId smoothed_reference_path = reference_path;
1331-
smoothPath(reference_path, smoothed_reference_path, planner_data);
1351+
if (!smoothPath(reference_path, smoothed_reference_path, planner_data)) {
1352+
smoothed_reference_path = reference_path;
1353+
}
13321354

13331355
// calculate when ego is going to reach each (interpolated) points on the path
13341356
TimeDistanceArray time_distance_array{};
1335-
dist_sum = 0.0;
1357+
double dist_sum = 0.0;
13361358
double passing_time = time_delay;
13371359
time_distance_array.emplace_back(passing_time, dist_sum);
13381360

13391361
// NOTE: `reference_path` is resampled in `reference_smoothed_path`, so
13401362
// `last_intersection_stop_line_candidate_idx` makes no sense
1341-
const auto last_intersection_stop_line_candidate_point_orig =
1342-
path.points.at(last_intersection_stop_line_candidate_idx).point.pose;
1343-
const auto last_intersection_stop_line_candidate_nearest_ind =
1344-
motion_utils::findFirstNearestIndexWithSoftConstraints(
1345-
smoothed_reference_path.points, last_intersection_stop_line_candidate_point_orig,
1346-
planner_data->ego_nearest_dist_threshold, planner_data->ego_nearest_yaw_threshold);
1363+
const auto smoothed_path_closest_idx = motion_utils::findFirstNearestIndexWithSoftConstraints(
1364+
smoothed_reference_path.points, path.points.at(closest_idx).point.pose,
1365+
planner_data->ego_nearest_dist_threshold, planner_data->ego_nearest_yaw_threshold);
1366+
1367+
const std::optional<size_t> upstream_stop_line_idx_opt = [&]() -> std::optional<size_t> {
1368+
if (upstream_stop_line) {
1369+
const auto upstream_stop_line_point = path.points.at(upstream_stop_line.value()).point.pose;
1370+
return motion_utils::findFirstNearestIndexWithSoftConstraints(
1371+
smoothed_reference_path.points, upstream_stop_line_point,
1372+
planner_data->ego_nearest_dist_threshold, planner_data->ego_nearest_yaw_threshold);
1373+
} else {
1374+
return std::nullopt;
1375+
}
1376+
}();
1377+
const bool has_upstream_stopline = upstream_stop_line_idx_opt.has_value();
1378+
const size_t upstream_stopline_ind = upstream_stop_line_idx_opt.value_or(0);
13471379

1348-
for (size_t i = 1; i < smoothed_reference_path.points.size(); ++i) {
1349-
const auto & p1 = smoothed_reference_path.points.at(i - 1);
1350-
const auto & p2 = smoothed_reference_path.points.at(i);
1380+
for (size_t i = smoothed_path_closest_idx; i < smoothed_reference_path.points.size() - 1; ++i) {
1381+
const auto & p1 = smoothed_reference_path.points.at(i);
1382+
const auto & p2 = smoothed_reference_path.points.at(i + 1);
13511383

13521384
const double dist = tier4_autoware_utils::calcDistance2d(p1, p2);
13531385
dist_sum += dist;
13541386

13551387
// use average velocity between p1 and p2
13561388
const double average_velocity =
13571389
(p1.point.longitudinal_velocity_mps + p2.point.longitudinal_velocity_mps) / 2.0;
1358-
const double minimum_ego_velocity_division =
1359-
(use_upstream_velocity && i > last_intersection_stop_line_candidate_nearest_ind)
1360-
? minimum_upstream_velocity /* to avoid null division */
1361-
: minimum_ego_velocity;
1362-
const double passing_velocity =
1363-
std::max<double>(minimum_ego_velocity_division, average_velocity);
1390+
const double passing_velocity = [=]() {
1391+
if (use_upstream_velocity) {
1392+
if (has_upstream_stopline && i > upstream_stopline_ind) {
1393+
return minimum_upstream_velocity;
1394+
}
1395+
return std::max<double>(average_velocity, minimum_ego_velocity);
1396+
} else {
1397+
return std::max<double>(average_velocity, minimum_ego_velocity);
1398+
}
1399+
}();
13641400
passing_time += (dist / passing_velocity);
13651401

13661402
time_distance_array.emplace_back(passing_time, dist_sum);
@@ -1370,6 +1406,8 @@ TimeDistanceArray calcIntersectionPassingTime(
13701406
debug_ttc_array->layout.dim.at(0).size = 5;
13711407
debug_ttc_array->layout.dim.at(1).label = "values";
13721408
debug_ttc_array->layout.dim.at(1).size = time_distance_array.size();
1409+
debug_ttc_array->data.reserve(
1410+
time_distance_array.size() * debug_ttc_array->layout.dim.at(0).size);
13731411
for (unsigned i = 0; i < time_distance_array.size(); ++i) {
13741412
debug_ttc_array->data.push_back(lane_id);
13751413
}
@@ -1379,11 +1417,13 @@ TimeDistanceArray calcIntersectionPassingTime(
13791417
for (const auto & [t, d] : time_distance_array) {
13801418
debug_ttc_array->data.push_back(d);
13811419
}
1382-
for (const auto & p : smoothed_reference_path.points) {
1383-
debug_ttc_array->data.push_back(p.point.pose.position.x);
1420+
for (size_t i = smoothed_path_closest_idx; i < smoothed_reference_path.points.size(); ++i) {
1421+
const auto & p = smoothed_reference_path.points.at(i).point.pose.position;
1422+
debug_ttc_array->data.push_back(p.x);
13841423
}
1385-
for (const auto & p : smoothed_reference_path.points) {
1386-
debug_ttc_array->data.push_back(p.point.pose.position.y);
1424+
for (size_t i = smoothed_path_closest_idx; i < smoothed_reference_path.points.size(); ++i) {
1425+
const auto & p = smoothed_reference_path.points.at(i).point.pose.position;
1426+
debug_ttc_array->data.push_back(p.y);
13871427
}
13881428
return time_distance_array;
13891429
}

planning/behavior_velocity_planner_common/src/utilization/trajectory_utils.cpp

+8-5
Original file line numberDiff line numberDiff line change
@@ -88,15 +88,14 @@ bool smoothPath(
8888
const auto & smoother = planner_data->velocity_smoother_;
8989

9090
auto trajectory = convertPathToTrajectoryPoints(in_path);
91-
if (external_v_limit) {
92-
motion_velocity_smoother::trajectory_utils::applyMaximumVelocityLimit(
93-
0, trajectory.size(), external_v_limit->max_velocity, trajectory);
94-
}
9591
const auto traj_lateral_acc_filtered = smoother->applyLateralAccelerationFilter(trajectory);
9692

93+
const auto traj_steering_rate_limited =
94+
smoother->applySteeringRateLimit(traj_lateral_acc_filtered, false);
95+
9796
// Resample trajectory with ego-velocity based interval distances
9897
auto traj_resampled = smoother->resampleTrajectory(
99-
traj_lateral_acc_filtered, v0, current_pose, planner_data->ego_nearest_dist_threshold,
98+
traj_steering_rate_limited, v0, current_pose, planner_data->ego_nearest_dist_threshold,
10099
planner_data->ego_nearest_yaw_threshold);
101100
const size_t traj_resampled_closest = motion_utils::findFirstNearestIndexWithSoftConstraints(
102101
traj_resampled, current_pose, planner_data->ego_nearest_dist_threshold,
@@ -114,6 +113,10 @@ bool smoothPath(
114113
traj_smoothed.insert(
115114
traj_smoothed.begin(), traj_resampled.begin(), traj_resampled.begin() + traj_resampled_closest);
116115

116+
if (external_v_limit) {
117+
motion_velocity_smoother::trajectory_utils::applyMaximumVelocityLimit(
118+
traj_resampled_closest, traj_smoothed.size(), external_v_limit->max_velocity, traj_smoothed);
119+
}
117120
out_path = convertTrajectoryPointsToPath(traj_smoothed);
118121
return true;
119122
}

0 commit comments

Comments
 (0)