diff --git a/hands-v2-yolov5-conferencedata-aarch64-qnn-v42.eim b/hands-v2-yolov5-conferencedata-aarch64-qnn-v42.eim new file mode 100644 index 0000000..972469c Binary files /dev/null and b/hands-v2-yolov5-conferencedata-aarch64-qnn-v42.eim differ diff --git a/ros2_ws/hand_controller/hand_controller/common.py b/ros2_ws/hand_controller/hand_controller/common.py new file mode 100644 index 0000000..2f1c807 --- /dev/null +++ b/ros2_ws/hand_controller/hand_controller/common.py @@ -0,0 +1,86 @@ +"""Common utilities and constants for VAI demo""" + +import subprocess + +GRAPH_SAMPLE_WINDOW_SIZE_s = 31 +HW_SAMPLING_PERIOD_ms = 250 +GRAPH_DRAW_PERIOD_ms = 30 +AUTOMATIC_DEMO_SWITCH_s = 60 +QUIT_CLEANUP_DELAY_ms = 1000 + +GRAPH_SAMPLE_SIZE = int(GRAPH_SAMPLE_WINDOW_SIZE_s * 1000 / GRAPH_DRAW_PERIOD_ms) + +TIME_KEY = "time" +CPU_UTIL_KEY = "cpu %" +MEM_UTIL_KEY = "lpddr5 %" +GPU_UTIL_KEY = "gpu %" +DSP_UTIL_KEY = "dsp %" +CPU_THERMAL_KEY = "cpu temp (°c)" +MEM_THERMAL_KEY = "lpddr5 temp (°c)" +GPU_THERMAL_KEY = "gpu temp (°c)" + +# Triadic colors, indexed on Tria pink +TRIA_PINK_RGBH = (0xFE, 0x00, 0xA2) +TRIA_BLUE_RGBH = (0x00, 0xA2, 0xFE) +TRIA_YELLOW_RGBH = (0xFE, 0xDB, 0x00) +TRIA_GREEN_RGBH = (0x22, 0xB1, 0x4C) + +APP_NAME = f"GTK GUI Node" + +TRIA = r""" +████████╗██████╗ ██╗ █████╗ +╚══██╔══╝██╔══██╗██║██╔══██╗ + ██║ ██████╔╝██║███████║ + ██║ ██╔══██╗██║██╔══██║ + ██║ ██║ ██║██║██║ ██║ + ╚═╝ ╚═╝ ╚═╝╚═╝╚═╝ ╚═╝ +""" + + +def lerp(a, b, t): + """Linear interpolation between two values""" + return a + t * (b - a) + + +def inverse_lerp(a, b, v): + """Inverse linear interpolation between two values""" + return (v - a) / (b - a) if a != b else 0.0 + + +def get_ema(x_cur, x_last, alpha=0.75): + """ + Exponential moving average + + Args: + x_cur: Current value + x_last: Last value + alpha: Smoothing factor + + Note: + alpha is a misnomer. alpha = 1.0 is equivalent to no smoothing + + Ref: + https://en.wikipedia.org/wiki/Exponential_smoothing + + """ + return alpha * x_cur + (1 - alpha) * x_last + + +def app_version(): + """Get the latest tag or commit hash if possible, unknown otherwise""" + + try: + version = subprocess.check_output( + ["git", "describe", "--tags", "--always"], text=True + ).strip() + date = subprocess.check_output( + ["git", "log", "-1", "--format=%cd", "--date=short"], text=True + ).strip() + + return f"{version} {date}" + except subprocess.CalledProcessError: + # Handle errors, such as not being in a Git repository + return "unknown" + + +APP_HEADER = f"{APP_NAME} v({app_version()})" \ No newline at end of file diff --git a/ros2_ws/hand_controller/hand_controller/graphing.py b/ros2_ws/hand_controller/hand_controller/graphing.py new file mode 100644 index 0000000..36537ec --- /dev/null +++ b/ros2_ws/hand_controller/hand_controller/graphing.py @@ -0,0 +1,315 @@ +from hand_controller.vai.common import inverse_lerp, lerp + +# Scaling boundaries (assumes res between 1920x1080 and 3840x2160) +GRAPH_LABEL_FONT_SIZE_MIN = 5 +GRAPH_LABEL_FONT_SIZE_MAX = 30 + +GRAPH_AXIS_LINE_WIDTH_MIN = 2 +GRAPH_AXIS_LINE_WIDTH_MAX = 4 + +GRAPH_DATA_LINE_WIDTH_MIN = 2 +GRAPH_DATA_LINE_WIDTH_MAX = 4 + +GRAPH_AXIS_TICK_LENGTH_MIN = 6 +GRAPH_AXIS_TICK_LENGTH_MAX = 12 + +MAX_RES_WIDTH = 3840 + + +def draw_graph_background_and_border( + width, + height, + cr, + bg_color=(0.1, 0.1, 0.1, 0.15), + border_color=None, + res_tuple=(1920, 1080), +): + """ + Draws a background and border for the graph area. Can pass res to scale border reasonably + + Args: + width: Width of the graph area + height: Height of the graph area + cr: Cairo context + bg_color: Background color (RGBA tuple) + border_color: Border color (RGBA tuple) + res_tuple: Tuple of (width, height) of the screen resolution + + """ + line_width = int( + lerp( + GRAPH_AXIS_LINE_WIDTH_MIN, + GRAPH_AXIS_LINE_WIDTH_MAX, + res_tuple[0] / MAX_RES_WIDTH, + ) + ) + cr.set_line_width(line_width) + cr.set_source_rgba(*bg_color) + + # Offset by half the line width so the stroke is fully visible inside the widget: + # In Cairo, rectangle(x, y, w, h) draws from (x, y) to (x + w, y + h). + # The stroke is centered on the path, so we typically offset the path by line_width/2 + # and reduce the drawn rectangle dimensions accordingly. + offset = line_width / 2.0 + + cr.rectangle( + offset, + offset, + width - line_width, + height - line_width, + ) + + cr.fill_preserve() + + if border_color: + cr.set_source_rgba(*border_color) + cr.stroke() + + +def draw_axes_and_labels( + cr, + width, + height, + x_lim, + y_lim, + x_ticks=4, + y_ticks=4, + dynamic_margin=True, + right_margin=20, + bottom_margin=20, + x_label=None, + y_label=None, + res_tuple=(1920, 1080), +): + """ + Draws simple axes with labeled tick marks along bottom (x-axis) and left (y-axis). + + Args: + cr : Cairo context + width : total width of the drawing area + height : total height of the drawing area + x_lim : (xmin, xmax) for the data domain you want to label + y_lim : (ymin, ymax) for the data domain (like (0, 100)) + x_ticks : how many segments (thus x_ticks+1 labeled steps) + y_ticks : how many segments (thus y_ticks+1 labeled steps) + dynamic_margin: If True, the right and bottom margins will be scaled based on the resolution + right_margin: Margin on the right side of the graph + bottom_margin: Margin on the bottom of the graph + x_label : Label for the x-axis + y_label : Label for the y-axis + res_tuple: Tuple of (width, height) of the screen resolution + + Returns: + (x, y) axis positions + """ + if not x_lim or not y_lim: + return width, height + + if dynamic_margin: + right_margin = lerp(20, 100, res_tuple[0] / MAX_RES_WIDTH) + bottom_margin = lerp(20, 40, res_tuple[0] / MAX_RES_WIDTH) + + width -= right_margin # Leave a little space on the right for the legend + height -= bottom_margin # Leave a little space on the bottom for the x-axis labels + + if not x_ticks or not y_ticks: # No ticks, nothing to draw + return width, height + + axis_width = lerp( + GRAPH_AXIS_LINE_WIDTH_MIN, + GRAPH_AXIS_LINE_WIDTH_MAX, + res_tuple[0] / MAX_RES_WIDTH, + ) + font_size = lerp( + GRAPH_LABEL_FONT_SIZE_MIN, + GRAPH_LABEL_FONT_SIZE_MAX, + res_tuple[0] / MAX_RES_WIDTH, + ) + + cr.save() # save the current transformation/state + + cr.set_line_width(axis_width) + cr.set_source_rgb(1, 1, 1) # white lines & text + + # --- Draw X-axis --- + cr.move_to(0, height) + cr.line_to(width, height) + cr.stroke() + + # --- Draw Y-axis --- + cr.move_to(width, 0) + cr.line_to(width, height) + cr.stroke() + + # Set font for labels + cr.select_font_face("Sans", 0, 0) # (slant=0 normal, weight=0 normal) + cr.set_font_size(font_size) + + # --- X Ticks and Labels --- + # e.g. if x_lim = (0,100), for 4 ticks => labeled at x=0,25,50,75,100 + x_min, x_max = x_lim + dx = (x_max - x_min) / (x_ticks or 1) + for i in range(x_ticks + 1): + x_val = x_min + i * dx + # Convert data → screen coordinate: 0..width + x_screen = int((x_val - x_min) / ((x_max - x_min) or 1) * width) + + # Tick mark from (x_screen, height) up a bit + tick_length = 6 + cr.move_to(x_screen, height) + cr.line_to(x_screen, height - tick_length) + cr.stroke() + + # Draw text label under the axis + text = f"{int(x_val)}" + te = cr.text_extents(text) + text_x = x_screen - te.width // 2 if i != 0 else te.width // 2 + # At the intersection of the x/y axes, adjust text position further + if i == x_ticks: + text_x -= te.width + text_y = height + te.height + 4 + cr.move_to(text_x, text_y) + if i != 0: + cr.show_text(text) + elif x_label: + cr.show_text(text + " " + x_label) + + # --- Y Ticks and Labels --- + y_min, y_max = y_lim + dy = (y_max - y_min) / (y_ticks or 1) + for j in range(y_ticks + 1): + y_val = y_min + j * dy + y_ratio = (y_val - y_min) / ((y_max - y_min) or 1) + y_screen = int(height - y_ratio * height) # 0 -> bottom, height -> top + + tick_length = lerp( + GRAPH_AXIS_TICK_LENGTH_MIN, + GRAPH_AXIS_TICK_LENGTH_MAX, + res_tuple[0] / MAX_RES_WIDTH, + ) + cr.move_to(width, y_screen) + cr.line_to(width - tick_length, y_screen) + cr.stroke() + + text = f"{int(y_val)}" + if y_label and j == y_ticks: + text += y_label + te = cr.text_extents(text) + text_x = width + 4 + text_y = ( + y_screen + te.height // 2 + if j != y_ticks + else y_screen + te.height + lerp(2, 4, res_tuple[0] / MAX_RES_WIDTH) + ) + if j == 0: + text_y -= te.height // 2 + cr.move_to(text_x, text_y) + cr.show_text(text) + + cr.restore() + return width, height + + +#def draw_graph_legend(label_color_map, width, cr, legend_x_width=None): +# """ +# Draw the legend for the graph, returning the x position of the legend +# +# Args: +# label_color_map: Dict of label to RGB color tuple +# width: Width of the graph area +# cr: Cairo context +# legend_x_width: Width of the legend box. If None, the width is determined by the labels +# """ +# # --- Draw Legend --- +# # TODO: Scale by res? +# legend_margin_x = 20 # Distance from the right edge +# legend_margin_y = 10 # Distance from the top edge +# box_size = 20 # Size of the color box +# spacing = 30 # Vertical spacing between entries +# legend_padding_x = 5 +# +# cr.select_font_face("Sans", 0, 1) # Bold weight & normal slant +# cr.set_font_size(10) +# +# text_guess_width = 11 * max(len(label) for label, _ in label_color_map.items()) +# legend_x = ( +# width - legend_x_width +# if legend_x_width +# else width - legend_margin_x - text_guess_width - box_size +# ) +# +# # Tuning offset variable +# for i, (label, color) in enumerate(label_color_map.items()): +# item_y = legend_margin_y + i * spacing +# +# # Draw color box +# cr.set_source_rgb(*color) +# cr.rectangle(legend_x, item_y, box_size, box_size) +# cr.fill() +# +# # Draw label text in white +# cr.set_source_rgb(1, 1, 1) +# text_x = legend_x + box_size + legend_padding_x +# text_y = ( +# item_y + box_size - 5 +# ) # Shift text slightly so it's vertically centered +# cr.move_to(text_x, text_y) +# cr.show_text(label.upper()) +# +# return legend_x + + +def draw_graph_data( + data_map, + data_color_map, + width, + height, + cr, + y_lim=(0, 100), + res_tuple=(1920, 1080), +): + """Draw the graph data on the draw area with the given colors + + Args: + data_map: Dict of data key to list of data values + data_color_map: Dict of data key to RGB color tuple + width: Width of the graph area + height: Height of the graph area + cr: Cairo context + y_lim (optional): Tuple of min and max y values + """ + if not data_map or not data_color_map: + return + + graph_line_width = lerp( + GRAPH_DATA_LINE_WIDTH_MIN, + GRAPH_DATA_LINE_WIDTH_MAX, + res_tuple[0] / MAX_RES_WIDTH, + ) + cr.set_line_width(graph_line_width) + + for data_key, data in data_map.items(): + if data_key not in data_color_map: + continue # Skip if no color for this data (like timestamps) + + cr.set_source_rgb(*data_color_map[data_key]) + # Start the cursor at the first data point if possible + # x is 0 or width because our data must always fit between 0 and width, (not the case for y values) + cr.move_to( + 0 if data else width, + ( + height + if not data + else int(lerp(0, height, 1 - inverse_lerp(y_lim[0], y_lim[1], data[0]))) + ), + ) + # We must inverse lerp to get the % of the y value between the min and max y values for instances where y_lim is not 0-100 + for x in range(1, len(data)): + y_pct = inverse_lerp(y_lim[0], y_lim[1], data[x]) + cr.line_to( + int( + lerp(0, width, x / len(data)) + ), # cant divide by 0 in this range definition + int(lerp(0, height, 1 - y_pct)), + ) + cr.stroke() diff --git a/ros2_ws/hand_controller/hand_controller/gtk_gui_node.py b/ros2_ws/hand_controller/hand_controller/gtk_gui_node.py new file mode 100644 index 0000000..8ea6329 --- /dev/null +++ b/ros2_ws/hand_controller/hand_controller/gtk_gui_node.py @@ -0,0 +1,839 @@ +#!/usr/bin/env python3 +from ament_index_python.packages import get_package_share_directory +import rclpy +from rclpy.node import Node +from rclpy.executors import SingleThreadedExecutor +from std_msgs.msg import String +from sensor_msgs.msg import Image as RosImage # Import ROS Image message +from geometry_msgs.msg import Twist # For velocity commands +from cv_bridge import CvBridge # To convert between ROS and OpenCV images +import cv2 # OpenCV library +import numpy as np # NumPy for array manipulation + +import gi +gi.require_version('Gtk', '3.0') +from gi.repository import Gtk, GObject, GLib, GdkPixbuf + + +import collections +import os +import threading +import time +import gi +#import pyopencl as cl + + +from hand_controller.vai.common import (APP_HEADER, CPU_THERMAL_KEY, CPU_UTIL_KEY, + GPU_THERMAL_KEY, GPU_UTIL_KEY, GRAPH_SAMPLE_SIZE, + MEM_THERMAL_KEY, MEM_UTIL_KEY, DSP_UTIL_KEY, TIME_KEY, + TRIA, TRIA_BLUE_RGBH, TRIA_PINK_RGBH, TRIA_YELLOW_RGBH, + TRIA_GREEN_RGBH, GRAPH_SAMPLE_WINDOW_SIZE_s, + get_ema) +from hand_controller.vai.graphing import (draw_axes_and_labels, + draw_graph_background_and_border, draw_graph_data) +from hand_controller.vai.handler import Handler +from hand_controller.vai.qprofile import QProfProcess + +package_name = 'hand_controller' +package_share_directory = get_package_share_directory(package_name) +latest_hand_data = None + +class FdFilter: + """ + Redirects low-level file descriptors (stdout, stderr) to a pipe, + filters the output in a separate thread, and writes the filtered + output back to the original destination. This is necessary to + suppress messages from C libraries that write directly to file + descriptors, bypassing sys.stdout/sys.stderr. + """ + def __init__(self, filter_strings): + self.filter_strings = [s.lower() for s in filter_strings] + self.original_stdout_fd = os.dup(1) + self.original_stderr_fd = os.dup(2) + + # Create pipes to intercept stdout and stderr + self.stdout_pipe_r, self.stdout_pipe_w = os.pipe() + self.stderr_pipe_r, self.stderr_pipe_w = os.pipe() + + # Redirect stdout and stderr to the write-ends of the pipes + os.dup2(self.stdout_pipe_w, 1) + os.dup2(self.stderr_pipe_w, 2) + + # Create threads to read from the pipes, filter, and write to original FDs + self.stdout_thread = threading.Thread(target=self._pipe_reader, args=(self.stdout_pipe_r, self.original_stdout_fd)) + self.stderr_thread = threading.Thread(target=self._pipe_reader, args=(self.stderr_pipe_r, self.original_stderr_fd)) + self.stdout_thread.daemon = True + self.stderr_thread.daemon = True + self.stdout_thread.start() + self.stderr_thread.start() + + def _pipe_reader(self, pipe_r_fd, original_dest_fd): + """Reads from a pipe, filters, and writes to the destination.""" + with os.fdopen(pipe_r_fd, 'r') as pipe_file: + for line in iter(pipe_file.readline, ''): + if not any(f in line.lower() for f in self.filter_strings): + os.write(original_dest_fd, line.encode('utf-8')) + +# Locks app version, prevents warnings +gi.require_version("Gdk", "3.0") +gi.require_version("Gst", "1.0") +gi.require_version("Gtk", "3.0") +from gi.repository import Gdk, Gst, Gtk + +# --- Graphing constants --- + +UTIL_GRAPH_COLORS_RGBF = { + CPU_UTIL_KEY: tuple(c / 255.0 for c in TRIA_PINK_RGBH), + MEM_UTIL_KEY: tuple(c / 255.0 for c in TRIA_BLUE_RGBH), + GPU_UTIL_KEY: tuple(c / 255.0 for c in TRIA_YELLOW_RGBH), + DSP_UTIL_KEY: tuple(c / 255.0 for c in TRIA_GREEN_RGBH), +} + +THERMAL_GRAPH_COLORS_RGBF = { + CPU_THERMAL_KEY: tuple(c / 255.0 for c in TRIA_PINK_RGBH), + MEM_THERMAL_KEY: tuple(c / 255.0 for c in TRIA_BLUE_RGBH), + GPU_THERMAL_KEY: tuple(c / 255.0 for c in TRIA_YELLOW_RGBH), +} + +GRAPH_LABEL_FONT_SIZE = 14 +MAX_TIME_DISPLAYED = 0 +MIN_TEMP_DISPLAYED = 35 +MAX_TEMP_DISPLAYED = 95 +MIN_UTIL_DISPLAYED = 0 +MAX_UTIL_DISPLAYED = 100 + +# --- End Graphing constants --- +def is_monitor_above_2k(): + """ + Checks if any connected monitor has a native resolution greater than 2K (2560x1440). + Uses EDID data from /sys/class/drm/ to determine resolution. + + Returns: + bool: True if any monitor has resolution > 2560x1440, False otherwise. + """ + drm_path = '/sys/class/drm/' + above_2k = False + + try: + for device in os.listdir(drm_path): + # Look for connected display devices (e.g., card0-HDMI-A-1, card0-eDP-1) + if not device.startswith('card'): + continue + + status_file = os.path.join(drm_path, device, 'status') + edid_file = os.path.join(drm_path, device, 'edid') + + # Only check if the monitor is connected + if os.path.exists(status_file) and os.path.exists(edid_file): + with open(status_file, 'r') as f: + if f.read().strip() != 'connected': + continue + + # Read EDID data + with open(edid_file, 'rb') as f: + edid_data = f.read() + + if len(edid_data) < 128: + continue # Invalid EDID + + # Parse EDID to get the native resolution + # Detailed Timing Descriptor 1 starts at 54th byte + dtd_start = 54 + if dtd_start + 18 <= len(edid_data): + # First DTD (usually the preferred/native mode) + dtd = edid_data[dtd_start:dtd_start+18] + + # Parse horizontal active pixels (bytes 2-3) + h_active_lo = dtd[2] + h_active_hi = (dtd[4] & 0xF0) >> 4 + width = h_active_lo + (h_active_hi << 8) + + # Parse vertical active lines (bytes 5-6) + v_active_lo = dtd[5] + v_active_hi = (dtd[7] & 0xF0) >> 4 + height = v_active_lo + (v_active_hi << 8) + + # Check if resolution is greater than 2K (2560x1440) + if width > 2560 or height > 1440: + # Confirm it's a valid resolution + if width >= 3840 or height >= 2160: + above_2k = True + break # Found a 4K or higher display + + except Exception as e: + print(f"Error reading EDID: {e}") + return False + + return above_2k + +GladeBuilder = Gtk.Builder() + +if is_monitor_above_2k(): + print("Connected monitor resolution is above 2K (e.g., 4K).") + RESOURCE_FOLDER = os.path.join(package_share_directory, "resources_high") +else: + print("No monitor above 2K resolution detected.") + RESOURCE_FOLDER = os.path.join(package_share_directory, "resources_low") + +LAYOUT_PATH = os.path.join(RESOURCE_FOLDER, "GSTLauncher.glade") + +def get_min_time_delta_smoothed(time_series: list): + """Returns the delta from the current time to the first entry in the time series. If the time series is empty, returns 0.""" + if not time_series: return 0 + + x_min = -int(time.monotonic() - time_series[0]) + + # Help with the jittering of the graph + if abs(x_min - GRAPH_SAMPLE_WINDOW_SIZE_s) <= 1: + x_min = -GRAPH_SAMPLE_WINDOW_SIZE_s + + return x_min + + +# --- ROS2 Node Class --- +# Handles all ROS2 logic, now including image subscription. +class GuiLogicNode(Node): + def __init__(self, gui_window): + super().__init__('gtk_gui_node') + self.gui = gui_window + self.message_count = 0 + self.bridge = CvBridge() # Initialize CvBridge + + # --- ROS2 Publishers and Subscribers --- + self.publisher_ = self.create_publisher(String, 'gui_button_clicks', 10) + + self.status_subscription = self.create_subscription( + String, + 'gui_status_topic', + self.status_callback, + 10) + + # New subscriber for the annotated image stream + self.image_subscription = self.create_subscription( + RosImage, + '/hand_controller/image_annotated', # The topic to view + self.image_callback, + 10) # QoS profile depth + + # Subscription for Twist data (velocity commands) + self.twist_subscription = self.create_subscription( + Twist, + '/hand_controller/cmd_vel', + self.twist_callback, + 10) + + # Subscription for Twist data (velocity commands) + self.twist_subscription = self.create_subscription( + Twist, + '/cmd_vel', + self.twist_callback, + 10) + + def status_callback(self, msg: String): + GLib.idle_add(self.gui.update_status_label, f"Status: {msg.data}") + ''' + def image_callback(self, msg: RosImage): + try: + # Convert ROS Image to OpenCV (BGR) + cv_image = self.bridge.imgmsg_to_cv2(msg, "bgr8") + h, w = cv_image.shape[:2] + + # Upload to GPU (as BGRA for alignment) + cv_image_rgba = cv2.cvtColor(cv_image, cv2.COLOR_BGR2RGBA) + img_gpu = cl.image_from_array(self.ctx, cv_image_rgba, 4) + + # Output buffer + dest_rgba = np.empty_like(cv_image_rgba) + dest_gpu = cl.Buffer(self.ctx, cl.mem_flags.WRITE_ONLY, dest_rgba.nbytes) + + # Run GPU kernel + self.prg.bgr_to_rgb(self.queue, (w, h), None, dest_gpu, np.int32(w), np.int32(h)) + + # Read back result + cl.enqueue_copy(self.queue, dest_rgba, dest_gpu) + rgb_image = cv2.cvtColor(dest_rgba, cv2.COLOR_RGBA2RGB) + + # Convert to Pixbuf + pixbuf = GdkPixbuf.Pixbuf.new_from_data( + rgb_image.tobytes(), + GdkPixbuf.Colorspace.RGB, False, 8, w, h, w * 3 + ) + GLib.idle_add(self.gui.update_image_view, pixbuf) + + except Exception as e: + print(f"Error in image callback: {e}") + + ''' + def image_callback(self, msg: RosImage): + """Callback for receiving a ROS Image message.""" + try: + # Convert the ROS Image message to an OpenCV image (BGR format) + cv_image = self.bridge.imgmsg_to_cv2(msg, "bgr8") + + # Convert the OpenCV image to a GdkPixbuf for GTK display. + # OpenCV uses BGR, but GdkPixbuf expects RGB. + h, w, c = cv_image.shape + # Create a Pixbuf from the NumPy array data + pixbuf = GdkPixbuf.Pixbuf.new_from_data( + cv2.cvtColor(cv_image, cv2.COLOR_BGR2RGB).tobytes(), + GdkPixbuf.Colorspace.RGB, + False, # has_alpha + 8, # bits_per_sample + w, h, # width, height + w * c # rowstride + ) + + # Call the GUI update method in a thread-safe way + GLib.idle_add(self.gui.update_image_view, pixbuf) + + except Exception as e: + self.get_logger().error(f'Failed to process image: {e}') + + def twist_callback(self, twist_msg: Twist): + """Callback for receiving Twist messages.""" + global latest_hand_data + try: + self.last_message_time = time.time() + self.message_count += 1 + + # Extract control data from twist message + linear_x = twist_msg.linear.x # Forward/Backward (Right hand) + angular_z = twist_msg.angular.z # Left/Right turning (Left hand) + + # Log first few messages for debugging + if self.message_count <= 10: + self.get_logger().info(f'Message {self.message_count}: linear.x={linear_x:.3f}, angular.z={angular_z:.3f}') + elif self.message_count == 11: + self.get_logger().info('... continuing to receive messages (suppressing logs)') + + # Calculate normalized values - SEPARATE CONTROLS + # Left hand: only angular_z (turning) + normalized_angular = max(min(angular_z / 2.0, 1.0), -1.0) if abs(angular_z) > 0.1 else 0.0 + + # Right hand: only linear_x (forward/backward) + normalized_linear = max(min(linear_x / 2.0, 1.0), -1.0) if abs(linear_x) > 0.1 else 0.0 + + # Determine hand activity based on SEPARATE controls + left_hand_active = abs(normalized_angular) > 0.1 # Only angular movement + right_hand_active = abs(normalized_linear) > 0.1 # Only linear movement + + # Calculate visual positions based on SEPARATE controls + # Left hand: only moves left/right based on angular control + lh_screen_x = 0.5 + (normalized_angular * 0.4) # Map to 0.1-0.9 range + lh_screen_y = 0.5 # Fixed vertical position - doesn't move up/down + + # Right hand: only moves up/down based on linear control + rh_screen_x = 0.5 # Fixed horizontal position - doesn't move left/right + rh_screen_y = 0.5 + (normalized_linear * 0.4) # Map to 0.1-0.9 range + + hand_data = { + 'left_hand': { + 'x_position': normalized_angular, # Turning control + 'y_position': 0.0, # No vertical movement for left hand + 'screen_x': max(min(lh_screen_x, 0.9), 0.1), + 'screen_y': max(min(lh_screen_y, 0.9), 0.1), # Fixed at 0.5 + 'active': left_hand_active + }, + 'right_hand': { + 'z_position': normalized_linear, # Forward/backward control + 'screen_x': max(min(rh_screen_x, 0.9), 0.1), # Fixed at 0.5 + 'screen_y': max(min(rh_screen_y, 0.9), 0.1), + 'active': right_hand_active + }, + 'timestamp': time.time(), + 'raw_data': { + 'linear_x': linear_x, + 'angular_z': angular_z + }, + 'status': 'active', + 'message_count': self.message_count + } + latest_hand_data = hand_data + + except Exception as e: + self.get_logger().error(f"Error processing hand data: {e}") + + + +class VaiDemoManager: + def __init__(self): + self.eventHandler = Handler() + self.running = True + self.ros_node = None + self.set_video_sink0 = None + self.leftHandStatus = None + self.rightHandStatus = None + self.leftHandImage = None + self.rightHandImage = None + self.leftHandImageLoaded = None + self.rightHandImageLoaded = None + self.leftHandActive = False + self.rightHandActive = False + + GLib.idle_add(self.update_hand_data) + + def update_image_view(self, pixbuf): + """Thread-safe method to update the Gtk.Image widget.""" + if pixbuf: + width = self.set_video_sink0.get_allocated_width() + height = self.set_video_sink0.get_allocated_height() + + # Only scale if the widget has a size and the pixbuf size is different + if width > 0 and height > 0 and (pixbuf.get_width() != width or pixbuf.get_height() != height): + # GdkPixbuf.InterpType.BILINEAR is a good compromise between speed and quality. + # For higher quality, you could use GdkPixbuf.InterpType.HYPER. + scaled_pixbuf = pixbuf.scale_simple(width, height, GdkPixbuf.InterpType.BILINEAR) + self.set_video_sink0.set_from_pixbuf(scaled_pixbuf) + else: + self.set_video_sink0.set_from_pixbuf(pixbuf) + + def update_hand_data(self): + global latest_hand_data + if latest_hand_data == None: + return GLib.SOURCE_CONTINUE + + left_hand_active = latest_hand_data['left_hand']['active'] + right_hand_active = latest_hand_data['right_hand']['active'] + normalized_angular = latest_hand_data['left_hand']['x_position'] + normalized_linear = latest_hand_data['right_hand']['z_position'] + + if self.leftHandActive != left_hand_active: + self.leftHandActive = left_hand_active + + if self.leftHandActive: + left_status_text = "Active" + GLib.idle_add(self.leftHandStatus.get_style_context().remove_class, "status") + GLib.idle_add(self.leftHandStatus.get_style_context().add_class, "status_active") + else: + left_status_text = "Inactive" + GLib.idle_add(self.leftHandStatus.get_style_context().remove_class, "status_active") + GLib.idle_add(self.leftHandStatus.get_style_context().add_class, "status") + + + GLib.idle_add(self.leftHandStatus.set_text, f"Status: {left_status_text}") + + if self.rightHandActive != right_hand_active: + self.rightHandActive = right_hand_active + + if self.rightHandActive: + right_status_text = "Active" + GLib.idle_add(self.rightHandStatus.get_style_context().remove_class, "status") + GLib.idle_add(self.rightHandStatus.get_style_context().add_class, "status_active") + else: + right_status_text = "Inactive" + GLib.idle_add(self.rightHandStatus.get_style_context().remove_class, "status_active") + GLib.idle_add(self.rightHandStatus.get_style_context().add_class, "status") + + GLib.idle_add(self.rightHandStatus.set_text, f"Status: {right_status_text}") + + if not left_hand_active: + loadLeftImage = "leftright.png" + elif (normalized_angular > 0.1): + loadLeftImage = "left.png" + elif (normalized_angular < -0.1): + loadLeftImage = "right.png" + else: + loadLeftImage = "leftright.png" + + if loadLeftImage != self.leftHandImageLoaded: + self.leftHandImageLoaded = loadLeftImage + GLib.idle_add(self.leftHandImage.set_from_file, os.path.join(RESOURCE_FOLDER, self.leftHandImageLoaded)) + + + if not right_hand_active: + loadrightImage = "updown.png" + elif (normalized_linear > 0.1): + loadrightImage = "up.png" + elif (normalized_linear < -0.1): + loadrightImage = "down.png" + else: + loadrightImage = "updown.png" + + if loadrightImage != self.rightHandImageLoaded: + self.rightHandImageLoaded = loadrightImage + GLib.idle_add(self.rightHandImage.set_from_file, os.path.join(RESOURCE_FOLDER, self.rightHandImageLoaded)) + + return GLib.SOURCE_CONTINUE + + def resize_graphs_dynamically(self, parent_widget, _allocation): + if not self.eventHandler.GraphDrawAreaTop or not self.eventHandler.GraphDrawAreaBottom: + return + + """Resize graphing areas to be uniform and fill remaining space. To be called on size-allocate signal.""" + + # Total width will be a function of the current lifecycle of the widget, it may have a surprising value + total_width = parent_widget.get_allocated_width() + total_height = parent_widget.get_allocated_height() + + self.main_window_dims = (total_width, total_height) + if total_width == 0: + return + + BottomBox = GladeBuilder.get_object("BottomBox") + if not BottomBox: + return + + BottomBox_width = BottomBox.get_allocated_width() + if BottomBox_width == 0: + return + + # These datagrid widths are what determine the remaining space + data_grid = GladeBuilder.get_object("DataGrid") + data_grid1 = GladeBuilder.get_object("DataGrid1") + if not data_grid or not data_grid1: + return + + remaining_graph_width = BottomBox_width - ( + data_grid.get_allocated_width() + data_grid1.get_allocated_width() + ) + # Account for margins that arent included in the allocated width + remaining_graph_width -= ( + data_grid.get_margin_start() + data_grid.get_margin_end() + 10 + ) + remaining_graph_width -= ( + data_grid1.get_margin_start() + data_grid1.get_margin_end() + 10 + ) + + half = remaining_graph_width // 2 + if half < 0: + return + + try: + window_x, window_y = self.eventHandler.DrawArea1.translate_coordinates(self.eventHandler.DrawArea1.get_toplevel(), 0, 0) + + camera_bottom_position = window_y + self.eventHandler.DrawArea1.get_allocated_height() + + if camera_bottom_position > 148: + BottomBox.set_size_request(-1, round(total_height - camera_bottom_position)) + except: + pass + + graph_top = self.eventHandler.GraphDrawAreaTop + graph_bottom = self.eventHandler.GraphDrawAreaBottom + # Only resize if changed, otherwise it can cause a loop + if ( + graph_top.get_allocated_width() != half + or graph_bottom.get_allocated_width() != half + ): + graph_top.set_size_request(half, -1) + graph_bottom.set_size_request(half, -1) + + def init_graph_data(self, sample_size=GRAPH_SAMPLE_SIZE): + """Initialize the graph data according to graph box size""" + self.util_data = { + TIME_KEY: collections.deque([], maxlen=sample_size), + CPU_UTIL_KEY: collections.deque([], maxlen=sample_size), + MEM_UTIL_KEY: collections.deque([], maxlen=sample_size), + GPU_UTIL_KEY: collections.deque([], maxlen=sample_size), + DSP_UTIL_KEY: collections.deque([], maxlen=sample_size), + } + self.thermal_data = { + TIME_KEY: collections.deque([], maxlen=sample_size), + CPU_THERMAL_KEY: collections.deque([], maxlen=sample_size), + MEM_THERMAL_KEY: collections.deque([], maxlen=sample_size), + GPU_THERMAL_KEY: collections.deque([], maxlen=sample_size), + } + + def _sample_util_data(self): + """Sample the utilization data; prefer this function because it timestamps entries to util data""" + + if self.util_data is None or self.thermal_data is None: + self.init_graph_data() + + self.util_data[TIME_KEY].append(time.monotonic()) + + # Sample and smooth the data with exponential smoothing + cur_cpu = self.eventHandler.sample_data[CPU_UTIL_KEY] + cur_gpu = self.eventHandler.sample_data[GPU_UTIL_KEY] + cur_mem = self.eventHandler.sample_data[MEM_UTIL_KEY] + cur_dsp = self.eventHandler.sample_data[DSP_UTIL_KEY] + + last_cpu = self.util_data[CPU_UTIL_KEY][-1] if self.util_data[CPU_UTIL_KEY] else cur_cpu + last_gpu = self.util_data[GPU_UTIL_KEY][-1] if self.util_data[GPU_UTIL_KEY] else cur_gpu + last_mem = self.util_data[MEM_UTIL_KEY][-1] if self.util_data[MEM_UTIL_KEY] else cur_mem + last_dsp = self.util_data[DSP_UTIL_KEY][-1] if self.util_data[DSP_UTIL_KEY] else cur_dsp + + ema_cpu = get_ema(cur_cpu, last_cpu) + ema_gpu = get_ema(cur_gpu, last_gpu) + ema_mem = get_ema(cur_mem, last_mem) + ema_dsp = get_ema(cur_dsp, last_dsp) + + self.util_data[CPU_UTIL_KEY].append(ema_cpu) + self.util_data[GPU_UTIL_KEY].append(ema_gpu) + self.util_data[MEM_UTIL_KEY].append(ema_mem) + self.util_data[DSP_UTIL_KEY].append(ema_dsp) + + cur_time = time.monotonic() + while ( + self.util_data[TIME_KEY] + and cur_time - self.util_data[TIME_KEY][0] > GRAPH_SAMPLE_WINDOW_SIZE_s + ): + self.util_data[TIME_KEY].popleft() + self.util_data[CPU_UTIL_KEY].popleft() + self.util_data[GPU_UTIL_KEY].popleft() + self.util_data[MEM_UTIL_KEY].popleft() + self.util_data[DSP_UTIL_KEY].popleft() + + def on_util_graph_draw(self, widget, cr): + if not self.eventHandler.GraphDrawAreaTop: + return True + + if not self.util_data: + self.eventHandler.GraphDrawAreaTop.queue_draw() + return True + + """Draw the util graph on the draw area""" + + self._sample_util_data() + + width = widget.get_allocated_width() + height = widget.get_allocated_height() + + draw_graph_background_and_border( + width, height, cr, res_tuple=self.main_window_dims + ) + + x_min = get_min_time_delta_smoothed(self.util_data[TIME_KEY]) + + x_lim = (x_min, MAX_TIME_DISPLAYED) + y_lim = (MIN_UTIL_DISPLAYED, MAX_UTIL_DISPLAYED) + + x_axis, y_axis = draw_axes_and_labels( + cr, + width, + height, + x_lim, + y_lim, + x_ticks=4, + y_ticks=2, + dynamic_margin=True, + x_label="seconds", + y_label="%", + res_tuple=self.main_window_dims, + ) + draw_graph_data( + self.util_data, + UTIL_GRAPH_COLORS_RGBF, + x_axis, + y_axis, + cr, + y_lim=y_lim, + res_tuple=self.main_window_dims, + ) + + self.eventHandler.GraphDrawAreaTop.queue_draw() + + return True + + def _sample_thermal_data(self): + """Sample the thermal data; prefer this function because it timestamps entries to thermal data""" + if self.thermal_data is None: + self.init_graph_data() + + self.thermal_data[TIME_KEY].append(time.monotonic()) + + # Sample and smooth the data with exponential smoothing + cur_cpu = self.eventHandler.sample_data[CPU_THERMAL_KEY] + cur_gpu = self.eventHandler.sample_data[GPU_THERMAL_KEY] + cur_mem = self.eventHandler.sample_data[MEM_THERMAL_KEY] + + last_cpu = self.thermal_data[CPU_THERMAL_KEY][-1] if self.thermal_data[CPU_THERMAL_KEY] else cur_cpu + last_gpu = self.thermal_data[GPU_THERMAL_KEY][-1] if self.thermal_data[GPU_THERMAL_KEY] else cur_gpu + last_mem = self.thermal_data[MEM_THERMAL_KEY][-1] if self.thermal_data[MEM_THERMAL_KEY] else cur_mem + + ema_cpu = get_ema(cur_cpu, last_cpu) + ema_gpu = get_ema(cur_gpu, last_gpu) + ema_mem = get_ema(cur_mem, last_mem) + + self.thermal_data[CPU_THERMAL_KEY].append( + ema_cpu + ) + self.thermal_data[GPU_THERMAL_KEY].append( + ema_gpu + ) + self.thermal_data[MEM_THERMAL_KEY].append( + ema_mem + ) + + cur_time = time.monotonic() + while ( + self.thermal_data[TIME_KEY] + and cur_time - self.thermal_data[TIME_KEY][0] > GRAPH_SAMPLE_WINDOW_SIZE_s + ): + self.thermal_data[TIME_KEY].popleft() + self.thermal_data[CPU_THERMAL_KEY].popleft() + self.thermal_data[GPU_THERMAL_KEY].popleft() + self.thermal_data[MEM_THERMAL_KEY].popleft() + + def on_thermal_graph_draw(self, widget, cr): + if not self.eventHandler.GraphDrawAreaBottom: + return + + if not self.thermal_data: + self.eventHandler.GraphDrawAreaBottom.queue_draw() + return True + + """Draw the graph on the draw area""" + + self._sample_thermal_data() + + width = widget.get_allocated_width() + height = widget.get_allocated_height() + + draw_graph_background_and_border( + width, height, cr, res_tuple=self.main_window_dims + ) + x_min = get_min_time_delta_smoothed(self.thermal_data[TIME_KEY]) + x_lim = (x_min, MAX_TIME_DISPLAYED) + y_lim = (MIN_TEMP_DISPLAYED, MAX_TEMP_DISPLAYED) + + x_axis, y_axis = draw_axes_and_labels( + cr, + width, + height, + x_lim, + y_lim, + x_ticks=4, + y_ticks=2, + dynamic_margin=True, + x_label="seconds", + y_label="°C", + res_tuple=self.main_window_dims, + ) + draw_graph_data( + self.thermal_data, + THERMAL_GRAPH_COLORS_RGBF, + x_axis, + y_axis, + cr, + y_lim=y_lim, + res_tuple=self.main_window_dims, + ) + + self.eventHandler.GraphDrawAreaBottom.queue_draw() + return True + + def localApp(self): + global GladeBuilder + + # Initialize GStreamer. The log level is now controlled by the GST_DEBUG environment variable. + Gst.init(None) + + self.init_graph_data() + + """Build application window and connect signals""" + GladeBuilder.add_from_file(LAYOUT_PATH) + GladeBuilder.connect_signals(self.eventHandler) + + screen = Gdk.Screen.get_default() + provider = Gtk.CssProvider() + provider.load_from_path(os.path.join(RESOURCE_FOLDER, "app.css")) + Gtk.StyleContext.add_provider_for_screen( + screen, provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION + ) + + self.eventHandler.MainWindow = GladeBuilder.get_object("mainWindow") + self.eventHandler.MainWindow.connect( + "size-allocate", self.resize_graphs_dynamically + ) + self.eventHandler.aboutWindow = GladeBuilder.get_object("aboutWindow") + self.eventHandler.FPSRate0 = GladeBuilder.get_object("FPS_rate_0") + self.eventHandler.FPSRate1 = GladeBuilder.get_object("FPS_rate_1") + self.eventHandler.CPU_load = GladeBuilder.get_object("CPU_load") + self.eventHandler.GPU_load = GladeBuilder.get_object("GPU_load") + self.eventHandler.DSP_load = GladeBuilder.get_object("DSP_load") + self.eventHandler.MEM_load = GladeBuilder.get_object("MEM_load") + self.eventHandler.CPU_temp = GladeBuilder.get_object("CPU_temp") + self.eventHandler.GPU_temp = GladeBuilder.get_object("GPU_temp") + self.eventHandler.MEM_temp = GladeBuilder.get_object("MEM_temp") + self.eventHandler.TopBox = GladeBuilder.get_object("TopBox") + self.eventHandler.DataGrid = GladeBuilder.get_object("DataGrid") + self.eventHandler.BottomBox = GladeBuilder.get_object("BottomBox") + self.eventHandler.DrawArea1 = GladeBuilder.get_object("videosink0") + self.eventHandler.DrawArea2 = GladeBuilder.get_object("videosink1") + + self.set_video_sink0 = GladeBuilder.get_object("videosink0") + self.leftHandStatus = GladeBuilder.get_object("leftHandStatus") + self.rightHandStatus = GladeBuilder.get_object("rightHandStatus") + self.leftHandImage = GladeBuilder.get_object("leftHandImage") + self.rightHandImage = GladeBuilder.get_object("rightHandImage") + + self.eventHandler.GraphDrawAreaTop = GladeBuilder.get_object("GraphDrawAreaTop") + self.eventHandler.GraphDrawAreaBottom = GladeBuilder.get_object("GraphDrawAreaBottom") + + self.eventHandler.dialogWindow = GladeBuilder.get_object("dialogWindow") + + # TODO: Dynamic sizing, positioning + self.eventHandler.GraphDrawAreaTop.connect("draw", self.on_util_graph_draw) + self.eventHandler.GraphDrawAreaBottom.connect("draw", self.on_thermal_graph_draw) + + self.eventHandler.QProf = QProfProcess() + self.eventHandler.QProf.daemon = True # Ensure thread doesn't block app exit + + # TODO: Can just put these in CSS + #self.eventHandler.MainWindow.override_background_color( + # Gtk.StateFlags.NORMAL, Gdk.RGBA(23 / 255, 23 / 255, 23 / 255, 0) + #) + #self.eventHandler.TopBox.override_background_color( + # Gtk.StateType.NORMAL, Gdk.RGBA(23 / 255, 23 / 255, 23 / 255, 0.5) + #) + #self.eventHandler.BottomBox.override_background_color( + # Gtk.StateType.NORMAL, Gdk.RGBA(0 / 255, 23 / 255, 23 / 255, 1) + #) + + self.eventHandler.MainWindow.set_decorated(False) + self.eventHandler.MainWindow.set_keep_below(True) + self.eventHandler.MainWindow.maximize() + self.eventHandler.MainWindow.show_all() + + + self.eventHandler.QProf.start() + + settings = Gtk.Settings.get_default() + settings.set_property("gtk-cursor-theme-name","Adwaita") + settings.set_property("gtk-cursor-theme-size", 32) + + # --- Filter unwanted log messages from ML plugin --- + # The QNN plugin prints log messages that can't be suppressed via + # environment variables, so we filter them from the low-level + # file descriptors directly. + filter_list = [ + " No usable logger handle was found", + " Logs will be sent to the system's default channel", + "Could not find ncvt for conv cost", + "Could not find conv_ctrl for conv cost" + ] + self.log_filter = FdFilter(filter_list) + # --- End Filter --- + +# --- Main Function --- +# (This function remains unchanged) +def main(args=None): + rclpy.init(args=args) + print(TRIA) + print(f"\nLaunching {APP_HEADER}") + # Create the video object + # Add port= if is necessary to use a different one + window = VaiDemoManager() + window.localApp() + ros_node = GuiLogicNode(gui_window=window) + window.ros_node = ros_node + executor = SingleThreadedExecutor() + executor.add_node(ros_node) + + def ros_spin(): + executor.spin_once(timeout_sec=0.01) + return True + + GObject.timeout_add(30, ros_spin) + try: + Gtk.main() + except KeyboardInterrupt: + ros_node.get_logger().info("Keyboard interrupt, shutting down.") + finally: + ros_node.get_logger().info("Shutting down.") + executor.shutdown() + ros_node.destroy_node() + +if __name__ == '__main__': + main() diff --git a/ros2_ws/hand_controller/hand_controller/gtk_gui_node_1.py b/ros2_ws/hand_controller/hand_controller/gtk_gui_node_1.py new file mode 100644 index 0000000..e8f21b3 --- /dev/null +++ b/ros2_ws/hand_controller/hand_controller/gtk_gui_node_1.py @@ -0,0 +1,147 @@ +# gtk_gui_node.py + +import rclpy +from rclpy.node import Node +from rclpy.executors import SingleThreadedExecutor +from std_msgs.msg import String +from sensor_msgs.msg import Image as RosImage # Import ROS Image message +from cv_bridge import CvBridge # To convert between ROS and OpenCV images +import cv2 # OpenCV library +import numpy as np # NumPy for array manipulation + +import gi +gi.require_version('Gtk', '3.0') +from gi.repository import Gtk, GObject, GLib, GdkPixbuf + +# --- ROS2 Node Class --- +# Handles all ROS2 logic, now including image subscription. +class GuiLogicNode(Node): + def __init__(self, gui_window): + super().__init__('gtk_gui_node') + self.gui = gui_window + self.bridge = CvBridge() # Initialize CvBridge + + # --- ROS2 Publishers and Subscribers --- + self.publisher_ = self.create_publisher(String, 'gui_button_clicks', 10) + + self.status_subscription = self.create_subscription( + String, + 'gui_status_topic', + self.status_callback, + 10) + + # New subscriber for the annotated image stream + self.image_subscription = self.create_subscription( + RosImage, + '/hand_controller/image_annotated', # The topic to view + self.image_callback, + 10) # QoS profile depth + + def status_callback(self, msg: String): + GLib.idle_add(self.gui.update_status_label, f"Status: {msg.data}") + + def image_callback(self, msg: RosImage): + """Callback for receiving a ROS Image message.""" + try: + # Convert the ROS Image message to an OpenCV image (BGR format) + cv_image = self.bridge.imgmsg_to_cv2(msg, "bgr8") + + # Convert the OpenCV image to a GdkPixbuf for GTK display. + # OpenCV uses BGR, but GdkPixbuf expects RGB. + h, w, c = cv_image.shape + # Create a Pixbuf from the NumPy array data + pixbuf = GdkPixbuf.Pixbuf.new_from_data( + cv2.cvtColor(cv_image, cv2.COLOR_BGR2RGB).tobytes(), + GdkPixbuf.Colorspace.RGB, + False, # has_alpha + 8, # bits_per_sample + w, h, # width, height + w * c # rowstride + ) + + # Call the GUI update method in a thread-safe way + GLib.idle_add(self.gui.update_image_view, pixbuf) + + except Exception as e: + self.get_logger().error(f'Failed to process image: {e}') + + def on_button_clicked(self): + msg = String() + msg.data = f"Button clicked at {self.get_clock().now().to_msg().sec}" + self.publisher_.publish(msg) + self.get_logger().info(f'Publishing: "{msg.data}"') + +# --- GTK Window Class --- +# Handles all GUI logic, now including an image widget. +class GtkWindow(Gtk.Window): + def __init__(self): + super().__init__(title="GTK+ 3 ROS2 Node with Camera") + self.set_default_size(800, 600) # Increased window size for the camera view + self.ros_node = None + + # Use a vertical box to stack the camera view and controls + vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=10) + vbox.set_margin_top(10) + vbox.set_margin_bottom(10) + vbox.set_margin_start(10) + vbox.set_margin_end(10) + self.add(vbox) + + # --- New Gtk.Image widget for the camera stream --- + self.image_view = Gtk.Image() + self.image_view.set_hexpand(True) + self.image_view.set_vexpand(True) + vbox.pack_start(self.image_view, True, True, 0) + + # --- Control widgets at the bottom --- + controls_grid = Gtk.Grid(column_spacing=10, row_spacing=10) + vbox.pack_start(controls_grid, False, False, 0) + + self.status_label = Gtk.Label(label="Waiting for status...") + self.status_label.set_hexpand(True) + + self.click_button = Gtk.Button(label="Publish Message") + self.click_button.connect("clicked", self.button_callback) + + controls_grid.attach(self.status_label, 0, 0, 1, 1) + controls_grid.attach(self.click_button, 1, 0, 1, 1) + + def button_callback(self, widget): + if self.ros_node: + self.ros_node.on_button_clicked() + + def update_status_label(self, text): + self.status_label.set_text(text) + + def update_image_view(self, pixbuf): + """Thread-safe method to update the Gtk.Image widget.""" + self.image_view.set_from_pixbuf(pixbuf) + +# --- Main Function --- +# (This function remains unchanged) +def main(args=None): + rclpy.init(args=args) + window = GtkWindow() + ros_node = GuiLogicNode(gui_window=window) + window.ros_node = ros_node + window.connect("destroy", Gtk.main_quit) + window.show_all() + executor = SingleThreadedExecutor() + executor.add_node(ros_node) + + def ros_spin(): + executor.spin_once(timeout_sec=0.01) + return True + + GObject.timeout_add(100, ros_spin) + try: + Gtk.main() + except KeyboardInterrupt: + ros_node.get_logger().info("Keyboard interrupt, shutting down.") + finally: + ros_node.get_logger().info("Shutting down.") + executor.shutdown() + ros_node.destroy_node() + +if __name__ == '__main__': + main() diff --git a/ros2_ws/hand_controller/hand_controller/gtk_gui_node_2.py b/ros2_ws/hand_controller/hand_controller/gtk_gui_node_2.py new file mode 100644 index 0000000..34e2809 --- /dev/null +++ b/ros2_ws/hand_controller/hand_controller/gtk_gui_node_2.py @@ -0,0 +1,153 @@ +# gtk_gui_node.py + +import rclpy +from rclpy.node import Node +from rclpy.executors import SingleThreadedExecutor +from rclpy.qos import QoSProfile, QoSReliabilityPolicy, QoSHistoryPolicy +from std_msgs.msg import String +from sensor_msgs.msg import Image as RosImage +from cv_bridge import CvBridge +import cv2 +import threading +from collections import deque +import time + +import gi +gi.require_version('Gtk', '3.0') +from gi.repository import Gtk, GObject, GLib, GdkPixbuf + +# --- 1. The Worker Thread: For Heavy Lifting --- +class FrameProcessor(threading.Thread): + """A dedicated worker thread for converting OpenCV frames to GdkPixbufs.""" + def __init__(self, input_queue, output_queue, logger): + super().__init__() + self.input_queue = input_queue + self.output_queue = output_queue + self.logger = logger + self.daemon = True # Allows main program to exit even if this thread is running + self._is_running = threading.Event() + self._is_running.set() + + def run(self): + """The main loop of the worker thread.""" + while self._is_running.is_set(): + try: + # Block until a frame is available or timeout + cv_image = self.input_queue.popleft() + + # --- The expensive work happens here --- + h, w, c = cv_image.shape + rgb_frame = cv2.cvtColor(cv_image, cv2.COLOR_BGR2RGB) + pixbuf = GdkPixbuf.Pixbuf.new_from_data( + rgb_frame.tobytes(), GdkPixbuf.Colorspace.RGB, False, 8, w, h, w * c) + + # Put the finished product in the output queue + self.output_queue.append(pixbuf) + + except IndexError: + # This is normal, means the input queue was empty + time.sleep(0.005) # Sleep briefly to prevent busy-waiting + except Exception as e: + self.logger.error(f'Frame processing failed: {e}') + + def stop(self): + """Signals the thread to stop.""" + self._is_running.clear() + +# --- 2. The ROS Node: For Communication --- +class GuiLogicNode(Node): + """Handles all ROS2 communication and pushes frames to the processing queue.""" + def __init__(self, frame_queue): + super().__init__('gtk_gui_node') + self.bridge = CvBridge() + self.frame_queue = frame_queue # The queue for raw OpenCV frames + + qos_profile = QoSProfile( + reliability=QoSReliabilityPolicy.BEST_EFFORT, + history=QoSHistoryPolicy.KEEP_LAST, + depth=1 + ) + + self.image_subscription = self.create_subscription( + RosImage, + '/hand_controller/image_annotated', + self.image_callback, + qos_profile) + + def image_callback(self, msg: RosImage): + """Extremely lightweight callback. Just converts and queues the frame.""" + try: + cv_image = self.bridge.imgmsg_to_cv2(msg, "bgr8") + self.frame_queue.append(cv_image) + except Exception as e: + self.get_logger().error(f'Image callback failed: {e}') + +# --- 3. The GTK Window: For Display --- +class GtkWindow(Gtk.Window): + """Handles all GUI elements and rendering.""" + def __init__(self): + super().__init__(title="High-Performance ROS2 Camera Viewer") + self.set_default_size(800, 600) + self.connect("destroy", Gtk.main_quit) + + vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6, margin=6) + self.add(vbox) + + self.image_view = Gtk.Image() + vbox.pack_start(self.image_view, True, True, 0) + + def update_image(self, pixbuf): + """Receives a finished GdkPixbuf and displays it.""" + self.image_view.set_from_pixbuf(pixbuf) + +# --- 4. The Main Application Director --- +def main(args=None): + rclpy.init(args=args) + + # --- Create the components --- + window = GtkWindow() + ros_node = GuiLogicNode(frame_queue=deque(maxlen=1)) + pixbuf_queue = deque(maxlen=1) + + # Create and start the dedicated worker thread + worker = FrameProcessor(ros_node.frame_queue, pixbuf_queue, ros_node.get_logger()) + worker.start() + + # Set up the ROS executor + executor = SingleThreadedExecutor() + executor.add_node(ros_node) + + # --- Set up the event loops --- + def gui_update_loop(): + """Pulls from the finished queue and updates the GUI. Runs in the GUI thread.""" + try: + pixbuf = pixbuf_queue.popleft() + window.update_image(pixbuf) + except IndexError: + pass # No new frame to display + return True + + def ros_spin_loop(): + """Spins the ROS executor. Runs in the GUI thread.""" + executor.spin_once(timeout_sec=0) + return True + + # Add loops to the GLib event manager + GObject.timeout_add(16, gui_update_loop) # ~60fps for GUI updates + GObject.timeout_add(10, ros_spin_loop) # ~100Hz for ROS spinning + + window.show_all() + + try: + Gtk.main() + except KeyboardInterrupt: + ros_node.get_logger().info("Keyboard interrupt received.") + finally: + ros_node.get_logger().info("Shutting down...") + worker.stop() # Signal the worker thread to exit + worker.join(1.0) # Wait for the worker to finish + executor.shutdown() + ros_node.destroy_node() + +if __name__ == '__main__': + main() diff --git a/ros2_ws/hand_controller/hand_controller/gtk_gui_node_threaded_Not_good.py b/ros2_ws/hand_controller/hand_controller/gtk_gui_node_threaded_Not_good.py new file mode 100644 index 0000000..34e2809 --- /dev/null +++ b/ros2_ws/hand_controller/hand_controller/gtk_gui_node_threaded_Not_good.py @@ -0,0 +1,153 @@ +# gtk_gui_node.py + +import rclpy +from rclpy.node import Node +from rclpy.executors import SingleThreadedExecutor +from rclpy.qos import QoSProfile, QoSReliabilityPolicy, QoSHistoryPolicy +from std_msgs.msg import String +from sensor_msgs.msg import Image as RosImage +from cv_bridge import CvBridge +import cv2 +import threading +from collections import deque +import time + +import gi +gi.require_version('Gtk', '3.0') +from gi.repository import Gtk, GObject, GLib, GdkPixbuf + +# --- 1. The Worker Thread: For Heavy Lifting --- +class FrameProcessor(threading.Thread): + """A dedicated worker thread for converting OpenCV frames to GdkPixbufs.""" + def __init__(self, input_queue, output_queue, logger): + super().__init__() + self.input_queue = input_queue + self.output_queue = output_queue + self.logger = logger + self.daemon = True # Allows main program to exit even if this thread is running + self._is_running = threading.Event() + self._is_running.set() + + def run(self): + """The main loop of the worker thread.""" + while self._is_running.is_set(): + try: + # Block until a frame is available or timeout + cv_image = self.input_queue.popleft() + + # --- The expensive work happens here --- + h, w, c = cv_image.shape + rgb_frame = cv2.cvtColor(cv_image, cv2.COLOR_BGR2RGB) + pixbuf = GdkPixbuf.Pixbuf.new_from_data( + rgb_frame.tobytes(), GdkPixbuf.Colorspace.RGB, False, 8, w, h, w * c) + + # Put the finished product in the output queue + self.output_queue.append(pixbuf) + + except IndexError: + # This is normal, means the input queue was empty + time.sleep(0.005) # Sleep briefly to prevent busy-waiting + except Exception as e: + self.logger.error(f'Frame processing failed: {e}') + + def stop(self): + """Signals the thread to stop.""" + self._is_running.clear() + +# --- 2. The ROS Node: For Communication --- +class GuiLogicNode(Node): + """Handles all ROS2 communication and pushes frames to the processing queue.""" + def __init__(self, frame_queue): + super().__init__('gtk_gui_node') + self.bridge = CvBridge() + self.frame_queue = frame_queue # The queue for raw OpenCV frames + + qos_profile = QoSProfile( + reliability=QoSReliabilityPolicy.BEST_EFFORT, + history=QoSHistoryPolicy.KEEP_LAST, + depth=1 + ) + + self.image_subscription = self.create_subscription( + RosImage, + '/hand_controller/image_annotated', + self.image_callback, + qos_profile) + + def image_callback(self, msg: RosImage): + """Extremely lightweight callback. Just converts and queues the frame.""" + try: + cv_image = self.bridge.imgmsg_to_cv2(msg, "bgr8") + self.frame_queue.append(cv_image) + except Exception as e: + self.get_logger().error(f'Image callback failed: {e}') + +# --- 3. The GTK Window: For Display --- +class GtkWindow(Gtk.Window): + """Handles all GUI elements and rendering.""" + def __init__(self): + super().__init__(title="High-Performance ROS2 Camera Viewer") + self.set_default_size(800, 600) + self.connect("destroy", Gtk.main_quit) + + vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6, margin=6) + self.add(vbox) + + self.image_view = Gtk.Image() + vbox.pack_start(self.image_view, True, True, 0) + + def update_image(self, pixbuf): + """Receives a finished GdkPixbuf and displays it.""" + self.image_view.set_from_pixbuf(pixbuf) + +# --- 4. The Main Application Director --- +def main(args=None): + rclpy.init(args=args) + + # --- Create the components --- + window = GtkWindow() + ros_node = GuiLogicNode(frame_queue=deque(maxlen=1)) + pixbuf_queue = deque(maxlen=1) + + # Create and start the dedicated worker thread + worker = FrameProcessor(ros_node.frame_queue, pixbuf_queue, ros_node.get_logger()) + worker.start() + + # Set up the ROS executor + executor = SingleThreadedExecutor() + executor.add_node(ros_node) + + # --- Set up the event loops --- + def gui_update_loop(): + """Pulls from the finished queue and updates the GUI. Runs in the GUI thread.""" + try: + pixbuf = pixbuf_queue.popleft() + window.update_image(pixbuf) + except IndexError: + pass # No new frame to display + return True + + def ros_spin_loop(): + """Spins the ROS executor. Runs in the GUI thread.""" + executor.spin_once(timeout_sec=0) + return True + + # Add loops to the GLib event manager + GObject.timeout_add(16, gui_update_loop) # ~60fps for GUI updates + GObject.timeout_add(10, ros_spin_loop) # ~100Hz for ROS spinning + + window.show_all() + + try: + Gtk.main() + except KeyboardInterrupt: + ros_node.get_logger().info("Keyboard interrupt received.") + finally: + ros_node.get_logger().info("Shutting down...") + worker.stop() # Signal the worker thread to exit + worker.join(1.0) # Wait for the worker to finish + executor.shutdown() + ros_node.destroy_node() + +if __name__ == '__main__': + main() diff --git a/ros2_ws/hand_controller/hand_controller/usbcam_subscriber_flask_node.py b/ros2_ws/hand_controller/hand_controller/usbcam_subscriber_flask_node.py index 4f6c042..484f33a 100644 --- a/ros2_ws/hand_controller/hand_controller/usbcam_subscriber_flask_node.py +++ b/ros2_ws/hand_controller/hand_controller/usbcam_subscriber_flask_node.py @@ -278,9 +278,9 @@ def main(): # Run with debug mode for auto-reload if os.environ.get('FLASK_DEBUG'): - app.run(host='0.0.0.0', port=5000, debug=True, use_reloader=True, threaded=True) + app.run(host='0.0.0.0', port=5001, debug=True, use_reloader=True, threaded=True) else: - app.run(host='0.0.0.0', port=5000, debug=False, threaded=True) + app.run(host='0.0.0.0', port=5001, debug=False, threaded=True) if __name__ == '__main__': main() \ No newline at end of file diff --git a/ros2_ws/hand_controller/hand_controller/vai/__init__.py b/ros2_ws/hand_controller/hand_controller/vai/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ros2_ws/hand_controller/hand_controller/vai/common.py b/ros2_ws/hand_controller/hand_controller/vai/common.py new file mode 100644 index 0000000..2f1c807 --- /dev/null +++ b/ros2_ws/hand_controller/hand_controller/vai/common.py @@ -0,0 +1,86 @@ +"""Common utilities and constants for VAI demo""" + +import subprocess + +GRAPH_SAMPLE_WINDOW_SIZE_s = 31 +HW_SAMPLING_PERIOD_ms = 250 +GRAPH_DRAW_PERIOD_ms = 30 +AUTOMATIC_DEMO_SWITCH_s = 60 +QUIT_CLEANUP_DELAY_ms = 1000 + +GRAPH_SAMPLE_SIZE = int(GRAPH_SAMPLE_WINDOW_SIZE_s * 1000 / GRAPH_DRAW_PERIOD_ms) + +TIME_KEY = "time" +CPU_UTIL_KEY = "cpu %" +MEM_UTIL_KEY = "lpddr5 %" +GPU_UTIL_KEY = "gpu %" +DSP_UTIL_KEY = "dsp %" +CPU_THERMAL_KEY = "cpu temp (°c)" +MEM_THERMAL_KEY = "lpddr5 temp (°c)" +GPU_THERMAL_KEY = "gpu temp (°c)" + +# Triadic colors, indexed on Tria pink +TRIA_PINK_RGBH = (0xFE, 0x00, 0xA2) +TRIA_BLUE_RGBH = (0x00, 0xA2, 0xFE) +TRIA_YELLOW_RGBH = (0xFE, 0xDB, 0x00) +TRIA_GREEN_RGBH = (0x22, 0xB1, 0x4C) + +APP_NAME = f"GTK GUI Node" + +TRIA = r""" +████████╗██████╗ ██╗ █████╗ +╚══██╔══╝██╔══██╗██║██╔══██╗ + ██║ ██████╔╝██║███████║ + ██║ ██╔══██╗██║██╔══██║ + ██║ ██║ ██║██║██║ ██║ + ╚═╝ ╚═╝ ╚═╝╚═╝╚═╝ ╚═╝ +""" + + +def lerp(a, b, t): + """Linear interpolation between two values""" + return a + t * (b - a) + + +def inverse_lerp(a, b, v): + """Inverse linear interpolation between two values""" + return (v - a) / (b - a) if a != b else 0.0 + + +def get_ema(x_cur, x_last, alpha=0.75): + """ + Exponential moving average + + Args: + x_cur: Current value + x_last: Last value + alpha: Smoothing factor + + Note: + alpha is a misnomer. alpha = 1.0 is equivalent to no smoothing + + Ref: + https://en.wikipedia.org/wiki/Exponential_smoothing + + """ + return alpha * x_cur + (1 - alpha) * x_last + + +def app_version(): + """Get the latest tag or commit hash if possible, unknown otherwise""" + + try: + version = subprocess.check_output( + ["git", "describe", "--tags", "--always"], text=True + ).strip() + date = subprocess.check_output( + ["git", "log", "-1", "--format=%cd", "--date=short"], text=True + ).strip() + + return f"{version} {date}" + except subprocess.CalledProcessError: + # Handle errors, such as not being in a Git repository + return "unknown" + + +APP_HEADER = f"{APP_NAME} v({app_version()})" \ No newline at end of file diff --git a/ros2_ws/hand_controller/hand_controller/vai/graphing.py b/ros2_ws/hand_controller/hand_controller/vai/graphing.py new file mode 100644 index 0000000..36537ec --- /dev/null +++ b/ros2_ws/hand_controller/hand_controller/vai/graphing.py @@ -0,0 +1,315 @@ +from hand_controller.vai.common import inverse_lerp, lerp + +# Scaling boundaries (assumes res between 1920x1080 and 3840x2160) +GRAPH_LABEL_FONT_SIZE_MIN = 5 +GRAPH_LABEL_FONT_SIZE_MAX = 30 + +GRAPH_AXIS_LINE_WIDTH_MIN = 2 +GRAPH_AXIS_LINE_WIDTH_MAX = 4 + +GRAPH_DATA_LINE_WIDTH_MIN = 2 +GRAPH_DATA_LINE_WIDTH_MAX = 4 + +GRAPH_AXIS_TICK_LENGTH_MIN = 6 +GRAPH_AXIS_TICK_LENGTH_MAX = 12 + +MAX_RES_WIDTH = 3840 + + +def draw_graph_background_and_border( + width, + height, + cr, + bg_color=(0.1, 0.1, 0.1, 0.15), + border_color=None, + res_tuple=(1920, 1080), +): + """ + Draws a background and border for the graph area. Can pass res to scale border reasonably + + Args: + width: Width of the graph area + height: Height of the graph area + cr: Cairo context + bg_color: Background color (RGBA tuple) + border_color: Border color (RGBA tuple) + res_tuple: Tuple of (width, height) of the screen resolution + + """ + line_width = int( + lerp( + GRAPH_AXIS_LINE_WIDTH_MIN, + GRAPH_AXIS_LINE_WIDTH_MAX, + res_tuple[0] / MAX_RES_WIDTH, + ) + ) + cr.set_line_width(line_width) + cr.set_source_rgba(*bg_color) + + # Offset by half the line width so the stroke is fully visible inside the widget: + # In Cairo, rectangle(x, y, w, h) draws from (x, y) to (x + w, y + h). + # The stroke is centered on the path, so we typically offset the path by line_width/2 + # and reduce the drawn rectangle dimensions accordingly. + offset = line_width / 2.0 + + cr.rectangle( + offset, + offset, + width - line_width, + height - line_width, + ) + + cr.fill_preserve() + + if border_color: + cr.set_source_rgba(*border_color) + cr.stroke() + + +def draw_axes_and_labels( + cr, + width, + height, + x_lim, + y_lim, + x_ticks=4, + y_ticks=4, + dynamic_margin=True, + right_margin=20, + bottom_margin=20, + x_label=None, + y_label=None, + res_tuple=(1920, 1080), +): + """ + Draws simple axes with labeled tick marks along bottom (x-axis) and left (y-axis). + + Args: + cr : Cairo context + width : total width of the drawing area + height : total height of the drawing area + x_lim : (xmin, xmax) for the data domain you want to label + y_lim : (ymin, ymax) for the data domain (like (0, 100)) + x_ticks : how many segments (thus x_ticks+1 labeled steps) + y_ticks : how many segments (thus y_ticks+1 labeled steps) + dynamic_margin: If True, the right and bottom margins will be scaled based on the resolution + right_margin: Margin on the right side of the graph + bottom_margin: Margin on the bottom of the graph + x_label : Label for the x-axis + y_label : Label for the y-axis + res_tuple: Tuple of (width, height) of the screen resolution + + Returns: + (x, y) axis positions + """ + if not x_lim or not y_lim: + return width, height + + if dynamic_margin: + right_margin = lerp(20, 100, res_tuple[0] / MAX_RES_WIDTH) + bottom_margin = lerp(20, 40, res_tuple[0] / MAX_RES_WIDTH) + + width -= right_margin # Leave a little space on the right for the legend + height -= bottom_margin # Leave a little space on the bottom for the x-axis labels + + if not x_ticks or not y_ticks: # No ticks, nothing to draw + return width, height + + axis_width = lerp( + GRAPH_AXIS_LINE_WIDTH_MIN, + GRAPH_AXIS_LINE_WIDTH_MAX, + res_tuple[0] / MAX_RES_WIDTH, + ) + font_size = lerp( + GRAPH_LABEL_FONT_SIZE_MIN, + GRAPH_LABEL_FONT_SIZE_MAX, + res_tuple[0] / MAX_RES_WIDTH, + ) + + cr.save() # save the current transformation/state + + cr.set_line_width(axis_width) + cr.set_source_rgb(1, 1, 1) # white lines & text + + # --- Draw X-axis --- + cr.move_to(0, height) + cr.line_to(width, height) + cr.stroke() + + # --- Draw Y-axis --- + cr.move_to(width, 0) + cr.line_to(width, height) + cr.stroke() + + # Set font for labels + cr.select_font_face("Sans", 0, 0) # (slant=0 normal, weight=0 normal) + cr.set_font_size(font_size) + + # --- X Ticks and Labels --- + # e.g. if x_lim = (0,100), for 4 ticks => labeled at x=0,25,50,75,100 + x_min, x_max = x_lim + dx = (x_max - x_min) / (x_ticks or 1) + for i in range(x_ticks + 1): + x_val = x_min + i * dx + # Convert data → screen coordinate: 0..width + x_screen = int((x_val - x_min) / ((x_max - x_min) or 1) * width) + + # Tick mark from (x_screen, height) up a bit + tick_length = 6 + cr.move_to(x_screen, height) + cr.line_to(x_screen, height - tick_length) + cr.stroke() + + # Draw text label under the axis + text = f"{int(x_val)}" + te = cr.text_extents(text) + text_x = x_screen - te.width // 2 if i != 0 else te.width // 2 + # At the intersection of the x/y axes, adjust text position further + if i == x_ticks: + text_x -= te.width + text_y = height + te.height + 4 + cr.move_to(text_x, text_y) + if i != 0: + cr.show_text(text) + elif x_label: + cr.show_text(text + " " + x_label) + + # --- Y Ticks and Labels --- + y_min, y_max = y_lim + dy = (y_max - y_min) / (y_ticks or 1) + for j in range(y_ticks + 1): + y_val = y_min + j * dy + y_ratio = (y_val - y_min) / ((y_max - y_min) or 1) + y_screen = int(height - y_ratio * height) # 0 -> bottom, height -> top + + tick_length = lerp( + GRAPH_AXIS_TICK_LENGTH_MIN, + GRAPH_AXIS_TICK_LENGTH_MAX, + res_tuple[0] / MAX_RES_WIDTH, + ) + cr.move_to(width, y_screen) + cr.line_to(width - tick_length, y_screen) + cr.stroke() + + text = f"{int(y_val)}" + if y_label and j == y_ticks: + text += y_label + te = cr.text_extents(text) + text_x = width + 4 + text_y = ( + y_screen + te.height // 2 + if j != y_ticks + else y_screen + te.height + lerp(2, 4, res_tuple[0] / MAX_RES_WIDTH) + ) + if j == 0: + text_y -= te.height // 2 + cr.move_to(text_x, text_y) + cr.show_text(text) + + cr.restore() + return width, height + + +#def draw_graph_legend(label_color_map, width, cr, legend_x_width=None): +# """ +# Draw the legend for the graph, returning the x position of the legend +# +# Args: +# label_color_map: Dict of label to RGB color tuple +# width: Width of the graph area +# cr: Cairo context +# legend_x_width: Width of the legend box. If None, the width is determined by the labels +# """ +# # --- Draw Legend --- +# # TODO: Scale by res? +# legend_margin_x = 20 # Distance from the right edge +# legend_margin_y = 10 # Distance from the top edge +# box_size = 20 # Size of the color box +# spacing = 30 # Vertical spacing between entries +# legend_padding_x = 5 +# +# cr.select_font_face("Sans", 0, 1) # Bold weight & normal slant +# cr.set_font_size(10) +# +# text_guess_width = 11 * max(len(label) for label, _ in label_color_map.items()) +# legend_x = ( +# width - legend_x_width +# if legend_x_width +# else width - legend_margin_x - text_guess_width - box_size +# ) +# +# # Tuning offset variable +# for i, (label, color) in enumerate(label_color_map.items()): +# item_y = legend_margin_y + i * spacing +# +# # Draw color box +# cr.set_source_rgb(*color) +# cr.rectangle(legend_x, item_y, box_size, box_size) +# cr.fill() +# +# # Draw label text in white +# cr.set_source_rgb(1, 1, 1) +# text_x = legend_x + box_size + legend_padding_x +# text_y = ( +# item_y + box_size - 5 +# ) # Shift text slightly so it's vertically centered +# cr.move_to(text_x, text_y) +# cr.show_text(label.upper()) +# +# return legend_x + + +def draw_graph_data( + data_map, + data_color_map, + width, + height, + cr, + y_lim=(0, 100), + res_tuple=(1920, 1080), +): + """Draw the graph data on the draw area with the given colors + + Args: + data_map: Dict of data key to list of data values + data_color_map: Dict of data key to RGB color tuple + width: Width of the graph area + height: Height of the graph area + cr: Cairo context + y_lim (optional): Tuple of min and max y values + """ + if not data_map or not data_color_map: + return + + graph_line_width = lerp( + GRAPH_DATA_LINE_WIDTH_MIN, + GRAPH_DATA_LINE_WIDTH_MAX, + res_tuple[0] / MAX_RES_WIDTH, + ) + cr.set_line_width(graph_line_width) + + for data_key, data in data_map.items(): + if data_key not in data_color_map: + continue # Skip if no color for this data (like timestamps) + + cr.set_source_rgb(*data_color_map[data_key]) + # Start the cursor at the first data point if possible + # x is 0 or width because our data must always fit between 0 and width, (not the case for y values) + cr.move_to( + 0 if data else width, + ( + height + if not data + else int(lerp(0, height, 1 - inverse_lerp(y_lim[0], y_lim[1], data[0]))) + ), + ) + # We must inverse lerp to get the % of the y value between the min and max y values for instances where y_lim is not 0-100 + for x in range(1, len(data)): + y_pct = inverse_lerp(y_lim[0], y_lim[1], data[x]) + cr.line_to( + int( + lerp(0, width, x / len(data)) + ), # cant divide by 0 in this range definition + int(lerp(0, height, 1 - y_pct)), + ) + cr.stroke() diff --git a/ros2_ws/hand_controller/hand_controller/vai/handler.py b/ros2_ws/hand_controller/hand_controller/vai/handler.py new file mode 100644 index 0000000..287808a --- /dev/null +++ b/ros2_ws/hand_controller/hand_controller/vai/handler.py @@ -0,0 +1,185 @@ +import pathlib +import subprocess +import os +from hand_controller.vai.qprofile import QProfProcess +import gi +import threading +from gi.repository import Gdk + +from hand_controller.vai.common import ( + APP_NAME, + CPU_THERMAL_KEY, + CPU_UTIL_KEY, + GPU_THERMAL_KEY, + GPU_UTIL_KEY, + MEM_THERMAL_KEY, + MEM_UTIL_KEY, + DSP_UTIL_KEY, + HW_SAMPLING_PERIOD_ms, + AUTOMATIC_DEMO_SWITCH_s, + QUIT_CLEANUP_DELAY_ms +) +from hand_controller.vai.temp_profile import get_cpu_gpu_mem_temps + +# Locks app version, prevents warnings +gi.require_version("Gtk", "3.0") +gi.require_version('Gst', '1.0') + +from gi.repository import GLib, Gtk, Gst + +# needed to release gstreamer with cv2 +os.environ['OPENCV_VIDEOIO_PRIORITY_MSMF'] = '0' + +# Tuning variable to adjust the height of the video display +HEIGHT_OFFSET = 17 +MAX_WINDOW_WIDTH = 1920 // 2 +MAX_WINDOW_HEIGHT = 720 +MIPI_CSI_CAMERA_SCAN_TIMEOUT = 5 +CLOSE_APPLICATION_DELAY = 2 + +DUAL_WINDOW_DEMOS = ["add drop down items here if needed"] + +class Handler: + def __init__(self, display_fps_metrics=True): + + self.demoList = [ + None, + ] + + self.QProf = QProfProcess() + self.MainWindowShown = False + self.MainWindow = None + self.aboutWindow = None + self.FPSRate0 = None + self.FPSRate1 = None + self.CPU_load = None + self.GPU_load = None + self.DSP_load = None + self.MEM_load = None + self.CPU_temp = None + self.GPU_temp = None + self.MEM_temp = None + self.TopBox = None + self.DataGrid = None + self.BottomBox = None + self.DrawArea1 = None + self.DrawArea2 = None + self.AspectFrame1 = None + self.AspectFrame2 = None + self.GraphDrawAreaTop = None + self.GraphDrawAreaBottom = None + self.demo_selection0 = None + self.demo_selection1 = None + self.display_fps_metrics = display_fps_metrics + self.systemCameras = [] + self.dualDemoRunning0 = False + self.dualDemoRunning1 = False + self.CycleDemo0 = False + self.CycleDemo1 = False + self.demoSelection0Cnt = 0 + self.demoSelection1Cnt = 0 + + # TODO: protect with sync primitive? + self.sample_data = { + CPU_UTIL_KEY: 0, + MEM_UTIL_KEY: 0, + GPU_UTIL_KEY: 0, + DSP_UTIL_KEY: 0, + CPU_THERMAL_KEY: 0, + MEM_THERMAL_KEY: 0, + GPU_THERMAL_KEY: 0, + } + GLib.timeout_add(HW_SAMPLING_PERIOD_ms, self.update_sample_data) + + def update_temps(self): + if not self.sample_data: + return GLib.SOURCE_REMOVE + + cpu_temp, gpu_temp, mem_temp = get_cpu_gpu_mem_temps() + + self.sample_data[CPU_THERMAL_KEY] = cpu_temp + if cpu_temp is not None: + GLib.idle_add(self.CPU_temp.set_text, "{:6.2f}".format(cpu_temp)) + self.sample_data[GPU_THERMAL_KEY] = gpu_temp + if gpu_temp is not None: + GLib.idle_add(self.GPU_temp.set_text, "{:6.2f}".format(gpu_temp)) + self.sample_data[MEM_THERMAL_KEY] = mem_temp + if mem_temp is not None: + GLib.idle_add(self.MEM_temp.set_text, "{:6.2f}".format(mem_temp)) + + return GLib.SOURCE_REMOVE + + def update_loads(self): + if not self.sample_data: + return GLib.SOURCE_REMOVE + + cpu_util, gpu_util, mem_util, dsp_util = ( + self.QProf.get_cpu_usage_pct(), + self.QProf.get_gpu_usage_pct(), + self.QProf.get_memory_usage_pct(), + self.QProf.get_dsp_usage_pct(), + ) + self.sample_data[CPU_UTIL_KEY] = cpu_util + self.sample_data[GPU_UTIL_KEY] = gpu_util + self.sample_data[MEM_UTIL_KEY] = mem_util + self.sample_data[DSP_UTIL_KEY] = dsp_util + GLib.idle_add(self.CPU_load.set_text, "{:6.2f}".format(cpu_util)) + GLib.idle_add(self.GPU_load.set_text, "{:6.2f}".format(gpu_util)) + GLib.idle_add(self.MEM_load.set_text, "{:6.2f}".format(mem_util)) + GLib.idle_add(self.DSP_load.set_text, "{:6.2f}".format(dsp_util)) + return GLib.SOURCE_REMOVE + + def update_sample_data(self): + # Run blocking I/O in separate threads to avoid freezing the UI. + # The update functions will then schedule UI updates on the main thread. + threading.Thread(target=self.update_temps, daemon=True).start() + threading.Thread(target=self.update_loads, daemon=True).start() + return GLib.SOURCE_CONTINUE + + def close_about(self, *args): + if self.aboutWindow: + self.aboutWindow.hide() + + def open_about(self, *args): + if self.aboutWindow: + self.aboutWindow.set_transient_for(self.MainWindow) + self.aboutWindow.run() + + def on_mainWindow_destroy(self, *args): + """Handle exit signals and clean up resources before exiting the application. + + Due to the threaded nature of the application, this function needs to be carefully linked with Gtk + """ + print("Shutdown initiated...") + if self.QProf is not None: + self.QProf.Close() + + # Schedule the final shutdown sequence. + GLib.timeout_add(QUIT_CLEANUP_DELAY_ms, self.quit_application, *args) + + def quit_application(self, *args): + print("Waiting for background threads to join...") + # Join threads to ensure they exit cleanly before the main process + if self.QProf and self.QProf.is_alive(): + self.QProf.Close() + self.QProf.join(timeout=2.0) + + print("Exiting GTK main loop.") + Gtk.main_quit(*args) + return GLib.SOURCE_REMOVE # Stop the timer + + def close_dialog(self, *args): + if self.dialogWindow: + self.dialogWindow.hide() + return GLib.SOURCE_REMOVE + + def show_message(self): + if self.dialogWindow: + self.dialogWindow.set_transient_for(self.MainWindow) + self.dialogWindow.show_all() + return GLib.SOURCE_REMOVE + + def on_mainWindow_show(self, *args): + if not self.MainWindowShown: + self.MainWindowShown = True + #GLib.idle_add(self.show_message) diff --git a/ros2_ws/hand_controller/hand_controller/vai/qprofile.py b/ros2_ws/hand_controller/hand_controller/vai/qprofile.py new file mode 100644 index 0000000..077db69 --- /dev/null +++ b/ros2_ws/hand_controller/hand_controller/vai/qprofile.py @@ -0,0 +1,116 @@ +import re +import subprocess +import threading +import time + +from hand_controller.vai.common import HW_SAMPLING_PERIOD_ms + +# NOTE: Its expected that you have QProf installed on your device with the necessary exports/pathing enabled as well +# refer to QC documentation as necessary + + +class QProfProcess(threading.Thread): + """Run the Qualcomm profiler and extract metrics of interest""" + + def __init__(self): + self.enabled = True + self.CPU = 0 + self.GPU = 0 + self.MEM = 0 + self.DSP = 0 + self.p = None + threading.Thread.__init__(self) + + def run(self): + """Run a qprof subprocess until the thread is disabled via Close().""" + + ansi_escape_8bit = re.compile( + rb"(?:\x1B[@-Z\\-_]|[\x80-\x9A\x9C-\x9F]|(?:\x1B\[|\x9B)[0-?]*[ -/]*[@-~])" + ) + while self.enabled: + try: + self.p = subprocess.Popen( + f"qprof \ + --profile \ + --profile-type async \ + --result-format CSV \ + --capabilities-list profiler:apps-proc-cpu-metrics profiler:proc-gpu-specific-metrics profiler:apps-proc-mem-metrics profiler:cdsp-dsp-metrics \ + --profile-time 10 \ + --sampling-rate {HW_SAMPLING_PERIOD_ms} \ + --streaming-rate {HW_SAMPLING_PERIOD_ms} \ + --live \ + --metric-id-list 4648 4616 4865 4098".split(), + shell=False, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) + except FileNotFoundError: + print("Error: 'qprof' command not found. Is it installed and in your PATH?") + self.enabled = False + continue + except Exception as e: + print(f"Error starting qprof: {e}") + self.enabled = False + continue + + while self.enabled and self.p and self.p.poll() is None: + line = self.p.stdout.readline() + line = line.decode("utf-8", "ignore").encode("ascii", "ignore") + line = ansi_escape_8bit.sub(b"", line) + + if not line: + break + # the real code does filtering here + try: + if line.find(b"CPU Total Load:") > -1: + result = re.search(b"CPU Total Load:(.*)%", line) + if result is not None: + self.CPU = float(result.group(1)) + elif line.find(b"GPU Utilization:") > -1: + result = re.search(b"GPU Utilization:(.*)%", line) + if result is not None: + self.GPU = float(result.group(1)) + elif line.find(b"Memory Usage %:") > -1: + result = re.search(b"Memory Usage %:(.*)%", line) + if result is not None: + self.MEM = float(result.group(1)) + elif line.find(b"QDSP6 Utilization:") > -1: + result = re.search(b"QDSP6 Utilization:(.*) Percentage", line) + if result is not None: + self.DSP = float(result.group(1)) + except: + pass + + # cleanup output files + subprocess.call( + "/bin/rm -rf /var/QualcommProfiler/profilingresults/*", + shell=True, + ) + subprocess.call( + "/bin/rm -rf /data/shared/QualcommProfiler/profilingresults/*", + shell=True, + ) + time.sleep(HW_SAMPLING_PERIOD_ms/1000) + + def Close(self): + self.enabled = False + if hasattr(self, 'p') and self.p and self.p.poll() is None: + print("Terminating QProf subprocess...") + try: + self.p.terminate() + self.p.wait(timeout=1) + except (ProcessLookupError, subprocess.TimeoutExpired, AttributeError): + pass # Already gone or couldn't be killed. + + + def get_cpu_usage_pct(self): + return round(self.CPU, 2) + + def get_gpu_usage_pct(self): + return round(self.GPU, 2) + + def get_memory_usage_pct(self): + return round(self.MEM, 2) + + def get_dsp_usage_pct(self): + return round(self.DSP, 2) diff --git a/ros2_ws/hand_controller/hand_controller/vai/temp_profile.py b/ros2_ws/hand_controller/hand_controller/vai/temp_profile.py new file mode 100644 index 0000000..d4326bb --- /dev/null +++ b/ros2_ws/hand_controller/hand_controller/vai/temp_profile.py @@ -0,0 +1,42 @@ +import re +import glob +import os + +def get_cpu_gpu_mem_temps(): + thermal_zones = {} + gpu_temp = 35 + mem_temp = 35 + max_temp = 35 + for zone_path in glob.glob("/sys/class/thermal/thermal_zone*"): + zone_id = os.path.basename(zone_path) + try: + with open(os.path.join(zone_path, "type"), "r") as f_type: + zone_type = f_type.read().strip() + with open(os.path.join(zone_path, "temp"), "r") as f_temp: + try: + # Read the temperature value + f_tempValue = f_temp.read() + except: + f_tempValue = None + + if f_tempValue: + # Convert temperature from millidegrees Celsius to degrees Celsius + temp_millicelsius = int(f_tempValue.strip()) + temp_celsius = temp_millicelsius / 1000.0 + thermal_zones[zone_id] = {"type": zone_type, "temperature": temp_celsius} + + if re.match(r'cpu\d+-thermal', zone_type): + max_temp = max(max_temp, temp_celsius) + elif zone_type == 'ddr-thermal': + mem_temp = temp_celsius + elif zone_type == 'video-thermal': + gpu_temp = temp_celsius + + except FileNotFoundError: + print(f"Warning: Could not find 'type' or 'temp' file for {zone_id}") + except ValueError: + print(f"Warning: Could not parse temperature for {zone_id}") + except Exception as e: + #print(f"An error occurred with {zone_id}: {e}") + pass + return max_temp, gpu_temp, mem_temp diff --git a/ros2_ws/hand_controller/launch/demo11_mogiros_car_part1_ei1dials.launch.py b/ros2_ws/hand_controller/launch/demo11_mogiros_car_part1_ei1dials.launch.py index b709c00..67a1a3d 100644 --- a/ros2_ws/hand_controller/launch/demo11_mogiros_car_part1_ei1dials.launch.py +++ b/ros2_ws/hand_controller/launch/demo11_mogiros_car_part1_ei1dials.launch.py @@ -31,6 +31,11 @@ def generate_launch_description(): default_value="False", description="Enable Flask Server." ), + DeclareLaunchArgument( + "flask_port", + default_value="5001", + description="Port for Flask server." + ), DeclareLaunchArgument( "x_t", default_value="0.0", @@ -82,6 +87,7 @@ def generate_launch_description(): "model": LaunchConfiguration("model"), "verbose": LaunchConfiguration("verbose"), "use_imshow": LaunchConfiguration("use_imshow"), + "flask_port": LaunchConfiguration("flask_port"), # Add any other parameters you need }], condition=IfCondition(PythonExpression(['"', LaunchConfiguration('use_flask'), '" == "True"'])) diff --git a/ros2_ws/hand_controller/resources_high/1. QCS6490 Dev Kit.png b/ros2_ws/hand_controller/resources_high/1. QCS6490 Dev Kit.png new file mode 100644 index 0000000..477aaef Binary files /dev/null and b/ros2_ws/hand_controller/resources_high/1. QCS6490 Dev Kit.png differ diff --git a/ros2_ws/hand_controller/resources_high/2. Kit Includes.png b/ros2_ws/hand_controller/resources_high/2. Kit Includes.png new file mode 100644 index 0000000..bb46881 Binary files /dev/null and b/ros2_ws/hand_controller/resources_high/2. Kit Includes.png differ diff --git a/ros2_ws/hand_controller/resources_high/3. Demo HW.png b/ros2_ws/hand_controller/resources_high/3. Demo HW.png new file mode 100644 index 0000000..11f2a06 Binary files /dev/null and b/ros2_ws/hand_controller/resources_high/3. Demo HW.png differ diff --git a/ros2_ws/hand_controller/resources_high/4. Demo UI.png b/ros2_ws/hand_controller/resources_high/4. Demo UI.png new file mode 100644 index 0000000..4c79b89 Binary files /dev/null and b/ros2_ws/hand_controller/resources_high/4. Demo UI.png differ diff --git a/ros2_ws/hand_controller/resources_high/5. Demo SW.png b/ros2_ws/hand_controller/resources_high/5. Demo SW.png new file mode 100644 index 0000000..b6e1eed Binary files /dev/null and b/ros2_ws/hand_controller/resources_high/5. Demo SW.png differ diff --git a/ros2_ws/hand_controller/resources_high/6. Demo AI.png b/ros2_ws/hand_controller/resources_high/6. Demo AI.png new file mode 100644 index 0000000..a712344 Binary files /dev/null and b/ros2_ws/hand_controller/resources_high/6. Demo AI.png differ diff --git a/ros2_ws/hand_controller/resources_high/7. IQ9 Kit.png b/ros2_ws/hand_controller/resources_high/7. IQ9 Kit.png new file mode 100644 index 0000000..c4db0cf Binary files /dev/null and b/ros2_ws/hand_controller/resources_high/7. IQ9 Kit.png differ diff --git a/ros2_ws/hand_controller/resources_high/8. IQ9 SBC.png b/ros2_ws/hand_controller/resources_high/8. IQ9 SBC.png new file mode 100644 index 0000000..0f5ae63 Binary files /dev/null and b/ros2_ws/hand_controller/resources_high/8. IQ9 SBC.png differ diff --git a/ros2_ws/hand_controller/resources_high/9. IQ9 AI Box.png b/ros2_ws/hand_controller/resources_high/9. IQ9 AI Box.png new file mode 100644 index 0000000..641b45c Binary files /dev/null and b/ros2_ws/hand_controller/resources_high/9. IQ9 AI Box.png differ diff --git a/ros2_ws/hand_controller/resources_high/GSTLauncher.glade b/ros2_ws/hand_controller/resources_high/GSTLauncher.glade new file mode 100644 index 0000000..749a484 --- /dev/null +++ b/ros2_ws/hand_controller/resources_high/GSTLauncher.glade @@ -0,0 +1,1323 @@ + + + + + + True + False + close.png + + + True + False + close.png + + + False + popup + False + center-always + True + True + dialog + center + + + + + + False + vertical + 2 + + + False + end + + + True + True + bottom + False + + + True + False + 1. QCS6490 Dev Kit.png + + + + + True + False + 1. QCS6490 Dev Kit + + + + True + False + + + + + True + False + 2. Kit Includes.png + + + 1 + + + + + True + False + 2. Kit Includes + + + + 1 + True + False + + + + + True + False + 3. Demo HW.png + + + 2 + + + + + True + False + 3. Demo HW + + + + 2 + True + False + + + + + True + False + 4. Demo UI.png + + + 3 + + + + + True + False + 4. Demo UI + + + + 3 + True + False + + + + + True + False + 5. Demo SW.png + + + 4 + + + + + True + False + 5. Demo SW + + + + 4 + True + False + + + + + True + False + 6. Demo AI.png + + + 5 + + + + + True + False + 6. Demo AI + + + + 5 + True + False + + + + + True + False + 7. IQ9 Kit.png + + + 6 + + + + + True + False + 7. IQ9 Kit + + + + 6 + True + False + + + + + True + False + 8. IQ9 SBC.png + + + 7 + + + + + True + False + 8. IQ9 SBC + + + + 7 + True + False + + + + + True + False + 9. IQ9 AI Box.png + + + 8 + + + + + True + False + 9. IQ9 AI Box + + + + 8 + True + False + + + + + + False + False + 0 + + + + + False + False + 0 + + + + + Close + 100 + True + True + True + end + start + Close1 + True + + + + + False + True + 0 + + + + + + + True + False + info.png + + + True + False + exit.png + + + False + + + + + + + + True + False + True + True + + + True + True + False + start + 5 + + + True + False + center + 15 + VisionAI-Kit_6490_100.png + + + False + True + 0 + + + + + 1500 + True + False + center + center + 20 + 3 + top + + + Info + 150 + True + True + True + 3 + 3 + Info + True + + + + + False + True + 0 + + + + + True + False + True + + + False + True + 1 + + + + + demo_title + True + False + Hand-Controlled Robot Car + + + + True + True + 2 + + + + + True + False + True + + + False + True + 3 + + + + + Exit + 150 + True + True + True + 3 + 3 + Quit + True + + + + + False + True + end + 4 + + + + + + False + True + 2 + + + + + True + False + start + center + 15 + TRIA-Color-RGB-100.png + + + False + False + end + 1 + + + + + 0 + 0 + + + + + True + False + True + True + + + True + False + 35 + True + True + + + AspectFrame1 + True + False + True + True + 0 + none + 0 + 1.3300000429153442 + False + + + True + False + True + True + True + + + rightHandImage + True + False + True + True + updown.png + + + 1 + 1 + + + + + leftHandImage + True + False + True + True + leftright.png + + + 0 + 1 + + + + + leftHandStatus + True + False + 50 + False + Status: Inactive + + + + 0 + 2 + + + + + rightHandStatus + True + False + 50 + False + Status: Inactive + + + + 1 + 2 + + + + + leftHandTurn + True + False + 50 + False + Left hand: Left/Right + + + + 0 + 0 + + + + + rightHandMovement + True + False + 50 + False + Right hand: Forward/Reverse + + + + 1 + 0 + + + + + + + 0 + 0 + + + + + AspectFrame2 + True + False + True + True + 0 + none + 0 + 1.3300000429153442 + False + + + videosink0 + True + False + True + True + gtk-missing-image + + + + + 1 + 0 + + + + + + + 0 + 1 + 2 + + + + + 568 + True + False + end + True + immediate + + + GraphDrawAreaBottom + True + True + False + True + True + + + 0 + 1 + + + + + 0 + True + False + start + 2 + vertical + 2 + 8 + True + + + True + False + start + center + CPU use + + + + 1 + 1 + + + + + True + False + start + center + True + 0 + 7 + 7 + + + + 2 + 1 + + + + + True + False + start + center + DDR use + + + + 1 + 2 + + + + + True + False + start + center + GPU use + + + + 1 + 3 + + + + + True + False + start + center + NPU use + + + + 1 + 0 + + + + + True + False + start + center + True + 0 + 7 + 7 + + + + 2 + 2 + + + + + True + False + start + center + True + 0 + 7 + True + 7 + + + + 2 + 3 + + + + + True + False + start + center + True + 0 + 7 + True + 7 + + + + 2 + 0 + + + + + True + False + legend_pink.png + + + 0 + 1 + + + + + True + False + legend_blue.png + + + 0 + 2 + + + + + True + False + legend_yellow.png + + + 0 + 3 + + + + + True + False + legend_green.png + + + 0 + 0 + + + + + True + False + start + center + 10 + % + + + + 3 + 0 + + + + + True + False + start + center + 10 + % + + + + 3 + 1 + + + + + True + False + start + center + 10 + % + + + + 3 + 2 + + + + + True + False + start + center + 10 + % + + + + 3 + 3 + + + + + 3 + 1 + + + + + 0 + True + False + start + 2 + 25 + vertical + 2 + 8 + True + + + True + False + center + center + legend_pink.png + + + 0 + 0 + + + + + True + False + legend_blue.png + + + 0 + 1 + + + + + True + False + legend_yellow.png + + + 0 + 2 + + + + + True + False + start + center + CPU + + + + 1 + 0 + + + + + True + False + start + center + True + 0 + 7 + 7 + + + + 2 + 0 + + + + + True + False + start + center + DDR + + + + 1 + 1 + + + + + True + False + start + center + True + 0 + 7 + 7 + + + + 2 + 1 + + + + + True + False + start + center + GPU + + + + 1 + 2 + + + + + True + False + start + center + True + 0 + 7 + 7 + + + + 2 + 2 + + + + + True + False + start + center + °C + + + + 3 + 0 + + + + + True + False + start + center + °C + + + + 3 + 1 + + + + + True + False + start + center + °C + + + + 3 + 2 + + + + + 1 + 1 + + + + + True + False + center + center + System Thermals + + + + 0 + 0 + + + + + True + False + center + center + System Utilization + + + + 2 + 0 + + + + + GraphDrawAreaTop + True + True + False + True + True + + + 2 + 1 + + + + + 25 + True + False + end + Built with GTK, GStreamer, and Python to effectively utilize VPU, NPU, and GPU acceleration + + + + 0 + 2 + 4 + + + + + + + + + + + 0 + 2 + + + + + + + + dialogWindow + 480 + 240 + False + popup + False + center-always + True + True + dialog + False + center + + + + + + False + vertical + 2 + + + False + end + + + + + + + + + False + False + 0 + + + + + dialogMessage + True + False + center + center + 10 + 10 + 50 + 50 + True + True + Establishing ROS2 connection, please wait... + + + + + + False + True + 1 + + + + + + + + + + + + + None + + + Camera + + + Pose detection + + + Segmentation + + + Classification + + + Object detection + + + Depth Segmentation + + + + diff --git a/ros2_ws/hand_controller/resources_high/TRIA-Color-RGB-100.png b/ros2_ws/hand_controller/resources_high/TRIA-Color-RGB-100.png new file mode 100644 index 0000000..fb54fa7 Binary files /dev/null and b/ros2_ws/hand_controller/resources_high/TRIA-Color-RGB-100.png differ diff --git a/ros2_ws/hand_controller/resources_high/VisionAI-Kit_6490_100.png b/ros2_ws/hand_controller/resources_high/VisionAI-Kit_6490_100.png new file mode 100644 index 0000000..c4ae44b Binary files /dev/null and b/ros2_ws/hand_controller/resources_high/VisionAI-Kit_6490_100.png differ diff --git a/ros2_ws/hand_controller/resources_high/app.css b/ros2_ws/hand_controller/resources_high/app.css new file mode 100644 index 0000000..92ce42a --- /dev/null +++ b/ros2_ws/hand_controller/resources_high/app.css @@ -0,0 +1,261 @@ + .body { + -ms-overflow-style: none; + background-color: #171717; + font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; + font-weight: normal; + font-size: larger; + color: #ffffff; + margin: 0px; +} + +.title { + font-size:3rem; + font-weight: bold; + color: #ffffff; + } + +.label { + font-size: 3.5rem; + font-weight: bold; + color: #ffffff; + } + +.status { + font-size: 4rem; + font-weight: bold; + color: #ffffff; + background-color: rgb(0, 31, 99); + } + +.status_active { + font-size: 4rem; + font-weight: bold; + color: rgb(255, 0, 163); + background-color: rgb(0, 31, 99); +} + + .demo_title { + font-size:5rem; + font-weight: bold; + color: #ffffff; + } + +.tab { + font-size: 3rem; + font-weight:normal; + color: rgb(0, 0, 0); + border-image: none; + border-radius: 2px; + border-style: none; + box-shadow: 1px 1px 3px rgb(57, 57, 57); + padding: 5px; +} + +.nbContent > header.top { + background: rgb(4, 18, 83); + border-bottom: none; +} + +/* +.nbContent > header.top > tabs > tab { + background: rgb(17, 136, 130); + border-image: none; + border: 10px solid rgba(0,0,0,0.4); + border-radius: 8px 8px 0px 0px; + border-bottom: none; + box-shadow: 3px 3px 10px #707070; + padding: 10px; + +} +*/ +.nbContent > header > tabs > tab:hover { + background: rgb(255, 255, 255); + box-shadow: inset 0 0 15px rgb(255, 255, 255); +} + +.nbContent > header > tabs > tab:checked { + border: none; + box-shadow: 1px 1px 3px rgb(176, 203, 235); + padding: 5px; + background: rgb(176, 222, 248); +} + +.labelm { + font-size: 3.5rem; + font-weight: bold; + color: rgb(255, 0, 163); + } + +.label_m { + font-size: 3.8rem; + font-weight: bold; + color: #ffffff; + } + +.label_xl_m { + font-size: 3rem; + font-weight: bold; + color: rgb(255, 0, 163); + } + +.label_xl { + font-size: 3rem; + font-weight: bold; + color: #ffffff; + } + +.label_l { + font-size: 4rem; + font-weight: bold; + color: rgb(255, 255, 255); + } + +.label_l_m { + font-family: Consolas, monaco, monospace; + font-size: 4rem; + font-weight: bold; + color: rgb(255, 0, 163); +} + +.label_l_t { + font-size: 3.2rem; + font-weight: bold; + color: rgb(255, 255, 255); +} + +.dropdown { + text-decoration: none; + font-size: 3rem; + color: rgb(0,0,0); + text-shadow: 0px 1px 1px rgba(255,255,255,0.3), + 0px 0px 0px rgba(0,0,0,0.7); + border-radius: 2px; + border-style: none; + box-shadow: 3px 3px 10px #888888; + padding: 0px; + background: rgb(255, 255, 255); + transition: color 0.1s, background 0.1s; + border: 3px solid rgb(250, 250, 250); +} + +.dropdown menu { + text-decoration: none; + font-size: 3rem; + color: rgba(0,30,95,1.0); + text-shadow: 0px 1px 1px rgba(0,0,0,0.3), + 0px 0px 0px rgba(255,255,255,1); + border-radius: 2px; + border-style: none; + box-shadow: 3px 3px 10px #888888; + padding: 0px; + background: rgb(208, 210, 212); + transition: color 0.1s, background 0.1s; + border: 3px solid rgb(250, 250, 250); +} + +.dropdown button:hover { + text-decoration: none; + color: rgba(248,248,248,0.9); + text-shadow: 0px 1px 1px rgba(0,0,0,0.3), + 0px 0px 0px rgb(255,255,255); + background: rgb(255, 0, 163); + box-shadow: 3px 3px 10px rgb(255, 0, 163); +} + +.menu_button { + text-decoration: none; + font-size: 3.5rem; + color: rgb(0,0,0); + text-shadow: 0px 1px 1px rgba(255,255,255,0.3), + 0px 0px 0px rgba(0,0,0,0.7); + border-radius: 2px; + border-style: none; + box-shadow: 3px 3px 10px #888888; + padding: 3px; + background: rgb(255, 255, 255); + transition: color 0.1s, background 0.1s; + border: 3px solid rgb(250, 250, 250); +} + +/*.menu_button:focus { + background: rgb(255, 0, 163); + box-shadow: 3px 3px 10px rgb(255, 0, 163); + +}*/ + +.menu_button:hover { + text-decoration: none; + color: rgba(248,248,248,0.9); + text-shadow: 0px 1px 1px rgba(0,0,0,0.3), + 0px 0px 0px rgba(255,255,255,1); + background: rgb(255, 0, 163); + box-shadow: 3px 3px 10px rgb(255, 0, 163); +} + + +.box { + box-shadow: 3px 3px 10px #888888; + padding: 3px; +} + + + + + +/* Green button */ +#scan_button, #ap_password_page button{ + background: rgb(201, 210, 0); + border: 3px solid #668a08; +} + +#scan_button:hover, #ap_password_page button:hover { + background: rgb(115, 152, 51); +} + + +/* Red button */ +#clear_confirm button.warning, #clientJoin_banner button.warning { + background: #d72c2c; + border: 3px solid #9e1212; +} +#clear_confirm button.warning:hover, #clientJoin_banner button.warning:hover { + background: #b31414; +} + +.menu_button:focus, #ap_password_page button:focus +{ + outline: 0; +} + + +.bar { + margin: 5px; + -webkit-transition: all 0.3s ease-in-out; + transition: all 0.3s ease-in-out; + background-color: #ffffff; +} + +.nav-item a{ + font-size: 1.3rem; + font-weight: 400; + color: #ffffff; + background: #171717; + padding: 1.2rem 1.2rem; +} + +.nav-item a:hover{ + background: #41c363; +} +.nav-item a.current { + background: black; + outline: solid 1px white; + outline-offset: -1px; +} + +.footer +{ + padding: 1px 10px 0px 10px; + background: #41c363; + font-size: 1.5em; + font-weight: bold; +} diff --git a/ros2_ws/hand_controller/resources_high/bg_tria.png b/ros2_ws/hand_controller/resources_high/bg_tria.png new file mode 100644 index 0000000..59ab6d5 Binary files /dev/null and b/ros2_ws/hand_controller/resources_high/bg_tria.png differ diff --git a/ros2_ws/hand_controller/resources_high/blank.png b/ros2_ws/hand_controller/resources_high/blank.png new file mode 100644 index 0000000..1bd1be5 Binary files /dev/null and b/ros2_ws/hand_controller/resources_high/blank.png differ diff --git a/ros2_ws/hand_controller/resources_high/close.png b/ros2_ws/hand_controller/resources_high/close.png new file mode 100644 index 0000000..e8499d5 Binary files /dev/null and b/ros2_ws/hand_controller/resources_high/close.png differ diff --git a/ros2_ws/hand_controller/resources_high/down.png b/ros2_ws/hand_controller/resources_high/down.png new file mode 100644 index 0000000..c7d94e3 Binary files /dev/null and b/ros2_ws/hand_controller/resources_high/down.png differ diff --git a/ros2_ws/hand_controller/resources_high/exit.png b/ros2_ws/hand_controller/resources_high/exit.png new file mode 100644 index 0000000..3e47a81 Binary files /dev/null and b/ros2_ws/hand_controller/resources_high/exit.png differ diff --git a/ros2_ws/hand_controller/resources_high/info.png b/ros2_ws/hand_controller/resources_high/info.png new file mode 100644 index 0000000..6c5fd29 Binary files /dev/null and b/ros2_ws/hand_controller/resources_high/info.png differ diff --git a/ros2_ws/hand_controller/resources_high/left.png b/ros2_ws/hand_controller/resources_high/left.png new file mode 100644 index 0000000..c8c0a66 Binary files /dev/null and b/ros2_ws/hand_controller/resources_high/left.png differ diff --git a/ros2_ws/hand_controller/resources_high/leftright.png b/ros2_ws/hand_controller/resources_high/leftright.png new file mode 100644 index 0000000..560bb0d Binary files /dev/null and b/ros2_ws/hand_controller/resources_high/leftright.png differ diff --git a/ros2_ws/hand_controller/resources_high/legend_blue.png b/ros2_ws/hand_controller/resources_high/legend_blue.png new file mode 100644 index 0000000..30ff117 Binary files /dev/null and b/ros2_ws/hand_controller/resources_high/legend_blue.png differ diff --git a/ros2_ws/hand_controller/resources_high/legend_green.png b/ros2_ws/hand_controller/resources_high/legend_green.png new file mode 100644 index 0000000..ee4235c Binary files /dev/null and b/ros2_ws/hand_controller/resources_high/legend_green.png differ diff --git a/ros2_ws/hand_controller/resources_high/legend_pink.png b/ros2_ws/hand_controller/resources_high/legend_pink.png new file mode 100644 index 0000000..34882b1 Binary files /dev/null and b/ros2_ws/hand_controller/resources_high/legend_pink.png differ diff --git a/ros2_ws/hand_controller/resources_high/legend_yellow.png b/ros2_ws/hand_controller/resources_high/legend_yellow.png new file mode 100644 index 0000000..702508c Binary files /dev/null and b/ros2_ws/hand_controller/resources_high/legend_yellow.png differ diff --git a/ros2_ws/hand_controller/resources_high/right.png b/ros2_ws/hand_controller/resources_high/right.png new file mode 100644 index 0000000..652f710 Binary files /dev/null and b/ros2_ws/hand_controller/resources_high/right.png differ diff --git a/ros2_ws/hand_controller/resources_high/sample_weston.ini b/ros2_ws/hand_controller/resources_high/sample_weston.ini new file mode 100644 index 0000000..79ce3f0 --- /dev/null +++ b/ros2_ws/hand_controller/resources_high/sample_weston.ini @@ -0,0 +1,22 @@ +# configuration file for Weston +[core] +idle-time=0 +backend=sdm-backend.so +repaint-window=10 + +[output] +name=DSI-1 +mode=on + +[output] +name=DP-1 +mode=on + +[shell] +clock-format=seconds +background-image=/opt/bg_tria.png +background-type=scale-crop + +[launcher] +icon=/opt/tria_demo_launcher_icon.png +path=VISIONAI_PATH_OVERRIDE=/opt/QCS6490-Vision-AI-Demo/visionai.py /opt/QCS6490-Vision-AI-Demo/launch_visionai_with_env.sh diff --git a/ros2_ws/hand_controller/resources_high/tria_demo_launcher_icon.png b/ros2_ws/hand_controller/resources_high/tria_demo_launcher_icon.png new file mode 100644 index 0000000..ff6fc34 Binary files /dev/null and b/ros2_ws/hand_controller/resources_high/tria_demo_launcher_icon.png differ diff --git a/ros2_ws/hand_controller/resources_high/up.png b/ros2_ws/hand_controller/resources_high/up.png new file mode 100644 index 0000000..c500cfd Binary files /dev/null and b/ros2_ws/hand_controller/resources_high/up.png differ diff --git a/ros2_ws/hand_controller/resources_high/updown.png b/ros2_ws/hand_controller/resources_high/updown.png new file mode 100644 index 0000000..6d57a01 Binary files /dev/null and b/ros2_ws/hand_controller/resources_high/updown.png differ diff --git a/ros2_ws/hand_controller/resources_low/1. QCS6490 Dev Kit.png b/ros2_ws/hand_controller/resources_low/1. QCS6490 Dev Kit.png new file mode 100644 index 0000000..24bb3ce Binary files /dev/null and b/ros2_ws/hand_controller/resources_low/1. QCS6490 Dev Kit.png differ diff --git a/ros2_ws/hand_controller/resources_low/2. Kit Includes.png b/ros2_ws/hand_controller/resources_low/2. Kit Includes.png new file mode 100644 index 0000000..871a641 Binary files /dev/null and b/ros2_ws/hand_controller/resources_low/2. Kit Includes.png differ diff --git a/ros2_ws/hand_controller/resources_low/3. Demo HW.png b/ros2_ws/hand_controller/resources_low/3. Demo HW.png new file mode 100644 index 0000000..d64a6be Binary files /dev/null and b/ros2_ws/hand_controller/resources_low/3. Demo HW.png differ diff --git a/ros2_ws/hand_controller/resources_low/4. Demo UI.png b/ros2_ws/hand_controller/resources_low/4. Demo UI.png new file mode 100644 index 0000000..f4e7713 Binary files /dev/null and b/ros2_ws/hand_controller/resources_low/4. Demo UI.png differ diff --git a/ros2_ws/hand_controller/resources_low/5. Demo SW.png b/ros2_ws/hand_controller/resources_low/5. Demo SW.png new file mode 100644 index 0000000..da023fc Binary files /dev/null and b/ros2_ws/hand_controller/resources_low/5. Demo SW.png differ diff --git a/ros2_ws/hand_controller/resources_low/6. Demo AI.png b/ros2_ws/hand_controller/resources_low/6. Demo AI.png new file mode 100644 index 0000000..e38cd19 Binary files /dev/null and b/ros2_ws/hand_controller/resources_low/6. Demo AI.png differ diff --git a/ros2_ws/hand_controller/resources_low/7. IQ9 Kit.png b/ros2_ws/hand_controller/resources_low/7. IQ9 Kit.png new file mode 100644 index 0000000..da5bdf6 Binary files /dev/null and b/ros2_ws/hand_controller/resources_low/7. IQ9 Kit.png differ diff --git a/ros2_ws/hand_controller/resources_low/8. IQ9 SBC.png b/ros2_ws/hand_controller/resources_low/8. IQ9 SBC.png new file mode 100644 index 0000000..c920fdc Binary files /dev/null and b/ros2_ws/hand_controller/resources_low/8. IQ9 SBC.png differ diff --git a/ros2_ws/hand_controller/resources_low/9. IQ9 AI Box.png b/ros2_ws/hand_controller/resources_low/9. IQ9 AI Box.png new file mode 100644 index 0000000..c1485ea Binary files /dev/null and b/ros2_ws/hand_controller/resources_low/9. IQ9 AI Box.png differ diff --git a/ros2_ws/hand_controller/resources_low/GSTLauncher.glade b/ros2_ws/hand_controller/resources_low/GSTLauncher.glade new file mode 100644 index 0000000..3a92a62 --- /dev/null +++ b/ros2_ws/hand_controller/resources_low/GSTLauncher.glade @@ -0,0 +1,1324 @@ + + + + + + True + False + close.png + + + True + False + close.png + + + False + popup + False + center-always + True + dialog + center + + + + + + False + vertical + 2 + + + False + vertical + end + + + True + True + bottom + False + + + True + False + 1. QCS6490 Dev Kit.png + + + + + True + False + 1. QCS6490 Dev Kit + + + + True + False + + + + + True + False + 2. Kit Includes.png + + + 1 + + + + + True + False + 2. Kit Includes + + + + 1 + True + False + + + + + True + False + 3. Demo HW.png + + + 2 + + + + + True + False + 3. Demo HW + + + + 2 + True + False + + + + + True + False + 4. Demo UI.png + + + 3 + + + + + True + False + 4. Demo UI + + + + 3 + True + False + + + + + True + False + 5. Demo SW.png + + + 4 + + + + + True + False + 5. Demo SW + + + + 4 + True + False + + + + + True + False + 6. Demo AI.png + + + 5 + + + + + True + False + 6. Demo AI + + + + 5 + True + False + + + + + True + False + 7. IQ9 Kit.png + + + 6 + + + + + True + False + 7. IQ9 Kit + + + + 6 + True + False + + + + + True + False + 8. IQ9 SBC.png + + + 7 + + + + + True + False + 8. IQ9 SBC + + + + 7 + True + False + + + + + True + False + 9. IQ9 AI Box.png + + + 8 + + + + + True + False + 9. IQ9 AI Box + + + + 8 + True + False + + + + + + False + False + 1 + + + + + False + False + 0 + + + + + Close + 100 + True + True + True + end + start + Close1 + True + + + + + False + False + end + 1 + + + + + + + True + False + info.png + + + True + False + exit.png + + + False + + + + + + + + True + False + True + True + + + True + True + False + start + 5 + + + True + False + center + 15 + VisionAI-Kit_6490_100.png + + + False + True + 0 + + + + + 800 + True + False + center + center + 5 + 3 + top + + + Info + 50 + True + True + True + 3 + 3 + Info + True + + + + + False + True + 0 + + + + + True + False + True + + + False + True + 1 + + + + + demo_title + True + False + Hand-Controlled Robot Car + + + + True + True + 2 + + + + + True + False + True + + + False + True + 3 + + + + + Exit + 50 + True + True + True + 3 + 3 + Quit + True + + + + + False + True + end + 4 + + + + + + False + True + 2 + + + + + True + False + start + center + 15 + TRIA-Color-RGB-100.png + + + False + False + end + 1 + + + + + 0 + 0 + + + + + True + False + True + True + + + True + False + 10 + True + True + + + AspectFrame1 + True + False + True + True + 0 + none + 0 + 1.3300000429153442 + False + + + True + False + True + True + True + + + rightHandImage + True + False + True + True + updown.png + + + 1 + 1 + + + + + leftHandImage + True + False + True + True + leftright.png + + + 0 + 1 + + + + + leftHandStatus + True + False + 50 + False + Status: Inactive + + + + 0 + 2 + + + + + rightHandStatus + True + False + 50 + False + Status: Inactive + + + + 1 + 2 + + + + + leftHandTurn + True + False + 50 + False + Left hand: Left/Right + + + + 0 + 0 + + + + + rightHandMovement + True + False + 50 + False + Right hand: Forward/Reverse + + + + 1 + 0 + + + + + + + 0 + 0 + + + + + AspectFrame2 + True + False + True + True + 0 + none + 0 + 1.3300000429153442 + False + + + videosink0 + True + False + True + True + gtk-missing-image + + + + + 1 + 0 + + + + + + + 0 + 1 + 2 + + + + + 200 + True + False + end + True + immediate + + + GraphDrawAreaBottom + True + True + False + True + True + + + 0 + 1 + + + + + 0 + True + False + start + 2 + vertical + 2 + 8 + True + + + True + False + start + center + CPU use + + + + 1 + 1 + + + + + True + False + start + center + True + 0 + 7 + 7 + + + + 2 + 1 + + + + + True + False + start + center + DDR use + + + + 1 + 2 + + + + + True + False + start + center + GPU use + + + + 1 + 3 + + + + + True + False + start + center + NPU use + + + + 1 + 0 + + + + + True + False + start + center + True + 0 + 7 + 7 + + + + 2 + 2 + + + + + True + False + start + center + True + 0 + 7 + True + 7 + + + + 2 + 3 + + + + + True + False + start + center + True + 0 + 7 + True + 7 + + + + 2 + 0 + + + + + True + False + legend_pink.png + + + 0 + 1 + + + + + True + False + legend_blue.png + + + 0 + 2 + + + + + True + False + legend_yellow.png + + + 0 + 3 + + + + + True + False + legend_green.png + + + 0 + 0 + + + + + True + False + start + center + 5 + % + + + + 3 + 0 + + + + + True + False + start + center + 5 + % + + + + 3 + 1 + + + + + True + False + start + center + 5 + % + + + + 3 + 2 + + + + + True + False + start + center + 5 + % + + + + 3 + 3 + + + + + 3 + 1 + + + + + 0 + True + False + start + 2 + 25 + vertical + 2 + 8 + True + + + True + False + center + center + legend_pink.png + + + 0 + 0 + + + + + True + False + legend_blue.png + + + 0 + 1 + + + + + True + False + legend_yellow.png + + + 0 + 2 + + + + + True + False + start + center + CPU + + + + 1 + 0 + + + + + True + False + start + center + True + 0 + 7 + 7 + + + + 2 + 0 + + + + + True + False + start + center + DDR + + + + 1 + 1 + + + + + True + False + start + center + True + 0 + 7 + 7 + + + + 2 + 1 + + + + + True + False + start + center + GPU + + + + 1 + 2 + + + + + True + False + start + center + True + 0 + 7 + 7 + + + + 2 + 2 + + + + + True + False + start + center + °C + + + + 3 + 0 + + + + + True + False + start + center + °C + + + + 3 + 1 + + + + + True + False + start + center + °C + + + + 3 + 2 + + + + + 1 + 1 + + + + + True + False + center + center + System Thermals + + + + 0 + 0 + + + + + True + False + center + center + System Utilization + + + + 2 + 0 + + + + + GraphDrawAreaTop + True + True + False + True + True + + + 2 + 1 + + + + + 25 + True + False + end + Built with GTK, Edge Impulse FOMO model, ROS 2 and Python for the Vision AI-Kit 6490 + + + + 0 + 2 + 4 + + + + + + + + + + + 0 + 2 + + + + + + + + dialogWindow + 120 + 60 + False + popup + False + center-always + True + True + dialog + False + center + + + + + + False + vertical + 2 + + + False + end + + + + + + + + + False + False + 0 + + + + + dialogMessage + True + False + center + center + 10 + 10 + 50 + 50 + True + True + Establishing ROS2 connection, please wait... + + + + + + False + True + 1 + + + + + + + + + + + + + None + + + Camera + + + Pose detection + + + Segmentation + + + Classification + + + Object detection + + + Depth Segmentation + + + + diff --git a/ros2_ws/hand_controller/resources_low/TRIA-Color-RGB-100.png b/ros2_ws/hand_controller/resources_low/TRIA-Color-RGB-100.png new file mode 100644 index 0000000..3ce3a1a Binary files /dev/null and b/ros2_ws/hand_controller/resources_low/TRIA-Color-RGB-100.png differ diff --git a/ros2_ws/hand_controller/resources_low/VisionAI-Kit_6490_100.png b/ros2_ws/hand_controller/resources_low/VisionAI-Kit_6490_100.png new file mode 100644 index 0000000..f9bc8d0 Binary files /dev/null and b/ros2_ws/hand_controller/resources_low/VisionAI-Kit_6490_100.png differ diff --git a/ros2_ws/hand_controller/resources_low/app.css b/ros2_ws/hand_controller/resources_low/app.css new file mode 100644 index 0000000..d2f9fa5 --- /dev/null +++ b/ros2_ws/hand_controller/resources_low/app.css @@ -0,0 +1,261 @@ +.body { + -ms-overflow-style: none; + background-color: #171717; + font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; + font-weight: normal; + font-size: larger; + color: #ffffff; + margin: 0px; +} + +.title { + font-size:1.0rem; + font-weight: bold; + color: #ffffff; + } + +.label { + font-size: 1.5rem; + font-weight: bold; + color: #ffffff; + } + +.status { + font-size: 2.2rem; + font-weight: bold; + color: #ffffff; + background-color: rgb(0, 31, 99); + } + +.status_active { + font-size: 2.2rem; + font-weight: bold; + color: rgb(255, 0, 163); + background-color: rgb(0, 31, 99); +} + +.demo_title { + font-size:3rem; + font-weight: bold; + color: #ffffff; + } + +.tab { + font-size: 1rem; + font-weight:normal; + color: rgb(0, 0, 0); + border-image: none; + border-radius: 2px; + border-style: none; + box-shadow: 1px 1px 3px rgb(57, 57, 57); + padding: 5px; +} + +.nbContent > header.top { + background: rgb(4, 18, 83); + border-bottom: none; +} + +/* +.nbContent > header.top > tabs > tab { + background: rgb(17, 136, 130); + border-image: none; + border: 10px solid rgba(0,0,0,0.4); + border-radius: 8px 8px 0px 0px; + border-bottom: none; + box-shadow: 3px 3px 10px #707070; + padding: 10px; + +} +*/ +.nbContent > header > tabs > tab:hover { + background: rgb(255, 255, 255); + box-shadow: inset 0 0 15px rgb(255, 255, 255); +} + +.nbContent > header > tabs > tab:checked { + border: none; + box-shadow: 1px 1px 3px rgb(176, 203, 235); + padding: 5px; + background: rgb(176, 222, 248); +} + +.labelm { + font-size: 1.5rem; + font-weight: bold; + color: rgb(255, 0, 163); + } + +.label_m { + font-size: 1.5rem; + font-weight: bold; + color: #ffffff; + } + +.label_xl_m { + font-size: 1.0rem; + font-weight: bold; + color: rgb(255, 0, 163); + } + +.label_xl { + font-size: 1.0rem; + font-weight: bold; + color: #ffffff; + } + +.label_l { + font-size: 2.2rem; + font-weight: bold; + color: rgb(255, 255, 255); + } + +.label_l_m { + font-family: Consolas, monaco, monospace; + font-size: 2.2rem; + font-weight: bold; + color: rgb(255, 0, 163); +} + +.label_l_t { + font-size: 1.3rem; + font-weight: bold; + color: rgb(255, 255, 255); +} + +.dropdown { + text-decoration: none; + font-size: 1.2rem; + color: rgb(0,0,0); + text-shadow: 0px 1px 1px rgba(255,255,255,0.3), + 0px 0px 0px rgba(0,0,0,0.7); + border-radius: 2px; + border-style: none; + box-shadow: 3px 3px 10px #888888; + padding: 0px; + background: rgb(255, 255, 255); + transition: color 0.1s, background 0.1s; + border: 3px solid rgb(250, 250, 250); +} + +.dropdown menu { + text-decoration: none; + font-size: 1.2rem; + color: rgba(0,30,95,1.0); + text-shadow: 0px 1px 1px rgba(0,0,0,0.3), + 0px 0px 0px rgba(255,255,255,1); + border-radius: 2px; + border-style: none; + box-shadow: 3px 3px 10px #888888; + padding: 0px; + background: rgb(208, 210, 212); + transition: color 0.1s, background 0.1s; + border: 3px solid rgb(250, 250, 250); +} + +.dropdown button:hover { + text-decoration: none; + color: rgba(248,248,248,0.9); + text-shadow: 0px 1px 1px rgba(0,0,0,0.3), + 0px 0px 0px rgb(255,255,255); + background: rgb(255, 0, 163); + box-shadow: 3px 3px 10px rgb(255, 0, 163); +} + +.menu_button { + text-decoration: none; + font-size: 1.5rem; + color: rgb(0,0,0); + text-shadow: 0px 1px 1px rgba(255,255,255,0.3), + 0px 0px 0px rgba(0,0,0,0.7); + border-radius: 2px; + border-style: none; + box-shadow: 3px 3px 10px #888888; + padding: 3px; + background: rgb(255, 255, 255); + transition: color 0.1s, background 0.1s; + border: 3px solid rgb(250, 250, 250); +} + +/*.menu_button:focus { + background: rgb(255, 0, 163); + box-shadow: 3px 3px 10px rgb(255, 0, 163); + +}*/ + +.menu_button:hover { + text-decoration: none; + color: rgba(248,248,248,0.9); + text-shadow: 0px 1px 1px rgba(0,0,0,0.3), + 0px 0px 0px rgba(255,255,255,1); + background: rgb(255, 0, 163); + box-shadow: 3px 3px 10px rgb(255, 0, 163); +} + + +.box { + box-shadow: 3px 3px 10px #888888; + padding: 3px; +} + + + + + +/* Green button */ +#scan_button, #ap_password_page button{ + background: rgb(201, 210, 0); + border: 3px solid #668a08; +} + +#scan_button:hover, #ap_password_page button:hover { + background: rgb(115, 152, 51); +} + + +/* Red button */ +#clear_confirm button.warning, #clientJoin_banner button.warning { + background: #d72c2c; + border: 3px solid #9e1212; +} +#clear_confirm button.warning:hover, #clientJoin_banner button.warning:hover { + background: #b31414; +} + +.menu_button:focus, #ap_password_page button:focus +{ + outline: 0; +} + + +.bar { + margin: 5px; + -webkit-transition: all 0.3s ease-in-out; + transition: all 0.3s ease-in-out; + background-color: #ffffff; +} + +.nav-item a{ + font-size: 1.3rem; + font-weight: 400; + color: #ffffff; + background: #171717; + padding: 1.0rem 1.0rem; +} + +.nav-item a:hover{ + background: #41c363; +} +.nav-item a.current { + background: black; + outline: solid 1px white; + outline-offset: -1px; +} + +.footer +{ + padding: 1px 10px 0px 10px; + background: #41c363; + font-size: 1.5em; + font-weight: bold; +} diff --git a/ros2_ws/hand_controller/resources_low/bg_tria.png b/ros2_ws/hand_controller/resources_low/bg_tria.png new file mode 100644 index 0000000..59ab6d5 Binary files /dev/null and b/ros2_ws/hand_controller/resources_low/bg_tria.png differ diff --git a/ros2_ws/hand_controller/resources_low/blank.png b/ros2_ws/hand_controller/resources_low/blank.png new file mode 100644 index 0000000..5a0e167 Binary files /dev/null and b/ros2_ws/hand_controller/resources_low/blank.png differ diff --git a/ros2_ws/hand_controller/resources_low/close.png b/ros2_ws/hand_controller/resources_low/close.png new file mode 100644 index 0000000..e8499d5 Binary files /dev/null and b/ros2_ws/hand_controller/resources_low/close.png differ diff --git a/ros2_ws/hand_controller/resources_low/down.png b/ros2_ws/hand_controller/resources_low/down.png new file mode 100644 index 0000000..a96aef0 Binary files /dev/null and b/ros2_ws/hand_controller/resources_low/down.png differ diff --git a/ros2_ws/hand_controller/resources_low/exit.png b/ros2_ws/hand_controller/resources_low/exit.png new file mode 100644 index 0000000..3e47a81 Binary files /dev/null and b/ros2_ws/hand_controller/resources_low/exit.png differ diff --git a/ros2_ws/hand_controller/resources_low/info.png b/ros2_ws/hand_controller/resources_low/info.png new file mode 100644 index 0000000..6c5fd29 Binary files /dev/null and b/ros2_ws/hand_controller/resources_low/info.png differ diff --git a/ros2_ws/hand_controller/resources_low/left.png b/ros2_ws/hand_controller/resources_low/left.png new file mode 100644 index 0000000..e87349e Binary files /dev/null and b/ros2_ws/hand_controller/resources_low/left.png differ diff --git a/ros2_ws/hand_controller/resources_low/leftright.png b/ros2_ws/hand_controller/resources_low/leftright.png new file mode 100644 index 0000000..1311872 Binary files /dev/null and b/ros2_ws/hand_controller/resources_low/leftright.png differ diff --git a/ros2_ws/hand_controller/resources_low/legend_blue.png b/ros2_ws/hand_controller/resources_low/legend_blue.png new file mode 100644 index 0000000..30ff117 Binary files /dev/null and b/ros2_ws/hand_controller/resources_low/legend_blue.png differ diff --git a/ros2_ws/hand_controller/resources_low/legend_green.png b/ros2_ws/hand_controller/resources_low/legend_green.png new file mode 100644 index 0000000..a60eca8 Binary files /dev/null and b/ros2_ws/hand_controller/resources_low/legend_green.png differ diff --git a/ros2_ws/hand_controller/resources_low/legend_pink.png b/ros2_ws/hand_controller/resources_low/legend_pink.png new file mode 100644 index 0000000..34882b1 Binary files /dev/null and b/ros2_ws/hand_controller/resources_low/legend_pink.png differ diff --git a/ros2_ws/hand_controller/resources_low/legend_yellow.png b/ros2_ws/hand_controller/resources_low/legend_yellow.png new file mode 100644 index 0000000..702508c Binary files /dev/null and b/ros2_ws/hand_controller/resources_low/legend_yellow.png differ diff --git a/ros2_ws/hand_controller/resources_low/right.png b/ros2_ws/hand_controller/resources_low/right.png new file mode 100644 index 0000000..405c5e0 Binary files /dev/null and b/ros2_ws/hand_controller/resources_low/right.png differ diff --git a/ros2_ws/hand_controller/resources_low/sample_weston.ini b/ros2_ws/hand_controller/resources_low/sample_weston.ini new file mode 100644 index 0000000..79ce3f0 --- /dev/null +++ b/ros2_ws/hand_controller/resources_low/sample_weston.ini @@ -0,0 +1,22 @@ +# configuration file for Weston +[core] +idle-time=0 +backend=sdm-backend.so +repaint-window=10 + +[output] +name=DSI-1 +mode=on + +[output] +name=DP-1 +mode=on + +[shell] +clock-format=seconds +background-image=/opt/bg_tria.png +background-type=scale-crop + +[launcher] +icon=/opt/tria_demo_launcher_icon.png +path=VISIONAI_PATH_OVERRIDE=/opt/QCS6490-Vision-AI-Demo/visionai.py /opt/QCS6490-Vision-AI-Demo/launch_visionai_with_env.sh diff --git a/ros2_ws/hand_controller/resources_low/tria_demo_launcher_icon.png b/ros2_ws/hand_controller/resources_low/tria_demo_launcher_icon.png new file mode 100644 index 0000000..ff6fc34 Binary files /dev/null and b/ros2_ws/hand_controller/resources_low/tria_demo_launcher_icon.png differ diff --git a/ros2_ws/hand_controller/resources_low/up.png b/ros2_ws/hand_controller/resources_low/up.png new file mode 100644 index 0000000..5b5a278 Binary files /dev/null and b/ros2_ws/hand_controller/resources_low/up.png differ diff --git a/ros2_ws/hand_controller/resources_low/updown.png b/ros2_ws/hand_controller/resources_low/updown.png new file mode 100644 index 0000000..0ad4b8c Binary files /dev/null and b/ros2_ws/hand_controller/resources_low/updown.png differ diff --git a/ros2_ws/install/.colcon_install_layout b/ros2_ws/install/.colcon_install_layout new file mode 100644 index 0000000..3aad533 --- /dev/null +++ b/ros2_ws/install/.colcon_install_layout @@ -0,0 +1 @@ +isolated diff --git a/ros2_ws/install/COLCON_IGNORE b/ros2_ws/install/COLCON_IGNORE new file mode 100644 index 0000000..e69de29 diff --git a/ros2_ws/install/_local_setup_util_sh.py b/ros2_ws/install/_local_setup_util_sh.py new file mode 100644 index 0000000..f67eaa9 --- /dev/null +++ b/ros2_ws/install/_local_setup_util_sh.py @@ -0,0 +1,407 @@ +# Copyright 2016-2019 Dirk Thomas +# Licensed under the Apache License, Version 2.0 + +import argparse +from collections import OrderedDict +import os +from pathlib import Path +import sys + + +FORMAT_STR_COMMENT_LINE = '# {comment}' +FORMAT_STR_SET_ENV_VAR = 'export {name}="{value}"' +FORMAT_STR_USE_ENV_VAR = '${name}' +FORMAT_STR_INVOKE_SCRIPT = 'COLCON_CURRENT_PREFIX="{prefix}" _colcon_prefix_sh_source_script "{script_path}"' # noqa: E501 +FORMAT_STR_REMOVE_LEADING_SEPARATOR = 'if [ "$(echo -n ${name} | head -c 1)" = ":" ]; then export {name}=${{{name}#?}} ; fi' # noqa: E501 +FORMAT_STR_REMOVE_TRAILING_SEPARATOR = 'if [ "$(echo -n ${name} | tail -c 1)" = ":" ]; then export {name}=${{{name}%?}} ; fi' # noqa: E501 + +DSV_TYPE_APPEND_NON_DUPLICATE = 'append-non-duplicate' +DSV_TYPE_PREPEND_NON_DUPLICATE = 'prepend-non-duplicate' +DSV_TYPE_PREPEND_NON_DUPLICATE_IF_EXISTS = 'prepend-non-duplicate-if-exists' +DSV_TYPE_SET = 'set' +DSV_TYPE_SET_IF_UNSET = 'set-if-unset' +DSV_TYPE_SOURCE = 'source' + + +def main(argv=sys.argv[1:]): # noqa: D103 + parser = argparse.ArgumentParser( + description='Output shell commands for the packages in topological ' + 'order') + parser.add_argument( + 'primary_extension', + help='The file extension of the primary shell') + parser.add_argument( + 'additional_extension', nargs='?', + help='The additional file extension to be considered') + parser.add_argument( + '--merged-install', action='store_true', + help='All install prefixes are merged into a single location') + args = parser.parse_args(argv) + + packages = get_packages(Path(__file__).parent, args.merged_install) + + ordered_packages = order_packages(packages) + for pkg_name in ordered_packages: + if _include_comments(): + print( + FORMAT_STR_COMMENT_LINE.format_map( + {'comment': 'Package: ' + pkg_name})) + prefix = os.path.abspath(os.path.dirname(__file__)) + if not args.merged_install: + prefix = os.path.join(prefix, pkg_name) + for line in get_commands( + pkg_name, prefix, args.primary_extension, + args.additional_extension + ): + print(line) + + for line in _remove_ending_separators(): + print(line) + + +def get_packages(prefix_path, merged_install): + """ + Find packages based on colcon-specific files created during installation. + + :param Path prefix_path: The install prefix path of all packages + :param bool merged_install: The flag if the packages are all installed + directly in the prefix or if each package is installed in a subdirectory + named after the package + :returns: A mapping from the package name to the set of runtime + dependencies + :rtype: dict + """ + packages = {} + # since importing colcon_core isn't feasible here the following constant + # must match colcon_core.location.get_relative_package_index_path() + subdirectory = 'share/colcon-core/packages' + if merged_install: + # return if workspace is empty + if not (prefix_path / subdirectory).is_dir(): + return packages + # find all files in the subdirectory + for p in (prefix_path / subdirectory).iterdir(): + if not p.is_file(): + continue + if p.name.startswith('.'): + continue + add_package_runtime_dependencies(p, packages) + else: + # for each subdirectory look for the package specific file + for p in prefix_path.iterdir(): + if not p.is_dir(): + continue + if p.name.startswith('.'): + continue + p = p / subdirectory / p.name + if p.is_file(): + add_package_runtime_dependencies(p, packages) + + # remove unknown dependencies + pkg_names = set(packages.keys()) + for k in packages.keys(): + packages[k] = {d for d in packages[k] if d in pkg_names} + + return packages + + +def add_package_runtime_dependencies(path, packages): + """ + Check the path and if it exists extract the packages runtime dependencies. + + :param Path path: The resource file containing the runtime dependencies + :param dict packages: A mapping from package names to the sets of runtime + dependencies to add to + """ + content = path.read_text() + dependencies = set(content.split(os.pathsep) if content else []) + packages[path.name] = dependencies + + +def order_packages(packages): + """ + Order packages topologically. + + :param dict packages: A mapping from package name to the set of runtime + dependencies + :returns: The package names + :rtype: list + """ + # select packages with no dependencies in alphabetical order + to_be_ordered = list(packages.keys()) + ordered = [] + while to_be_ordered: + pkg_names_without_deps = [ + name for name in to_be_ordered if not packages[name]] + if not pkg_names_without_deps: + reduce_cycle_set(packages) + raise RuntimeError( + 'Circular dependency between: ' + ', '.join(sorted(packages))) + pkg_names_without_deps.sort() + pkg_name = pkg_names_without_deps[0] + to_be_ordered.remove(pkg_name) + ordered.append(pkg_name) + # remove item from dependency lists + for k in list(packages.keys()): + if pkg_name in packages[k]: + packages[k].remove(pkg_name) + return ordered + + +def reduce_cycle_set(packages): + """ + Reduce the set of packages to the ones part of the circular dependency. + + :param dict packages: A mapping from package name to the set of runtime + dependencies which is modified in place + """ + last_depended = None + while len(packages) > 0: + # get all remaining dependencies + depended = set() + for pkg_name, dependencies in packages.items(): + depended = depended.union(dependencies) + # remove all packages which are not dependent on + for name in list(packages.keys()): + if name not in depended: + del packages[name] + if last_depended: + # if remaining packages haven't changed return them + if last_depended == depended: + return packages.keys() + # otherwise reduce again + last_depended = depended + + +def _include_comments(): + # skipping comment lines when COLCON_TRACE is not set speeds up the + # processing especially on Windows + return bool(os.environ.get('COLCON_TRACE')) + + +def get_commands(pkg_name, prefix, primary_extension, additional_extension): + commands = [] + package_dsv_path = os.path.join(prefix, 'share', pkg_name, 'package.dsv') + if os.path.exists(package_dsv_path): + commands += process_dsv_file( + package_dsv_path, prefix, primary_extension, additional_extension) + return commands + + +def process_dsv_file( + dsv_path, prefix, primary_extension=None, additional_extension=None +): + commands = [] + if _include_comments(): + commands.append(FORMAT_STR_COMMENT_LINE.format_map({'comment': dsv_path})) + with open(dsv_path, 'r') as h: + content = h.read() + lines = content.splitlines() + + basenames = OrderedDict() + for i, line in enumerate(lines): + # skip over empty or whitespace-only lines + if not line.strip(): + continue + # skip over comments + if line.startswith('#'): + continue + try: + type_, remainder = line.split(';', 1) + except ValueError: + raise RuntimeError( + "Line %d in '%s' doesn't contain a semicolon separating the " + 'type from the arguments' % (i + 1, dsv_path)) + if type_ != DSV_TYPE_SOURCE: + # handle non-source lines + try: + commands += handle_dsv_types_except_source( + type_, remainder, prefix) + except RuntimeError as e: + raise RuntimeError( + "Line %d in '%s' %s" % (i + 1, dsv_path, e)) from e + else: + # group remaining source lines by basename + path_without_ext, ext = os.path.splitext(remainder) + if path_without_ext not in basenames: + basenames[path_without_ext] = set() + assert ext.startswith('.') + ext = ext[1:] + if ext in (primary_extension, additional_extension): + basenames[path_without_ext].add(ext) + + # add the dsv extension to each basename if the file exists + for basename, extensions in basenames.items(): + if not os.path.isabs(basename): + basename = os.path.join(prefix, basename) + if os.path.exists(basename + '.dsv'): + extensions.add('dsv') + + for basename, extensions in basenames.items(): + if not os.path.isabs(basename): + basename = os.path.join(prefix, basename) + if 'dsv' in extensions: + # process dsv files recursively + commands += process_dsv_file( + basename + '.dsv', prefix, primary_extension=primary_extension, + additional_extension=additional_extension) + elif primary_extension in extensions and len(extensions) == 1: + # source primary-only files + commands += [ + FORMAT_STR_INVOKE_SCRIPT.format_map({ + 'prefix': prefix, + 'script_path': basename + '.' + primary_extension})] + elif additional_extension in extensions: + # source non-primary files + commands += [ + FORMAT_STR_INVOKE_SCRIPT.format_map({ + 'prefix': prefix, + 'script_path': basename + '.' + additional_extension})] + + return commands + + +def handle_dsv_types_except_source(type_, remainder, prefix): + commands = [] + if type_ in (DSV_TYPE_SET, DSV_TYPE_SET_IF_UNSET): + try: + env_name, value = remainder.split(';', 1) + except ValueError: + raise RuntimeError( + "doesn't contain a semicolon separating the environment name " + 'from the value') + try_prefixed_value = os.path.join(prefix, value) if value else prefix + if os.path.exists(try_prefixed_value): + value = try_prefixed_value + if type_ == DSV_TYPE_SET: + commands += _set(env_name, value) + elif type_ == DSV_TYPE_SET_IF_UNSET: + commands += _set_if_unset(env_name, value) + else: + assert False + elif type_ in ( + DSV_TYPE_APPEND_NON_DUPLICATE, + DSV_TYPE_PREPEND_NON_DUPLICATE, + DSV_TYPE_PREPEND_NON_DUPLICATE_IF_EXISTS + ): + try: + env_name_and_values = remainder.split(';') + except ValueError: + raise RuntimeError( + "doesn't contain a semicolon separating the environment name " + 'from the values') + env_name = env_name_and_values[0] + values = env_name_and_values[1:] + for value in values: + if not value: + value = prefix + elif not os.path.isabs(value): + value = os.path.join(prefix, value) + if ( + type_ == DSV_TYPE_PREPEND_NON_DUPLICATE_IF_EXISTS and + not os.path.exists(value) + ): + comment = f'skip extending {env_name} with not existing ' \ + f'path: {value}' + if _include_comments(): + commands.append( + FORMAT_STR_COMMENT_LINE.format_map({'comment': comment})) + elif type_ == DSV_TYPE_APPEND_NON_DUPLICATE: + commands += _append_unique_value(env_name, value) + else: + commands += _prepend_unique_value(env_name, value) + else: + raise RuntimeError( + 'contains an unknown environment hook type: ' + type_) + return commands + + +env_state = {} + + +def _append_unique_value(name, value): + global env_state + if name not in env_state: + if os.environ.get(name): + env_state[name] = set(os.environ[name].split(os.pathsep)) + else: + env_state[name] = set() + # append even if the variable has not been set yet, in case a shell script sets the + # same variable without the knowledge of this Python script. + # later _remove_ending_separators() will cleanup any unintentional leading separator + extend = FORMAT_STR_USE_ENV_VAR.format_map({'name': name}) + os.pathsep + line = FORMAT_STR_SET_ENV_VAR.format_map( + {'name': name, 'value': extend + value}) + if value not in env_state[name]: + env_state[name].add(value) + else: + if not _include_comments(): + return [] + line = FORMAT_STR_COMMENT_LINE.format_map({'comment': line}) + return [line] + + +def _prepend_unique_value(name, value): + global env_state + if name not in env_state: + if os.environ.get(name): + env_state[name] = set(os.environ[name].split(os.pathsep)) + else: + env_state[name] = set() + # prepend even if the variable has not been set yet, in case a shell script sets the + # same variable without the knowledge of this Python script. + # later _remove_ending_separators() will cleanup any unintentional trailing separator + extend = os.pathsep + FORMAT_STR_USE_ENV_VAR.format_map({'name': name}) + line = FORMAT_STR_SET_ENV_VAR.format_map( + {'name': name, 'value': value + extend}) + if value not in env_state[name]: + env_state[name].add(value) + else: + if not _include_comments(): + return [] + line = FORMAT_STR_COMMENT_LINE.format_map({'comment': line}) + return [line] + + +# generate commands for removing prepended underscores +def _remove_ending_separators(): + # do nothing if the shell extension does not implement the logic + if FORMAT_STR_REMOVE_TRAILING_SEPARATOR is None: + return [] + + global env_state + commands = [] + for name in env_state: + # skip variables that already had values before this script started prepending + if name in os.environ: + continue + commands += [ + FORMAT_STR_REMOVE_LEADING_SEPARATOR.format_map({'name': name}), + FORMAT_STR_REMOVE_TRAILING_SEPARATOR.format_map({'name': name})] + return commands + + +def _set(name, value): + global env_state + env_state[name] = value + line = FORMAT_STR_SET_ENV_VAR.format_map( + {'name': name, 'value': value}) + return [line] + + +def _set_if_unset(name, value): + global env_state + line = FORMAT_STR_SET_ENV_VAR.format_map( + {'name': name, 'value': value}) + if env_state.get(name, os.environ.get(name)): + line = FORMAT_STR_COMMENT_LINE.format_map({'comment': line}) + return [line] + + +if __name__ == '__main__': # pragma: no cover + try: + rc = main() + except RuntimeError as e: + print(str(e), file=sys.stderr) + rc = 1 + sys.exit(rc) diff --git a/ros2_ws/install/hand_controller/share/ament_index/resource_index/packages/hand_controller b/ros2_ws/install/hand_controller/share/ament_index/resource_index/packages/hand_controller new file mode 100644 index 0000000..e69de29 diff --git a/ros2_ws/install/hand_controller/share/colcon-core/packages/hand_controller b/ros2_ws/install/hand_controller/share/colcon-core/packages/hand_controller new file mode 100644 index 0000000..13ff7c6 --- /dev/null +++ b/ros2_ws/install/hand_controller/share/colcon-core/packages/hand_controller @@ -0,0 +1 @@ +cv_bridge:rclpy:ros2launch:sensor_msgs:std_msgs:turtlesim:twist_stamper:v4l2_camera \ No newline at end of file diff --git a/ros2_ws/install/hand_controller/share/hand_controller/hook/ament_prefix_path.dsv b/ros2_ws/install/hand_controller/share/hand_controller/hook/ament_prefix_path.dsv new file mode 100644 index 0000000..79d4c95 --- /dev/null +++ b/ros2_ws/install/hand_controller/share/hand_controller/hook/ament_prefix_path.dsv @@ -0,0 +1 @@ +prepend-non-duplicate;AMENT_PREFIX_PATH; diff --git a/ros2_ws/install/hand_controller/share/hand_controller/hook/ament_prefix_path.sh b/ros2_ws/install/hand_controller/share/hand_controller/hook/ament_prefix_path.sh new file mode 100644 index 0000000..f3041f6 --- /dev/null +++ b/ros2_ws/install/hand_controller/share/hand_controller/hook/ament_prefix_path.sh @@ -0,0 +1,3 @@ +# generated from colcon_core/shell/template/hook_prepend_value.sh.em + +_colcon_prepend_unique_value AMENT_PREFIX_PATH "$COLCON_CURRENT_PREFIX" diff --git a/ros2_ws/install/hand_controller/share/hand_controller/hook/pythonpath.dsv b/ros2_ws/install/hand_controller/share/hand_controller/hook/pythonpath.dsv new file mode 100644 index 0000000..c2ddcdb --- /dev/null +++ b/ros2_ws/install/hand_controller/share/hand_controller/hook/pythonpath.dsv @@ -0,0 +1 @@ +prepend-non-duplicate;PYTHONPATH;lib/python3.12/site-packages diff --git a/ros2_ws/install/hand_controller/share/hand_controller/hook/pythonpath.sh b/ros2_ws/install/hand_controller/share/hand_controller/hook/pythonpath.sh new file mode 100644 index 0000000..45388fe --- /dev/null +++ b/ros2_ws/install/hand_controller/share/hand_controller/hook/pythonpath.sh @@ -0,0 +1,3 @@ +# generated from colcon_core/shell/template/hook_prepend_value.sh.em + +_colcon_prepend_unique_value PYTHONPATH "$COLCON_CURRENT_PREFIX/lib/python3.12/site-packages" diff --git a/ros2_ws/install/hand_controller/share/hand_controller/launch/demo01_turtlesim_part1_asl.launch.py b/ros2_ws/install/hand_controller/share/hand_controller/launch/demo01_turtlesim_part1_asl.launch.py new file mode 100644 index 0000000..b4ae59f --- /dev/null +++ b/ros2_ws/install/hand_controller/share/hand_controller/launch/demo01_turtlesim_part1_asl.launch.py @@ -0,0 +1,63 @@ +from launch import LaunchDescription +from launch.actions import DeclareLaunchArgument +from launch.substitutions import LaunchConfiguration, PythonExpression +from launch_ros.actions import Node + +def generate_launch_description(): + return LaunchDescription([ + DeclareLaunchArgument( + "repo_path", + default_value="/root/hand_controller", + description="Path (absolute) to hand_controller repo." + ), + DeclareLaunchArgument( + "blaze_target", + default_value="blaze_tflite", + description="blaze target implementation." + ), + DeclareLaunchArgument( + "blaze_model1", + default_value="palm_detection_lite.tflite", + description="Name of blaze detection model." + ), + DeclareLaunchArgument( + "blaze_model2", + default_value="hand_landmark_lite.tflite", + description="Name of blaze landmark model." + ), + DeclareLaunchArgument( + "verbose", + default_value="True", + description="Verbose mode." + ), + DeclareLaunchArgument( + "use_imshow", + default_value="False", + description="Enable OpenCV display." + ), + Node( + package='hand_controller', + executable='usbcam_publisher_node', + name="usbcam_publisher", + remappings=[ + ("image_raw", "usbcam_image") + ] + ), + Node( + package='hand_controller', + executable='hand_controller_asl_twist_node', + name="hand_controller", + parameters=[ + {"repo_path":LaunchConfiguration("repo_path")}, + {"blaze_target":LaunchConfiguration("blaze_target")}, + {"blaze_model1":LaunchConfiguration("blaze_model1")}, + {"blaze_model2":LaunchConfiguration("blaze_model2")}, + {"verbose":PythonExpression(['"', LaunchConfiguration('verbose'), '" == "True"'])}, + {"use_imshow":PythonExpression(['"', LaunchConfiguration('use_imshow'), '" == "True"'])} + ], + remappings=[ + ("image_raw", "usbcam_image"), + ("hand_controller/cmd_vel", "turtle1/cmd_vel") + ] + ) + ]) diff --git a/ros2_ws/install/hand_controller/share/hand_controller/launch/demo01_turtlesim_part1_mp1dials.launch.py b/ros2_ws/install/hand_controller/share/hand_controller/launch/demo01_turtlesim_part1_mp1dials.launch.py new file mode 100644 index 0000000..6f5a69d --- /dev/null +++ b/ros2_ws/install/hand_controller/share/hand_controller/launch/demo01_turtlesim_part1_mp1dials.launch.py @@ -0,0 +1,63 @@ +from launch import LaunchDescription +from launch.actions import DeclareLaunchArgument +from launch.substitutions import LaunchConfiguration, PythonExpression +from launch_ros.actions import Node + +def generate_launch_description(): + return LaunchDescription([ + DeclareLaunchArgument( + "repo_path", + default_value="/root/hand_controller", + description="Path (absolute) to hand_controller repo." + ), + DeclareLaunchArgument( + "blaze_target", + default_value="blaze_tflite", + description="blaze target implementation." + ), + DeclareLaunchArgument( + "blaze_model1", + default_value="palm_detection_lite.tflite", + description="Name of blaze detection model." + ), + DeclareLaunchArgument( + "blaze_model2", + default_value="hand_landmark_lite.tflite", + description="Name of blaze landmark model." + ), + DeclareLaunchArgument( + "verbose", + default_value="True", + description="Verbose mode." + ), + DeclareLaunchArgument( + "use_imshow", + default_value="False", + description="Enable OpenCV display." + ), + Node( + package='hand_controller', + executable='usbcam_publisher_node', + name="usbcam_publisher", + remappings=[ + ("image_raw", "usbcam_image") + ] + ), + Node( + package='hand_controller', + executable='hand_controller_mp1dials_twist_node', + name="hand_controller", + parameters=[ + {"repo_path":LaunchConfiguration("repo_path")}, + {"blaze_target":LaunchConfiguration("blaze_target")}, + {"blaze_model1":LaunchConfiguration("blaze_model1")}, + {"blaze_model2":LaunchConfiguration("blaze_model2")}, + {"verbose":PythonExpression(['"', LaunchConfiguration('verbose'), '" == "True"'])}, + {"use_imshow":PythonExpression(['"', LaunchConfiguration('use_imshow'), '" == "True"'])} + ], + remappings=[ + ("image_raw", "usbcam_image"), + ("hand_controller/cmd_vel", "turtle1/cmd_vel") + ] + ) + ]) diff --git a/ros2_ws/install/hand_controller/share/hand_controller/launch/demo01_turtlesim_part2.launch.py b/ros2_ws/install/hand_controller/share/hand_controller/launch/demo01_turtlesim_part2.launch.py new file mode 100644 index 0000000..e267ccd --- /dev/null +++ b/ros2_ws/install/hand_controller/share/hand_controller/launch/demo01_turtlesim_part2.launch.py @@ -0,0 +1,29 @@ +from launch import LaunchDescription +from launch.actions import DeclareLaunchArgument +from launch.substitutions import LaunchConfiguration +from launch_ros.actions import Node + +def generate_launch_description(): + return LaunchDescription([ + DeclareLaunchArgument( + "viewer1_name", + default_value="Hand Controller", + description="Name of Image Viewer for hand_controller/annotations." + ), + Node( + package='hand_controller', + executable='usbcam_subscriber_node', + name="hand_controller_annotations", + parameters=[ + {"viewer_name":LaunchConfiguration("viewer1_name")} + ], + remappings=[ + ("image_raw", "hand_controller/image_annotated") + ] + ), + Node( + package='turtlesim', + executable='turtlesim_node', + name="my_turtlesim", + ) + ]) diff --git a/ros2_ws/install/hand_controller/share/hand_controller/launch/demo11_mogiros_car_part1_asl.launch.py b/ros2_ws/install/hand_controller/share/hand_controller/launch/demo11_mogiros_car_part1_asl.launch.py new file mode 100644 index 0000000..67e7492 --- /dev/null +++ b/ros2_ws/install/hand_controller/share/hand_controller/launch/demo11_mogiros_car_part1_asl.launch.py @@ -0,0 +1,61 @@ +from launch import LaunchDescription +from launch.actions import DeclareLaunchArgument +from launch.substitutions import LaunchConfiguration, PythonExpression +from launch_ros.actions import Node + +def generate_launch_description(): + return LaunchDescription([ + DeclareLaunchArgument( + "repo_path", + default_value="/root/hand_controller", + description="Path (absolute) to hand_controller repo." + ), + DeclareLaunchArgument( + "blaze_target", + default_value="blaze_tflite", + description="blaze target implementation." + ), + DeclareLaunchArgument( + "blaze_model1", + default_value="palm_detection_lite.tflite", + description="Name of blaze detection model." + ), + DeclareLaunchArgument( + "blaze_model2", + default_value="hand_landmark_lite.tflite", + description="Name of blaze landmark model." + ), + DeclareLaunchArgument( + "verbose", + default_value="True", + description="Verbose mode." + ), + DeclareLaunchArgument( + "use_imshow", + default_value="False", + description="Enable OpenCV display." + ), + Node( + package='hand_controller', + executable='usbcam_publisher_node', + name="usbcam_publisher", + remappings=[("image_raw", "usbcam_image")] + ), + Node( + package='hand_controller', + executable='hand_controller_asl_twist_node', + name="hand_controller", + parameters=[ + {"repo_path":LaunchConfiguration("repo_path")}, + {"blaze_target":LaunchConfiguration("blaze_target")}, + {"blaze_model1":LaunchConfiguration("blaze_model1")}, + {"blaze_model2":LaunchConfiguration("blaze_model2")}, + {"verbose":PythonExpression(['"', LaunchConfiguration('verbose'), '" == "True"'])}, + {"use_imshow":PythonExpression(['"', LaunchConfiguration('use_imshow'), '" == "True"'])} + ], + remappings=[ + ("image_raw", "usbcam_image"), + ("hand_controller/cmd_vel", "cmd_vel") + ] + ) + ]) diff --git a/ros2_ws/install/hand_controller/share/hand_controller/launch/demo11_mogiros_car_part1_ei1dials.launch.py b/ros2_ws/install/hand_controller/share/hand_controller/launch/demo11_mogiros_car_part1_ei1dials.launch.py new file mode 100644 index 0000000..1fe181a --- /dev/null +++ b/ros2_ws/install/hand_controller/share/hand_controller/launch/demo11_mogiros_car_part1_ei1dials.launch.py @@ -0,0 +1,111 @@ +from launch import LaunchDescription +from launch.actions import DeclareLaunchArgument +from launch.conditions import IfCondition, UnlessCondition +from launch.substitutions import LaunchConfiguration, PythonExpression +from launch_ros.actions import Node + +def generate_launch_description(): + return LaunchDescription([ + DeclareLaunchArgument( + "repo_path", + default_value="/root/hand_controller", + description="Path (absolute) to hand_controller repo." + ), + DeclareLaunchArgument( + "model", + default_value="/root/hand_controller/handsv2.eim", + description="Name of Edge Impulse FOMO model." + ), + DeclareLaunchArgument( + "verbose", + default_value="True", + description="Verbose mode." + ), + DeclareLaunchArgument( + "use_imshow", + default_value="False", + description="Enable OpenCV display." + ), + DeclareLaunchArgument( + "use_flask", + default_value="False", + description="Enable Flask Server." + ), + DeclareLaunchArgument( + "x_t", + default_value="0.1", + description="threshold for activation of twist linear.x control." + ), + DeclareLaunchArgument( + "x_a", + default_value="0.0", + description="fixed (offset) value for twist linear.x control." + ), + DeclareLaunchArgument( + "x_b", + default_value="10.0", + description="scale (multiplier) value for twist linear.x control." + ), + DeclareLaunchArgument( + "z_t", + default_value="0.1", + description="threshold for activation of twist angular.z control." + ), + DeclareLaunchArgument( + "z_a", + default_value="0.0", + description="fixed (offset) value for twist angular.z control." + ), + DeclareLaunchArgument( + "z_b", + default_value="10.0", + description="scale (multiplier) value for twist angular.z control." + ), + DeclareLaunchArgument( + "dials_single", + default_value="False", + description="Use single dials (left) for both linear.x and angular.z." + ), + Node( + package='hand_controller', + executable='usbcam_publisher_node', + name="usbcam_publisher", + remappings=[("image_raw", "usbcam_image")] + ), + Node( + package='hand_controller', + executable='usbcam_subscriber_flask_node', + remappings=[ + ("image_raw", "hand_controller/image_annotated") + ], + parameters=[{ + "model": LaunchConfiguration("model"), + "verbose": LaunchConfiguration("verbose"), + "use_imshow": LaunchConfiguration("use_imshow"), + # Add any other parameters you need + }], + condition=IfCondition(PythonExpression(['"', LaunchConfiguration('use_flask'), '" == "True"'])) + ), + Node( + package='hand_controller', + executable='hand_controller_ei1dials_twist_node', + name="hand_controller", + parameters=[ + {"repo_path":LaunchConfiguration("repo_path")}, + {"model":LaunchConfiguration("model")}, + {"verbose":PythonExpression(['"', LaunchConfiguration('verbose'), '" == "True"'])}, + {"use_imshow":PythonExpression(['"', LaunchConfiguration('use_imshow'), '" == "True"'])}, + {"x_t":LaunchConfiguration("x_t")}, + {"x_a":LaunchConfiguration("x_a")}, + {"x_b":LaunchConfiguration("x_b")}, + {"z_t":LaunchConfiguration("z_t")}, + {"z_a":LaunchConfiguration("z_a")}, + {"z_b":LaunchConfiguration("z_b")}, + {"dials_single":PythonExpression(['"', LaunchConfiguration('dials_single'), '" == "True"'])} + ], + remappings=[ + ("image_raw", "usbcam_image"), + ("hand_controller/cmd_vel", "cmd_vel") + ] + ) + ]) diff --git a/ros2_ws/install/hand_controller/share/hand_controller/launch/demo11_mogiros_car_part1_mp1dials.launch.py b/ros2_ws/install/hand_controller/share/hand_controller/launch/demo11_mogiros_car_part1_mp1dials.launch.py new file mode 100644 index 0000000..520ea37 --- /dev/null +++ b/ros2_ws/install/hand_controller/share/hand_controller/launch/demo11_mogiros_car_part1_mp1dials.launch.py @@ -0,0 +1,117 @@ +from launch import LaunchDescription +from launch.actions import DeclareLaunchArgument +from launch.conditions import IfCondition, UnlessCondition +from launch.substitutions import LaunchConfiguration, PythonExpression +from launch_ros.actions import Node + +def generate_launch_description(): + return LaunchDescription([ + DeclareLaunchArgument( + "repo_path", + default_value="/root/hand_controller", + description="Path (absolute) to hand_controller repo." + ), + DeclareLaunchArgument( + "blaze_target", + default_value="blaze_tflite", + description="blaze target implementation." + ), + DeclareLaunchArgument( + "blaze_model1", + default_value="palm_detection_lite.tflite", + description="Name of blaze detection model." + ), + DeclareLaunchArgument( + "blaze_model2", + default_value="hand_landmark_lite.tflite", + description="Name of blaze landmark model." + ), + DeclareLaunchArgument( + "verbose", + default_value="True", + description="Verbose mode." + ), + DeclareLaunchArgument( + "use_imshow", + default_value="False", + description="Enable OpenCV display." + ), + DeclareLaunchArgument( + "use_flask", + default_value="False", + description="Enable Flask Server." + ), + DeclareLaunchArgument( + "x_t", + default_value="0.0", + description="threshold for activation of twist linear.x control." + ), + DeclareLaunchArgument( + "x_a", + default_value="0.0", + description="fixed (offset) value for twist linear.x control." + ), + DeclareLaunchArgument( + "x_b", + default_value="10.0", + description="scale (multiplier) value for twist linear.x control." + ), + DeclareLaunchArgument( + "z_t", + default_value="0.0", + description="threshold for activation of twist angular.z control." + ), + DeclareLaunchArgument( + "z_a", + default_value="0.0", + description="fixed (offset) value for twist angular.z control." + ), + DeclareLaunchArgument( + "z_b", + default_value="10.0", + description="scale (multiplier) value for twist angular.z control." + ), + DeclareLaunchArgument( + "dials_single", + default_value="False", + description="Use single dials (left) for both linear.x and angular.z." + ), + Node( + package='hand_controller', + executable='usbcam_publisher_node', + name="usbcam_publisher", + remappings=[("image_raw", "usbcam_image")] + ), + Node( + package='hand_controller', + executable='usbcam_subscriber_flask_node', + remappings=[ + ("image_raw", "hand_controller/image_annotated") + ], + condition=IfCondition(PythonExpression(['"', LaunchConfiguration('use_flask'), '" == "True"'])) + ), + Node( + package='hand_controller', + executable='hand_controller_mp1dials_twist_node', + name="hand_controller", + parameters=[ + {"repo_path":LaunchConfiguration("repo_path")}, + {"blaze_target":LaunchConfiguration("blaze_target")}, + {"blaze_model1":LaunchConfiguration("blaze_model1")}, + {"blaze_model2":LaunchConfiguration("blaze_model2")}, + {"verbose":PythonExpression(['"', LaunchConfiguration('verbose'), '" == "True"'])}, + {"use_imshow":PythonExpression(['"', LaunchConfiguration('use_imshow'), '" == "True"'])}, + {"x_t":LaunchConfiguration("x_t")}, + {"x_a":LaunchConfiguration("x_a")}, + {"x_b":LaunchConfiguration("x_b")}, + {"z_t":LaunchConfiguration("z_t")}, + {"z_a":LaunchConfiguration("z_a")}, + {"z_b":LaunchConfiguration("z_b")}, + {"dials_single":PythonExpression(['"', LaunchConfiguration('dials_single'), '" == "True"'])} + ], + remappings=[ + ("image_raw", "usbcam_image"), + ("hand_controller/cmd_vel", "cmd_vel") + ] + ) + ]) diff --git a/ros2_ws/install/hand_controller/share/hand_controller/launch/demo11_mogiros_car_part2.launch.py b/ros2_ws/install/hand_controller/share/hand_controller/launch/demo11_mogiros_car_part2.launch.py new file mode 100644 index 0000000..5493efb --- /dev/null +++ b/ros2_ws/install/hand_controller/share/hand_controller/launch/demo11_mogiros_car_part2.launch.py @@ -0,0 +1,153 @@ +import os +from launch import LaunchDescription +from launch.actions import DeclareLaunchArgument, IncludeLaunchDescription +from launch.conditions import IfCondition, UnlessCondition +from launch.substitutions import PythonExpression +from launch.launch_description_sources import PythonLaunchDescriptionSource +from launch.substitutions import LaunchConfiguration, PathJoinSubstitution, Command +from launch_ros.actions import Node +from ament_index_python.packages import get_package_share_directory + +def generate_launch_description(): + + pkg_bme_gazebo_basics = get_package_share_directory('bme_gazebo_basics') + + gazebo_models_path, ignore_last_dir = os.path.split(pkg_bme_gazebo_basics) + os.environ["GZ_SIM_RESOURCE_PATH"] += os.pathsep + gazebo_models_path + + viewer1_name_arg = DeclareLaunchArgument( + "viewer1_name", + default_value="Hand Controller", + description="Name of Image Viewer for hand_controller/annotations." + ) + use_imshow_arg = DeclareLaunchArgument( + "use_imshow", + default_value="True", + description="Use usbcam_subscriber to view annotated image." + ) + + rviz_launch_arg = DeclareLaunchArgument( + 'rviz', default_value='true', + description='Open RViz.' + ) + + world_arg = DeclareLaunchArgument( + 'world', default_value='world.sdf', + description='Name of the Gazebo world file to load' + ) + + model_arg = DeclareLaunchArgument( + 'model', default_value='mogi_bot.urdf', + description='Name of the URDF description to load' + ) + + # Define the path to your URDF or Xacro file + urdf_file_path = PathJoinSubstitution([ + pkg_bme_gazebo_basics, # Replace with your package name + "urdf", + LaunchConfiguration('model') # Replace with your URDF or Xacro file + ]) + + world_launch = IncludeLaunchDescription( + PythonLaunchDescriptionSource( + os.path.join(pkg_bme_gazebo_basics, 'launch', 'world.launch.py'), + ), + launch_arguments={ + 'world': LaunchConfiguration('world'), + }.items() + ) + + # Hand controller (most nodes are launch in *_part1.launch.py) + hand_controller_viewer_node = Node( + package='hand_controller', + executable='usbcam_subscriber_node', + name="hand_controller_annotations", + parameters=[ + {"viewer_name":LaunchConfiguration("viewer1_name")} + ], + remappings=[ + ("image_raw", "hand_controller/image_annotated") + ], + condition=IfCondition(PythonExpression(['"', LaunchConfiguration('use_imshow'), '" == "True"'])) + ) + + # Launch rviz + rviz_node = Node( + package='rviz2', + executable='rviz2', + arguments=['-d', os.path.join(pkg_bme_gazebo_basics, 'rviz', 'rviz.rviz')], + condition=IfCondition(LaunchConfiguration('rviz')), + parameters=[ + {'use_sim_time': True}, + ] + ) + + # Spawn the URDF model using the `/world//create` service + spawn_urdf_node = Node( + package="ros_gz_sim", + executable="create", + arguments=[ + "-name", "my_robot", + "-topic", "robot_description", + "-x", "0.0", "-y", "0.0", "-z", "0.5", "-Y", "0.0" # Initial spawn position + ], + output="screen", + parameters=[ + {'use_sim_time': True}, + ] + ) + + robot_state_publisher_node = Node( + package='robot_state_publisher', + executable='robot_state_publisher', + name='robot_state_publisher', + output='screen', + parameters=[ + {'robot_description': Command(['xacro', ' ', urdf_file_path]), + 'use_sim_time': True}, + ], + remappings=[ + ('/tf', 'tf'), + ('/tf_static', 'tf_static') + ] + ) + + # Node to bridge messages like /cmd_vel and /odom + gz_bridge_node = Node( + package="ros_gz_bridge", + executable="parameter_bridge", + arguments=[ + "/clock@rosgraph_msgs/msg/Clock[gz.msgs.Clock", + "/cmd_vel@geometry_msgs/msg/Twist@gz.msgs.Twist", + "/odom@nav_msgs/msg/Odometry@gz.msgs.Odometry", + "/joint_states@sensor_msgs/msg/JointState@gz.msgs.Model", + "/tf@tf2_msgs/msg/TFMessage@gz.msgs.Pose_V" + ], + output="screen", + parameters=[ + {'use_sim_time': True}, + ] + ) + + trajectory_node = Node( + package='mogi_trajectory_server', + executable='mogi_trajectory_server', + name='mogi_trajectory_server', + ) + + launchDescriptionObject = LaunchDescription() + + launchDescriptionObject.add_action(viewer1_name_arg) + launchDescriptionObject.add_action(use_imshow_arg) + launchDescriptionObject.add_action(rviz_launch_arg) + launchDescriptionObject.add_action(world_arg) + launchDescriptionObject.add_action(model_arg) + launchDescriptionObject.add_action(world_launch) + launchDescriptionObject.add_action(hand_controller_viewer_node) + launchDescriptionObject.add_action(rviz_node) + launchDescriptionObject.add_action(spawn_urdf_node) + launchDescriptionObject.add_action(robot_state_publisher_node) + launchDescriptionObject.add_action(gz_bridge_node) + launchDescriptionObject.add_action(trajectory_node) + + return launchDescriptionObject diff --git a/ros2_ws/install/hand_controller/share/hand_controller/launch/demo12_rosmaster_part1_asl.launch.py b/ros2_ws/install/hand_controller/share/hand_controller/launch/demo12_rosmaster_part1_asl.launch.py new file mode 100644 index 0000000..bc1e316 --- /dev/null +++ b/ros2_ws/install/hand_controller/share/hand_controller/launch/demo12_rosmaster_part1_asl.launch.py @@ -0,0 +1,67 @@ +from launch import LaunchDescription +from launch.actions import DeclareLaunchArgument +from launch.substitutions import LaunchConfiguration, PythonExpression +from launch_ros.actions import Node + +def generate_launch_description(): + return LaunchDescription([ + DeclareLaunchArgument( + "repo_path", + default_value="/root/hand_controller", + description="Path (absolute) to hand_controller repo." + ), + DeclareLaunchArgument( + "blaze_target", + default_value="blaze_tflite", + description="blaze target implementation." + ), + DeclareLaunchArgument( + "blaze_model1", + default_value="palm_detection_lite.tflite", + description="Name of blaze detection model." + ), + DeclareLaunchArgument( + "blaze_model2", + default_value="hand_landmark_lite.tflite", + description="Name of blaze landmark model." + ), + DeclareLaunchArgument( + "verbose", + default_value="True", + description="Verbose mode." + ), + DeclareLaunchArgument( + "use_imshow", + default_value="False", + description="Enable OpenCV display." + ), + Node( + package='hand_controller', + executable='usbcam_publisher_node', + name="usbcam_publisher", + remappings=[("image_raw", "usbcam_image")] + ), + Node( + package='hand_controller', + executable='hand_controller_asl_twist_node', + name="hand_controller", + parameters=[ + {"repo_path":LaunchConfiguration("repo_path")}, + {"blaze_target":LaunchConfiguration("blaze_target")}, + {"blaze_model1":LaunchConfiguration("blaze_model1")}, + {"blaze_model2":LaunchConfiguration("blaze_model2")}, + {"verbose":PythonExpression(['"', LaunchConfiguration('verbose'), '" == "True"'])}, + {"use_imshow":PythonExpression(['"', LaunchConfiguration('use_imshow'), '" == "True"'])} + ], + remappings=[ + ("image_raw", "usbcam_image"), + ("hand_controller/cmd_vel", "cmd_vel") + ] + ) + # not available on QIRP v1.4 + #Node( + # package='twist_stamper', + # executable='twist_stamper', + # remappings=[("cmd_vel_in", "cmd_vel"),("cmd_vel_out", "mecanum_drive_controller/cmd_vel")] + #) + ]) diff --git a/ros2_ws/install/hand_controller/share/hand_controller/launch/demo12_rosmaster_part1_mp1dials.launch.py b/ros2_ws/install/hand_controller/share/hand_controller/launch/demo12_rosmaster_part1_mp1dials.launch.py new file mode 100644 index 0000000..5555872 --- /dev/null +++ b/ros2_ws/install/hand_controller/share/hand_controller/launch/demo12_rosmaster_part1_mp1dials.launch.py @@ -0,0 +1,67 @@ +from launch import LaunchDescription +from launch.actions import DeclareLaunchArgument +from launch.substitutions import LaunchConfiguration, PythonExpression +from launch_ros.actions import Node + +def generate_launch_description(): + return LaunchDescription([ + DeclareLaunchArgument( + "repo_path", + default_value="/root/hand_controller", + description="Path (absolute) to hand_controller repo." + ), + DeclareLaunchArgument( + "blaze_target", + default_value="blaze_tflite", + description="blaze target implementation." + ), + DeclareLaunchArgument( + "blaze_model1", + default_value="palm_detection_lite.tflite", + description="Name of blaze detection model." + ), + DeclareLaunchArgument( + "blaze_model2", + default_value="hand_landmark_lite.tflite", + description="Name of blaze landmark model." + ), + DeclareLaunchArgument( + "verbose", + default_value="True", + description="Verbose mode." + ), + DeclareLaunchArgument( + "use_imshow", + default_value="False", + description="Enable OpenCV display." + ), + Node( + package='hand_controller', + executable='usbcam_publisher_node', + name="usbcam_publisher", + remappings=[("image_raw", "usbcam_image")] + ), + Node( + package='hand_controller', + executable='hand_controller_mp1dials_twist_node', + name="hand_controller", + parameters=[ + {"repo_path":LaunchConfiguration("repo_path")}, + {"blaze_target":LaunchConfiguration("blaze_target")}, + {"blaze_model1":LaunchConfiguration("blaze_model1")}, + {"blaze_model2":LaunchConfiguration("blaze_model2")}, + {"verbose":PythonExpression(['"', LaunchConfiguration('verbose'), '" == "True"'])}, + {"use_imshow":PythonExpression(['"', LaunchConfiguration('use_imshow'), '" == "True"'])} + ], + remappings=[ + ("image_raw", "usbcam_image"), + ("hand_controller/cmd_vel", "cmd_vel") + ] + ) + # not available on QIRP v1.4 + #Node( + # package='twist_stamper', + # executable='twist_stamper', + # remappings=[("cmd_vel_in", "cmd_vel"),("cmd_vel_out", "mecanum_drive_controller/cmd_vel")] + #) + ]) diff --git a/ros2_ws/install/hand_controller/share/hand_controller/launch/demo12_rosmaster_part2.launch.py b/ros2_ws/install/hand_controller/share/hand_controller/launch/demo12_rosmaster_part2.launch.py new file mode 100644 index 0000000..b4787dc --- /dev/null +++ b/ros2_ws/install/hand_controller/share/hand_controller/launch/demo12_rosmaster_part2.launch.py @@ -0,0 +1,334 @@ +#!/usr/bin/env python3 +""" +Launch Gazebo simulation with a robot. + +This launch file sets up a complete ROS 2 simulation environment with Gazebo for +a Yahboom ROSMASTER robot: https://github.com/YahboomTechnology + +:author: Addison Sears-Collins +:date: November 21, 2024 +""" + +import os +from launch import LaunchDescription +from launch.actions import ( + AppendEnvironmentVariable, + DeclareLaunchArgument, + IncludeLaunchDescription +) +from launch.conditions import IfCondition +from launch.launch_description_sources import PythonLaunchDescriptionSource +from launch.substitutions import LaunchConfiguration, PathJoinSubstitution, PythonExpression +from launch_ros.actions import Node +from launch_ros.substitutions import FindPackageShare + + +def generate_launch_description(): + """ + Generate a launch description for the Gazebo simulation. + + This function sets up all necessary parameters, paths, and nodes required to launch + the Gazebo simulation with a robot. It handles: + 1. Setting up package paths and constants + 2. Declaring launch arguments for robot configuration + 3. Setting up the Gazebo environment + 4. Spawning the robot in simulation + + Returns: + LaunchDescription: A complete launch description for the simulation + """ + # Constants for paths to different files and folders + package_name_gazebo = 'yahboom_rosmaster_gazebo' + package_name_description = 'yahboom_rosmaster_description' + package_name_bringup = 'yahboom_rosmaster_bringup' + + default_robot_name = 'rosmaster_x3' + gazebo_models_path = 'models' + #default_world_file = 'empty.world' + #default_z = '0.05' + default_world_file = 'cafe.world' + default_z = '0.20' + gazebo_worlds_path = 'worlds' + ros_gz_bridge_config_file_path = 'config/ros_gz_bridge.yaml' + rviz_config_filename = 'yahboom_rosmaster_gazebo_sim.rviz' + + # Set the path to different files and folders + pkg_ros_gz_sim = FindPackageShare(package='ros_gz_sim').find('ros_gz_sim') + pkg_share_gazebo = FindPackageShare(package=package_name_gazebo).find(package_name_gazebo) + pkg_share_description = FindPackageShare( + package=package_name_description).find(package_name_description) + pkg_share_bringup = FindPackageShare(package=package_name_bringup).find(package_name_bringup) + + default_ros_gz_bridge_config_file_path = os.path.join( + pkg_share_gazebo, ros_gz_bridge_config_file_path) + default_rviz_config_path = PathJoinSubstitution( + [pkg_share_gazebo, 'rviz', rviz_config_filename]) + gazebo_models_path = os.path.join(pkg_share_gazebo, gazebo_models_path) + + # Launch configuration variables + viewer1_name_arg = DeclareLaunchArgument( + "viewer1_name", + default_value="Hand Controller", + description="Name of Image Viewer for hand_controller/annotations." + ) + # + enable_odom_tf = LaunchConfiguration('enable_odom_tf') + headless = LaunchConfiguration('headless') + jsp_gui = LaunchConfiguration('jsp_gui') + load_controllers = LaunchConfiguration('load_controllers') + robot_name = LaunchConfiguration('robot_name') + rviz_config_file = LaunchConfiguration('rviz_config_file') + use_rviz = LaunchConfiguration('use_rviz') + use_gazebo = LaunchConfiguration('use_gazebo') + use_robot_state_pub = LaunchConfiguration('use_robot_state_pub') + use_sim_time = LaunchConfiguration('use_sim_time') + world_file = LaunchConfiguration('world_file') + + world_path = PathJoinSubstitution([ + pkg_share_gazebo, + gazebo_worlds_path, + world_file + ]) + + # Set the pose configuration variables + x = LaunchConfiguration('x') + y = LaunchConfiguration('y') + z = LaunchConfiguration('z') + roll = LaunchConfiguration('roll') + pitch = LaunchConfiguration('pitch') + yaw = LaunchConfiguration('yaw') + + # Declare the launch arguments + declare_enable_odom_tf_cmd = DeclareLaunchArgument( + name='enable_odom_tf', + default_value='true', + choices=['true', 'false'], + description='Whether to enable odometry transform broadcasting via ROS 2 Control') + + declare_headless_cmd = DeclareLaunchArgument( + name='headless', + default_value='False', + description='Whether to execute gzclient (visualization)') + + declare_robot_name_cmd = DeclareLaunchArgument( + name='robot_name', + default_value=default_robot_name, + description='The name for the robot') + + declare_rviz_config_file_cmd = DeclareLaunchArgument( + name='rviz_config_file', + default_value=default_rviz_config_path, + description='Full path to the RVIZ config file to use') + + declare_load_controllers_cmd = DeclareLaunchArgument( + name='load_controllers', + default_value='true', + description='Flag to enable loading of ROS 2 controllers') + + declare_use_robot_state_pub_cmd = DeclareLaunchArgument( + name='use_robot_state_pub', + default_value='true', + description='Flag to enable robot state publisher') + + # GUI and visualization arguments + declare_jsp_gui_cmd = DeclareLaunchArgument( + name='jsp_gui', + default_value='false', + description='Flag to enable joint_state_publisher_gui') + + declare_use_rviz_cmd = DeclareLaunchArgument( + name='use_rviz', + default_value='true', + description='Flag to enable RViz') + + declare_use_gazebo_cmd = DeclareLaunchArgument( + name='use_gazebo', + default_value='true', + description='Flag to enable Gazebo') + + declare_use_sim_time_cmd = DeclareLaunchArgument( + name='use_sim_time', + default_value='true', + description='Use simulation (Gazebo) clock if true') + + declare_world_cmd = DeclareLaunchArgument( + name='world_file', + default_value=default_world_file, + description='World file name (e.g., empty.world, house.world, pick_and_place_demo.world)') + + # Pose arguments + declare_x_cmd = DeclareLaunchArgument( + name='x', + default_value='0.0', + description='x component of initial position, meters') + + declare_y_cmd = DeclareLaunchArgument( + name='y', + default_value='0.0', + description='y component of initial position, meters') + + declare_z_cmd = DeclareLaunchArgument( + name='z', + #default_value='0.05', + default_value=default_z, + description='z component of initial position, meters') + + declare_roll_cmd = DeclareLaunchArgument( + name='roll', + default_value='0.0', + description='roll angle of initial orientation, radians') + + declare_pitch_cmd = DeclareLaunchArgument( + name='pitch', + default_value='0.0', + description='pitch angle of initial orientation, radians') + + declare_yaw_cmd = DeclareLaunchArgument( + name='yaw', + default_value='0.0', + description='yaw angle of initial orientation, radians') + + # Include Robot State Publisher launch file if enabled + robot_state_publisher_cmd = IncludeLaunchDescription( + PythonLaunchDescriptionSource([ + os.path.join(pkg_share_description, 'launch', 'robot_state_publisher.launch.py') + ]), + launch_arguments={ + 'enable_odom_tf': enable_odom_tf, + 'jsp_gui': jsp_gui, + 'rviz_config_file': rviz_config_file, + 'use_rviz': use_rviz, + 'use_gazebo': use_gazebo, + 'use_sim_time': use_sim_time + }.items(), + condition=IfCondition(use_robot_state_pub) + ) + + # Include ROS 2 Controllers launch file if enabled + load_controllers_cmd = IncludeLaunchDescription( + PythonLaunchDescriptionSource([ + os.path.join(pkg_share_bringup, 'launch', 'load_ros2_controllers.launch.py') + ]), + launch_arguments={ + 'use_sim_time': use_sim_time + }.items(), + condition=IfCondition(load_controllers) + ) + + # Set Gazebo model path + set_env_vars_resources = AppendEnvironmentVariable( + 'GZ_SIM_RESOURCE_PATH', + gazebo_models_path) + + # Start Gazebo + start_gazebo_server_cmd = IncludeLaunchDescription( + PythonLaunchDescriptionSource( + os.path.join(pkg_ros_gz_sim, 'launch', 'gz_sim.launch.py')), + launch_arguments=[('gz_args', [' -r -s -v 4 ', world_path])]) + + # Start Gazebo client (GUI) if not headless + start_gazebo_client_cmd = IncludeLaunchDescription( + PythonLaunchDescriptionSource( + os.path.join(pkg_ros_gz_sim, 'launch', 'gz_sim.launch.py')), + launch_arguments={'gz_args': ['-g ']}.items(), + condition=IfCondition(PythonExpression(['not ', headless]))) + + + # Hand controller (most nodes are launch in *_part1.launch.py) + hand_controller_viewer_node = Node( + package='hand_controller', + executable='usbcam_subscriber_node', + name="hand_controller_annotations", + parameters=[ + {"viewer_name":LaunchConfiguration("viewer1_name")} + ], + remappings=[ + ("image_raw", "hand_controller/image_annotated") + ] + ) + twist_stamper_node = Node( + package='twist_stamper', + executable='twist_stamper', + remappings=[("cmd_vel_in", "cmd_vel"),("cmd_vel_out", "mecanum_drive_controller/cmd_vel")] + ) + + # Bridge ROS topics and Gazebo messages for establishing communication + start_gazebo_ros_bridge_cmd = Node( + package='ros_gz_bridge', + executable='parameter_bridge', + parameters=[{ + 'config_file': default_ros_gz_bridge_config_file_path, + }], + output='screen') + + # Includes optimizations to minimize latency and bandwidth when streaming image data + start_gazebo_ros_image_bridge_cmd = Node( + package='ros_gz_image', + executable='image_bridge', + arguments=[ + '/cam_1/image' + ], + remappings=[ + ('/cam_1/image', '/cam_1/color/image_raw') + ]) + + # Spawn the robot + start_gazebo_ros_spawner_cmd = Node( + package='ros_gz_sim', + executable='create', + output='screen', + arguments=[ + '-topic', '/robot_description', + '-name', robot_name, + '-allow_renaming', 'true', + '-x', x, + '-y', y, + '-z', z, + '-R', roll, + '-P', pitch, + '-Y', yaw + ]) + + # Create the launch description and populate + ld = LaunchDescription() + + # Declar parameters for the Hand Controller + ld.add_action(viewer1_name_arg) + + # Declare the launch options + ld.add_action(declare_enable_odom_tf_cmd) + ld.add_action(declare_headless_cmd) + ld.add_action(declare_robot_name_cmd) + ld.add_action(declare_rviz_config_file_cmd) + ld.add_action(declare_jsp_gui_cmd) + ld.add_action(declare_load_controllers_cmd) + ld.add_action(declare_use_rviz_cmd) + ld.add_action(declare_use_gazebo_cmd) + ld.add_action(declare_use_robot_state_pub_cmd) + ld.add_action(declare_use_sim_time_cmd) + ld.add_action(declare_world_cmd) + + # Add pose arguments + ld.add_action(declare_x_cmd) + ld.add_action(declare_y_cmd) + ld.add_action(declare_z_cmd) + ld.add_action(declare_roll_cmd) + ld.add_action(declare_pitch_cmd) + ld.add_action(declare_yaw_cmd) + + # Add the actions to the launch description + ld.add_action(set_env_vars_resources) + ld.add_action(robot_state_publisher_cmd) + ld.add_action(load_controllers_cmd) + ld.add_action(start_gazebo_server_cmd) + ld.add_action(start_gazebo_client_cmd) + + # Launch nodes for Hand Controller + ld.add_action(hand_controller_viewer_node) + ld.add_action(twist_stamper_node) + + ld.add_action(start_gazebo_ros_bridge_cmd) + ld.add_action(start_gazebo_ros_image_bridge_cmd) + ld.add_action(start_gazebo_ros_spawner_cmd) + + return ld diff --git a/ros2_ws/install/hand_controller/share/hand_controller/launch/demo21_mogiros_arm_part1_asl.launch.py b/ros2_ws/install/hand_controller/share/hand_controller/launch/demo21_mogiros_arm_part1_asl.launch.py new file mode 100644 index 0000000..eb0ff3f --- /dev/null +++ b/ros2_ws/install/hand_controller/share/hand_controller/launch/demo21_mogiros_arm_part1_asl.launch.py @@ -0,0 +1,62 @@ +from launch import LaunchDescription +from launch.actions import DeclareLaunchArgument +from launch.substitutions import LaunchConfiguration, PythonExpression +from launch_ros.actions import Node + +def generate_launch_description(): + return LaunchDescription([ + DeclareLaunchArgument( + "repo_path", + default_value="/root/hand_controller", + description="Path (absolute) to hand_controller repo." + ), + DeclareLaunchArgument( + "blaze_target", + default_value="blaze_tflite", + description="blaze target implementation." + ), + DeclareLaunchArgument( + "blaze_model1", + default_value="palm_detection_lite.tflite", + description="Name of blaze detection model." + ), + DeclareLaunchArgument( + "blaze_model2", + default_value="hand_landmark_lite.tflite", + description="Name of blaze landmark model." + ), + DeclareLaunchArgument( + "verbose", + default_value="True", + description="Verbose mode." + ), + DeclareLaunchArgument( + "use_imshow", + default_value="False", + description="Enable OpenCV display." + ), + Node( + package='hand_controller', + executable='usbcam_publisher_node', + name="usbcam_publisher", + remappings=[("image_raw", "usbcam_image")] + ), + Node( + package='hand_controller', + executable='hand_controller_asl_joints_node', + name="hand_controller", + parameters=[ + {"repo_path":LaunchConfiguration("repo_path")}, + {"blaze_target":LaunchConfiguration("blaze_target")}, + {"blaze_model1":LaunchConfiguration("blaze_model1")}, + {"blaze_model2":LaunchConfiguration("blaze_model2")}, + {"verbose":PythonExpression(['"', LaunchConfiguration('verbose'), '" == "True"'])}, + {"use_imshow":PythonExpression(['"', LaunchConfiguration('use_imshow'), '" == "True"'])} + ], + remappings=[ + ("image_raw", "usbcam_image"), + ("/arm_controller/joint_trajectory", "/arm_controller/joint_trajectory"), + ("/gripper_controller/joint_trajectory", "/gripper_controller/joint_trajectory") + ] + ) + ]) diff --git a/ros2_ws/install/hand_controller/share/hand_controller/launch/demo21_mogiros_arm_part1_mp1dials.launch.py b/ros2_ws/install/hand_controller/share/hand_controller/launch/demo21_mogiros_arm_part1_mp1dials.launch.py new file mode 100644 index 0000000..2706c67 --- /dev/null +++ b/ros2_ws/install/hand_controller/share/hand_controller/launch/demo21_mogiros_arm_part1_mp1dials.launch.py @@ -0,0 +1,62 @@ +from launch import LaunchDescription +from launch.actions import DeclareLaunchArgument +from launch.substitutions import LaunchConfiguration, PythonExpression +from launch_ros.actions import Node + +def generate_launch_description(): + return LaunchDescription([ + DeclareLaunchArgument( + "repo_path", + default_value="/root/hand_controller", + description="Path (absolute) to hand_controller repo." + ), + DeclareLaunchArgument( + "blaze_target", + default_value="blaze_tflite", + description="blaze target implementation." + ), + DeclareLaunchArgument( + "blaze_model1", + default_value="palm_detection_lite.tflite", + description="Name of blaze detection model." + ), + DeclareLaunchArgument( + "blaze_model2", + default_value="hand_landmark_lite.tflite", + description="Name of blaze landmark model." + ), + DeclareLaunchArgument( + "verbose", + default_value="True", + description="Verbose mode." + ), + DeclareLaunchArgument( + "use_imshow", + default_value="False", + description="Enable OpenCV display." + ), + Node( + package='hand_controller', + executable='usbcam_publisher_node', + name="usbcam_publisher", + remappings=[("image_raw", "usbcam_image")] + ), + Node( + package='hand_controller', + executable='hand_controller_mp1dials_joints_node', + name="hand_controller", + parameters=[ + {"repo_path":LaunchConfiguration("repo_path")}, + {"blaze_target":LaunchConfiguration("blaze_target")}, + {"blaze_model1":LaunchConfiguration("blaze_model1")}, + {"blaze_model2":LaunchConfiguration("blaze_model2")}, + {"verbose":PythonExpression(['"', LaunchConfiguration('verbose'), '" == "True"'])}, + {"use_imshow":PythonExpression(['"', LaunchConfiguration('use_imshow'), '" == "True"'])} + ], + remappings=[ + ("image_raw", "usbcam_image"), + ("/arm_controller/joint_trajectory", "/arm_controller/joint_trajectory"), + ("/gripper_controller/joint_trajectory", "/gripper_controller/joint_trajectory") + ] + ) + ]) diff --git a/ros2_ws/install/hand_controller/share/hand_controller/launch/demo21_mogiros_arm_part2.launch.py b/ros2_ws/install/hand_controller/share/hand_controller/launch/demo21_mogiros_arm_part2.launch.py new file mode 100644 index 0000000..0fcf3cd --- /dev/null +++ b/ros2_ws/install/hand_controller/share/hand_controller/launch/demo21_mogiros_arm_part2.launch.py @@ -0,0 +1,294 @@ +import os +from launch import LaunchDescription +from launch.actions import DeclareLaunchArgument, IncludeLaunchDescription +from launch.conditions import IfCondition +from launch.launch_description_sources import PythonLaunchDescriptionSource +from launch.substitutions import LaunchConfiguration, PathJoinSubstitution, Command +from launch_ros.actions import Node +from ament_index_python.packages import get_package_share_directory + +def generate_launch_description(): + + pkg_bme_ros2_simple_arm = get_package_share_directory('bme_ros2_simple_arm') + + gazebo_models_path, ignore_last_dir = os.path.split(pkg_bme_ros2_simple_arm) + os.environ["GZ_SIM_RESOURCE_PATH"] += os.pathsep + gazebo_models_path + + viewer1_name_arg = DeclareLaunchArgument( + "viewer1_name", + default_value="Hand Controller", + description="Name of Image Viewer for hand_controller/annotations." + ) + viewer2_name_arg = DeclareLaunchArgument( + "viewer2_name", + default_value="Gripper Camera", + description="Name of Image Viewer for gripper mounted camera (in Gazebo)." + ) + viewer3_name_arg = DeclareLaunchArgument( + "viewer3_name", + default_value="Table Camera", + description="Name of Image Viewer for table mounted camera (in Gazebo)." + ) + + rviz_launch_arg = DeclareLaunchArgument( + 'rviz', default_value='true', + description='Open RViz' + ) + + rviz_config_arg = DeclareLaunchArgument( + 'rviz_config', default_value='rviz.rviz', + description='RViz config file' + ) + + world_arg = DeclareLaunchArgument( + 'world', default_value='world.sdf', + description='Name of the Gazebo world file to load' + ) + + model_arg = DeclareLaunchArgument( + 'model', default_value='mogi_arm.xacro', + description='Name of the URDF description to load' + ) + + x_arg = DeclareLaunchArgument( + 'x', default_value='0.0', + description='x coordinate of spawned robot' + ) + + y_arg = DeclareLaunchArgument( + 'y', default_value='0.0', + description='y coordinate of spawned robot' + ) + + z_arg = DeclareLaunchArgument( + 'z', default_value='1.04', + description='z coordinate of spawned robot' + ) + + yaw_arg = DeclareLaunchArgument( + 'yaw', default_value='0.0', + description='yaw angle of spawned robot' + ) + + sim_time_arg = DeclareLaunchArgument( + 'use_sim_time', default_value='True', + description='Flag to enable use_sim_time' + ) + + # Define the path to your URDF or Xacro file + urdf_file_path = PathJoinSubstitution([ + pkg_bme_ros2_simple_arm, # Replace with your package name + "urdf", + LaunchConfiguration('model') # Replace with your URDF or Xacro file + ]) + + gz_bridge_params_path = os.path.join( + get_package_share_directory('bme_ros2_simple_arm'), + 'config', + 'gz_bridge.yaml' + ) + + robot_controllers = PathJoinSubstitution( + [ + get_package_share_directory('bme_ros2_simple_arm'), + 'config', + 'controller_position.yaml', + ] + ) + + world_launch = IncludeLaunchDescription( + PythonLaunchDescriptionSource( + os.path.join(pkg_bme_ros2_simple_arm, 'launch', 'world.launch.py'), + ), + launch_arguments={ + 'world': LaunchConfiguration('world'), + }.items() + ) + + # Hand controller (most nodes are launch in *_part1.launch.py) + hand_controller_viewer_node = Node( + package='hand_controller', + executable='usbcam_subscriber_node', + name="hand_controller_annotations", + parameters=[ + {"viewer_name":LaunchConfiguration("viewer1_name")} + ], + remappings=[ + ("image_raw", "hand_controller/image_annotated") + ] + ) + gripper_camera_viewer_node = Node( + package='hand_controller', + executable='usbcam_subscriber_node', + name="gripper_camera_viewer", + parameters=[ + {"viewer_name":LaunchConfiguration("viewer2_name")} + ], + remappings=[ + ("image_raw", "gripper_camera/image") + ] + ) + table_camera_viewer_node = Node( + package='hand_controller', + executable='usbcam_subscriber_node', + name="table_camera_viewer", + parameters=[ + {"viewer_name":LaunchConfiguration("viewer3_name")} + ], + remappings=[ + ("image_raw", "table_camera/image") + ] + ) + + # Launch rviz + rviz_node = Node( + package='rviz2', + executable='rviz2', + arguments=['-d', PathJoinSubstitution([pkg_bme_ros2_simple_arm, 'rviz', LaunchConfiguration('rviz_config')])], + condition=IfCondition(LaunchConfiguration('rviz')), + parameters=[ + {'use_sim_time': LaunchConfiguration('use_sim_time')}, + ] + ) + + # Spawn the URDF model using the `/world//create` service + spawn_urdf_node = Node( + package="ros_gz_sim", + executable="create", + arguments=[ + "-name", "mogi_arm", + "-topic", "robot_description", + "-x", LaunchConfiguration('x'), "-y", LaunchConfiguration('y'), "-z", LaunchConfiguration('z'), "-Y", LaunchConfiguration('yaw') # Initial spawn position + ], + output="screen", + parameters=[ + {'use_sim_time': LaunchConfiguration('use_sim_time')}, + ] + ) + + # Node to bridge /cmd_vel and /odom + gz_bridge_node = Node( + package="ros_gz_bridge", + executable="parameter_bridge", + arguments=[ + '--ros-args', '-p', + f'config_file:={gz_bridge_params_path}' + ], + output="screen", + parameters=[ + {'use_sim_time': LaunchConfiguration('use_sim_time')}, + ] + ) + + # Node to bridge camera topics + gz_image_bridge_node = Node( + package="ros_gz_image", + executable="image_bridge", + arguments=[ + "/gripper_camera/image", + "/table_camera/image", + ], + output="screen", + parameters=[ + {'use_sim_time': LaunchConfiguration('use_sim_time'), + 'gripper_camera.image.compressed.jpeg_quality': 75, + 'table_camera.image.compressed.jpeg_quality': 75,}, + ], + ) + + # Relay node to republish camera_info to image/camera_info + relay_gripper_camera_info_node = Node( + package='topic_tools', + executable='relay', + name='relay_camera_info', + output='screen', + arguments=['gripper_camera/camera_info', 'gripper_camera/image/camera_info'], + parameters=[ + {'use_sim_time': LaunchConfiguration('use_sim_time')}, + ] + ) + + # Relay node to republish camera_info to image/camera_info + relay_table_camera_info_node = Node( + package='topic_tools', + executable='relay', + name='relay_camera_info', + output='screen', + arguments=['table_camera/camera_info', 'table_camera/image/camera_info'], + parameters=[ + {'use_sim_time': LaunchConfiguration('use_sim_time')}, + ] + ) + + robot_state_publisher_node = Node( + package='robot_state_publisher', + executable='robot_state_publisher', + name='robot_state_publisher', + output='screen', + parameters=[ + {'robot_description': Command(['xacro', ' ', urdf_file_path]), + 'use_sim_time': LaunchConfiguration('use_sim_time')}, + ], + remappings=[ + ('/tf', 'tf'), + ('/tf_static', 'tf_static') + ] + ) + joint_state_publisher_gui_node = Node( + package='joint_state_publisher_gui', + executable='joint_state_publisher_gui', + ) + + joint_state_broadcaster_spawner = Node( + package='controller_manager', + executable='spawner', + arguments=['joint_state_broadcaster'], + parameters=[ + {'use_sim_time': LaunchConfiguration('use_sim_time')}, + ] + ) + + joint_trajectory_controller_spawner = Node( + package='controller_manager', + executable='spawner', + arguments=[ + 'arm_controller', + 'gripper_controller', + '--param-file', + robot_controllers, + ], + parameters=[ + {'use_sim_time': LaunchConfiguration('use_sim_time')}, + ] + ) + + launchDescriptionObject = LaunchDescription() + + launchDescriptionObject.add_action(viewer1_name_arg) + launchDescriptionObject.add_action(viewer2_name_arg) + launchDescriptionObject.add_action(viewer3_name_arg) + launchDescriptionObject.add_action(rviz_launch_arg) + launchDescriptionObject.add_action(rviz_config_arg) + launchDescriptionObject.add_action(world_arg) + launchDescriptionObject.add_action(model_arg) + launchDescriptionObject.add_action(x_arg) + launchDescriptionObject.add_action(y_arg) + launchDescriptionObject.add_action(z_arg) + launchDescriptionObject.add_action(yaw_arg) + launchDescriptionObject.add_action(sim_time_arg) + launchDescriptionObject.add_action(world_launch) + launchDescriptionObject.add_action(hand_controller_viewer_node) + launchDescriptionObject.add_action(gripper_camera_viewer_node) + launchDescriptionObject.add_action(table_camera_viewer_node) + launchDescriptionObject.add_action(rviz_node) + launchDescriptionObject.add_action(spawn_urdf_node) + launchDescriptionObject.add_action(gz_bridge_node) + launchDescriptionObject.add_action(gz_image_bridge_node) + launchDescriptionObject.add_action(relay_gripper_camera_info_node) + launchDescriptionObject.add_action(relay_table_camera_info_node) + launchDescriptionObject.add_action(robot_state_publisher_node) + #launchDescriptionObject.add_action(joint_state_publisher_gui_node) + launchDescriptionObject.add_action(joint_state_broadcaster_spawner) + launchDescriptionObject.add_action(joint_trajectory_controller_spawner) + + return launchDescriptionObject diff --git a/ros2_ws/install/hand_controller/share/hand_controller/launch/demo31_mycobot_part1_asl.launch.py b/ros2_ws/install/hand_controller/share/hand_controller/launch/demo31_mycobot_part1_asl.launch.py new file mode 100644 index 0000000..33ba4f4 --- /dev/null +++ b/ros2_ws/install/hand_controller/share/hand_controller/launch/demo31_mycobot_part1_asl.launch.py @@ -0,0 +1,61 @@ +from launch import LaunchDescription +from launch.actions import DeclareLaunchArgument +from launch.substitutions import LaunchConfiguration, PythonExpression +from launch_ros.actions import Node + +def generate_launch_description(): + return LaunchDescription([ + DeclareLaunchArgument( + "repo_path", + default_value="/root/hand_controller", + description="Path (absolute) to hand_controller repo." + ), + DeclareLaunchArgument( + "blaze_target", + default_value="blaze_tflite", + description="blaze target implementation." + ), + DeclareLaunchArgument( + "blaze_model1", + default_value="palm_detection_lite.tflite", + description="Name of blaze detection model." + ), + DeclareLaunchArgument( + "blaze_model2", + default_value="hand_landmark_lite.tflite", + description="Name of blaze landmark model." + ), + DeclareLaunchArgument( + "verbose", + default_value="True", + description="Verbose mode." + ), + DeclareLaunchArgument( + "use_imshow", + default_value="False", + description="Enable OpenCV display." + ), + Node( + package='hand_controller', + executable='usbcam_publisher_node', + name="usbcam_publisher", + remappings=[("image_raw", "usbcam_image")] + ), + Node( + package='hand_controller', + executable='hand_controller_asl_pose_node', + name="hand_controller", + parameters=[ + {"repo_path":LaunchConfiguration("repo_path")}, + {"blaze_target":LaunchConfiguration("blaze_target")}, + {"blaze_model1":LaunchConfiguration("blaze_model1")}, + {"blaze_model2":LaunchConfiguration("blaze_model2")}, + {"verbose":PythonExpression(['"', LaunchConfiguration('verbose'), '" == "True"'])}, + {"use_imshow":PythonExpression(['"', LaunchConfiguration('use_imshow'), '" == "True"'])} + ], + remappings=[ + ("image_raw", "usbcam_image"), + ("hand_controller/target_pose", "target_pose") + ] + ) + ]) diff --git a/ros2_ws/install/hand_controller/share/hand_controller/launch/demo31_mycobot_part1_mp1dials.launch.py b/ros2_ws/install/hand_controller/share/hand_controller/launch/demo31_mycobot_part1_mp1dials.launch.py new file mode 100644 index 0000000..3f3a9fb --- /dev/null +++ b/ros2_ws/install/hand_controller/share/hand_controller/launch/demo31_mycobot_part1_mp1dials.launch.py @@ -0,0 +1,61 @@ +from launch import LaunchDescription +from launch.actions import DeclareLaunchArgument +from launch.substitutions import LaunchConfiguration, PythonExpression +from launch_ros.actions import Node + +def generate_launch_description(): + return LaunchDescription([ + DeclareLaunchArgument( + "repo_path", + default_value="/root/hand_controller", + description="Path (absolute) to hand_controller repo." + ), + DeclareLaunchArgument( + "blaze_target", + default_value="blaze_tflite", + description="blaze target implementation." + ), + DeclareLaunchArgument( + "blaze_model1", + default_value="palm_detection_lite.tflite", + description="Name of blaze detection model." + ), + DeclareLaunchArgument( + "blaze_model2", + default_value="hand_landmark_lite.tflite", + description="Name of blaze landmark model." + ), + DeclareLaunchArgument( + "verbose", + default_value="True", + description="Verbose mode." + ), + DeclareLaunchArgument( + "use_imshow", + default_value="False", + description="Enable OpenCV display." + ), + Node( + package='hand_controller', + executable='usbcam_publisher_node', + name="usbcam_publisher", + remappings=[("image_raw", "usbcam_image")] + ), + Node( + package='hand_controller', + executable='hand_controller_mp1dials_pose_node', + name="hand_controller", + parameters=[ + {"repo_path":LaunchConfiguration("repo_path")}, + {"blaze_target":LaunchConfiguration("blaze_target")}, + {"blaze_model1":LaunchConfiguration("blaze_model1")}, + {"blaze_model2":LaunchConfiguration("blaze_model2")}, + {"verbose":PythonExpression(['"', LaunchConfiguration('verbose'), '" == "True"'])}, + {"use_imshow":PythonExpression(['"', LaunchConfiguration('use_imshow'), '" == "True"'])} + ], + remappings=[ + ("image_raw", "usbcam_image"), + ("hand_controller/target_pose", "target_pose") + ] + ) + ]) diff --git a/ros2_ws/install/hand_controller/share/hand_controller/launch/demo31_mycobot_part2.launch.py b/ros2_ws/install/hand_controller/share/hand_controller/launch/demo31_mycobot_part2.launch.py new file mode 100644 index 0000000..e5cf128 --- /dev/null +++ b/ros2_ws/install/hand_controller/share/hand_controller/launch/demo31_mycobot_part2.launch.py @@ -0,0 +1,30 @@ +from launch import LaunchDescription +from launch.actions import DeclareLaunchArgument +from launch.substitutions import LaunchConfiguration +from launch_ros.actions import Node + +def generate_launch_description(): + return LaunchDescription([ + DeclareLaunchArgument( + "viewer1_name", + default_value="Hand Controller", + description="Name of Image Viewer for hand_controller/annotations." + ), + Node( + package='hand_controller', + executable='usbcam_subscriber_node', + name="hand_controller_annotations", + parameters=[ + {"viewer_name":LaunchConfiguration("viewer1_name")} + ], + remappings=[ + ("image_raw", "hand_controller/image_annotated") + ] + ), + Node( + package='asl_moveit_demos', + executable='pose_controlled_moveit', + name="my_pose_controller", + remappings=[("target_pose", "target_pose")] + ) + ]) diff --git a/ros2_ws/install/hand_controller/share/hand_controller/launch/hand_controller_asl_mogiros_arm.launch.py b/ros2_ws/install/hand_controller/share/hand_controller/launch/hand_controller_asl_mogiros_arm.launch.py new file mode 100644 index 0000000..830e798 --- /dev/null +++ b/ros2_ws/install/hand_controller/share/hand_controller/launch/hand_controller_asl_mogiros_arm.launch.py @@ -0,0 +1,289 @@ +import os +from launch import LaunchDescription +from launch.actions import DeclareLaunchArgument, IncludeLaunchDescription +from launch.conditions import IfCondition +from launch.launch_description_sources import PythonLaunchDescriptionSource +from launch.substitutions import LaunchConfiguration, PathJoinSubstitution, Command +from launch_ros.actions import Node +from ament_index_python.packages import get_package_share_directory + +def generate_launch_description(): + + pkg_bme_ros2_simple_arm = get_package_share_directory('bme_ros2_simple_arm') + + gazebo_models_path, ignore_last_dir = os.path.split(pkg_bme_ros2_simple_arm) + os.environ["GZ_SIM_RESOURCE_PATH"] += os.pathsep + gazebo_models_path + + repo_path_arg = DeclareLaunchArgument( + "repo_path", + default_value="/root/hand_controller", + description="Path (absolute) to hand_controller repo." + ) + blaze_target_arg = DeclareLaunchArgument( + "blaze_target", + default_value="blaze_tflite", + description="blaze target implementation." + ) + blaze_model1_arg = DeclareLaunchArgument( + "blaze_model1", + default_value="palm_detection_lite.tflite", + description="Name of blaze detection model." + ) + blaze_model2_arg = DeclareLaunchArgument( + "blaze_model2", + default_value="hand_landmark_lite.tflite", + description="Name of blaze landmark model." + ) + + rviz_launch_arg = DeclareLaunchArgument( + 'rviz', default_value='true', + description='Open RViz' + ) + + rviz_config_arg = DeclareLaunchArgument( + 'rviz_config', default_value='rviz.rviz', + description='RViz config file' + ) + + world_arg = DeclareLaunchArgument( + 'world', default_value='world.sdf', + description='Name of the Gazebo world file to load' + ) + + model_arg = DeclareLaunchArgument( + 'model', default_value='mogi_arm.xacro', + description='Name of the URDF description to load' + ) + + x_arg = DeclareLaunchArgument( + 'x', default_value='0.0', + description='x coordinate of spawned robot' + ) + + y_arg = DeclareLaunchArgument( + 'y', default_value='0.0', + description='y coordinate of spawned robot' + ) + + z_arg = DeclareLaunchArgument( + 'z', default_value='1.04', + description='z coordinate of spawned robot' + ) + + yaw_arg = DeclareLaunchArgument( + 'yaw', default_value='0.0', + description='yaw angle of spawned robot' + ) + + sim_time_arg = DeclareLaunchArgument( + 'use_sim_time', default_value='True', + description='Flag to enable use_sim_time' + ) + + # Define the path to your URDF or Xacro file + urdf_file_path = PathJoinSubstitution([ + pkg_bme_ros2_simple_arm, # Replace with your package name + "urdf", + LaunchConfiguration('model') # Replace with your URDF or Xacro file + ]) + + gz_bridge_params_path = os.path.join( + get_package_share_directory('bme_ros2_simple_arm'), + 'config', + 'gz_bridge.yaml' + ) + + robot_controllers = PathJoinSubstitution( + [ + get_package_share_directory('bme_ros2_simple_arm'), + 'config', + 'controller_position.yaml', + ] + ) + + world_launch = IncludeLaunchDescription( + PythonLaunchDescriptionSource( + os.path.join(pkg_bme_ros2_simple_arm, 'launch', 'world.launch.py'), + ), + launch_arguments={ + 'world': LaunchConfiguration('world'), + }.items() + ) + + # Launch ASL controller + usbcam_publisher_node = Node( + package='v4l2_camera', + executable='v4l2_camera_node', + name="usbcam_publisher", + remappings=[("image_raw", "usbcam_image")] + ) + hand_controller_node = Node( + package='hand_controller', + executable='hand_controller_asl_joints_node', + name="hand_controller", + parameters=[ + {"repo_path":LaunchConfiguration("repo_path")}, + {"blaze_target":LaunchConfiguration("blaze_target")}, + {"blaze_model1":LaunchConfiguration("blaze_model1")}, + {"blaze_model2":LaunchConfiguration("blaze_model2")}, + {"verbose":True}, + {"use_imshow":True} + ], + remappings=[ + ("image_raw", "usbcam_image"), + ("/arm_controller/joint_trajectory", "/arm_controller/joint_trajectory") + ] + ) + + # Launch rviz + rviz_node = Node( + package='rviz2', + executable='rviz2', + arguments=['-d', PathJoinSubstitution([pkg_bme_ros2_simple_arm, 'rviz', LaunchConfiguration('rviz_config')])], + condition=IfCondition(LaunchConfiguration('rviz')), + parameters=[ + {'use_sim_time': LaunchConfiguration('use_sim_time')}, + ] + ) + + # Spawn the URDF model using the `/world//create` service + spawn_urdf_node = Node( + package="ros_gz_sim", + executable="create", + arguments=[ + "-name", "mogi_arm", + "-topic", "robot_description", + "-x", LaunchConfiguration('x'), "-y", LaunchConfiguration('y'), "-z", LaunchConfiguration('z'), "-Y", LaunchConfiguration('yaw') # Initial spawn position + ], + output="screen", + parameters=[ + {'use_sim_time': LaunchConfiguration('use_sim_time')}, + ] + ) + + # Node to bridge /cmd_vel and /odom + gz_bridge_node = Node( + package="ros_gz_bridge", + executable="parameter_bridge", + arguments=[ + '--ros-args', '-p', + f'config_file:={gz_bridge_params_path}' + ], + output="screen", + parameters=[ + {'use_sim_time': LaunchConfiguration('use_sim_time')}, + ] + ) + + # Node to bridge camera topics + gz_image_bridge_node = Node( + package="ros_gz_image", + executable="image_bridge", + arguments=[ + "/gripper_camera/image", + "/table_camera/image", + ], + output="screen", + parameters=[ + {'use_sim_time': LaunchConfiguration('use_sim_time'), + 'gripper_camera.image.compressed.jpeg_quality': 75, + 'table_camera.image.compressed.jpeg_quality': 75,}, + ], + ) + + # Relay node to republish camera_info to image/camera_info + relay_gripper_camera_info_node = Node( + package='topic_tools', + executable='relay', + name='relay_camera_info', + output='screen', + arguments=['gripper_camera/camera_info', 'gripper_camera/image/camera_info'], + parameters=[ + {'use_sim_time': LaunchConfiguration('use_sim_time')}, + ] + ) + + # Relay node to republish camera_info to image/camera_info + relay_table_camera_info_node = Node( + package='topic_tools', + executable='relay', + name='relay_camera_info', + output='screen', + arguments=['table_camera/camera_info', 'table_camera/image/camera_info'], + parameters=[ + {'use_sim_time': LaunchConfiguration('use_sim_time')}, + ] + ) + + robot_state_publisher_node = Node( + package='robot_state_publisher', + executable='robot_state_publisher', + name='robot_state_publisher', + output='screen', + parameters=[ + {'robot_description': Command(['xacro', ' ', urdf_file_path]), + 'use_sim_time': LaunchConfiguration('use_sim_time')}, + ], + remappings=[ + ('/tf', 'tf'), + ('/tf_static', 'tf_static') + ] + ) + joint_state_publisher_gui_node = Node( + package='joint_state_publisher_gui', + executable='joint_state_publisher_gui', + ) + + joint_state_broadcaster_spawner = Node( + package='controller_manager', + executable='spawner', + arguments=['joint_state_broadcaster'], + parameters=[ + {'use_sim_time': LaunchConfiguration('use_sim_time')}, + ] + ) + + joint_trajectory_controller_spawner = Node( + package='controller_manager', + executable='spawner', + arguments=[ + 'arm_controller', + 'gripper_controller', + '--param-file', + robot_controllers, + ], + parameters=[ + {'use_sim_time': LaunchConfiguration('use_sim_time')}, + ] + ) + + launchDescriptionObject = LaunchDescription() + + launchDescriptionObject.add_action(repo_path_arg) + launchDescriptionObject.add_action(blaze_target_arg) + launchDescriptionObject.add_action(blaze_model1_arg) + launchDescriptionObject.add_action(blaze_model2_arg) + launchDescriptionObject.add_action(rviz_launch_arg) + launchDescriptionObject.add_action(rviz_config_arg) + launchDescriptionObject.add_action(world_arg) + launchDescriptionObject.add_action(model_arg) + launchDescriptionObject.add_action(x_arg) + launchDescriptionObject.add_action(y_arg) + launchDescriptionObject.add_action(z_arg) + launchDescriptionObject.add_action(yaw_arg) + launchDescriptionObject.add_action(sim_time_arg) + launchDescriptionObject.add_action(world_launch) + launchDescriptionObject.add_action(usbcam_publisher_node) + launchDescriptionObject.add_action(hand_controller_node) + launchDescriptionObject.add_action(rviz_node) + launchDescriptionObject.add_action(spawn_urdf_node) + launchDescriptionObject.add_action(gz_bridge_node) + launchDescriptionObject.add_action(gz_image_bridge_node) + launchDescriptionObject.add_action(relay_gripper_camera_info_node) + launchDescriptionObject.add_action(relay_table_camera_info_node) + launchDescriptionObject.add_action(robot_state_publisher_node) + #launchDescriptionObject.add_action(joint_state_publisher_gui_node) + launchDescriptionObject.add_action(joint_state_broadcaster_spawner) + launchDescriptionObject.add_action(joint_trajectory_controller_spawner) + + return launchDescriptionObject diff --git a/ros2_ws/install/hand_controller/share/hand_controller/launch/hand_controller_asl_mogiros_car.launch.py b/ros2_ws/install/hand_controller/share/hand_controller/launch/hand_controller_asl_mogiros_car.launch.py new file mode 100644 index 0000000..7d35a7d --- /dev/null +++ b/ros2_ws/install/hand_controller/share/hand_controller/launch/hand_controller_asl_mogiros_car.launch.py @@ -0,0 +1,176 @@ +import os +from launch import LaunchDescription +from launch.actions import DeclareLaunchArgument, IncludeLaunchDescription +from launch.conditions import IfCondition +from launch.launch_description_sources import PythonLaunchDescriptionSource +from launch.substitutions import LaunchConfiguration, PathJoinSubstitution, Command +from launch_ros.actions import Node +from ament_index_python.packages import get_package_share_directory + +def generate_launch_description(): + + pkg_bme_gazebo_basics = get_package_share_directory('bme_gazebo_basics') + + gazebo_models_path, ignore_last_dir = os.path.split(pkg_bme_gazebo_basics) + os.environ["GZ_SIM_RESOURCE_PATH"] += os.pathsep + gazebo_models_path + + repo_path_arg = DeclareLaunchArgument( + "repo_path", + default_value="/root/hand_controller", + description="Path (absolute) to hand_controller repo." + ) + blaze_target_arg = DeclareLaunchArgument( + "blaze_target", + default_value="blaze_tflite", + description="blaze target implementation." + ) + blaze_model1_arg = DeclareLaunchArgument( + "blaze_model1", + default_value="palm_detection_lite.tflite", + description="Name of blaze detection model." + ) + blaze_model2_arg = DeclareLaunchArgument( + "blaze_model2", + default_value="hand_landmark_lite.tflite", + description="Name of blaze landmark model." + ) + + rviz_launch_arg = DeclareLaunchArgument( + 'rviz', default_value='true', + description='Open RViz.' + ) + + world_arg = DeclareLaunchArgument( + 'world', default_value='world.sdf', + description='Name of the Gazebo world file to load' + ) + + model_arg = DeclareLaunchArgument( + 'model', default_value='mogi_bot.urdf', + description='Name of the URDF description to load' + ) + + # Define the path to your URDF or Xacro file + urdf_file_path = PathJoinSubstitution([ + pkg_bme_gazebo_basics, # Replace with your package name + "urdf", + LaunchConfiguration('model') # Replace with your URDF or Xacro file + ]) + + world_launch = IncludeLaunchDescription( + PythonLaunchDescriptionSource( + os.path.join(pkg_bme_gazebo_basics, 'launch', 'world.launch.py'), + ), + launch_arguments={ + 'world': LaunchConfiguration('world'), + }.items() + ) + + # Launch ASL controller + usbcam_publisher_node = Node( + package='v4l2_camera', + executable='v4l2_camera_node', + name="usbcam_publisher", + remappings=[("image_raw", "usbcam_image")] + ) + hand_controller_node = Node( + package='hand_controller', + executable='hand_controller_asl_twist_node', + name="hand_controller", + parameters=[ + {"repo_path":LaunchConfiguration("repo_path")}, + {"blaze_target":LaunchConfiguration("blaze_target")}, + {"blaze_model1":LaunchConfiguration("blaze_model1")}, + {"blaze_model2":LaunchConfiguration("blaze_model2")}, + {"verbose":True}, + {"use_imshow":True} + ], + remappings=[ + ("image_raw", "usbcam_image"), + ("hand_controller/cmd_vel", "cmd_vel") + ] + ) + + # Launch rviz + rviz_node = Node( + package='rviz2', + executable='rviz2', + arguments=['-d', os.path.join(pkg_bme_gazebo_basics, 'rviz', 'rviz.rviz')], + condition=IfCondition(LaunchConfiguration('rviz')), + parameters=[ + {'use_sim_time': True}, + ] + ) + + # Spawn the URDF model using the `/world//create` service + spawn_urdf_node = Node( + package="ros_gz_sim", + executable="create", + arguments=[ + "-name", "my_robot", + "-topic", "robot_description", + "-x", "0.0", "-y", "0.0", "-z", "0.5", "-Y", "0.0" # Initial spawn position + ], + output="screen", + parameters=[ + {'use_sim_time': True}, + ] + ) + + robot_state_publisher_node = Node( + package='robot_state_publisher', + executable='robot_state_publisher', + name='robot_state_publisher', + output='screen', + parameters=[ + {'robot_description': Command(['xacro', ' ', urdf_file_path]), + 'use_sim_time': True}, + ], + remappings=[ + ('/tf', 'tf'), + ('/tf_static', 'tf_static') + ] + ) + + # Node to bridge messages like /cmd_vel and /odom + gz_bridge_node = Node( + package="ros_gz_bridge", + executable="parameter_bridge", + arguments=[ + "/clock@rosgraph_msgs/msg/Clock[gz.msgs.Clock", + "/cmd_vel@geometry_msgs/msg/Twist@gz.msgs.Twist", + "/odom@nav_msgs/msg/Odometry@gz.msgs.Odometry", + "/joint_states@sensor_msgs/msg/JointState@gz.msgs.Model", + "/tf@tf2_msgs/msg/TFMessage@gz.msgs.Pose_V" + ], + output="screen", + parameters=[ + {'use_sim_time': True}, + ] + ) + + trajectory_node = Node( + package='mogi_trajectory_server', + executable='mogi_trajectory_server', + name='mogi_trajectory_server', + ) + + launchDescriptionObject = LaunchDescription() + + launchDescriptionObject.add_action(repo_path_arg) + launchDescriptionObject.add_action(blaze_target_arg) + launchDescriptionObject.add_action(blaze_model1_arg) + launchDescriptionObject.add_action(blaze_model2_arg) + launchDescriptionObject.add_action(rviz_launch_arg) + launchDescriptionObject.add_action(world_arg) + launchDescriptionObject.add_action(model_arg) + launchDescriptionObject.add_action(world_launch) + launchDescriptionObject.add_action(usbcam_publisher_node) + launchDescriptionObject.add_action(hand_controller_node) + launchDescriptionObject.add_action(rviz_node) + launchDescriptionObject.add_action(spawn_urdf_node) + launchDescriptionObject.add_action(robot_state_publisher_node) + launchDescriptionObject.add_action(gz_bridge_node) + launchDescriptionObject.add_action(trajectory_node) + + return launchDescriptionObject diff --git a/ros2_ws/install/hand_controller/share/hand_controller/launch/hand_controller_asl_mycobot.launch.py b/ros2_ws/install/hand_controller/share/hand_controller/launch/hand_controller_asl_mycobot.launch.py new file mode 100644 index 0000000..619ebb5 --- /dev/null +++ b/ros2_ws/install/hand_controller/share/hand_controller/launch/hand_controller_asl_mycobot.launch.py @@ -0,0 +1,57 @@ +from launch import LaunchDescription +from launch.actions import DeclareLaunchArgument +from launch.substitutions import LaunchConfiguration +from launch_ros.actions import Node + +def generate_launch_description(): + return LaunchDescription([ + DeclareLaunchArgument( + "repo_path", + default_value="/root/hand_controller", + description="Path (absolute) to hand_controller repo." + ), + DeclareLaunchArgument( + "blaze_target", + default_value="blaze_tflite", + description="blaze target implementation." + ), + DeclareLaunchArgument( + "blaze_model1", + default_value="palm_detection_lite.tflite", + description="Name of blaze detection model." + ), + DeclareLaunchArgument( + "blaze_model2", + default_value="hand_landmark_lite.tflite", + description="Name of blaze landmark model." + ), + Node( + package='v4l2_camera', + executable='v4l2_camera_node', + name="usbcam_publisher", + remappings=[("image_raw", "usbcam_image")] + ), + Node( + package='hand_controller', + executable='hand_controller_asl_pose_node', + name="hand_controller", + parameters=[ + {"repo_path":LaunchConfiguration("repo_path")}, + {"blaze_target":LaunchConfiguration("blaze_target")}, + {"blaze_model1":LaunchConfiguration("blaze_model1")}, + {"blaze_model2":LaunchConfiguration("blaze_model2")}, + {"verbose":True}, + {"use_imshow":True} + ], + remappings=[ + ("image_raw", "usbcam_image"), + ("hand_controller/target_pose", "target_pose") + ] + ), + Node( + package='asl_moveit_demos', + executable='pose_controlled_moveit', + name="my_pose_controller", + remappings=[("target_pose", "target_pose")] + ) + ]) diff --git a/ros2_ws/install/hand_controller/share/hand_controller/launch/hand_controller_asl_rosmaster.launch.py b/ros2_ws/install/hand_controller/share/hand_controller/launch/hand_controller_asl_rosmaster.launch.py new file mode 100644 index 0000000..3b33d0b --- /dev/null +++ b/ros2_ws/install/hand_controller/share/hand_controller/launch/hand_controller_asl_rosmaster.launch.py @@ -0,0 +1,360 @@ +#!/usr/bin/env python3 +""" +Launch Gazebo simulation with a robot. + +This launch file sets up a complete ROS 2 simulation environment with Gazebo for +a Yahboom ROSMASTER robot: https://github.com/YahboomTechnology + +:author: Addison Sears-Collins +:date: November 21, 2024 +""" + +import os +from launch import LaunchDescription +from launch.actions import ( + AppendEnvironmentVariable, + DeclareLaunchArgument, + IncludeLaunchDescription +) +from launch.conditions import IfCondition +from launch.launch_description_sources import PythonLaunchDescriptionSource +from launch.substitutions import LaunchConfiguration, PathJoinSubstitution, PythonExpression +from launch_ros.actions import Node +from launch_ros.substitutions import FindPackageShare + + +def generate_launch_description(): + """ + Generate a launch description for the Gazebo simulation. + + This function sets up all necessary parameters, paths, and nodes required to launch + the Gazebo simulation with a robot. It handles: + 1. Setting up package paths and constants + 2. Declaring launch arguments for robot configuration + 3. Setting up the Gazebo environment + 4. Spawning the robot in simulation + + Returns: + LaunchDescription: A complete launch description for the simulation + """ + # Constants for paths to different files and folders + package_name_gazebo = 'yahboom_rosmaster_gazebo' + package_name_description = 'yahboom_rosmaster_description' + package_name_bringup = 'yahboom_rosmaster_bringup' + + default_robot_name = 'rosmaster_x3' + gazebo_models_path = 'models' + default_world_file = 'empty.world' + gazebo_worlds_path = 'worlds' + ros_gz_bridge_config_file_path = 'config/ros_gz_bridge.yaml' + rviz_config_filename = 'yahboom_rosmaster_gazebo_sim.rviz' + + # Set the path to different files and folders + pkg_ros_gz_sim = FindPackageShare(package='ros_gz_sim').find('ros_gz_sim') + pkg_share_gazebo = FindPackageShare(package=package_name_gazebo).find(package_name_gazebo) + pkg_share_description = FindPackageShare( + package=package_name_description).find(package_name_description) + pkg_share_bringup = FindPackageShare(package=package_name_bringup).find(package_name_bringup) + + default_ros_gz_bridge_config_file_path = os.path.join( + pkg_share_gazebo, ros_gz_bridge_config_file_path) + default_rviz_config_path = PathJoinSubstitution( + [pkg_share_gazebo, 'rviz', rviz_config_filename]) + gazebo_models_path = os.path.join(pkg_share_gazebo, gazebo_models_path) + + # Launch configuration variables + repo_path_arg = DeclareLaunchArgument( + "repo_path", + default_value="/root/hand_controller", + description="Path (absolute) to hand_controller repo." + ) + blaze_target_arg = DeclareLaunchArgument( + "blaze_target", + default_value="blaze_tflite", + description="blaze target implementation." + ) + blaze_model1_arg = DeclareLaunchArgument( + "blaze_model1", + default_value="palm_detection_lite.tflite", + description="Name of blaze detection model." + ) + blaze_model2_arg = DeclareLaunchArgument( + "blaze_model2", + default_value="hand_landmark_lite.tflite", + description="Name of blaze landmark model." + ) + # + enable_odom_tf = LaunchConfiguration('enable_odom_tf') + headless = LaunchConfiguration('headless') + jsp_gui = LaunchConfiguration('jsp_gui') + load_controllers = LaunchConfiguration('load_controllers') + robot_name = LaunchConfiguration('robot_name') + rviz_config_file = LaunchConfiguration('rviz_config_file') + use_rviz = LaunchConfiguration('use_rviz') + use_gazebo = LaunchConfiguration('use_gazebo') + use_robot_state_pub = LaunchConfiguration('use_robot_state_pub') + use_sim_time = LaunchConfiguration('use_sim_time') + world_file = LaunchConfiguration('world_file') + + world_path = PathJoinSubstitution([ + pkg_share_gazebo, + gazebo_worlds_path, + world_file + ]) + + # Set the pose configuration variables + x = LaunchConfiguration('x') + y = LaunchConfiguration('y') + z = LaunchConfiguration('z') + roll = LaunchConfiguration('roll') + pitch = LaunchConfiguration('pitch') + yaw = LaunchConfiguration('yaw') + + # Declare the launch arguments + declare_enable_odom_tf_cmd = DeclareLaunchArgument( + name='enable_odom_tf', + default_value='true', + choices=['true', 'false'], + description='Whether to enable odometry transform broadcasting via ROS 2 Control') + + declare_headless_cmd = DeclareLaunchArgument( + name='headless', + default_value='False', + description='Whether to execute gzclient (visualization)') + + declare_robot_name_cmd = DeclareLaunchArgument( + name='robot_name', + default_value=default_robot_name, + description='The name for the robot') + + declare_rviz_config_file_cmd = DeclareLaunchArgument( + name='rviz_config_file', + default_value=default_rviz_config_path, + description='Full path to the RVIZ config file to use') + + declare_load_controllers_cmd = DeclareLaunchArgument( + name='load_controllers', + default_value='true', + description='Flag to enable loading of ROS 2 controllers') + + declare_use_robot_state_pub_cmd = DeclareLaunchArgument( + name='use_robot_state_pub', + default_value='true', + description='Flag to enable robot state publisher') + + # GUI and visualization arguments + declare_jsp_gui_cmd = DeclareLaunchArgument( + name='jsp_gui', + default_value='false', + description='Flag to enable joint_state_publisher_gui') + + declare_use_rviz_cmd = DeclareLaunchArgument( + name='use_rviz', + default_value='true', + description='Flag to enable RViz') + + declare_use_gazebo_cmd = DeclareLaunchArgument( + name='use_gazebo', + default_value='true', + description='Flag to enable Gazebo') + + declare_use_sim_time_cmd = DeclareLaunchArgument( + name='use_sim_time', + default_value='true', + description='Use simulation (Gazebo) clock if true') + + declare_world_cmd = DeclareLaunchArgument( + name='world_file', + default_value=default_world_file, + description='World file name (e.g., empty.world, house.world, pick_and_place_demo.world)') + + # Pose arguments + declare_x_cmd = DeclareLaunchArgument( + name='x', + default_value='0.0', + description='x component of initial position, meters') + + declare_y_cmd = DeclareLaunchArgument( + name='y', + default_value='0.0', + description='y component of initial position, meters') + + declare_z_cmd = DeclareLaunchArgument( + name='z', + default_value='0.05', + description='z component of initial position, meters') + + declare_roll_cmd = DeclareLaunchArgument( + name='roll', + default_value='0.0', + description='roll angle of initial orientation, radians') + + declare_pitch_cmd = DeclareLaunchArgument( + name='pitch', + default_value='0.0', + description='pitch angle of initial orientation, radians') + + declare_yaw_cmd = DeclareLaunchArgument( + name='yaw', + default_value='0.0', + description='yaw angle of initial orientation, radians') + + # Include Robot State Publisher launch file if enabled + robot_state_publisher_cmd = IncludeLaunchDescription( + PythonLaunchDescriptionSource([ + os.path.join(pkg_share_description, 'launch', 'robot_state_publisher.launch.py') + ]), + launch_arguments={ + 'enable_odom_tf': enable_odom_tf, + 'jsp_gui': jsp_gui, + 'rviz_config_file': rviz_config_file, + 'use_rviz': use_rviz, + 'use_gazebo': use_gazebo, + 'use_sim_time': use_sim_time + }.items(), + condition=IfCondition(use_robot_state_pub) + ) + + # Include ROS 2 Controllers launch file if enabled + load_controllers_cmd = IncludeLaunchDescription( + PythonLaunchDescriptionSource([ + os.path.join(pkg_share_bringup, 'launch', 'load_ros2_controllers.launch.py') + ]), + launch_arguments={ + 'use_sim_time': use_sim_time + }.items(), + condition=IfCondition(load_controllers) + ) + + # Set Gazebo model path + set_env_vars_resources = AppendEnvironmentVariable( + 'GZ_SIM_RESOURCE_PATH', + gazebo_models_path) + + # Start Gazebo + start_gazebo_server_cmd = IncludeLaunchDescription( + PythonLaunchDescriptionSource( + os.path.join(pkg_ros_gz_sim, 'launch', 'gz_sim.launch.py')), + launch_arguments=[('gz_args', [' -r -s -v 4 ', world_path])]) + + # Start Gazebo client (GUI) if not headless + start_gazebo_client_cmd = IncludeLaunchDescription( + PythonLaunchDescriptionSource( + os.path.join(pkg_ros_gz_sim, 'launch', 'gz_sim.launch.py')), + launch_arguments={'gz_args': ['-g ']}.items(), + condition=IfCondition(PythonExpression(['not ', headless]))) + + # Launch ASL controller + usbcam_publisher_node = Node( + package='v4l2_camera', + executable='v4l2_camera_node', + name="usbcam_publisher", + remappings=[("image_raw", "usbcam_image")] + ) + hand_controller_node = Node( + package='hand_controller', + executable='hand_controller_asl_twist_node', + name="hand_controller", + parameters=[ + {"repo_path":LaunchConfiguration("repo_path")}, + {"blaze_target":LaunchConfiguration("blaze_target")}, + {"blaze_model1":LaunchConfiguration("blaze_model1")}, + {"blaze_model2":LaunchConfiguration("blaze_model2")}, + {"verbose":True}, + {"use_imshow":True} + ], + remappings=[ + ("image_raw", "usbcam_image"), + ("hand_controller/cmd_vel", "cmd_vel") + ] + ) + twist_stamper_node = Node( + package='twist_stamper', + executable='twist_stamper', + remappings=[("cmd_vel_in", "cmd_vel"),("cmd_vel_out", "mecanum_drive_controller/cmd_vel")] + ) + + # Bridge ROS topics and Gazebo messages for establishing communication + start_gazebo_ros_bridge_cmd = Node( + package='ros_gz_bridge', + executable='parameter_bridge', + parameters=[{ + 'config_file': default_ros_gz_bridge_config_file_path, + }], + output='screen') + + # Includes optimizations to minimize latency and bandwidth when streaming image data + start_gazebo_ros_image_bridge_cmd = Node( + package='ros_gz_image', + executable='image_bridge', + arguments=[ + '/cam_1/image' + ], + remappings=[ + ('/cam_1/image', '/cam_1/color/image_raw') + ]) + + # Spawn the robot + start_gazebo_ros_spawner_cmd = Node( + package='ros_gz_sim', + executable='create', + output='screen', + arguments=[ + '-topic', '/robot_description', + '-name', robot_name, + '-allow_renaming', 'true', + '-x', x, + '-y', y, + '-z', z, + '-R', roll, + '-P', pitch, + '-Y', yaw + ]) + + # Create the launch description and populate + ld = LaunchDescription() + + # Declar parameters for the ASL controller + ld.add_action(repo_path_arg) + ld.add_action(blaze_target_arg) + ld.add_action(blaze_model1_arg) + ld.add_action(blaze_model2_arg) + + # Declare the launch options + ld.add_action(declare_enable_odom_tf_cmd) + ld.add_action(declare_headless_cmd) + ld.add_action(declare_robot_name_cmd) + ld.add_action(declare_rviz_config_file_cmd) + ld.add_action(declare_jsp_gui_cmd) + ld.add_action(declare_load_controllers_cmd) + ld.add_action(declare_use_rviz_cmd) + ld.add_action(declare_use_gazebo_cmd) + ld.add_action(declare_use_robot_state_pub_cmd) + ld.add_action(declare_use_sim_time_cmd) + ld.add_action(declare_world_cmd) + + # Add pose arguments + ld.add_action(declare_x_cmd) + ld.add_action(declare_y_cmd) + ld.add_action(declare_z_cmd) + ld.add_action(declare_roll_cmd) + ld.add_action(declare_pitch_cmd) + ld.add_action(declare_yaw_cmd) + + # Add the actions to the launch description + ld.add_action(set_env_vars_resources) + ld.add_action(robot_state_publisher_cmd) + ld.add_action(load_controllers_cmd) + ld.add_action(start_gazebo_server_cmd) + ld.add_action(start_gazebo_client_cmd) + + # Launch ASL controller + ld.add_action(usbcam_publisher_node) + ld.add_action(hand_controller_node) + ld.add_action(twist_stamper_node) + + ld.add_action(start_gazebo_ros_bridge_cmd) + ld.add_action(start_gazebo_ros_image_bridge_cmd) + ld.add_action(start_gazebo_ros_spawner_cmd) + + return ld diff --git a/ros2_ws/install/hand_controller/share/hand_controller/launch/hand_controller_asl_turtlesim.launch.py b/ros2_ws/install/hand_controller/share/hand_controller/launch/hand_controller_asl_turtlesim.launch.py new file mode 100644 index 0000000..77dcfa2 --- /dev/null +++ b/ros2_ws/install/hand_controller/share/hand_controller/launch/hand_controller_asl_turtlesim.launch.py @@ -0,0 +1,80 @@ +from launch import LaunchDescription +from launch.actions import DeclareLaunchArgument +from launch.substitutions import LaunchConfiguration +from launch_ros.actions import Node + +def generate_launch_description(): + return LaunchDescription([ + DeclareLaunchArgument( + "repo_path", + default_value="/root/hand_controller", + description="Path (absolute) to hand_controller repo." + ), + DeclareLaunchArgument( + "blaze_target", + default_value="blaze_tflite", + description="blaze target implementation." + ), + DeclareLaunchArgument( + "blaze_model1", + default_value="palm_detection_lite.tflite", + description="Name of blaze detection model." + ), + DeclareLaunchArgument( + "blaze_model2", + default_value="hand_landmark_lite.tflite", + description="Name of blaze landmark model." + ), + DeclareLaunchArgument( + "viewer1_name", + default_value="Hand Controller", + description="Name of Image Viewer for hand_controller/annotations." + ), + #Node( + # package='v4l2_camera', + # executable='v4l2_camera_node', + # name="usbcam_publisher", + # remappings=[("image_raw", "usbcam_image")] + #), + Node( + package='hand_controller', + executable='usbcam_publisher_node', + name="usbcam_publisher", + remappings=[ + ("image_raw", "usbcam_image") + ] + ), + Node( + package='hand_controller', + executable='hand_controller_asl_twist_node', + name="hand_controller", + parameters=[ + {"repo_path":LaunchConfiguration("repo_path")}, + {"blaze_target":LaunchConfiguration("blaze_target")}, + {"blaze_model1":LaunchConfiguration("blaze_model1")}, + {"blaze_model2":LaunchConfiguration("blaze_model2")}, + {"verbose":True}, + {"use_imshow":False} + ], + remappings=[ + ("image_raw", "usbcam_image"), + ("hand_controller/cmd_vel", "turtle1/cmd_vel") + ] + ), + Node( + package='hand_controller', + executable='usbcam_subscriber_node', + name="hand_controller_annotations", + parameters=[ + {"viewer_name":LaunchConfiguration("viewer1_name")} + ], + remappings=[ + ("image_raw", "hand_controller/image_annotated") + ] + ), + Node( + package='turtlesim', + executable='turtlesim_node', + name="my_turtlesim", + ) + ]) diff --git a/ros2_ws/install/hand_controller/share/hand_controller/package.bash b/ros2_ws/install/hand_controller/share/hand_controller/package.bash new file mode 100644 index 0000000..6662b91 --- /dev/null +++ b/ros2_ws/install/hand_controller/share/hand_controller/package.bash @@ -0,0 +1,31 @@ +# generated from colcon_bash/shell/template/package.bash.em + +# This script extends the environment for this package. + +# a bash script is able to determine its own path if necessary +if [ -z "$COLCON_CURRENT_PREFIX" ]; then + # the prefix is two levels up from the package specific share directory + _colcon_package_bash_COLCON_CURRENT_PREFIX="$(builtin cd "`dirname "${BASH_SOURCE[0]}"`/../.." > /dev/null && pwd)" +else + _colcon_package_bash_COLCON_CURRENT_PREFIX="$COLCON_CURRENT_PREFIX" +fi + +# function to source another script with conditional trace output +# first argument: the path of the script +# additional arguments: arguments to the script +_colcon_package_bash_source_script() { + if [ -f "$1" ]; then + if [ -n "$COLCON_TRACE" ]; then + echo "# . \"$1\"" + fi + . "$@" + else + echo "not found: \"$1\"" 1>&2 + fi +} + +# source sh script of this package +_colcon_package_bash_source_script "$_colcon_package_bash_COLCON_CURRENT_PREFIX/share/hand_controller/package.sh" + +unset _colcon_package_bash_source_script +unset _colcon_package_bash_COLCON_CURRENT_PREFIX diff --git a/ros2_ws/install/hand_controller/share/hand_controller/package.dsv b/ros2_ws/install/hand_controller/share/hand_controller/package.dsv new file mode 100644 index 0000000..b3f91b7 --- /dev/null +++ b/ros2_ws/install/hand_controller/share/hand_controller/package.dsv @@ -0,0 +1,4 @@ +source;share/hand_controller/hook/pythonpath.dsv +source;share/hand_controller/hook/pythonpath.sh +source;share/hand_controller/hook/ament_prefix_path.dsv +source;share/hand_controller/hook/ament_prefix_path.sh diff --git a/ros2_ws/install/hand_controller/share/hand_controller/package.sh b/ros2_ws/install/hand_controller/share/hand_controller/package.sh new file mode 100644 index 0000000..01da962 --- /dev/null +++ b/ros2_ws/install/hand_controller/share/hand_controller/package.sh @@ -0,0 +1,87 @@ +# generated from colcon_core/shell/template/package.sh.em + +# This script extends the environment for this package. + +# function to prepend a value to a variable +# which uses colons as separators +# duplicates as well as trailing separators are avoided +# first argument: the name of the result variable +# second argument: the value to be prepended +_colcon_prepend_unique_value() { + # arguments + _listname="$1" + _value="$2" + + # get values from variable + eval _values=\"\$$_listname\" + # backup the field separator + _colcon_prepend_unique_value_IFS=$IFS + IFS=":" + # start with the new value + _all_values="$_value" + # workaround SH_WORD_SPLIT not being set in zsh + if [ "$(command -v colcon_zsh_convert_to_array)" ]; then + colcon_zsh_convert_to_array _values + fi + # iterate over existing values in the variable + for _item in $_values; do + # ignore empty strings + if [ -z "$_item" ]; then + continue + fi + # ignore duplicates of _value + if [ "$_item" = "$_value" ]; then + continue + fi + # keep non-duplicate values + _all_values="$_all_values:$_item" + done + unset _item + # restore the field separator + IFS=$_colcon_prepend_unique_value_IFS + unset _colcon_prepend_unique_value_IFS + # export the updated variable + eval export $_listname=\"$_all_values\" + unset _all_values + unset _values + + unset _value + unset _listname +} + +# since a plain shell script can't determine its own path when being sourced +# either use the provided COLCON_CURRENT_PREFIX +# or fall back to the build time prefix (if it exists) +_colcon_package_sh_COLCON_CURRENT_PREFIX="/var/roothome/hand_controller/ros2_ws/install/hand_controller" +if [ -z "$COLCON_CURRENT_PREFIX" ]; then + if [ ! -d "$_colcon_package_sh_COLCON_CURRENT_PREFIX" ]; then + echo "The build time path \"$_colcon_package_sh_COLCON_CURRENT_PREFIX\" doesn't exist. Either source a script for a different shell or set the environment variable \"COLCON_CURRENT_PREFIX\" explicitly." 1>&2 + unset _colcon_package_sh_COLCON_CURRENT_PREFIX + return 1 + fi + COLCON_CURRENT_PREFIX="$_colcon_package_sh_COLCON_CURRENT_PREFIX" +fi +unset _colcon_package_sh_COLCON_CURRENT_PREFIX + +# function to source another script with conditional trace output +# first argument: the path of the script +# additional arguments: arguments to the script +_colcon_package_sh_source_script() { + if [ -f "$1" ]; then + if [ -n "$COLCON_TRACE" ]; then + echo "# . \"$1\"" + fi + . "$@" + else + echo "not found: \"$1\"" 1>&2 + fi +} + +# source sh hooks +_colcon_package_sh_source_script "$COLCON_CURRENT_PREFIX/share/hand_controller/hook/pythonpath.sh" +_colcon_package_sh_source_script "$COLCON_CURRENT_PREFIX/share/hand_controller/hook/ament_prefix_path.sh" + +unset _colcon_package_sh_source_script +unset COLCON_CURRENT_PREFIX + +# do not unset _colcon_prepend_unique_value since it might be used by non-primary shell hooks diff --git a/ros2_ws/install/hand_controller/share/hand_controller/package.xml b/ros2_ws/install/hand_controller/share/hand_controller/package.xml new file mode 100644 index 0000000..df620ac --- /dev/null +++ b/ros2_ws/install/hand_controller/share/hand_controller/package.xml @@ -0,0 +1,27 @@ + + + + hand_controller + 0.0.0 + Hand Controller using mediapipe models and ASL. + albertabeef + Apache License 2.0 + + rclpy + std_msgs + sensor_msgs + cv_bridge + ros2launch + v4l2_camera + twist_stamper + turtlesim + + ament_copyright + ament_flake8 + ament_pep257 + python3-pytest + + + ament_python + + diff --git a/ros2_ws/install/hand_controller/share/hand_controller/resources_high/1. QCS6490 Dev Kit.png b/ros2_ws/install/hand_controller/share/hand_controller/resources_high/1. QCS6490 Dev Kit.png new file mode 100644 index 0000000..477aaef Binary files /dev/null and b/ros2_ws/install/hand_controller/share/hand_controller/resources_high/1. QCS6490 Dev Kit.png differ diff --git a/ros2_ws/install/hand_controller/share/hand_controller/resources_high/2. Kit Includes.png b/ros2_ws/install/hand_controller/share/hand_controller/resources_high/2. Kit Includes.png new file mode 100644 index 0000000..bb46881 Binary files /dev/null and b/ros2_ws/install/hand_controller/share/hand_controller/resources_high/2. Kit Includes.png differ diff --git a/ros2_ws/install/hand_controller/share/hand_controller/resources_high/3. Demo HW.png b/ros2_ws/install/hand_controller/share/hand_controller/resources_high/3. Demo HW.png new file mode 100644 index 0000000..11f2a06 Binary files /dev/null and b/ros2_ws/install/hand_controller/share/hand_controller/resources_high/3. Demo HW.png differ diff --git a/ros2_ws/install/hand_controller/share/hand_controller/resources_high/4. Demo UI.png b/ros2_ws/install/hand_controller/share/hand_controller/resources_high/4. Demo UI.png new file mode 100644 index 0000000..4c79b89 Binary files /dev/null and b/ros2_ws/install/hand_controller/share/hand_controller/resources_high/4. Demo UI.png differ diff --git a/ros2_ws/install/hand_controller/share/hand_controller/resources_high/5. Demo SW.png b/ros2_ws/install/hand_controller/share/hand_controller/resources_high/5. Demo SW.png new file mode 100644 index 0000000..b6e1eed Binary files /dev/null and b/ros2_ws/install/hand_controller/share/hand_controller/resources_high/5. Demo SW.png differ diff --git a/ros2_ws/install/hand_controller/share/hand_controller/resources_high/6. Demo AI.png b/ros2_ws/install/hand_controller/share/hand_controller/resources_high/6. Demo AI.png new file mode 100644 index 0000000..a712344 Binary files /dev/null and b/ros2_ws/install/hand_controller/share/hand_controller/resources_high/6. Demo AI.png differ diff --git a/ros2_ws/install/hand_controller/share/hand_controller/resources_high/7. IQ9 Kit.png b/ros2_ws/install/hand_controller/share/hand_controller/resources_high/7. IQ9 Kit.png new file mode 100644 index 0000000..c4db0cf Binary files /dev/null and b/ros2_ws/install/hand_controller/share/hand_controller/resources_high/7. IQ9 Kit.png differ diff --git a/ros2_ws/install/hand_controller/share/hand_controller/resources_high/8. IQ9 SBC.png b/ros2_ws/install/hand_controller/share/hand_controller/resources_high/8. IQ9 SBC.png new file mode 100644 index 0000000..0f5ae63 Binary files /dev/null and b/ros2_ws/install/hand_controller/share/hand_controller/resources_high/8. IQ9 SBC.png differ diff --git a/ros2_ws/install/hand_controller/share/hand_controller/resources_high/9. IQ9 AI Box.png b/ros2_ws/install/hand_controller/share/hand_controller/resources_high/9. IQ9 AI Box.png new file mode 100644 index 0000000..641b45c Binary files /dev/null and b/ros2_ws/install/hand_controller/share/hand_controller/resources_high/9. IQ9 AI Box.png differ diff --git a/ros2_ws/install/hand_controller/share/hand_controller/resources_high/GSTLauncher.glade b/ros2_ws/install/hand_controller/share/hand_controller/resources_high/GSTLauncher.glade new file mode 100644 index 0000000..749a484 --- /dev/null +++ b/ros2_ws/install/hand_controller/share/hand_controller/resources_high/GSTLauncher.glade @@ -0,0 +1,1323 @@ + + + + + + True + False + close.png + + + True + False + close.png + + + False + popup + False + center-always + True + True + dialog + center + + + + + + False + vertical + 2 + + + False + end + + + True + True + bottom + False + + + True + False + 1. QCS6490 Dev Kit.png + + + + + True + False + 1. QCS6490 Dev Kit + + + + True + False + + + + + True + False + 2. Kit Includes.png + + + 1 + + + + + True + False + 2. Kit Includes + + + + 1 + True + False + + + + + True + False + 3. Demo HW.png + + + 2 + + + + + True + False + 3. Demo HW + + + + 2 + True + False + + + + + True + False + 4. Demo UI.png + + + 3 + + + + + True + False + 4. Demo UI + + + + 3 + True + False + + + + + True + False + 5. Demo SW.png + + + 4 + + + + + True + False + 5. Demo SW + + + + 4 + True + False + + + + + True + False + 6. Demo AI.png + + + 5 + + + + + True + False + 6. Demo AI + + + + 5 + True + False + + + + + True + False + 7. IQ9 Kit.png + + + 6 + + + + + True + False + 7. IQ9 Kit + + + + 6 + True + False + + + + + True + False + 8. IQ9 SBC.png + + + 7 + + + + + True + False + 8. IQ9 SBC + + + + 7 + True + False + + + + + True + False + 9. IQ9 AI Box.png + + + 8 + + + + + True + False + 9. IQ9 AI Box + + + + 8 + True + False + + + + + + False + False + 0 + + + + + False + False + 0 + + + + + Close + 100 + True + True + True + end + start + Close1 + True + + + + + False + True + 0 + + + + + + + True + False + info.png + + + True + False + exit.png + + + False + + + + + + + + True + False + True + True + + + True + True + False + start + 5 + + + True + False + center + 15 + VisionAI-Kit_6490_100.png + + + False + True + 0 + + + + + 1500 + True + False + center + center + 20 + 3 + top + + + Info + 150 + True + True + True + 3 + 3 + Info + True + + + + + False + True + 0 + + + + + True + False + True + + + False + True + 1 + + + + + demo_title + True + False + Hand-Controlled Robot Car + + + + True + True + 2 + + + + + True + False + True + + + False + True + 3 + + + + + Exit + 150 + True + True + True + 3 + 3 + Quit + True + + + + + False + True + end + 4 + + + + + + False + True + 2 + + + + + True + False + start + center + 15 + TRIA-Color-RGB-100.png + + + False + False + end + 1 + + + + + 0 + 0 + + + + + True + False + True + True + + + True + False + 35 + True + True + + + AspectFrame1 + True + False + True + True + 0 + none + 0 + 1.3300000429153442 + False + + + True + False + True + True + True + + + rightHandImage + True + False + True + True + updown.png + + + 1 + 1 + + + + + leftHandImage + True + False + True + True + leftright.png + + + 0 + 1 + + + + + leftHandStatus + True + False + 50 + False + Status: Inactive + + + + 0 + 2 + + + + + rightHandStatus + True + False + 50 + False + Status: Inactive + + + + 1 + 2 + + + + + leftHandTurn + True + False + 50 + False + Left hand: Left/Right + + + + 0 + 0 + + + + + rightHandMovement + True + False + 50 + False + Right hand: Forward/Reverse + + + + 1 + 0 + + + + + + + 0 + 0 + + + + + AspectFrame2 + True + False + True + True + 0 + none + 0 + 1.3300000429153442 + False + + + videosink0 + True + False + True + True + gtk-missing-image + + + + + 1 + 0 + + + + + + + 0 + 1 + 2 + + + + + 568 + True + False + end + True + immediate + + + GraphDrawAreaBottom + True + True + False + True + True + + + 0 + 1 + + + + + 0 + True + False + start + 2 + vertical + 2 + 8 + True + + + True + False + start + center + CPU use + + + + 1 + 1 + + + + + True + False + start + center + True + 0 + 7 + 7 + + + + 2 + 1 + + + + + True + False + start + center + DDR use + + + + 1 + 2 + + + + + True + False + start + center + GPU use + + + + 1 + 3 + + + + + True + False + start + center + NPU use + + + + 1 + 0 + + + + + True + False + start + center + True + 0 + 7 + 7 + + + + 2 + 2 + + + + + True + False + start + center + True + 0 + 7 + True + 7 + + + + 2 + 3 + + + + + True + False + start + center + True + 0 + 7 + True + 7 + + + + 2 + 0 + + + + + True + False + legend_pink.png + + + 0 + 1 + + + + + True + False + legend_blue.png + + + 0 + 2 + + + + + True + False + legend_yellow.png + + + 0 + 3 + + + + + True + False + legend_green.png + + + 0 + 0 + + + + + True + False + start + center + 10 + % + + + + 3 + 0 + + + + + True + False + start + center + 10 + % + + + + 3 + 1 + + + + + True + False + start + center + 10 + % + + + + 3 + 2 + + + + + True + False + start + center + 10 + % + + + + 3 + 3 + + + + + 3 + 1 + + + + + 0 + True + False + start + 2 + 25 + vertical + 2 + 8 + True + + + True + False + center + center + legend_pink.png + + + 0 + 0 + + + + + True + False + legend_blue.png + + + 0 + 1 + + + + + True + False + legend_yellow.png + + + 0 + 2 + + + + + True + False + start + center + CPU + + + + 1 + 0 + + + + + True + False + start + center + True + 0 + 7 + 7 + + + + 2 + 0 + + + + + True + False + start + center + DDR + + + + 1 + 1 + + + + + True + False + start + center + True + 0 + 7 + 7 + + + + 2 + 1 + + + + + True + False + start + center + GPU + + + + 1 + 2 + + + + + True + False + start + center + True + 0 + 7 + 7 + + + + 2 + 2 + + + + + True + False + start + center + °C + + + + 3 + 0 + + + + + True + False + start + center + °C + + + + 3 + 1 + + + + + True + False + start + center + °C + + + + 3 + 2 + + + + + 1 + 1 + + + + + True + False + center + center + System Thermals + + + + 0 + 0 + + + + + True + False + center + center + System Utilization + + + + 2 + 0 + + + + + GraphDrawAreaTop + True + True + False + True + True + + + 2 + 1 + + + + + 25 + True + False + end + Built with GTK, GStreamer, and Python to effectively utilize VPU, NPU, and GPU acceleration + + + + 0 + 2 + 4 + + + + + + + + + + + 0 + 2 + + + + + + + + dialogWindow + 480 + 240 + False + popup + False + center-always + True + True + dialog + False + center + + + + + + False + vertical + 2 + + + False + end + + + + + + + + + False + False + 0 + + + + + dialogMessage + True + False + center + center + 10 + 10 + 50 + 50 + True + True + Establishing ROS2 connection, please wait... + + + + + + False + True + 1 + + + + + + + + + + + + + None + + + Camera + + + Pose detection + + + Segmentation + + + Classification + + + Object detection + + + Depth Segmentation + + + + diff --git a/ros2_ws/install/hand_controller/share/hand_controller/resources_high/TRIA-Color-RGB-100.png b/ros2_ws/install/hand_controller/share/hand_controller/resources_high/TRIA-Color-RGB-100.png new file mode 100644 index 0000000..fb54fa7 Binary files /dev/null and b/ros2_ws/install/hand_controller/share/hand_controller/resources_high/TRIA-Color-RGB-100.png differ diff --git a/ros2_ws/install/hand_controller/share/hand_controller/resources_high/VisionAI-Kit_6490_100.png b/ros2_ws/install/hand_controller/share/hand_controller/resources_high/VisionAI-Kit_6490_100.png new file mode 100644 index 0000000..c4ae44b Binary files /dev/null and b/ros2_ws/install/hand_controller/share/hand_controller/resources_high/VisionAI-Kit_6490_100.png differ diff --git a/ros2_ws/install/hand_controller/share/hand_controller/resources_high/app.css b/ros2_ws/install/hand_controller/share/hand_controller/resources_high/app.css new file mode 100644 index 0000000..92ce42a --- /dev/null +++ b/ros2_ws/install/hand_controller/share/hand_controller/resources_high/app.css @@ -0,0 +1,261 @@ + .body { + -ms-overflow-style: none; + background-color: #171717; + font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; + font-weight: normal; + font-size: larger; + color: #ffffff; + margin: 0px; +} + +.title { + font-size:3rem; + font-weight: bold; + color: #ffffff; + } + +.label { + font-size: 3.5rem; + font-weight: bold; + color: #ffffff; + } + +.status { + font-size: 4rem; + font-weight: bold; + color: #ffffff; + background-color: rgb(0, 31, 99); + } + +.status_active { + font-size: 4rem; + font-weight: bold; + color: rgb(255, 0, 163); + background-color: rgb(0, 31, 99); +} + + .demo_title { + font-size:5rem; + font-weight: bold; + color: #ffffff; + } + +.tab { + font-size: 3rem; + font-weight:normal; + color: rgb(0, 0, 0); + border-image: none; + border-radius: 2px; + border-style: none; + box-shadow: 1px 1px 3px rgb(57, 57, 57); + padding: 5px; +} + +.nbContent > header.top { + background: rgb(4, 18, 83); + border-bottom: none; +} + +/* +.nbContent > header.top > tabs > tab { + background: rgb(17, 136, 130); + border-image: none; + border: 10px solid rgba(0,0,0,0.4); + border-radius: 8px 8px 0px 0px; + border-bottom: none; + box-shadow: 3px 3px 10px #707070; + padding: 10px; + +} +*/ +.nbContent > header > tabs > tab:hover { + background: rgb(255, 255, 255); + box-shadow: inset 0 0 15px rgb(255, 255, 255); +} + +.nbContent > header > tabs > tab:checked { + border: none; + box-shadow: 1px 1px 3px rgb(176, 203, 235); + padding: 5px; + background: rgb(176, 222, 248); +} + +.labelm { + font-size: 3.5rem; + font-weight: bold; + color: rgb(255, 0, 163); + } + +.label_m { + font-size: 3.8rem; + font-weight: bold; + color: #ffffff; + } + +.label_xl_m { + font-size: 3rem; + font-weight: bold; + color: rgb(255, 0, 163); + } + +.label_xl { + font-size: 3rem; + font-weight: bold; + color: #ffffff; + } + +.label_l { + font-size: 4rem; + font-weight: bold; + color: rgb(255, 255, 255); + } + +.label_l_m { + font-family: Consolas, monaco, monospace; + font-size: 4rem; + font-weight: bold; + color: rgb(255, 0, 163); +} + +.label_l_t { + font-size: 3.2rem; + font-weight: bold; + color: rgb(255, 255, 255); +} + +.dropdown { + text-decoration: none; + font-size: 3rem; + color: rgb(0,0,0); + text-shadow: 0px 1px 1px rgba(255,255,255,0.3), + 0px 0px 0px rgba(0,0,0,0.7); + border-radius: 2px; + border-style: none; + box-shadow: 3px 3px 10px #888888; + padding: 0px; + background: rgb(255, 255, 255); + transition: color 0.1s, background 0.1s; + border: 3px solid rgb(250, 250, 250); +} + +.dropdown menu { + text-decoration: none; + font-size: 3rem; + color: rgba(0,30,95,1.0); + text-shadow: 0px 1px 1px rgba(0,0,0,0.3), + 0px 0px 0px rgba(255,255,255,1); + border-radius: 2px; + border-style: none; + box-shadow: 3px 3px 10px #888888; + padding: 0px; + background: rgb(208, 210, 212); + transition: color 0.1s, background 0.1s; + border: 3px solid rgb(250, 250, 250); +} + +.dropdown button:hover { + text-decoration: none; + color: rgba(248,248,248,0.9); + text-shadow: 0px 1px 1px rgba(0,0,0,0.3), + 0px 0px 0px rgb(255,255,255); + background: rgb(255, 0, 163); + box-shadow: 3px 3px 10px rgb(255, 0, 163); +} + +.menu_button { + text-decoration: none; + font-size: 3.5rem; + color: rgb(0,0,0); + text-shadow: 0px 1px 1px rgba(255,255,255,0.3), + 0px 0px 0px rgba(0,0,0,0.7); + border-radius: 2px; + border-style: none; + box-shadow: 3px 3px 10px #888888; + padding: 3px; + background: rgb(255, 255, 255); + transition: color 0.1s, background 0.1s; + border: 3px solid rgb(250, 250, 250); +} + +/*.menu_button:focus { + background: rgb(255, 0, 163); + box-shadow: 3px 3px 10px rgb(255, 0, 163); + +}*/ + +.menu_button:hover { + text-decoration: none; + color: rgba(248,248,248,0.9); + text-shadow: 0px 1px 1px rgba(0,0,0,0.3), + 0px 0px 0px rgba(255,255,255,1); + background: rgb(255, 0, 163); + box-shadow: 3px 3px 10px rgb(255, 0, 163); +} + + +.box { + box-shadow: 3px 3px 10px #888888; + padding: 3px; +} + + + + + +/* Green button */ +#scan_button, #ap_password_page button{ + background: rgb(201, 210, 0); + border: 3px solid #668a08; +} + +#scan_button:hover, #ap_password_page button:hover { + background: rgb(115, 152, 51); +} + + +/* Red button */ +#clear_confirm button.warning, #clientJoin_banner button.warning { + background: #d72c2c; + border: 3px solid #9e1212; +} +#clear_confirm button.warning:hover, #clientJoin_banner button.warning:hover { + background: #b31414; +} + +.menu_button:focus, #ap_password_page button:focus +{ + outline: 0; +} + + +.bar { + margin: 5px; + -webkit-transition: all 0.3s ease-in-out; + transition: all 0.3s ease-in-out; + background-color: #ffffff; +} + +.nav-item a{ + font-size: 1.3rem; + font-weight: 400; + color: #ffffff; + background: #171717; + padding: 1.2rem 1.2rem; +} + +.nav-item a:hover{ + background: #41c363; +} +.nav-item a.current { + background: black; + outline: solid 1px white; + outline-offset: -1px; +} + +.footer +{ + padding: 1px 10px 0px 10px; + background: #41c363; + font-size: 1.5em; + font-weight: bold; +} diff --git a/ros2_ws/install/hand_controller/share/hand_controller/resources_high/bg_tria.png b/ros2_ws/install/hand_controller/share/hand_controller/resources_high/bg_tria.png new file mode 100644 index 0000000..59ab6d5 Binary files /dev/null and b/ros2_ws/install/hand_controller/share/hand_controller/resources_high/bg_tria.png differ diff --git a/ros2_ws/install/hand_controller/share/hand_controller/resources_high/blank.png b/ros2_ws/install/hand_controller/share/hand_controller/resources_high/blank.png new file mode 100644 index 0000000..1bd1be5 Binary files /dev/null and b/ros2_ws/install/hand_controller/share/hand_controller/resources_high/blank.png differ diff --git a/ros2_ws/install/hand_controller/share/hand_controller/resources_high/close.png b/ros2_ws/install/hand_controller/share/hand_controller/resources_high/close.png new file mode 100644 index 0000000..e8499d5 Binary files /dev/null and b/ros2_ws/install/hand_controller/share/hand_controller/resources_high/close.png differ diff --git a/ros2_ws/install/hand_controller/share/hand_controller/resources_high/down.png b/ros2_ws/install/hand_controller/share/hand_controller/resources_high/down.png new file mode 100644 index 0000000..c7d94e3 Binary files /dev/null and b/ros2_ws/install/hand_controller/share/hand_controller/resources_high/down.png differ diff --git a/ros2_ws/install/hand_controller/share/hand_controller/resources_high/exit.png b/ros2_ws/install/hand_controller/share/hand_controller/resources_high/exit.png new file mode 100644 index 0000000..3e47a81 Binary files /dev/null and b/ros2_ws/install/hand_controller/share/hand_controller/resources_high/exit.png differ diff --git a/ros2_ws/install/hand_controller/share/hand_controller/resources_high/info.png b/ros2_ws/install/hand_controller/share/hand_controller/resources_high/info.png new file mode 100644 index 0000000..6c5fd29 Binary files /dev/null and b/ros2_ws/install/hand_controller/share/hand_controller/resources_high/info.png differ diff --git a/ros2_ws/install/hand_controller/share/hand_controller/resources_high/left.png b/ros2_ws/install/hand_controller/share/hand_controller/resources_high/left.png new file mode 100644 index 0000000..c8c0a66 Binary files /dev/null and b/ros2_ws/install/hand_controller/share/hand_controller/resources_high/left.png differ diff --git a/ros2_ws/install/hand_controller/share/hand_controller/resources_high/leftright.png b/ros2_ws/install/hand_controller/share/hand_controller/resources_high/leftright.png new file mode 100644 index 0000000..560bb0d Binary files /dev/null and b/ros2_ws/install/hand_controller/share/hand_controller/resources_high/leftright.png differ diff --git a/ros2_ws/install/hand_controller/share/hand_controller/resources_high/legend_blue.png b/ros2_ws/install/hand_controller/share/hand_controller/resources_high/legend_blue.png new file mode 100644 index 0000000..30ff117 Binary files /dev/null and b/ros2_ws/install/hand_controller/share/hand_controller/resources_high/legend_blue.png differ diff --git a/ros2_ws/install/hand_controller/share/hand_controller/resources_high/legend_green.png b/ros2_ws/install/hand_controller/share/hand_controller/resources_high/legend_green.png new file mode 100644 index 0000000..ee4235c Binary files /dev/null and b/ros2_ws/install/hand_controller/share/hand_controller/resources_high/legend_green.png differ diff --git a/ros2_ws/install/hand_controller/share/hand_controller/resources_high/legend_pink.png b/ros2_ws/install/hand_controller/share/hand_controller/resources_high/legend_pink.png new file mode 100644 index 0000000..34882b1 Binary files /dev/null and b/ros2_ws/install/hand_controller/share/hand_controller/resources_high/legend_pink.png differ diff --git a/ros2_ws/install/hand_controller/share/hand_controller/resources_high/legend_yellow.png b/ros2_ws/install/hand_controller/share/hand_controller/resources_high/legend_yellow.png new file mode 100644 index 0000000..702508c Binary files /dev/null and b/ros2_ws/install/hand_controller/share/hand_controller/resources_high/legend_yellow.png differ diff --git a/ros2_ws/install/hand_controller/share/hand_controller/resources_high/right.png b/ros2_ws/install/hand_controller/share/hand_controller/resources_high/right.png new file mode 100644 index 0000000..652f710 Binary files /dev/null and b/ros2_ws/install/hand_controller/share/hand_controller/resources_high/right.png differ diff --git a/ros2_ws/install/hand_controller/share/hand_controller/resources_high/sample_weston.ini b/ros2_ws/install/hand_controller/share/hand_controller/resources_high/sample_weston.ini new file mode 100644 index 0000000..79ce3f0 --- /dev/null +++ b/ros2_ws/install/hand_controller/share/hand_controller/resources_high/sample_weston.ini @@ -0,0 +1,22 @@ +# configuration file for Weston +[core] +idle-time=0 +backend=sdm-backend.so +repaint-window=10 + +[output] +name=DSI-1 +mode=on + +[output] +name=DP-1 +mode=on + +[shell] +clock-format=seconds +background-image=/opt/bg_tria.png +background-type=scale-crop + +[launcher] +icon=/opt/tria_demo_launcher_icon.png +path=VISIONAI_PATH_OVERRIDE=/opt/QCS6490-Vision-AI-Demo/visionai.py /opt/QCS6490-Vision-AI-Demo/launch_visionai_with_env.sh diff --git a/ros2_ws/install/hand_controller/share/hand_controller/resources_high/tria_demo_launcher_icon.png b/ros2_ws/install/hand_controller/share/hand_controller/resources_high/tria_demo_launcher_icon.png new file mode 100644 index 0000000..ff6fc34 Binary files /dev/null and b/ros2_ws/install/hand_controller/share/hand_controller/resources_high/tria_demo_launcher_icon.png differ diff --git a/ros2_ws/install/hand_controller/share/hand_controller/resources_high/up.png b/ros2_ws/install/hand_controller/share/hand_controller/resources_high/up.png new file mode 100644 index 0000000..c500cfd Binary files /dev/null and b/ros2_ws/install/hand_controller/share/hand_controller/resources_high/up.png differ diff --git a/ros2_ws/install/hand_controller/share/hand_controller/resources_high/updown.png b/ros2_ws/install/hand_controller/share/hand_controller/resources_high/updown.png new file mode 100644 index 0000000..6d57a01 Binary files /dev/null and b/ros2_ws/install/hand_controller/share/hand_controller/resources_high/updown.png differ diff --git a/ros2_ws/install/hand_controller/share/hand_controller/resources_low/1. QCS6490 Dev Kit.png b/ros2_ws/install/hand_controller/share/hand_controller/resources_low/1. QCS6490 Dev Kit.png new file mode 100644 index 0000000..24bb3ce Binary files /dev/null and b/ros2_ws/install/hand_controller/share/hand_controller/resources_low/1. QCS6490 Dev Kit.png differ diff --git a/ros2_ws/install/hand_controller/share/hand_controller/resources_low/2. Kit Includes.png b/ros2_ws/install/hand_controller/share/hand_controller/resources_low/2. Kit Includes.png new file mode 100644 index 0000000..871a641 Binary files /dev/null and b/ros2_ws/install/hand_controller/share/hand_controller/resources_low/2. Kit Includes.png differ diff --git a/ros2_ws/install/hand_controller/share/hand_controller/resources_low/3. Demo HW.png b/ros2_ws/install/hand_controller/share/hand_controller/resources_low/3. Demo HW.png new file mode 100644 index 0000000..d64a6be Binary files /dev/null and b/ros2_ws/install/hand_controller/share/hand_controller/resources_low/3. Demo HW.png differ diff --git a/ros2_ws/install/hand_controller/share/hand_controller/resources_low/4. Demo UI.png b/ros2_ws/install/hand_controller/share/hand_controller/resources_low/4. Demo UI.png new file mode 100644 index 0000000..f4e7713 Binary files /dev/null and b/ros2_ws/install/hand_controller/share/hand_controller/resources_low/4. Demo UI.png differ diff --git a/ros2_ws/install/hand_controller/share/hand_controller/resources_low/5. Demo SW.png b/ros2_ws/install/hand_controller/share/hand_controller/resources_low/5. Demo SW.png new file mode 100644 index 0000000..da023fc Binary files /dev/null and b/ros2_ws/install/hand_controller/share/hand_controller/resources_low/5. Demo SW.png differ diff --git a/ros2_ws/install/hand_controller/share/hand_controller/resources_low/6. Demo AI.png b/ros2_ws/install/hand_controller/share/hand_controller/resources_low/6. Demo AI.png new file mode 100644 index 0000000..e38cd19 Binary files /dev/null and b/ros2_ws/install/hand_controller/share/hand_controller/resources_low/6. Demo AI.png differ diff --git a/ros2_ws/install/hand_controller/share/hand_controller/resources_low/7. IQ9 Kit.png b/ros2_ws/install/hand_controller/share/hand_controller/resources_low/7. IQ9 Kit.png new file mode 100644 index 0000000..da5bdf6 Binary files /dev/null and b/ros2_ws/install/hand_controller/share/hand_controller/resources_low/7. IQ9 Kit.png differ diff --git a/ros2_ws/install/hand_controller/share/hand_controller/resources_low/8. IQ9 SBC.png b/ros2_ws/install/hand_controller/share/hand_controller/resources_low/8. IQ9 SBC.png new file mode 100644 index 0000000..c920fdc Binary files /dev/null and b/ros2_ws/install/hand_controller/share/hand_controller/resources_low/8. IQ9 SBC.png differ diff --git a/ros2_ws/install/hand_controller/share/hand_controller/resources_low/9. IQ9 AI Box.png b/ros2_ws/install/hand_controller/share/hand_controller/resources_low/9. IQ9 AI Box.png new file mode 100644 index 0000000..c1485ea Binary files /dev/null and b/ros2_ws/install/hand_controller/share/hand_controller/resources_low/9. IQ9 AI Box.png differ diff --git a/ros2_ws/install/hand_controller/share/hand_controller/resources_low/GSTLauncher.glade b/ros2_ws/install/hand_controller/share/hand_controller/resources_low/GSTLauncher.glade new file mode 100644 index 0000000..3a92a62 --- /dev/null +++ b/ros2_ws/install/hand_controller/share/hand_controller/resources_low/GSTLauncher.glade @@ -0,0 +1,1324 @@ + + + + + + True + False + close.png + + + True + False + close.png + + + False + popup + False + center-always + True + dialog + center + + + + + + False + vertical + 2 + + + False + vertical + end + + + True + True + bottom + False + + + True + False + 1. QCS6490 Dev Kit.png + + + + + True + False + 1. QCS6490 Dev Kit + + + + True + False + + + + + True + False + 2. Kit Includes.png + + + 1 + + + + + True + False + 2. Kit Includes + + + + 1 + True + False + + + + + True + False + 3. Demo HW.png + + + 2 + + + + + True + False + 3. Demo HW + + + + 2 + True + False + + + + + True + False + 4. Demo UI.png + + + 3 + + + + + True + False + 4. Demo UI + + + + 3 + True + False + + + + + True + False + 5. Demo SW.png + + + 4 + + + + + True + False + 5. Demo SW + + + + 4 + True + False + + + + + True + False + 6. Demo AI.png + + + 5 + + + + + True + False + 6. Demo AI + + + + 5 + True + False + + + + + True + False + 7. IQ9 Kit.png + + + 6 + + + + + True + False + 7. IQ9 Kit + + + + 6 + True + False + + + + + True + False + 8. IQ9 SBC.png + + + 7 + + + + + True + False + 8. IQ9 SBC + + + + 7 + True + False + + + + + True + False + 9. IQ9 AI Box.png + + + 8 + + + + + True + False + 9. IQ9 AI Box + + + + 8 + True + False + + + + + + False + False + 1 + + + + + False + False + 0 + + + + + Close + 100 + True + True + True + end + start + Close1 + True + + + + + False + False + end + 1 + + + + + + + True + False + info.png + + + True + False + exit.png + + + False + + + + + + + + True + False + True + True + + + True + True + False + start + 5 + + + True + False + center + 15 + VisionAI-Kit_6490_100.png + + + False + True + 0 + + + + + 800 + True + False + center + center + 5 + 3 + top + + + Info + 50 + True + True + True + 3 + 3 + Info + True + + + + + False + True + 0 + + + + + True + False + True + + + False + True + 1 + + + + + demo_title + True + False + Hand-Controlled Robot Car + + + + True + True + 2 + + + + + True + False + True + + + False + True + 3 + + + + + Exit + 50 + True + True + True + 3 + 3 + Quit + True + + + + + False + True + end + 4 + + + + + + False + True + 2 + + + + + True + False + start + center + 15 + TRIA-Color-RGB-100.png + + + False + False + end + 1 + + + + + 0 + 0 + + + + + True + False + True + True + + + True + False + 10 + True + True + + + AspectFrame1 + True + False + True + True + 0 + none + 0 + 1.3300000429153442 + False + + + True + False + True + True + True + + + rightHandImage + True + False + True + True + updown.png + + + 1 + 1 + + + + + leftHandImage + True + False + True + True + leftright.png + + + 0 + 1 + + + + + leftHandStatus + True + False + 50 + False + Status: Inactive + + + + 0 + 2 + + + + + rightHandStatus + True + False + 50 + False + Status: Inactive + + + + 1 + 2 + + + + + leftHandTurn + True + False + 50 + False + Left hand: Left/Right + + + + 0 + 0 + + + + + rightHandMovement + True + False + 50 + False + Right hand: Forward/Reverse + + + + 1 + 0 + + + + + + + 0 + 0 + + + + + AspectFrame2 + True + False + True + True + 0 + none + 0 + 1.3300000429153442 + False + + + videosink0 + True + False + True + True + gtk-missing-image + + + + + 1 + 0 + + + + + + + 0 + 1 + 2 + + + + + 200 + True + False + end + True + immediate + + + GraphDrawAreaBottom + True + True + False + True + True + + + 0 + 1 + + + + + 0 + True + False + start + 2 + vertical + 2 + 8 + True + + + True + False + start + center + CPU use + + + + 1 + 1 + + + + + True + False + start + center + True + 0 + 7 + 7 + + + + 2 + 1 + + + + + True + False + start + center + DDR use + + + + 1 + 2 + + + + + True + False + start + center + GPU use + + + + 1 + 3 + + + + + True + False + start + center + NPU use + + + + 1 + 0 + + + + + True + False + start + center + True + 0 + 7 + 7 + + + + 2 + 2 + + + + + True + False + start + center + True + 0 + 7 + True + 7 + + + + 2 + 3 + + + + + True + False + start + center + True + 0 + 7 + True + 7 + + + + 2 + 0 + + + + + True + False + legend_pink.png + + + 0 + 1 + + + + + True + False + legend_blue.png + + + 0 + 2 + + + + + True + False + legend_yellow.png + + + 0 + 3 + + + + + True + False + legend_green.png + + + 0 + 0 + + + + + True + False + start + center + 5 + % + + + + 3 + 0 + + + + + True + False + start + center + 5 + % + + + + 3 + 1 + + + + + True + False + start + center + 5 + % + + + + 3 + 2 + + + + + True + False + start + center + 5 + % + + + + 3 + 3 + + + + + 3 + 1 + + + + + 0 + True + False + start + 2 + 25 + vertical + 2 + 8 + True + + + True + False + center + center + legend_pink.png + + + 0 + 0 + + + + + True + False + legend_blue.png + + + 0 + 1 + + + + + True + False + legend_yellow.png + + + 0 + 2 + + + + + True + False + start + center + CPU + + + + 1 + 0 + + + + + True + False + start + center + True + 0 + 7 + 7 + + + + 2 + 0 + + + + + True + False + start + center + DDR + + + + 1 + 1 + + + + + True + False + start + center + True + 0 + 7 + 7 + + + + 2 + 1 + + + + + True + False + start + center + GPU + + + + 1 + 2 + + + + + True + False + start + center + True + 0 + 7 + 7 + + + + 2 + 2 + + + + + True + False + start + center + °C + + + + 3 + 0 + + + + + True + False + start + center + °C + + + + 3 + 1 + + + + + True + False + start + center + °C + + + + 3 + 2 + + + + + 1 + 1 + + + + + True + False + center + center + System Thermals + + + + 0 + 0 + + + + + True + False + center + center + System Utilization + + + + 2 + 0 + + + + + GraphDrawAreaTop + True + True + False + True + True + + + 2 + 1 + + + + + 25 + True + False + end + Built with GTK, Edge Impulse FOMO model, ROS 2 and Python for the Vision AI-Kit 6490 + + + + 0 + 2 + 4 + + + + + + + + + + + 0 + 2 + + + + + + + + dialogWindow + 120 + 60 + False + popup + False + center-always + True + True + dialog + False + center + + + + + + False + vertical + 2 + + + False + end + + + + + + + + + False + False + 0 + + + + + dialogMessage + True + False + center + center + 10 + 10 + 50 + 50 + True + True + Establishing ROS2 connection, please wait... + + + + + + False + True + 1 + + + + + + + + + + + + + None + + + Camera + + + Pose detection + + + Segmentation + + + Classification + + + Object detection + + + Depth Segmentation + + + + diff --git a/ros2_ws/install/hand_controller/share/hand_controller/resources_low/TRIA-Color-RGB-100.png b/ros2_ws/install/hand_controller/share/hand_controller/resources_low/TRIA-Color-RGB-100.png new file mode 100644 index 0000000..3ce3a1a Binary files /dev/null and b/ros2_ws/install/hand_controller/share/hand_controller/resources_low/TRIA-Color-RGB-100.png differ diff --git a/ros2_ws/install/hand_controller/share/hand_controller/resources_low/VisionAI-Kit_6490_100.png b/ros2_ws/install/hand_controller/share/hand_controller/resources_low/VisionAI-Kit_6490_100.png new file mode 100644 index 0000000..f9bc8d0 Binary files /dev/null and b/ros2_ws/install/hand_controller/share/hand_controller/resources_low/VisionAI-Kit_6490_100.png differ diff --git a/ros2_ws/install/hand_controller/share/hand_controller/resources_low/app.css b/ros2_ws/install/hand_controller/share/hand_controller/resources_low/app.css new file mode 100644 index 0000000..d2f9fa5 --- /dev/null +++ b/ros2_ws/install/hand_controller/share/hand_controller/resources_low/app.css @@ -0,0 +1,261 @@ +.body { + -ms-overflow-style: none; + background-color: #171717; + font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; + font-weight: normal; + font-size: larger; + color: #ffffff; + margin: 0px; +} + +.title { + font-size:1.0rem; + font-weight: bold; + color: #ffffff; + } + +.label { + font-size: 1.5rem; + font-weight: bold; + color: #ffffff; + } + +.status { + font-size: 2.2rem; + font-weight: bold; + color: #ffffff; + background-color: rgb(0, 31, 99); + } + +.status_active { + font-size: 2.2rem; + font-weight: bold; + color: rgb(255, 0, 163); + background-color: rgb(0, 31, 99); +} + +.demo_title { + font-size:3rem; + font-weight: bold; + color: #ffffff; + } + +.tab { + font-size: 1rem; + font-weight:normal; + color: rgb(0, 0, 0); + border-image: none; + border-radius: 2px; + border-style: none; + box-shadow: 1px 1px 3px rgb(57, 57, 57); + padding: 5px; +} + +.nbContent > header.top { + background: rgb(4, 18, 83); + border-bottom: none; +} + +/* +.nbContent > header.top > tabs > tab { + background: rgb(17, 136, 130); + border-image: none; + border: 10px solid rgba(0,0,0,0.4); + border-radius: 8px 8px 0px 0px; + border-bottom: none; + box-shadow: 3px 3px 10px #707070; + padding: 10px; + +} +*/ +.nbContent > header > tabs > tab:hover { + background: rgb(255, 255, 255); + box-shadow: inset 0 0 15px rgb(255, 255, 255); +} + +.nbContent > header > tabs > tab:checked { + border: none; + box-shadow: 1px 1px 3px rgb(176, 203, 235); + padding: 5px; + background: rgb(176, 222, 248); +} + +.labelm { + font-size: 1.5rem; + font-weight: bold; + color: rgb(255, 0, 163); + } + +.label_m { + font-size: 1.5rem; + font-weight: bold; + color: #ffffff; + } + +.label_xl_m { + font-size: 1.0rem; + font-weight: bold; + color: rgb(255, 0, 163); + } + +.label_xl { + font-size: 1.0rem; + font-weight: bold; + color: #ffffff; + } + +.label_l { + font-size: 2.2rem; + font-weight: bold; + color: rgb(255, 255, 255); + } + +.label_l_m { + font-family: Consolas, monaco, monospace; + font-size: 2.2rem; + font-weight: bold; + color: rgb(255, 0, 163); +} + +.label_l_t { + font-size: 1.3rem; + font-weight: bold; + color: rgb(255, 255, 255); +} + +.dropdown { + text-decoration: none; + font-size: 1.2rem; + color: rgb(0,0,0); + text-shadow: 0px 1px 1px rgba(255,255,255,0.3), + 0px 0px 0px rgba(0,0,0,0.7); + border-radius: 2px; + border-style: none; + box-shadow: 3px 3px 10px #888888; + padding: 0px; + background: rgb(255, 255, 255); + transition: color 0.1s, background 0.1s; + border: 3px solid rgb(250, 250, 250); +} + +.dropdown menu { + text-decoration: none; + font-size: 1.2rem; + color: rgba(0,30,95,1.0); + text-shadow: 0px 1px 1px rgba(0,0,0,0.3), + 0px 0px 0px rgba(255,255,255,1); + border-radius: 2px; + border-style: none; + box-shadow: 3px 3px 10px #888888; + padding: 0px; + background: rgb(208, 210, 212); + transition: color 0.1s, background 0.1s; + border: 3px solid rgb(250, 250, 250); +} + +.dropdown button:hover { + text-decoration: none; + color: rgba(248,248,248,0.9); + text-shadow: 0px 1px 1px rgba(0,0,0,0.3), + 0px 0px 0px rgb(255,255,255); + background: rgb(255, 0, 163); + box-shadow: 3px 3px 10px rgb(255, 0, 163); +} + +.menu_button { + text-decoration: none; + font-size: 1.5rem; + color: rgb(0,0,0); + text-shadow: 0px 1px 1px rgba(255,255,255,0.3), + 0px 0px 0px rgba(0,0,0,0.7); + border-radius: 2px; + border-style: none; + box-shadow: 3px 3px 10px #888888; + padding: 3px; + background: rgb(255, 255, 255); + transition: color 0.1s, background 0.1s; + border: 3px solid rgb(250, 250, 250); +} + +/*.menu_button:focus { + background: rgb(255, 0, 163); + box-shadow: 3px 3px 10px rgb(255, 0, 163); + +}*/ + +.menu_button:hover { + text-decoration: none; + color: rgba(248,248,248,0.9); + text-shadow: 0px 1px 1px rgba(0,0,0,0.3), + 0px 0px 0px rgba(255,255,255,1); + background: rgb(255, 0, 163); + box-shadow: 3px 3px 10px rgb(255, 0, 163); +} + + +.box { + box-shadow: 3px 3px 10px #888888; + padding: 3px; +} + + + + + +/* Green button */ +#scan_button, #ap_password_page button{ + background: rgb(201, 210, 0); + border: 3px solid #668a08; +} + +#scan_button:hover, #ap_password_page button:hover { + background: rgb(115, 152, 51); +} + + +/* Red button */ +#clear_confirm button.warning, #clientJoin_banner button.warning { + background: #d72c2c; + border: 3px solid #9e1212; +} +#clear_confirm button.warning:hover, #clientJoin_banner button.warning:hover { + background: #b31414; +} + +.menu_button:focus, #ap_password_page button:focus +{ + outline: 0; +} + + +.bar { + margin: 5px; + -webkit-transition: all 0.3s ease-in-out; + transition: all 0.3s ease-in-out; + background-color: #ffffff; +} + +.nav-item a{ + font-size: 1.3rem; + font-weight: 400; + color: #ffffff; + background: #171717; + padding: 1.0rem 1.0rem; +} + +.nav-item a:hover{ + background: #41c363; +} +.nav-item a.current { + background: black; + outline: solid 1px white; + outline-offset: -1px; +} + +.footer +{ + padding: 1px 10px 0px 10px; + background: #41c363; + font-size: 1.5em; + font-weight: bold; +} diff --git a/ros2_ws/install/hand_controller/share/hand_controller/resources_low/bg_tria.png b/ros2_ws/install/hand_controller/share/hand_controller/resources_low/bg_tria.png new file mode 100644 index 0000000..59ab6d5 Binary files /dev/null and b/ros2_ws/install/hand_controller/share/hand_controller/resources_low/bg_tria.png differ diff --git a/ros2_ws/install/hand_controller/share/hand_controller/resources_low/blank.png b/ros2_ws/install/hand_controller/share/hand_controller/resources_low/blank.png new file mode 100644 index 0000000..5a0e167 Binary files /dev/null and b/ros2_ws/install/hand_controller/share/hand_controller/resources_low/blank.png differ diff --git a/ros2_ws/install/hand_controller/share/hand_controller/resources_low/close.png b/ros2_ws/install/hand_controller/share/hand_controller/resources_low/close.png new file mode 100644 index 0000000..e8499d5 Binary files /dev/null and b/ros2_ws/install/hand_controller/share/hand_controller/resources_low/close.png differ diff --git a/ros2_ws/install/hand_controller/share/hand_controller/resources_low/down.png b/ros2_ws/install/hand_controller/share/hand_controller/resources_low/down.png new file mode 100644 index 0000000..a96aef0 Binary files /dev/null and b/ros2_ws/install/hand_controller/share/hand_controller/resources_low/down.png differ diff --git a/ros2_ws/install/hand_controller/share/hand_controller/resources_low/exit.png b/ros2_ws/install/hand_controller/share/hand_controller/resources_low/exit.png new file mode 100644 index 0000000..3e47a81 Binary files /dev/null and b/ros2_ws/install/hand_controller/share/hand_controller/resources_low/exit.png differ diff --git a/ros2_ws/install/hand_controller/share/hand_controller/resources_low/info.png b/ros2_ws/install/hand_controller/share/hand_controller/resources_low/info.png new file mode 100644 index 0000000..6c5fd29 Binary files /dev/null and b/ros2_ws/install/hand_controller/share/hand_controller/resources_low/info.png differ diff --git a/ros2_ws/install/hand_controller/share/hand_controller/resources_low/left.png b/ros2_ws/install/hand_controller/share/hand_controller/resources_low/left.png new file mode 100644 index 0000000..e87349e Binary files /dev/null and b/ros2_ws/install/hand_controller/share/hand_controller/resources_low/left.png differ diff --git a/ros2_ws/install/hand_controller/share/hand_controller/resources_low/leftright.png b/ros2_ws/install/hand_controller/share/hand_controller/resources_low/leftright.png new file mode 100644 index 0000000..1311872 Binary files /dev/null and b/ros2_ws/install/hand_controller/share/hand_controller/resources_low/leftright.png differ diff --git a/ros2_ws/install/hand_controller/share/hand_controller/resources_low/legend_blue.png b/ros2_ws/install/hand_controller/share/hand_controller/resources_low/legend_blue.png new file mode 100644 index 0000000..30ff117 Binary files /dev/null and b/ros2_ws/install/hand_controller/share/hand_controller/resources_low/legend_blue.png differ diff --git a/ros2_ws/install/hand_controller/share/hand_controller/resources_low/legend_green.png b/ros2_ws/install/hand_controller/share/hand_controller/resources_low/legend_green.png new file mode 100644 index 0000000..a60eca8 Binary files /dev/null and b/ros2_ws/install/hand_controller/share/hand_controller/resources_low/legend_green.png differ diff --git a/ros2_ws/install/hand_controller/share/hand_controller/resources_low/legend_pink.png b/ros2_ws/install/hand_controller/share/hand_controller/resources_low/legend_pink.png new file mode 100644 index 0000000..34882b1 Binary files /dev/null and b/ros2_ws/install/hand_controller/share/hand_controller/resources_low/legend_pink.png differ diff --git a/ros2_ws/install/hand_controller/share/hand_controller/resources_low/legend_yellow.png b/ros2_ws/install/hand_controller/share/hand_controller/resources_low/legend_yellow.png new file mode 100644 index 0000000..702508c Binary files /dev/null and b/ros2_ws/install/hand_controller/share/hand_controller/resources_low/legend_yellow.png differ diff --git a/ros2_ws/install/hand_controller/share/hand_controller/resources_low/right.png b/ros2_ws/install/hand_controller/share/hand_controller/resources_low/right.png new file mode 100644 index 0000000..405c5e0 Binary files /dev/null and b/ros2_ws/install/hand_controller/share/hand_controller/resources_low/right.png differ diff --git a/ros2_ws/install/hand_controller/share/hand_controller/resources_low/sample_weston.ini b/ros2_ws/install/hand_controller/share/hand_controller/resources_low/sample_weston.ini new file mode 100644 index 0000000..79ce3f0 --- /dev/null +++ b/ros2_ws/install/hand_controller/share/hand_controller/resources_low/sample_weston.ini @@ -0,0 +1,22 @@ +# configuration file for Weston +[core] +idle-time=0 +backend=sdm-backend.so +repaint-window=10 + +[output] +name=DSI-1 +mode=on + +[output] +name=DP-1 +mode=on + +[shell] +clock-format=seconds +background-image=/opt/bg_tria.png +background-type=scale-crop + +[launcher] +icon=/opt/tria_demo_launcher_icon.png +path=VISIONAI_PATH_OVERRIDE=/opt/QCS6490-Vision-AI-Demo/visionai.py /opt/QCS6490-Vision-AI-Demo/launch_visionai_with_env.sh diff --git a/ros2_ws/install/hand_controller/share/hand_controller/resources_low/tria_demo_launcher_icon.png b/ros2_ws/install/hand_controller/share/hand_controller/resources_low/tria_demo_launcher_icon.png new file mode 100644 index 0000000..ff6fc34 Binary files /dev/null and b/ros2_ws/install/hand_controller/share/hand_controller/resources_low/tria_demo_launcher_icon.png differ diff --git a/ros2_ws/install/hand_controller/share/hand_controller/resources_low/up.png b/ros2_ws/install/hand_controller/share/hand_controller/resources_low/up.png new file mode 100644 index 0000000..5b5a278 Binary files /dev/null and b/ros2_ws/install/hand_controller/share/hand_controller/resources_low/up.png differ diff --git a/ros2_ws/install/hand_controller/share/hand_controller/resources_low/updown.png b/ros2_ws/install/hand_controller/share/hand_controller/resources_low/updown.png new file mode 100644 index 0000000..0ad4b8c Binary files /dev/null and b/ros2_ws/install/hand_controller/share/hand_controller/resources_low/updown.png differ diff --git a/ros2_ws/install/local_setup.bash b/ros2_ws/install/local_setup.bash new file mode 100644 index 0000000..03f0025 --- /dev/null +++ b/ros2_ws/install/local_setup.bash @@ -0,0 +1,121 @@ +# generated from colcon_bash/shell/template/prefix.bash.em + +# This script extends the environment with all packages contained in this +# prefix path. + +# a bash script is able to determine its own path if necessary +if [ -z "$COLCON_CURRENT_PREFIX" ]; then + _colcon_prefix_bash_COLCON_CURRENT_PREFIX="$(builtin cd "`dirname "${BASH_SOURCE[0]}"`" > /dev/null && pwd)" +else + _colcon_prefix_bash_COLCON_CURRENT_PREFIX="$COLCON_CURRENT_PREFIX" +fi + +# function to prepend a value to a variable +# which uses colons as separators +# duplicates as well as trailing separators are avoided +# first argument: the name of the result variable +# second argument: the value to be prepended +_colcon_prefix_bash_prepend_unique_value() { + # arguments + _listname="$1" + _value="$2" + + # get values from variable + eval _values=\"\$$_listname\" + # backup the field separator + _colcon_prefix_bash_prepend_unique_value_IFS="$IFS" + IFS=":" + # start with the new value + _all_values="$_value" + _contained_value="" + # iterate over existing values in the variable + for _item in $_values; do + # ignore empty strings + if [ -z "$_item" ]; then + continue + fi + # ignore duplicates of _value + if [ "$_item" = "$_value" ]; then + _contained_value=1 + continue + fi + # keep non-duplicate values + _all_values="$_all_values:$_item" + done + unset _item + if [ -z "$_contained_value" ]; then + if [ -n "$COLCON_TRACE" ]; then + if [ "$_all_values" = "$_value" ]; then + echo "export $_listname=$_value" + else + echo "export $_listname=$_value:\$$_listname" + fi + fi + fi + unset _contained_value + # restore the field separator + IFS="$_colcon_prefix_bash_prepend_unique_value_IFS" + unset _colcon_prefix_bash_prepend_unique_value_IFS + # export the updated variable + eval export $_listname=\"$_all_values\" + unset _all_values + unset _values + + unset _value + unset _listname +} + +# add this prefix to the COLCON_PREFIX_PATH +_colcon_prefix_bash_prepend_unique_value COLCON_PREFIX_PATH "$_colcon_prefix_bash_COLCON_CURRENT_PREFIX" +unset _colcon_prefix_bash_prepend_unique_value + +# check environment variable for custom Python executable +if [ -n "$COLCON_PYTHON_EXECUTABLE" ]; then + if [ ! -f "$COLCON_PYTHON_EXECUTABLE" ]; then + echo "error: COLCON_PYTHON_EXECUTABLE '$COLCON_PYTHON_EXECUTABLE' doesn't exist" + return 1 + fi + _colcon_python_executable="$COLCON_PYTHON_EXECUTABLE" +else + # try the Python executable known at configure time + _colcon_python_executable="/usr/bin/python3" + # if it doesn't exist try a fall back + if [ ! -f "$_colcon_python_executable" ]; then + if ! /usr/bin/env python3 --version > /dev/null 2> /dev/null; then + echo "error: unable to find python3 executable" + return 1 + fi + _colcon_python_executable=`/usr/bin/env python3 -c "import sys; print(sys.executable)"` + fi +fi + +# function to source another script with conditional trace output +# first argument: the path of the script +_colcon_prefix_sh_source_script() { + if [ -f "$1" ]; then + if [ -n "$COLCON_TRACE" ]; then + echo "# . \"$1\"" + fi + . "$1" + else + echo "not found: \"$1\"" 1>&2 + fi +} + +# get all commands in topological order +_colcon_ordered_commands="$($_colcon_python_executable "$_colcon_prefix_bash_COLCON_CURRENT_PREFIX/_local_setup_util_sh.py" sh bash)" +unset _colcon_python_executable +if [ -n "$COLCON_TRACE" ]; then + echo "$(declare -f _colcon_prefix_sh_source_script)" + echo "# Execute generated script:" + echo "# <<<" + echo "${_colcon_ordered_commands}" + echo "# >>>" + echo "unset _colcon_prefix_sh_source_script" +fi +eval "${_colcon_ordered_commands}" +unset _colcon_ordered_commands + +unset _colcon_prefix_sh_source_script + +unset _colcon_prefix_bash_COLCON_CURRENT_PREFIX diff --git a/ros2_ws/install/local_setup.sh b/ros2_ws/install/local_setup.sh new file mode 100644 index 0000000..289d496 --- /dev/null +++ b/ros2_ws/install/local_setup.sh @@ -0,0 +1,137 @@ +# generated from colcon_core/shell/template/prefix.sh.em + +# This script extends the environment with all packages contained in this +# prefix path. + +# since a plain shell script can't determine its own path when being sourced +# either use the provided COLCON_CURRENT_PREFIX +# or fall back to the build time prefix (if it exists) +_colcon_prefix_sh_COLCON_CURRENT_PREFIX="/var/roothome/hand_controller/ros2_ws/install" +if [ -z "$COLCON_CURRENT_PREFIX" ]; then + if [ ! -d "$_colcon_prefix_sh_COLCON_CURRENT_PREFIX" ]; then + echo "The build time path \"$_colcon_prefix_sh_COLCON_CURRENT_PREFIX\" doesn't exist. Either source a script for a different shell or set the environment variable \"COLCON_CURRENT_PREFIX\" explicitly." 1>&2 + unset _colcon_prefix_sh_COLCON_CURRENT_PREFIX + return 1 + fi +else + _colcon_prefix_sh_COLCON_CURRENT_PREFIX="$COLCON_CURRENT_PREFIX" +fi + +# function to prepend a value to a variable +# which uses colons as separators +# duplicates as well as trailing separators are avoided +# first argument: the name of the result variable +# second argument: the value to be prepended +_colcon_prefix_sh_prepend_unique_value() { + # arguments + _listname="$1" + _value="$2" + + # get values from variable + eval _values=\"\$$_listname\" + # backup the field separator + _colcon_prefix_sh_prepend_unique_value_IFS="$IFS" + IFS=":" + # start with the new value + _all_values="$_value" + _contained_value="" + # iterate over existing values in the variable + for _item in $_values; do + # ignore empty strings + if [ -z "$_item" ]; then + continue + fi + # ignore duplicates of _value + if [ "$_item" = "$_value" ]; then + _contained_value=1 + continue + fi + # keep non-duplicate values + _all_values="$_all_values:$_item" + done + unset _item + if [ -z "$_contained_value" ]; then + if [ -n "$COLCON_TRACE" ]; then + if [ "$_all_values" = "$_value" ]; then + echo "export $_listname=$_value" + else + echo "export $_listname=$_value:\$$_listname" + fi + fi + fi + unset _contained_value + # restore the field separator + IFS="$_colcon_prefix_sh_prepend_unique_value_IFS" + unset _colcon_prefix_sh_prepend_unique_value_IFS + # export the updated variable + eval export $_listname=\"$_all_values\" + unset _all_values + unset _values + + unset _value + unset _listname +} + +# add this prefix to the COLCON_PREFIX_PATH +_colcon_prefix_sh_prepend_unique_value COLCON_PREFIX_PATH "$_colcon_prefix_sh_COLCON_CURRENT_PREFIX" +unset _colcon_prefix_sh_prepend_unique_value + +# check environment variable for custom Python executable +if [ -n "$COLCON_PYTHON_EXECUTABLE" ]; then + if [ ! -f "$COLCON_PYTHON_EXECUTABLE" ]; then + echo "error: COLCON_PYTHON_EXECUTABLE '$COLCON_PYTHON_EXECUTABLE' doesn't exist" + return 1 + fi + _colcon_python_executable="$COLCON_PYTHON_EXECUTABLE" +else + # try the Python executable known at configure time + _colcon_python_executable="/usr/bin/python3" + # if it doesn't exist try a fall back + if [ ! -f "$_colcon_python_executable" ]; then + if ! /usr/bin/env python3 --version > /dev/null 2> /dev/null; then + echo "error: unable to find python3 executable" + return 1 + fi + _colcon_python_executable=`/usr/bin/env python3 -c "import sys; print(sys.executable)"` + fi +fi + +# function to source another script with conditional trace output +# first argument: the path of the script +_colcon_prefix_sh_source_script() { + if [ -f "$1" ]; then + if [ -n "$COLCON_TRACE" ]; then + echo "# . \"$1\"" + fi + . "$1" + else + echo "not found: \"$1\"" 1>&2 + fi +} + +# get all commands in topological order +_colcon_ordered_commands="$($_colcon_python_executable "$_colcon_prefix_sh_COLCON_CURRENT_PREFIX/_local_setup_util_sh.py" sh)" +unset _colcon_python_executable +if [ -n "$COLCON_TRACE" ]; then + echo "_colcon_prefix_sh_source_script() { + if [ -f \"\$1\" ]; then + if [ -n \"\$COLCON_TRACE\" ]; then + echo \"# . \\\"\$1\\\"\" + fi + . \"\$1\" + else + echo \"not found: \\\"\$1\\\"\" 1>&2 + fi + }" + echo "# Execute generated script:" + echo "# <<<" + echo "${_colcon_ordered_commands}" + echo "# >>>" + echo "unset _colcon_prefix_sh_source_script" +fi +eval "${_colcon_ordered_commands}" +unset _colcon_ordered_commands + +unset _colcon_prefix_sh_source_script + +unset _colcon_prefix_sh_COLCON_CURRENT_PREFIX diff --git a/ros2_ws/install/setup.bash b/ros2_ws/install/setup.bash new file mode 100644 index 0000000..d3b212c --- /dev/null +++ b/ros2_ws/install/setup.bash @@ -0,0 +1,31 @@ +# generated from colcon_bash/shell/template/prefix_chain.bash.em + +# This script extends the environment with the environment of other prefix +# paths which were sourced when this file was generated as well as all packages +# contained in this prefix path. + +# function to source another script with conditional trace output +# first argument: the path of the script +_colcon_prefix_chain_bash_source_script() { + if [ -f "$1" ]; then + if [ -n "$COLCON_TRACE" ]; then + echo "# . \"$1\"" + fi + . "$1" + else + echo "not found: \"$1\"" 1>&2 + fi +} + +# source chained prefixes +# setting COLCON_CURRENT_PREFIX avoids determining the prefix in the sourced script +COLCON_CURRENT_PREFIX="/root/hand_controller/ros2_ws/install" +_colcon_prefix_chain_bash_source_script "$COLCON_CURRENT_PREFIX/local_setup.bash" + +# source this prefix +# setting COLCON_CURRENT_PREFIX avoids determining the prefix in the sourced script +COLCON_CURRENT_PREFIX="$(builtin cd "`dirname "${BASH_SOURCE[0]}"`" > /dev/null && pwd)" +_colcon_prefix_chain_bash_source_script "$COLCON_CURRENT_PREFIX/local_setup.bash" + +unset COLCON_CURRENT_PREFIX +unset _colcon_prefix_chain_bash_source_script diff --git a/ros2_ws/install/setup.sh b/ros2_ws/install/setup.sh new file mode 100644 index 0000000..4869779 --- /dev/null +++ b/ros2_ws/install/setup.sh @@ -0,0 +1,45 @@ +# generated from colcon_core/shell/template/prefix_chain.sh.em + +# This script extends the environment with the environment of other prefix +# paths which were sourced when this file was generated as well as all packages +# contained in this prefix path. + +# since a plain shell script can't determine its own path when being sourced +# either use the provided COLCON_CURRENT_PREFIX +# or fall back to the build time prefix (if it exists) +_colcon_prefix_chain_sh_COLCON_CURRENT_PREFIX=/var/roothome/hand_controller/ros2_ws/install +if [ ! -z "$COLCON_CURRENT_PREFIX" ]; then + _colcon_prefix_chain_sh_COLCON_CURRENT_PREFIX="$COLCON_CURRENT_PREFIX" +elif [ ! -d "$_colcon_prefix_chain_sh_COLCON_CURRENT_PREFIX" ]; then + echo "The build time path \"$_colcon_prefix_chain_sh_COLCON_CURRENT_PREFIX\" doesn't exist. Either source a script for a different shell or set the environment variable \"COLCON_CURRENT_PREFIX\" explicitly." 1>&2 + unset _colcon_prefix_chain_sh_COLCON_CURRENT_PREFIX + return 1 +fi + +# function to source another script with conditional trace output +# first argument: the path of the script +_colcon_prefix_chain_sh_source_script() { + if [ -f "$1" ]; then + if [ -n "$COLCON_TRACE" ]; then + echo "# . \"$1\"" + fi + . "$1" + else + echo "not found: \"$1\"" 1>&2 + fi +} + +# source chained prefixes +# setting COLCON_CURRENT_PREFIX avoids relying on the build time prefix of the sourced script +COLCON_CURRENT_PREFIX="/root/hand_controller/ros2_ws/install" +_colcon_prefix_chain_sh_source_script "$COLCON_CURRENT_PREFIX/local_setup.sh" + + +# source this prefix +# setting COLCON_CURRENT_PREFIX avoids relying on the build time prefix of the sourced script +COLCON_CURRENT_PREFIX="$_colcon_prefix_chain_sh_COLCON_CURRENT_PREFIX" +_colcon_prefix_chain_sh_source_script "$COLCON_CURRENT_PREFIX/local_setup.sh" + +unset _colcon_prefix_chain_sh_COLCON_CURRENT_PREFIX +unset _colcon_prefix_chain_sh_source_script +unset COLCON_CURRENT_PREFIX diff --git a/ros2_ws/log/COLCON_IGNORE b/ros2_ws/log/COLCON_IGNORE new file mode 100644 index 0000000..e69de29