Skip to content

Commit 323c350

Browse files
committed
add theta x and y publishing for dynamic PID targetting
1 parent 7064295 commit 323c350

File tree

3 files changed

+54
-6
lines changed

3 files changed

+54
-6
lines changed

python/src/blimp_vision/blimp_vision/camera_node.py

Lines changed: 52 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,9 @@ def __init__(self):
8383
# Set up GStreamer streaming pipeline.
8484
self._setup_gstreamer()
8585

86+
# Set up Video Recorder if flag enabled.
87+
self._setup_videosaver()
88+
8689
def _declare_parameters(self):
8790
self.declare_parameters(
8891
namespace='',
@@ -159,6 +162,15 @@ def _load_calibration(self):
159162
# Use the vertical focal length (the [1,1] element) from the calibration file.
160163
self.goal_vertical_focal = self.left_camera_matrix[1, 1]
161164

165+
self.h_fov = 2 * np.arctan(((self.left_image_size[1] / 2) / self.left_camera_matrix[0][0]))
166+
self.v_fov = 2 * np.arctan(((self.left_image_size[0] / 2) / self.left_camera_matrix[1][1]))
167+
168+
self.fx = self.left_camera_matrix[0][0]
169+
self.fy = self.left_camera_matrix[1][1]
170+
171+
self.cx = self.left_camera_matrix[0][2]
172+
self.cy = self.left_camera_matrix[1][2]
173+
162174
self.get_logger().info('Successfully loaded camera calibration files')
163175
except Exception as e:
164176
self.get_logger().error(f'Failed to load calibration files: {e}')
@@ -246,6 +258,23 @@ def _setup_gstreamer(self):
246258
self.stream_timestamp = 0.0
247259
self.frame_duration = 1.0 / 12.0
248260

261+
def _setup_videosaver(self):
262+
if self.save_frames:
263+
# Ensure the save directory exists.
264+
os.makedirs(self.save_location, exist_ok=True)
265+
# Capture an initial frame to determine the frame size.
266+
ret, frame = self.capture_frame()
267+
if ret:
268+
# Left frame dimensions: half the width of the full frame.
269+
frame_width = frame.shape[1] // 2
270+
frame_height = frame.shape[0]
271+
fourcc = cv2.VideoWriter_fourcc(*'XVID')
272+
video_path = os.path.join(self.save_location, f'{time.time()}.avi')
273+
self.video_writer = cv2.VideoWriter(video_path, fourcc, 12, (frame_width, frame_height))
274+
self.get_logger().info(f'Video recording initialized: {video_path}')
275+
else:
276+
self.get_logger().error('Failed to capture initial frame for video writer initialization')
277+
249278
def capture_frame(self):
250279
"""Capture a frame from the camera."""
251280
return self.cap.read()
@@ -398,6 +427,14 @@ def compute_grid_depths(self, disparity):
398427
grid_depths.append(depth)
399428
return grid_depths
400429

430+
def get_bbox_theta_offsets(self, bbox, depth):
431+
offset_x = bbox[0] - self.cx
432+
offset_y = bbox[1] - self.cy
433+
434+
theta_x = np.arctan(offset_x / self.fx)
435+
theta_y = np.arctan(offset_y / self.fy)
436+
return theta_x, theta_y
437+
401438
def camera_callback(self):
402439
"""Main callback: capture, process, stream frames, and publish metrics and grid distances."""
403440
timing = {}
@@ -439,7 +476,7 @@ def camera_callback(self):
439476
if self.ball_search_mode:
440477
disp_depth = self.filter_disparity(disparity, detection_msg.bbox)
441478
regr_depth = self.mono_depth_estimator(detection_msg.bbox[2], detection_msg.bbox[3])
442-
detection_msg.depth = np.min([disp_depth, regr_depth]) if (detection_msg.bbox[2]*detection_msg.bbox[3]) > 400 else 100.0
479+
detection_msg.depth = np.min([disp_depth, regr_depth]) if (np.square(np.max([detection_msg.bbox[2], detection_msg.bbox[3]])) > 900.0) else 100.0
443480
else:
444481
# Goal detection mode: use the calibrated vertical focal length and real goal height.
445482
# Assume detection_msg.obj_class contains a string like "circle", "square", or "triangle"
@@ -450,18 +487,24 @@ def camera_callback(self):
450487
real_height = self.goal_triangle_height
451488
elif "square" in obj_class:
452489
real_height = self.goal_square_height
453-
490+
raw_goal_height = (self.goal_vertical_focal * real_height) / detection_msg.bbox[3]
491+
raw_depth = (self.goal_vertical_focal * real_height) / detection_msg.bbox[3]
454492
# Use the goal_vertical_focal loaded from calibration.
455-
detection_msg.depth = (self.goal_vertical_focal * real_height) / detection_msg.bbox[3]
493+
detection_msg.depth = 0.31804 * np.exp(0.59 * raw_depth) + 1.424
456494

495+
theta_x, theta_y = self.get_bbox_theta_offsets(detection_msg.bbox, detection_msg.depth)
457496
self.pub_detections.publish(Float64MultiArray(data=[
458497
detection_msg.bbox[0],
459498
detection_msg.bbox[1],
460499
detection_msg.depth,
461500
detection_msg.track_id * 1.0,
462-
(not self.ball_search_mode) * 1.0
501+
(not self.ball_search_mode) * 1.0,
502+
theta_x,
503+
theta_y
463504
]))
464505

506+
507+
465508
# Prepare debug view.
466509
debug_view = left_frame.copy()
467510
if detection_msg is not None:
@@ -496,6 +539,9 @@ def camera_callback(self):
496539
perf_msg.fps = (1 / perf_msg.total_time) * 1000
497540
self.pub_performance.publish(perf_msg)
498541

542+
if self.save_frames:
543+
self.video_writer.write(left_frame)
544+
499545

500546
def main(args=None):
501547
import rclpy
@@ -506,6 +552,8 @@ def main(args=None):
506552
except KeyboardInterrupt:
507553
pass
508554
finally:
555+
if node.save_frames:
556+
node.video_writer.release()
509557
node.thread_pool.shutdown(wait=True)
510558
node.destroy_node()
511559
rclpy.shutdown()

python/src/blimp_vision/launch/2_SillyAh.launch.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ def generate_launch_description():
4141
'models/goal/goal_v11n_rknn_model'
4242
]),
4343
'verbose_mode': 'false',
44-
'save_frames': 'false',
44+
'save_frames': 'true',
4545
'save_location': 'frames/'
4646
}.items()
4747
)

python/src/blimp_vision/launch/6_SuperBeef.launch.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ def generate_launch_description():
2626
IncludeLaunchDescription(
2727
PythonLaunchDescriptionSource([base_launch_file]),
2828
launch_arguments={
29-
'camera_number': '6',
29+
'camera_number': '13',
3030
'device_path': '/dev/video0',
3131
'calibration_path': PathJoinSubstitution([
3232
get_package_share_directory('blimp_vision'),

0 commit comments

Comments
 (0)