|
| 1 | +#!/usr/bin/env python3 |
| 2 | + |
| 3 | +# Copyright 2023 TIER IV, Inc. |
| 4 | +# |
| 5 | +# Licensed under the Apache License, Version 2.0 (the "License"); |
| 6 | +# you may not use this file except in compliance with the License. |
| 7 | +# You may obtain a copy of the License at |
| 8 | +# |
| 9 | +# http://www.apache.org/licenses/LICENSE-2.0 |
| 10 | +# |
| 11 | +# Unless required by applicable law or agreed to in writing, software |
| 12 | +# distributed under the License is distributed on an "AS IS" BASIS, |
| 13 | +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 14 | +# See the License for the specific language governing permissions and |
| 15 | +# limitations under the License. |
| 16 | + |
| 17 | +import os |
| 18 | +from subprocess import CalledProcessError |
| 19 | +from subprocess import check_output |
| 20 | +import time |
| 21 | + |
| 22 | +from autoware_auto_perception_msgs.msg import DetectedObjects |
| 23 | +from autoware_auto_perception_msgs.msg import PredictedObjects |
| 24 | +from autoware_auto_perception_msgs.msg import TrackedObjects |
| 25 | +from autoware_auto_perception_msgs.msg import TrafficSignalArray as AutoTrafficSignalArray |
| 26 | +from autoware_perception_msgs.msg import TrafficSignalArray |
| 27 | +from geometry_msgs.msg import PoseWithCovarianceStamped |
| 28 | +from nav_msgs.msg import Odometry |
| 29 | +import psutil |
| 30 | +from rclpy.node import Node |
| 31 | +from rclpy.serialization import deserialize_message |
| 32 | +from rosbag2_py import StorageFilter |
| 33 | +from rosidl_runtime_py.utilities import get_message |
| 34 | +from sensor_msgs.msg import PointCloud2 |
| 35 | +from utils import open_reader |
| 36 | + |
| 37 | + |
| 38 | +class PerceptionReplayerCommon(Node): |
| 39 | + def __init__(self, args, name): |
| 40 | + super().__init__(name) |
| 41 | + self.args = args |
| 42 | + |
| 43 | + self.ego_pose = None |
| 44 | + self.rosbag_objects_data = [] |
| 45 | + self.rosbag_ego_odom_data = [] |
| 46 | + self.rosbag_traffic_signals_data = [] |
| 47 | + self.is_auto_traffic_signals = False |
| 48 | + |
| 49 | + # subscriber |
| 50 | + self.sub_odom = self.create_subscription( |
| 51 | + Odometry, "/localization/kinematic_state", self.on_odom, 1 |
| 52 | + ) |
| 53 | + |
| 54 | + # publisher |
| 55 | + if self.args.detected_object: |
| 56 | + self.objects_pub = self.create_publisher( |
| 57 | + DetectedObjects, "/perception/object_recognition/detection/objects", 1 |
| 58 | + ) |
| 59 | + elif self.args.tracked_object: |
| 60 | + self.objects_pub = self.create_publisher( |
| 61 | + TrackedObjects, "/perception/object_recognition/tracking/objects", 1 |
| 62 | + ) |
| 63 | + else: |
| 64 | + self.objects_pub = self.create_publisher( |
| 65 | + PredictedObjects, "/perception/object_recognition/objects", 1 |
| 66 | + ) |
| 67 | + |
| 68 | + self.pointcloud_pub = self.create_publisher( |
| 69 | + PointCloud2, "/perception/obstacle_segmentation/pointcloud", 1 |
| 70 | + ) |
| 71 | + self.recorded_ego_pub_as_initialpose = self.create_publisher( |
| 72 | + PoseWithCovarianceStamped, "/initialpose", 1 |
| 73 | + ) |
| 74 | + |
| 75 | + self.recorded_ego_pub = self.create_publisher( |
| 76 | + Odometry, "/perception_reproducer/rosbag_ego_odom", 1 |
| 77 | + ) |
| 78 | + |
| 79 | + # load rosbag |
| 80 | + print("Stared loading rosbag") |
| 81 | + if os.path.isdir(args.bag): |
| 82 | + for bag_file in sorted(os.listdir(args.bag)): |
| 83 | + if bag_file.endswith(self.args.rosbag_format): |
| 84 | + self.load_rosbag(args.bag + "/" + bag_file) |
| 85 | + else: |
| 86 | + self.load_rosbag(args.bag) |
| 87 | + print("Ended loading rosbag") |
| 88 | + |
| 89 | + # temporary support old auto msgs |
| 90 | + if self.is_auto_traffic_signals: |
| 91 | + self.auto_traffic_signals_pub = self.create_publisher( |
| 92 | + AutoTrafficSignalArray, "/perception/traffic_light_recognition/traffic_signals", 1 |
| 93 | + ) |
| 94 | + else: |
| 95 | + self.traffic_signals_pub = self.create_publisher( |
| 96 | + TrafficSignalArray, "/perception/traffic_light_recognition/traffic_signals", 1 |
| 97 | + ) |
| 98 | + |
| 99 | + # wait for ready to publish/subscribe |
| 100 | + time.sleep(1.0) |
| 101 | + |
| 102 | + def on_odom(self, odom): |
| 103 | + self.ego_pose = odom.pose.pose |
| 104 | + |
| 105 | + def load_rosbag(self, rosbag2_path: str): |
| 106 | + reader = open_reader(str(rosbag2_path)) |
| 107 | + |
| 108 | + topic_types = reader.get_all_topics_and_types() |
| 109 | + # Create a map for quicker lookup |
| 110 | + type_map = {topic_types[i].name: topic_types[i].type for i in range(len(topic_types))} |
| 111 | + |
| 112 | + objects_topic = ( |
| 113 | + "/perception/object_recognition/detection/objects" |
| 114 | + if self.args.detected_object |
| 115 | + else "/perception/object_recognition/tracking/objects" |
| 116 | + if self.args.tracked_object |
| 117 | + else "/perception/object_recognition/objects" |
| 118 | + ) |
| 119 | + ego_odom_topic = "/localization/kinematic_state" |
| 120 | + traffic_signals_topic = "/perception/traffic_light_recognition/traffic_signals" |
| 121 | + topic_filter = StorageFilter(topics=[objects_topic, ego_odom_topic, traffic_signals_topic]) |
| 122 | + reader.set_filter(topic_filter) |
| 123 | + |
| 124 | + while reader.has_next(): |
| 125 | + (topic, data, stamp) = reader.read_next() |
| 126 | + msg_type = get_message(type_map[topic]) |
| 127 | + msg = deserialize_message(data, msg_type) |
| 128 | + if topic == objects_topic: |
| 129 | + self.rosbag_objects_data.append((stamp, msg)) |
| 130 | + if topic == ego_odom_topic: |
| 131 | + self.rosbag_ego_odom_data.append((stamp, msg)) |
| 132 | + if topic == traffic_signals_topic: |
| 133 | + self.rosbag_traffic_signals_data.append((stamp, msg)) |
| 134 | + self.is_auto_traffic_signals = ( |
| 135 | + "autoware_auto_perception_msgs" in type(msg).__module__ |
| 136 | + ) |
| 137 | + |
| 138 | + def kill_online_perception_node(self): |
| 139 | + # kill node if required |
| 140 | + kill_process_name = None |
| 141 | + if self.args.detected_object: |
| 142 | + kill_process_name = "dummy_perception_publisher_node" |
| 143 | + elif self.args.tracked_object: |
| 144 | + kill_process_name = "multi_object_tracker" |
| 145 | + else: |
| 146 | + kill_process_name = "map_based_prediction" |
| 147 | + if kill_process_name: |
| 148 | + try: |
| 149 | + pid = check_output(["pidof", kill_process_name]) |
| 150 | + process = psutil.Process(int(pid[:-1])) |
| 151 | + process.terminate() |
| 152 | + except CalledProcessError: |
| 153 | + pass |
| 154 | + |
| 155 | + def binary_search(self, data, timestamp): |
| 156 | + if data[-1][0] < timestamp: |
| 157 | + return data[-1][1] |
| 158 | + elif data[0][0] > timestamp: |
| 159 | + return data[0][1] |
| 160 | + |
| 161 | + low, high = 0, len(data) - 1 |
| 162 | + |
| 163 | + while low <= high: |
| 164 | + mid = (low + high) // 2 |
| 165 | + if data[mid][0] < timestamp: |
| 166 | + low = mid + 1 |
| 167 | + elif data[mid][0] > timestamp: |
| 168 | + high = mid - 1 |
| 169 | + else: |
| 170 | + return data[mid][1] |
| 171 | + |
| 172 | + # Return the next timestamp's data if available |
| 173 | + if low < len(data): |
| 174 | + return data[low][1] |
| 175 | + return None |
| 176 | + |
| 177 | + def find_topics_by_timestamp(self, timestamp): |
| 178 | + objects_data = self.binary_search(self.rosbag_objects_data, timestamp) |
| 179 | + traffic_signals_data = self.binary_search(self.rosbag_traffic_signals_data, timestamp) |
| 180 | + return objects_data, traffic_signals_data |
| 181 | + |
| 182 | + def find_ego_odom_by_timestamp(self, timestamp): |
| 183 | + return self.binary_search(self.rosbag_ego_odom_data, timestamp) |
0 commit comments