diff --git a/FORMATTING.md b/FORMATTING.md
deleted file mode 100644
index 299aae8..0000000
--- a/FORMATTING.md
+++ /dev/null
@@ -1,101 +0,0 @@
-# Python Code Formatting Guide
-
-This project uses automated Python code formatting to ensure consistent code style across the entire codebase.
-
-## ๐ Quick Start
-
-### Option 1: Use the Virtual Environment (Recommended)
-```bash
-# One-time setup
-./setup_venv_formatters.sh
-
-# Format your code
-./format_with_venv.sh
-
-# Or activate and use manually
-source activate_formatters.sh
-black . --line-length 88 --target-version py38
-isort . --profile black
-flake8 .
-```
-
-### Option 2: Use Existing Environment
-```bash
-# If you already have the tools installed
-./format.sh
-```
-
-## ๐ง Tools Used
-
-- **Black** (24.10.0+): Automatic Python code formatter
-- **isort** (6.0.1+): Import statement organizer
-- **flake8** (7.2.0+): Code linter and style checker
-- **pre-commit** (3.8.0+): Git hooks for automatic formatting
-
-## ๐ Files in This Setup
-
-| File | Purpose |
-|------|---------|
-| `setup_venv_formatters.sh` | Creates virtual environment with formatters |
-| `activate_formatters.sh` | Quick activation script |
-| `format_with_venv.sh` | Formats code using virtual environment |
-| `format.sh` | Legacy formatting script |
-| `pyproject.toml` | Tool configuration |
-| `.flake8` | Flake8-specific configuration |
-| `.pre-commit-config.yaml` | Git hooks configuration |
-
-## ๐ CI/CD Integration
-
-The CI pipeline automatically checks code formatting on every pull request:
-
-1. **Black formatting check** - Ensures consistent code style
-2. **Import sorting check** - Validates import organization
-3. **Flake8 linting** - Checks for code quality issues
-
-If any check fails, the CI will block the PR with clear instructions to run the formatter.
-
-## ๐ ๏ธ Configuration
-
-### Black Settings
-- Line length: 88 characters
-- Target Python version: 3.8
-- Profile: Compatible with isort
-
-### Flake8 Settings
-- Ignores common style issues that don't affect functionality
-- Focuses on important code quality checks
-- Uses same line length as Black
-
-## ๐ก Usage Tips
-
-1. **Before committing**: Pre-commit hooks will automatically format your code
-2. **If CI fails**: Run `./format_with_venv.sh` to fix all issues
-3. **For specific files**: Use tools directly after activating the environment
-
-## ๐ Troubleshooting
-
-### Virtual Environment Issues
-```bash
-# If the environment is corrupted, recreate it
-rm -rf .venv-formatters
-./setup_venv_formatters.sh
-```
-
-### CI Version Conflicts
-The CI uses flexible version ranges to ensure compatibility with the latest available public versions of the formatting tools.
-
-### Pre-commit Hook Issues
-```bash
-# Reinstall hooks if needed
-source activate_formatters.sh
-pre-commit install --overwrite
-```
-
-## ๐ Version Compatibility
-
-This setup is designed to work with the latest publicly available versions:
-- Black: `>=24.0.0,<25.0.0`
-- isort: `>=5.12.0,<7.0.0`
-- flake8: `>=6.0.0,<8.0.0`
-
-The virtual environment approach ensures consistent versions across all developers and CI environments.
diff --git a/README.md b/README.md
index 6ffc34f..1e06c41 100644
--- a/README.md
+++ b/README.md
@@ -9,7 +9,7 @@
- Philosophy
+ Philosophy ยท Autonomy Docs ยท
diff --git a/autonomy/.python-version b/autonomy/.python-version
new file mode 100644
index 0000000..24ee5b1
--- /dev/null
+++ b/autonomy/.python-version
@@ -0,0 +1 @@
+3.13
diff --git a/autonomy/CMakeLists.txt b/autonomy/CMakeLists.txt
index 5581c17..4995384 100644
--- a/autonomy/CMakeLists.txt
+++ b/autonomy/CMakeLists.txt
@@ -8,6 +8,7 @@ add_compile_options(-std=c++11)
catkin_python_setup()
find_package(catkin REQUIRED COMPONENTS
+ autonomy_interfaces
nav_msgs
sensor_msgs
stereo_msgs
@@ -26,25 +27,70 @@ if (CATKIN_ENABLE_TESTING)
find_package(rostest REQUIRED)
endif()
+# --- Begin: Copy shared interface definitions into this package ---
+# Location of shared interface source files (relative to this package)
+set(SHARED_INTERFACES_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../shared_interfaces")
+
+# Ensure local interface directories exist
+file(MAKE_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/msg")
+file(MAKE_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/srv")
+file(MAKE_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/action")
+
+# Gather shared files
+file(GLOB SHARED_MSGS "${SHARED_INTERFACES_DIR}/msg/*.msg")
+file(GLOB SHARED_SRVS "${SHARED_INTERFACES_DIR}/srv/*.srv")
+file(GLOB SHARED_ACTIONS "${SHARED_INTERFACES_DIR}/action/*.action")
+
+# Copy shared files into this package so catkin can find them
+foreach(f ${SHARED_MSGS})
+ get_filename_component(_name "${f}" NAME)
+ configure_file("${f}" "${CMAKE_CURRENT_SOURCE_DIR}/msg/${_name}" COPYONLY)
+endforeach()
+foreach(f ${SHARED_SRVS})
+ get_filename_component(_name "${f}" NAME)
+ configure_file("${f}" "${CMAKE_CURRENT_SOURCE_DIR}/srv/${_name}" COPYONLY)
+endforeach()
+foreach(f ${SHARED_ACTIONS})
+ get_filename_component(_name "${f}" NAME)
+ configure_file("${f}" "${CMAKE_CURRENT_SOURCE_DIR}/action/${_name}" COPYONLY)
+endforeach()
+
+# Build lists of filenames relative to package dirs
+set(MSG_FILES)
+foreach(f ${SHARED_MSGS})
+ get_filename_component(_name "${f}" NAME)
+ list(APPEND MSG_FILES ${_name})
+endforeach()
+
+set(SRV_FILES)
+foreach(f ${SHARED_SRVS})
+ get_filename_component(_name "${f}" NAME)
+ list(APPEND SRV_FILES ${_name})
+endforeach()
+
+set(ACTION_FILES)
+foreach(f ${SHARED_ACTIONS})
+ get_filename_component(_name "${f}" NAME)
+ list(APPEND ACTION_FILES ${_name})
+endforeach()
+# --- End: Copy shared interface definitions ---
+
# Add message files
add_message_files(
FILES
- Detection.msg
- Detections.msg
+ ${MSG_FILES}
)
add_action_files(
FILES
- NavigateToWaypoint.action
+ ${ACTION_FILES}
)
# Add service files
add_service_files(
FILES
- SetColorFilter.srv
- SetYoloModel.srv
- FireTorpedo.srv
+ ${SRV_FILES}
)
# Generate added messages and services
@@ -58,6 +104,7 @@ generate_messages(
catkin_package(
CATKIN_DEPENDS
+ autonomy_interfaces
nav_msgs
sensor_msgs
stereo_msgs
diff --git a/autonomy/README.md b/autonomy/README.md
index 31b780a..733a67c 100644
--- a/autonomy/README.md
+++ b/autonomy/README.md
@@ -1,5 +1,11 @@
# Hydrus Autonomy System
+
+This module is going to be deprecated for the following reasons:
+
+
+
+
The Hydrus Autonomy System is a comprehensive ROS-based underwater vehicle control and mission management framework. This README documents all available scripts, their purpose, and usage instructions.
> โ ๏ธ **Warning**: The mission API is currently under development. Some features may not work as expected and are subject to change.
diff --git a/autonomy/launch/depth_estimation.launch b/autonomy/launch/depth_estimation.launch
deleted file mode 100644
index 5e05225..0000000
--- a/autonomy/launch/depth_estimation.launch
+++ /dev/null
@@ -1,22 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/autonomy/launch/godot_bridge.launch b/autonomy/launch/godot_bridge.launch
deleted file mode 100644
index b91975a..0000000
--- a/autonomy/launch/godot_bridge.launch
+++ /dev/null
@@ -1,47 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/autonomy/launch/profiler.launch b/autonomy/launch/profiler.launch
deleted file mode 100644
index a7677e1..0000000
--- a/autonomy/launch/profiler.launch
+++ /dev/null
@@ -1,23 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- $(arg target_nodes)
-
-
-
diff --git a/autonomy/launch/simulation.launch b/autonomy/launch/simulation.launch
deleted file mode 100644
index 3e8c19e..0000000
--- a/autonomy/launch/simulation.launch
+++ /dev/null
@@ -1,37 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/autonomy/main.py b/autonomy/main.py
new file mode 100644
index 0000000..428c106
--- /dev/null
+++ b/autonomy/main.py
@@ -0,0 +1,6 @@
+def main():
+ print("Hello from autonomy!")
+
+
+if __name__ == "__main__":
+ main()
diff --git a/autonomy/package.xml b/autonomy/package.xml
index 20d4ffe..b503802 100644
--- a/autonomy/package.xml
+++ b/autonomy/package.xml
@@ -14,6 +14,9 @@
catkin_pkgpython-catkin-pkg
+
+ autonomy_interfaces
+
nav_msgssensor_msgs
diff --git a/autonomy/pyproject.toml b/autonomy/pyproject.toml
new file mode 100644
index 0000000..5294fc3
--- /dev/null
+++ b/autonomy/pyproject.toml
@@ -0,0 +1,16 @@
+[project]
+name = "autonomy"
+version = "0.1.0"
+description = "Add your description here"
+readme = "README.md"
+requires-python = ">=3.12"
+dependencies = [
+ "colorama>=0.4.6",
+ "fastapi>=0.116.1",
+ "matplotlib>=3.10.5",
+ "opencv-python>=4.11.0.86",
+ "pyserial>=3.5",
+ "requests>=2.32.5",
+ "termcolor>=3.1.0",
+ "uvicorn>=0.35.0",
+]
diff --git a/autonomy/scripts/cv/depth_estimation_node.py b/autonomy/scripts/cv/depth_estimation_node.py
deleted file mode 100644
index 463a612..0000000
--- a/autonomy/scripts/cv/depth_estimation_node.py
+++ /dev/null
@@ -1,135 +0,0 @@
-#!/usr/bin/env python3
-"""
-ROS node for depth estimation using Depth-Anything-ONNX
-Publishes depth maps and provides depth estimation services
-"""
-
-import os
-import sys
-
-import cv2
-import numpy as np
-import rospy
-from cv_bridge import CvBridge
-from sensor_msgs.msg import CompressedImage, Image
-from std_msgs.msg import Float32
-
-# Add the computer vision module to path
-sys.path.append(os.path.dirname(__file__))
-
-try:
- from computer_vision.depth_estimation import DepthEstimator
-
- DEPTH_ESTIMATION_AVAILABLE = True
-except ImportError as e:
- DEPTH_ESTIMATION_AVAILABLE = False
- rospy.logwarn(f"Depth estimation dependencies not available: {e}")
- rospy.logwarn("Install with: pip install -e .[depth-estimation]")
-
-
-class DepthEstimationNode:
- """ROS node for depth estimation"""
-
- def __init__(self):
- rospy.init_node("depth_estimation_node")
-
- # Check if depth estimation is available
- if not DEPTH_ESTIMATION_AVAILABLE:
- rospy.logerr("Depth estimation dependencies not installed!")
- rospy.logerr("Install with: pip install -e .[depth-estimation]")
- rospy.signal_shutdown("Missing dependencies")
- return
-
- # Parameters
- self.encoder = rospy.get_param("~encoder", "vitb")
- self.model_path = rospy.get_param("~model_path", None)
- self.input_topic = rospy.get_param("~input_topic", "/camera/image_raw")
- self.use_compressed = rospy.get_param("~use_compressed", False)
- self.publish_colored = rospy.get_param("~publish_colored", True)
-
- # Initialize components
- self.bridge = CvBridge()
-
- try:
- self.depth_estimator = DepthEstimator(
- model_path=self.model_path, encoder=self.encoder
- )
- except ImportError as e:
- rospy.logerr(f"Failed to initialize depth estimator: {e}")
- rospy.signal_shutdown("Depth estimator initialization failed")
- return
-
- # Publishers
- self.depth_pub = rospy.Publisher("~depth_map", Image, queue_size=1)
- self.depth_colored_pub = rospy.Publisher("~depth_colored", Image, queue_size=1)
- self.center_depth_pub = rospy.Publisher("~center_depth", Float32, queue_size=1)
-
- # Subscribers
- if self.use_compressed:
- self.image_sub = rospy.Subscriber(
- self.input_topic, CompressedImage, self.compressed_image_callback
- )
- else:
- self.image_sub = rospy.Subscriber(
- self.input_topic, Image, self.image_callback
- )
-
- rospy.loginfo(f"Depth estimation node started with encoder: {self.encoder}")
- rospy.loginfo(f"Subscribing to: {self.input_topic}")
-
- def image_callback(self, msg):
- """Process uncompressed image messages"""
- try:
- # Convert ROS image to OpenCV
- cv_image = self.bridge.imgmsg_to_cv2(msg, "bgr8")
- self.process_image(cv_image, msg.header)
- except Exception as e:
- rospy.logerr(f"Error processing image: {e}")
-
- def compressed_image_callback(self, msg):
- """Process compressed image messages"""
- try:
- # Convert compressed image to OpenCV
- np_arr = np.frombuffer(msg.data, np.uint8)
- cv_image = cv2.imdecode(np_arr, cv2.IMREAD_COLOR)
- self.process_image(cv_image, msg.header)
- except Exception as e:
- rospy.logerr(f"Error processing compressed image: {e}")
-
- def process_image(self, cv_image, header):
- """Process image and publish depth information"""
- try:
- # Estimate depth
- raw_depth, viz_depth = self.depth_estimator.estimate_depth(cv_image)
-
- # Publish raw depth map
- depth_msg = self.bridge.cv2_to_imgmsg(raw_depth.astype(np.float32), "32FC1")
- depth_msg.header = header
- self.depth_pub.publish(depth_msg)
-
- # Publish colored depth map if enabled
- if self.publish_colored:
- colored_depth = self.depth_estimator.create_colored_depth_map(viz_depth)
- colored_msg = self.bridge.cv2_to_imgmsg(colored_depth, "bgr8")
- colored_msg.header = header
- self.depth_colored_pub.publish(colored_msg)
-
- # Publish center depth value
- h, w = cv_image.shape[:2]
- center_depth = self.depth_estimator.get_depth_at_point(
- raw_depth, w // 2, h // 2
- )
- center_msg = Float32()
- center_msg.data = center_depth
- self.center_depth_pub.publish(center_msg)
-
- except Exception as e:
- rospy.logerr(f"Error in depth processing: {e}")
-
-
-if __name__ == "__main__":
- try:
- node = DepthEstimationNode()
- rospy.spin()
- except rospy.ROSInterruptException:
- pass
diff --git a/autonomy/scripts/profiler/profiling_decorators.py b/autonomy/scripts/profiler/profiling_decorators.py
deleted file mode 100644
index a155adb..0000000
--- a/autonomy/scripts/profiler/profiling_decorators.py
+++ /dev/null
@@ -1,276 +0,0 @@
-#!/usr/bin/env python3
-"""
-Function Profiling Decorators
-
-Decorators and utilities for profiling function execution times in ROS nodes.
-Use these to instrument critical functions for detailed performance analysis.
-
-Usage:
- from autonomy.scripts.services.profiling_decorators import profile_function, ProfileManager
-
- # Decorate functions you want to profile
- @profile_function("cv_processing")
- def process_image(self, image):
- # Your function code here
- pass
-
- # Get profiling results
- ProfileManager.get_stats()
-"""
-
-import functools
-import json
-import threading
-import time
-from collections import defaultdict, deque
-from typing import Any, Callable, Dict, List, Optional
-
-
-class ProfileManager:
- """Global manager for function profiling data"""
-
- _instance = None
- _lock = threading.Lock()
-
- def __new__(cls):
- if cls._instance is None:
- with cls._lock:
- if cls._instance is None:
- cls._instance = super().__new__(cls)
- cls._instance._initialized = False
- return cls._instance
-
- def __init__(self):
- if self._initialized:
- return
-
- self.function_stats = defaultdict(
- lambda: {
- "call_count": 0,
- "total_time": 0.0,
- "min_time": float("inf"),
- "max_time": 0.0,
- "recent_times": deque(maxlen=100), # Last 100 calls
- "avg_time": 0.0,
- "calls_per_second": 0.0,
- "last_call": 0.0,
- }
- )
- self.node_name = "unknown"
- self._initialized = True
-
- def set_node_name(self, name: str):
- """Set the name of the current node"""
- self.node_name = name
-
- def record_function_call(self, function_name: str, execution_time: float):
- """Record a function call and its execution time"""
- with self._lock:
- stats = self.function_stats[function_name]
- current_time = time.time()
-
- # Update basic stats
- stats["call_count"] += 1
- stats["total_time"] += execution_time
- stats["min_time"] = min(stats["min_time"], execution_time)
- stats["max_time"] = max(stats["max_time"], execution_time)
- stats["recent_times"].append(execution_time)
- stats["last_call"] = current_time
-
- # Calculate averages
- stats["avg_time"] = stats["total_time"] / stats["call_count"]
-
- # Calculate calls per second (based on recent calls)
- if len(stats["recent_times"]) > 1:
- time_span = current_time - (current_time - len(stats["recent_times"]))
- if time_span > 0:
- stats["calls_per_second"] = len(stats["recent_times"]) / time_span
-
- def get_stats(self) -> Dict[str, Any]:
- """Get current profiling statistics"""
- with self._lock:
- return {"node_name": self.node_name, "functions": dict(self.function_stats)}
-
- def get_top_functions(
- self, metric: str = "total_time", limit: int = 10
- ) -> List[tuple]:
- """Get top functions by specified metric"""
- with self._lock:
- functions = []
- for func_name, stats in self.function_stats.items():
- if metric in stats:
- functions.append((func_name, stats[metric]))
-
- functions.sort(key=lambda x: x[1], reverse=True)
- return functions[:limit]
-
- def reset_stats(self):
- """Reset all profiling statistics"""
- with self._lock:
- self.function_stats.clear()
-
- @classmethod
- def get_instance(cls):
- """Get the singleton instance"""
- return cls()
-
-
-def profile_function(name: Optional[str] = None, category: str = "general"):
- """
- Decorator to profile function execution time
-
- Args:
- name: Custom name for the function (defaults to function name)
- category: Category for grouping related functions
- """
-
- def decorator(func: Callable) -> Callable:
- function_name = name or f"{category}.{func.__name__}"
-
- @functools.wraps(func)
- def wrapper(*args, **kwargs):
- start_time = time.perf_counter()
- try:
- result = func(*args, **kwargs)
- return result
- finally:
- end_time = time.perf_counter()
- execution_time = end_time - start_time
- ProfileManager.get_instance().record_function_call(
- function_name, execution_time
- )
-
- return wrapper
-
- return decorator
-
-
-def profile_method(name: Optional[str] = None, category: str = "methods"):
- """
- Decorator specifically for class methods
-
- Args:
- name: Custom name for the method (defaults to class.method)
- category: Category for grouping related methods
- """
-
- def decorator(func: Callable) -> Callable:
- @functools.wraps(func)
- def wrapper(self, *args, **kwargs):
- if name:
- function_name = name
- else:
- class_name = self.__class__.__name__
- function_name = f"{category}.{class_name}.{func.__name__}"
-
- start_time = time.perf_counter()
- try:
- result = func(self, *args, **kwargs)
- return result
- finally:
- end_time = time.perf_counter()
- execution_time = end_time - start_time
- ProfileManager.get_instance().record_function_call(
- function_name, execution_time
- )
-
- return wrapper
-
- return decorator
-
-
-class FunctionProfiler:
- """Context manager for profiling code blocks"""
-
- def __init__(self, name: str):
- self.name = name
- self.start_time = None
-
- def __enter__(self):
- self.start_time = time.perf_counter()
- return self
-
- def __exit__(self, exc_type, exc_val, exc_tb):
- if self.start_time:
- execution_time = time.perf_counter() - self.start_time
- ProfileManager.get_instance().record_function_call(
- self.name, execution_time
- )
-
-
-def profile_ros_callback(topic_name: str):
- """
- Decorator specifically for ROS callback functions
-
- Args:
- topic_name: Name of the ROS topic for identification
- """
-
- def decorator(func: Callable) -> Callable:
- function_name = f"ros_callback.{topic_name}.{func.__name__}"
-
- @functools.wraps(func)
- def wrapper(*args, **kwargs):
- start_time = time.perf_counter()
- try:
- result = func(*args, **kwargs)
- return result
- finally:
- end_time = time.perf_counter()
- execution_time = end_time - start_time
- ProfileManager.get_instance().record_function_call(
- function_name, execution_time
- )
-
- return wrapper
-
- return decorator
-
-
-def export_profiling_data(filename: str):
- """Export profiling data to JSON file"""
- stats = ProfileManager.get_instance().get_stats()
-
- # Convert deque objects to lists for JSON serialization
- for func_stats in stats["functions"].values():
- if "recent_times" in func_stats:
- func_stats["recent_times"] = list(func_stats["recent_times"])
-
- with open(filename, "w") as f:
- json.dump(stats, f, indent=2, default=str)
-
-
-# Example usage and testing
-if __name__ == "__main__":
- # Example of how to use the profiling decorators
-
- @profile_function("test_function", "testing")
- def slow_function():
- time.sleep(0.1)
- return "done"
-
- @profile_method("test_method", "testing")
- class TestClass:
- def slow_method(self):
- time.sleep(0.05)
- return "method done"
-
- # Test the profiling
- ProfileManager.get_instance().set_node_name("test_node")
-
- # Call functions multiple times
- for i in range(5):
- slow_function()
-
- test_obj = TestClass()
- for i in range(3):
- test_obj.slow_method()
-
- # Use context manager
- with FunctionProfiler("manual_timing"):
- time.sleep(0.02)
-
- # Print results
- stats = ProfileManager.get_instance().get_stats()
- print("Profiling Results:")
- print(json.dumps(stats, indent=2, default=str))
diff --git a/autonomy/scripts/profiler/profiling_test.py b/autonomy/scripts/profiler/profiling_test.py
deleted file mode 100644
index 60a9bc8..0000000
--- a/autonomy/scripts/profiler/profiling_test.py
+++ /dev/null
@@ -1,114 +0,0 @@
-#!/usr/bin/env python3
-"""
-Simple test of the profiling decorators without ROS dependencies
-This allows testing the profiling functionality independently
-"""
-
-import os
-import sys
-import time
-
-import numpy as np
-
-# Add the current directory to path
-sys.path.append(os.path.dirname(__file__))
-
-from profiling_decorators import (
- FunctionProfiler,
- ProfileManager,
- export_profiling_data,
- profile_function,
- profile_method,
-)
-
-# Initialize profiling
-ProfileManager.get_instance().set_node_name("profiling_test")
-
-
-@profile_function("image_processing", "cv")
-def process_image(image):
- """Mock image processing function"""
- with FunctionProfiler("cv.blur"):
- # Simulate Gaussian blur
- time.sleep(0.01) # Simulate processing time
-
- with FunctionProfiler("cv.threshold"):
- # Simulate thresholding
- time.sleep(0.005)
-
- return True
-
-
-@profile_function("detection", "cv")
-def detect_objects(image):
- """Mock object detection"""
- with FunctionProfiler("cv.inference"):
- time.sleep(0.02) # Simulate model inference
-
- with FunctionProfiler("cv.postprocess"):
- time.sleep(0.003) # Simulate post-processing
-
- return [{"bbox": [100, 100, 50, 50], "confidence": 0.8}]
-
-
-class MockProcessor:
- @profile_method("process_frame", "pipeline")
- def process_frame(self, frame):
- """Mock frame processing pipeline"""
- process_image(frame)
- detections = detect_objects(frame)
- return detections
-
-
-def main():
- print("๐ง Testing Profiling Decorators")
- print("=" * 40)
-
- # Create mock data
- mock_image = np.zeros((480, 640, 3), dtype=np.uint8)
- processor = MockProcessor()
-
- # Run multiple iterations to collect data
- print("Running profiling test iterations...")
- for i in range(20):
- processor.process_frame(mock_image)
- if i % 5 == 0:
- print(f"Completed {i+1}/20 iterations")
-
- # Get and display results
- stats = ProfileManager.get_instance().get_stats()
- print(f"\n๐ Profiling Results:")
- print(f"Node: {stats['node_name']}")
- print(f"Functions profiled: {len(stats['functions'])}")
- print()
-
- # Sort functions by total time
- sorted_functions = sorted(
- stats["functions"].items(), key=lambda x: x[1]["total_time"], reverse=True
- )
-
- print("Function Performance (sorted by total time):")
- print("-" * 60)
- print(f"{'Function':<25} {'Calls':<8} {'Avg (ms)':<10} {'Total (ms)':<12}")
- print("-" * 60)
-
- for func_name, func_stats in sorted_functions:
- avg_time = func_stats["avg_time"] * 1000
- total_time = func_stats["total_time"] * 1000
- call_count = func_stats["call_count"]
- print(f"{func_name:<25} {call_count:<8} {avg_time:<10.2f} {total_time:<12.2f}")
-
- # Export data
- export_filename = "profiling_test_results.json"
- export_profiling_data(export_filename)
- print(f"\nโ Detailed results exported to {export_filename}")
-
- # Show top slowest functions
- print(f"\n๐ Top 3 slowest functions (by average time):")
- top_slow = ProfileManager.get_instance().get_top_functions("avg_time", 3)
- for i, (func_name, avg_time) in enumerate(top_slow, 1):
- print(f" {i}. {func_name}: {avg_time*1000:.2f}ms avg")
-
-
-if __name__ == "__main__":
- main()
diff --git a/autonomy/src/cv_publishers.py b/autonomy/src/cv_publishers.py
index 913734d..463f1df 100755
--- a/autonomy/src/cv_publishers.py
+++ b/autonomy/src/cv_publishers.py
@@ -8,15 +8,14 @@
from typing import List, Optional, Tuple
-import custom_types
import numpy as np
import rospy
from geometry_msgs.msg import Point, PoseStamped
from sensor_msgs.msg import CameraInfo, Image, RegionOfInterest
from visualization_msgs.msg import Marker, MarkerArray
-from autonomy.msg import Detection, Detections
-from autonomy.srv import (
+from autonomy_interfaces.msg import Detection, Detections
+from autonomy_interfaces.srv import (
SetColorFilter,
SetColorFilterResponse,
SetYoloModel,
@@ -25,6 +24,7 @@
# Import our custom detection core module
from computer_vision.detection_core import ColorFilterConfig, DetectionPipelineManager
+from computer_vision import custom_types
############################
# Global variables
diff --git a/autonomy2/CMakeLists.txt b/autonomy2/CMakeLists.txt
new file mode 100644
index 0000000..f41b40b
--- /dev/null
+++ b/autonomy2/CMakeLists.txt
@@ -0,0 +1,71 @@
+cmake_minimum_required(VERSION 3.8)
+project(autonomy)
+
+if(NOT CMAKE_CXX_STANDARD)
+ set(CMAKE_CXX_STANDARD 17)
+endif()
+
+find_package(ament_cmake REQUIRED)
+find_package(rclcpp REQUIRED)
+find_package(sensor_msgs REQUIRED)
+find_package(geometry_msgs REQUIRED)
+find_package(rosidl_default_generators REQUIRED)
+
+# --- Begin: Copy shared interface definitions into this package ---
+# Location of shared interface source files (relative to this package)
+set(SHARED_INTERFACES_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../shared_interfaces")
+
+# Ensure local interface directories exist
+file(MAKE_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/msg")
+file(MAKE_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/srv")
+file(MAKE_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/action")
+
+# Gather shared files
+file(GLOB SHARED_MSGS "${SHARED_INTERFACES_DIR}/msg/*.msg")
+file(GLOB SHARED_SRVS "${SHARED_INTERFACES_DIR}/srv/*.srv")
+file(GLOB SHARED_ACTIONS "${SHARED_INTERFACES_DIR}/action/*.action")
+
+# Copy shared files into this package so rosidl can find them
+foreach(f ${SHARED_MSGS})
+ get_filename_component(_name "${f}" NAME)
+ configure_file("${f}" "${CMAKE_CURRENT_SOURCE_DIR}/msg/${_name}" COPYONLY)
+endforeach()
+foreach(f ${SHARED_SRVS})
+ get_filename_component(_name "${f}" NAME)
+ configure_file("${f}" "${CMAKE_CURRENT_SOURCE_DIR}/srv/${_name}" COPYONLY)
+endforeach()
+foreach(f ${SHARED_ACTIONS})
+ get_filename_component(_name "${f}" NAME)
+ configure_file("${f}" "${CMAKE_CURRENT_SOURCE_DIR}/action/${_name}" COPYONLY)
+endforeach()
+
+# Build lists of filenames relative to package dirs
+set(MSG_FILES)
+foreach(f ${SHARED_MSGS})
+ get_filename_component(_name "${f}" NAME)
+ list(APPEND MSG_FILES "msg/${_name}")
+endforeach()
+
+set(SRV_FILES)
+foreach(f ${SHARED_SRVS})
+ get_filename_component(_name "${f}" NAME)
+ list(APPEND SRV_FILES "srv/${_name}")
+endforeach()
+
+set(ACTION_FILES)
+foreach(f ${SHARED_ACTIONS})
+ get_filename_component(_name "${f}" NAME)
+ list(APPEND ACTION_FILES "action/${_name}")
+endforeach()
+# --- End: Copy shared interface definitions ---
+
+rosidl_generate_interfaces(${PROJECT_NAME}
+ ${MSG_FILES}
+ ${SRV_FILES}
+ ${ACTION_FILES}
+ DEPENDENCIES geometry_msgs sensor_msgs
+)
+
+ament_export_dependencies(rosidl_default_runtime)
+
+ament_package()
diff --git a/autonomy2/README.md b/autonomy2/README.md
new file mode 100644
index 0000000..13d1a08
--- /dev/null
+++ b/autonomy2/README.md
@@ -0,0 +1,9 @@
+# ROS 2 messages for Hydrus autonomy
+
+This package defines ROS 2 versions of:
+- `autonomy/Detection`
+- `autonomy/Detections`
+
+They mirror the fields from the ROS 1 messages.
+
+Build with colcon from the workspace root.
diff --git a/autonomy2/colcon.pkg b/autonomy2/colcon.pkg
new file mode 100644
index 0000000..cd1d2a9
--- /dev/null
+++ b/autonomy2/colcon.pkg
@@ -0,0 +1 @@
+{ "name": "autonomy" }
diff --git a/autonomy2/src/cv_node_py/CMakeLists.txt b/autonomy2/src/cv_node_py/CMakeLists.txt
new file mode 100644
index 0000000..755f811
--- /dev/null
+++ b/autonomy2/src/cv_node_py/CMakeLists.txt
@@ -0,0 +1,5 @@
+cmake_minimum_required(VERSION 3.8)
+project(cv_node_py)
+
+find_package(ament_cmake REQUIRED)
+ament_package()
diff --git a/autonomy2/src/cv_node_py/README.md b/autonomy2/src/cv_node_py/README.md
new file mode 100644
index 0000000..23b859e
--- /dev/null
+++ b/autonomy2/src/cv_node_py/README.md
@@ -0,0 +1,12 @@
+# cv_node_py
+
+ROS 2 Python node wrapping existing `src/computer_vision/detection_core.py`.
+
+## Build
+
+```bash
+# In workspace root (ros2_ws)
+colcon build --symlink-install
+. install/setup.bash
+ros2 launch cv_node_py cv_node.launch.py
+```
diff --git a/autonomy2/src/cv_node_py/colcon.pkg b/autonomy2/src/cv_node_py/colcon.pkg
new file mode 100644
index 0000000..a06ee9d
--- /dev/null
+++ b/autonomy2/src/cv_node_py/colcon.pkg
@@ -0,0 +1 @@
+{ "name": "cv_node_py" }
diff --git a/scripts/commands/test/test_cache.py b/autonomy2/src/cv_node_py/cv_node_py/__init__.py
similarity index 100%
rename from scripts/commands/test/test_cache.py
rename to autonomy2/src/cv_node_py/cv_node_py/__init__.py
diff --git a/autonomy2/src/cv_node_py/cv_node_py/node.py b/autonomy2/src/cv_node_py/cv_node_py/node.py
new file mode 100644
index 0000000..89789a5
--- /dev/null
+++ b/autonomy2/src/cv_node_py/cv_node_py/node.py
@@ -0,0 +1,116 @@
+#!/usr/bin/env python3
+import os
+
+# Reuse existing detection core utilities
+import sys
+from typing import List, Tuple
+
+import rclpy
+from geometry_msgs.msg import Point
+from rclpy.node import Node
+from rclpy.qos import qos_profile_sensor_data
+from sensor_msgs.msg import Image, RegionOfInterest
+
+sys.path.append(
+ os.path.abspath(
+ os.path.join(os.path.dirname(__file__), "../../../../src/computer_vision")
+ )
+)
+from detection_core import ColorFilterConfig, DetectionPipelineManager, ros_img_to_cv2
+
+from autonomy.msg import Detection as DetectionMsg
+from autonomy.msg import Detections as DetectionsMsg
+
+
+class CvNode(Node):
+ def __init__(self):
+ super().__init__("cv_node")
+ # Parameters
+ self.declare_parameter("yolo_model", "yolo11n.pt")
+ self.declare_parameter("color_target_rgb", [255, 0, 0])
+ self.declare_parameter("color_tolerance", 0.4)
+ self.declare_parameter("color_min_confidence", 0.3)
+ self.declare_parameter("color_min_area", 0.2)
+ self.declare_parameter("image_topic", "/camera/image_raw")
+ self.declare_parameter("detections_topic", "/detections")
+
+ # Build pipeline
+ self.pipeline = DetectionPipelineManager()
+ model = self.get_parameter("yolo_model").get_parameter_value().string_value
+ try:
+ self.pipeline.load_yolo_model(model)
+ except Exception as e:
+ self.get_logger().warn(f"YOLO model load failed: {e}")
+
+ rgb = (
+ self.get_parameter("color_target_rgb")
+ .get_parameter_value()
+ .integer_array_value
+ )
+ tol = float(self.get_parameter("color_tolerance").value)
+ min_conf = float(self.get_parameter("color_min_confidence").value)
+ min_area = float(self.get_parameter("color_min_area").value)
+ self.pipeline.update_color_filter_config(
+ ColorFilterConfig(
+ tolerance=tol,
+ min_confidence=min_conf,
+ min_area=min_area,
+ rgb_range=tuple(int(x) for x in rgb),
+ )
+ )
+
+ # Pub/Sub
+ image_topic = self.get_parameter("image_topic").value
+ self.sub = self.create_subscription(
+ Image, image_topic, self.on_image, qos_profile_sensor_data
+ )
+ det_topic = self.get_parameter("detections_topic").value
+ self.pub = self.create_publisher(DetectionsMsg, det_topic, 10)
+
+ self.get_logger().info(
+ f"cv_node started. Subscribing to {image_topic}, publishing {det_topic}"
+ )
+
+ def on_image(self, msg: Image):
+ try:
+ img = ros_img_to_cv2(msg, encoding=msg.encoding if msg.encoding else "bgr8")
+ except Exception:
+ # fallback assume bgr8
+ img = ros_img_to_cv2(msg, encoding="bgr8")
+
+ results: List[Tuple[str, list]] = self.pipeline.run_detections(img)
+ for name, detections in results:
+ out = DetectionsMsg()
+ out.detector_name = name
+ out.detections = []
+ for d in detections:
+ dm = DetectionMsg()
+ dm.cls = int(d.cls)
+ dm.confidence = float(d.conf)
+ # point may be None
+ if getattr(d, "point", None) is not None:
+ dm.point = Point(
+ x=float(d.point.x), y=float(d.point.y), z=float(d.point.z)
+ )
+ else:
+ dm.point = Point()
+ roi = RegionOfInterest()
+ roi.x_offset = int(d.x1)
+ roi.y_offset = int(d.y1)
+ roi.width = int(d.x2 - d.x1)
+ roi.height = int(d.y2 - d.y1)
+ dm.bounding_box = roi
+ out.detections.append(dm)
+ self.pub.publish(out)
+
+
+def main():
+ rclpy.init()
+ node = CvNode()
+ rclpy.spin(node)
+ node.destroy_node()
+ rclpy.shutdown()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/autonomy2/src/cv_node_py/launch/cv_node.launch.py b/autonomy2/src/cv_node_py/launch/cv_node.launch.py
new file mode 100644
index 0000000..6b291aa
--- /dev/null
+++ b/autonomy2/src/cv_node_py/launch/cv_node.launch.py
@@ -0,0 +1,24 @@
+from launch import LaunchDescription
+from launch_ros.actions import Node
+
+
+def generate_launch_description():
+ return LaunchDescription(
+ [
+ Node(
+ package="cv_node_py",
+ executable="cv_node",
+ name="cv_node",
+ output="screen",
+ parameters=[
+ {"yolo_model": "yolo11n.pt"},
+ {"color_target_rgb": [255, 0, 0]},
+ {"color_tolerance": 0.4},
+ {"color_min_confidence": 0.3},
+ {"color_min_area": 0.2},
+ {"image_topic": "/camera/image_raw"},
+ {"detections_topic": "/detections"},
+ ],
+ )
+ ]
+ )
diff --git a/autonomy2/src/cv_node_py/pyproject.toml b/autonomy2/src/cv_node_py/pyproject.toml
new file mode 100644
index 0000000..9787c3b
--- /dev/null
+++ b/autonomy2/src/cv_node_py/pyproject.toml
@@ -0,0 +1,3 @@
+[build-system]
+requires = ["setuptools", "wheel"]
+build-backend = "setuptools.build_meta"
diff --git a/autonomy2/src/cv_node_py/setup.cfg b/autonomy2/src/cv_node_py/setup.cfg
new file mode 100644
index 0000000..6c72b4f
--- /dev/null
+++ b/autonomy2/src/cv_node_py/setup.cfg
@@ -0,0 +1,4 @@
+[develop]
+script-dir=$base/lib/cv_node_py
+[install]
+install-scripts=$base/lib/cv_node_py
diff --git a/autonomy2/src/cv_node_py/setup.py b/autonomy2/src/cv_node_py/setup.py
new file mode 100644
index 0000000..9cd3130
--- /dev/null
+++ b/autonomy2/src/cv_node_py/setup.py
@@ -0,0 +1,26 @@
+from setuptools import setup
+
+package_name = "cv_node_py"
+
+setup(
+ name=package_name,
+ version="0.1.0",
+ packages=[package_name],
+ data_files=[
+ ("share/ament_index/resource_index/packages", ["resource/" + package_name]),
+ ("share/" + package_name, ["package.xml"]),
+ ("share/" + package_name + "/launch", ["launch/cv_node.launch.py"]),
+ ],
+ install_requires=["setuptools"],
+ zip_safe=True,
+ maintainer="Your Name",
+ maintainer_email="you@example.com",
+ description="ROS2 Python CV node using detection_core.py",
+ license="MIT",
+ tests_require=["pytest"],
+ entry_points={
+ "console_scripts": [
+ "cv_node = cv_node_py.node:main",
+ ],
+ },
+)
diff --git a/autonomy_interfaces/CMakeLists.txt b/autonomy_interfaces/CMakeLists.txt
new file mode 100644
index 0000000..0c07b4f
--- /dev/null
+++ b/autonomy_interfaces/CMakeLists.txt
@@ -0,0 +1,54 @@
+cmake_minimum_required(VERSION 3.0.2)
+project(autonomy_interfaces)
+
+## Compile as C++11, supported in ROS Kinetic and newer
+add_compile_options(-std=c++11)
+
+## Find catkin macros and libraries
+find_package(catkin REQUIRED COMPONENTS
+ std_msgs
+ geometry_msgs
+ sensor_msgs
+ actionlib_msgs
+ message_generation
+)
+
+## Generate messages in the 'msg' folder
+add_message_files(
+ FILES
+ Detection.msg
+ Detections.msg
+)
+
+## Generate services in the 'srv' folder
+add_service_files(
+ FILES
+ FireTorpedo.srv
+ SetColorFilter.srv
+ SetYoloModel.srv
+)
+
+## Generate actions in the 'action' folder
+add_action_files(
+ FILES
+ NavigateToWaypoint.action
+)
+
+## Generate added messages and services with any dependencies listed here
+generate_messages(
+ DEPENDENCIES
+ std_msgs
+ geometry_msgs
+ sensor_msgs
+ actionlib_msgs
+)
+
+## Declare a catkin package
+catkin_package(
+ CATKIN_DEPENDS
+ std_msgs
+ geometry_msgs
+ sensor_msgs
+ actionlib_msgs
+ message_runtime
+)
diff --git a/autonomy_interfaces/README.md b/autonomy_interfaces/README.md
new file mode 100644
index 0000000..cdeda1e
--- /dev/null
+++ b/autonomy_interfaces/README.md
@@ -0,0 +1,14 @@
+# Shared Interfaces (ROS 1 and ROS 2)
+
+This folder contains the single source of truth for Hydrus message, service, and action interface definitions.
+
+Structure:
+- `msg/` โ `.msg` message files
+- `srv/` โ `.srv` service files
+- `action/` โ `.action` action files
+
+Both ROS 1 (`autonomy_ros`) and ROS 2 (`autonomy_ros2`) packages copy these files at CMake configure-time into their local `msg/`, `srv/`, and `action/` folders and generate code from them. Edit files here only, then rebuild the ROS 1/ROS 2 workspaces.
+
+To add a new interface:
+1. Add the `.msg` / `.srv` / `.action` file in this folder hierarchy.
+2. Rebuild your ROS 1 and/or ROS 2 workspaces as usual.
diff --git a/autonomy/action/NavigateToWaypoint.action b/autonomy_interfaces/action/NavigateToWaypoint.action
similarity index 100%
rename from autonomy/action/NavigateToWaypoint.action
rename to autonomy_interfaces/action/NavigateToWaypoint.action
diff --git a/autonomy/msg/Detection.msg b/autonomy_interfaces/msg/Detection.msg
similarity index 100%
rename from autonomy/msg/Detection.msg
rename to autonomy_interfaces/msg/Detection.msg
diff --git a/autonomy/msg/Detections.msg b/autonomy_interfaces/msg/Detections.msg
similarity index 100%
rename from autonomy/msg/Detections.msg
rename to autonomy_interfaces/msg/Detections.msg
diff --git a/autonomy_interfaces/package.xml b/autonomy_interfaces/package.xml
new file mode 100644
index 0000000..f842779
--- /dev/null
+++ b/autonomy_interfaces/package.xml
@@ -0,0 +1,29 @@
+
+
+ autonomy_interfaces
+ 0.1.0
+ Custom ROS messages, services, and actions for the Hydrus autonomy system
+
+ Hydrus Team
+ MIT
+
+ catkin
+
+
+ message_generation
+ message_runtime
+
+
+ std_msgs
+ geometry_msgs
+ sensor_msgs
+ actionlib_msgs
+
+ std_msgs
+ geometry_msgs
+ sensor_msgs
+ actionlib_msgs
+
+
+
+
diff --git a/autonomy/srv/FireTorpedo.srv b/autonomy_interfaces/srv/FireTorpedo.srv
similarity index 100%
rename from autonomy/srv/FireTorpedo.srv
rename to autonomy_interfaces/srv/FireTorpedo.srv
diff --git a/autonomy/srv/SetColorFilter.srv b/autonomy_interfaces/srv/SetColorFilter.srv
similarity index 100%
rename from autonomy/srv/SetColorFilter.srv
rename to autonomy_interfaces/srv/SetColorFilter.srv
diff --git a/autonomy/srv/SetYoloModel.srv b/autonomy_interfaces/srv/SetYoloModel.srv
similarity index 100%
rename from autonomy/srv/SetYoloModel.srv
rename to autonomy_interfaces/srv/SetYoloModel.srv
diff --git a/docker/amd64/cuda/camera.Dockerfile b/docker/amd64/camera.Dockerfile
similarity index 100%
rename from docker/amd64/cuda/camera.Dockerfile
rename to docker/amd64/camera.Dockerfile
diff --git a/docker/amd64/cpu/hydrus.Dockerfile b/docker/amd64/cpu/hydrus.Dockerfile
deleted file mode 100644
index ac7e556..0000000
--- a/docker/amd64/cpu/hydrus.Dockerfile
+++ /dev/null
@@ -1,109 +0,0 @@
-# Use Ubuntu 20.04 as base image
-FROM ubuntu:20.04
-
-ARG DEBIAN_FRONTEND=noninteractive
-# Update package list
-RUN apt-get update && apt-get install -y lsb-release gnupg curl software-properties-common
-
-# Setup ROS repositories
-RUN sh -c 'echo "deb http://packages.ros.org/ros/ubuntu $(lsb_release -sc) main" > /etc/apt/sources.list.d/ros-latest.list'
-RUN curl -s https://raw.githubusercontent.com/ros/rosdistro/master/ros.asc | apt-key add -
-
-# Install ROS Noetic base
-RUN apt-get update && apt-get install -y ros-noetic-ros-base
-
-# Add the deadsnakes PPA and install Python 3.8
-RUN add-apt-repository -y ppa:deadsnakes/ppa && \
- apt-get update && \
- apt-get install -y python3.8 python3.8-distutils python3.8-venv
-
-# Set Python 3.8 as the default
-RUN update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.8 1
-
-# Install pip for Python 3.8
-RUN curl -sS https://bootstrap.pypa.io/pip/3.8/get-pip.py | python3.8
-
-#Add kisak-mesa PPA (for latest graphics drivers)
-RUN add-apt-repository -y ppa:kisak/kisak-mesa
-
-# Camera and Computer Vision Dependencies Python-3
-RUN apt-get update && apt-get install -y \
- python3-pip \
- python3-numpy\
- python3-opencv \
- libgl1-mesa-glx \
- ros-noetic-cv-bridge \
- ros-noetic-vision-opencv\
- libbullet-dev \
- python3-empy
-
-
-RUN apt-get update && apt-get install -y\
- ros-noetic-tf2-geometry-msgs\
- python3-tf2-kdl
-
-RUN sudo sh -c \
- 'echo "deb http://packages.osrfoundation.org/gazebo/ubuntu-stable `lsb_release -cs` main" > /etc/apt/sources.list.d/gazebo-stable.list' &&\
- curl https://packages.osrfoundation.org/gazebo.key | sudo apt-key add - &&\
- apt-get update && apt-get install -y ignition-fortress ros-noetic-ros-ign &&\
- echo "export GZ_VERSION=fortress" >> /root/.bashrc
-
-
-# Embedded Node Dependencies
-RUN apt-get install -y --no-install-recommends \
- gcc \
- curl \
- git
-
-# ROS setup
-RUN /bin/bash -c 'source /opt/ros/noetic/setup.bash && \
- mkdir -p /home/catkin_ws/src && \
- cd /home/catkin_ws/ && \
- catkin_make'
-RUN echo "source /opt/ros/noetic/setup.bash" >> /root/.bashrc
-
-# Install Arduino CLI and libraries
-WORKDIR /usr/local/
-RUN curl -fsSL https://raw.githubusercontent.com/arduino/arduino-cli/master/install.sh | sh && \
- arduino-cli core update-index && \
- arduino-cli core install arduino:avr
-RUN arduino-cli lib install "Servo@1.2.1" && \
- arduino-cli lib install "BlueRobotics MS5837 Library@1.1.1"
-
-
-# Copy embedded Arduino code in the Arduino libraries folder
-
-# Copy dvl embedded driver
-COPY ./devices/DVL/Wayfinder /opt/Wayfinder
-WORKDIR /opt/Wayfinder
-
-# Ultralytics with NO GPU
-RUN python3 -m pip install --extra-index-url https://download.pytorch.org/whl/cpu ultralytics
-# Copy the Python Dependencies and Install them
-COPY ./requirements.txt /requirements.txt
-RUN python3 -m pip install --ignore-installed -r /requirements.txt
-
-# Install Default models for YOLO
-RUN echo "export MESA_GL_VERSION_OVERRIDE=3.3" >> /root/.bashrc
-
-# Install additional dependencies for the embedded node
-# Install tmux, vim, git, and htop in a single RUN command
-RUN apt-get update && apt-get install -y tmux vim git htop socat
-
-RUN apt-get update && apt-get install -y \
- ros-noetic-rviz \
- ros-noetic-rqt \
- ros-noetic-rosbag \
- ros-noetic-image-view \
- ros-noetic-tf \
- ros-noetic-tf2-ros \
- ros-noetic-image-transport \
- ros-noetic-laser-proc
-
-COPY ./devices/Arduino/HydrusModule /root/Arduino/libraries/HydrusModule
-
-COPY ./ /catkin_ws/src/hydrus-software-stack
-WORKDIR /catkin_ws/src/hydrus-software-stack
-
-
-CMD ["/bin/bash", "-c", "sleep infinity"]
diff --git a/docker/amd64/cuda/hydrus.Dockerfile b/docker/amd64/cuda/hydrus.Dockerfile
deleted file mode 100644
index 3d4cf0d..0000000
--- a/docker/amd64/cuda/hydrus.Dockerfile
+++ /dev/null
@@ -1,100 +0,0 @@
-# Use Ubuntu 20.04 as base image
-FROM ubuntu:20.04
-
-# Update package list
-ARG DEBIAN_FRONTEND=noninteractive
-RUN apt-get update && apt-get install -y lsb-release gnupg curl software-properties-common
-
-# Setup ROS repositories
-RUN sh -c 'echo "deb http://packages.ros.org/ros/ubuntu $(lsb_release -sc) main" > /etc/apt/sources.list.d/ros-latest.list'
-RUN curl -s https://raw.githubusercontent.com/ros/rosdistro/master/ros.asc | apt-key add -
-
-# Install ROS Noetic base and catkin build tools
-RUN apt-get update && apt-get install -y ros-noetic-ros-base python3-catkin-tools ros-noetic-catkin
-
-# Add the deadsnakes PPA and install Python 3.8
-RUN add-apt-repository -y ppa:deadsnakes/ppa && \
- apt-get update && \
- apt-get install -y python3.8 python3.8-distutils python3.8-venv
-
-# Set Python 3.8 as the default
-RUN update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.8 1
-
-# Install pip for Python 3.8
-RUN curl -sS https://bootstrap.pypa.io/pip/3.8/get-pip.py | python3.8
-
-#Add kisak-mesa PPA (for latest graphics drivers)
-RUN add-apt-repository -y ppa:kisak/kisak-mesa
-
-
-# Camera and Computer Vision Dependencies Python-3
-RUN apt-get update && apt-get install -y \
- python3-pip \
- python3-numpy\
- python3-opencv \
- libgl1-mesa-glx \
- ros-noetic-cv-bridge \
- ros-noetic-vision-opencv\
- libbullet-dev \
- python3-empy
-
-
-RUN apt-get update && apt-get install -y\
- ros-noetic-tf2-geometry-msgs\
- python3-tf2-kdl
-
-
-RUN sudo sh -c \
- 'echo "deb http://packages.osrfoundation.org/gazebo/ubuntu-stable `lsb_release -cs` main" > /etc/apt/sources.list.d/gazebo-stable.list' &&\
- curl https://packages.osrfoundation.org/gazebo.key | sudo apt-key add - &&\
- apt-get update && apt-get install -y ignition-fortress ros-noetic-ros-ign &&\
- echo "export GZ_VERSION=fortress" >> /root/.bashrc
-
-
-# Embedded Node Dependencies
-RUN apt-get install -y --no-install-recommends \
- gcc \
- curl \
- git
-
-# ROS setup
-RUN /bin/bash -c 'source /opt/ros/noetic/setup.bash && \
- mkdir -p /home/catkin_ws/src && \
- cd /home/catkin_ws/ && \
- catkin_make'
-RUN echo "source /opt/ros/noetic/setup.bash" >> /root/.bashrc
-
-# Install Arduino CLI and libraries
-WORKDIR /usr/local/
-RUN curl -fsSL https://raw.githubusercontent.com/arduino/arduino-cli/master/install.sh | sh && \
- arduino-cli core update-index && \
- arduino-cli core install arduino:avr &&\
- arduino-cli lib install "Servo@1.2.1" && \
- arduino-cli lib install "BlueRobotics MS5837 Library@1.1.1"
-
-
-# Copy the Python Dependencies and Install them
-COPY ./requirements.txt /requirements.txt
-
-# Ultralytics with GPU
-RUN python3 -m pip install ultralytics
-RUN python3 -m pip install -r /requirements.txt
-
-# Install Default models for YOLO
-RUN curl -Lo /yolov8n.pt https://github.com/ultralytics/assets/releases/latest/download/yolov8n.pt
-RUN curl -Lo /yolov8s-world.pt https://github.com/ultralytics/assets/releases/latest/download/yolov8s-world.pt
-
-RUN apt-get install -y libeigen3-dev python3-tf2-kdl
-RUN apt-get update && apt-get install -y ros-noetic-tf2-geometry-msgs
-RUN echo "export MESA_GL_VERSION_OVERRIDE=3.3" >> /root/.bashrc
-
-# Install additional dependencies for the embedded node
-# Install tmux, vim, git, and htop in a single RUN command
-RUN apt-get update && apt-get install -y tmux vim git htop socat
-
-# Copy embedded Arduino code in the Arduino libraries folder
-COPY ./devices/Arduino/HydrusModule /root/Arduino/libraries/HydrusModule
-
-COPY ./ /catkin_ws/src/hydrus-software-stack
-WORKDIR /catkin_ws/src/hydrus-software-stack
-CMD ["/bin/bash", "-c", "sleep infinity"]
diff --git a/docker/amd64/foxy.Dockerfile b/docker/amd64/foxy.Dockerfile
new file mode 100644
index 0000000..04f8a9b
--- /dev/null
+++ b/docker/amd64/foxy.Dockerfile
@@ -0,0 +1,50 @@
+# Hydrus ROS 2 (Foxy) layer built on shared base
+FROM ubuntu:20.04
+
+ENV DEBIAN_FRONTEND=noninteractive \
+ LANG=en_US.UTF-8 \
+ LC_ALL=en_US.UTF-8
+
+# Common packages for all Hydrus images
+RUN apt-get update && apt-get install -y --no-install-recommends \
+ build-essential \
+ gcc \
+ cmake \
+ git \
+ wget \
+ curl \
+ lsb-release \
+ gnupg \
+ software-properties-common \
+ ca-certificates \
+ tmux \
+ vim \
+ htop \
+ socat \
+ locales \
+ && locale-gen en_US en_US.UTF-8 \
+ && update-locale LC_ALL=en_US.UTF-8 LANG=en_US.UTF-8 \
+ && rm -rf /var/lib/apt/lists/*
+
+
+ENV ROS_DISTRO=foxy \
+ LANG=en_US.UTF-8 \
+ LC_ALL=en_US.UTF-8
+
+# Configure locale and add ROS 2 apt repository; install core + dev tools
+RUN locale-gen en_US en_US.UTF-8 && update-locale LC_ALL=en_US.UTF-8 LANG=en_US.UTF-8 \
+ && echo "deb [arch=$(dpkg --print-architecture)] http://packages.ros.org/ros2/ubuntu $(lsb_release -sc) main" > /etc/apt/sources.list.d/ros2.list \
+ && curl -sSL https://raw.githubusercontent.com/ros/rosdistro/master/ros.key | apt-key add - \
+ && apt-get update \
+ && apt-get install -y --no-install-recommends \
+ ros-foxy-ros-base \
+ ros-foxy-tf2-ros \
+ ros-foxy-tf2-geometry-msgs \
+ ros-foxy-image-transport \
+ ros-foxy-rviz2 \
+ ros-foxy-rqt \
+ ros-foxy-rosbag2 \
+ ros-foxy-ros2bag \
+ ros-foxy-rqt-image-view \
+ python3-colcon-common-extensions \
+ && rm -rf /var/lib/apt/lists/*
diff --git a/docker/amd64/noetic.Dockerfile b/docker/amd64/noetic.Dockerfile
new file mode 100644
index 0000000..fca2418
--- /dev/null
+++ b/docker/amd64/noetic.Dockerfile
@@ -0,0 +1,46 @@
+# Hydrus ROS 1 (Noetic) layer built on shared base
+FROM ubuntu:20.04
+
+ENV DEBIAN_FRONTEND=noninteractive \
+ LANG=en_US.UTF-8 \
+ LC_ALL=en_US.UTF-8
+
+# Common packages for all Hydrus images
+RUN apt-get update && apt-get install -y --no-install-recommends \
+ build-essential \
+ gcc \
+ cmake \
+ git \
+ wget \
+ curl \
+ lsb-release \
+ gnupg \
+ software-properties-common \
+ ca-certificates \
+ tmux \
+ vim \
+ htop \
+ socat \
+ locales \
+ && locale-gen en_US en_US.UTF-8 \
+ && update-locale LC_ALL=en_US.UTF-8 LANG=en_US.UTF-8 \
+ && rm -rf /var/lib/apt/lists/*
+
+
+# Add ROS 1 repo and install core + commonly used packages
+RUN echo "deb http://packages.ros.org/ros/ubuntu $(lsb_release -sc) main" > /etc/apt/sources.list.d/ros-latest.list \
+ && curl -sSL https://raw.githubusercontent.com/ros/rosdistro/master/ros.asc | apt-key add - \
+ && apt-get update \
+ && apt-get install -y --no-install-recommends \
+ ros-noetic-ros-base \
+ ros-noetic-tf2-geometry-msgs \
+ python3-tf2-kdl \
+ ros-noetic-rviz \
+ ros-noetic-rqt \
+ ros-noetic-rosbag \
+ ros-noetic-image-view \
+ ros-noetic-tf \
+ ros-noetic-tf2-ros \
+ ros-noetic-image-transport \
+ ros-noetic-laser-proc \
+ && rm -rf /var/lib/apt/lists/*
diff --git a/docker/amd64/pkgmanagers.Dockerfile b/docker/amd64/pkgmanagers.Dockerfile
new file mode 100644
index 0000000..e724c09
--- /dev/null
+++ b/docker/amd64/pkgmanagers.Dockerfile
@@ -0,0 +1,39 @@
+# Hydrus package managers layer: Python uv, Arduino CLI, Cargo
+# Inherit from either the base or a ROS layer depending on use case
+ARG PARENT_IMAGE
+FROM ${PARENT_IMAGE}
+
+# Ensure common env is present if base is used
+ENV PATH="/root/.local/bin:${PATH}"
+
+# Python and venv utilities
+RUN apt-get update && apt-get install -y --no-install-recommends \
+ python3 \
+ python3-venv \
+ python3-pip \
+ && rm -rf /var/lib/apt/lists/*
+
+# Install uv (https://astral.sh/uv)
+RUN curl -LsSf https://astral.sh/uv/install.sh | sh
+
+# Install Arduino CLI (package manager for Arduino cores/libs)
+RUN curl -fsSL https://raw.githubusercontent.com/arduino/arduino-cli/master/install.sh | sh
+
+# Install Cargo (Rust package manager) without baking toolchains/deps
+RUN apt-get update && apt-get install -y --no-install-recommends cargo && rm -rf /var/lib/apt/lists/*
+
+# Set cache locations to bind-mounted repo by default; can be overridden at runtime
+ENV WORKDIR_PATH=/home/catkin_ws/src/hydrus-software-stack \
+ UV_CACHE_DIR=/home/catkin_ws/src/hydrus-software-stack/.uv-cache \
+ PIP_CACHE_DIR=/home/catkin_ws/src/hydrus-software-stack/.pip-cache \
+ ARDUINO_DATA_DIR=/home/catkin_ws/src/hydrus-software-stack/.arduino15 \
+ ARDUINO_SKETCHBOOK_DIR=/home/catkin_ws/src/hydrus-software-stack/Arduino \
+ CARGO_HOME=/home/catkin_ws/src/hydrus-software-stack/.cargo \
+ RUSTUP_HOME=/home/catkin_ws/src/hydrus-software-stack/.rustup \
+ PATH="/root/.local/bin:${CARGO_HOME}/bin:${PATH}"
+
+# Prepare directories and symlinks for caches
+RUN mkdir -p "$ARDUINO_DATA_DIR" "$ARDUINO_SKETCHBOOK_DIR/libraries" "$CARGO_HOME/bin" "$RUSTUP_HOME" \
+ && rm -rf /root/.arduino15 /root/Arduino \
+ && ln -s "$ARDUINO_DATA_DIR" /root/.arduino15 \
+ && ln -s "$ARDUINO_SKETCHBOOK_DIR" /root/Arduino
diff --git a/docker/docker-compose-amd64-cpu.yaml b/docker/docker-compose-amd64-cpu.yaml
index 46150f7..32927e5 100644
--- a/docker/docker-compose-amd64-cpu.yaml
+++ b/docker/docker-compose-amd64-cpu.yaml
@@ -1,5 +1,3 @@
-version: '3.8'
-
services:
ros-master:
image: ros:noetic-ros-core
@@ -14,36 +12,21 @@ services:
hydrus_cpu:
build:
context: ../../hydrus-software-stack
- dockerfile: docker/amd64/cpu/hydrus.Dockerfile
+ args:
+ PARENT_IMAGE: "noetic:latest"
+ dockerfile: docker/amd64/pkgmanagers.Dockerfile
privileged: true
stdin_open: true
tty: true
ports:
- "8000:8000"
- - "5000:5000" # Added port for Flask detection viewer
- devices:
- - "/dev/ttyACM0:/dev/ttyACM0"
- - "/dev/ttyACM1:/dev/ttyACM1" # Added ttyACM1 mapping
- - "/dev/ttyUSB0:/dev/ttyUSB0" # Added ttyUSB0 mapping
- - "/dev/dri:/dev/dri"
+ - "5000:5000"
environment:
- ROS_MASTER_URI=http://ros-master:11311
- - ARDUINO_BOARD=arduino:avr:uno
- - VOLUME
- - ROSBAG_PLAYBACK
- - DEBUG_ARDUINO
- - DISPLAY=${DISPLAY} # Pass the DISPLAY variable from the host
- - RVIZ
- - TEST
- - NO_BUILD
- - TMUX_SESSIONS
- - ARDUINO_COMPILE
- - VIRTUAL_ARDUINO
+ - DISPLAY=${DISPLAY}
volumes:
- "../:/home/catkin_ws/src/hydrus-software-stack"
- "/tmp/.X11-unix:/tmp/.X11-unix"
- - "../rosbags:/rosbags"
- - "../yolo_models:/yolo_models" # Volume for YOLO models
command: /bin/bash -c "sleep infinity"
depends_on:
- ros-master
diff --git a/docker/docker-compose-jetson-tx2.yaml b/docker/docker-compose-jetson-tx2.yaml
deleted file mode 100644
index 17f4367..0000000
--- a/docker/docker-compose-jetson-tx2.yaml
+++ /dev/null
@@ -1,66 +0,0 @@
-version: '3.8'
-
-services:
- ros-master:
- image: ros:noetic-ros-core
- command: stdbuf -o L roscore
- ports:
- - "11311:11311"
-
- zed-camera:
- build:
- context: ../
- dockerfile: jetson/camera.Dockerfile
- privileged: true
- deploy:
- resources:
- reservations:
- devices:
- - driver: nvidia
- count: all
- capabilities: [gpu]
- environment:
- - ROS_MASTER_URI=http://ros-master:11311
- depends_on:
- - ros-master
-
- hydrus_jetson:
- build:
- context: ../
- dockerfile: jetson/hydrus.Dockerfile
- privileged: true
- deploy:
- resources:
- reservations:
- devices:
- - driver: nvidia
- count: all
- capabilities: [gpu]
- stdin_open: true
- tty: true
- volumes:
- - "../:/home/catkin_ws/src"
- - "/tmp/.X11-unix:/tmp/.X11-unix"
- - "../rosbags:/rosbags"
- - "../yolo_models:/yolo_models" # Volume for YOLO models
- ports:
- - "8000:8000"
- devices:
- - "/dev/ttyACM0:/dev/ttyACM0"
- environment:
- - ROS_MASTER_URI=http://ros-master:11311
- - ARDUINO_BOARD=arduino:avr:uno
- - VOLUME
- - ROSBAG_PLAYBACK
- - DEBUG_ARDUINO
- - DISPLAY=${DISPLAY}
- - RVIZ
- - TEST
- - NO_BUILD
- - TMUX_SESSIONS
- - ARDUINO_COMPILE
- - VIRTUAL_ARDUINO
- command: /bin/bash -c "sleep infinity"
- depends_on:
- - ros-master
- - zed-camera
diff --git a/docker/run_docker.py b/docker/run_docker.py
deleted file mode 100755
index 9ed4a77..0000000
--- a/docker/run_docker.py
+++ /dev/null
@@ -1,36 +0,0 @@
-#!/usr/bin/env python3
-"""
-Hydrus Docker Deployment - Main Entry Point
-Simple wrapper that calls the modular hocker script
-"""
-
-import os
-import sys
-from pathlib import Path
-
-
-def main():
- """Entry point that delegates to the hocker script"""
- hocker_script = Path(__file__).parent / "hydrus-docker" / "hocker"
-
- if not hocker_script.exists():
- print("โ Error: hocker script not found in hydrus-docker folder")
- sys.exit(1)
-
- # Pass all arguments to the hocker script and replace current process
- try:
- # Use execve to replace current process with hocker
- env = os.environ.copy()
- os.execve(
- sys.executable, [sys.executable, str(hocker_script)] + sys.argv[1:], env
- )
- except KeyboardInterrupt:
- print("\n๐ Operation cancelled by user")
- sys.exit(1)
- except Exception as e:
- print(f"โ Error running hocker: {e}")
- sys.exit(1)
-
-
-if __name__ == "__main__":
- main()
diff --git a/docs/ARDUINO_SERIAL_COMMUNICATION.md b/docs/ARDUINO_SERIAL_COMMUNICATION.md
deleted file mode 100644
index 31188b1..0000000
--- a/docs/ARDUINO_SERIAL_COMMUNICATION.md
+++ /dev/null
@@ -1,445 +0,0 @@
-# Arduino Serial Communication System Documentation
-
-## Overview
-
-The Hydrus submarine project uses a sophisticated serial communication system to interface between ROS (Robot Operating System) and an Arduino microcontroller. This system handles real-time control of thrusters, depth motors, and torpedo mechanisms while providing comprehensive monitoring and logging capabilities.
-
-## System Architecture
-
-```
-โโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโ
-โ ROS Topics โโโโโถโ Serial ROS โโโโโถโ Arduino โ
-โ โ โ Bridge โ โ Microcontrollerโ
-โโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโ
- โ
- โผ
-โโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโ
-โ Virtual Arduino โ โ Serial Monitor โ โ Arduino Log โ
-โ (Fallback) โ โ Setup โ โ Monitor โ
-โโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโ
-```
-
-## Core Components
-
-### 1. Serial ROS Bridge (`serial_ros_bridge.py`)
-
-**Purpose**: Acts as the main communication hub between ROS and Arduino.
-
-**Function**:
-- Subscribes to ROS topics for thruster control
-- Converts ROS messages to Arduino serial commands
-- Maintains bidirectional communication with Arduino
-- Handles connection recovery and error management
-
-**ROS Topics Subscribed**:
-- `/hydrus/thrusters/1-4` (Int16): Individual thruster PWM values (1000-2000)
-- `/hydrus/depth` (Int16): Depth motor PWM control
-- `/hydrus/torpedo` (Int16): Torpedo mechanism control
-
-**Serial Command Format**:
-```
-T1:1500 # Thruster 1 with PWM value 1500 (neutral)
-T2:1650 # Thruster 2 with PWM value 1650 (forward)
-T3:1350 # Thruster 3 with PWM value 1350 (reverse)
-T4:1500 # Thruster 4 with PWM value 1500 (neutral)
-D:1600 # Depth motors with PWM value 1600
-P:1500 # Torpedo with PWM value 1500 (neutral)
-C:30 # Camera motor angle (30 degrees)
-```
-
-**Key Features**:
-- **PWM Range**: 1000-2000 (standard ESC range)
-- **Neutral Position**: 1500 (no movement)
-- **Thread-based Response Reading**: Continuous monitoring of Arduino responses
-- **Connection Management**: Automatic reconnection attempts
-- **Command Formatting**: Proper termination and encoding
-
-### 2. Virtual Arduino System (`virtual_arduino.py`)
-
-**Purpose**: Provides a fallback when no physical Arduino is connected.
-
-**How it Works**:
-
-#### SOCAT Usage (Virtual Arduino Only)
-The virtual Arduino system uses `socat` (Socket Cat) to create virtual serial ports **only when no physical Arduino is connected**:
-
-```bash
-socat pty,link=/dev/ttyACM0,raw,echo=0 pty,raw,echo=0
-```
-
-**SOCAT Parameters Explained**:
-- `pty`: Creates a pseudo-terminal
-- `link=/dev/ttyACM0`: Creates a symbolic link to the specified device path
-- `raw`: Sets raw mode (no character processing)
-- `echo=0`: Disables echo
-
-**Important**: SOCAT is NOT used for normal serial monitoring - it's only used in `virtual_arduino.py` to create fake devices when testing without hardware.
-
-#### Virtual Device Creation Process
-1. **Check Existing Device**: Verifies if real Arduino exists at `/dev/ttyACM0`
-2. **Install Dependencies**: Automatically installs `socat` if missing
-3. **Create Virtual Port**: Uses socat to create bidirectional virtual serial communication
-4. **Set Permissions**: Ensures proper read/write permissions (0o666)
-5. **Response Simulation**: Mimics Arduino responses to commands
-
-#### Simulated Responses
-```python
-# Command: "T1:1500"
-# Response: "ACK:T1:1500"
-
-# Command: "STATUS"
-# Response: "STATUS:OK,THRUSTERS:8,DEPTH:0.0"
-
-# Command: "PING"
-# Response: "PONG"
-```
-
-### 3. Serial Monitor Setup (`setup_serial_monitor.py`)
-
-**Purpose**: Establishes direct serial port monitoring and logging infrastructure.
-
-**Process Flow**:
-
-1. **Port Configuration**:
- ```bash
- stty -F /dev/ttyACM0 raw speed 115200 cs8 -cstopb -parenb
- ```
- - `raw`: Raw mode (no line processing)
- - `speed 115200`: Baud rate setting
- - `cs8`: 8 data bits
- - `-cstopb`: 1 stop bit
- - `-parenb`: No parity
-
-2. **Directory Setup**:
- - Creates `/tmp/hydrus_serial/` directory
- - Initializes log files and PID tracking
-
-3. **Direct Serial Logging**:
- ```python
- # Directly reads from Arduino device (no SOCAT involved)
- cat_process = subprocess.Popen(["cat", "/dev/ttyACM0"], stdout=log_file)
- ```
- **Note**: This setup does NOT use SOCAT - it reads directly from the physical device.
-
-4. **PID Management**:
- - Saves process ID to `/tmp/hydrus_serial/catpid.txt`
- - Enables process monitoring and cleanup
-
-### 4. Arduino Log Monitor (`monitor_arduino_logs.py`)
-
-**Purpose**: Provides real-time monitoring of Arduino serial communication.
-
-**Functionality**:
-
-#### Log File Monitoring
-- **Target File**: `/tmp/hydrus_serial/arduinolog.txt`
-- **Monitoring Method**: Python-based `tail -f` equivalent
-- **Real-time Display**: Continuous output of Arduino responses
-
-#### Monitoring Process
-```python
-# Wait for log file creation
-while not log_file.exists():
- time.sleep(1)
-
-# Continuous monitoring
-with open(log_file, "r") as f:
- f.seek(0, 2) # Go to end of file
- while True:
- line = f.readline()
- if line:
- print(line.rstrip())
- else:
- time.sleep(0.1)
-```
-
-## Data Flow Architecture
-
-### Command Flow (ROS โ Arduino)
-```
-1. ROS Node publishes to /hydrus/thrusters/1
- โ
-2. serial_ros_bridge.py receives Int16 message
- โ
-3. Convert to command: "T1:1500"
- โ
-4. Send via serial.write() to /dev/ttyACM0
- โ
-5. Arduino receives and processes command
- โ
-6. Arduino sends acknowledgment: "ACK:T1:1500"
-```
-
-### Logging Flow (Arduino โ Monitoring)
-```
-1. Arduino sends response via serial
- โ
-2. setup_serial_monitor.py cat process reads directly from /dev/ttyACM0
- โ
-3. Data written to /tmp/hydrus_serial/arduinolog.txt
- โ
-4. monitor_arduino_logs.py reads and displays log
- โ
-5. Real-time display in tmux pane
-```
-
-**Note**: No SOCAT involved in logging - direct device access only.
-
-## File System Structure
-
-```
-/tmp/hydrus_serial/
-โโโ arduinolog.txt # Main Arduino communication log
-โโโ catpid.txt # Process ID of cat monitoring process
-โโโ (other temp files)
-
-/dev/
-โโโ ttyACM0 # Primary Arduino device (real or virtual)
-โโโ (other devices)
-```
-
-## Integration with TMUX Sessions
-
-The system integrates with the main Hydrus tmux session manager:
-
-### Arduino Window Layout
-```
-โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
-โ Setup Serial Monitor โ Arduino Logs โ
-โ (setup_serial_monitor) โ (monitor_logs) โ
-โ โ โ
-โ - Port configuration โ - Real-time โ
-โ - Directory setup โ log display โ
-โ - Background logging โ - Error โ
-โ โ monitoring โ
-โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
-```
-
-### Session Commands
-```bash
-# Arduino window pane 0: Setup
-bash ${CATKIN_WS}/src/hydrus-software-stack/scripts/setup_serial_monitor.py
-
-# Arduino window pane 1: Monitor
-python3 ${CATKIN_WS}/src/hydrus-software-stack/scripts/monitor_arduino_logs.py
-```
-
-## Error Handling and Recovery
-
-### Connection Recovery
-- **Automatic Reconnection**: serial_ros_bridge.py attempts reconnection on failure
-- **Graceful Degradation**: System continues operation with virtual Arduino
-- **Status Monitoring**: Continuous connection health checks
-
-### Error Types and Responses
-1. **Physical Disconnection**: Switches to virtual Arduino
-2. **Permission Issues**: Automatic permission setting (chmod 666)
-3. **Serial Timeout**: Connection reset and retry
-4. **Process Crashes**: PID-based process monitoring and restart
-
-### Cleanup Procedures
-```python
-def _cleanup(self):
- # Stop cat process
- if self.cat_process:
- self.cat_process.terminate()
-
- # Kill socat process
- if hasattr(self, "socat_process"):
- os.killpg(os.getpgid(self.socat_process.pid), signal.SIGTERM)
-
- # Remove virtual device
- if os.path.exists(self.device_path):
- os.unlink(self.device_path)
-```
-
-## Security and Permissions
-
-### Device Access
-- **Permission Setting**: Automatic chmod 666 on device creation
-- **User Groups**: Proper dialout group membership required
-- **Directory Permissions**: /tmp/hydrus_serial/ with appropriate access
-
-### Process Management
-- **Signal Handling**: Proper SIGINT/SIGTERM handling
-- **Process Groups**: SOCAT runs in separate process group
-- **PID Tracking**: All processes tracked for cleanup
-
-## Performance Considerations
-
-### Serial Communication
-- **Baud Rate**: 115200 bps (optimal for submarine control)
-- **Command Timing**: 10ms delay between commands to prevent merging
-- **Buffer Management**: Proper serial buffer flushing
-
-### System Resources
-- **Thread Usage**: Dedicated thread for Arduino response reading
-- **Memory**: Minimal memory footprint with efficient logging
-- **CPU**: Low CPU usage with optimized polling intervals
-
-## Troubleshooting Guide
-
-### Common Issues
-1. **Device Not Found**: Check USB connection, try virtual Arduino
-2. **Permission Denied**: Verify user in dialout group
-3. **No Response**: Check baud rate, verify Arduino firmware
-4. **Log File Missing**: Ensure setup_serial_monitor.py ran successfully
-
-### Diagnostic Commands
-```bash
-# Check device existence
-ls -la /dev/ttyACM*
-
-# Test serial communication
-echo "PING" > /dev/ttyACM0
-
-# Monitor process status
-cat /tmp/hydrus_serial/catpid.txt
-
-# Check log contents
-tail -f /tmp/hydrus_serial/arduinolog.txt
-```
-
-## Future Enhancements
-
-### Planned Improvements
-1. **Protocol Versioning**: Structured command/response protocol
-2. **Error Reporting**: Enhanced error codes from Arduino
-3. **Configuration Management**: Dynamic parameter adjustment
-4. **Performance Metrics**: Communication latency monitoring
-5. **Security**: Encrypted communication for sensitive operations
-
-This comprehensive serial communication system ensures reliable, monitored, and fault-tolerant communication between the ROS-based submarine control system and the Arduino microcontroller, with complete logging and monitoring capabilities for debugging and operational oversight.
-
-## Architecture Efficiency Analysis
-
-### Current Implementation Limitations
-
-The current architecture has several efficiency issues:
-
-#### File I/O Bottleneck
-```
-Arduino โ /dev/ttyACM0 โ cat process โ file write โ monitor process โ display
- โ
- serial_ros_bridge (direct access)
-```
-
-**Problems with Current Approach**:
-
-1. **Constant File Writing**: `cat /dev/ttyACM0 > arduinolog.txt` continuously writes to disk
-2. **File Reading Overhead**: `monitor_arduino_logs.py` must constantly poll and read the file
-3. **Disk I/O Latency**: File system operations introduce latency in log display
-4. **Resource Waste**: Unnecessary disk writes for ephemeral monitoring data
-5. **Limited Scalability**: Adding more monitoring processes requires more file I/O
-
-#### Performance Metrics
-- **File I/O**: ~10-100ms latency per write/read cycle
-- **Disk Usage**: Continuous writing (potentially GB/day depending on Arduino output)
-- **CPU Overhead**: File system calls, polling, buffer management
-- **Memory**: File buffers in kernel and userspace
-
-### Proposed SOCAT-Based Architecture
-
-A more efficient approach would use SOCAT to create virtual serial port multiplexing:
-
-```
- โโ /dev/ttyVIRT0 โ serial_ros_bridge.py
-Arduino โ /dev/ttyACM0 โ SOCAT โค
- โโ /dev/ttyVIRT1 โ monitor_display.py
-```
-
-#### SOCAT Multiplexing Command
-```bash
-# Create bidirectional virtual ports
-socat -d -d pty,raw,echo=0,link=/dev/ttyVIRT0 pty,raw,echo=0,link=/dev/ttyVIRT1 &
-
-# Bridge physical device to virtual ports
-socat /dev/ttyACM0,raw,echo=0 EXEC:'tee >(socat - /dev/ttyVIRT0) | socat - /dev/ttyVIRT1'
-```
-
-#### Benefits of SOCAT Approach
-
-1. **Zero File I/O**: Direct memory-to-memory data streaming
-2. **Real-time Access**: Multiple processes can access serial data simultaneously
-3. **Lower Latency**: ~1-10ms vs 10-100ms for file-based approach
-4. **Better Resource Usage**: No disk writes for monitoring
-5. **Scalability**: Easy to add more virtual endpoints
-6. **Isolation**: Each consumer gets independent data stream
-
-#### Advanced SOCAT Architecture
-```bash
-# Master serial multiplexer
-socat /dev/ttyACM0,raw,echo=0,b115200 \
- EXEC:'tee >(socat - pty,link=/dev/hydrus_control,raw,echo=0) \
- >(socat - pty,link=/dev/hydrus_monitor,raw,echo=0) \
- >(socat - pty,link=/dev/hydrus_log,raw,echo=0)'
-```
-
-**Result**:
-- `/dev/hydrus_control` โ serial_ros_bridge.py (bidirectional)
-- `/dev/hydrus_monitor` โ real-time monitoring display
-- `/dev/hydrus_log` โ optional file logging (if needed)
-
-### Performance Comparison
-
-| Aspect | Current (File-based) | Proposed (SOCAT-based) |
-|--------|---------------------|------------------------|
-| Latency | 10-100ms | 1-10ms |
-| Disk I/O | Continuous | Optional/None |
-| CPU Usage | High (file ops) | Low (memory ops) |
-| Scalability | Poor | Excellent |
-| Real-time | No | Yes |
-| Resource Usage | High | Low |
-
-### Implementation Considerations
-
-#### Advantages of SOCAT Approach
-- **Performance**: Significantly reduced latency and resource usage
-- **Flexibility**: Easy to add/remove monitoring endpoints
-- **Real-time**: True real-time data access for all consumers
-- **Reliability**: Less failure points (no file system dependencies)
-
-#### Potential Drawbacks
-- **Complexity**: More complex setup and process management
-- **Dependencies**: Requires SOCAT to be properly installed and configured
-- **Debugging**: Harder to debug virtual port issues
-- **Process Management**: More processes to monitor and restart
-
-### Recommended Architecture Migration
-
-#### Phase 1: Hybrid Approach
-- Keep current file-based logging as backup
-- Add SOCAT virtual ports for real-time monitoring
-- Test performance improvements
-
-#### Phase 2: Full Migration
-- Replace file-based monitoring with SOCAT endpoints
-- Implement proper process management for SOCAT multiplexer
-- Add configuration management for virtual port creation
-
-#### Implementation Script Example
-```python
-def setup_socat_multiplexer(device="/dev/ttyACM0"):
- """Setup SOCAT-based serial multiplexing"""
-
- # Create virtual device paths
- control_port = "/dev/hydrus_control"
- monitor_port = "/dev/hydrus_monitor"
- log_port = "/dev/hydrus_log"
-
- # SOCAT multiplexer command
- socat_cmd = [
- "socat",
- f"{device},raw,echo=0,b115200",
- f"EXEC:tee >(socat - pty,link={control_port},raw,echo=0) " +
- f">(socat - pty,link={monitor_port},raw,echo=0) " +
- f">(socat - pty,link={log_port},raw,echo=0)"
- ]
-
- # Start multiplexer process
- process = subprocess.Popen(socat_cmd, preexec_fn=os.setsid)
-
- return process, [control_port, monitor_port, log_port]
-```
-
-**Conclusion**: You are absolutely correct - the current file-based approach is inefficient. A SOCAT-based virtual port multiplexing system would provide significant performance improvements, better resource utilization, and true real-time monitoring capabilities.
diff --git a/docs/PROFILING.md b/docs/PROFILING.md
deleted file mode 100644
index a369af6..0000000
--- a/docs/PROFILING.md
+++ /dev/null
@@ -1,300 +0,0 @@
-# Function and ROS Node Profiling Guide
-
-This document explains how to instrument and profile your Hydrus software using the provided profiling decorators and ROS node profiler.
-
-## Table of Contents
-
-- [Overview](#overview)
-- [Prerequisites](#prerequisites)
-- [Profiling Decorators](#profiling-decorators)
- - [Installation](#installation)
- - [Decorator Usage](#decorator-usage)
- - [Context Manager Usage](#context-manager-usage)
- - [Example: `profiling_decorators.py`](#example-profiling_decoratorspy)
-- [Instrumenting Your Code](#instrumenting-your-code)
- - [Python Functions](#python-functions)
- - [Class Methods](#class-methods)
- - [ROS Callbacks](#ros-callbacks)
-- [Example Profiling Script](#example-profiling-script)
-- [ROS Node Profiler](#ros-node-profiler)
- - [Usage](#usage)
- - [CLI Options](#cli-options)
-- [Exporting and Analyzing Results](#exporting-and-analyzing-results)
-- [Best Practices](#best-practices)
-
----
-
-## Overview
-
-Hydrus provides two complementary profiling tools:
-
-1. **Function-level Decorators**: Lightweight decorators and context managers for measuring execution time of individual functions or code blocks.
-2. **ROS Node Profiler**: A standalone ROS node (`ros_profiler.py`) for real-time resource monitoring and FPS measurement of active ROS nodes.
-
----
-
-## Prerequisites
-
-- Python 3.8+ environment
-- ROS (Noetic/Melodic) installed and sourced in your environment
-- `psutil` and `colorama` Python packages installed for the ROS profiler:
-
-```bash
-pip install psutil colorama
-```
-
-**Important**: Make sure to source your ROS environment before running any profiling tools:
-
-```bash
-# Source ROS
-source /opt/ros/noetic/setup.bash # or melodic
-
-# Source your workspace (if built)
-source devel/setup.bash
-```
-
----
-
-## Profiling Decorators
-
-### Installation
-
-The decorators and utilities live in `autonomy/scripts/profiler/profiling_decorators.py`. No additional install steps are needed if your ROS package path includes this folder.
-
-### Decorator Usage
-
-- `@profile_function(name?: str, category: str="general")`
-- `@profile_method(name?: str, category: str="methods")`
-- `@profile_ros_callback(topic_name: str)`
-
-Apply these directly above the function or method definition:
-
-```python
-from autonomy.scripts.profiler.profiling_decorators import profile_function, profile_method, profile_ros_callback
-
-@profile_function("cv.yolo_inference", "cv")
-def yolo_object_detection(image):
- # ...
- pass
-
-@profile_method(category="mission")
-def run(self):
- # ...
- pass
-
-@profile_ros_callback("camera/image_raw")
-def image_callback(msg):
- # ...
- pass
-```
-
-### Context Manager Usage
-
-Use `FunctionProfiler(name: str)` to wrap arbitrary code blocks:
-
-```python
-from autonomy.scripts.profiler.profiling_decorators import FunctionProfiler
-
-with FunctionProfiler("cv.contour_detection"):
- contours, _ = cv2.findContours(mask, ...)
-```
-
-### Example: `profiling_decorators.py`
-
-See `autonomy/scripts/profiler/profiling_decorators.py` for full implementation details and helper functions:
-
-- `ProfileManager` singleton collects statistics
-- `export_profiling_data(filename)` to dump JSON
-
----
-
-## Instrumenting Your Code
-
-### Python Functions
-
-Decorate any standalone function:
-
-```python
-@profile_function("depth.calculation")
-def calculate_depth(points, intrinsics):
- # core logic
- return points_3d
-```
-
-### Class Methods
-
-Decorate methods inside your classes:
-
-```python
-class MissionPlanner:
-
- @profile_method(category="mission")
- def feedback_callback(self, feedback):
- # process feedback
- pass
-```
-
-### ROS Callbacks
-
-Wrap ROS subscriber callbacks:
-
-```python
-@profile_ros_callback("/detector/box_detection")
-def detection_callback(msg):
- # parse detections
- pass
-```
-
----
-
-## Example Profiling Script
-
-An example instrumentation is provided in `autonomy/scripts/profiler/profiling_example.py`. To run:
-
-```bash
-# First, ensure ROS is sourced
-source /opt/ros/noetic/setup.bash # or melodic
-source devel/setup.bash # if workspace is built
-
-# Method 1: Direct execution
-python3 autonomy/scripts/profiler/profiling_example.py
-
-# Method 2: Using rosrun (after making executable)
-chmod +x autonomy/scripts/profiler/profiling_example.py
-rosrun autonomy profiling_example.py
-```
-
-**Note**: The example script requires ROS to be properly sourced and `rospy` to be available.
-
-### Simple Profiling Test (No ROS Required)
-
-A standalone test without ROS dependencies is available in `autonomy/scripts/profiler/profiling_test.py`:
-
-```bash
-# Run the simple profiling test
-python3 autonomy/scripts/profiler/profiling_test.py
-
-# This will:
-# - Test all profiling decorators
-# - Generate mock data and run profiled functions
-# - Display performance results
-# - Export detailed JSON results
-```
-
-This test is useful for:
-- Verifying profiling functionality without ROS
-- Understanding profiling output format
-- Testing on systems without ROS installed
-
-Inside the example script, you will see usage of:
-- `ProfileManager.get_instance().set_node_name("cv_publisher")`
-- Decorators and `FunctionProfiler` blocks
-- Automatic export via `atexit` to `cv_publisher_profile.json`
-
----
-
-## ROS Node Profiler
-
-The ROS Node Profiler (`autonomy/scripts/profiler/ros_profiler.py`) monitors:
-- CPU & memory usage per node
-- Topic publish rates (FPS)
-- Optional function-level profiling if your nodes use the decorators
-
-### Usage
-
-1. Compile and source your workspace:
-
- ```bash
- # Build the workspace
- catkin_make
- source devel/setup.bash
- ```
-
-2. Run the profiler with:
-
- ```bash
- # Method 1: Direct execution
- python3 autonomy/scripts/profiler/ros_profiler.py [options]
-
- # Method 2: Using rosrun (after making executable)
- chmod +x autonomy/scripts/profiler/ros_profiler.py
- rosrun autonomy ros_profiler.py [options]
- ```
-
-3. View real-time console dashboard.
-
-**Example commands:**
-
-```bash
-# Monitor all nodes with default settings
-python3 autonomy/scripts/profiler/ros_profiler.py
-
-# Monitor specific nodes only
-python3 autonomy/scripts/profiler/ros_profiler.py --nodes cv_publisher controller_action
-
-# Enable function-level profiling and export data
-python3 autonomy/scripts/profiler/ros_profiler.py --profile-functions --export profile_data.csv
-
-# Custom update rate
-python3 autonomy/scripts/profiler/ros_profiler.py --update-rate 1.0
-```
-
-### CLI Options
-
-- `--nodes`: List of specific node names to monitor (default = all)
-- `--profile-functions`: Include function-level stats (requires your nodes to use decorators)
-- `--export`: CSV filename to record data over time
-- `--update-rate`: Sampling interval in seconds (default = 0.5s)
-
----
-
-## Exporting and Analyzing Results
-
-- **Function Decorator Exports**: Use `export_profiling_data("output.json")` at shutdown to dump collected JSON.
-- **ROS Node Profiler Exports**: CSV export via `--export` and summary JSON created alongside.
-
-Imported JSON can be visualized with tools like Excel, Python scripts, or custom dashboards.
-
----
-
-## Best Practices
-
-- Profile only critical or slow code paths to reduce overhead.
-- Use categories and clear naming conventions for easy filtering.
-- Combine real-time `ros_profiler` with offline JSON analysis for comprehensive insight.
-
-## Troubleshooting
-
-### Common Issues
-
-**1. "ModuleNotFoundError: No module named 'rospy'"**
-```bash
-# Solution: Source your ROS environment
-source /opt/ros/noetic/setup.bash # or melodic
-source devel/setup.bash # if workspace is built
-```
-
-**2. "rosrun: Couldn't find executable"**
-```bash
-# Solution: Make script executable and ensure it's in the right location
-chmod +x autonomy/scripts/profiler/profiling_example.py
-# Or run directly with python3
-python3 autonomy/scripts/profiler/profiling_example.py
-```
-
-**3. "package 'autonomy' not found"**
-```bash
-# Solution: Build and source your workspace
-catkin_make
-source devel/setup.bash
-```
-
-**4. Missing dependencies for ros_profiler.py**
-```bash
-# Solution: Install required Python packages
-pip install psutil colorama
-```
-
----
-
-_This guide helps you leverage detailed profiling across your Hydrus software stack for performance tuning and diagnostics._
diff --git a/docs/SOCAT_ARDUINO_MULTIPLEXER_REFACTORING.md b/docs/SOCAT_ARDUINO_MULTIPLEXER_REFACTORING.md
deleted file mode 100644
index 26c1c7a..0000000
--- a/docs/SOCAT_ARDUINO_MULTIPLEXER_REFACTORING.md
+++ /dev/null
@@ -1,771 +0,0 @@
-# SOCAT Virtual Serial Port Multiplexing - Refactoring Guide
-
-## Overview
-
-This document provides a comprehensive guide to refactor the Hydrus Arduino serial communication system to use SOCAT virtual device ports, enabling multiple processes to access the same Arduino while maintaining proper write access control.
-
-## Current Architecture Problems
-
-### Existing Issues
-```
-Arduino (/dev/ttyACM0) โ Multiple competing processes
-```
-
-**Problems:**
-- Only one process can open `/dev/ttyACM0` at a time
-- File-based logging creates I/O bottlenecks
-- No concurrent monitoring during ROS operation
-- Manual testing requires stopping all ROS processes
-
-## Proposed SOCAT Architecture
-
-### Design Principles
-1. **Single Writer**: Only one process can write to Arduino
-2. **Multiple Readers**: Multiple processes can read Arduino responses
-3. **Virtual Multiplexing**: SOCAT creates virtual endpoints
-4. **Process Isolation**: Each consumer gets independent data stream
-
-### Architecture Diagram
-```
- โโ /dev/hydrus_control (RW) โ serial_ros_bridge.py
-Arduino (/dev/ttyACM0) โ SOCAT โโโค
- โโ /dev/hydrus_monitor (RO) โ monitor_display.py
- โโ /dev/hydrus_debug (RO) โ manual testing
- โโ /dev/hydrus_log (RO) โ file logging (optional)
-```
-
-## Implementation Strategy
-
-### Phase 1: SOCAT Multiplexer Setup
-
-#### 1.1 Master Multiplexer Script
-
-Create `scripts/socat_arduino_multiplexer.py`:
-
-```python
-#!/usr/bin/env python3
-"""
-SOCAT Arduino Multiplexer
-Creates virtual serial ports for multiple Arduino access
-"""
-
-import os
-import signal
-import subprocess
-import sys
-import time
-from pathlib import Path
-from typing import Dict, List, Optional
-
-
-class ArduinoMultiplexer:
- def __init__(self, arduino_device="/dev/ttyACM0", baud_rate=115200):
- self.arduino_device = arduino_device
- self.baud_rate = baud_rate
-
- # Virtual device paths
- self.control_port = "/dev/hydrus_control" # Read/Write for ROS bridge
- self.monitor_port = "/dev/hydrus_monitor" # Read-only for monitoring
- self.debug_port = "/dev/hydrus_debug" # Read-only for manual testing
- self.log_port = "/dev/hydrus_log" # Read-only for file logging
-
- # Process management
- self.socat_processes: Dict[str, subprocess.Popen] = {}
- self.running = False
-
- # Setup signal handlers
- signal.signal(signal.SIGINT, self._signal_handler)
- signal.signal(signal.SIGTERM, self._signal_handler)
-
- def _check_dependencies(self):
- """Ensure SOCAT is installed"""
- try:
- subprocess.run(["socat", "-V"], capture_output=True, check=True)
- print("โ SOCAT found")
- except (subprocess.CalledProcessError, FileNotFoundError):
- print("Installing SOCAT...")
- subprocess.run(["apt-get", "update"], check=True)
- subprocess.run(["apt-get", "install", "-y", "socat"], check=True)
-
- def _check_arduino_device(self):
- """Verify Arduino device exists and is accessible"""
- if not os.path.exists(self.arduino_device):
- raise FileNotFoundError(f"Arduino device {self.arduino_device} not found")
-
- if not os.access(self.arduino_device, os.R_OK | os.W_OK):
- raise PermissionError(f"No read/write access to {self.arduino_device}")
-
- def _create_virtual_port(self, name: str, virtual_path: str, readonly: bool = False) -> subprocess.Popen:
- """Create a single virtual serial port"""
-
- # Remove existing virtual device
- if os.path.exists(virtual_path):
- os.unlink(virtual_path)
-
- # Create virtual port with SOCAT
- if readonly:
- # Read-only virtual port
- socat_cmd = [
- "socat",
- f"pty,link={virtual_path},raw,echo=0,user=nobody,group=dialout,perm=0644",
- f"pipe:/tmp/hydrus_arduino_data"
- ]
- else:
- # Read/write virtual port (control port)
- socat_cmd = [
- "socat",
- f"pty,link={virtual_path},raw,echo=0,user=nobody,group=dialout,perm=0666",
- f"{self.arduino_device},raw,echo=0,b{self.baud_rate}"
- ]
-
- print(f"Creating {name} virtual port: {virtual_path}")
- process = subprocess.Popen(
- socat_cmd,
- stdout=subprocess.PIPE,
- stderr=subprocess.PIPE,
- preexec_fn=os.setsid
- )
-
- return process
-
- def _create_data_splitter(self) -> subprocess.Popen:
- """Create data splitter that feeds multiple read-only ports"""
-
- # Create named pipe for data distribution
- pipe_path = "/tmp/hydrus_arduino_data"
- if os.path.exists(pipe_path):
- os.unlink(pipe_path)
- os.mkfifo(pipe_path, 0o666)
-
- # SOCAT command to split Arduino data to multiple outputs
- tee_targets = f">(socat - pty,link={self.monitor_port},raw,echo=0,perm=0444) " + \
- f">(socat - pty,link={self.debug_port},raw,echo=0,perm=0444) " + \
- f">(socat - pty,link={self.log_port},raw,echo=0,perm=0444)"
-
- splitter_cmd = [
- "bash", "-c",
- f"socat {self.arduino_device},raw,echo=0,b{self.baud_rate} " +
- f"EXEC:'tee {tee_targets}'"
- ]
-
- print("Creating data splitter...")
- process = subprocess.Popen(
- splitter_cmd,
- stdout=subprocess.PIPE,
- stderr=subprocess.PIPE,
- preexec_fn=os.setsid
- )
-
- return process
-
- def start_multiplexer(self):
- """Start the complete multiplexer system"""
- print("Starting Arduino SOCAT Multiplexer...")
-
- try:
- # Check dependencies and device
- self._check_dependencies()
- self._check_arduino_device()
-
- # Method 1: Simple approach with single control port + data splitter
- self._start_simple_multiplexer()
-
- self.running = True
- print("โ Arduino multiplexer started successfully")
- print(f"โ Control port (RW): {self.control_port}")
- print(f"โ Monitor port (RO): {self.monitor_port}")
- print(f"โ Debug port (RO): {self.debug_port}")
- print(f"โ Log port (RO): {self.log_port}")
-
- except Exception as e:
- print(f"โ Failed to start multiplexer: {e}")
- self.stop_multiplexer()
- raise
-
- def _start_simple_multiplexer(self):
- """Simple multiplexer implementation"""
-
- # 1. Create control port (bidirectional)
- control_process = self._create_virtual_port("Control", self.control_port, readonly=False)
- self.socat_processes["control"] = control_process
- time.sleep(1) # Allow port creation
-
- # 2. Create data splitter for read-only ports
- splitter_process = self._create_data_splitter()
- self.socat_processes["splitter"] = splitter_process
- time.sleep(2) # Allow all ports to be created
-
- # 3. Verify all ports exist
- self._verify_ports()
-
- def _verify_ports(self):
- """Verify all virtual ports were created successfully"""
- ports = [self.control_port, self.monitor_port, self.debug_port, self.log_port]
-
- for port in ports:
- if not os.path.exists(port):
- raise RuntimeError(f"Failed to create virtual port: {port}")
- print(f"โ Virtual port created: {port}")
-
- def stop_multiplexer(self):
- """Stop all SOCAT processes and cleanup"""
- print("Stopping Arduino multiplexer...")
- self.running = False
-
- # Kill all SOCAT processes
- for name, process in self.socat_processes.items():
- try:
- print(f"Stopping {name} process...")
- os.killpg(os.getpgid(process.pid), signal.SIGTERM)
- process.wait(timeout=5)
- except Exception as e:
- print(f"Error stopping {name}: {e}")
- try:
- os.killpg(os.getpgid(process.pid), signal.SIGKILL)
- except:
- pass
-
- # Cleanup virtual device files
- ports = [self.control_port, self.monitor_port, self.debug_port, self.log_port]
- for port in ports:
- try:
- if os.path.exists(port):
- os.unlink(port)
- print(f"โ Removed {port}")
- except Exception as e:
- print(f"Error removing {port}: {e}")
-
- # Cleanup named pipes
- try:
- pipe_path = "/tmp/hydrus_arduino_data"
- if os.path.exists(pipe_path):
- os.unlink(pipe_path)
- except:
- pass
-
- def _signal_handler(self, signum, frame):
- """Handle shutdown signals"""
- print(f"\nReceived signal {signum}, shutting down...")
- self.stop_multiplexer()
- sys.exit(0)
-
- def run_daemon(self):
- """Run multiplexer as daemon"""
- self.start_multiplexer()
-
- try:
- while self.running:
- # Monitor processes health
- for name, process in self.socat_processes.items():
- if process.poll() is not None:
- print(f"Warning: {name} process died, restarting...")
- # Could implement restart logic here
-
- time.sleep(5)
- except KeyboardInterrupt:
- pass
- finally:
- self.stop_multiplexer()
-
-
-def main():
- if len(sys.argv) > 1:
- arduino_device = sys.argv[1]
- else:
- arduino_device = "/dev/ttyACM0"
-
- multiplexer = ArduinoMultiplexer(arduino_device)
- multiplexer.run_daemon()
-
-
-if __name__ == "__main__":
- main()
-```
-
-### Phase 2: Refactor Existing Components
-
-#### 2.1 Modified Serial ROS Bridge
-
-Update `autonomy/scripts/controller/serial_ros_bridge.py`:
-
-```python
-#!/usr/bin/env python3
-"""
-Modified Serial ROS Bridge using SOCAT virtual ports
-"""
-
-import threading
-import time
-import rospy
-import serial
-from std_msgs.msg import Int16
-from termcolor import colored
-
-
-class SerialROSBridge:
- def __init__(self):
- rospy.init_node("serial_ros_bridge", anonymous=True)
-
- # Use virtual control port instead of physical device
- self.control_port = rospy.get_param("~control_port", "/dev/hydrus_control")
- baud_rate = rospy.get_param("~baud_rate", 115200)
-
- # Serial connection to virtual control port
- self.ser = None
- self.is_connected = False
- self._connect_to_control_port(baud_rate)
-
- # Start response reading thread
- self.running = True
- self.response_thread = threading.Thread(target=self._read_responses)
- self.response_thread.daemon = True
- self.response_thread.start()
-
- # ROS Subscribers (unchanged)
- rospy.Subscriber("/hydrus/thrusters/1", Int16, lambda msg: self.thruster_callback(1, msg))
- rospy.Subscriber("/hydrus/thrusters/2", Int16, lambda msg: self.thruster_callback(2, msg))
- rospy.Subscriber("/hydrus/thrusters/3", Int16, lambda msg: self.thruster_callback(3, msg))
- rospy.Subscriber("/hydrus/thrusters/4", Int16, lambda msg: self.thruster_callback(4, msg))
- rospy.Subscriber("/hydrus/depth", Int16, self.depth_callback)
- rospy.Subscriber("/hydrus/torpedo", Int16, self.torpedo_callback)
-
- rospy.on_shutdown(self.shutdown)
-
- def _connect_to_control_port(self, baud_rate):
- """Connect to SOCAT virtual control port"""
- max_retries = 10
- retry_count = 0
-
- while retry_count < max_retries and not rospy.is_shutdown():
- try:
- if not os.path.exists(self.control_port):
- rospy.logwarn(f"Control port {self.control_port} not found, waiting...")
- time.sleep(2)
- retry_count += 1
- continue
-
- self.ser = serial.Serial(port=self.control_port, baudrate=baud_rate, timeout=1)
- self.is_connected = True
- rospy.loginfo(f"Connected to Arduino via {self.control_port}")
- time.sleep(2) # Allow Arduino reset
- return True
-
- except serial.SerialException as e:
- rospy.logwarn(f"Failed to connect to {self.control_port}: {e}")
- time.sleep(2)
- retry_count += 1
-
- rospy.logerr(f"Failed to connect to control port after {max_retries} attempts")
- return False
-
- # Rest of the class remains the same...
- # (send_command, _read_responses, callbacks, shutdown methods unchanged)
-```
-
-#### 2.2 New Monitoring System
-
-Create `scripts/arduino_monitor.py`:
-
-```python
-#!/usr/bin/env python3
-"""
-Arduino Monitor using SOCAT virtual ports
-Real-time monitoring without interfering with ROS bridge
-"""
-
-import os
-import time
-import serial
-from datetime import datetime
-from termcolor import colored
-
-
-class ArduinoMonitor:
- def __init__(self, monitor_port="/dev/hydrus_monitor"):
- self.monitor_port = monitor_port
- self.running = False
-
- def start_monitoring(self):
- """Start real-time Arduino monitoring"""
- print(colored("Arduino Monitor Starting...", "cyan"))
- print(colored(f"Monitoring port: {self.monitor_port}", "yellow"))
- print(colored("Press Ctrl+C to stop", "yellow"))
- print("=" * 60)
-
- while not os.path.exists(self.monitor_port):
- print(colored("Waiting for monitor port to be available...", "yellow"))
- time.sleep(1)
-
- try:
- with serial.Serial(self.monitor_port, 115200, timeout=1) as ser:
- self.running = True
- print(colored("โ Connected to Arduino monitor", "green"))
-
- while self.running:
- try:
- if ser.in_waiting > 0:
- line = ser.readline().decode('utf-8').strip()
- if line:
- timestamp = datetime.now().strftime("%H:%M:%S.%f")[:-3]
- print(f"[{colored(timestamp, 'blue')}] {line}")
- except Exception as e:
- print(colored(f"Monitor error: {e}", "red"))
- break
-
- time.sleep(0.01)
-
- except Exception as e:
- print(colored(f"Failed to connect to monitor port: {e}", "red"))
-
- def stop_monitoring(self):
- """Stop monitoring"""
- self.running = False
-
-
-def main():
- monitor = ArduinoMonitor()
- try:
- monitor.start_monitoring()
- except KeyboardInterrupt:
- print(colored("\nStopping Arduino monitor...", "yellow"))
- monitor.stop_monitoring()
-
-
-if __name__ == "__main__":
- main()
-```
-
-#### 2.3 Enhanced Debug Terminal
-
-Create `scripts/arduino_debug_terminal.py`:
-
-```python
-#!/usr/bin/env python3
-"""
-Arduino Debug Terminal using SOCAT virtual ports
-Manual testing without stopping ROS systems
-"""
-
-import os
-import sys
-import threading
-import time
-import serial
-from termcolor import colored
-
-
-class ArduinoDebugTerminal:
- def __init__(self, debug_port="/dev/hydrus_debug", control_port="/dev/hydrus_control"):
- self.debug_port = debug_port # Read-only monitoring
- self.control_port = control_port # Write access for commands
- self.running = False
-
- def start_terminal(self):
- """Start interactive debug terminal"""
- print(colored("Arduino Debug Terminal", "cyan", attrs=["bold"]))
- print(colored("Commands will be sent via control port", "yellow"))
- print(colored("Responses monitored via debug port", "yellow"))
- print(colored("Type 'quit' to exit", "yellow"))
- print("=" * 50)
-
- # Wait for ports to be available
- while not (os.path.exists(self.debug_port) and os.path.exists(self.control_port)):
- print("Waiting for virtual ports...")
- time.sleep(1)
-
- try:
- # Open control port for sending commands
- control_ser = serial.Serial(self.control_port, 115200, timeout=1)
-
- # Open debug port for monitoring responses
- debug_ser = serial.Serial(self.debug_port, 115200, timeout=1)
-
- print(colored("โ Connected to Arduino debug interface", "green"))
-
- # Start response monitoring thread
- self.running = True
- monitor_thread = threading.Thread(target=self._monitor_responses, args=(debug_ser,))
- monitor_thread.daemon = True
- monitor_thread.start()
-
- # Main command input loop
- self._command_loop(control_ser)
-
- except Exception as e:
- print(colored(f"Failed to connect: {e}", "red"))
- finally:
- self.running = False
-
- def _monitor_responses(self, debug_ser):
- """Monitor Arduino responses in background thread"""
- while self.running:
- try:
- if debug_ser.in_waiting > 0:
- line = debug_ser.readline().decode('utf-8').strip()
- if line:
- print(colored(f"<< {line}", "green"))
- except Exception as e:
- if self.running:
- print(colored(f"Monitor error: {e}", "red"))
- break
- time.sleep(0.01)
-
- def _command_loop(self, control_ser):
- """Interactive command input loop"""
- while self.running:
- try:
- # Get user input
- cmd = input(colored(">> ", "blue")).strip()
-
- if cmd.lower() in ['quit', 'exit', 'q']:
- break
-
- if cmd:
- # Send command to Arduino
- control_ser.write(f"{cmd}\n".encode('utf-8'))
- control_ser.flush()
- print(colored(f">> {cmd}", "blue"))
-
- except KeyboardInterrupt:
- break
- except Exception as e:
- print(colored(f"Input error: {e}", "red"))
-
- def stop_terminal(self):
- """Stop debug terminal"""
- self.running = False
-
-
-def main():
- terminal = ArduinoDebugTerminal()
- try:
- terminal.start_terminal()
- except KeyboardInterrupt:
- pass
- finally:
- print(colored("\nExiting debug terminal...", "yellow"))
- terminal.stop_terminal()
-
-
-if __name__ == "__main__":
- main()
-```
-
-### Phase 3: Integration Scripts
-
-#### 3.1 Startup Script
-
-Create `scripts/start_arduino_multiplexer.sh`:
-
-```bash
-#!/bin/bash
-"""
-Start Arduino SOCAT Multiplexer System
-"""
-
-SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
-ARDUINO_DEVICE="${1:-/dev/ttyACM0}"
-
-echo "Starting Arduino SOCAT Multiplexer..."
-echo "Arduino device: $ARDUINO_DEVICE"
-
-# Check if Arduino device exists
-if [ ! -e "$ARDUINO_DEVICE" ]; then
- echo "Error: Arduino device $ARDUINO_DEVICE not found"
- echo "Available devices:"
- ls /dev/ttyACM* /dev/ttyUSB* 2>/dev/null || echo "No serial devices found"
- exit 1
-fi
-
-# Start multiplexer in background
-python3 "$SCRIPT_DIR/socat_arduino_multiplexer.py" "$ARDUINO_DEVICE" &
-MULTIPLEXER_PID=$!
-
-# Save PID for cleanup
-echo $MULTIPLEXER_PID > /tmp/arduino_multiplexer.pid
-
-echo "Arduino multiplexer started with PID: $MULTIPLEXER_PID"
-echo "Virtual ports will be available shortly..."
-
-# Wait for virtual ports to be created
-sleep 3
-
-# Verify ports exist
-if [ -e "/dev/hydrus_control" ] && [ -e "/dev/hydrus_monitor" ]; then
- echo "โ Virtual ports created successfully"
- echo " Control port (RW): /dev/hydrus_control"
- echo " Monitor port (RO): /dev/hydrus_monitor"
- echo " Debug port (RO): /dev/hydrus_debug"
- echo " Log port (RO): /dev/hydrus_log"
-else
- echo "โ Failed to create virtual ports"
- exit 1
-fi
-
-echo "Arduino multiplexer ready!"
-```
-
-#### 3.2 Stop Script
-
-Create `scripts/stop_arduino_multiplexer.sh`:
-
-```bash
-#!/bin/bash
-"""
-Stop Arduino SOCAT Multiplexer System
-"""
-
-echo "Stopping Arduino SOCAT Multiplexer..."
-
-# Kill multiplexer process
-if [ -f /tmp/arduino_multiplexer.pid ]; then
- PID=$(cat /tmp/arduino_multiplexer.pid)
- echo "Stopping multiplexer process (PID: $PID)..."
- kill $PID 2>/dev/null
- rm -f /tmp/arduino_multiplexer.pid
-fi
-
-# Kill any remaining SOCAT processes
-pkill -f "socat.*hydrus" 2>/dev/null
-
-# Clean up virtual devices
-echo "Cleaning up virtual devices..."
-rm -f /dev/hydrus_control /dev/hydrus_monitor /dev/hydrus_debug /dev/hydrus_log
-rm -f /tmp/hydrus_arduino_data
-
-echo "Arduino multiplexer stopped"
-```
-
-### Phase 4: Modified TMUX Integration
-
-#### 4.1 Updated TMUX Sessions
-
-Modify `start_tmux_sessions.py` to use the new system:
-
-```python
-def _setup_arduino_multiplexer(self):
- """Setup Arduino SOCAT multiplexer before starting sessions"""
- print("Starting Arduino SOCAT multiplexer...")
-
- multiplexer_script = self.catkin_ws / "src/hydrus-software-stack/scripts/start_arduino_multiplexer.sh"
-
- if multiplexer_script.exists():
- self._run_command([str(multiplexer_script)], check=True)
- print("โ Arduino multiplexer started")
- else:
- print("โ Arduino multiplexer script not found, using fallback")
- self._setup_virtual_arduino() # Fallback to existing system
-
-def _create_arduino_window(self):
- """Create Arduino window using new monitoring system"""
- print("Creating Arduino window...")
-
- # Create new window for Arduino monitoring
- self._tmux_command(["new-window", "-t", "hydrus:1", "-n", "Arduino"])
-
- # First pane: Real-time Arduino monitor
- monitor_script = self.catkin_ws / "src/hydrus-software-stack/scripts/arduino_monitor.py"
-
- if monitor_script.exists():
- monitor_cmd = f"python3 {monitor_script}"
- else:
- monitor_cmd = f"python3 {self.catkin_ws}/src/hydrus-software-stack/scripts/monitor_arduino_logs.py"
-
- self._tmux_command([
- "send-keys", "-t", "hydrus:1.0",
- f"echo 'Starting Arduino Monitor'; {monitor_cmd}", "C-m"
- ])
-
- # Second pane: Debug terminal
- self._tmux_command(["split-window", "-h", "-t", "hydrus:1"])
- debug_script = self.catkin_ws / "src/hydrus-software-stack/scripts/arduino_debug_terminal.py"
-
- if debug_script.exists():
- debug_cmd = f"python3 {debug_script}"
- else:
- debug_cmd = "echo 'Debug terminal not available'"
-
- self._tmux_command([
- "send-keys", "-t", "hydrus:1.1",
- f"echo 'Starting Debug Terminal'; {debug_cmd}", "C-m"
- ])
-
- # Set layout
- self._tmux_command(["select-layout", "-t", "hydrus:1", "even-horizontal"])
-```
-
-## Usage Examples
-
-### Starting the System
-
-```bash
-# 1. Start Arduino multiplexer
-./scripts/start_arduino_multiplexer.sh
-
-# 2. Start ROS systems (now uses /dev/hydrus_control)
-./start_tmux_sessions.sh
-
-# 3. Use debug terminal (concurrent with ROS)
-python3 scripts/arduino_debug_terminal.py
-```
-
-### Manual Testing (Concurrent with ROS)
-
-```bash
-# Terminal 1: Monitor Arduino responses
-python3 scripts/arduino_monitor.py
-
-# Terminal 2: Send manual commands
-python3 scripts/arduino_debug_terminal.py
->> T1:1600
-<< ACK:T1:1600
->> D:1400
-<< ACK:D:1400
-
-# Terminal 3: ROS continues operating normally
-rostopic pub /hydrus/thrusters/2 std_msgs/Int16 "data: 1550"
-```
-
-## Benefits of New Architecture
-
-### Performance Improvements
-- **Zero file I/O** for monitoring
-- **1-10ms latency** instead of 10-100ms
-- **Real-time access** for all consumers
-- **Better resource utilization**
-
-### Operational Benefits
-- **Concurrent debugging** while ROS runs
-- **Multiple monitors** can watch Arduino simultaneously
-- **No service interruption** for manual testing
-- **Isolated failure domains**
-
-### Development Benefits
-- **Easier debugging** with real-time access
-- **Better testing workflows**
-- **Scalable monitoring** architecture
-- **Professional system management**
-
-## Migration Checklist
-
-### Pre-Migration
-- [ ] Backup current serial communication scripts
-- [ ] Test SOCAT installation and functionality
-- [ ] Verify Arduino device permissions
-- [ ] Document current system behavior
-
-### Migration Steps
-- [ ] Implement SOCAT multiplexer script
-- [ ] Update serial_ros_bridge.py to use control port
-- [ ] Create new monitoring and debug scripts
-- [ ] Update TMUX session management
-- [ ] Test entire system integration
-
-### Post-Migration Testing
-- [ ] Verify ROS bridge functionality
-- [ ] Test concurrent monitoring
-- [ ] Validate manual debug terminal
-- [ ] Confirm system startup/shutdown
-- [ ] Performance benchmarking
-
-This refactoring provides a robust, efficient, and scalable Arduino communication system that eliminates the current limitations while maintaining full compatibility with existing ROS operations.
diff --git a/docs/TMUX_MODULAR_CONFIGURATION.md b/docs/TMUX_MODULAR_CONFIGURATION.md
deleted file mode 100644
index 1486eab..0000000
--- a/docs/TMUX_MODULAR_CONFIGURATION.md
+++ /dev/null
@@ -1,225 +0,0 @@
-# Modular Tmux Session Manager
-
-The Hydrus tmux session manager has been refactored to use a dictionary-based configuration system, making it highly modular and customizable.
-
-## Features
-
-- **Dictionary-based configuration**: Each window is defined by a configuration dictionary
-- **Flexible layouts**: Support for different tmux layouts per window
-- **Command-line interface**: Various options for managing configurations
-- **Configuration validation**: Ensure window configurations are properly structured
-- **Selective window creation**: Create only specific windows
-- **External configuration files**: Load configurations from JSON files
-
-## Configuration Structure
-
-Each window is defined by a dictionary with the following structure:
-
-```python
-{
- "window_name": {
- "window_index": 0, # Unique index for the window
- "layout": "even-horizontal", # Tmux layout (optional)
- "panes": [
- {
- "name": "Pane Name", # Descriptive name
- "command": "echo 'Hello'", # Command to run
- "split": None # No split for first pane
- },
- {
- "name": "Second Pane",
- "command": "htop",
- "split": "horizontal" # Split direction: "horizontal" or "vertical"
- }
- ]
- }
-}
-```
-
-### Required Fields
-
-- `window_index`: Unique integer identifying the window order
-- `panes`: List of pane configurations
-- `panes[].command`: Command to execute in the pane
-- `panes[].split`: Split direction for panes after the first (None for first pane)
-
-### Optional Fields
-
-- `layout`: Tmux layout name (default: "even-horizontal")
-- `panes[].name`: Descriptive name for the pane (for documentation)
-
-## Usage Examples
-
-### Basic Usage
-```bash
-# Create all configured windows
-python3 start_tmux_sessions.py
-
-# Create only specific windows
-python3 start_tmux_sessions.py --windows "Controls" "Arduino"
-```
-
-### Configuration Management
-```bash
-# List all configured windows
-python3 start_tmux_sessions.py --list-windows
-
-# Validate current configuration
-python3 start_tmux_sessions.py --validate
-
-# Save current configuration to file
-python3 start_tmux_sessions.py --save-config my_config.json
-
-# Load configuration from file
-python3 start_tmux_sessions.py --config config/tmux_windows.json
-```
-
-### Development Workflow
-```bash
-# Create only development and monitoring windows
-python3 start_tmux_sessions.py --windows "Development" "Arduino"
-
-# Load custom configuration for testing
-python3 start_tmux_sessions.py --config test_config.json --windows "Controls"
-```
-
-## Available Layouts
-
-- `even-horizontal`: Panes evenly distributed horizontally
-- `even-vertical`: Panes evenly distributed vertically
-- `main-horizontal`: Large main pane on top, others below
-- `main-vertical`: Large main pane on left, others on right
-- `tiled`: Panes arranged in a tiled pattern
-
-## Pane Split Directions
-
-- `"horizontal"`: Split horizontally (new pane below)
-- `"vertical"`: Split vertically (new pane to the right)
-- `null`/`None`: No split (only for first pane)
-
-## Example Configurations
-
-### Simple Two-Pane Window
-```json
-{
- "Simple": {
- "window_index": 0,
- "layout": "even-horizontal",
- "panes": [
- {
- "name": "Main Terminal",
- "command": "bash",
- "split": null
- },
- {
- "name": "System Monitor",
- "command": "htop",
- "split": "horizontal"
- }
- ]
- }
-}
-```
-
-### Complex Four-Pane Development Window
-```json
-{
- "Development": {
- "window_index": 1,
- "layout": "tiled",
- "panes": [
- {
- "name": "Code Editor",
- "command": "cd /workspace && vim",
- "split": null
- },
- {
- "name": "Build Output",
- "command": "cd /workspace && tail -f build.log",
- "split": "horizontal"
- },
- {
- "name": "Git Status",
- "command": "cd /workspace && watch git status",
- "split": "vertical"
- },
- {
- "name": "Test Runner",
- "command": "cd /workspace && pytest --watch",
- "split": "horizontal"
- }
- ]
- }
-}
-```
-
-## Programming Interface
-
-You can also modify configurations programmatically:
-
-```python
-from start_tmux_sessions import HydrusTmuxManager
-
-manager = HydrusTmuxManager()
-
-# Add a new window configuration
-new_window = {
- "window_index": 5,
- "layout": "main-vertical",
- "panes": [
- {
- "name": "Main Task",
- "command": "python3 my_script.py",
- "split": None
- },
- {
- "name": "Monitor",
- "command": "watch ps aux",
- "split": "horizontal"
- }
- ]
-}
-
-manager.add_window_config("Custom Window", new_window)
-
-# Validate the configuration
-if manager.validate_window_config(new_window):
- print("Configuration is valid!")
-
-# Create the session
-manager.main()
-```
-
-## Migration from Old System
-
-The old individual window creation methods have been replaced with the modular system. If you need to customize windows:
-
-1. **Export current config**: `python3 start_tmux_sessions.py --save-config current.json`
-2. **Edit the JSON file** with your customizations
-3. **Load the config**: `python3 start_tmux_sessions.py --config current.json`
-
-## Troubleshooting
-
-### Configuration Validation Errors
-```bash
-# Check what's wrong with your configuration
-python3 start_tmux_sessions.py --validate
-```
-
-Common issues:
-- First pane has a split defined (should be `null`)
-- Missing required fields (`command`, `split` for non-first panes)
-- Invalid split directions (must be "horizontal" or "vertical")
-- Duplicate window indices
-
-### Window Creation Failures
-- Check that all script paths exist
-- Verify ROS environment is properly sourced
-- Ensure required dependencies are installed
-
-### Tmux Issues
-- Make sure tmux is installed: `sudo apt install tmux`
-- Check existing sessions: `tmux list-sessions`
-- Kill problematic sessions: `tmux kill-session -t hydrus`
-
-This modular system provides much greater flexibility while maintaining compatibility with the existing Hydrus infrastructure.
diff --git a/hocker b/hocker
index 20b74cc..474d715 120000
--- a/hocker
+++ b/hocker
@@ -1 +1 @@
-/home/catkin_ws/src/hydrus-software-stack/docker/hydrus-docker/hocker.py
\ No newline at end of file
+/home/cesar/Projects/hydrus-software-stack/docker/hydrus-docker/hocker.py
\ No newline at end of file
diff --git a/hydrus-cli b/hydrus-cli
index 3074f4a..03199d0 100755
--- a/hydrus-cli
+++ b/hydrus-cli
@@ -5,9 +5,6 @@
# Get the directory where this wrapper is located (should be project root)
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
-# Set HYDRUS_ROOT environment variable to ensure test system works correctly
-export HYDRUS_ROOT="$SCRIPT_DIR"
-
# Change to the project directory to ensure proper module resolution
cd "$SCRIPT_DIR"
diff --git a/hydrus_software_stack.egg-info/PKG-INFO b/hydrus_software_stack.egg-info/PKG-INFO
deleted file mode 100644
index 93c0108..0000000
--- a/hydrus_software_stack.egg-info/PKG-INFO
+++ /dev/null
@@ -1,120 +0,0 @@
-Metadata-Version: 2.4
-Name: hydrus-software-stack
-Version: 0.1.0
-Summary: Autonomous underwater vehicle software stack with computer vision and navigation capabilities
-Home-page: https://github.com/your-username/hydrus-software-stack
-Author: Cesar
-Author-email: cesar@example.com
-Project-URL: Bug Reports, https://github.com/your-username/hydrus-software-stack/issues
-Project-URL: Source, https://github.com/your-username/hydrus-software-stack
-Classifier: Development Status :: 3 - Alpha
-Classifier: Intended Audience :: Developers
-Classifier: License :: OSI Approved :: MIT License
-Classifier: Programming Language :: Python :: 3
-Classifier: Programming Language :: Python :: 3.8
-Classifier: Programming Language :: Python :: 3.9
-Classifier: Programming Language :: Python :: 3.10
-Classifier: Programming Language :: Python :: 3.11
-Classifier: Operating System :: OS Independent
-Requires-Python: >=3.8
-Description-Content-Type: text/markdown
-License-File: LICENSE
-Requires-Dist: opencv-python
-Requires-Dist: numpy
-Requires-Dist: matplotlib
-Requires-Dist: requests
-Requires-Dist: colorama
-Requires-Dist: pyserial
-Requires-Dist: fastapi
-Requires-Dist: Flask
-Requires-Dist: black>=23.0.0
-Requires-Dist: isort>=5.12.0
-Requires-Dist: flake8>=6.0.0
-Requires-Dist: pre-commit>=3.0.0
-Requires-Dist: mypy>=1.0.0
-Provides-Extra: depth-estimation
-Requires-Dist: onnx; extra == "depth-estimation"
-Requires-Dist: onnxruntime-gpu; extra == "depth-estimation"
-Requires-Dist: onnxscript; extra == "depth-estimation"
-Requires-Dist: onnxslim; extra == "depth-estimation"
-Requires-Dist: torch; extra == "depth-estimation"
-Requires-Dist: torchvision; extra == "depth-estimation"
-Requires-Dist: tqdm; extra == "depth-estimation"
-Requires-Dist: typer; extra == "depth-estimation"
-Provides-Extra: all
-Requires-Dist: onnx; extra == "all"
-Requires-Dist: onnxruntime-gpu; extra == "all"
-Requires-Dist: onnxscript; extra == "all"
-Requires-Dist: onnxslim; extra == "all"
-Requires-Dist: torch; extra == "all"
-Requires-Dist: torchvision; extra == "all"
-Requires-Dist: tqdm; extra == "all"
-Requires-Dist: typer; extra == "all"
-Dynamic: author
-Dynamic: author-email
-Dynamic: classifier
-Dynamic: description
-Dynamic: description-content-type
-Dynamic: home-page
-Dynamic: license-file
-Dynamic: project-url
-Dynamic: provides-extra
-Dynamic: requires-dist
-Dynamic: requires-python
-Dynamic: summary
-
-
-
-
๐ Hydrus Software Stack
-
-
- A comprehensive ROS-based toolkit for autonomous underwater vehicles.
-
- Built for RobSub competitions with maintainability and usability at its core.
-