Updates to use process for camera stream

This commit is contained in:
Rune Harlyk
2024-03-07 15:14:10 +01:00
parent 971418fce8
commit 9e40e01065
5 changed files with 54 additions and 36 deletions
+4 -2
View File
@@ -27,8 +27,9 @@ def main():
) )
kinematics = SpotModel() kinematics = SpotModel()
camera = WebCamera() # camera = WebCamera()
camera = PyBulletCamera(env.spot.quadruped) frame_queue = Queue(maxsize=10)
camera = PyBulletCamera(env.spot.quadruped, frame_queue=frame_queue)
imu = IMU() imu = IMU()
hardware_interface = PyBulletHardwareInterface(env) hardware_interface = PyBulletHardwareInterface(env)
# shared_controller_state = SharedState() # shared_controller_state = SharedState()
@@ -37,6 +38,7 @@ def main():
spot = Spot( spot = Spot(
kinematics, kinematics,
camera, camera,
frame_queue,
imu, imu,
hardware_interface, hardware_interface,
controller_interface, controller_interface,
+1 -1
View File
@@ -8,7 +8,7 @@ class WebCamera(CameraBase):
self._last_frame = None self._last_frame = None
super().__init__() super().__init__()
def get_frame(self): def get_image(self):
self._last_frame self._last_frame
def update(self) -> None: def update(self) -> None:
+32 -21
View File
@@ -1,5 +1,6 @@
from http.server import BaseHTTPRequestHandler, HTTPServer from http.server import BaseHTTPRequestHandler, HTTPServer
from threading import Thread from threading import Thread
from multiprocessing import Process
import cv2 import cv2
import time import time
@@ -12,42 +13,52 @@ class StreamingHandler(BaseHTTPRequestHandler):
self.end_headers() self.end_headers()
try: try:
while True: while True:
frame = self.server.camera.get_frame() if not self.server.frame_queue.empty():
_, jpeg = cv2.imencode('.jpg', frame) frame = self.server.frame_queue.get()
self.wfile.write(b'--frame\r\n')
self.send_header('Content-Type', 'image/jpeg') _, jpeg = cv2.imencode('.jpg', frame)
self.send_header('Content-Length', len(jpeg)) self.wfile.write(b'--frame\r\n')
self.end_headers() self.send_header('Content-Type', 'image/jpeg')
self.wfile.write(jpeg.tobytes()) self.send_header('Content-Length', len(jpeg))
self.wfile.write(b'\r\n') self.end_headers()
time.sleep(0.5) self.wfile.write(jpeg.tobytes())
self.wfile.write(b'\r\n')
time.sleep(0.1)
except Exception as e: except Exception as e:
print(f"Stream stopped: {e}") print(f"Stream stopped: {e}")
raise e
else: else:
self.send_error(404) self.send_error(404)
self.end_headers() self.end_headers()
class StreamingServer(HTTPServer): class StreamingServer(HTTPServer):
def __init__(self, server_address, camera): def __init__(self, server_address, frame_queue):
super().__init__(server_address, StreamingHandler) super().__init__(server_address, StreamingHandler)
self.camera = camera self.frame_queue = frame_queue
class StreamingServerThread: class StreamingServerThread:
def __init__(self, camera, port=8080): def __init__(self, frame_queue, port=8080):
self.camera = camera self.frame_queue = frame_queue
self.port = port self.port = port
self.server_thread = None self.server_thread = None
def start(self): def run_server(self, frame_queue):
def run_server(): address = ('', self.port)
address = ('', self.port) server = StreamingServer(address, frame_queue)
server = StreamingServer(address, self.camera) print(f"Starting server at http://localhost:{self.port}/stream.mjpg")
print(f"Starting server at http://localhost:{self.port}/stream.mjpg") server.serve_forever()
server.serve_forever()
self.server_thread = Thread(target=run_server) def start(self):
self.server_thread = Thread(target=self.run_server)
self.server_thread.daemon = True self.server_thread.daemon = True
self.server_thread.start() self.server_thread.start()
def start_process(self):
self.server_process = Process(target=self.run_server, args=(self.frame_queue,))
self.server_process.start()
def stop(self): def stop(self):
self.server_thread.join() if self.server_thread:
self.server_thread.join()
if self.server_process:
self.server_process.join()
+4 -5
View File
@@ -14,7 +14,7 @@ def map_value(x, from_low, from_high, to_low, to_high):
return ((x - from_low) / (from_high - from_low)) * (to_high - to_low) + to_low return ((x - from_low) / (from_high - from_low)) * (to_high - to_low) + to_low
class Spot: class Spot:
def __init__(self, kinematic, camera:CameraBase, imu, hardware_interface, controller_interface, shared_controller_state): def __init__(self, kinematic, camera:CameraBase, frame_queue, imu, hardware_interface, controller_interface, shared_controller_state):
self.kinematic = kinematic self.kinematic = kinematic
self.camera = camera self.camera = camera
self.imu = imu self.imu = imu
@@ -22,7 +22,7 @@ class Spot:
self.controller_interface = controller_interface self.controller_interface = controller_interface
self.shared_controller_state = shared_controller_state self.shared_controller_state = shared_controller_state
self.camera_stream = StreamingServerThread(self.camera) self.camera_stream = StreamingServerThread(frame_queue)
self.body_state = BodyState() self.body_state = BodyState()
self.gait_state = GaitState() self.gait_state = GaitState()
@@ -31,9 +31,8 @@ class Spot:
self.body_state.worldFeetPositions = copy.deepcopy(self.kinematic.WorldToFoot) self.body_state.worldFeetPositions = copy.deepcopy(self.kinematic.WorldToFoot)
def start(self): def start(self):
self.controller_interface.start() self.controller_interface.start()
self.camera_stream.start() self.camera_stream.start_process()
def stop(self): def stop(self):
self.controller_interface.stop() self.controller_interface.stop()
@@ -52,7 +51,7 @@ class Spot:
# self.gait_state.target_yaw_rate = map_value(controller.rx, -127, 128, -2.0, 2.0) # self.gait_state.target_yaw_rate = map_value(controller.rx, -127, 128, -2.0, 2.0)
def run(self, dt=.01): def run(self, dt=.01):
# self.camera.update() self.camera.update()
controller = self.shared_controller_state.get_latest_state() controller = self.shared_controller_state.get_latest_state()
self.handle_input(controller) self.handle_input(controller)
self.gait_state.update_gait_state(dt) self.gait_state.update_gait_state(dt)
+12 -6
View File
@@ -1,4 +1,5 @@
import math import math
from queue import Empty
import cv2 import cv2
import numpy as np import numpy as np
import pybullet as pb import pybullet as pb
@@ -18,7 +19,7 @@ projectionMatrix = pb.computeProjectionMatrixFOV(
distance = 100000 distance = 100000
class PyBulletCamera(CameraBase): class PyBulletCamera(CameraBase):
def __init__(self, model_id, view_matrix=viewMatrix, projection_matrix=projectionMatrix, width=640, height=480): def __init__(self, model_id, frame_queue, view_matrix=viewMatrix, projection_matrix=projectionMatrix, width=640, height=480):
""" """
Initializes the camera with PyBullet view and projection matrices. Initializes the camera with PyBullet view and projection matrices.
:param view_matrix: The view matrix for the camera in PyBullet. :param view_matrix: The view matrix for the camera in PyBullet.
@@ -32,10 +33,7 @@ class PyBulletCamera(CameraBase):
self.projection_matrix = projection_matrix self.projection_matrix = projection_matrix
self.width = width self.width = width
self.height = height self.height = height
self._last_frame = None self.frame_queue = frame_queue
def get_frame(self):
self._last_frame
def update(self): def update(self):
""" """
@@ -44,7 +42,15 @@ class PyBulletCamera(CameraBase):
""" """
self.update_view_matrix() self.update_view_matrix()
self._last_frame = self.get_frame() frame = self.get_frame()
if frame is not None:
# Only keep the latest frame to avoid filling up the queue
while not self.frame_queue.empty():
try:
self.frame_queue.get_nowait()
except Empty:
pass
self.frame_queue.put(frame)
def get_frame(self): def get_frame(self):
# Capture an image from the simulation # Capture an image from the simulation