Lidar now reads from pcap with multiple remotes
This commit is contained in:
parent
f925f1e31a
commit
c4ef961a78
2 changed files with 113 additions and 39 deletions
|
|
@ -26,10 +26,11 @@ autostart=false
|
||||||
# command=uv run trap_video_source --homography ../DATASETS/hof3/homography.json --video-src ../DATASETS/hof3/hof3-cam-demo-twoperson.mp4 --calibration ../DATASETS/hof3/calibration.json --video-loop
|
# command=uv run trap_video_source --homography ../DATASETS/hof3/homography.json --video-src ../DATASETS/hof3/hof3-cam-demo-twoperson.mp4 --calibration ../DATASETS/hof3/calibration.json --video-loop
|
||||||
command=uv run trap_video_source --homography ../DATASETS/hof3-cam-baumer-cropped/homography.json --video-src gige://../DATASETS/hof3-cam-baumer-cropped/gige_config.json --calibration ../DATASETS/hof3-cam-baumer-cropped/calibration.json
|
command=uv run trap_video_source --homography ../DATASETS/hof3-cam-baumer-cropped/homography.json --video-src gige://../DATASETS/hof3-cam-baumer-cropped/gige_config.json --calibration ../DATASETS/hof3-cam-baumer-cropped/calibration.json
|
||||||
directory=%(here)s
|
directory=%(here)s
|
||||||
directory=%(here)s
|
|
||||||
|
|
||||||
[program:tracker]
|
[program:tracker]
|
||||||
command=uv run trap_tracker --smooth-tracks
|
# command=uv run trap_tracker --smooth-tracks
|
||||||
|
command=uv run trap_lidar --min-box-area 0 --viz --smooth-tracks
|
||||||
|
environment=DISPLAY=":0"
|
||||||
directory=%(here)s
|
directory=%(here)s
|
||||||
|
|
||||||
[program:stage]
|
[program:stage]
|
||||||
|
|
@ -37,6 +38,12 @@ directory=%(here)s
|
||||||
command=uv run trap_stage --verbose --camera-fps 12 --homography ../DATASETS/hof3/homography.json --calibration ../DATASETS/hof3/calibration.json --cache-path /tmp/history_cache-hof3.pcl --tracker-output-dir EXPERIMENTS/raw/hof3/
|
command=uv run trap_stage --verbose --camera-fps 12 --homography ../DATASETS/hof3/homography.json --calibration ../DATASETS/hof3/calibration.json --cache-path /tmp/history_cache-hof3.pcl --tracker-output-dir EXPERIMENTS/raw/hof3/
|
||||||
directory=%(here)s
|
directory=%(here)s
|
||||||
|
|
||||||
|
[program:settings]
|
||||||
|
command=uv run trap_settings
|
||||||
|
autostart=true
|
||||||
|
environment=DISPLAY=":0"
|
||||||
|
directory=%(here)s
|
||||||
|
|
||||||
[program:predictor]
|
[program:predictor]
|
||||||
command=uv run trap_prediction --eval_device cuda:0 --model_dir EXPERIMENTS/models/models_20241229_21_35_13_hof3-m2-ud-split-conv12-f2.0-map-2024-12-29/ --num-samples 1 --map_encoding --eval_data_dict EXPERIMENTS/trajectron-data/hof3-m2-ud-split-nostep-conv12-f2.0-map-2024-12-29_val.pkl --prediction-horizon 120 --gmm-mode True --z-mode
|
command=uv run trap_prediction --eval_device cuda:0 --model_dir EXPERIMENTS/models/models_20241229_21_35_13_hof3-m2-ud-split-conv12-f2.0-map-2024-12-29/ --num-samples 1 --map_encoding --eval_data_dict EXPERIMENTS/trajectron-data/hof3-m2-ud-split-nostep-conv12-f2.0-map-2024-12-29_val.pkl --prediction-horizon 120 --gmm-mode True --z-mode
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ from pathlib import Path
|
||||||
import select
|
import select
|
||||||
import socket
|
import socket
|
||||||
from typing import DefaultDict, Dict, List
|
from typing import DefaultDict, Dict, List
|
||||||
|
import dpkt
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import open3d as o3d
|
import open3d as o3d
|
||||||
import shapely
|
import shapely
|
||||||
|
|
@ -19,6 +20,8 @@ from trap.node import Node
|
||||||
from bytetracker import BYTETracker
|
from bytetracker import BYTETracker
|
||||||
import velodyne_decoder as vd
|
import velodyne_decoder as vd
|
||||||
|
|
||||||
|
from trap.tracker import Smoother
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger('lidar')
|
logger = logging.getLogger('lidar')
|
||||||
# import sort
|
# import sort
|
||||||
|
|
@ -27,7 +30,82 @@ logger = logging.getLogger('lidar')
|
||||||
|
|
||||||
# This is what is used by velodyne_decoder internally on read_pcap:
|
# This is what is used by velodyne_decoder internally on read_pcap:
|
||||||
ResultTuple = namedtuple("StampCloudTuple", ("stamp", "points"))
|
ResultTuple = namedtuple("StampCloudTuple", ("stamp", "points"))
|
||||||
# In bag reader this is: Result = namedtuple("ResultTuple", ("stamp", "points", "topic", "frame_id"))
|
|
||||||
|
def read_pcap(pcap_file, config, as_pcl_structs=False):
|
||||||
|
"""
|
||||||
|
Adapted from velodyne_decoder to handle multiple streams
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
remotes = []
|
||||||
|
decoders = defaultdict(lambda: vd.StreamDecoder(config))
|
||||||
|
|
||||||
|
with open(pcap_file, 'rb') as f:
|
||||||
|
pcap = dpkt.pcap.Reader(f)
|
||||||
|
|
||||||
|
latest_result = {}
|
||||||
|
|
||||||
|
start_t = time.perf_counter()
|
||||||
|
start_timestamp, _ = next(pcap)
|
||||||
|
|
||||||
|
prev_data = bytes()
|
||||||
|
|
||||||
|
for timestamp, packet in pcap:
|
||||||
|
|
||||||
|
# first we need to figure out when to yield the data. Simulating exhausting the socket
|
||||||
|
# this should happen when the currently reading timestamp > the wall clock timediff
|
||||||
|
now_t = time.perf_counter()
|
||||||
|
|
||||||
|
time_passed = now_t - start_t
|
||||||
|
pkg_time_passed = timestamp - start_timestamp
|
||||||
|
|
||||||
|
if pkg_time_passed > time_passed:
|
||||||
|
if len(latest_result):
|
||||||
|
# return data if available
|
||||||
|
yield latest_result
|
||||||
|
|
||||||
|
|
||||||
|
# now delay processing the package until it should 'arrive'
|
||||||
|
# but, time may have passed after returning from the yield:
|
||||||
|
time_passed = time.perf_counter() - start_t
|
||||||
|
time.sleep(max(0, pkg_time_passed - time_passed))
|
||||||
|
|
||||||
|
latest_result = {}
|
||||||
|
|
||||||
|
|
||||||
|
try:
|
||||||
|
eth = dpkt.ethernet.Ethernet(packet)
|
||||||
|
if not isinstance(eth.data, dpkt.ip.IP):
|
||||||
|
continue
|
||||||
|
|
||||||
|
ip = eth.data
|
||||||
|
if not isinstance(ip.data, dpkt.udp.UDP):
|
||||||
|
continue
|
||||||
|
|
||||||
|
udp = ip.data
|
||||||
|
|
||||||
|
src_ip = socket.inet_ntoa(ip.src)
|
||||||
|
src_port = udp.sport
|
||||||
|
|
||||||
|
data = udp.data
|
||||||
|
|
||||||
|
if len(data) != vd.PACKET_SIZE:
|
||||||
|
# other udp data
|
||||||
|
continue
|
||||||
|
|
||||||
|
if src_ip not in remotes:
|
||||||
|
logger.info(f"New remote {src_ip}")
|
||||||
|
remotes.append(src_ip)
|
||||||
|
|
||||||
|
result = decoders[src_ip].decode(timestamp, data, as_pcl_structs)
|
||||||
|
|
||||||
|
|
||||||
|
if result is not None:
|
||||||
|
latest_result[src_ip] = ResultTuple(*result)
|
||||||
|
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"Error processing packet: {e}")
|
||||||
|
|
||||||
|
|
||||||
# like velodyne_decoder.read_pcap but with live streamed data
|
# like velodyne_decoder.read_pcap but with live streamed data
|
||||||
|
|
@ -44,7 +122,6 @@ def read_live_data(ip, port, config, as_pcl_structs=False):
|
||||||
|
|
||||||
remotes = []
|
remotes = []
|
||||||
|
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
readable, _, _ = select.select([sock], [], [], 1.0)
|
readable, _, _ = select.select([sock], [], [], 1.0)
|
||||||
latest_result = {}
|
latest_result = {}
|
||||||
|
|
@ -121,19 +198,9 @@ def filter_pcd_points_below_z(pcd: o3d.geometry.PointCloud, z_threshold: float)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def filter_pcd_points_outside_bounds(pcd: o3d.geometry.PointCloud, polygon: shapely.Polygon) -> o3d.geometry.PointCloud:
|
|
||||||
points = np.asarray(pcd.points)
|
|
||||||
geoms = np.array([shapely.Point(p[0], p[1]) for p in points])
|
|
||||||
|
|
||||||
inside_idxs = shapely.contains(polygon, geoms)
|
|
||||||
return pcd.select_by_index(np.where(inside_idxs)[0])
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class Lidar(Node):
|
class Lidar(Node):
|
||||||
def setup(self):
|
def setup(self):
|
||||||
if self.config.smooth_tracks:
|
|
||||||
self.logger.warning("Smoothing not yet implemented")
|
|
||||||
self.track_sock = self.pub(self.config.zmq_trajectory_addr)
|
self.track_sock = self.pub(self.config.zmq_trajectory_addr)
|
||||||
self.detection_sock = self.pub(self.config.zmq_detection_addr)
|
self.detection_sock = self.pub(self.config.zmq_detection_addr)
|
||||||
|
|
||||||
|
|
@ -142,8 +209,10 @@ class Lidar(Node):
|
||||||
|
|
||||||
if self.config.pcap and self.config.pcap.exists():
|
if self.config.pcap and self.config.pcap.exists():
|
||||||
# self.sequence_generator = vd.read_pcap(self.config.pcap, config, time_range=[1761139039+100,None])
|
# self.sequence_generator = vd.read_pcap(self.config.pcap, config, time_range=[1761139039+100,None])
|
||||||
self.sequence_generator = vd.read_pcap(self.config.pcap, config, time_range=[1761139039+30,None])
|
# read_pcap(self.config.pcap)
|
||||||
# self.sequence_generator = vd.read_pcap(self.config.pcap, config)
|
# self.sequence_generator = vd.read_pcap(self.config.pcap, config)
|
||||||
|
self.sequence_generator = read_pcap(self.config.pcap, config)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
self.sequence_generator = read_live_data(self.config.ip, self.config.port, config)
|
self.sequence_generator = read_live_data(self.config.ip, self.config.port, config)
|
||||||
|
|
||||||
|
|
@ -175,15 +244,20 @@ class Lidar(Node):
|
||||||
|
|
||||||
self.map_outline_volume = o3d.visualization.SelectionPolygonVolume()
|
self.map_outline_volume = o3d.visualization.SelectionPolygonVolume()
|
||||||
self.map_outline_volume.orthogonal_axis = "Z" # extrusion direction of polygon
|
self.map_outline_volume.orthogonal_axis = "Z" # extrusion direction of polygon
|
||||||
self.map_outline_volume.axis_min = 0
|
self.map_outline_volume.axis_min = .3 # filter from slightly above ground
|
||||||
self.map_outline_volume.axis_max = 3.5
|
self.map_outline_volume.axis_max = 2.2
|
||||||
polygon_points =np.array([[*p.position, 0] for p in lines[0].points])
|
polygon_points =np.array([[*p.position, 0] for p in lines[0].points])
|
||||||
|
|
||||||
|
|
||||||
self.map_outline_volume.bounding_polygon = o3d.utility.Vector3dVector(polygon_points)
|
self.map_outline_volume.bounding_polygon = o3d.utility.Vector3dVector(polygon_points)
|
||||||
|
|
||||||
|
|
||||||
|
if self.config.smooth_tracks:
|
||||||
|
fps = 12 # TODO)) make configurable, or get from cam
|
||||||
|
logger.info(f"Smoother enabled, assuming {fps} fps")
|
||||||
|
self.smoother = Smoother(window_len=fps*5, convolution=False)
|
||||||
|
else:
|
||||||
|
logger.info("Smoother Disabled (enable with --smooth-tracks)")
|
||||||
|
|
||||||
self.remotes = {}
|
self.remotes = {}
|
||||||
|
|
||||||
|
|
@ -289,8 +363,16 @@ class Lidar(Node):
|
||||||
frame_idx += 1
|
frame_idx += 1
|
||||||
|
|
||||||
lidar_items = next(self.sequence_generator)
|
lidar_items = next(self.sequence_generator)
|
||||||
|
|
||||||
|
|
||||||
|
# remove some keys. Don't use dict comprehension to not copy the data unnecesarrily
|
||||||
|
remove_keys = [ip for ip in lidar_items.keys() if not self.get_setting(f"lidar.{self.ip_to_name(ip)}.enabled", True)]
|
||||||
|
for key in remove_keys:
|
||||||
|
del lidar_items[key]
|
||||||
|
|
||||||
|
|
||||||
current_pcd = self.build_cloud(lidar_items)
|
current_pcd = self.build_cloud(lidar_items)
|
||||||
|
|
||||||
# current_pcd = filter_pcd_points_below_z(current_pcd, .1)
|
# current_pcd = filter_pcd_points_below_z(current_pcd, .1)
|
||||||
# current_pcd = filter_raw_points_by_distance(current_pcd, 10)
|
# current_pcd = filter_raw_points_by_distance(current_pcd, 10)
|
||||||
|
|
||||||
|
|
@ -344,7 +426,7 @@ class Lidar(Node):
|
||||||
stat_static = len(filtered_pcd.points)
|
stat_static = len(filtered_pcd.points)
|
||||||
counter.set('lidar.unstatic', stat_static)
|
counter.set('lidar.unstatic', stat_static)
|
||||||
|
|
||||||
if self.room_filter.initialised:
|
if self.room_filter.initialised and self.get_setting('lidar.tracking_enabled',True):
|
||||||
# filtered_pcd, _ = filtered_pcd.remove_statistical_outlier(nb_neighbors=20, std_ratio=2.0)
|
# filtered_pcd, _ = filtered_pcd.remove_statistical_outlier(nb_neighbors=20, std_ratio=2.0)
|
||||||
denoised_pcd, inlier_indexes = filtered_pcd.remove_radius_outlier(nb_points=5, radius=0.8)
|
denoised_pcd, inlier_indexes = filtered_pcd.remove_radius_outlier(nb_points=5, radius=0.8)
|
||||||
stat_denoise = len(filtered_pcd.points)
|
stat_denoise = len(filtered_pcd.points)
|
||||||
|
|
@ -359,7 +441,7 @@ class Lidar(Node):
|
||||||
filtered_pcd = denoised_pcd
|
filtered_pcd = denoised_pcd
|
||||||
|
|
||||||
# down sample
|
# down sample
|
||||||
filtered_pcd = filtered_pcd.voxel_down_sample(voxel_size=0.01)
|
filtered_pcd = filtered_pcd.voxel_down_sample(voxel_size=0.08)
|
||||||
stat_downsampled = len(filtered_pcd.points)
|
stat_downsampled = len(filtered_pcd.points)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -369,19 +451,11 @@ class Lidar(Node):
|
||||||
|
|
||||||
|
|
||||||
counter.set('lidar.downsampled', stat_downsampled)
|
counter.set('lidar.downsampled', stat_downsampled)
|
||||||
|
|
||||||
# print(f"in: {stat_orig} / crop: {stat_crop} / static object removal: {stat_static} / outlier removal: {stat_denoise} / downsample: {stat_downsampled}")
|
|
||||||
|
|
||||||
# pcd.points = filtered_pcd.points
|
|
||||||
|
|
||||||
points_2d = project_to_xy(filtered_pcd)
|
points_2d = project_to_xy(filtered_pcd)
|
||||||
|
|
||||||
# points_2d = project_to_plane(filtered_pcd, ground_normal)
|
labels = cluster_2d(points_2d, .3, min_samples=8)
|
||||||
|
|
||||||
# non_ground = remove_ground(pcd)
|
|
||||||
# points_2d = project_to_plane(pro)
|
|
||||||
# points_2d = non_ground[:, :2]
|
|
||||||
labels = cluster_2d(points_2d)
|
|
||||||
|
|
||||||
# boxes, centroids = get_cluster_boxes(points_2d, labels, min_area= 0.3*0.3)
|
# boxes, centroids = get_cluster_boxes(points_2d, labels, min_area= 0.3*0.3)
|
||||||
boxes, centroids = get_cluster_boxes(points_2d, labels, min_area= self.config.min_box_area)
|
boxes, centroids = get_cluster_boxes(points_2d, labels, min_area= self.config.min_box_area)
|
||||||
|
|
@ -390,10 +464,6 @@ class Lidar(Node):
|
||||||
|
|
||||||
detections = np.ones((boxes.shape[0],boxes.shape[1]+2));
|
detections = np.ones((boxes.shape[0],boxes.shape[1]+2));
|
||||||
detections[:,:-2] = boxes
|
detections[:,:-2] = boxes
|
||||||
# detections = np.array([
|
|
||||||
# # coords in xyxy
|
|
||||||
# box.tolist() + [1,1] for box in boxes
|
|
||||||
# ])
|
|
||||||
|
|
||||||
online_tracks = self.tracker.update(detections)
|
online_tracks = self.tracker.update(detections)
|
||||||
self.logger.debug(f"online tracks: {[t[4] for t in online_tracks]}")
|
self.logger.debug(f"online tracks: {[t[4] for t in online_tracks]}")
|
||||||
|
|
@ -476,15 +546,12 @@ class Lidar(Node):
|
||||||
if self.config.viz:
|
if self.config.viz:
|
||||||
|
|
||||||
if len(dropped_pcds):
|
if len(dropped_pcds):
|
||||||
print(dropped_pcds)
|
|
||||||
dropped_pcd = sum(dropped_pcds, o3d.geometry.PointCloud())
|
dropped_pcd = sum(dropped_pcds, o3d.geometry.PointCloud())
|
||||||
else:
|
else:
|
||||||
dropped_pcd = o3d.geometry.PointCloud()
|
dropped_pcd = o3d.geometry.PointCloud()
|
||||||
live_pcd.points = dropped_pcd.points
|
live_pcd.points = dropped_pcd.points
|
||||||
live_pcd.colors = dropped_pcd.colors
|
live_pcd.colors = dropped_pcd.colors
|
||||||
|
|
||||||
print(live_pcd)
|
|
||||||
|
|
||||||
color = [0.5, 0.5, 0.7] if self.room_filter.initialised else [0.7, 0.7, 0.7]
|
color = [0.5, 0.5, 0.7] if self.room_filter.initialised else [0.7, 0.7, 0.7]
|
||||||
# live_pcd.paint_uniform_color(color)
|
# live_pcd.paint_uniform_color(color)
|
||||||
live_filtered_pcd.paint_uniform_color([0.2, 0.7, 0.2])
|
live_filtered_pcd.paint_uniform_color([0.2, 0.7, 0.2])
|
||||||
|
|
@ -562,7 +629,7 @@ class Lidar(Node):
|
||||||
argparser.add_argument('--map-outlines',
|
argparser.add_argument('--map-outlines',
|
||||||
help='specify a map (svg-file) from which to load boundary of the map. Any points outside will be filtered (removing floor and walls)',
|
help='specify a map (svg-file) from which to load boundary of the map. Any points outside will be filtered (removing floor and walls)',
|
||||||
type=Path,
|
type=Path,
|
||||||
default="../DATASETS/hof3/map_hof_boundaries.svg")
|
default="../DATASETS/hof3/map_hof_lidar_boundaries.svg")
|
||||||
|
|
||||||
return argparser
|
return argparser
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue