Skip to content

Commit ac8fff1

Browse files
feat(autonomous_emergency_braking): add obstacle velocity estimation for AEB (autowarefoundation#6912)
* enable aeb, fix topic problem Signed-off-by: Daniel Sanchez <danielsanchezaran@gmail.com> * add clustering method to eliminate small objects/noise Signed-off-by: Daniel Sanchez <danielsanchezaran@gmail.com> * remove comments Signed-off-by: Daniel Sanchez <danielsanchezaran@gmail.com> * Crop points outside of EGO predicted path Signed-off-by: Daniel Sanchez <danielsanchezaran@gmail.com> * remove offset Signed-off-by: Daniel Sanchez <danielsanchezaran@gmail.com> * Update library use Signed-off-by: Daniel Sanchez <danielsanchezaran@gmail.com> * add check for empty cloud Signed-off-by: Daniel Sanchez <danielsanchezaran@gmail.com> * add extra width for pointcloud cropping Signed-off-by: Daniel Sanchez <danielsanchezaran@gmail.com> * Use single PC ptr Signed-off-by: Daniel Sanchez <danielsanchezaran@gmail.com> * Revert "Use single PC ptr" This reverts commit b5091fc. Signed-off-by: Daniel Sanchez <danielsanchezaran@gmail.com> * Add back timestamp Signed-off-by: Daniel Sanchez <danielsanchezaran@gmail.com> * USe object hull to detect collisions Signed-off-by: Daniel Sanchez <danielsanchezaran@gmail.com> * remove unused functions Signed-off-by: Daniel Sanchez <danielsanchezaran@gmail.com> * remove debug timer out of code Signed-off-by: Daniel Sanchez <danielsanchezaran@gmail.com> * eliminate member var in favor of local pointcloud ptr Signed-off-by: Daniel Sanchez <danielsanchezaran@gmail.com> * remove unused chrono dependency Signed-off-by: Daniel Sanchez <danielsanchezaran@gmail.com> * Add object history to etimate object speed with pointcloud Signed-off-by: Daniel Sanchez <danielsanchezaran@gmail.com> * Add median velocity calc Signed-off-by: Daniel Sanchez <danielsanchezaran@gmail.com> * add velocity thresholds Signed-off-by: Daniel Sanchez <danielsanchezaran@gmail.com> * refactoring Signed-off-by: Daniel Sanchez <danielsanchezaran@gmail.com> * cleaning Signed-off-by: Daniel Sanchez <danielsanchezaran@gmail.com> * refactor Signed-off-by: Daniel Sanchez <danielsanchezaran@gmail.com> * change voxwl Signed-off-by: Daniel Sanchez <danielsanchezaran@gmail.com> * readme Signed-off-by: Daniel Sanchez <danielsanchezaran@gmail.com> * remove mutex since they are not necessary Signed-off-by: Daniel Sanchez <danielsanchezaran@gmail.com> * add markers for closest object velocity Signed-off-by: Daniel Sanchez <danielsanchezaran@gmail.com> * added units to marker Signed-off-by: Daniel Sanchez <danielsanchezaran@gmail.com> * delete duplicated param Signed-off-by: Daniel Sanchez <danielsanchezaran@gmail.com> * delete duplicated param Signed-off-by: Daniel Sanchez <danielsanchezaran@gmail.com> --------- Signed-off-by: Daniel Sanchez <danielsanchezaran@gmail.com>
1 parent 474f82a commit ac8fff1

File tree

4 files changed

+277
-77
lines changed

4 files changed

+277
-77
lines changed

control/autonomous_emergency_braking/README.md

+27
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,33 @@ After Noise filtering, it performs a geometric collision check to determine whet
108108

109109
Finally, the vertex that is closest to the ego vehicle is chosen as the candidate for collision checking: Since rss distance is used to judge if a collision will happen or not, if the closest vertex to the ego is deemed to be safe, the rest of the vertices (and the points in the clusters) will also be safe.
110110

111+
#### Obstacle velocity estimation
112+
113+
Once the position of the closest obstacle/point is determined, the AEB modules uses the history of previously detected objects to estimate the closest object speed using the following equations:
114+
115+
$$
116+
d_{t} = o_{time stamp} - prev_{time stamp}
117+
$$
118+
119+
$$
120+
d_{pos} = norm(o_{pos} - prev_{pos})
121+
$$
122+
123+
$$
124+
v_{norm} = d_{pos} / d_{t}
125+
$$
126+
127+
Where $o_{time stamp}$ and $prev_{time stamp}$ are the timestamps of the point clouds used to detect the current closest object and the closest object of the previous point cloud frame, and $o_{pos}$ and $prev_{pos}$ are the positions of those objects, respectively.
128+
129+
Finally, the velocity vector is compared against the ego's predicted path to get the longitudinal velocity $v_{obj}$:
130+
131+
$$
132+
v_{obj} = v_{norm} * Cos(yaw_{diff}) + v_{ego}
133+
$$
134+
135+
where $yaw_{diff}$ is the difference in yaw between the ego path and the displacement vector $$v_{pos} = o_{pos} - prev_{pos} $$ and $v_{ego}$ is the ego's speed, which accounts for the movement of points caused by the ego moving and not the object.
136+
All these equations are performed disregarding the z axis (in 2D).
137+
111138
### 4. Collision check with target obstacles
112139

113140
In the fourth step, it checks the collision with filtered obstacles using RSS distance. RSS is formulated as:
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,38 @@
11
/**:
22
ros__parameters:
3-
publish_debug_pointcloud: false
3+
# Ego path calculation
44
use_predicted_trajectory: true
5-
use_imu_path: true
6-
path_footprint_extra_margin: 1.0
5+
use_imu_path: false
6+
min_generated_path_length: 0.5
7+
imu_prediction_time_horizon: 1.5
8+
imu_prediction_time_interval: 0.1
9+
mpc_prediction_time_horizon: 1.5
10+
mpc_prediction_time_interval: 0.1
11+
12+
# Debug
13+
publish_debug_pointcloud: false
14+
15+
# Point cloud partitioning
716
detection_range_min_height: 0.0
817
detection_range_max_height_margin: 0.0
918
voxel_grid_x: 0.05
1019
voxel_grid_y: 0.05
1120
voxel_grid_z: 100000.0
12-
min_generated_path_length: 0.5
21+
22+
# Point cloud cropping
1323
expand_width: 0.1
24+
path_footprint_extra_margin: 4.0
25+
26+
# Point cloud clustering
27+
cluster_tolerance: 0.1 #[m]
28+
minimum_cluster_size: 10
29+
maximum_cluster_size: 10000
30+
31+
# RSS distance collision check
1432
longitudinal_offset: 2.0
1533
t_response: 1.0
1634
a_ego_min: -3.0
1735
a_obj_min: -1.0
18-
cluster_tolerance: 0.1 #[m]
19-
minimum_cluster_size: 10
20-
maximum_cluster_size: 10000
21-
imu_prediction_time_horizon: 1.5
22-
imu_prediction_time_interval: 0.1
23-
mpc_prediction_time_horizon: 1.5
24-
mpc_prediction_time_interval: 0.1
25-
collision_keeping_sec: 0.0
36+
collision_keeping_sec: 2.0
37+
previous_obstacle_keep_time: 1.0
2638
aeb_hz: 10.0

control/autonomous_emergency_braking/include/autonomous_emergency_braking/node.hpp

+140-18
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#define AUTONOMOUS_EMERGENCY_BRAKING__NODE_HPP_
1717

1818
#include <diagnostic_updater/diagnostic_updater.hpp>
19+
#include <motion_utils/trajectory/trajectory.hpp>
1920
#include <pcl_ros/transforms.hpp>
2021
#include <rclcpp/rclcpp.hpp>
2122
#include <tier4_autoware_utils/geometry/geometry.hpp>
@@ -40,12 +41,13 @@
4041
#include <tf2_ros/buffer.h>
4142
#include <tf2_ros/transform_listener.h>
4243

44+
#include <deque>
45+
#include <limits>
4346
#include <memory>
44-
#include <mutex>
4547
#include <optional>
4648
#include <string>
49+
#include <utility>
4750
#include <vector>
48-
4951
namespace autoware::motion::control::autonomous_emergency_braking
5052
{
5153

@@ -79,30 +81,142 @@ class CollisionDataKeeper
7981
public:
8082
explicit CollisionDataKeeper(rclcpp::Clock::SharedPtr clock) { clock_ = clock; }
8183

82-
void setTimeout(const double timeout_sec) { timeout_sec_ = timeout_sec; }
84+
void setTimeout(const double collision_keep_time, const double previous_obstacle_keep_time)
85+
{
86+
collision_keep_time_ = collision_keep_time;
87+
previous_obstacle_keep_time_ = previous_obstacle_keep_time;
88+
}
8389

84-
bool checkExpired()
90+
bool checkObjectDataExpired(std::optional<ObjectData> & data, const double timeout)
8591
{
86-
if (data_ && (clock_->now() - data_->stamp).seconds() > timeout_sec_) {
87-
data_.reset();
92+
if (!data.has_value()) return true;
93+
const auto now = clock_->now();
94+
const auto & prev_obj = data.value();
95+
const auto & data_time_stamp = prev_obj.stamp;
96+
if ((now - data_time_stamp).nanoseconds() * 1e-9 > timeout) {
97+
data = std::nullopt;
98+
return true;
8899
}
89-
return (data_ == nullptr);
100+
return false;
101+
}
102+
103+
bool checkCollisionExpired()
104+
{
105+
return this->checkObjectDataExpired(closest_object_, collision_keep_time_);
90106
}
91107

92-
void update(const ObjectData & data) { data_.reset(new ObjectData(data)); }
108+
bool checkPreviousObjectDataExpired()
109+
{
110+
return this->checkObjectDataExpired(prev_closest_object_, previous_obstacle_keep_time_);
111+
}
93112

94-
ObjectData get()
113+
ObjectData get() const
95114
{
96-
if (data_) {
97-
return *data_;
98-
} else {
99-
return ObjectData();
115+
return (closest_object_.has_value()) ? closest_object_.value() : ObjectData();
116+
}
117+
118+
ObjectData getPreviousObjectData() const
119+
{
120+
return (prev_closest_object_.has_value()) ? prev_closest_object_.value() : ObjectData();
121+
}
122+
123+
void setCollisionData(const ObjectData & data)
124+
{
125+
closest_object_ = std::make_optional<ObjectData>(data);
126+
}
127+
128+
void setPreviousObjectData(const ObjectData & data)
129+
{
130+
prev_closest_object_ = std::make_optional<ObjectData>(data);
131+
}
132+
133+
void resetVelocityHistory() { obstacle_velocity_history_.clear(); }
134+
135+
void updateVelocityHistory(
136+
const double current_object_velocity, const rclcpp::Time & current_object_velocity_time_stamp)
137+
{
138+
// remove old msg from deque
139+
const auto now = clock_->now();
140+
std::remove_if(
141+
obstacle_velocity_history_.begin(), obstacle_velocity_history_.end(),
142+
[&](const auto & velocity_time_pair) {
143+
const auto & vel_time = velocity_time_pair.second;
144+
return ((now - vel_time).nanoseconds() * 1e-9 > previous_obstacle_keep_time_);
145+
});
146+
147+
obstacle_velocity_history_.emplace_back(
148+
std::make_pair(current_object_velocity, current_object_velocity_time_stamp));
149+
}
150+
151+
std::optional<double> getMedianObstacleVelocity() const
152+
{
153+
if (obstacle_velocity_history_.empty()) return std::nullopt;
154+
std::vector<double> raw_velocities;
155+
for (const auto & vel_time_pair : obstacle_velocity_history_) {
156+
raw_velocities.emplace_back(vel_time_pair.first);
157+
}
158+
159+
const size_t med1 = (raw_velocities.size() % 2 == 0) ? (raw_velocities.size()) / 2 - 1
160+
: (raw_velocities.size()) / 2.0;
161+
const size_t med2 = (raw_velocities.size()) / 2.0;
162+
std::nth_element(raw_velocities.begin(), raw_velocities.begin() + med1, raw_velocities.end());
163+
const double vel1 = raw_velocities.at(med1);
164+
std::nth_element(raw_velocities.begin(), raw_velocities.begin() + med2, raw_velocities.end());
165+
const double vel2 = raw_velocities.at(med2);
166+
return (vel1 + vel2) / 2.0;
167+
}
168+
169+
std::optional<double> calcObjectSpeedFromHistory(
170+
const ObjectData & closest_object, const Path & path, const double current_ego_speed)
171+
{
172+
if (this->checkPreviousObjectDataExpired()) {
173+
this->setPreviousObjectData(closest_object);
174+
this->resetVelocityHistory();
175+
return std::nullopt;
176+
}
177+
178+
const auto estimated_velocity_opt = std::invoke([&]() -> std::optional<double> {
179+
const auto & prev_object = this->getPreviousObjectData();
180+
const double p_dt =
181+
(closest_object.stamp.nanoseconds() - prev_object.stamp.nanoseconds()) * 1e-9;
182+
if (p_dt < std::numeric_limits<double>::epsilon()) return std::nullopt;
183+
const auto & nearest_collision_point = closest_object.position;
184+
const auto & prev_collision_point = prev_object.position;
185+
186+
const double p_dx = nearest_collision_point.x - prev_collision_point.x;
187+
const double p_dy = nearest_collision_point.y - prev_collision_point.y;
188+
const double p_dist = std::hypot(p_dx, p_dy);
189+
const double p_yaw = std::atan2(p_dy, p_dx);
190+
const double p_vel = p_dist / p_dt;
191+
192+
const auto nearest_idx = motion_utils::findNearestIndex(path, nearest_collision_point);
193+
const auto & nearest_path_pose = path.at(nearest_idx);
194+
const auto & traj_yaw = tf2::getYaw(nearest_path_pose.orientation);
195+
const auto estimated_velocity = p_vel * std::cos(p_yaw - traj_yaw) + current_ego_speed;
196+
197+
// Current RSS distance calculation does not account for negative velocities
198+
return (estimated_velocity > 0.0) ? estimated_velocity : 0.0;
199+
});
200+
201+
if (!estimated_velocity_opt.has_value()) {
202+
this->setPreviousObjectData(closest_object);
203+
this->resetVelocityHistory();
204+
return std::nullopt;
100205
}
206+
207+
const auto & estimated_velocity = estimated_velocity_opt.value();
208+
this->setPreviousObjectData(closest_object);
209+
this->updateVelocityHistory(estimated_velocity, closest_object.stamp);
210+
return this->getMedianObstacleVelocity();
101211
}
102212

103213
private:
104-
std::unique_ptr<ObjectData> data_;
105-
double timeout_sec_{0.0};
214+
std::optional<ObjectData> prev_closest_object_{std::nullopt};
215+
std::optional<ObjectData> closest_object_{std::nullopt};
216+
double collision_keep_time_{0.0};
217+
double previous_obstacle_keep_time_{0.0};
218+
219+
std::deque<std::pair<double, rclcpp::Time>> obstacle_velocity_history_;
106220
rclcpp::Clock::SharedPtr clock_;
107221
};
108222

@@ -152,14 +266,22 @@ class AEB : public rclcpp::Node
152266
void cropPointCloudWithEgoFootprintPath(
153267
const std::vector<Polygon2d> & ego_polys, pcl::PointCloud<pcl::PointXYZ>::Ptr filtered_objects);
154268

269+
void createObjectDataUsingPointCloudClusters(
270+
const Path & ego_path, const std::vector<Polygon2d> & ego_polys, const rclcpp::Time & stamp,
271+
std::vector<ObjectData> & objects);
272+
void cropPointCloudWithEgoFootprintPath(const std::vector<Polygon2d> & ego_polys);
273+
155274
void addMarker(
156275
const rclcpp::Time & current_time, const Path & path, const std::vector<Polygon2d> & polygons,
157-
const std::vector<ObjectData> & objects, const double color_r, const double color_g,
158-
const double color_b, const double color_a, const std::string & ns,
159-
MarkerArray & debug_markers);
276+
const std::vector<ObjectData> & objects, const std::optional<ObjectData> & closest_object,
277+
const double color_r, const double color_g, const double color_b, const double color_a,
278+
const std::string & ns, MarkerArray & debug_markers);
160279

161280
void addCollisionMarker(const ObjectData & data, MarkerArray & debug_markers);
162281

282+
std::optional<double> calcObjectSpeedFromHistory(
283+
const ObjectData & closest_object, const Path & path, const double current_ego_speed);
284+
163285
PointCloud2::SharedPtr obstacle_ros_pointcloud_ptr_{nullptr};
164286
VelocityReport::ConstSharedPtr current_velocity_ptr_{nullptr};
165287
Vector3::SharedPtr angular_velocity_ptr_{nullptr};

0 commit comments

Comments
 (0)