Skip to content

Commit b59cd8d

Browse files
badai-nguyenMasatoSaekipre-commit-ci[bot]
authored
feat(traffic_light_selector): add new node for traffic light selection (#9721)
* feat: add traffic light selector node Signed-off-by: badai-nguyen <dai.nguyen@tier4.jp> feat: add traffic ligth selector node Signed-off-by: badai-nguyen <dai.nguyen@tier4.jp> * fix: add check expect roi iou Signed-off-by: badai-nguyen <dai.nguyen@tier4.jp> * fix: tl selector Signed-off-by: badai-nguyen <dai.nguyen@tier4.jp> * fix: launch file Signed-off-by: badai-nguyen <dai.nguyen@tier4.jp> * fix: update matching score Signed-off-by: badai-nguyen <dai.nguyen@tier4.jp> * fix: calc sum IOU for whole shifted image Signed-off-by: badai-nguyen <dai.nguyen@tier4.jp> * fix: check inside rough roi Signed-off-by: badai-nguyen <dai.nguyen@tier4.jp> * fix: check inside function Signed-off-by: badai-nguyen <dai.nguyen@tier4.jp> * feat: add max_iou_threshold Signed-off-by: badai-nguyen <dai.nguyen@tier4.jp> * chore: pre-commit Signed-off-by: badai-nguyen <dai.nguyen@tier4.jp> * docs: add readme Signed-off-by: badai-nguyen <dai.nguyen@tier4.jp> * refactor: launch file Signed-off-by: badai-nguyen <dai.nguyen@tier4.jp> * docs: pre-commit Signed-off-by: badai-nguyen <dai.nguyen@tier4.jp> * docs Signed-off-by: badai-nguyen <dai.nguyen@tier4.jp> * chore: typo Signed-off-by: badai-nguyen <dai.nguyen@tier4.jp> * refactor Signed-off-by: badai-nguyen <dai.nguyen@tier4.jp> * fix: add unknown in selector Signed-off-by: badai-nguyen <dai.nguyen@tier4.jp> * fix: change to GenIOU Signed-off-by: badai-nguyen <dai.nguyen@tier4.jp> * feat: add debug topic Signed-off-by: badai-nguyen <dai.nguyen@tier4.jp> * fix: add maintainer Signed-off-by: badai-nguyen <dai.nguyen@tier4.jp> * chore: pre-commit Signed-off-by: badai-nguyen <dai.nguyen@tier4.jp> * fix:cmake Signed-off-by: badai-nguyen <dai.nguyen@tier4.jp> * fix: move param to yaml file Signed-off-by: badai-nguyen <dai.nguyen@tier4.jp> * fix: typo Signed-off-by: badai-nguyen <dai.nguyen@tier4.jp> * fix: add schema Signed-off-by: badai-nguyen <dai.nguyen@tier4.jp> * fix Signed-off-by: MasatoSaeki <masato.saeki@tier4.jp> * style(pre-commit): autofix * fix typo Signed-off-by: MasatoSaeki <masato.saeki@tier4.jp> --------- Signed-off-by: badai-nguyen <dai.nguyen@tier4.jp> Signed-off-by: MasatoSaeki <masato.saeki@tier4.jp> Co-authored-by: Masato Saeki <78376491+MasatoSaeki@users.noreply.github.com> Co-authored-by: MasatoSaeki <masato.saeki@tier4.jp> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent 7f9cdb6 commit b59cd8d

10 files changed

+626
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
cmake_minimum_required(VERSION 3.8)
2+
project(autoware_traffic_light_selector)
3+
4+
# find dependencies
5+
find_package(autoware_cmake REQUIRED)
6+
find_package(OpenCV REQUIRED)
7+
find_package(Eigen3 REQUIRED)
8+
find_package(Sophus REQUIRED)
9+
find_package(Boost REQUIRED)
10+
find_package(PCL REQUIRED)
11+
find_package(CGAL REQUIRED COMPONENTS Core)
12+
13+
include_directories(
14+
include
15+
SYSTEM
16+
${Boost_INCLUDE_DIRS}
17+
${PCL_INCLUDE_DIRS}
18+
${EIGEN3_INCLUDE_DIRS}
19+
${Sophus_INCLUDE_DIRS}
20+
${OpenCV_INCLUDE_DIRS}
21+
)
22+
23+
autoware_package()
24+
25+
# Targets
26+
ament_auto_add_library(${PROJECT_NAME} SHARED
27+
src/traffic_light_selector_node.cpp
28+
src/utils.cpp
29+
)
30+
31+
rclcpp_components_register_node(${PROJECT_NAME}
32+
PLUGIN "autoware::traffic_light::TrafficLightSelectorNode"
33+
EXECUTABLE traffic_light_selector_node)
34+
35+
36+
if(BUILD_TESTING)
37+
list(APPEND AMENT_LINT_AUTO_EXCLUDE ament_cmake_uncrustify)
38+
find_package(ament_lint_auto REQUIRED)
39+
ament_lint_auto_find_test_dependencies()
40+
41+
endif()
42+
43+
ament_auto_package(
44+
INSTALL_TO_SHARE
45+
launch
46+
config
47+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
`autoware_traffic_light_selector`
2+
3+
## Overview
4+
5+
`autoware_traffic_light_selector` selects the interest traffic light from the list of accurately detected traffic lights by something (e.g. deep learning neural network) based on the expect ROIs and rough ROIs information and then assign traffic_light_id for them.
6+
7+
## Input topics
8+
9+
| Name | Type | Description |
10+
| --------------------- | ------------------------------------------------------ | -------------------------------------------------------------------- |
11+
| `input/detected_rois` | tier4_perception_msgs::msg::DetectedObjectsWithFeature | accurately detected traffic light |
12+
| `input/rough_rois` | tier4_perception_msgs::msg::TrafficLightRoiArray | location of traffic lights in image corresponding to the camera info |
13+
| `input/expect_rois` | tier4_perception_msgs::msg::TrafficLightRoiArray | location of traffic lights in image without any offset |
14+
15+
## Output topics
16+
17+
| Name | Type | Description |
18+
| --------------------- | ------------------------------------------- | ------------------------------------------ |
19+
| `output/traffic_rois` | tier4_perception_msgs::TrafficLightRoiArray | detected traffic light of interest with id |
20+
21+
## Node parameters
22+
23+
{{json_to_markdown("perception/autoware_traffic_light_selector/schema/traffic_light_selector.schema.json")}}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
/**:
2+
ros__parameters:
3+
max_iou_threshold: -0.5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<launch>
2+
<arg name="input/detected_rois" default="input/detected_rois"/>
3+
<arg name="input/rough_rois" default="input/rough_rois"/>
4+
<arg name="input/camera_info" default="input/camera_info"/>
5+
<arg name="output/traffic_rois" default="output/traffic_light_rois"/>
6+
<arg name="traffic_light_selector_param_path" default="$(find-pkg-share autoware_traffic_light_selector)/config/traffic_light_selector.param.yaml"/>
7+
8+
<node pkg="autoware_traffic_light_selector" exec="traffic_light_selector_node" name="traffic_light_selector" output="screen">
9+
<remap from="input/detected_rois" to="$(var input/detected_rois)"/>
10+
<remap from="input/rough_rois" to="$(var input/rough_rois)"/>
11+
<remap from="input/camera_info" to="input/camera_info"/>
12+
<remap from="output/traffic_rois" to="$(var output/traffic_rois)"/>
13+
<param from="$(var traffic_light_selector_param_path)"/>
14+
</node>
15+
</launch>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?xml version="1.0"?>
2+
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
3+
<package format="3">
4+
<name>autoware_traffic_light_selector</name>
5+
<version>0.1.0</version>
6+
<description>The ROS 2 autoware_traffic_light_selector package</description>
7+
<maintainer email="yukihiro.saito@tier4.jp">Yukihiro Saito</maintainer>
8+
<maintainer email="dai.nguyen@tier4.jp">Dai Nguyen</maintainer>
9+
<maintainer email="yoshi.ri@tier4.jp">Yoshi Ri</maintainer>
10+
<maintainer email="masato.saeki@tier4.jp">Masato Saeki</maintainer>
11+
<license>Apache License 2.0</license>
12+
<author email="dai.nguyen@tier4.jp">Dai Nguyen</author>
13+
14+
<buildtool_depend>ament_cmake_auto</buildtool_depend>
15+
16+
<depend>autoware_test_utils</depend>
17+
<depend>autoware_universe_utils</depend>
18+
<depend>cv_bridge</depend>
19+
<depend>geometry_msgs</depend>
20+
<depend>libopencv-dev</depend>
21+
<depend>message_filters</depend>
22+
<depend>rclcpp</depend>
23+
<depend>rclcpp_components</depend>
24+
<depend>sensor_msgs</depend>
25+
<depend>tier4_perception_msgs</depend>
26+
<build_depend>autoware_cmake</build_depend>
27+
<test_depend>ament_lint_auto</test_depend>
28+
<test_depend>autoware_lint_common</test_depend>
29+
30+
<export>
31+
<build_type>ament_cmake</build_type>
32+
</export>
33+
</package>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
{
2+
"$schema": "http://json-schema.org/draft-07/schema#",
3+
"title": "Parameters for traffic_light_selector",
4+
"type": "object",
5+
"definitions": {
6+
"traffic_light_selector": {
7+
"type": "object",
8+
"properties": {
9+
"max_iou_threshold": {
10+
"type": "number",
11+
"description": "Threshold of generalized IOU for detected traffic light selection",
12+
"default": -0.5,
13+
"minimum": -1.0,
14+
"maximum": 1.0
15+
}
16+
},
17+
"required": ["max_iou_threshold"]
18+
}
19+
},
20+
"properties": {
21+
"/**": {
22+
"type": "object",
23+
"properties": {
24+
"ros__parameters": {
25+
"$ref": "#/definitions/traffic_light_selector"
26+
}
27+
},
28+
"required": ["ros__parameters"]
29+
}
30+
},
31+
"required": ["/**"]
32+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
// Copyright 2025 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 "traffic_light_selector_node.hpp"
16+
17+
#include <opencv2/core/types.hpp>
18+
#include <opencv2/opencv.hpp>
19+
20+
#include <sensor_msgs/msg/region_of_interest.hpp>
21+
22+
#include <map>
23+
#include <memory>
24+
#include <vector>
25+
26+
namespace autoware::traffic_light
27+
{
28+
29+
TrafficLightSelectorNode::TrafficLightSelectorNode(const rclcpp::NodeOptions & node_options)
30+
: rclcpp::Node("traffic_light_selector_node", node_options),
31+
tf_buffer_(get_clock()),
32+
tf_listener_(tf_buffer_),
33+
detected_rois_sub_(this, "input/detected_rois", rclcpp::QoS{1}.get_rmw_qos_profile()),
34+
rough_rois_sub_(this, "input/rough_rois", rclcpp::QoS{1}.get_rmw_qos_profile()),
35+
expected_rois_sub_(this, "input/expect_rois", rclcpp::QoS{1}.get_rmw_qos_profile()),
36+
sync_(SyncPolicy(10), detected_rois_sub_, rough_rois_sub_, expected_rois_sub_)
37+
{
38+
{
39+
stop_watch_ptr_ =
40+
std::make_unique<autoware::universe_utils::StopWatch<std::chrono::milliseconds>>();
41+
debug_publisher_ =
42+
std::make_unique<autoware::universe_utils::DebugPublisher>(this, this->get_name());
43+
stop_watch_ptr_->tic("cyclic_time");
44+
stop_watch_ptr_->tic("processing_time");
45+
}
46+
47+
max_iou_threshold_ = declare_parameter<double>("max_iou_threshold");
48+
using std::placeholders::_1;
49+
using std::placeholders::_2;
50+
using std::placeholders::_3;
51+
sync_.registerCallback(std::bind(&TrafficLightSelectorNode::objectsCallback, this, _1, _2, _3));
52+
53+
camera_info_sub_ = create_subscription<sensor_msgs::msg::CameraInfo>(
54+
"input/camera_info", rclcpp::SensorDataQoS(),
55+
std::bind(&TrafficLightSelectorNode::cameraInfoCallback, this, _1));
56+
// Publisher
57+
pub_traffic_light_rois_ =
58+
create_publisher<TrafficLightRoiArray>("output/traffic_rois", rclcpp::QoS{1});
59+
}
60+
61+
void TrafficLightSelectorNode::cameraInfoCallback(
62+
const sensor_msgs::msg::CameraInfo::ConstSharedPtr input_msg)
63+
{
64+
if (camera_info_subscribed_) {
65+
return;
66+
}
67+
RCLCPP_INFO(get_logger(), "camera_info received");
68+
image_width_ = input_msg->width;
69+
image_height_ = input_msg->height;
70+
camera_info_subscribed_ = true;
71+
}
72+
73+
void TrafficLightSelectorNode::objectsCallback(
74+
const DetectedObjectsWithFeature::ConstSharedPtr & detected_traffic_light_msg,
75+
const TrafficLightRoiArray::ConstSharedPtr & rough_rois_msg,
76+
const TrafficLightRoiArray::ConstSharedPtr & expected_rois_msg)
77+
{
78+
stop_watch_ptr_->toc("processing_time", true);
79+
if (!camera_info_subscribed_) {
80+
return;
81+
}
82+
std::map<int, sensor_msgs::msg::RegionOfInterest> rough_rois_map;
83+
for (const auto & roi : rough_rois_msg->rois) {
84+
rough_rois_map[roi.traffic_light_id] = roi.roi;
85+
}
86+
// TODO(badai-nguyen): implement this function on CUDA or refactor the code
87+
88+
TrafficLightRoiArray output;
89+
output.header = detected_traffic_light_msg->header;
90+
float max_matching_score = 0.0;
91+
int det_roi_shift_x = 0;
92+
int det_roi_shift_y = 0;
93+
std::vector<sensor_msgs::msg::RegionOfInterest> det_rois;
94+
std::vector<sensor_msgs::msg::RegionOfInterest> expect_rois;
95+
for (const auto & detected_roi : detected_traffic_light_msg->feature_objects) {
96+
det_rois.push_back(detected_roi.feature.roi);
97+
}
98+
for (const auto & expected_roi : expected_rois_msg->rois) {
99+
expect_rois.push_back(expected_roi.roi);
100+
}
101+
cv::Mat expect_roi_img =
102+
utils::createBinaryImageFromRois(expect_rois, cv::Size(image_width_, image_height_));
103+
cv::Mat det_roi_img =
104+
utils::createBinaryImageFromRois(det_rois, cv::Size(image_width_, image_height_));
105+
// for (const auto expect_roi : expect_rois) {
106+
for (const auto & expect_traffic_roi : expected_rois_msg->rois) {
107+
const auto & expect_roi = expect_traffic_roi.roi;
108+
auto traffic_light_id = expect_traffic_roi.traffic_light_id;
109+
const auto & rough_roi = rough_rois_map[traffic_light_id];
110+
111+
for (const auto & detected_roi : det_rois) {
112+
// check if the detected roi is inside the rough roi
113+
if (!utils::isInsideRoughRoi(detected_roi, rough_roi)) {
114+
continue;
115+
}
116+
int dx = static_cast<int>(detected_roi.x_offset) - static_cast<int>(expect_roi.x_offset);
117+
int dy = static_cast<int>(detected_roi.y_offset) - static_cast<int>(expect_roi.y_offset);
118+
cv::Mat det_roi_shifted = utils::shiftAndPaddingImage(det_roi_img, dx, dy);
119+
double iou = utils::getIoUOf2BinaryImages(expect_roi_img, det_roi_shifted);
120+
if (iou > max_matching_score) {
121+
max_matching_score = iou;
122+
det_roi_shift_x = dx;
123+
det_roi_shift_y = dy;
124+
}
125+
}
126+
}
127+
128+
for (const auto & expect_roi : expected_rois_msg->rois) {
129+
// check max IOU after shift
130+
double max_iou = -1.0;
131+
sensor_msgs::msg::RegionOfInterest max_iou_roi;
132+
for (const auto & detected_roi : detected_traffic_light_msg->feature_objects) {
133+
// shift detected roi by det_roi_shift_x, det_roi_shift_y and calculate IOU
134+
sensor_msgs::msg::RegionOfInterest detected_roi_shifted = detected_roi.feature.roi;
135+
// fit top lef corner of detected roi to inside of image
136+
detected_roi_shifted.x_offset = std::clamp(
137+
static_cast<int>(detected_roi.feature.roi.x_offset) - det_roi_shift_x, 0,
138+
static_cast<int>(image_width_ - detected_roi.feature.roi.width));
139+
detected_roi_shifted.y_offset = std::clamp(
140+
static_cast<int>(detected_roi.feature.roi.y_offset) - det_roi_shift_y, 0,
141+
static_cast<int>(image_height_ - detected_roi.feature.roi.height));
142+
143+
double iou = utils::getGenIoU(expect_roi.roi, detected_roi_shifted);
144+
if (iou > max_iou) {
145+
max_iou = iou;
146+
max_iou_roi = detected_roi.feature.roi;
147+
}
148+
}
149+
if (max_iou > max_iou_threshold_) {
150+
TrafficLightRoi traffic_light_roi;
151+
traffic_light_roi.traffic_light_id = expect_roi.traffic_light_id;
152+
traffic_light_roi.traffic_light_type = expect_roi.traffic_light_type;
153+
traffic_light_roi.roi = max_iou_roi;
154+
output.rois.push_back(traffic_light_roi);
155+
} else {
156+
TrafficLightRoi traffic_light_roi;
157+
traffic_light_roi.traffic_light_id = expect_roi.traffic_light_id;
158+
traffic_light_roi.traffic_light_type = expect_roi.traffic_light_type;
159+
output.rois.push_back(traffic_light_roi);
160+
}
161+
}
162+
pub_traffic_light_rois_->publish(output);
163+
if (debug_publisher_) {
164+
const double processing_time_ms = stop_watch_ptr_->toc("processing_time", true);
165+
const double cyclic_time_ms = stop_watch_ptr_->toc("cyclic_time", true);
166+
const double pipeline_latency_ms =
167+
std::chrono::duration<double, std::milli>(
168+
std::chrono::nanoseconds((this->get_clock()->now() - output.header.stamp).nanoseconds()))
169+
.count();
170+
debug_publisher_->publish<tier4_debug_msgs::msg::Float64Stamped>(
171+
"debug/cyclic_time_ms", cyclic_time_ms);
172+
debug_publisher_->publish<tier4_debug_msgs::msg::Float64Stamped>(
173+
"debug/processing_time_ms", processing_time_ms);
174+
debug_publisher_->publish<tier4_debug_msgs::msg::Float64Stamped>(
175+
"debug/pipeline_latency_ms", pipeline_latency_ms);
176+
}
177+
return;
178+
}
179+
} // namespace autoware::traffic_light
180+
181+
#include "rclcpp_components/register_node_macro.hpp"
182+
RCLCPP_COMPONENTS_REGISTER_NODE(autoware::traffic_light::TrafficLightSelectorNode)

0 commit comments

Comments
 (0)