Skip to content

Commit b51e87e

Browse files
committed
Putting this here for now
1 parent b62c6fc commit b51e87e

4 files changed

Lines changed: 221 additions & 32 deletions

File tree

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
import math
2+
from typing import Dict, List
3+
4+
from GEMstack.onboard.perception.IdTracker import IdTracker
5+
from GEMstack.onboard.perception.PrevAgent import PrevAgent
6+
from GEMstack.state.agent import AgentState
7+
8+
9+
class AgentTracker():
10+
"""Associates and tracks AgentState agents.
11+
"""
12+
def __init__(self):
13+
# List of PrevAgent objects (each keeps track of the last seen state and time since seen)
14+
self.prev_agents: List[PrevAgent] = []
15+
# List of currently visible AgentState objects
16+
self.current_agents: List[AgentState] = []
17+
# Maximum time (in seconds) to keep a lost agent before dropping it.
18+
self.drop_agent_t: float = 1.0
19+
# Id tracker for creating new unique pedestrian IDs.
20+
self.id_tracker = IdTracker()
21+
22+
def assign_ids(self, agents: list) -> Dict[str,AgentState]:
23+
# Act with the assumption that you are being sent a list of AgentState objects and you need to use the object fields to keep track of them for your task
24+
# Further act on the assumption that we will decide the id's of the pedestrians by assuming that 2 pedestrians are the same pedestrian if a
25+
# previously stored AgentState pose and dimensions overlap with a newly passed in AgentState
26+
# Act on the assumption that the AgentState objects are all in reference to the start frame of the vehicle
27+
# some helper functions in this class, LostAgent.py, and IdTracker.py have been created to try to help you out with your task.
28+
# Assume that the output returned from this function will be a dictionary of AgentState objects with the key corresponding to their id
29+
"""
30+
Associates new AgentState objects with existing tracked agents based on overlap.
31+
If an agent does not match any previously tracked agent, a new unique id is assigned.
32+
Also updates the “lost” time for agents that are not matched in this frame.
33+
34+
Parameters:
35+
agents (list): List of AgentState objects for the current frame
36+
(already converted to the start frame of reference).
37+
38+
Returns:
39+
Dict[str, AgentState]: Dictionary mapping agent id (as a string) to AgentState.
40+
"""
41+
dt = 1.0 # Assuming a fixed time-step of 1 second (for simplicity)
42+
output_agents: Dict[str, AgentState] = {}
43+
matched_ids = set()
44+
updated_prev_agents: List[PrevAgent] = []
45+
46+
# Process each new agent from the current frame.
47+
for new_agent in agents:
48+
found_match = None
49+
# Look for a previously tracked agent whose bounding box overlaps.
50+
for prev in self.prev_agents:
51+
if prev.last_id in matched_ids:
52+
continue # Already matched with another new agent.
53+
if self.__agents_overlap(prev.last_state, new_agent):
54+
found_match = prev
55+
break
56+
57+
if found_match is not None:
58+
# update velocity using the change in position.
59+
if hasattr(new_agent, 'velocity'):
60+
new_agent.velocity = self.__calculate_velocity(found_match.last_state, new_agent, dt)
61+
62+
# Update the matched agent’s state and reset its lost-time counter.
63+
found_match.last_state = new_agent
64+
found_match.time_since_seen = 0.0
65+
matched_ids.add(found_match.last_id)
66+
output_agents[str(found_match.last_id)] = new_agent
67+
updated_prev_agents.append(found_match)
68+
else:
69+
# No match found – assign a new unique id.
70+
new_id = self.id_tracker.get_new_ped_id()
71+
new_prev = PrevAgent(new_id, new_agent)
72+
# initialize velocity to 0.
73+
if hasattr(new_agent, 'velocity'):
74+
new_agent.velocity = 0.0
75+
output_agents[str(new_id)] = new_agent
76+
updated_prev_agents.append(new_prev)
77+
78+
# For all previously tracked agents that were not matched this frame,
79+
# update their time-since-seen and only keep them if they have not timed out.
80+
for prev in self.prev_agents:
81+
if prev.last_id not in matched_ids:
82+
prev.update_time(dt)
83+
if prev.time_since_seen < self.drop_agent_t:
84+
updated_prev_agents.append(prev)
85+
86+
# Save the updated list of tracked agents.
87+
self.prev_agents = updated_prev_agents
88+
# update current_agents to include only those seen in the current frame.
89+
self.current_agents = list(output_agents.values())
90+
return output_agents
91+
92+
def __convert_to_start_frame(self):
93+
"""Converts a list of AgentState agents from ouster Lidar frame of
94+
reference (which is in reference to the current frame) to start
95+
frame frame of reference
96+
"""
97+
# you can ignore this function akul
98+
pass
99+
100+
def __agents_overlap(self, ped1, ped2) -> bool:
101+
"""
102+
Determines if two AgentState objects overlap based on their pose and dimensions.
103+
104+
Assumes each AgentState has:
105+
- pose with attributes x and y.
106+
- dimensions with attributes width and height.
107+
108+
Returns:
109+
bool: True if they overlap; False otherwise.
110+
"""
111+
# Get first agent's properties.
112+
x1, y1 = ped1.pose.x, ped1.pose.y
113+
w1, h1 = ped1.dimensions.width, ped1.dimensions.height
114+
115+
# Get second agent's properties.
116+
x2, y2 = ped2.pose.x, ped2.pose.y
117+
w2, h2 = ped2.dimensions.width, ped2.dimensions.height
118+
119+
# Compute bounding boxes (assuming (x, y) is the center).
120+
left1, right1 = x1 - w1 / 2, x1 + w1 / 2
121+
top1, bottom1 = y1 - h1 / 2, y1 + h1 / 2
122+
123+
left2, right2 = x2 - w2 / 2, x2 + w2 / 2
124+
top2, bottom2 = y2 - h2 / 2, y2 + h2 / 2
125+
126+
# Check for overlap between the bounding boxes.
127+
overlap = not (right1 < left2 or left1 > right2 or bottom1 < top2 or top1 > bottom2)
128+
return overlap
129+
130+
def __calculate_velocity(self, old_state: AgentState, new_state: AgentState, dt: float) -> float:
131+
"""
132+
Calculates the velocity based on the change in pose over time.
133+
134+
Parameters:
135+
old_state (AgentState): The previous state.
136+
new_state (AgentState): The current state.
137+
dt (float): Time difference between the two states.
138+
139+
Returns:
140+
float: The computed velocity.
141+
"""
142+
dx = new_state.pose.x - old_state.pose.x
143+
dy = new_state.pose.y - old_state.pose.y
144+
return math.sqrt(dx * dx + dy * dy) / dt if dt > 0 else 0.0
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
class IdTracker():
2+
"""Abstracts out id tracking
3+
"""
4+
def __init__(self):
5+
self.__ped_id = 0
6+
7+
def get_new_ped_id(self) -> int:
8+
"""Returns a unique pedestrian id
9+
"""
10+
assigned_id = self.__id
11+
self.__ped_id += 1 # id will intentionally overflow to get back to 0
12+
return assigned_id
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# GEM imports:
2+
from ...state import AgentState
3+
4+
class PrevAgent():
5+
def __init__(self, last_id: int, last_state: AgentState):
6+
self.time_since_seen: float = 0.0 # Time since the agent was last seen in seconds
7+
self.last_id: int = last_id
8+
self.last_state: AgentState = last_state
9+
10+
def update_time(time: float):
11+
"""Updates the time since the agent was last seen
12+
"""
13+
if time <= 0:
14+
# TODO: log error here
15+
print("UPDATE TIME FOR LOST AGENT WAS LESS THAN OR EQUAL TO 0")
16+
else:
17+
self.time_since_seen += time

GEMstack/onboard/perception/pedestrian_detection.py

Lines changed: 48 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@
4848
from .pedestrian_detection_utils import *
4949
from ..interface.gem import GEMInterface
5050
from ..component import Component
51+
from AgentTracker import AgentTracker
52+
5153

5254
def box_to_fake_agent(box):
5355
"""Creates a fake agent state from an (x,y,w,h) bounding box.
@@ -101,6 +103,8 @@ def __init__(self, vehicle_interface : GEMInterface) -> None:
101103
# TF listener to get transformation from LiDAR to Camera
102104
self.tf_listener = tf.TransformListener()
103105

106+
self.agent_tracker = AgentTracker()
107+
104108
if self.debug: self.init_debug()
105109

106110
def init_debug(self,) -> None:
@@ -136,21 +140,21 @@ def find_dims(self, clusters: List[List[np.ndarray]]) -> List[np.ndarray]:
136140
# Work towards own tracking class instead of simple YOLO track?
137141
# Fix division by time
138142
# ret: Dict[track_id: vel[x, y, z]]
139-
def find_vels(self, track_ids: List[int], obj_centers: List[np.ndarray]) -> Dict[int, np.ndarray]:
140-
# Object not seen -> velocity = None
141-
track_id_center_map = dict(zip(track_ids, obj_centers))
142-
vels = defaultdict(lambda: np.array(())) # None is faster, np.array matches other find_ methods.
143-
144-
for prev_track_id, prev_agent in self.prev_agents.items():
145-
if prev_track_id in track_ids:
146-
# TODO: Add prev_agents to memory to avoid None velocity
147-
# We should only be missing prev pose on first sight of track_id Agent.
148-
# print("shape 1: ", track_id_center_map[prev_agent.track_id])
149-
# print("shape 2: ", np.array([prev_agent.pose.x, prev_agent.pose.y, prev_agent.pose.z]))
150-
# prev can be 3 separate Nones, current is just empty array... make this symmetrical
151-
if prev_agent.pose.x and prev_agent.pose.y and prev_agent.pose.z and track_id_center_map[prev_agent.track_id].shape == 3:
152-
vels[prev_track_id] = (track_id_center_map[prev_track_id] - np.array([prev_agent.pose.x, prev_agent.pose.y, prev_agent.pose.z])) / (self.curr_time - self.prev_time)
153-
return vels
143+
# def find_vels(self, track_ids: List[int], obj_centers: List[np.ndarray]) -> Dict[int, np.ndarray]:
144+
# # Object not seen -> velocity = None
145+
# track_id_center_map = dict(zip(track_ids, obj_centers))
146+
# vels = defaultdict(lambda: np.array(())) # None is faster, np.array matches other find_ methods.
147+
148+
# for prev_track_id, prev_agent in self.prev_agents.items():
149+
# if prev_track_id in track_ids:
150+
# # TODO: Add prev_agents to memory to avoid None velocity
151+
# # We should only be missing prev pose on first sight of track_id Agent.
152+
# # print("shape 1: ", track_id_center_map[prev_agent.track_id])
153+
# # print("shape 2: ", np.array([prev_agent.pose.x, prev_agent.pose.y, prev_agent.pose.z]))
154+
# # prev can be 3 separate Nones, current is just empty array... make this symmetrical
155+
# if prev_agent.pose.x and prev_agent.pose.y and prev_agent.pose.z and track_id_center_map[prev_agent.track_id].shape == 3:
156+
# vels[prev_track_id] = (track_id_center_map[prev_track_id] - np.array([prev_agent.pose.x, prev_agent.pose.y, prev_agent.pose.z])) / (self.curr_time - self.prev_time)
157+
# return vels
154158

155159

156160
# TODO: Separate debug/viz class, bbox and 2d 3d points funcs
@@ -231,25 +235,37 @@ def update_object_states(self, track_result: List[Results], extracted_pts_all: L
231235
# Separate numpy prob still faster for now
232236
obj_centers = self.find_centers(pedestrians_3d_pts)
233237
obj_dims = self.find_dims(pedestrians_3d_pts)
234-
obj_vels = self.find_vels(track_ids, obj_centers)
238+
239+
print("obj centers shape:")
240+
print(obj_centers.shape)
241+
242+
print("obj dims shape: ")
243+
print(obj_dims.shape)
244+
245+
# Assign id's based on whether or not agents overlap:
246+
# self.current_agents = self.agent_tracker.assign_ids(self.prev_agents, obj_centers, obj_dims)
247+
248+
249+
250+
# obj_vels = self.find_vels(track_ids, obj_centers)
235251

236252
# Update Current AgentStates
237-
for ind in range(num_objs):
238-
obj_center = (None, None, None) if obj_centers[ind].size == 0 else obj_centers[ind]
239-
obj_dim = (None, None, None) if obj_dims[ind].size == 0 else obj_dims[ind]
240-
self.current_agents[track_ids[ind]] = (
241-
AgentState(
242-
track_id = track_ids[ind],
243-
pose=ObjectPose(t=0, x=obj_center[0], y=obj_center[1], z=obj_center[2] ,yaw=0,pitch=0,roll=0,frame=ObjectFrameEnum.CURRENT),
244-
# (l, w, h)
245-
# TODO: confirm (z -> l, x -> w, y -> h)
246-
dimensions=(obj_dim[0], obj_dim[1], obj_dim[2]),
247-
outline=None,
248-
type=AgentEnum.PEDESTRIAN,
249-
activity=AgentActivityEnum.MOVING,
250-
velocity= None if obj_vels[track_ids[ind]].size == 0 else tuple(obj_vels[track_ids[ind]]),
251-
yaw_rate=0
252-
))
253+
# for ind in range(num_objs):
254+
# obj_center = (None, None, None) if obj_centers[ind].size == 0 else obj_centers[ind]
255+
# obj_dim = (None, None, None) if obj_dims[ind].size == 0 else obj_dims[ind]
256+
# self.current_agents[track_ids[ind]] = (
257+
# AgentState(
258+
# track_id = track_ids[ind],
259+
# pose=ObjectPose(t=0, x=obj_center[0], y=obj_center[1], z=obj_center[2] ,yaw=0,pitch=0,roll=0,frame=ObjectFrameEnum.CURRENT),
260+
# # (l, w, h)
261+
# # TODO: confirm (z -> l, x -> w, y -> h)
262+
# dimensions=(obj_dim[0], obj_dim[1], obj_dim[2]),
263+
# outline=None,
264+
# type=AgentEnum.PEDESTRIAN,
265+
# activity=AgentActivityEnum.MOVING,
266+
# velocity= None if obj_vels[track_ids[ind]].size == 0 else tuple(obj_vels[track_ids[ind]]),
267+
# yaw_rate=0
268+
# ))
253269

254270
def ouster_oak_callback(self, rgb_image_msg: Image, lidar_pc2_msg: PointCloud2):
255271
# Convert to cv2 image and run detector

0 commit comments

Comments
 (0)