Skip to content

Commit f4e6a5c

Browse files
authored
Merge pull request #3717 from JdeRobot/ros2-direct-e2e
Ros2 direct e2e
2 parents 4ad0d5e + d498a90 commit f4e6a5c

2 files changed

Lines changed: 47 additions & 41 deletions

File tree

exercises/end_to_end_visual_control/python_template/WebGUI.py

Lines changed: 43 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -2,45 +2,65 @@
22
import base64
33
import json
44
import threading
5+
import sys
56
import rclpy
7+
from rclpy.node import Node
8+
from rclpy.executors import MultiThreadedExecutor
9+
from sensor_msgs.msg import Image
10+
from cv_bridge import CvBridge
11+
612
from gui_interfaces.general.measuring_threading_gui import MeasuringThreadingGUI
713
from console_interfaces.general.console import start_console
8-
from hal_interfaces.general.odometry import OdometryNode
914
from lap import Lap
15+
from hal_interfaces.general.odometry import OdometryNode
1016

11-
import sys
1217

13-
sys.path.insert(0, "/RoboticsApplicationManager")
18+
class WebGUINode(Node):
19+
def __init__(self, gui_instance):
20+
super().__init__("webgui_end_to_end")
21+
self.gui = gui_instance
22+
self.bridge = CvBridge()
1423

15-
from robotics_application_manager import LogManager
24+
self.create_subscription(Image, "/webgui/image", self.img_cb, 10)
1625

17-
# Graphical User Interface Class
26+
def img_cb(self, msg):
27+
try:
28+
cv_image = self.bridge.imgmsg_to_cv2(msg, "bgr8")
29+
with self.gui.image_show_lock:
30+
self.gui.image_to_be_shown = cv_image
31+
self.gui.image_to_be_shown_updated = True
32+
except Exception:
33+
pass
1834

1935

2036
class WebGUI(MeasuringThreadingGUI):
2137
def __init__(self, host="ws://127.0.0.1:2303"):
2238
super().__init__(host)
2339

40+
if not rclpy.ok():
41+
rclpy.init()
42+
2443
self.image_to_be_shown = None
2544
self.image_to_be_shown_updated = False
2645
self.image_show_lock = threading.Lock()
2746

28-
# Payload vars
2947
self.payload = {"image": "", "lap": "", "map": ""}
30-
# TODO: maybe move this to HAL and have it be hybrid
31-
self.pose3d_object = OdometryNode("/odom")
32-
executor = rclpy.executors.MultiThreadedExecutor()
33-
executor.add_node(self.pose3d_object)
34-
executor_thread = threading.Thread(target=executor.spin, daemon=True)
35-
executor_thread.start()
36-
self.lap = Lap(self.pose3d_object)
48+
49+
self.ros_node = WebGUINode(self)
50+
self.odom_node = OdometryNode("/odom", "webgui_odometry")
51+
52+
self.executor = MultiThreadedExecutor()
53+
self.executor.add_node(self.ros_node)
54+
self.executor.add_node(self.odom_node)
55+
56+
self.executor_thread = threading.Thread(target=self.executor.spin, daemon=True)
57+
self.executor_thread.start()
58+
59+
self.lap = Lap(self.odom_node)
3760

3861
self.start()
3962

40-
# Process incoming messages to the GUI
4163
def gui_in_thread(self, ws, message):
42-
43-
# In this case, incoming msgs can only be acks
4464
if "ack" in message:
4565
with self.ack_lock:
4666
self.ack = True
@@ -51,55 +71,46 @@ def gui_in_thread(self, ws, message):
5171
self.lap.unpause()
5272
elif "pause" in message:
5373
self.lap.pause()
54-
else:
55-
LogManager.logger.error("Unsupported msg")
5674

57-
# Prepares and sends a map to the websocket server
5875
def update_gui(self):
59-
6076
payload = self.payloadImage()
6177
self.payload["image"] = json.dumps(payload)
6278

63-
# Payload Lap Message
6479
lapped = self.lap.check_threshold()
6580
self.payload["lap"] = ""
6681
if lapped is not None:
6782
self.payload["lap"] = str(lapped)
6883

69-
# Payload Map Message
70-
pose = self.pose3d_object.getPose3d()
84+
pose = self.odom_node.getPose3d()
7185
pos_message = str((pose.x, pose.y))
7286
self.payload["map"] = pos_message
7387

7488
message = json.dumps(self.payload)
7589
self.send_to_client(message)
7690

77-
# Function to prepare image payload
78-
# Encodes the image as a JSON string and sends through the WS
7991
def payloadImage(self):
8092
with self.image_show_lock:
8193
image_to_be_shown_updated = self.image_to_be_shown_updated
8294
image_to_be_shown = self.image_to_be_shown
8395

84-
image = image_to_be_shown
8596
payload = {"image": "", "shape": ""}
8697

8798
if not image_to_be_shown_updated:
8899
return payload
89100

90-
shape = image.shape
91-
frame = cv2.imencode(".JPEG", image)[1]
92-
encoded_image = base64.b64encode(frame)
101+
if image_to_be_shown is not None:
102+
shape = image_to_be_shown.shape
103+
frame = cv2.imencode(".JPEG", image_to_be_shown)[1]
104+
encoded_image = base64.b64encode(frame)
93105

94-
payload["image"] = encoded_image.decode("utf-8")
95-
payload["shape"] = shape
106+
payload["image"] = encoded_image.decode("utf-8")
107+
payload["shape"] = shape
96108

97109
with self.image_show_lock:
98110
self.image_to_be_shown_updated = False
99111

100112
return payload
101113

102-
# Function for student to call
103114
def showImage(self, image):
104115
with self.image_show_lock:
105116
self.image_to_be_shown = image
@@ -108,11 +119,8 @@ def showImage(self, image):
108119

109120
host = "ws://127.0.0.1:2303"
110121
gui = WebGUI(host)
111-
112-
# Redirect the console
113122
start_console()
114123

115124

116-
# Expose to the user
117125
def showImage(image):
118126
gui.showImage(image)
Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
1-
model_dir_path = "/workspace/code/model.onnx"
1+
import os
22

3+
model_dir_path = "/workspace/code"
34

4-
# This file is part of the Human Detection ROS2 package.
5-
def model_path_func() -> str:
6-
return model_dir_path
75

8-
9-
model_path = model_path_func()
6+
def model_path_func(name: str) -> str:
7+
return os.path.join(model_dir_path, name)

0 commit comments

Comments
 (0)