From 2e390f5aaeb67ae5e541b0639645577c03e55172 Mon Sep 17 00:00:00 2001 From: osu_uwrt_bot Date: Sun, 28 Sep 2025 16:05:34 -0400 Subject: [PATCH 01/50] Put uROS agent back in hardware launches --- riptide_hardware/launch/hardware.launch.py | 12 ++++++------ riptide_hardware/launch/hardware_fake_dvl.launch.py | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/riptide_hardware/launch/hardware.launch.py b/riptide_hardware/launch/hardware.launch.py index 1866813..58d6106 100644 --- a/riptide_hardware/launch/hardware.launch.py +++ b/riptide_hardware/launch/hardware.launch.py @@ -49,12 +49,12 @@ def generate_launch_description(): PushRosNamespace( LC("robot") ), - # IncludeLaunchDescription( - # AnyLaunchDescriptionSource(copro_agent_launch_file), - # launch_arguments=[ - # ('robot', LC('robot')), - # ] - # ), + IncludeLaunchDescription( + AnyLaunchDescriptionSource(copro_agent_launch_file), + launch_arguments=[ + ('robot', LC('robot')), + ] + ), IncludeLaunchDescription( AnyLaunchDescriptionSource(diagnostics_launch_file), launch_arguments=[ diff --git a/riptide_hardware/launch/hardware_fake_dvl.launch.py b/riptide_hardware/launch/hardware_fake_dvl.launch.py index ac887a1..408e9d0 100644 --- a/riptide_hardware/launch/hardware_fake_dvl.launch.py +++ b/riptide_hardware/launch/hardware_fake_dvl.launch.py @@ -56,12 +56,12 @@ def generate_launch_description(): # name='zenoh_router', # output='screen', # ), - # IncludeLaunchDescription( - # AnyLaunchDescriptionSource(copro_agent_launch_file), - # launch_arguments=[ - # ('robot', LC('robot')), - # ] - # ), + IncludeLaunchDescription( + AnyLaunchDescriptionSource(copro_agent_launch_file), + launch_arguments=[ + ('robot', LC('robot')), + ] + ), IncludeLaunchDescription( AnyLaunchDescriptionSource(diagnostics_launch_file), launch_arguments=[ From 15984d3e2cb2490010ba25f96a47e9b8a26f2e64 Mon Sep 17 00:00:00 2001 From: osu_uwrt_bot Date: Sat, 25 Oct 2025 00:06:06 -0400 Subject: [PATCH 02/50] Better FFC cal --- riptide_hardware/cfg/ffc_config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/riptide_hardware/cfg/ffc_config.yaml b/riptide_hardware/cfg/ffc_config.yaml index cec331f..84bab14 100644 --- a/riptide_hardware/cfg/ffc_config.yaml +++ b/riptide_hardware/cfg/ffc_config.yaml @@ -5,7 +5,7 @@ pub_resolution: "NATIVE" # The resolution used for image and depth map publishing. 'NATIVE' to use the same `general.grab_resolution` - `CUSTOM` to apply the `general.pub_downscale_factor` downscale factory to reduce bandwidth in transmission pub_downscale_factor: 2.0 # rescale factor used to rescale image before publishing when 'pub_resolution' is 'CUSTOM' pub_frame_rate: 30.0 - optional_opencv_calibration_file: "/home/ros/zed_cals/taloszedxffc_cal5.yaml" + optional_opencv_calibration_file: "/home/ros/zed_cals/taloszedxffc_cal7.yaml" grab_resolution: 'HD1200' self_calib: false pos_tracking: From 07cb544d1d3a3834d6d961fe61dc0e9155f7fc50 Mon Sep 17 00:00:00 2001 From: osu_uwrt_bot Date: Fri, 31 Oct 2025 17:19:49 -0400 Subject: [PATCH 03/50] Post-Sean tunes --- riptide_descriptions/config/talos.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/riptide_descriptions/config/talos.yaml b/riptide_descriptions/config/talos.yaml index 72b149b..34db370 100644 --- a/riptide_descriptions/config/talos.yaml +++ b/riptide_descriptions/config/talos.yaml @@ -123,10 +123,10 @@ controller: #[x,y,z,r,p,y] #first order eta - eta_order_0: [1.8, 0.5, 10.0, 0.2, 0.2, 0.3] + eta_order_0: [1.8, 0.2, 10.0, 0.2, 0.2, 0.3] #second order eta - eta_order_1: [0.08, 0.08, 0.3, 0.0, 0.0, 0.0] + eta_order_1: [0.08, 0.04, 0.3, 0.0, 0.0, 0.0] #lambda lambda: [0.25, 0.25, 1.0, 10.0, 10.0, 0.35] @@ -152,9 +152,9 @@ controller: #linear unit are newtons per meter, angular are newton meter per radian #the default gains for normal operation - p_gains: [3.0, 18.0, 200.0, 3.5, 2.0, 2.5] + p_gains: [3.0, 18.0, 200.0, 3.0, 2.0, 1.0] i_gains: [0.0, 0.0, 0.0, 0.01, 1.0, 0.0] - d_gains: [5.0, 5.0, 90.0, 1.0, 0.05, 0.9] + d_gains: [5.0, 5.0, 90.0, 1.5, 0.05, 0.9] # the control value thresholds for each DOF -- body frame max_control_thresholds: [30, 30, 30, 15, 15, 15] From 2c3ceb6fb59037ff672a4283ec4ae50b686576da Mon Sep 17 00:00:00 2001 From: osu_uwrt_bot Date: Sun, 2 Nov 2025 17:54:35 -0500 Subject: [PATCH 04/50] Better tuning, zed picture taker enabled --- riptide_descriptions/config/talos.yaml | 19 +++++++------- riptide_hardware/launch/zed.launch.py | 26 ++++++++++--------- .../riptide_hardware2/picture_taker.py | 5 ++-- 3 files changed, 27 insertions(+), 23 deletions(-) diff --git a/riptide_descriptions/config/talos.yaml b/riptide_descriptions/config/talos.yaml index 34db370..7e957c0 100644 --- a/riptide_descriptions/config/talos.yaml +++ b/riptide_descriptions/config/talos.yaml @@ -6,7 +6,7 @@ base_link: [-0.14, 0.03, -0.09] # Vehicle mass properties mass: 31.998 -com: [-0.159, 0.042, -0.056] +com: [-0.148, 0.040, -0.071] inertia: [0.72604692, 1.59322252, 1.65134701] # kg*m^2 hull_volume: .11 #make this real @@ -24,8 +24,8 @@ controller: drag_enable: [1, 1, 0, 0, 0, 0] feed_forward: - #base_wrench: [0.03, 0.01, -5.28, 0.57, 1.77, -0.020] - base_wrench: [-1.0, -1.0, 0.0, -0.95, 2.1, 0.0] + base_wrench: [-0.3, 0.16, -7.92, 0.03, 1.35, 0.3] + # base_wrench: [-1.0, -1.0, 0.0, 0.0, 0.0, 0.0] auto_tune: #positional error threshold to begin autotuning @@ -123,13 +123,14 @@ controller: #[x,y,z,r,p,y] #first order eta - eta_order_0: [1.8, 0.2, 10.0, 0.2, 0.2, 0.3] + eta_order_0: [0.8, 0.3, 4.5, 0.2, 0.2, 0.3] + #second order eta - eta_order_1: [0.08, 0.04, 0.3, 0.0, 0.0, 0.0] + eta_order_1: [0.08, 0.06, 0.3, 0.0, 0.0, 0.0] #lambda - lambda: [0.25, 0.25, 1.0, 10.0, 10.0, 0.35] + lambda: [0.2, 0.2, 1.0, 10.0, 10.0, 0.35] #format: 'a, m' for each degree of freedom #where: @@ -152,9 +153,9 @@ controller: #linear unit are newtons per meter, angular are newton meter per radian #the default gains for normal operation - p_gains: [3.0, 18.0, 200.0, 3.0, 2.0, 1.0] - i_gains: [0.0, 0.0, 0.0, 0.01, 1.0, 0.0] - d_gains: [5.0, 5.0, 90.0, 1.5, 0.05, 0.9] + p_gains: [3.0, 18.0, 200.0, 2.0, 2.0, 25.0] + i_gains: [0.0, 0.0, 0.0, 0.05, 1.0, 1.0] + d_gains: [5.0, 5.0, 90.0, 0.1, 0.1, 2.0] # the control value thresholds for each DOF -- body frame max_control_thresholds: [30, 30, 30, 15, 15, 15] diff --git a/riptide_hardware/launch/zed.launch.py b/riptide_hardware/launch/zed.launch.py index 651e436..873cab1 100644 --- a/riptide_hardware/launch/zed.launch.py +++ b/riptide_hardware/launch/zed.launch.py @@ -165,18 +165,20 @@ def generate_launch_description(): # output='screen' # ) - # Node( - # package='riptide_hardware2', - # executable='picture_taker.py', - # name='picture_taker', - # output='screen', - # parameters=[ - # {"robot_namespace": LC("robot")}, - # {"camera_name": "ffc"}, - # {"save_stereo": True}, - # {"save_split": True} - # ] - # ) + # Used for taking pictures, good for camera calibration + Node( + package='riptide_hardware2', + executable='picture_taker.py', + name='picture_taker', + output='screen', + parameters=[ + {"robot_namespace": LC("robot")}, + {"camera_name": "ffc"}, + {"subscription_enabled": True}, + {"save_stereo": True}, + {"save_split": True} + ] + ) ], scoped=True), diff --git a/riptide_hardware/riptide_hardware2/picture_taker.py b/riptide_hardware/riptide_hardware2/picture_taker.py index d41ed80..904730a 100644 --- a/riptide_hardware/riptide_hardware2/picture_taker.py +++ b/riptide_hardware/riptide_hardware2/picture_taker.py @@ -18,6 +18,7 @@ def __init__(self): self.declare_parameter('robot_namespace', 'talos') self.declare_parameter('camera_name', 'ffc') self.declare_parameter('save_directory', '/home/ros/cal_images') + self.declare_parameter('subscription_enabled', True) self.declare_parameter('save_stereo', False) self.declare_parameter('save_split', True) @@ -25,6 +26,7 @@ def __init__(self): robot_namespace = self.get_parameter('robot_namespace').get_parameter_value().string_value camera_name = self.get_parameter('camera_name').get_parameter_value().string_value self.save_directory = self.get_parameter('save_directory').get_parameter_value().string_value + self.subscription_enabled = self.get_parameter('subscription_enabled').get_parameter_value().bool_value self.save_stereo = self.get_parameter('save_stereo').get_parameter_value().bool_value self.save_split = self.get_parameter('save_split').get_parameter_value().bool_value @@ -44,7 +46,6 @@ def __init__(self): # Store the latest image and subscription state self.latest_image = None - self.subscription_enabled = True self.image_subscriber = None # Log the topic we're subscribing to @@ -100,7 +101,7 @@ def capture_image_callback(self, request, response): try: # Generate filename with timestamp - timestamp = datetime.now().strftime("%Y%m%d_%H%M%S_%f")[:-3] + timestamp = datetime.now().strftime("%Y_%m_%d_%H_%M_%S_%f")[:-3] camera_name = self.get_parameter('camera_name').get_parameter_value().string_value saved_files = [] From 8e76197ceb2ad4eb2ee1a3c779ae982f20fecb02 Mon Sep 17 00:00:00 2001 From: osu_uwrt_bot Date: Sun, 2 Nov 2025 19:40:32 -0500 Subject: [PATCH 05/50] Updated picture taker to handle param changes --- riptide_hardware/launch/zed.launch.py | 2 +- .../riptide_hardware2/picture_taker.py | 81 +++++++++++++++---- 2 files changed, 67 insertions(+), 16 deletions(-) diff --git a/riptide_hardware/launch/zed.launch.py b/riptide_hardware/launch/zed.launch.py index 873cab1..d7c588a 100644 --- a/riptide_hardware/launch/zed.launch.py +++ b/riptide_hardware/launch/zed.launch.py @@ -175,7 +175,7 @@ def generate_launch_description(): {"robot_namespace": LC("robot")}, {"camera_name": "ffc"}, {"subscription_enabled": True}, - {"save_stereo": True}, + {"save_stereo": False}, {"save_split": True} ] ) diff --git a/riptide_hardware/riptide_hardware2/picture_taker.py b/riptide_hardware/riptide_hardware2/picture_taker.py index 904730a..fbb3762 100644 --- a/riptide_hardware/riptide_hardware2/picture_taker.py +++ b/riptide_hardware/riptide_hardware2/picture_taker.py @@ -4,6 +4,7 @@ from rclpy.node import Node from sensor_msgs.msg import Image from std_srvs.srv import Trigger, SetBool +from rcl_interfaces.msg import SetParametersResult from cv_bridge import CvBridge import cv2 import os @@ -21,25 +22,20 @@ def __init__(self): self.declare_parameter('subscription_enabled', True) self.declare_parameter('save_stereo', False) self.declare_parameter('save_split', True) + self.add_on_set_parameters_callback(self.on_param_change) # Get parameter values - robot_namespace = self.get_parameter('robot_namespace').get_parameter_value().string_value - camera_name = self.get_parameter('camera_name').get_parameter_value().string_value + self.robot_namespace = self.get_parameter('robot_namespace').get_parameter_value().string_value + self.camera_name = self.get_parameter('camera_name').get_parameter_value().string_value self.save_directory = self.get_parameter('save_directory').get_parameter_value().string_value self.subscription_enabled = self.get_parameter('subscription_enabled').get_parameter_value().bool_value self.save_stereo = self.get_parameter('save_stereo').get_parameter_value().bool_value self.save_split = self.get_parameter('save_split').get_parameter_value().bool_value # Build the full image topic path - self.image_topic = f"/{robot_namespace}/{camera_name}/zed_node/stereo_raw/image_raw_color" + self.image_topic = f"/{self.robot_namespace}/{self.camera_name}/zed_node/stereo_raw/image_raw_color" - # Create save directory if it doesn't exist - os.makedirs(self.save_directory, exist_ok=True) - - # Create subdirectories for split images if needed - if self.save_split: - os.makedirs(os.path.join(self.save_directory, 'left'), exist_ok=True) - os.makedirs(os.path.join(self.save_directory, 'right'), exist_ok=True) + self.create_save_dir() # Initialize CV bridge for image conversion self.bridge = CvBridge() @@ -52,7 +48,8 @@ def __init__(self): self.get_logger().info(f"Image topic: {self.image_topic}") # Create initial subscriber for image topic - self.create_image_subscriber() + if self.subscription_enabled: + self.create_image_subscriber() # Create service for capturing images self.capture_service = self.create_service( @@ -68,6 +65,60 @@ def __init__(self): self.enable_subscription_callback ) + def create_save_dir(self): + # Create save directory if it doesn't exist + os.makedirs(self.save_directory, exist_ok=True) + + # Create subdirectories for split images if needed + if self.save_split: + os.makedirs(os.path.join(self.save_directory, 'left'), exist_ok=True) + os.makedirs(os.path.join(self.save_directory, 'right'), exist_ok=True) + + def on_param_change(self, params): + changed = [] + for p in params: + if hasattr(self, p.name): + current = getattr(self, p.name) + if current != p.value: + setattr(self, p.name, p.value) + self.get_logger().info(f"Parameter '{p.name}' changed: {current} -> {p.value}") + changed.append(p) + + + self.handle_param_update(changed) + return SetParametersResult(successful=True) + + def handle_param_update(self, params): + topic_changed = update_save = update_sub = False + for p in params: + if p.name in ['camera_name', 'robot_namespace']: + topic_changed = True + elif p.name in ['save_directory', 'save_split']: + update_save = True + elif p.name == 'subscription_enabled': + update_sub = True + + if update_save: + self.create_save_dir() + + if topic_changed: + self.image_topic = f"/{self.robot_namespace}/{self.camera_name}/zed_node/stereo_raw/image_raw_color" + + if update_sub: + if self.subscription_enabled: + if self.image_subscriber is None: + self.create_image_subscriber() + else: + if topic_changed: + self.destroy_image_subscriber() + self.create_image_subscriber() + else: + self.destroy_image_subscriber() + else: + if topic_changed: + self.destroy_image_subscriber() + self.create_image_subscriber() + def create_image_subscriber(self): """Create the image subscriber""" if self.image_subscriber is None: @@ -101,7 +152,7 @@ def capture_image_callback(self, request, response): try: # Generate filename with timestamp - timestamp = datetime.now().strftime("%Y_%m_%d_%H_%M_%S_%f")[:-3] + timestamp = datetime.now().strftime("%Y_%m_%d__%H_%M_%S_%f")[:-3] camera_name = self.get_parameter('camera_name').get_parameter_value().string_value saved_files = [] @@ -113,7 +164,7 @@ def capture_image_callback(self, request, response): success = cv2.imwrite(stereo_filepath, self.latest_image, [cv2.IMWRITE_PNG_COMPRESSION, 0]) if success: - saved_files.append(f"stereo: {stereo_filename}") + saved_files.append(f"\nStereo: {stereo_filepath}") self.get_logger().info(f"Stereo image saved: {stereo_filepath}") else: self.get_logger().error("Failed to save stereo image") @@ -138,13 +189,13 @@ def capture_image_callback(self, request, response): right_success = cv2.imwrite(right_filepath, right_image, [cv2.IMWRITE_PNG_COMPRESSION, 0]) if left_success: - saved_files.append(f"left: {left_filename}") + saved_files.append(f"\nLeft: {left_filepath}") self.get_logger().info(f"Left image saved: {left_filepath}") else: self.get_logger().error("Failed to save left image") if right_success: - saved_files.append(f"right: {right_filename}") + saved_files.append(f"\nRight: {right_filepath}") self.get_logger().info(f"Right image saved: {right_filepath}") else: self.get_logger().error("Failed to save right image") From b709a6f537f925b9fa77f43de5842074b8b99eea Mon Sep 17 00:00:00 2001 From: osu_uwrt_bot Date: Thu, 6 Nov 2025 17:43:59 -0500 Subject: [PATCH 06/50] ZED X mini for ffc --- riptide_descriptions/config/talos.yaml | 2 +- riptide_hardware/cfg/dfc_config.yaml | 3 ++- riptide_hardware/cfg/ffc_config.yaml | 4 +++- riptide_hardware/launch/zed.launch.py | 4 +++- riptide_hardware/riptide_hardware2/picture_taker.py | 6 ++++++ 5 files changed, 15 insertions(+), 4 deletions(-) diff --git a/riptide_descriptions/config/talos.yaml b/riptide_descriptions/config/talos.yaml index 7e957c0..da54281 100644 --- a/riptide_descriptions/config/talos.yaml +++ b/riptide_descriptions/config/talos.yaml @@ -303,7 +303,7 @@ cameras: [ { type: zed, name: ffc, - baseline: 0.12, # m. Distance between cameras (manually measured) + baseline: 0.045, # m. Distance between cameras (manually measured) pose: [-0.0195, -0.0889, 0, 0, 0, 0] }, { diff --git a/riptide_hardware/cfg/dfc_config.yaml b/riptide_hardware/cfg/dfc_config.yaml index a42807b..ad56f65 100644 --- a/riptide_hardware/cfg/dfc_config.yaml +++ b/riptide_hardware/cfg/dfc_config.yaml @@ -1,8 +1,9 @@ /**: ros__parameters: general: - camera_name: "talos/dfc" + camera_model: zedxm serial_number: 51491740 + camera_name: "talos/dfc" grab_resolution: 'HD1200' # For zedx/zedxm use HD1200, for zed2i use HD2K optional_opencv_calibration_file: "/home/ros/zed_cals/zed_dfc_calibration2.yaml" pub_resolution: "NATIVE" # The resolution used for image and depth map publishing. 'NATIVE' to use the same `general.grab_resolution` - `CUSTOM` to apply the `general.pub_downscale_factor` downscale factory to reduce bandwidth in transmission diff --git a/riptide_hardware/cfg/ffc_config.yaml b/riptide_hardware/cfg/ffc_config.yaml index 84bab14..ddedfc2 100644 --- a/riptide_hardware/cfg/ffc_config.yaml +++ b/riptide_hardware/cfg/ffc_config.yaml @@ -1,11 +1,13 @@ /**: ros__parameters: general: + camera_model: zedxm + serial_number: 55348591 camera_name: "talos/ffc" pub_resolution: "NATIVE" # The resolution used for image and depth map publishing. 'NATIVE' to use the same `general.grab_resolution` - `CUSTOM` to apply the `general.pub_downscale_factor` downscale factory to reduce bandwidth in transmission pub_downscale_factor: 2.0 # rescale factor used to rescale image before publishing when 'pub_resolution' is 'CUSTOM' pub_frame_rate: 30.0 - optional_opencv_calibration_file: "/home/ros/zed_cals/taloszedxffc_cal7.yaml" + optional_opencv_calibration_file: "/home/ros/zed_cals/ffc_zedxm_calibration.yaml" grab_resolution: 'HD1200' self_calib: false pos_tracking: diff --git a/riptide_hardware/launch/zed.launch.py b/riptide_hardware/launch/zed.launch.py index d7c588a..8950de3 100644 --- a/riptide_hardware/launch/zed.launch.py +++ b/riptide_hardware/launch/zed.launch.py @@ -94,8 +94,9 @@ def generate_launch_description(): name="zed_node", parameters=[ zed_config_path, - zedx_camera_path, + zedxm_camera_path, ffc_config_path, + {'general.serial_number': 55348591} ] ), ], @@ -121,6 +122,7 @@ def generate_launch_description(): zed_config_path, zedxm_camera_path, dfc_config_path, + {'general.serial_number': 51491740} ] ), ], diff --git a/riptide_hardware/riptide_hardware2/picture_taker.py b/riptide_hardware/riptide_hardware2/picture_taker.py index fbb3762..7514780 100644 --- a/riptide_hardware/riptide_hardware2/picture_taker.py +++ b/riptide_hardware/riptide_hardware2/picture_taker.py @@ -151,12 +151,18 @@ def capture_image_callback(self, request, response): return response try: + + # if not self.save_stereo or self.save_split: + # return + # Generate filename with timestamp timestamp = datetime.now().strftime("%Y_%m_%d__%H_%M_%S_%f")[:-3] camera_name = self.get_parameter('camera_name').get_parameter_value().string_value saved_files = [] + self.create_save_dir() + # Save stereo image if enabled if self.save_stereo: stereo_filename = f"captured_image_{camera_name}_{timestamp}.png" From 794571277feab7de64fae8675b2952471eda19ba Mon Sep 17 00:00:00 2001 From: osu_uwrt_bot Date: Mon, 10 Nov 2025 22:36:35 -0500 Subject: [PATCH 07/50] ZEDXm FFC --- riptide_hardware/launch/navigation.launch.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/riptide_hardware/launch/navigation.launch.py b/riptide_hardware/launch/navigation.launch.py index 30f3c1c..fad72ce 100644 --- a/riptide_hardware/launch/navigation.launch.py +++ b/riptide_hardware/launch/navigation.launch.py @@ -87,7 +87,7 @@ def evaluate_xacro(context, *args, **kwargs): # try: if robot == "talos": - nodes.append(get_zed_description("ffc", "zedx", robot, context, debug)) + nodes.append(get_zed_description("ffc", "zedxm", robot, context, debug)) nodes.append(get_zed_description("dfc", "zedxm", robot, context, debug)) if robot == "liltank": From 1e55bffffc04c7ef625c889452ef9f59ce7c6f80 Mon Sep 17 00:00:00 2001 From: osu_uwrt_bot Date: Mon, 10 Nov 2025 22:36:49 -0500 Subject: [PATCH 08/50] Disable DFC for now --- riptide_hardware/launch/zed.launch.py | 50 +++++++++++++-------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/riptide_hardware/launch/zed.launch.py b/riptide_hardware/launch/zed.launch.py index 8950de3..a88b7b4 100644 --- a/riptide_hardware/launch/zed.launch.py +++ b/riptide_hardware/launch/zed.launch.py @@ -104,31 +104,31 @@ def generate_launch_description(): PythonExpression(["'", LC("robot"), "' == 'talos'"])) ), - ComposableNodeContainer( - name="dfc_container", - namespace="", - package="rclcpp_components", - executable="component_container", - output="screen", - respawn=True, - composable_node_descriptions=[ - # Second ZED Node (DFC) - ComposableNode( - package="zed_components", - plugin="stereolabs::ZedCamera", - namespace="dfc", - name="zed_node", - parameters=[ - zed_config_path, - zedxm_camera_path, - dfc_config_path, - {'general.serial_number': 51491740} - ] - ), - ], - condition=IfCondition( - PythonExpression(["'", LC("robot"), "' == 'talos'"])) - ), + # ComposableNodeContainer( + # name="dfc_container", + # namespace="", + # package="rclcpp_components", + # executable="component_container", + # output="screen", + # respawn=True, + # composable_node_descriptions=[ + # # Second ZED Node (DFC) + # ComposableNode( + # package="zed_components", + # plugin="stereolabs::ZedCamera", + # namespace="dfc", + # name="zed_node", + # parameters=[ + # zed_config_path, + # zedxm_camera_path, + # dfc_config_path, + # {'general.serial_number': 51491740} + # ] + # ), + # ], + # condition=IfCondition( + # PythonExpression(["'", LC("robot"), "' == 'talos'"])) + # ), # ComposableNodeContainer( # name="ffc_container", From 4920a2fd35cdbadf6cef4992d97bc36240ad7fcb Mon Sep 17 00:00:00 2001 From: osu_uwrt_bot Date: Fri, 21 Nov 2025 15:11:41 -0500 Subject: [PATCH 09/50] Slightly better tunes --- riptide_descriptions/config/talos.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/riptide_descriptions/config/talos.yaml b/riptide_descriptions/config/talos.yaml index da54281..213e4bd 100644 --- a/riptide_descriptions/config/talos.yaml +++ b/riptide_descriptions/config/talos.yaml @@ -153,9 +153,9 @@ controller: #linear unit are newtons per meter, angular are newton meter per radian #the default gains for normal operation - p_gains: [3.0, 18.0, 200.0, 2.0, 2.0, 25.0] + p_gains: [3.0, 18.0, 200.0, 2.0, 2.0, 30.0] i_gains: [0.0, 0.0, 0.0, 0.05, 1.0, 1.0] - d_gains: [5.0, 5.0, 90.0, 0.1, 0.1, 2.0] + d_gains: [5.0, 5.0, 90.0, 0.1, 0.1, 0.1] # the control value thresholds for each DOF -- body frame max_control_thresholds: [30, 30, 30, 15, 15, 15] From d3cd486ad469beacfa6d9ced3d6458479c780812 Mon Sep 17 00:00:00 2001 From: osu_uwrt_bot Date: Fri, 21 Nov 2025 15:13:13 -0500 Subject: [PATCH 10/50] ZEDXm cal --- riptide_hardware/cfg/ffc_config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/riptide_hardware/cfg/ffc_config.yaml b/riptide_hardware/cfg/ffc_config.yaml index ddedfc2..c160ca0 100644 --- a/riptide_hardware/cfg/ffc_config.yaml +++ b/riptide_hardware/cfg/ffc_config.yaml @@ -5,9 +5,9 @@ serial_number: 55348591 camera_name: "talos/ffc" pub_resolution: "NATIVE" # The resolution used for image and depth map publishing. 'NATIVE' to use the same `general.grab_resolution` - `CUSTOM` to apply the `general.pub_downscale_factor` downscale factory to reduce bandwidth in transmission - pub_downscale_factor: 2.0 # rescale factor used to rescale image before publishing when 'pub_resolution' is 'CUSTOM' + # pub_downscale_factor: 2.0 # rescale factor used to rescale image before publishing when 'pub_resolution' is 'CUSTOM' pub_frame_rate: 30.0 - optional_opencv_calibration_file: "/home/ros/zed_cals/ffc_zedxm_calibration.yaml" + optional_opencv_calibration_file: "/home/ros/zed_cals/zedxmffc_calibration.yaml" grab_resolution: 'HD1200' self_calib: false pos_tracking: From 3141c9df9b15bc15a8d6e59c7945ce5df5a13e46 Mon Sep 17 00:00:00 2001 From: osu_uwrt_bot Date: Sat, 22 Nov 2025 17:21:52 -0500 Subject: [PATCH 11/50] Stable tunes with good convergence + good FF --- riptide_descriptions/config/talos.yaml | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/riptide_descriptions/config/talos.yaml b/riptide_descriptions/config/talos.yaml index 213e4bd..4ecb3b8 100644 --- a/riptide_descriptions/config/talos.yaml +++ b/riptide_descriptions/config/talos.yaml @@ -24,7 +24,7 @@ controller: drag_enable: [1, 1, 0, 0, 0, 0] feed_forward: - base_wrench: [-0.3, 0.16, -7.92, 0.03, 1.35, 0.3] + base_wrench: [-0.6, -1.0, -7.50, 0.03, 1.35, -0.2] # base_wrench: [-1.0, -1.0, 0.0, 0.0, 0.0, 0.0] auto_tune: @@ -123,14 +123,14 @@ controller: #[x,y,z,r,p,y] #first order eta - eta_order_0: [0.8, 0.3, 4.5, 0.2, 0.2, 0.3] + eta_order_0: [0.8, 0.8, 0.5, 0.2, 0.2, 0.3] #second order eta - eta_order_1: [0.08, 0.06, 0.3, 0.0, 0.0, 0.0] + eta_order_1: [0.1, 0.2, 0.08, 0.0, 0.0, 0.0] #lambda - lambda: [0.2, 0.2, 1.0, 10.0, 10.0, 0.35] + lambda: [0.6, 0.6, 1.0, 10.0, 10.0, 0.35] #format: 'a, m' for each degree of freedom #where: @@ -153,10 +153,14 @@ controller: #linear unit are newtons per meter, angular are newton meter per radian #the default gains for normal operation - p_gains: [3.0, 18.0, 200.0, 2.0, 2.0, 30.0] - i_gains: [0.0, 0.0, 0.0, 0.05, 1.0, 1.0] - d_gains: [5.0, 5.0, 90.0, 0.1, 0.1, 0.1] - + p_gains: [3.0, 18.0, 200.0, 2.0, 1.0, 20.0] + i_gains: [0.0, 0.0, 0.0, 0.05, 0.0, 0.03] + d_gains: [5.0, 5.0, 90.0, 0.1, 0.2, 0.1] + + # #the default gains for normal operation + # p_gains: [3.0, 18.0, 200.0, 2.0, 3.5, 8.0] + # i_gains: [0.0, 0.0, 0.0, 0.0, 0.0, 2.0] + # d_gains: [5.0, 5.0, 90.0, 0.25, 0.25, 0.2] # the control value thresholds for each DOF -- body frame max_control_thresholds: [30, 30, 30, 15, 15, 15] reset_threshold: 0.5 From 105d057add28b49f13983e8edbfc819e43234ff1 Mon Sep 17 00:00:00 2001 From: Effo12345 <4p7septipuses@gmail.com> Date: Fri, 5 Dec 2025 19:50:04 -0500 Subject: [PATCH 12/50] Updated COM --- riptide_descriptions/config/talos.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/riptide_descriptions/config/talos.yaml b/riptide_descriptions/config/talos.yaml index 4ecb3b8..bc3bd82 100644 --- a/riptide_descriptions/config/talos.yaml +++ b/riptide_descriptions/config/talos.yaml @@ -6,7 +6,7 @@ base_link: [-0.14, 0.03, -0.09] # Vehicle mass properties mass: 31.998 -com: [-0.148, 0.040, -0.071] +com: [-0.151, 0.041, -0.044] inertia: [0.72604692, 1.59322252, 1.65134701] # kg*m^2 hull_volume: .11 #make this real From 03f2fa8a515b4f3df4ae0363225b3e096afc1c79 Mon Sep 17 00:00:00 2001 From: Effo12345 <4p7septipuses@gmail.com> Date: Sat, 6 Dec 2025 14:45:27 -0500 Subject: [PATCH 13/50] Cursed awful ff tune + controller tuning --- riptide_descriptions/config/talos.yaml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/riptide_descriptions/config/talos.yaml b/riptide_descriptions/config/talos.yaml index bc3bd82..3b5a112 100644 --- a/riptide_descriptions/config/talos.yaml +++ b/riptide_descriptions/config/talos.yaml @@ -24,7 +24,7 @@ controller: drag_enable: [1, 1, 0, 0, 0, 0] feed_forward: - base_wrench: [-0.6, -1.0, -7.50, 0.03, 1.35, -0.2] + base_wrench: [-1.5, -0.4, -8.41, 0.45, 2.1, -0.1] # base_wrench: [-1.0, -1.0, 0.0, 0.0, 0.0, 0.0] auto_tune: @@ -123,14 +123,14 @@ controller: #[x,y,z,r,p,y] #first order eta - eta_order_0: [0.8, 0.8, 0.5, 0.2, 0.2, 0.3] - + eta_order_0: [1.2, 1.5, 5.0, 0.2, 0.2, 1.3] #second order eta - eta_order_1: [0.1, 0.2, 0.08, 0.0, 0.0, 0.0] + eta_order_1: [0.05, 0.04, 0.3, 0.0, 0.0, 0.2] #lambda - lambda: [0.6, 0.6, 1.0, 10.0, 10.0, 0.35] + lambda: [1.2, 0.2, 1.0, 10.0, 10.0, 0.35] + #format: 'a, m' for each degree of freedom #where: @@ -138,7 +138,7 @@ controller: # m = gravity well saturation error velocity_curve_params: [8.0, 0.8, #x 8.0, 0.8, #y - 10.0, 0.5, #z + 8.0, 0.5, #z 15.0, 1.0, #r 15.0, 1.0, #p 1.5, 0.3] #yaw @@ -153,9 +153,9 @@ controller: #linear unit are newtons per meter, angular are newton meter per radian #the default gains for normal operation - p_gains: [3.0, 18.0, 200.0, 2.0, 1.0, 20.0] - i_gains: [0.0, 0.0, 0.0, 0.05, 0.0, 0.03] - d_gains: [5.0, 5.0, 90.0, 0.1, 0.2, 0.1] + p_gains: [3.0, 18.0, 200.0, 6.0, 0.5, 10.0] + i_gains: [0.0, 0.0, 0.0, 0.01, 0.01, 1.0] + d_gains: [5.0, 5.0, 90.0, 1.2, 0.01, 0.03] # #the default gains for normal operation # p_gains: [3.0, 18.0, 200.0, 2.0, 3.5, 8.0] From 39279c5d569ddeba9e95b2ae595ca0ab73e79832 Mon Sep 17 00:00:00 2001 From: Effo12345 <4p7septipuses@gmail.com> Date: Mon, 12 Jan 2026 20:50:05 -0500 Subject: [PATCH 14/50] Really good RPAC tunes with fixed angular accumulators --- riptide_descriptions/config/talos.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/riptide_descriptions/config/talos.yaml b/riptide_descriptions/config/talos.yaml index 3b5a112..03c4c8f 100644 --- a/riptide_descriptions/config/talos.yaml +++ b/riptide_descriptions/config/talos.yaml @@ -24,7 +24,7 @@ controller: drag_enable: [1, 1, 0, 0, 0, 0] feed_forward: - base_wrench: [-1.5, -0.4, -8.41, 0.45, 2.1, -0.1] + base_wrench: [-1.5, -0.4, -8.41, 0.45, 2.1, 0.0] # base_wrench: [-1.0, -1.0, 0.0, 0.0, 0.0, 0.0] auto_tune: @@ -123,13 +123,13 @@ controller: #[x,y,z,r,p,y] #first order eta - eta_order_0: [1.2, 1.5, 5.0, 0.2, 0.2, 1.3] + eta_order_0: [0.8, 1.5, 5.0, 0.2, 0.2, 1.3] #second order eta - eta_order_1: [0.05, 0.04, 0.3, 0.0, 0.0, 0.2] + eta_order_1: [0.1, 0.04, 0.3, 0.0, 0.0, 0.2] #lambda - lambda: [1.2, 0.2, 1.0, 10.0, 10.0, 0.35] + lambda: [0.6, 0.2, 1.0, 10.0, 10.0, 0.35] #format: 'a, m' for each degree of freedom From 026b8b0a974153baf0238e4b1475e0a86581d73c Mon Sep 17 00:00:00 2001 From: zehdari Date: Tue, 20 Jan 2026 18:46:52 -0500 Subject: [PATCH 15/50] Tune w/ acoustics, 5/6 torps rpac --- riptide_descriptions/config/talos.yaml | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/riptide_descriptions/config/talos.yaml b/riptide_descriptions/config/talos.yaml index 03c4c8f..984a56f 100644 --- a/riptide_descriptions/config/talos.yaml +++ b/riptide_descriptions/config/talos.yaml @@ -6,7 +6,11 @@ base_link: [-0.14, 0.03, -0.09] # Vehicle mass properties mass: 31.998 -com: [-0.151, 0.041, -0.044] +# COM no IVC +# com: [-0.151, 0.041, -0.044] + +# COM w/ IVC +com: [-0.157, 0.040, -0.048] inertia: [0.72604692, 1.59322252, 1.65134701] # kg*m^2 hull_volume: .11 #make this real @@ -24,7 +28,7 @@ controller: drag_enable: [1, 1, 0, 0, 0, 0] feed_forward: - base_wrench: [-1.5, -0.4, -8.41, 0.45, 2.1, 0.0] + base_wrench: [-1.5, -0.4, -4.01, 0.45, 2.4, 0.0] # base_wrench: [-1.0, -1.0, 0.0, 0.0, 0.0, 0.0] auto_tune: @@ -129,7 +133,7 @@ controller: eta_order_1: [0.1, 0.04, 0.3, 0.0, 0.0, 0.2] #lambda - lambda: [0.6, 0.2, 1.0, 10.0, 10.0, 0.35] + lambda: [0.8, 0.2, 1.0, 10.0, 10.0, 0.35] #format: 'a, m' for each degree of freedom @@ -153,9 +157,9 @@ controller: #linear unit are newtons per meter, angular are newton meter per radian #the default gains for normal operation - p_gains: [3.0, 18.0, 200.0, 6.0, 0.5, 10.0] - i_gains: [0.0, 0.0, 0.0, 0.01, 0.01, 1.0] - d_gains: [5.0, 5.0, 90.0, 1.2, 0.01, 0.03] + p_gains: [3.0, 18.0, 200.0, 10.0, 8.0, 15.0] + i_gains: [0.0, 0.0, 0.0, 0.01, 0.005, 0.5] + d_gains: [5.0, 5.0, 90.0, 0.02, 0.1, 0.02] # #the default gains for normal operation # p_gains: [3.0, 18.0, 200.0, 2.0, 3.5, 8.0] @@ -364,4 +368,4 @@ thrusters: [ type: 1, pose: [-0.134, 0.440, -0.244, 0.0, 0.0, 0.0] } -] +] \ No newline at end of file From b8b76947c0b95c123e127286472d96866d960726 Mon Sep 17 00:00:00 2001 From: osu-uwrt-bot2 Date: Sun, 8 Feb 2026 21:44:38 -0500 Subject: [PATCH 16/50] Fix logging level --- riptide_hardware/launch/apriltag.launch.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/riptide_hardware/launch/apriltag.launch.py b/riptide_hardware/launch/apriltag.launch.py index 41a034a..9b68174 100644 --- a/riptide_hardware/launch/apriltag.launch.py +++ b/riptide_hardware/launch/apriltag.launch.py @@ -25,7 +25,7 @@ def generate_launch_description(): namespace="apriltag", package='rclcpp_components', executable='component_container', - arguments=['--ros-args', '--log-level', 'error'], + arguments=['--ros-args', '--log-level', 'info'], composable_node_descriptions=[ ComposableNode( name='apriltag_36h11', From e120cbc26d7d4da698ead4c37d9433425dd0513c Mon Sep 17 00:00:00 2001 From: osu-uwrt-bot2 Date: Sun, 22 Feb 2026 23:06:59 -0500 Subject: [PATCH 17/50] FFC depth limit --- riptide_hardware/cfg/ffc_config.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/riptide_hardware/cfg/ffc_config.yaml b/riptide_hardware/cfg/ffc_config.yaml index c160ca0..a34abb3 100644 --- a/riptide_hardware/cfg/ffc_config.yaml +++ b/riptide_hardware/cfg/ffc_config.yaml @@ -10,6 +10,8 @@ optional_opencv_calibration_file: "/home/ros/zed_cals/zedxmffc_calibration.yaml" grab_resolution: 'HD1200' self_calib: false + depth: + max_depth: 4.0 pos_tracking: publish_tf: false debug: From 3bebfd47e5b7cf85e018aa8b9f232104bb1aa312 Mon Sep 17 00:00:00 2001 From: osu-uwrt-bot2 Date: Sun, 1 Mar 2026 19:40:17 -0500 Subject: [PATCH 18/50] Claw COM and frame --- riptide_descriptions/config/talos.yaml | 5 ++++- riptide_descriptions/urdf/robot_actuators.xacro | 8 ++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/riptide_descriptions/config/talos.yaml b/riptide_descriptions/config/talos.yaml index 984a56f..d865420 100644 --- a/riptide_descriptions/config/talos.yaml +++ b/riptide_descriptions/config/talos.yaml @@ -10,7 +10,7 @@ mass: 31.998 # com: [-0.151, 0.041, -0.044] # COM w/ IVC -com: [-0.157, 0.040, -0.048] +com: [-0.152, 0.038, -0.065] inertia: [0.72604692, 1.59322252, 1.65134701] # kg*m^2 hull_volume: .11 #make this real @@ -307,6 +307,9 @@ droppers: hitch: pose: [0.0, 0.0, 0.0, 0.0, 0.0, 0.0] +claw: + pose: [0.13335, 0.03175, -0.5588, 0.0, 0.0, 0.0] + cameras: [ { type: zed, diff --git a/riptide_descriptions/urdf/robot_actuators.xacro b/riptide_descriptions/urdf/robot_actuators.xacro index 75d6b92..010ba49 100644 --- a/riptide_descriptions/urdf/robot_actuators.xacro +++ b/riptide_descriptions/urdf/robot_actuators.xacro @@ -106,4 +106,12 @@ parent="${namespace}/origin" pose="${config['hitch']['pose']}" /> + + + + From b905e3e5ad9f74c21e74e95841d317a96a25308e Mon Sep 17 00:00:00 2001 From: osu-uwrt-bot2 Date: Sun, 1 Mar 2026 19:40:45 -0500 Subject: [PATCH 19/50] Updated zed launches --- riptide_hardware/cfg/dfc_config.yaml | 1 + riptide_hardware/cfg/ffc_config.yaml | 1 + riptide_hardware/launch/zed.launch.py | 177 ++++---------------------- 3 files changed, 29 insertions(+), 150 deletions(-) diff --git a/riptide_hardware/cfg/dfc_config.yaml b/riptide_hardware/cfg/dfc_config.yaml index ad56f65..fc846f2 100644 --- a/riptide_hardware/cfg/dfc_config.yaml +++ b/riptide_hardware/cfg/dfc_config.yaml @@ -3,6 +3,7 @@ general: camera_model: zedxm serial_number: 51491740 + camera_id: 1 camera_name: "talos/dfc" grab_resolution: 'HD1200' # For zedx/zedxm use HD1200, for zed2i use HD2K optional_opencv_calibration_file: "/home/ros/zed_cals/zed_dfc_calibration2.yaml" diff --git a/riptide_hardware/cfg/ffc_config.yaml b/riptide_hardware/cfg/ffc_config.yaml index a34abb3..4fedb9e 100644 --- a/riptide_hardware/cfg/ffc_config.yaml +++ b/riptide_hardware/cfg/ffc_config.yaml @@ -3,6 +3,7 @@ general: camera_model: zedxm serial_number: 55348591 + camera_id: 0 camera_name: "talos/ffc" pub_resolution: "NATIVE" # The resolution used for image and depth map publishing. 'NATIVE' to use the same `general.grab_resolution` - `CUSTOM` to apply the `general.pub_downscale_factor` downscale factory to reduce bandwidth in transmission # pub_downscale_factor: 2.0 # rescale factor used to rescale image before publishing when 'pub_resolution' is 'CUSTOM' diff --git a/riptide_hardware/launch/zed.launch.py b/riptide_hardware/launch/zed.launch.py index a88b7b4..3cd2ad0 100644 --- a/riptide_hardware/launch/zed.launch.py +++ b/riptide_hardware/launch/zed.launch.py @@ -1,173 +1,53 @@ from launch import LaunchDescription -from launch.actions import DeclareLaunchArgument, GroupAction, Shutdown +from launch.actions import DeclareLaunchArgument, GroupAction from launch_ros.actions import PushRosNamespace, ComposableNodeContainer, Node from launch_ros.descriptions import ComposableNode -from launch.conditions import IfCondition -from launch.substitutions import PythonExpression, LaunchConfiguration as LC +from launch.substitutions import LaunchConfiguration as LC from ament_index_python import get_package_share_directory import os - - def generate_launch_description(): + zed_wrapper_share = get_package_share_directory("zed_wrapper") + riptide_share = get_package_share_directory("riptide_hardware2") - # Define common configuration path - zed_config_path = os.path.join( - get_package_share_directory('zed_wrapper'), - "config", - "common_stereo.yaml" - ) - + zed_common = os.path.join(zed_wrapper_share, "config", "common_stereo.yaml") + zedxm = os.path.join(zed_wrapper_share, "config", "zedxm.yaml") - # Paths for individual camera configurations + ffc_cfg = os.path.join(riptide_share, "cfg", "ffc_config.yaml") + dfc_cfg = os.path.join(riptide_share, "cfg", "dfc_config.yaml") - # FFC Zed 2i - zed2i_camera_path = os.path.join( - get_package_share_directory('zed_wrapper'), - 'config', - 'zed2i.yaml' - ) - - # FFC Zed X - zedx_camera_path = os.path.join( - get_package_share_directory('zed_wrapper'), - 'config', - 'zedx.yaml' - ) - - # DFC Zed X Mini - zedxm_camera_path = os.path.join( - get_package_share_directory('zed_wrapper'), - 'config', - 'zedxm.yaml' - ) - - # FFC Overrides - ffc_config_path = os.path.join( - get_package_share_directory('riptide_hardware2'), - 'cfg', - 'ffc_config.yaml' + ffc_node = ComposableNode( + package="zed_components", + plugin="stereolabs::ZedCamera", + namespace="ffc", + name="zed_node", + parameters=[zed_common, zedxm, ffc_cfg], ) - # DFC Overrides - dfc_config_path = os.path.join( - get_package_share_directory('riptide_hardware2'), - 'cfg', - 'dfc_config.yaml' + dfc_node = ComposableNode( + package="zed_components", + plugin="stereolabs::ZedCamera", + namespace="dfc", + name="zed_node", + parameters=[zed_common, zedxm, dfc_cfg], ) - - tank_config_path = os.path.join( - get_package_share_directory('riptide_hardware2'), - 'cfg', - 'tank_ffc_config.yaml' - ) - - zed_compression_path = os.path.join( - get_package_share_directory('riptide_hardware2'), - 'cfg', - 'zed_compression.yaml' - ) - + return LaunchDescription([ - DeclareLaunchArgument( - name="robot", - default_value="tempest", - description="name of the robot" - ), - # Group actions under the robot namespace + DeclareLaunchArgument("robot", default_value="tempest"), + GroupAction([ PushRosNamespace(LC("robot")), ComposableNodeContainer( - name="ffc_container", + name="zed_container", namespace="", package="rclcpp_components", executable="component_container", output="screen", - respawn=True, - composable_node_descriptions=[ - # First ZED Node (FFC) - ComposableNode( - package="zed_components", - plugin="stereolabs::ZedCamera", - namespace="ffc", - name="zed_node", - parameters=[ - zed_config_path, - zedxm_camera_path, - ffc_config_path, - {'general.serial_number': 55348591} - ] - ), - ], - condition=IfCondition( - PythonExpression(["'", LC("robot"), "' == 'talos'"])) + respawn=False, + composable_node_descriptions=[ffc_node, dfc_node], ), - - # ComposableNodeContainer( - # name="dfc_container", - # namespace="", - # package="rclcpp_components", - # executable="component_container", - # output="screen", - # respawn=True, - # composable_node_descriptions=[ - # # Second ZED Node (DFC) - # ComposableNode( - # package="zed_components", - # plugin="stereolabs::ZedCamera", - # namespace="dfc", - # name="zed_node", - # parameters=[ - # zed_config_path, - # zedxm_camera_path, - # dfc_config_path, - # {'general.serial_number': 51491740} - # ] - # ), - # ], - # condition=IfCondition( - # PythonExpression(["'", LC("robot"), "' == 'talos'"])) - # ), - - # ComposableNodeContainer( - # name="ffc_container", - # namespace="/", - # package="rclcpp_components", - # executable="component_container", - # output="screen", - # respawn=True, - # composable_node_descriptions=[ - # # The tank camera - # ComposableNode( - # package="zed_components", - # plugin="stereolabs::ZedCamera", - # namespace="ffc", - # name="zed_node", - # parameters=[ - # # zedxm_camera_path, - # zed_config_path, - # tank_config_path, - # # zed_compression_path, - # {"general.camera_name": "liltank/ffc"}, - # {"mapping.clicked_point_topic": "/clicked_point"}, - # ] - # ), - # ], - # condition=IfCondition( - # PythonExpression(["'", LC("robot"), "' == 'liltank'"])) - # ), - - # Disabled for now as both are on by default - # # Launch the ZedManager node - # Node( - # package='riptide_hardware2', - # executable='zed_manager.py', - # name='ZedManager', - # output='screen' - # ) - - # Used for taking pictures, good for camera calibration + Node( package='riptide_hardware2', executable='picture_taker.py', @@ -181,8 +61,5 @@ def generate_launch_description(): {"save_split": True} ] ) - ], scoped=True), - - ]) \ No newline at end of file From 7b61ef8abc79879fafbad8ac563225af973a170f Mon Sep 17 00:00:00 2001 From: osu-uwrt-bot2 Date: Sun, 29 Mar 2026 18:45:48 -0400 Subject: [PATCH 20/50] Revert to pre-claw COM --- riptide_descriptions/config/talos.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/riptide_descriptions/config/talos.yaml b/riptide_descriptions/config/talos.yaml index d865420..94fa740 100644 --- a/riptide_descriptions/config/talos.yaml +++ b/riptide_descriptions/config/talos.yaml @@ -10,7 +10,7 @@ mass: 31.998 # com: [-0.151, 0.041, -0.044] # COM w/ IVC -com: [-0.152, 0.038, -0.065] +com: [-0.157, 0.040, -0.048] inertia: [0.72604692, 1.59322252, 1.65134701] # kg*m^2 hull_volume: .11 #make this real From 37eb9006ddf5f6ca8c68273912c1f32e31695385 Mon Sep 17 00:00:00 2001 From: osu-uwrt-bot2 Date: Mon, 13 Apr 2026 14:28:10 -0400 Subject: [PATCH 21/50] Charuco board cal --- riptide_hardware/cfg/ffc_config.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/riptide_hardware/cfg/ffc_config.yaml b/riptide_hardware/cfg/ffc_config.yaml index 4fedb9e..d737a6e 100644 --- a/riptide_hardware/cfg/ffc_config.yaml +++ b/riptide_hardware/cfg/ffc_config.yaml @@ -8,9 +8,11 @@ pub_resolution: "NATIVE" # The resolution used for image and depth map publishing. 'NATIVE' to use the same `general.grab_resolution` - `CUSTOM` to apply the `general.pub_downscale_factor` downscale factory to reduce bandwidth in transmission # pub_downscale_factor: 2.0 # rescale factor used to rescale image before publishing when 'pub_resolution' is 'CUSTOM' pub_frame_rate: 30.0 - optional_opencv_calibration_file: "/home/ros/zed_cals/zedxmffc_calibration.yaml" + optional_opencv_calibration_file: "/home/ros/zed_cals/charuco_ffc_zedxm_cal.yaml" grab_resolution: 'HD1200' self_calib: false + # svo: + # svo_path: "/home/ros/svos/prequal/prequal1.svo2" depth: max_depth: 4.0 pos_tracking: From 1af94cb21c2f8a23f7d1eb1a603440710bc21a9b Mon Sep 17 00:00:00 2001 From: Effo12345 <4p7septipuses@gmail.com> Date: Mon, 13 Apr 2026 13:45:20 -0400 Subject: [PATCH 22/50] SetString service --- riptide_msgs/srv/SetString.srv | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 riptide_msgs/srv/SetString.srv diff --git a/riptide_msgs/srv/SetString.srv b/riptide_msgs/srv/SetString.srv new file mode 100644 index 0000000..7377b4c --- /dev/null +++ b/riptide_msgs/srv/SetString.srv @@ -0,0 +1,4 @@ +string data +--- +bool success +string message \ No newline at end of file From 931bc74240b9c5a36e3ff924d19b64d16f1c2249 Mon Sep 17 00:00:00 2001 From: zehdari Date: Sun, 7 Jun 2026 13:43:42 -0400 Subject: [PATCH 23/50] add StartBinaryClassifier srv --- riptide_msgs/srv/StartBinaryClassifier.srv | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 riptide_msgs/srv/StartBinaryClassifier.srv diff --git a/riptide_msgs/srv/StartBinaryClassifier.srv b/riptide_msgs/srv/StartBinaryClassifier.srv new file mode 100644 index 0000000..27c175b --- /dev/null +++ b/riptide_msgs/srv/StartBinaryClassifier.srv @@ -0,0 +1,6 @@ +string class_name +string frame1_name +string frame2_name +--- +bool success +string message \ No newline at end of file From aa39af2c8dbc7ebd592cb1871de12efa822c91f6 Mon Sep 17 00:00:00 2001 From: Effo12345 <4p7septipuses@gmail.com> Date: Thu, 11 Jun 2026 23:18:40 -0400 Subject: [PATCH 24/50] Fix classifier param names --- riptide_msgs/srv/StartBinaryClassifier.srv | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/riptide_msgs/srv/StartBinaryClassifier.srv b/riptide_msgs/srv/StartBinaryClassifier.srv index 27c175b..0eb09cf 100644 --- a/riptide_msgs/srv/StartBinaryClassifier.srv +++ b/riptide_msgs/srv/StartBinaryClassifier.srv @@ -1,6 +1,6 @@ string class_name -string frame1_name -string frame2_name +string object1_name +string object2_name --- bool success string message \ No newline at end of file From 589f9a1174b1a5a8419241feebb8b82f7380a295 Mon Sep 17 00:00:00 2001 From: osu-uwrt-bot2 Date: Sun, 14 Jun 2026 22:19:14 -0400 Subject: [PATCH 25/50] Tirpak 1 claw frame --- riptide_descriptions/config/talos.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/riptide_descriptions/config/talos.yaml b/riptide_descriptions/config/talos.yaml index 94fa740..a453ffd 100644 --- a/riptide_descriptions/config/talos.yaml +++ b/riptide_descriptions/config/talos.yaml @@ -308,7 +308,7 @@ hitch: pose: [0.0, 0.0, 0.0, 0.0, 0.0, 0.0] claw: - pose: [0.13335, 0.03175, -0.5588, 0.0, 0.0, 0.0] + pose: [0.19, 0.0655, -0.3712, 0.0, 0.0, 0.0] cameras: [ { From 36695d3ed1ccc3dc7b4b6335d8d0fd80ef6d3450 Mon Sep 17 00:00:00 2001 From: osu-uwrt-bot2 Date: Sun, 14 Jun 2026 22:19:24 -0400 Subject: [PATCH 26/50] Tirpak 1 DFC cal --- riptide_hardware/cfg/dfc_config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/riptide_hardware/cfg/dfc_config.yaml b/riptide_hardware/cfg/dfc_config.yaml index fc846f2..a7a4d47 100644 --- a/riptide_hardware/cfg/dfc_config.yaml +++ b/riptide_hardware/cfg/dfc_config.yaml @@ -6,7 +6,7 @@ camera_id: 1 camera_name: "talos/dfc" grab_resolution: 'HD1200' # For zedx/zedxm use HD1200, for zed2i use HD2K - optional_opencv_calibration_file: "/home/ros/zed_cals/zed_dfc_calibration2.yaml" + optional_opencv_calibration_file: "/home/ros/zed_cals/dfc_charuco_cal.yaml" pub_resolution: "NATIVE" # The resolution used for image and depth map publishing. 'NATIVE' to use the same `general.grab_resolution` - `CUSTOM` to apply the `general.pub_downscale_factor` downscale factory to reduce bandwidth in transmission pub_downscale_factor: 2.0 # rescale factor used to rescale image before publishing when 'pub_resolution' is 'CUSTOM' pub_frame_rate: 30.0 From a289d0407571b60f36accac94db6266eac5eebbf Mon Sep 17 00:00:00 2001 From: Effo12345 <4p7septipuses@gmail.com> Date: Sun, 14 Jun 2026 22:19:34 -0400 Subject: [PATCH 27/50] Fix feedforward --- riptide_descriptions/config/talos.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/riptide_descriptions/config/talos.yaml b/riptide_descriptions/config/talos.yaml index 94fa740..725b70c 100644 --- a/riptide_descriptions/config/talos.yaml +++ b/riptide_descriptions/config/talos.yaml @@ -28,7 +28,7 @@ controller: drag_enable: [1, 1, 0, 0, 0, 0] feed_forward: - base_wrench: [-1.5, -0.4, -4.01, 0.45, 2.4, 0.0] + base_wrench: [-1.5, -0.4, 0.0, 0.45, 2.4, 0.0] # base_wrench: [-1.0, -1.0, 0.0, 0.0, 0.0, 0.0] auto_tune: @@ -302,7 +302,7 @@ torpedoes: baseline: 0.0365125 droppers: - pose: [0.048, 0.17200625, -0.1501, 0.0, 0.0, 0.0] + pose: [0.0, 0.17200625, -0.1501, 0.0, 0.0, 0.0] hitch: pose: [0.0, 0.0, 0.0, 0.0, 0.0, 0.0] From a989ec68ccb664a62c6bfc495d8c5d435538263c Mon Sep 17 00:00:00 2001 From: Effo12345 <4p7septipuses@gmail.com> Date: Sun, 14 Jun 2026 22:19:48 -0400 Subject: [PATCH 28/50] Take z from the IMU --- riptide_hardware/cfg/talos_ekf.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/riptide_hardware/cfg/talos_ekf.yaml b/riptide_hardware/cfg/talos_ekf.yaml index 9e80c86..8466bfb 100644 --- a/riptide_hardware/cfg/talos_ekf.yaml +++ b/riptide_hardware/cfg/talos_ekf.yaml @@ -9,7 +9,7 @@ publish_acceleration: true #make camera happy - # predict_to_current_time: true + predict_to_current_time: true # transform_time_offset: -0.1 @@ -36,7 +36,7 @@ true, true, false, false, false, false, true, true, false, # Taking out yaw from imu since fog rate is lower now - true, true, true] + true, true, false] # Imu comes in faster than the ekf runs so we need a queue imu0_queue_size: 14 From 175595fbfa31d9538d42e0f41bd93e03b832faa7 Mon Sep 17 00:00:00 2001 From: osu-uwrt-bot2 Date: Sun, 28 Jun 2026 09:03:31 -0400 Subject: [PATCH 29/50] Need IMU in the Z since depth is too slow --- riptide_hardware/cfg/talos_ekf.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/riptide_hardware/cfg/talos_ekf.yaml b/riptide_hardware/cfg/talos_ekf.yaml index 8466bfb..90c2bc4 100644 --- a/riptide_hardware/cfg/talos_ekf.yaml +++ b/riptide_hardware/cfg/talos_ekf.yaml @@ -36,7 +36,7 @@ true, true, false, false, false, false, true, true, false, # Taking out yaw from imu since fog rate is lower now - true, true, false] + true, true, true] # Imu comes in faster than the ekf runs so we need a queue imu0_queue_size: 14 From 32e237f11084d9d4357eadb9b1985650fb953176 Mon Sep 17 00:00:00 2001 From: osu-uwrt-bot2 Date: Sun, 28 Jun 2026 09:03:56 -0400 Subject: [PATCH 30/50] Claw ff and ungaslight the torps --- riptide_descriptions/config/talos.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/riptide_descriptions/config/talos.yaml b/riptide_descriptions/config/talos.yaml index 9f512d3..e6cf37c 100644 --- a/riptide_descriptions/config/talos.yaml +++ b/riptide_descriptions/config/talos.yaml @@ -28,7 +28,7 @@ controller: drag_enable: [1, 1, 0, 0, 0, 0] feed_forward: - base_wrench: [-1.5, -0.4, 0.0, 0.45, 2.4, 0.0] + base_wrench: [-0.25, -0.1, -3.0, 0.0, 1.4, -0.005] # base_wrench: [-1.0, -1.0, 0.0, 0.0, 0.0, 0.0] auto_tune: @@ -298,7 +298,7 @@ poker: pose: [0.2286, 0.0127, -0.17145, 0, 0, 0] torpedoes: - pose: [0.048, 0.17200625, -0.0954, 0.0, 0.0, 0.0] #real z: -0.1524, real y: 0.17700625 + pose: [0.048, 0.17200625, -0.1524, 0.0, 0.0, 0.0] #real z: -0.1524, real y: 0.17700625 baseline: 0.0365125 droppers: From 722a8b50c8a71231a7d00d6fdea4b8eff8c98ed4 Mon Sep 17 00:00:00 2001 From: osu-uwrt-bot2 Date: Sun, 28 Jun 2026 09:04:23 -0400 Subject: [PATCH 31/50] Reduce orin load --- riptide_hardware/launch/diagnostics.launch.py | 2 +- riptide_hardware/launch/hardware.launch.py | 18 ----------------- .../launch/hardware_fake_dvl.launch.py | 20 +------------------ 3 files changed, 2 insertions(+), 38 deletions(-) diff --git a/riptide_hardware/launch/diagnostics.launch.py b/riptide_hardware/launch/diagnostics.launch.py index 3fc03d3..46a9b2a 100644 --- a/riptide_hardware/launch/diagnostics.launch.py +++ b/riptide_hardware/launch/diagnostics.launch.py @@ -77,7 +77,7 @@ def generate_launch_description(): electrical_monitor_node, firmware_monitor_node, voltage_monitor_node, - sensor_monitor_node, + # sensor_monitor_node, computer_monitor_node, aggregator ]) \ No newline at end of file diff --git a/riptide_hardware/launch/hardware.launch.py b/riptide_hardware/launch/hardware.launch.py index 58d6106..b656b9c 100644 --- a/riptide_hardware/launch/hardware.launch.py +++ b/riptide_hardware/launch/hardware.launch.py @@ -15,11 +15,6 @@ "launch", "copro_agent.launch.py", ) -diagnostics_launch_file = os.path.join( - get_package_share_directory('riptide_hardware2'), - "launch", "diagnostics.launch.py" -) - dvl_launch_file = os.path.join( get_package_share_directory('riptide_hardware2'), "launch", "dvl.launch.py" @@ -55,12 +50,6 @@ def generate_launch_description(): ('robot', LC('robot')), ] ), - IncludeLaunchDescription( - AnyLaunchDescriptionSource(diagnostics_launch_file), - launch_arguments=[ - ('robot', LC('robot')), - ] - ), IncludeLaunchDescription( AnyLaunchDescriptionSource(dvl_launch_file), launch_arguments=[ @@ -110,13 +99,6 @@ def generate_launch_description(): "imu_pwr_cycle.yaml" ) ] - ), - Node( - package='riptide_hardware2', - executable='pressure_monitor.py', - name='pressure_monitor', - output='screen', - parameters=[{"robot":LC('robot')}] ) ], scoped=True) ]) diff --git a/riptide_hardware/launch/hardware_fake_dvl.launch.py b/riptide_hardware/launch/hardware_fake_dvl.launch.py index 408e9d0..60f147e 100644 --- a/riptide_hardware/launch/hardware_fake_dvl.launch.py +++ b/riptide_hardware/launch/hardware_fake_dvl.launch.py @@ -15,11 +15,6 @@ "launch", "copro_agent.launch.py", ) -diagnostics_launch_file = os.path.join( - get_package_share_directory('riptide_hardware2'), - "launch", "diagnostics.launch.py" -) - imu_launch_file = os.path.join( get_package_share_directory('riptide_imu'), "launch", "imu.launch.py" @@ -62,12 +57,6 @@ def generate_launch_description(): ('robot', LC('robot')), ] ), - IncludeLaunchDescription( - AnyLaunchDescriptionSource(diagnostics_launch_file), - launch_arguments=[ - ('robot', LC('robot')), - ] - ), Node( package='riptide_hardware2', executable='fake_dvl.py', @@ -112,13 +101,6 @@ def generate_launch_description(): executable='imu_power_cycle.py', name='imu_power_cycle', output='screen', - ), - Node( - package='riptide_hardware2', - executable='pressure_monitor.py', - name='pressure_monitor', - output='screen', - parameters=[{"robot":LC('robot')}] - ) + ) ], scoped=True) ]) From bb6822abecb26cb8aba29d1aea5b6b8984da3b39 Mon Sep 17 00:00:00 2001 From: osu-uwrt-bot2 Date: Sun, 28 Jun 2026 09:04:37 -0400 Subject: [PATCH 32/50] Actually use firmware stamp --- riptide_hardware/riptide_hardware2/depth_converter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/riptide_hardware/riptide_hardware2/depth_converter.py b/riptide_hardware/riptide_hardware2/depth_converter.py index 1de7402..42147fc 100755 --- a/riptide_hardware/riptide_hardware2/depth_converter.py +++ b/riptide_hardware/riptide_hardware2/depth_converter.py @@ -51,7 +51,7 @@ def depthCb(self, msg): outMsg.header.frame_id = "odom" outMsg.pose.pose.position.z = msg.depth + addedDepth outMsg.pose.covariance[14] = msg.variance - outMsg.header.stamp = self.get_clock().now().to_msg() + outMsg.header.stamp = msg.header.stamp self.pub.publish(outMsg) From 9a249b2de405eb23184d265d0199ccc0695dd71b Mon Sep 17 00:00:00 2001 From: osu-uwrt-bot2 Date: Sun, 28 Jun 2026 21:29:52 -0400 Subject: [PATCH 33/50] Working pose and resolution --- riptide_descriptions/config/talos.yaml | 9 +++++---- riptide_hardware/cfg/dfc_config.yaml | 9 +++++---- riptide_hardware/cfg/ffc_config.yaml | 10 +++++++--- 3 files changed, 17 insertions(+), 11 deletions(-) diff --git a/riptide_descriptions/config/talos.yaml b/riptide_descriptions/config/talos.yaml index e6cf37c..6380591 100644 --- a/riptide_descriptions/config/talos.yaml +++ b/riptide_descriptions/config/talos.yaml @@ -298,7 +298,7 @@ poker: pose: [0.2286, 0.0127, -0.17145, 0, 0, 0] torpedoes: - pose: [0.048, 0.17200625, -0.1524, 0.0, 0.0, 0.0] #real z: -0.1524, real y: 0.17700625 + pose: [0.048, 0.18200625, -0.1524, 0.0, 0.0, 0.0] #real z: -0.1524, real y: 0.17700625 baseline: 0.0365125 droppers: @@ -314,14 +314,15 @@ cameras: [ { type: zed, name: ffc, - baseline: 0.045, # m. Distance between cameras (manually measured) - pose: [-0.0195, -0.0889, 0, 0, 0, 0] + baseline: 0.050, # m. Distance between cameras (manually measured) + pose: [-0.0195, -0.0889, 0.02, -0.01, 0.05, 0] }, { type: zed, name: dfc, baseline: 0.045, - pose: [-0.145, -0.080, -0.08, 1.5707, 1.5707, 0] + # pose: [-0.145, -0.080, -0.08, 1.5707, 1.5707, 0] + pose: [-0.24347, -0.065568, -0.15943, 1.5507, 1.5107, 0] } ] diff --git a/riptide_hardware/cfg/dfc_config.yaml b/riptide_hardware/cfg/dfc_config.yaml index a7a4d47..1502e50 100644 --- a/riptide_hardware/cfg/dfc_config.yaml +++ b/riptide_hardware/cfg/dfc_config.yaml @@ -5,11 +5,12 @@ serial_number: 51491740 camera_id: 1 camera_name: "talos/dfc" - grab_resolution: 'HD1200' # For zedx/zedxm use HD1200, for zed2i use HD2K - optional_opencv_calibration_file: "/home/ros/zed_cals/dfc_charuco_cal.yaml" + grab_resolution: "HD1200" # For zedx/zedxm use HD1200, for zed2i use HD2K + optional_opencv_calibration_file: "/home/ros/zed_cals/dfc_charuco_test.yaml" + self_calib: false pub_resolution: "NATIVE" # The resolution used for image and depth map publishing. 'NATIVE' to use the same `general.grab_resolution` - `CUSTOM` to apply the `general.pub_downscale_factor` downscale factory to reduce bandwidth in transmission - pub_downscale_factor: 2.0 # rescale factor used to rescale image before publishing when 'pub_resolution' is 'CUSTOM' - pub_frame_rate: 30.0 + #pub_downscale_factor: 2.0 # rescale factor used to rescale image before publishing when 'pub_resolution' is 'CUSTOM' + grab_frame_rate: 15 pos_tracking: publish_tf: false debug: diff --git a/riptide_hardware/cfg/ffc_config.yaml b/riptide_hardware/cfg/ffc_config.yaml index d737a6e..748cade 100644 --- a/riptide_hardware/cfg/ffc_config.yaml +++ b/riptide_hardware/cfg/ffc_config.yaml @@ -6,14 +6,18 @@ camera_id: 0 camera_name: "talos/ffc" pub_resolution: "NATIVE" # The resolution used for image and depth map publishing. 'NATIVE' to use the same `general.grab_resolution` - `CUSTOM` to apply the `general.pub_downscale_factor` downscale factory to reduce bandwidth in transmission - # pub_downscale_factor: 2.0 # rescale factor used to rescale image before publishing when 'pub_resolution' is 'CUSTOM' - pub_frame_rate: 30.0 - optional_opencv_calibration_file: "/home/ros/zed_cals/charuco_ffc_zedxm_cal.yaml" + pub_downscale_factor: 2.0 # rescale factor used to rescale image before publishing when 'pub_resolution' is 'CUSTOM' + # pub_frame_rate: 30.0 + optional_opencv_calibration_file: "/home/ros/zed_cals/tirpak_ffc_charuco_2_tan.yaml" grab_resolution: 'HD1200' + grab_frame_rate: 15 self_calib: false + sensors: + sensors_image_sync: true # svo: # svo_path: "/home/ros/svos/prequal/prequal1.svo2" depth: + depth_mode: "NEURAL_LIGHT" max_depth: 4.0 pos_tracking: publish_tf: false From b24fd9e472d5f2507ea9c646b534659b148c91ef Mon Sep 17 00:00:00 2001 From: osu-uwrt-bot2 Date: Fri, 10 Jul 2026 05:25:37 -0400 Subject: [PATCH 34/50] Good night tune --- riptide_descriptions/config/talos.yaml | 66 ++++++++++++++++++++++---- 1 file changed, 56 insertions(+), 10 deletions(-) diff --git a/riptide_descriptions/config/talos.yaml b/riptide_descriptions/config/talos.yaml index 6380591..fae8dbe 100644 --- a/riptide_descriptions/config/talos.yaml +++ b/riptide_descriptions/config/talos.yaml @@ -23,12 +23,12 @@ controller_overseer: controller: scaling_factor: 1000000 - active_force_mask: [2, 2, 2, 1, 1, 1] + active_force_mask: [2, 2, 2, 1, 1, 2] drag_enable: [1, 1, 0, 0, 0, 0] feed_forward: - base_wrench: [-0.25, -0.1, -3.0, 0.0, 1.4, -0.005] + base_wrench: [-0.2, -0.1, -3.00, 0.25, 1.875, 0.2] # base_wrench: [-1.0, -1.0, 0.0, 0.0, 0.0, 0.0] auto_tune: @@ -126,15 +126,45 @@ controller: SMC_params: #[x,y,z,r,p,y] + # cameron tune + # #first order eta + # eta_order_0: [0.8, 1.5, 4.0, 0.2, 0.2, 1.3] + + # #second order eta + # eta_order_1: [0.1, 0.04, 0.07, 0.0, 0.0, 0.2] + + # #lambda + # lambda: [0.6, 0.2, 1.0, 10.0, 10.0, 0.35] + + # ethan tune 1 + # # eta_order_0: [0.8, 0.8, 10.0, 0.2, 0.2, 0.3] + # eta_order_0: [0.8, 1.0, 7.0, 0.2, 0.2, 0.3] + + # #second order eta + # # eta_order_1: [0.04, 0.04, 0.3, 0.0, 0.0, 0.0] + # eta_order_1: [0.04, 0.04, 0.05, 0.0, 0.0, 0.0] + + # #lambda + # lambda: [0.2, 0.2, 1.0, 1.0, 10.0, 0.35] + + # ethan tune 2 + # eta_order_0: [1.0, 1.0, 1.0, 0.0, 0.0, 1.0] + + # #second order eta + # # eta_order_1: [0.04, 0.04, 0.3, 0.0, 0.0, 0.0] + # eta_order_1: [0.3, 0.3, 0.0, 0.0, 0.0, 0.0] + + # #lambda + # lambda: [0.1, 0.1, 10.0, 10.0, 10.0, 0.35] + #first order eta - eta_order_0: [0.8, 1.5, 5.0, 0.2, 0.2, 1.3] + eta_order_0: [0.8, 1.0, 1.0, 0.2, 0.2, 0.3] #second order eta - eta_order_1: [0.1, 0.04, 0.3, 0.0, 0.0, 0.2] + eta_order_1: [0.04, 0.04, 0.3, 0.0, 0.0, 0.0] #lambda - lambda: [0.8, 0.2, 1.0, 10.0, 10.0, 0.35] - + lambda: [0.2, 0.2, 1.0, 10.0, 10.0, 0.35] #format: 'a, m' for each degree of freedom #where: @@ -156,10 +186,26 @@ controller: #all gains are in body frame #linear unit are newtons per meter, angular are newton meter per radian - #the default gains for normal operation - p_gains: [3.0, 18.0, 200.0, 10.0, 8.0, 15.0] - i_gains: [0.0, 0.0, 0.0, 0.01, 0.005, 0.5] - d_gains: [5.0, 5.0, 90.0, 0.02, 0.1, 0.02] + # #the default gains for normal operation + + # cameron tune + # p_gains: [3.0, 18.0, 200.0, 6.0, 2.0, 10.0] + # i_gains: [0.0, 0.0, 0.0, 0.01, 0.01, 1.0] + # d_gains: [5.0, 5.0, 90.0, 0.1, 0.1, 0.03] + + # ethan tune 1 + p_gains: [3.0, 18.0, 200.0, 2.0, 5.0, 5.0] + i_gains: [0.0, 0.0, 0.0, 0.01, 0.0, 0.01] + d_gains: [5.0, 5.0, 90.0, 1.0, 2.0, 0.9] + + # ethan tune 2 + # p_gains: [3.0, 18.0, 200.0, 5.0, 5.0, 1.0] + # i_gains: [0.0, 0.0, 0.0, 0.0, 0.1, 0.0] + # d_gains: [5.0, 5.0, 90.0, 0.5, -20.0, 0.0] + + # p_gains: [3.0, 18.0, 200.0, 6.0, 1.0, 10.0] + # i_gains: [0.0, 0.0, 0.0, 0.01, 0.01, 1.0] + # d_gains: [5.0, 5.0, 90.0, 5.0, 0.01, 0.03] # #the default gains for normal operation # p_gains: [3.0, 18.0, 200.0, 2.0, 3.5, 8.0] From e1094b2e4fdf608d1ccba360e22e4305c35b2a07 Mon Sep 17 00:00:00 2001 From: osu-uwrt-bot2 Date: Fri, 10 Jul 2026 05:46:28 -0400 Subject: [PATCH 35/50] Better pitch damping --- riptide_descriptions/config/talos.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/riptide_descriptions/config/talos.yaml b/riptide_descriptions/config/talos.yaml index fae8dbe..d22f5be 100644 --- a/riptide_descriptions/config/talos.yaml +++ b/riptide_descriptions/config/talos.yaml @@ -194,9 +194,9 @@ controller: # d_gains: [5.0, 5.0, 90.0, 0.1, 0.1, 0.03] # ethan tune 1 - p_gains: [3.0, 18.0, 200.0, 2.0, 5.0, 5.0] + p_gains: [3.0, 18.0, 200.0, 2.0, 0.0, 5.0] i_gains: [0.0, 0.0, 0.0, 0.01, 0.0, 0.01] - d_gains: [5.0, 5.0, 90.0, 1.0, 2.0, 0.9] + d_gains: [5.0, 5.0, 90.0, 1.0, 20.0, 0.9] # ethan tune 2 # p_gains: [3.0, 18.0, 200.0, 5.0, 5.0, 1.0] From dc4b1ecda2c8cd10b04f8f3a8e817401d603bf79 Mon Sep 17 00:00:00 2001 From: zehdari Date: Fri, 10 Jul 2026 18:10:12 -0400 Subject: [PATCH 36/50] Acoustic sampling interface --- riptide_acoustics/CMakeLists.txt | 16 +- riptide_acoustics/launch/acoustics.launch.py | 23 +++ riptide_acoustics/package.xml | 6 +- riptide_acoustics/src/Acoustics.cpp | 176 ++++++++++++------ riptide_acoustics/src/Acoustics_Helper.py | 113 ----------- .../src/Acoustics_Initial_Development.zip | Bin 74046 -> 0 bytes .../Acoustics/Acoustics.SLDPRT | Bin 68728 -> 0 bytes .../Acoustics/acoustics.m | 118 ------------ .../Acoustics/acoustics_two_pulse_accurary.m | 114 ------------ .../Acoustics/solve_two_pulse_system.m | 73 -------- .../solve_two_pulse_system_depth.asv | 113 ----------- .../Acoustics/solve_two_pulse_system_depth.m | 115 ------------ .../Acoustics/transform.m | 40 ---- .../Acoustics/two_pulse_depth_test.m | 31 --- .../Acoustics/untitled.asv | 33 ---- .../Acoustics/~$Acoustics.SLDPRT | Bin 6 -> 0 bytes .../UndeterminantMethod/UndeterminantNode.asv | 159 ---------------- .../UndeterminantMethod/UndeterminantNode.m | 160 ---------------- .../calculate_distance_from_solve.asv | 113 ----------- .../calculate_distance_from_solve.m | 113 ----------- .../estimate_pinger_position.asv | 64 ------- .../estimate_pinger_position.m | 64 ------- .../src/UndeterminantMethod/test1.csv | 36 ---- .../src/UndeterminantMethod/test3.csv | 20 -- .../src/UndeterminantMethod/untitled2.asv | 46 ----- .../src/UndeterminantMethod/untitled2.m | 46 ----- .../src/matlab/AcousticsNode.asv | 120 ------------ riptide_acoustics/src/matlab/AcousticsNode.m | 130 ------------- .../src/matlab/GetTimingDIfference.asv | 9 - .../src/matlab/GetTimingDIfference.m | 12 -- riptide_acoustics/src/matlab/data/test1.csv | 36 ---- riptide_acoustics/src/matlab/data/test2.csv | 19 -- riptide_acoustics/src/matlab/data/test3.csv | 20 -- .../src/matlab/generatte_test_cases.asv | 57 ------ .../src/matlab/generatte_test_cases.m | 57 ------ .../src/matlab/parse_measurements.asv | 38 ---- .../src/matlab/parse_measurements.m | 30 --- .../src/matlab/run_measurements_from_csv.asv | 43 ----- .../src/matlab/run_measurements_from_csv.m | 42 ----- .../matlab/solve_two_pulse_system_depth.asv | 126 ------------- .../src/matlab/solve_two_pulse_system_depth.m | 130 ------------- .../src/matlab/two_pulse_depth_test.m | 31 --- 42 files changed, 155 insertions(+), 2537 deletions(-) create mode 100644 riptide_acoustics/launch/acoustics.launch.py delete mode 100644 riptide_acoustics/src/Acoustics_Helper.py delete mode 100644 riptide_acoustics/src/Acoustics_Initial_Development.zip delete mode 100644 riptide_acoustics/src/Acoustics_Initial_Development/Acoustics/Acoustics.SLDPRT delete mode 100644 riptide_acoustics/src/Acoustics_Initial_Development/Acoustics/acoustics.m delete mode 100644 riptide_acoustics/src/Acoustics_Initial_Development/Acoustics/acoustics_two_pulse_accurary.m delete mode 100644 riptide_acoustics/src/Acoustics_Initial_Development/Acoustics/solve_two_pulse_system.m delete mode 100644 riptide_acoustics/src/Acoustics_Initial_Development/Acoustics/solve_two_pulse_system_depth.asv delete mode 100644 riptide_acoustics/src/Acoustics_Initial_Development/Acoustics/solve_two_pulse_system_depth.m delete mode 100644 riptide_acoustics/src/Acoustics_Initial_Development/Acoustics/transform.m delete mode 100644 riptide_acoustics/src/Acoustics_Initial_Development/Acoustics/two_pulse_depth_test.m delete mode 100644 riptide_acoustics/src/Acoustics_Initial_Development/Acoustics/untitled.asv delete mode 100644 riptide_acoustics/src/Acoustics_Initial_Development/Acoustics/~$Acoustics.SLDPRT delete mode 100644 riptide_acoustics/src/UndeterminantMethod/UndeterminantNode.asv delete mode 100644 riptide_acoustics/src/UndeterminantMethod/UndeterminantNode.m delete mode 100644 riptide_acoustics/src/UndeterminantMethod/calculate_distance_from_solve.asv delete mode 100644 riptide_acoustics/src/UndeterminantMethod/calculate_distance_from_solve.m delete mode 100644 riptide_acoustics/src/UndeterminantMethod/estimate_pinger_position.asv delete mode 100644 riptide_acoustics/src/UndeterminantMethod/estimate_pinger_position.m delete mode 100644 riptide_acoustics/src/UndeterminantMethod/test1.csv delete mode 100644 riptide_acoustics/src/UndeterminantMethod/test3.csv delete mode 100644 riptide_acoustics/src/UndeterminantMethod/untitled2.asv delete mode 100644 riptide_acoustics/src/UndeterminantMethod/untitled2.m delete mode 100644 riptide_acoustics/src/matlab/AcousticsNode.asv delete mode 100644 riptide_acoustics/src/matlab/AcousticsNode.m delete mode 100644 riptide_acoustics/src/matlab/GetTimingDIfference.asv delete mode 100644 riptide_acoustics/src/matlab/GetTimingDIfference.m delete mode 100644 riptide_acoustics/src/matlab/data/test1.csv delete mode 100644 riptide_acoustics/src/matlab/data/test2.csv delete mode 100644 riptide_acoustics/src/matlab/data/test3.csv delete mode 100644 riptide_acoustics/src/matlab/generatte_test_cases.asv delete mode 100644 riptide_acoustics/src/matlab/generatte_test_cases.m delete mode 100644 riptide_acoustics/src/matlab/parse_measurements.asv delete mode 100644 riptide_acoustics/src/matlab/parse_measurements.m delete mode 100644 riptide_acoustics/src/matlab/run_measurements_from_csv.asv delete mode 100644 riptide_acoustics/src/matlab/run_measurements_from_csv.m delete mode 100644 riptide_acoustics/src/matlab/solve_two_pulse_system_depth.asv delete mode 100644 riptide_acoustics/src/matlab/solve_two_pulse_system_depth.m delete mode 100644 riptide_acoustics/src/matlab/two_pulse_depth_test.m diff --git a/riptide_acoustics/CMakeLists.txt b/riptide_acoustics/CMakeLists.txt index ba74c7d..22bf1b6 100644 --- a/riptide_acoustics/CMakeLists.txt +++ b/riptide_acoustics/CMakeLists.txt @@ -8,10 +8,8 @@ endif() # find dependencies find_package(ament_cmake REQUIRED) find_package(std_msgs REQUIRED) +find_package(std_srvs REQUIRED) find_package(rclcpp REQUIRED) -find_package(tf2 REQUIRED) -find_package(tf2_ros REQUIRED) -find_package(geometry_msgs REQUIRED) # uncomment the following section in order to fill in # further dependencies manually. # find_package( REQUIRED) @@ -19,16 +17,18 @@ find_package(geometry_msgs REQUIRED) add_executable(run_acoustics src/Acoustics.cpp) ament_target_dependencies(run_acoustics -std_msgs -geometry_msgs -rclcpp -tf2 -tf2_ros) +std_msgs +std_srvs +rclcpp) install(TARGETS run_acoustics DESTINATION lib/${PROJECT_NAME} ) +install(DIRECTORY launch + DESTINATION share/${PROJECT_NAME} +) + #yoink files file(GLOB src_py_files RELATIVE ${PROJECT_SOURCE_DIR} src/*.py) install(PROGRAMS ${src_py_files} diff --git a/riptide_acoustics/launch/acoustics.launch.py b/riptide_acoustics/launch/acoustics.launch.py new file mode 100644 index 0000000..422a85f --- /dev/null +++ b/riptide_acoustics/launch/acoustics.launch.py @@ -0,0 +1,23 @@ +from launch import LaunchDescription +from launch.actions import DeclareLaunchArgument +from launch.substitutions import LaunchConfiguration +from launch_ros.actions import Node + +def generate_launch_description(): + return LaunchDescription([ + DeclareLaunchArgument(name="percentile", default_value="95.0", + description="Percentile of each buffer used for the amplitude comparison"), + + Node( + package='riptide_acoustics', + executable='run_acoustics', + name='riptide_acoustics', + output='screen', + parameters=[ + { + "percentile": LaunchConfiguration("percentile") + } + ], + respawn=True + ) + ]) diff --git a/riptide_acoustics/package.xml b/riptide_acoustics/package.xml index d11f2a7..39b1686 100644 --- a/riptide_acoustics/package.xml +++ b/riptide_acoustics/package.xml @@ -12,9 +12,9 @@ ament_lint_auto ament_lint_common - riptide_msgs2 - rclpy - tf2 + rclcpp + std_msgs + std_srvs ament_cmake diff --git a/riptide_acoustics/src/Acoustics.cpp b/riptide_acoustics/src/Acoustics.cpp index 2a7c99a..b61f7bf 100644 --- a/riptide_acoustics/src/Acoustics.cpp +++ b/riptide_acoustics/src/Acoustics.cpp @@ -1,98 +1,164 @@ #include -#include -#include - - +#include +#include +#include +#include #include "rclcpp/rclcpp.hpp" #include "std_msgs/msg/float32.hpp" -#include "geometry_msgs/msg/transform_stamped.hpp" -#include "geometry_msgs/msg/transform.hpp" - -#include "tf2_ros/transform_listener.h" -#include "tf2_ros/buffer.h" -#include "tf2/exceptions.h" - +#include "std_msgs/msg/bool.hpp" +#include "std_srvs/srv/trigger.hpp" -using namespace std::chrono_literals; -using std::placeholders::_1; +using namespace std::placeholders; +// 1) start_sample -> collect amplitudes for buffer 0 +// 2) stop_sample -> close buffer 0 +// 3) start_sample -> collect ampltudes for buffer 1 +// 4) stop_sample -> close buffer 1, then compare and publish once to buffer_0_closer -class MinimalSubscriber : public rclcpp::Node +// Using p95 atm, but could be changed pretty easily +class Acoustics : public rclcpp::Node { public: - MinimalSubscriber() + Acoustics() : Node("riptide_acoustics") { - subscription_ = this->create_subscription( - "acoustics/delta_t", 10, std::bind(&MinimalSubscriber::topic_callback, this, _1)); + this->declare_parameter("percentile", 95.0); - //initialize tf buffer and tf listener - tf_buffer_ = std::make_unique(this->get_clock()); - tf_listener_ = std::make_shared(*tf_buffer_); + ampSubscription = this->create_subscription( + "ivc/pinger/selected_amp", 10, std::bind(&Acoustics::ampCallback, this, _1)); - portToStarboardTransformTimer = this->create_wall_timer(1s, std::bind(&MinimalSubscriber::getPortToStarboardTransform, this)); + resultPublisher = this->create_publisher( + "acoustics/buffer_0_closer", 10); + + resultTimer = this->create_wall_timer( + std::chrono::seconds(1), std::bind(&Acoustics::publishResult, this)); + + startSampleService = this->create_service( + "acoustics/start_sample", std::bind(&Acoustics::startSample, this, _1, _2)); + + stopSampleService = this->create_service( + "acoustics/stop_sample", std::bind(&Acoustics::stopSample, this, _1, _2)); } private: - void topic_callback(const std_msgs::msg::Float32::SharedPtr msg) const + void ampCallback(const std_msgs::msg::Float32::SharedPtr msg) + { + if (sampling) { + buffers[activeBuffer].push_back(msg->data); + } + } + + void startSample(const std_srvs::srv::Trigger::Request::SharedPtr, + std_srvs::srv::Trigger::Response::SharedPtr response) + { + if (sampling) { + response->success = false; + response->message = "Already sampling"; + return; + } + + buffers[activeBuffer].clear(); + sampling = true; + + response->success = true; + response->message = "Sampling into buffer " + std::to_string(activeBuffer); + RCLCPP_INFO(this->get_logger(), "Started sampling into buffer %d", activeBuffer); + } + + void stopSample(const std_srvs::srv::Trigger::Request::SharedPtr, + std_srvs::srv::Trigger::Response::SharedPtr response) { - geometry_msgs::msg::TransformStamped t; - - try { - t = tf_buffer_->lookupTransform( - "world", "talos/base_link", - tf2::TimePointZero); - } catch (const tf2::TransformException & ex) { - RCLCPP_INFO( - this->get_logger(), "Could not transform!"); + if (!sampling) { + response->success = false; + response->message = "Not sampling"; return; } - RCLCPP_INFO(this->get_logger(), "X: %f", t.transform.translation.x); + sampling = false; + + RCLCPP_INFO(this->get_logger(), "Stopped sampling buffer %d with %zu samples", + activeBuffer, buffers[activeBuffer].size()); + if (activeBuffer == 0) { + activeBuffer = 1; + response->success = true; + response->message = "Buffer 0 closed with " + std::to_string(buffers[0].size()) + " samples"; + return; + } + + // buffer 1 just closed, compare and reset + compareBuffers(response); + activeBuffer = 0; } - void getPortToStarboardTransform() + void compareBuffers(std_srvs::srv::Trigger::Response::SharedPtr response) { - //stamped msg - geometry_msgs::msg::TransformStamped stamped; + if (buffers[0].empty() || buffers[1].empty()) { + response->success = false; + response->message = "Cannot compare, buffer 0 has " + std::to_string(buffers[0].size()) + + " samples and buffer 1 has " + std::to_string(buffers[1].size()); + RCLCPP_WARN(this->get_logger(), "%s", response->message.c_str()); + return; + } + + float value0 = reduceBuffer(buffers[0]); + float value1 = reduceBuffer(buffers[1]); - //get transform from tf buffer - try { + buffer0Closer = value0 >= value1; + haveResult = true; + publishResult(); - //TODO: change to port and starboard pod frames - stamped = tf_buffer_->lookupTransform("talos/pose_sensor_link", "talos/base_link", tf2::TimePointZero); - - portToStarboardTransform = stamped.transform; + response->success = true; + response->message = "buffer " + std::to_string(buffer0Closer ? 0 : 1) + + " closer (buffer 0: " + std::to_string(value0) + + ", buffer 1: " + std::to_string(value1) + ")"; + RCLCPP_INFO(this->get_logger(), "%s", response->message.c_str()); + } - RCLCPP_INFO(this->get_logger(), "Successfuly collected port to starboard transform!"); - } catch (const tf2::TransformException & ex) { - RCLCPP_INFO( - this->get_logger(), "Could collect port to starboard transform, retrying!"); + void publishResult() + { + if (!haveResult) { return; } - portToStarboardTransformTimer->cancel(); + std_msgs::msg::Bool msg; + msg.data = buffer0Closer; + resultPublisher->publish(msg); } - rclcpp::Subscription::SharedPtr subscription_; + float reduceBuffer(const std::vector & buffer) + { + double p = this->get_parameter("percentile").as_double(); + + // it's c++ so sorting is O(n) right? + std::vector sorted = buffer; + size_t idx = static_cast(std::clamp(p, 0.0, 100.0) / 100.0 * (sorted.size() - 1)); + std::nth_element(sorted.begin(), sorted.begin() + idx, sorted.end()); + return sorted[idx]; + } - //tf - std::shared_ptr tf_listener_{nullptr}; - std::unique_ptr tf_buffer_; + rclcpp::Subscription::SharedPtr ampSubscription; + rclcpp::Publisher::SharedPtr resultPublisher; + rclcpp::TimerBase::SharedPtr resultTimer; + rclcpp::Service::SharedPtr startSampleService; + rclcpp::Service::SharedPtr stopSampleService; - //port pod to starboard pod transform - geometry_msgs::msg::Transform portToStarboardTransform; - rclcpp::TimerBase::SharedPtr portToStarboardTransformTimer {nullptr}; + // amplitude samples for the two comparison windows + std::vector buffers[2]; + int activeBuffer = 0; + bool sampling = false; + // last comparison result, invalid until the first compare completes + bool buffer0Closer = false; + bool haveResult = false; }; int main(int argc, char * argv[]) { rclcpp::init(argc, argv); - rclcpp::spin(std::make_shared()); + rclcpp::spin(std::make_shared()); rclcpp::shutdown(); return 0; -} \ No newline at end of file +} diff --git a/riptide_acoustics/src/Acoustics_Helper.py b/riptide_acoustics/src/Acoustics_Helper.py deleted file mode 100644 index 7ca8a96..0000000 --- a/riptide_acoustics/src/Acoustics_Helper.py +++ /dev/null @@ -1,113 +0,0 @@ -#! /usr/bin/env python3 - -import rclpy -from rclpy.node import Node -from rclpy.qos import qos_profile_sensor_data -from rclpy.duration import Duration -from riptide_msgs2.msg import DshotRPMFeedback, DshotPartialTelemetry -from geometry_msgs.msg import Vector3Stamped -from tf2_ros import TransformException -from tf2_ros.buffer import Buffer -from tf2_ros.transform_listener import TransformListener -from time import sleep -import os -import datetime -from rclpy.time import Time - - -#the purpose of this node is to concatinate all thruster data into a single message that includes the latest rpm from each thruster - -#TODO figure out real topic names - -ACOUSTICS_PINGER_LOCATION = [3.0, 2.0, -1.0, 0.0, 0.0, 0.0] -BASE_FRAME = "map" -TARGET_FRAME_STARTBOARD = "talos/acoustics_starboard_link" -TARGET_FRAME_PORT = "talos/acoustics_port_link" -DATA_FOLDER_NAME = "acoustics_data" - - -class Acoustics_Helper(Node): - - def __init__(self): - #init super node - super().__init__("Acoustics_Helpers") - - self.acoustics_sub = self.create_subscription(Vector3Stamped, "/talos/acoustics/delta_t", self.acoustics_cb, qos_profile_sensor_data) - - - self.tf_buffer = Buffer() - self.tf_listener = TransformListener(self.tf_buffer, self) - - try: - #ensure there is a data file - os.mkdir(DATA_FOLDER_NAME) - except: - self.get_logger().info('Created Data Folder') - - #open the log file - now = datetime.datetime.now() - # Format the date and time as a string - formatted_date_time = now.strftime("%Y-%m-%d %H:%M:%S") - filename = DATA_FOLDER_NAME + "/data" + formatted_date_time + ".csv" - self.data_file = open(filename, "w") - - #write the file header - headers = "number,time,delta_t,pos x 1,pos y 1,pos z 1,or w 1,or x 1,or y 1, or z 1,pos x 2,pos y 2,pos z 2,or w 2,or x 2,or y 2, or z 2\n" - self.data_file.write(headers) - - self.points_written = 0 - - - - - async def acoustics_cb(self, msg): - #wait to ensure transforms are available - try: - - delta_t_time = msg.header.stamp - - starboard_transform = await self.tf_buffer.lookup_transform_async( - BASE_FRAME, - TARGET_FRAME_STARTBOARD, - delta_t_time) - - port_transform = await self.tf_buffer.lookup_transform_async( - BASE_FRAME, - TARGET_FRAME_PORT, - delta_t_time) - - - msg_time = msg.header.stamp.sec + msg.header.stamp.nanosec * 10e-9 - delta_t = msg.vector.x - - #formulate the output line - self.points_written = self.points_written + 1 - - output_line = str(self.points_written) + "," + str(msg_time) + "," + str(delta_t) + "," + \ - str(starboard_transform.transform.translation.x) + "," + str(starboard_transform.transform.translation.y) + "," + str(starboard_transform.transform.translation.z) + "," + \ - str(starboard_transform.transform.rotation.w) + "," + str(starboard_transform.transform.rotation.x) + "," + str(starboard_transform.transform.rotation.y) + "," + str(starboard_transform.transform.rotation.z) + "," + \ - str(port_transform.transform.translation.x) + "," + str(port_transform.transform.translation.y) + "," + str(port_transform.transform.translation.z) + "," + \ - str(port_transform.transform.rotation.w) + "," + str(port_transform.transform.rotation.x) + "," + str(port_transform.transform.rotation.y) + "," + str(port_transform.transform.rotation.z) + "\n" - - #write to csv - self.data_file.write(output_line) - - except TransformException as ex: - self.get_logger().info( - f'Could not transform to pinger frame: {ex}') - return - - - - - -def main(args=None): - - rclpy.init(args=args) - node = Acoustics_Helper() - rclpy.spin(node) - node.data_file.close() - - -if __name__ == "__main__": - main() \ No newline at end of file diff --git a/riptide_acoustics/src/Acoustics_Initial_Development.zip b/riptide_acoustics/src/Acoustics_Initial_Development.zip deleted file mode 100644 index f25317a72037c61597c460fb657dc6aff0160114..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 74046 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

+_PHaFFZ9yW8Lj{KN8rA16 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 diff --git a/riptide_acoustics/src/Acoustics_Initial_Development/Acoustics/Acoustics.SLDPRT b/riptide_acoustics/src/Acoustics_Initial_Development/Acoustics/Acoustics.SLDPRT deleted file mode 100644 index 360d9d6b88e2b69619b248f5b7563a42d844a92b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 68728 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%0DF)_ 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%0AFHbO1Ij> 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 CPV_DS diff --git a/riptide_acoustics/src/Acoustics_Initial_Development/Acoustics/acoustics.m b/riptide_acoustics/src/Acoustics_Initial_Development/Acoustics/acoustics.m deleted file mode 100644 index cbb1056..0000000 --- a/riptide_acoustics/src/Acoustics_Initial_Development/Acoustics/acoustics.m +++ /dev/null @@ -1,118 +0,0 @@ -%Script to Calculate the resolution of 2 hydrophone acoustics system -clear; - -%All units in meters -hydrophone_base_width = 50;%0.05372; -water_speed = 1500; -adc_sampling_frequency = 5*10^5; - -%define transform between two pulses -x1 = -95.04; -y1 = 8.49; -heading_change = -21.8912/360 *2*pi(); %radians - -x2 = x1 - hydrophone_base_width * (1 - cos(heading_change)); -y2 = y1 + hydrophone_base_width * sin(heading_change); - - -%data gathered from acoustics -k1 = 2; -k2 = 19; - -% %equations to solve acoustics systems -% syms a1 a2 c1 c2 B1 B2 -% -% %equations of localization -% eq1 = sqrt(x1^2+y1^2) == sqrt((a1+c1)^2-B1^2) + sqrt((a2+c2^2)-B1^2); -% eq2 = sqrt(x2^2+y2^2) == sqrt((a1+c1+k1)^2-B2^2) + sqrt((a2+c2+k2)^2-B2^2); -% eq3 = B1^2 == (a2+c2)^2 -(((a1+c1)^2-(a2+c2)^2-(x1^2+y1^2))/(-2*sqrt(x1^2+y1^2)))^2; -% eq4 = B2^2 == (a2+c2+k2)^2 -(((a1+c1+k1)^2-(a2+c2+k2)^2-(x2^2+y2^2))/(-2*sqrt(x2^2+y2^2)))^2; -% eq5 = 2*(k1+1)*c1 + 2*k1*a1 + 4*a1*c1 == .5*hydrophone_base_width + k1^2; -% eq6 = 2*(k2+1)*c2 + 2*k2*a2 + 4*a2*c2 == .5*hydrophone_base_width + k2^2; -% -% solve(eq1,eq2,eq3,eq4,eq5,eq6,a1,a2,c1,c2,B1,B2) - -%equations to solve acoustics systems -%x0 = [64.7495,44,2.5505,12.30,46.2,41.04]'; - -answer_count = 20; -answer = zeros(6, answer_count); -counter = 0; - -while(counter < answer_count) - - norm1 = 5; - while(norm1 > .001) - - x0 = rand([6,1])*5000; - - %equations of localization -% F = @(x) [-sqrt(abs((x(1)+x(3))^2-x(5)^2)) + sqrt(abs((x(2)+x(4))^2-x(5)^2)) - sqrt(x1^2+y1^2); -% sqrt(abs((x(1)+x(3)+k1)^2-x(6)^2)) - sqrt(abs((x(2)+x(4)+k2)^2-x(6)^2))-sqrt(x2^2+y2^2); -% (x(2)+x(4))^2 - (((x(1)+x(3))^2-(x(2)+x(4))^2-(x1^2+y1^2))/(-2*sqrt(x1^2+y1^2)))^2 - x(5)^2; -% (x(2)+x(4)+k2)^2 - (((x(1)+x(3)+k1)^2-(x(2)+x(4)+k2)^2-(x2^2+y2^2))/(-2*sqrt(x2^2+y2^2)))^2 - x(6)^2; -% .5*hydrophone_base_width^2 - k1^2 - 2*(k1+x(3))*x(3) - 2*k1*x(1) - 4*x(1)*x(3); -% .5*hydrophone_base_width^2 - k2^2 - 2*(k2+x(4))*x(4) - 2*k2*x(2) - 4*x(2)*x(4)]; - - - - options = optimoptions('fsolve','Display','iter', "OptimalityTolerance", 10e-12); - - solve1 = fsolve(F, x0, options); - - norm1 = norm(F(solve1)); - end - - counter = counter + 1; - answer(:, counter) = solve1; - -end - - - -w = (2*answer(3,:)*k1 + answer(3,:).^2 + k1^2 + 2 .* answer(1,:) .* answer(3,:) + 2 * answer(1,:) * k1 - hydrophone_base_width^2 / 4) / -hydrophone_base_width; -h = sqrt(answer(1,:).^2 - w.^2); - -%rot = [cos(heading_change),-sin(heading_change);sin(heading_change),cos(heading_change)]; - -fx1 = [0;0]; -length = 1; -for counter = 1:answer_count - if(w(counter) < 0) - fx1(:,length) = [1;w(counter)]; - fy1(length)= h(counter); - - length = length + 1; - end -end - -b1 = inv(fx1*fx1') * fx1 * fy1'; - -w2t = (2*answer(4,:)*k2 + answer(4,:).^2 + k2^2 + 2 .* answer(2,:) .* answer(4,:) + 2 * answer(2,:) * k2 - hydrophone_base_width^2 / 4) / -hydrophone_base_width; -h2t = sqrt(answer(2,:).^2 - w2t.^2); - - -figure(1) - -fx2 = [ones(size(w2t(1,:)));w2t]; -fy2= h2t; - -b2 = inv(fx2*fx2') * fx2 * fy2'; -b2 = -tan(atan(b2) - heading_change); -b2_origin = [0; b2(1)]; -rot = [cos(heading_change), -sin(heading_change); sin(heading_change), cos(heading_change)]; -b2_origin = rot*b2_origin + [(x1+x2)/2; (y1+y2)/2]; - -plot(fx1(2,:),fy1, "or", w2t, h2t,"xg") - -figure(2) -w_vals = linspace(-200,200, 1000); -h1_vals = b1(1) + b1(2) * w_vals; -h2_vals = b2(2) * w_vals; - -plot(w_vals, h1_vals, w_vals + b2_origin(1), h2_vals + b2_origin(2), [-120.04], [2998], "go") - -b2_intercept = b2_origin(2) - b2_origin(1) * b2(2); - -intersection_x = -(b2_intercept - b1(1))/(b2(2) - b1(2)); -intersection_y = b1(2) * intersection_x + b1(1); diff --git a/riptide_acoustics/src/Acoustics_Initial_Development/Acoustics/acoustics_two_pulse_accurary.m b/riptide_acoustics/src/Acoustics_Initial_Development/Acoustics/acoustics_two_pulse_accurary.m deleted file mode 100644 index 74a53c9..0000000 --- a/riptide_acoustics/src/Acoustics_Initial_Development/Acoustics/acoustics_two_pulse_accurary.m +++ /dev/null @@ -1,114 +0,0 @@ -%do some accurary verification -%units = mm - -clear; - -%cases that need evaluated for - % - negative sqrts in eq1 and eq2 - % - sign of slope 2 - - -%using little boat base width - -hydrophone_base_width = 50; -water_speed = 1500000; -rp2040_adc_sampling_frequency = 5*10^5; -MHz1_adc_sampling_frequency = 5*10^6; - -adc_distance_accurarcy_rp2040 = 1 / (rp2040_adc_sampling_frequency) * water_speed; -adc_distance_accurarcy_1MHZ = 1 / (MHz1_adc_sampling_frequency) * water_speed; - -samples = 10; - -%should yeild (2998.5,95.04) -% distances = [2,19]; -% transform = [-95.04, 8.49, -.38207]; - -%should yeild (-44 ,98.17) -% distances = [20, -7.91]; -% transform = [-150, 10, -.6604]; - -%should yeild (52,116.92) -% distances = [-20, -18.3411]; -% transform = [-175, 10, -.6604]; - -%should yeild (52,116.92) -distances = [-20, 41.465]; -transform = [141.89, 22.89, -.26074]; - -%should yield [1,1] -%distances = [35.36;29.7]; - - -[slv_x,slv_y] = solve_two_pulse_system(distances(1),distances(2),transform(1),transform(2),transform(3),50, 3000) - -positions_5 = zeros(2,samples); -count = 1; -while count <= samples - - [x,y] = solve_two_pulse_system(distances(1) + (rand() - .5) *adc_distance_accurarcy_rp2040,distances(2) + (rand() - .5) *adc_distance_accurarcy_rp2040,transform(1),transform(2),transform(3),50, 1000); - positions_5(1, count) = x; - positions_5(2, count) = y; - - count = count + 1; -end - - -positions_6 = zeros(2,samples); -count = 1; -while count <= samples - - [x,y] = solve_two_pulse_system(distances(1) + (rand() - .5) *adc_distance_accurarcy_1MHZ,distances(2) + (rand() - .5) *adc_distance_accurarcy_1MHZ,transform(1),transform(2),transform(3),50, 1000); - positions_6(1, count) = x; - positions_6(2, count) = y; - - count = count + 1; -end - -figure(1) -plot([slv_x], [slv_y], "xg", positions_5(1,:), positions_5(2,:), "or", positions_6(1,:), positions_6(2,:), "ob") -title("Solve Results for Acoustics Target at -1m,1m for LB Baseline") -xlabel("Solved Distance (mm)") -ylabel("Solved Distance (mm)") -legend("Solve With No Error", "RP2040 ADC", "1MHz ADC") - -% -% %talos baseline -% -% %should yield[-1,1] -% distances = [141.2444;118.367]; -% -% %should yield[1,1] -% distances = [-141.2444; -167.7482]; -% -% [slv_x,slv_y] = solve_two_pulse_system(distances(1),distances(2),-400,100,.05,200, 10); -% -% positions_5 = zeros(2,samples); -% count = 1; -% while count <= samples -% -% [x,y] = solve_two_pulse_system(distances(1) + (rand() - .5) *adc_distance_accurarcy_rp2040,distances(2) + (rand() - .5) *adc_distance_accurarcy_rp2040,-400,100,.05,200, 10); -% positions_5(1, count) = x; -% positions_5(2, count) = y; -% -% count = count + 1 -% end -% -% -% positions_6 = zeros(2,samples); -% count = 1; -% while count <= samples -% -% [x,y] = solve_two_pulse_system(distances(1) + (rand() - .5) * adc_distance_accurarcy_1MHZ,distances(2) + (rand() - .5) *adc_distance_accurarcy_1MHZ,-400,100,.05,200, 10); -% positions_6(1, count) = x; -% positions_6(2, count) = y; -% -% count = count + 1 -% end -% -% figure(2) -% plot([slv_x], [slv_y], "xg", positions_5(1,:), positions_5(2,:), "or", positions_6(1,:), positions_6(2,:), "ob") -% title("Solve Results for Acoustics Target at -1m,1m for Talos Baseline") -% xlabel("Solved Distance (mm)") -% ylabel("Solved Distance (mm)") -% legend("Solve With No Error", "RP2040 ADC", "1MHz ADC") \ No newline at end of file diff --git a/riptide_acoustics/src/Acoustics_Initial_Development/Acoustics/solve_two_pulse_system.m b/riptide_acoustics/src/Acoustics_Initial_Development/Acoustics/solve_two_pulse_system.m deleted file mode 100644 index c4286ba..0000000 --- a/riptide_acoustics/src/Acoustics_Initial_Development/Acoustics/solve_two_pulse_system.m +++ /dev/null @@ -1,73 +0,0 @@ -function [intersection_x, intersection_y] = solve_two_pulse_system(k1, k2, x1, y1, heading_change, hydrophone_base_width, estimated_distance) -%k1: hydrophone 1 pulse time - hydrophone 2 pulse time for first pulse -%k2: hydrophone 1 pulse time - hydrophone 2 pulse time for second pulse -%x1: x distance traveled between pulses in the coordinate frame of first -%pulse -%y1: y distance traveled between pulses in the coordinate frame of second -%pulse -%heading change: the change in heading of vehicle between the two pulses: -%ccw is positive -%solves: the number of successful solves to use in estimatation process -%hydrophone_base_width: distance between the two hydrophones - -%transform location of other hydrophone -x2 = x1 + hydrophone_base_width * (cos(heading_change)); -y2 = y1 + hydrophone_base_width * sin(heading_change); - -%define core functions -Y = @(a,k) sqrt(a^2 + 2*a*k + k^2 - ((hydrophone_base_width^2 + 2*a*k +k^2)/(2*hydrophone_base_width))^2); -X = @(a,y,k) sqrt(a^2 - y^2) * -sign(k) - hydrophone_base_width / 2; - - -norm1 = 5; -answer = [0;0]; -while(norm1 > .001) - - x0 = [rand() * estimated_distance;rand() * estimated_distance];% [estimated_distance; estimated_distance; estimated_distance; estimated_distance; k1 / 2 ; k2 / 2] * (.2 - rand()*.2); - - %equations of localization -% F = @(x) [sqrt(abs((x(1)+x(3))^2-x(5)^2))*sign((x(1)+x(3))^2-x(5)^2) + sqrt(abs((x(2)+x(4))^2-x(5)^2))*sign((x(1)+x(3))^2-x(5)^2) - sqrt(x1^2+y1^2); -% sqrt(abs((x(1)+x(3)+k1)^2-x(6)^2))*sign((x(1)+x(3))^2-x(5)^2) + sqrt(abs((x(2)+x(4)+k2)^2-x(6)^2))*sign((x(1)+x(3))^2-x(5)^2) - sqrt(x2^2+y2^2); -% (x(2)+x(4))^2 - (((x(1)+x(3))^2-(x(2)+x(4))^2-(x1^2+y1^2))/(-2*sqrt(x1^2+y1^2)))^2 - x(5)^2; -% (x(2)+x(4)+k2)^2 - (((x(1)+x(3)+k1)^2-(x(2)+x(4)+k2)^2-(x2^2+y2^2))/(-2*sqrt(x2^2+y2^2)))^2 - x(6)^2; -% .5*hydrophone_base_width^2 - k1^2 - 2*(k1+x(3))*x(3) - 2*k1*x(1) - 4*x(1)*x(3); -% .5*hydrophone_base_width^2 - k2^2 - 2*(k2+x(4))*x(4) - 2*k2*x(2) - 4*x(2)*x(4)]; - - - - - %cramers rule & determinant of rot matrix is one - F = @(x) [cos(-heading_change)*(Y(x(1), k1) - (y1 + y2) / 2) + (sin(-heading_change) * (X(x(1), Y(x(1), k1), k1) - (x1 + x2 - hydrophone_base_width) / 2)) - Y(x(2),k2); - (X(x(1), Y(x(1), k1), k1) - (x1 + x2 - hydrophone_base_width) / 2) * cos(-heading_change) - (Y(x(1), k1) - (y1 + y2) / 2) * sin(-heading_change) - (X(x(2),Y(x(2),k2), k2))]; - - %options = optimoptions('fsolve','Display','iter', "OptimalityTolerance", 10e-12); - options = optimoptions('fsolve', 'Display', 'off', "OptimalityTolerance", 10e-6); - - solve1 = fsolve(F, x0, options); - - norm1 = norm(F(solve1)); - answer = solve1; -end - -%calculate position - -intersection_x = X(answer(1),Y(answer(1), k1),k1); -intersection_y = Y(answer(1), k1); - -%for quadratic fit -% intersection_x = (-(b2(2) - b1(2)) - sqrt(abs((b2(2) - b1(2))^2 - 4 * (b2(1) - b1(1))*(b2(3) - b1(3))))) / (2 * (b2(3) - b1(3))); -% intersection_y = b1(1) + b1(2)* intersection_x + b1(3) * intersection_x^2; - - -%plots for debug -% figure(1); -% plot(fx1(2,:),fy1, "or", rot_points(1,:), rot_points(2,:),"xg") - -% figure(2) -% w_vals = linspace(-1000,1000, 1000); -% h1_vals = b1(1) + b1(2) * w_vals + b1(3)*w_vals.^2; -% h2_vals = b2(2) * w_vals + b2(1) + b2(3) * w_vals.^2;%b2_intercept ; -% -% plot(w_vals, h1_vals,"r", w_vals, h2_vals, "g", [-1000], [1000], "bo") - - diff --git a/riptide_acoustics/src/Acoustics_Initial_Development/Acoustics/solve_two_pulse_system_depth.asv b/riptide_acoustics/src/Acoustics_Initial_Development/Acoustics/solve_two_pulse_system_depth.asv deleted file mode 100644 index 11fe75a..0000000 --- a/riptide_acoustics/src/Acoustics_Initial_Development/Acoustics/solve_two_pulse_system_depth.asv +++ /dev/null @@ -1,113 +0,0 @@ -function [intersection_x, intersection_y] = solve_two_pulse_system_depth(k1, k2, pose_1, pose_2, hydrophone_base_vector, estimated_distance, pinger_depth) -%k1: hydrophone 1 pulse time - hydrophone 2 pulse time for first pulse -%k2: hydrophone 1 pulse time - hydrophone 2 pulse time for second pulse -%pose_1: the pose of the front hydrophone at the time of the first pulse -%pose_2: the pose of the front hydrophone at the time of the second pulse -%hydrophone_base_vector: the position vector between hydrophone 1 and 2 -%estimated disance: the estimated distance from the front hydrophone and -%pinger -%pinger_depth: the estimated depth of the pinger - -%pose format = [x,y,z,rw,rx,ry,rz] - -%base frame is hydrophone 1 pulse 1 = [0,0,0,0,0,0] -x1_1 = 0; -y1_1 = 0; -z1_1 = 0; - -%calculate position of second hydrophone -%order [Z,Y,X] -eul_1 = quat2eul([pose_1(4), pose_1(5), pose_1(6), pose_1(7)]); -norm_matrix = eul2rotm([-eul_1(1), 0, 0]); -rot_1 = norm_matrix * quat2rotm([pose_1(4), pose_1(5), pose_1(6), pose_1(7)]); -pos2_1 = rot_1 * hydrophone_base_vector; - -x1_2 = pose_2(1) - pose_1(1); -y1_2 = pose_2(2) - pose_1(2); -z1_2 = pose_2(3) - pose; - -%calculate position of second hydrophone in second pulse -rot_2 = norm_matrix * quat2rotm([pose_2(4), pose_2(5), pose_2(6), pose_2(7)]); -pos2_2 = rot_2 * hydrophone_base_vector + [x1_2;y1_2;z1_2]; - -%base widths in x,y plane -h1 = pos2_1(1); -h2 = norm([pos2_2(1) - x1_2 , pos2_2(2) - y1_2]); - -%calculate delta positions -x1 = x1_2 - x1_1; -x2 = pos2_2(1) - x1_1; - -y1 = y1_2 - y1_1; -y2 = pos2_2(2) - pos2_1(2); - -%changes in depth -dz1_1 = pose_1(3) + pinger_depth; -dz2_1 = pos2_1(3) + pinger_depth; -dz1_2 = pose_2(3) + pinger_depth; -dz2_2 = pos2_2(3) + pinger_depth; - -%calculate heading change -eul_2 = rotm2eul(rot_2); -heading_change = eul_2(1); - -%define core functions -K = @(a,k_bar,z1,z2) (-(2*a) + sqrt(4*a^2 + 4 * (k_bar^2 + 2 * k_bar * sqrt(a^2 + z1^2) + z1^2 - z2^2))) / 2; -Y = @(a,k,h) sqrt(a^2 + 2*a*k + k^2 - ((h^2 + 2*a*k +k^2)/(2*h))^2); -X = @(a,y,k,h) sqrt(a^2 - y^2) * -sign(k) - h / 2; - -norm1 = 5; -answer = [0;0]; -while(norm1 > .001) - - x0 = [rand() * estimated_distance;rand() * estimated_distance];% [estimated_distance; estimated_distance; estimated_distance; estimated_distance; k1 / 2 ; k2 / 2] * (.2 - rand()*.2); - - %equations of localization -% F = @(x) [sqrt(abs((x(1)+x(3))^2-x(5)^2))*sign((x(1)+x(3))^2-x(5)^2) + sqrt(abs((x(2)+x(4))^2-x(5)^2))*sign((x(1)+x(3))^2-x(5)^2) - sqrt(x1^2+y1^2); -% sqrt(abs((x(1)+x(3)+k1)^2-x(6)^2))*sign((x(1)+x(3))^2-x(5)^2) + sqrt(abs((x(2)+x(4)+k2)^2-x(6)^2))*sign((x(1)+x(3))^2-x(5)^2) - sqrt(x2^2+y2^2); -% (x(2)+x(4))^2 - (((x(1)+x(3))^2-(x(2)+x(4))^2-(x1^2+y1^2))/(-2*sqrt(x1^2+y1^2)))^2 - x(5)^2; -% (x(2)+x(4)+k2)^2 - (((x(1)+x(3)+k1)^2-(x(2)+x(4)+k2)^2-(x2^2+y2^2))/(-2*sqrt(x2^2+y2^2)))^2 - x(6)^2; -% .5*hydrophone_base_width^2 - k1^2 - 2*(k1+x(3))*x(3) - 2*k1*x(1) - 4*x(1)*x(3); -% .5*hydrophone_base_width^2 - k2^2 - 2*(k2+x(4))*x(4) - 2*k2*x(2) - 4*x(2)*x(4)]; - - - %cramers rule & determinant of rot matrix is one -% F = @(x) [cos(-heading_change)*(Y(x(1), k1, h1) - (y1 + y2) / 2) + (sin(-heading_change) * (X(x(1), Y(x(1), k1, h1), k1, h1) - (x1 + x2 - hydrophone_base_vector(1)) / 2)) - Y(x(2),k2, h2); -% (X(x(1), Y(x(1), k1, h1), k1, h1) - (x1 + x2 - hydrophone_base_vector(1)) / 2) * cos(-heading_change) - (Y(x(1), k1, h1) - (y1 + y2) / 2) * sin(-heading_change) - (X(x(2),Y(x(2),k2,h2), k2, h2))]; - F = @(x) [cos(-heading_change)*(Y(x(1), K(x(1), k1, dz1_1, dz2_1), h1) - (y1 + y2) / 2) + (sin(-heading_change) * (X(x(1), Y(x(1), K(x(1), k1, dz1_1, dz2_1), h1), K(x(1), k1, dz1_1, dz2_1), h1) - (x1 + x2 - hydrophone_base_vector(1)) / 2)) - Y(x(2),K(x(2), k2, dz1_2, dz2_2), h2); - (X(x(1), Y(x(1), K(x(1), k1, dz1_1, dz2_1), h1), K(x(1), k1, dz1_1, dz2_1), h1) - (x1 + x2 - hydrophone_base_vector(1)) / 2) * cos(-heading_change) - (Y(x(1), K(x(1), k1, dz1_1, dz2_1), h1) - (y1 + y2) / 2) * sin(-heading_change) - (X(x(2),Y(x(2),K(x(2), k2, dz1_2, dz2_2),h2), K(x(2), k2, dz1_2, dz2_2), h2))]; - - %options = optimoptions('fsolve','Display','iter', "OptimalityTolerance", 10e-12); - options = optimoptions('fsolve', 'Display', 'off', "OptimalityTolerance", 10e-6); - - %solve non-linear system - solve1 = fsolve(F, x0, options); - - norm1 = norm(F(solve1)); - answer = solve1; - -end - - -%calculate position - -intersection_x = X(answer(1),Y(answer(1), K(answer(1), k1, dz1_1, dz2_1), h1), K(answer(1), k1, dz1_1, dz2_1), h1) + x1_1; -intersection_y = Y(answer(1), K(answer(1), k1, dz1_1, dz2_1), h1) + y1_1; - -%for quadratic fit -% intersection_x = (-(b2(2) - b1(2)) - sqrt(abs((b2(2) - b1(2))^2 - 4 * (b2(1) - b1(1))*(b2(3) - b1(3))))) / (2 * (b2(3) - b1(3))); -% intersection_y = b1(1) + b1(2)* intersection_x + b1(3) * intersection_x^2; - - -%plots for debug -% figure(1); -% plot(fx1(2,:),fy1, "or", rot_points(1,:), rot_points(2,:),"xg") - -% figure(2) -% w_vals = linspace(-1000,1000, 1000); -% h1_vals = b1(1) + b1(2) * w_vals + b1(3)*w_vals.^2; -% h2_vals = b2(2) * w_vals + b2(1) + b2(3) * w_vals.^2;%b2_intercept ; -% -% plot(w_vals, h1_vals,"r", w_vals, h2_vals, "g", [-1000], [1000], "bo") - - diff --git a/riptide_acoustics/src/Acoustics_Initial_Development/Acoustics/solve_two_pulse_system_depth.m b/riptide_acoustics/src/Acoustics_Initial_Development/Acoustics/solve_two_pulse_system_depth.m deleted file mode 100644 index 81cd8f2..0000000 --- a/riptide_acoustics/src/Acoustics_Initial_Development/Acoustics/solve_two_pulse_system_depth.m +++ /dev/null @@ -1,115 +0,0 @@ -function [intersection_x, intersection_y] = solve_two_pulse_system_depth(k1, k2, pose_1, pose_2, hydrophone_base_vector, estimated_distance, pinger_depth) -%k1: hydrophone 1 pulse time - hydrophone 2 pulse time for first pulse -%k2: hydrophone 1 pulse time - hydrophone 2 pulse time for second pulse -%pose_1: the pose of the front hydrophone at the time of the first pulse -%pose_2: the pose of the front hydrophone at the time of the second pulse -%hydrophone_base_vector: the position vector between hydrophone 1 and 2 -%estimated disance: the estimated distance from the front hydrophone and -%pinger -%pinger_depth: the estimated depth of the pinger - -%pose format = [x,y,z,rw,rx,ry,rz] - -%base frame is hydrophone 1 pulse 1 = [0,0,0,0,0,0] -x1_1 = 0; -y1_1 = 0; -z1_1 = 0; - -pinger_depth = pinger_depth - pose_1(3); - -%calculate position of second hydrophone -%order [Z,Y,X] -eul_1 = quat2eul([pose_1(4), pose_1(5), pose_1(6), pose_1(7)]); -norm_matrix = eul2rotm([-eul_1(1), 0, 0]); -rot_1 = norm_matrix * quat2rotm([pose_1(4), pose_1(5), pose_1(6), pose_1(7)]); -pos2_1 = rot_1 * hydrophone_base_vector; - -x1_2 = pose_2(1) - pose_1(1); -y1_2 = pose_2(2) - pose_1(2); -z1_2 = pose_2(3) - pose_1(3); - -%calculate position of second hydrophone in second pulse -rot_2 = norm_matrix * quat2rotm([pose_2(4), pose_2(5), pose_2(6), pose_2(7)]); -pos2_2 = rot_2 * hydrophone_base_vector + [x1_2;y1_2;z1_2]; - -%base widths in x,y plane -h1 = pos2_1(1); -h2 = norm([pos2_2(1) - x1_2 , pos2_2(2) - y1_2]); - -%calculate delta positions -x1 = x1_2 - x1_1; -x2 = pos2_2(1) - x1_1; - -y1 = y1_2 - y1_1; -y2 = pos2_2(2) - pos2_1(2); - -%changes in depth -dz1_1 = pinger_depth; -dz2_1 = pos2_1(3) + pinger_depth; -dz1_2 = pose_2(3) + pinger_depth; -dz2_2 = pos2_2(3) + pinger_depth; - -%calculate heading change -eul_2 = rotm2eul(rot_2); -heading_change = eul_2(1); - -%define core functions -K = @(a,k_bar,z1,z2) (-(2*a) + sqrt(4*a^2 + 4 * (k_bar^2 + 2 * k_bar * sqrt(a^2 + z1^2) + z1^2 - z2^2))) / 2; -Y = @(a,k,h) sqrt(a^2 + 2*a*k + k^2 - ((h^2 + 2*a*k +k^2)/(2*h))^2); -X = @(a,y,k,h) sqrt(a^2 - y^2) * -sign(k) - h / 2; - -norm1 = 5; -answer = [0;0]; -while(norm1 > .001) - - x0 = [rand() * estimated_distance;rand() * estimated_distance];% [estimated_distance; estimated_distance; estimated_distance; estimated_distance; k1 / 2 ; k2 / 2] * (.2 - rand()*.2); - - %equations of localization -% F = @(x) [sqrt(abs((x(1)+x(3))^2-x(5)^2))*sign((x(1)+x(3))^2-x(5)^2) + sqrt(abs((x(2)+x(4))^2-x(5)^2))*sign((x(1)+x(3))^2-x(5)^2) - sqrt(x1^2+y1^2); -% sqrt(abs((x(1)+x(3)+k1)^2-x(6)^2))*sign((x(1)+x(3))^2-x(5)^2) + sqrt(abs((x(2)+x(4)+k2)^2-x(6)^2))*sign((x(1)+x(3))^2-x(5)^2) - sqrt(x2^2+y2^2); -% (x(2)+x(4))^2 - (((x(1)+x(3))^2-(x(2)+x(4))^2-(x1^2+y1^2))/(-2*sqrt(x1^2+y1^2)))^2 - x(5)^2; -% (x(2)+x(4)+k2)^2 - (((x(1)+x(3)+k1)^2-(x(2)+x(4)+k2)^2-(x2^2+y2^2))/(-2*sqrt(x2^2+y2^2)))^2 - x(6)^2; -% .5*hydrophone_base_width^2 - k1^2 - 2*(k1+x(3))*x(3) - 2*k1*x(1) - 4*x(1)*x(3); -% .5*hydrophone_base_width^2 - k2^2 - 2*(k2+x(4))*x(4) - 2*k2*x(2) - 4*x(2)*x(4)]; - - - %cramers rule & determinant of rot matrix is one -% F = @(x) [cos(-heading_change)*(Y(x(1), k1, h1) - (y1 + y2) / 2) + (sin(-heading_change) * (X(x(1), Y(x(1), k1, h1), k1, h1) - (x1 + x2 - hydrophone_base_vector(1)) / 2)) - Y(x(2),k2, h2); -% (X(x(1), Y(x(1), k1, h1), k1, h1) - (x1 + x2 - hydrophone_base_vector(1)) / 2) * cos(-heading_change) - (Y(x(1), k1, h1) - (y1 + y2) / 2) * sin(-heading_change) - (X(x(2),Y(x(2),k2,h2), k2, h2))]; - F = @(x) [cos(-heading_change)*(Y(x(1), K(x(1), k1, dz1_1, dz2_1), h1) - (y1 + y2) / 2) + (sin(-heading_change) * (X(x(1), Y(x(1), K(x(1), k1, dz1_1, dz2_1), h1), K(x(1), k1, dz1_1, dz2_1), h1) - (x1 + x2 - hydrophone_base_vector(1)) / 2)) - Y(x(2),K(x(2), k2, dz1_2, dz2_2), h2); - (X(x(1), Y(x(1), K(x(1), k1, dz1_1, dz2_1), h1), K(x(1), k1, dz1_1, dz2_1), h1) - (x1 + x2 - hydrophone_base_vector(1)) / 2) * cos(-heading_change) - (Y(x(1), K(x(1), k1, dz1_1, dz2_1), h1) - (y1 + y2) / 2) * sin(-heading_change) - (X(x(2),Y(x(2),K(x(2), k2, dz1_2, dz2_2),h2), K(x(2), k2, dz1_2, dz2_2), h2))]; - - %options = optimoptions('fsolve','Display','iter', "OptimalityTolerance", 10e-12); - options = optimoptions('fsolve', 'Display', 'off', "OptimalityTolerance", 10e-6); - - %solve non-linear system - solve1 = fsolve(F, x0, options); - - norm1 = norm(F(solve1)); - answer = solve1; - -end - - -%calculate position - -intersection_x = X(answer(1),Y(answer(1), K(answer(1), k1, dz1_1, dz2_1), h1), K(answer(1), k1, dz1_1, dz2_1), h1) + pose_1(1); -intersection_y = Y(answer(1), K(answer(1), k1, dz1_1, dz2_1), h1) + pose_1(2); - -%for quadratic fit -% intersection_x = (-(b2(2) - b1(2)) - sqrt(abs((b2(2) - b1(2))^2 - 4 * (b2(1) - b1(1))*(b2(3) - b1(3))))) / (2 * (b2(3) - b1(3))); -% intersection_y = b1(1) + b1(2)* intersection_x + b1(3) * intersection_x^2; - - -%plots for debug -% figure(1); -% plot(fx1(2,:),fy1, "or", rot_points(1,:), rot_points(2,:),"xg") - -% figure(2) -% w_vals = linspace(-1000,1000, 1000); -% h1_vals = b1(1) + b1(2) * w_vals + b1(3)*w_vals.^2; -% h2_vals = b2(2) * w_vals + b2(1) + b2(3) * w_vals.^2;%b2_intercept ; -% -% plot(w_vals, h1_vals,"r", w_vals, h2_vals, "g", [-1000], [1000], "bo") - - diff --git a/riptide_acoustics/src/Acoustics_Initial_Development/Acoustics/transform.m b/riptide_acoustics/src/Acoustics_Initial_Development/Acoustics/transform.m deleted file mode 100644 index 7df340c..0000000 --- a/riptide_acoustics/src/Acoustics_Initial_Development/Acoustics/transform.m +++ /dev/null @@ -1,40 +0,0 @@ -pose_1 = [0,0,0,.9061,-.0747,.1802,.3753]; -pose_2 = [2,0,0,1,0,0,0]; -hydrophone_base_vector = [1;0;0]; - -disp("Point 1") -x1_1 = pose_1(1) -y1_1 = pose_1(2) -z1_1 = pose_1(3) - - -%calculate position of second hydrophone -%order [Z,Y,X] -eul_1 = quat2eul([pose_1(4), pose_1(5), pose_1(6), pose_1(7)]); -norm_matrix = eul2rotm([-eul_1(1), 0, 0]); -rot_1 = norm_matrix * quat2rotm([pose_1(4), pose_1(5), pose_1(6), pose_1(7)]); -pos2_1 = rot_1 * hydrophone_base_vector; - -disp("Disp") -x1_2 = pose_2(1) - x1_1; -y1_2 = pose_2(2) - y1_1; -z1_2 = pose_2(3) - z1_1; - -%calculate position of second hydrophone in second pulse -rot_2 = norm_matrix * quat2rotm([pose_2(4), pose_2(5), pose_2(6), pose_2(7)]); -pos2_2 = rot_2 * hydrophone_base_vector + [x1_2;y1_2;z1_2]; - -%base widths in x,y plane -h1 = pos2_1(1); -h2 = norm([pos2_2(1) - x1_2 , pos2_2(2) - y1_2]); - -%calculate delta positions -x1 = x1_2 - x1_1; -x2 = pos2_2(1) - pos2_1(1); - -y1 = y1_2 - y1_1; -y2 = pos2_2(2) - pos2_1(2); - -%calculate heading change -eul_2 = rotm2eul(rot_2); -heading_change = eul_2(1); \ No newline at end of file diff --git a/riptide_acoustics/src/Acoustics_Initial_Development/Acoustics/two_pulse_depth_test.m b/riptide_acoustics/src/Acoustics_Initial_Development/Acoustics/two_pulse_depth_test.m deleted file mode 100644 index acb581c..0000000 --- a/riptide_acoustics/src/Acoustics_Initial_Development/Acoustics/two_pulse_depth_test.m +++ /dev/null @@ -1,31 +0,0 @@ -%should yeild (52,116.92) -distances = [-20, 41.465]; -pose1 = [0,0,0,1,0,0,0]; -pose2 = [141.89, 22.89,0, .9915,0,0,-.1300]; -pinger_depth = 0; - - -[slv_x,slv_y] = solve_two_pulse_system_depth(distances(1),distances(2),pose1, pose2, [50;0;0], 3000, pinger_depth) - -%should yeild (52,116.92) -distances = [-10.893, 23.1377]; -pose1 = [0,0,0,1,0,0,0]; -pose2 = [141.89, 22.89,0, .9915,0,0,-.1300]; -pinger_depth = -200; - - -[slv_x,slv_y] = solve_two_pulse_system_depth(distances(1),distances(2),pose1, pose2, [50;0;0], 100000, pinger_depth) - - -%should yeild (152,216.92) -distances = [-10.893, 23.1377]; -pose1 = [100,100,0,1,0,0,0]; -pose2 = [241.89, 122.89,0, .9915,0,0,-.1300]; -pinger_depth = -200; - - -[slv_x,slv_y] = solve_two_pulse_system_depth(distances(1),distances(2),pose1, pose2, [50;0;0], 100000, pinger_depth) - -%should yeild (2998.5,95.04) -% distances = [2,19]; -% transform = [-95.04, 8.49, -.38207]; diff --git a/riptide_acoustics/src/Acoustics_Initial_Development/Acoustics/untitled.asv b/riptide_acoustics/src/Acoustics_Initial_Development/Acoustics/untitled.asv deleted file mode 100644 index 05abb62..0000000 --- a/riptide_acoustics/src/Acoustics_Initial_Development/Acoustics/untitled.asv +++ /dev/null @@ -1,33 +0,0 @@ -pose_1 = [0,0,0,.9061,-.0747,.1802,.3753]; -pose_2 = [2,0,0,1,0,0,0]; -hydrophone_base_vector = [1;0;0]; - -x1_1 = pose_1(1); -y1_1 = pose_1(2); -z1_1 = pose_1(3); - -%calculate position of second hydrophone -%order [Z,Y,X] -eul_1 = quat2eul([pose_1(4), pose_1(5), pose_1(6), pose_1(7)]); -norm_matrix = eul2rotm([-eul_1(1), 0, 0]); -rot_1 = norm_matrix * quat2rotm([pose_1(4), pose_1(5), pose_1(6), pose_1(7)]); -pos2_1 = rot_1 * hydrophone_base_vector; - -x1_2 = pose_2(1) - x1_1; -y1_2 = pose_2(2) - y1_1; -z1_2 = pose_2(3) - z1_1; - -%calculate position of second hydrophone in second pulse -rot_2 = norm_matrix * quat2rotm([pose_2(4), pose_2(5), pose_2(6), pose_2(7)]); -pos2_2 = rot_2 * hydrophone_base_vector + [x1_2;y1_2;z1_2]; - -%base widths in x,y plane -h1 = pos2_1(1); -h2 = norm([pos2_2(1) - x1_2 , pos2_2(2) - y1_2]); - -%calculate delta positions -x1 = x2_1 - x1_1; -x2 = pos2_2(1) - pos2_2; - -y1 = y1_2 - y1_1; -y2 = pos2_2(1) - pos2_1() diff --git a/riptide_acoustics/src/Acoustics_Initial_Development/Acoustics/~$Acoustics.SLDPRT b/riptide_acoustics/src/Acoustics_Initial_Development/Acoustics/~$Acoustics.SLDPRT deleted file mode 100644 index 8d5a03ac0924ac5f023a61ccf469ab591ec0e3ad..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6 NcmWG8s7%aZ000OF0tx^C diff --git a/riptide_acoustics/src/UndeterminantMethod/UndeterminantNode.asv b/riptide_acoustics/src/UndeterminantMethod/UndeterminantNode.asv deleted file mode 100644 index 747d94c..0000000 --- a/riptide_acoustics/src/UndeterminantMethod/UndeterminantNode.asv +++ /dev/null @@ -1,159 +0,0 @@ -clear; -clear global; - -%read from config -speed_of_sound = 1500; -pinger_depth = -1.0; - -%initialize node -global node; -node = ros2node("/acoustics_node"); - -%intialize tf2 -global tftree; -tftree = ros2tf(node); - -%initialize transform array -global measurements; - -%get acoustic pod transform -global origin_to_port_transform -global port_to_startboard_transform - -global initial_pinger_estimate -initial_pinger_estimate = [4.0,1.8,-1.1,0.0,0.0,0.0]; - -transforms_recieved = false; -while(transforms_recieved == false) - %loop until transforms become available - - try - origin_to_port_transform = getTransform(tftree, "talos/acoustics_port_link", "talos/origin"); - port_to_startboard_transform = getTransform(tftree, "talos/acoustics_starboard_link", "talos/acoustics_port_link"); - - transforms_recieved = true; - catch - pause(1); - disp("Waiting for acoustic transforms to become available!"); - end -end - - -%initialize publishers and subscribers -pub = ros2publisher(node, "/test_matlab", "std_msgs/UInt8"); - -%add subscriber to add to measurements -delta_t_sub = ros2subscriber(node, "/talos/acoustics/delta_t", @delta_t_callback); - -%add rate function to analyze measurements -r = ros2rate(node, 1); -reset(r) - -x = 0; -estimated_pinger_position = initial_pinger_estimate(1:3)'; -while(x < 1) - estimated_pinger_position = run_analysis(port_to_startboard_transform, origin_to_port_transform, measurements, pinger_depth, estimated_pinger_position, speed_of_sound) - waitfor(r) -end - -function [estimated_pinger_position] = run_analysis(port_to_startboard_transform, origin_to_port_transform, measurements, pinger_depth, pinger_pose, speed_of_sound) - %wait until at least five samples have been taken - points_to_use = 15; - - if(max(size((measurements))) > points_to_use) - disp("Running Analysis") - - %get base vector translation - hydrophone_base_vector = [port_to_startboard_transform.transform.translation.x; port_to_startboard_transform.transform.translation.y; port_to_startboard_transform.transform.translation.z]; - - %origin to port vector - origin_to_port_vector = [origin_to_port_transform.transform.translation.x; origin_to_port_transform.transform.translation.y; origin_to_port_transform.transform.translation.z]; - - %determine the points to use - num_points_saved = max(size(measurements)); - - points_index_array = linspace(num_points_saved - points_to_use + 1, num_points_saved, points_to_use); - counter = points_to_use - 1; - point_seperation = .1; - counter2 = num_points_saved - 1; - while(counter > 0 && counter2 > 0) - - auv_position_test = [measurements(counter2).auv_origin.transform.translation.x;measurements(counter2).auv_origin.transform.translation.y;measurements(counter2).auv_origin.transform.translation.z]; - auv_position_previous = [measurements(counter + 1).auv_origin.transform.translation.x;measurements(counter + 1).auv_origin.transform.translation.y;measurements(counter + 1).auv_origin.transform.translation.z]; - - %if the points are sperated by enough distance - if(norm(auv_position_test - auv_position_previous) > point_seperation) - points_index_array(counter) = counter2; - - counter = counter - 1; - end - - counter2 = counter2 - 1; - end - - %only run if the specified number of points was found - if(counter == 0) - - %assemble poses matrix and delta ds - poses = zeros(7, points_to_use); - delta_ds = zeros(points_to_use, 1); - for counter = 1:points_to_use - auv_position = [measurements(points_index_array(counter)).auv_origin.transform.translation.x;measurements(points_index_array(counter)).auv_origin.transform.translation.y;measurements(points_index_array(counter)).auv_origin.transform.translation.z]; - auv_rotation = [measurements(points_index_array(counter)).auv_origin.transform.rotation.w;measurements(points_index_array(counter)).auv_origin.transform.rotation.x;measurements(points_index_array(counter)).auv_origin.transform.rotation.y;measurements(points_index_array(counter)).auv_origin.transform.rotation.z]; - - %calculate the port hydrophones pose in world frame - poses(:, counter) = [auv_position + quat2rotm(auv_rotation') * origin_to_port_vector; auv_rotation]; - - %convert delta_t to delta ds - delta_ds(counter) = measurements(points_index_array(counter)).delta_t * speed_of_sound; - end - - disp(poses) - disp(delta_ds ) - - estimated_pinger_position = estimate_pinger_position(poses, delta_ds, pinger_pose, hydrophone_base_vector); - %estimated_pinger_position(3) = pinger_depth; - else - estimated_pinger_position = pinger_pose; - - disp("Points to close") - end - else - estimated_pinger_position = pinger_pose; - end - -end - - -function delta_t_callback(message) - %introduce global variables - global tftree; - global measurements; - - try - pause(.1) - - %get the transform at the time of the measurement - map_to_origin_transform = getTransform(tftree, "map", 'talos/origin', message.header.stamp); - - %fill out measurement data struct - measurement = struct(); - measurement.delta_t = message.vector.x; - measurement.frequency = message.vector.y; - measurement.stamp = message.header.stamp; - measurement.auv_origin = map_to_origin_transform; - - if(isempty(measurements)) - measurements = [measurement]; - - else - measurements(end+1) = measurement; - end - - catch - disp("Failed to get transform") - - %tftree.AvailableFrames - end - -end \ No newline at end of file diff --git a/riptide_acoustics/src/UndeterminantMethod/UndeterminantNode.m b/riptide_acoustics/src/UndeterminantMethod/UndeterminantNode.m deleted file mode 100644 index 0b108d0..0000000 --- a/riptide_acoustics/src/UndeterminantMethod/UndeterminantNode.m +++ /dev/null @@ -1,160 +0,0 @@ -clear; -clear global; - -%read from config -speed_of_sound = 1500; -pinger_depth = -1.0; - -%initialize node -global node; -node = ros2node("/acoustics_node"); - -%intialize tf2 -global tftree; -tftree = ros2tf(node); - -%initialize transform array -global measurements; - -%get acoustic pod transform -global origin_to_port_transform -global port_to_startboard_transform - -global initial_pinger_estimate -initial_pinger_estimate = [4.0,1.8,-1.1,0.0,0.0,0.0]; - -transforms_recieved = false; -while(transforms_recieved == false) - %loop until transforms become available - - try - origin_to_port_transform = getTransform(tftree, "talos/origin", "talos/acoustics_port_link"); - port_to_startboard_transform = getTransform(tftree, "talos/acoustics_port_link", "talos/acoustics_starboard_link"); - - transforms_recieved = true; - catch - pause(1); - disp("Waiting for acoustic transforms to become available!"); - end -end - - -%initialize publishers and subscribers -pub = ros2publisher(node, "/test_matlab", "std_msgs/UInt8"); - -%add subscriber to add to measurements -delta_t_sub = ros2subscriber(node, "/talos/acoustics/delta_t", @delta_t_callback); - -%add rate function to analyze measurements -r = ros2rate(node, 100); -reset(r) - -x = 0; -estimated_pinger_position = initial_pinger_estimate(1:3)'; -while(x < 1) - estimated_pinger_position = run_analysis(port_to_startboard_transform, origin_to_port_transform, measurements, pinger_depth, estimated_pinger_position, speed_of_sound) - waitfor(r) -end - -function [estimated_pinger_position] = run_analysis(port_to_startboard_transform, origin_to_port_transform, measurements, pinger_depth, pinger_pose, speed_of_sound) - %wait until at least five samples have been taken - points_to_use = 15; - - if(max(size((measurements))) > points_to_use) - disp("Running Analysis") - - %get base vector translation - hydrophone_base_vector = [port_to_startboard_transform.transform.translation.x; port_to_startboard_transform.transform.translation.y; port_to_startboard_transform.transform.translation.z]; - - %origin to port vector - origin_to_port_vector = [origin_to_port_transform.transform.translation.x; origin_to_port_transform.transform.translation.y; origin_to_port_transform.transform.translation.z]; - - %determine the points to use - num_points_saved = max(size(measurements)); - - points_index_array = linspace(num_points_saved - points_to_use + 1, num_points_saved, points_to_use); - counter = points_to_use - 1; - point_seperation = .1; - counter2 = num_points_saved - 1; - while(counter > 0 && counter2 > 0) - - auv_position_test = [measurements(counter2).auv_origin.transform.translation.x;measurements(counter2).auv_origin.transform.translation.y;measurements(counter2).auv_origin.transform.translation.z]; - auv_position_previous = [measurements(points_index_array(counter + 1)).auv_origin.transform.translation.x;measurements(points_index_array(counter + 1)).auv_origin.transform.translation.y;measurements(points_index_array(counter + 1)).auv_origin.transform.translation.z]; - - %if the points are sperated by enough distance - if(norm(auv_position_test - auv_position_previous) > point_seperation) - - points_index_array(counter) = counter2; - - counter = counter - 1; - end - - counter2 = counter2 - 1; - end - - %only run if the specified number of points was found - if(counter == 0) - - %assemble poses matrix and delta ds - poses = zeros(7, points_to_use); - delta_ds = zeros(points_to_use, 1); - for counter = 1:points_to_use - auv_position = [measurements(points_index_array(counter)).auv_origin.transform.translation.x;measurements(points_index_array(counter)).auv_origin.transform.translation.y;measurements(points_index_array(counter)).auv_origin.transform.translation.z]; - auv_rotation = [measurements(points_index_array(counter)).auv_origin.transform.rotation.w;measurements(points_index_array(counter)).auv_origin.transform.rotation.x;measurements(points_index_array(counter)).auv_origin.transform.rotation.y;measurements(points_index_array(counter)).auv_origin.transform.rotation.z]; - - %calculate the port hydrophones pose in world frame - poses(:, counter) = [auv_position + quat2rotm(auv_rotation') * origin_to_port_vector; auv_rotation]; - - %convert delta_t to delta ds - delta_ds(counter) = measurements(points_index_array(counter)).delta_t * speed_of_sound; - end - - disp(poses) - disp(delta_ds ) - - estimated_pinger_position = estimate_pinger_position(poses, delta_ds, pinger_pose, hydrophone_base_vector); - %estimated_pinger_position(3) = pinger_depth; - else - estimated_pinger_position = pinger_pose; - - disp("Points to close") - end - else - estimated_pinger_position = pinger_pose; - end - -end - - -function delta_t_callback(message) - %introduce global variables - global tftree; - global measurements; - - try - pause(.1) - - %get the transform at the time of the measurement - map_to_origin_transform = getTransform(tftree, "map", 'talos/origin', message.header.stamp); - - %fill out measurement data struct - measurement = struct(); - measurement.delta_t = message.vector.x; - measurement.frequency = message.vector.y; - measurement.stamp = message.header.stamp; - measurement.auv_origin = map_to_origin_transform; - - if(isempty(measurements)) - measurements = [measurement]; - - else - measurements(end+1) = measurement; - end - - catch - disp("Failed to get transform") - - %tftree.AvailableFrames - end - -end \ No newline at end of file diff --git a/riptide_acoustics/src/UndeterminantMethod/calculate_distance_from_solve.asv b/riptide_acoustics/src/UndeterminantMethod/calculate_distance_from_solve.asv deleted file mode 100644 index 7d73835..0000000 --- a/riptide_acoustics/src/UndeterminantMethod/calculate_distance_from_solve.asv +++ /dev/null @@ -1,113 +0,0 @@ -function [test_point, current_distance] = calculate_distance_from_solve(delta_d, characteristic_distance, transformed_point) - %delta_d - distance from hydrophone 2 to pinger - distance from - %hydrophone 1 to pinger - %characteristic distance - (+) distance between the two hydrophones - %hydrophone 1 is assumed to be at (0,0,0) - %transformed point - the point to be evalulated transformed into the - %frame in which h1 is (0,0,0) and h2 is (0, cd, 0) - - convergence_rate = 0.001; - - %calculate function based on delta d and cd - if(delta_d > 0) - y_xz = @(x, z, d, s) (d^3 + sqrt(d^4*s^2 - 2*d^2*s^4 + 4*d^2*s^2*x^2 + 4*d^2*s^2*z^2 + s^6 - 4*s^4*x^2 - 4*s^4*z^2) - d*s^2)/(2*(d^2 - s^2)); - else - y_xz = @(x, z, d, s) (d^3 - sqrt(d^4*s^2 - 2*d^2*s^4 + 4*d^2*s^2*x^2 + 4*d^2*s^2*z^2 + s^6 - 4*s^4*x^2 - 4*s^4*z^2) - d*s^2)/(2*(d^2 - s^2)); - end - - %dz_dx = @(x,y,d,s) (2*s*x) / sqrt(abs(d^4 - 2*d^2*s^2 + s^4 - 4*s^2*x^2 - 4*d^3*y + 4*d*s^2*y + 4*d^2*y^2 - 4*s^2*y^2)) * sign(d^4 - 2*d^2*s^2 + s^4 - 4*s^2*x^2 - 4*d^3*y + 4*d*s^2*y + 4*d^2*y^2 - 4*s^2*y^2); - %dz_dy = @(x,y,d,s) ((d^2 - s^2)*(d - 2*y))/(s*sqrt(abs(d^4 + s^4 - 4*d^3*y + 4*d*s^2*y - 2*d^2*(s^2 - 2*y^2) - 4*s^2*(x^2 + y^2)))) * sign(d^4 + s^4 - 4*d^3*y + 4*d*s^2*y - 2*d^2*(s^2 - 2*y^2) - 4*s^2*(x^2 + y^2)); - distance = @(x,y,z,point) norm(point - [x;y;z]); - %ddist_dx = @(x,y,d,s,point) -((point(1) - x) + (point(3) - z_xy(x,y,d,s)) * dz_dx(x,y,d,s)) / norm(point - [x,y,z_xy(x,y,d,s)]); - %ddist_dy = @(x,y,d,s,point) -((point(2) - y) + (point(3) - z_xy(x,y,d,s)) * dz_dy(x,y,d,s)) / norm(point - [x,y,z_xy(x,y,d,s)]); - - %guess where the cloest point is -- directly up - test_point = [transformed_point(1), y_xz(transformed_point(1), transformed_point(3), characteristic_distance, delta_d), transformed_point(3)]; - - %find the clostest point - counter = 5; - relative_error_threshold = 1e-4; - previous_distance = -1; - convergence_multiplier = .001; - while counter > 0 - %calculate the gradient - - temp_test_1 = test_point(1) - convergence_multiplier * (distance(test_point(1) + convergence_rate, y_xz(convergence_rate + test_point(1), test_point(3), characteristic_distance, delta_d), test_point(3), transformed_point) - distance(test_point(1) - convergence_rate, y_xz(test_point(1) - convergence_rate, test_point(3), characteristic_distance, delta_d), test_point(3), transformed_point)) / (2); - test_point(3) = test_point(3) - convergence_multiplier * (distance(test_point(1), y_xz(test_point(1), test_point(3) + convergence_rate, characteristic_distance, delta_d), test_point(3) + convergence_rate, transformed_point) - distance(test_point(1), y_xz(test_point(1), test_point(3) - convergence_rate, characteristic_distance, delta_d), test_point(3) - convergence_rate, transformed_point)) / (2); - test_point(1) = temp_test_1; - - test_point(2) = y_xz(test_point(1), test_point(3), characteristic_distance, delta_d); - - current_distance = distance(test_point(1), y_xz(test_point(1), test_point(3), characteristic_distance, delta_d), test_point(3), transformed_point); - - if(previous_distance - relative_error_threshold < current_distance) - counter = counter - 1; - else - counter = 5; - end - - if(previous_distance == -1 || previous_distance > current_distance) - previous_distance = current_distance; - else - convergence_multiplier = convergence_multiplier / 2; - end - - end - - %calculate the vector between the closest point and the guessed - %vector = test_point - transformed_point; - % - % gradient = [1 / ddist_dx(test_point(1), test_point(1), characteristic_distance, delta_d, transformed_point), 1 / ddist_dy(test_point(1), test_point(1), characteristic_distance, delta_d, transformed_point), 1] - % - % ddist_dx(-1, 1, characteristic_distance, delta_d, transformed_point) - % ddist_dy(-1, -1, characteristic_distance, delta_d, transformed_point) - % - % plotting vals for surface - - %UNCOMMENT FOR DEBUG - num_x = 200; - num_z = 100; - x_vals = linspace(-abs(test_point(1)) - .5, abs(test_point(1)) + .5, num_x); - z_vals = linspace(-abs(test_point(2) - .5), abs(test_point(2)) + .5, 100); - x_mat = zeros(1, num_x * 100); - z_mat = zeros(1, num_x * 100); - y_mat = zeros(1, num_x * 100); - dist_mat = zeros(1, num_x*100); - - for xi = 0:(num_x - 1) - for yi = 1:num_z - x_mat(1, xi * num_z + yi) = x_vals(xi + 1); - _mat(1, xi * num_y + yi) = y_vals(yi); - - y_mat(1, xi * num_z + yi) = y_xz(x_vals(xi + 1), z_vals(yi), characteristic_distance, delta_d); - dist_mat(1, xi * num_z + yi) = norm(transformed_point - [x_mat(1, xi * num_z + yi); z_mat(1, xi * num_z + yi); y_mat(1, xi * num_z + yi)]); - end - end - - figure; - hold on - plot3([0,0], [0,characteristic_distance], [0,0], "*r") - plot3(x_mat, z_mat, y_mat, ".b") - plot3(test_point(1), test_point(2), test_point(3), "*g") - plot3(transformed_point(1), transformed_point(2), transformed_point(3), "*g") - title("Calculate Distance Visualization") - subtitle_string = "Delta d: " + num2str(delta_d); - subtitle(subtitle_string); - xlabel("X") - ylabel("Y") - zlabel("Z") - hold off - - figure; - hold on - plot3(x_mat, z_mat, dist_mat, ".r") - plot3(test_point(1), test_point(2), distance(test_point(1), test_point(2), test_point(3), transformed_point), "*b") - title("Distance from pinger point") - subtitle_string = "Norm Distance: " + num2str(distance(test_point(1), test_point(2), test_point(3), transformed_point)); - subtitle(subtitle_string); - xlabel("X") - ylabel("Y") - zlabel("Dist") - hold off - -end diff --git a/riptide_acoustics/src/UndeterminantMethod/calculate_distance_from_solve.m b/riptide_acoustics/src/UndeterminantMethod/calculate_distance_from_solve.m deleted file mode 100644 index 241a5c6..0000000 --- a/riptide_acoustics/src/UndeterminantMethod/calculate_distance_from_solve.m +++ /dev/null @@ -1,113 +0,0 @@ -function [test_point, current_distance] = calculate_distance_from_solve(delta_d, characteristic_distance, transformed_point) - %delta_d - distance from hydrophone 2 to pinger - distance from - %hydrophone 1 to pinger - %characteristic distance - (+) distance between the two hydrophones - %hydrophone 1 is assumed to be at (0,0,0) - %transformed point - the point to be evalulated transformed into the - %frame in which h1 is (0,0,0) and h2 is (0, cd, 0) - - convergence_rate = 0.001; - - %calculate function based on delta d and cd - if(delta_d > 0) - y_xz = @(x, z, d, s) (d^3 + sqrt(d^4*s^2 - 2*d^2*s^4 + 4*d^2*s^2*x^2 + 4*d^2*s^2*z^2 + s^6 - 4*s^4*x^2 - 4*s^4*z^2) - d*s^2)/(2*(d^2 - s^2)); - else - y_xz = @(x, z, d, s) (d^3 - sqrt(d^4*s^2 - 2*d^2*s^4 + 4*d^2*s^2*x^2 + 4*d^2*s^2*z^2 + s^6 - 4*s^4*x^2 - 4*s^4*z^2) - d*s^2)/(2*(d^2 - s^2)); - end - - %dz_dx = @(x,y,d,s) (2*s*x) / sqrt(abs(d^4 - 2*d^2*s^2 + s^4 - 4*s^2*x^2 - 4*d^3*y + 4*d*s^2*y + 4*d^2*y^2 - 4*s^2*y^2)) * sign(d^4 - 2*d^2*s^2 + s^4 - 4*s^2*x^2 - 4*d^3*y + 4*d*s^2*y + 4*d^2*y^2 - 4*s^2*y^2); - %dz_dy = @(x,y,d,s) ((d^2 - s^2)*(d - 2*y))/(s*sqrt(abs(d^4 + s^4 - 4*d^3*y + 4*d*s^2*y - 2*d^2*(s^2 - 2*y^2) - 4*s^2*(x^2 + y^2)))) * sign(d^4 + s^4 - 4*d^3*y + 4*d*s^2*y - 2*d^2*(s^2 - 2*y^2) - 4*s^2*(x^2 + y^2)); - distance = @(x,y,z,point) norm(point - [x;y;z]); - %ddist_dx = @(x,y,d,s,point) -((point(1) - x) + (point(3) - z_xy(x,y,d,s)) * dz_dx(x,y,d,s)) / norm(point - [x,y,z_xy(x,y,d,s)]); - %ddist_dy = @(x,y,d,s,point) -((point(2) - y) + (point(3) - z_xy(x,y,d,s)) * dz_dy(x,y,d,s)) / norm(point - [x,y,z_xy(x,y,d,s)]); - - %guess where the cloest point is -- directly up - test_point = [transformed_point(1), y_xz(transformed_point(1), transformed_point(3), characteristic_distance, delta_d), transformed_point(3)]; - - %find the clostest point - counter = 5; - relative_error_threshold = 1e-8; - previous_distance = -1; - convergence_multiplier = .1; - while counter > 0 - %calculate the gradient - - temp_test_1 = test_point(1) - convergence_multiplier * (distance(test_point(1) + convergence_rate, y_xz(convergence_rate + test_point(1), test_point(3), characteristic_distance, delta_d), test_point(3), transformed_point) - distance(test_point(1) - convergence_rate, y_xz(test_point(1) - convergence_rate, test_point(3), characteristic_distance, delta_d), test_point(3), transformed_point)) / (2); - test_point(3) = test_point(3) - convergence_multiplier * (distance(test_point(1), y_xz(test_point(1), test_point(3) + convergence_rate, characteristic_distance, delta_d), test_point(3) + convergence_rate, transformed_point) - distance(test_point(1), y_xz(test_point(1), test_point(3) - convergence_rate, characteristic_distance, delta_d), test_point(3) - convergence_rate, transformed_point)) / (2); - test_point(1) = temp_test_1; - - test_point(2) = y_xz(test_point(1), test_point(3), characteristic_distance, delta_d); - - current_distance = distance(test_point(1), y_xz(test_point(1), test_point(3), characteristic_distance, delta_d), test_point(3), transformed_point); - - if(previous_distance - relative_error_threshold < current_distance) - counter = counter - 1; - else - counter = 5; - end - - if(previous_distance == -1 || previous_distance > current_distance) - previous_distance = current_distance; - else - convergence_multiplier = convergence_multiplier / 2; - end - - end - - %calculate the vector between the closest point and the guessed - %vector = test_point - transformed_point; - % - % gradient = [1 / ddist_dx(test_point(1), test_point(1), characteristic_distance, delta_d, transformed_point), 1 / ddist_dy(test_point(1), test_point(1), characteristic_distance, delta_d, transformed_point), 1] - % - % ddist_dx(-1, 1, characteristic_distance, delta_d, transformed_point) - % ddist_dy(-1, -1, characteristic_distance, delta_d, transformed_point) - % - % plotting vals for surface - - %UNCOMMENT FOR DEBUG - % num_x = 200; - % num_z = 100; - % x_vals = linspace(-abs(test_point(1)) - .5, abs(test_point(1)) + .5, num_x); - % z_vals = linspace(-abs(test_point(2) - .5), abs(test_point(2)) + .5, 100); - % x_mat = zeros(1, num_x * 100); - % z_mat = zeros(1, num_x * 100); - % y_mat = zeros(1, num_x * 100); - % dist_mat = zeros(1, num_x*100); - % - % for xi = 0:(num_x - 1) - % for zi = 1:num_z - % x_mat(1, xi * num_z + zi) = x_vals(xi + 1); - % z_mat(1, xi * num_z + zi) = z_vals(zi); - % - % y_mat(1, xi * num_z + zi) = y_xz(x_vals(xi + 1), z_vals(zi), characteristic_distance, delta_d); - % dist_mat(1, xi * num_z + zi) = norm(transformed_point - [x_mat(1, xi * num_z + zi); y_mat(1, xi * num_z + zi); z_mat(1, xi * num_z + zi);]); - % end - % end - % - % figure; - % hold on - % plot3([0,0], [0,characteristic_distance], [0,0], "*r") - % plot3(x_mat, y_mat, z_mat, ".b") - % plot3(test_point(1), test_point(2), test_point(3), "*g") - % plot3(transformed_point(1), transformed_point(2), transformed_point(3), "*g") - % title("Calculate Distance Visualization") - % subtitle_string = "Delta d: " + num2str(delta_d); - % subtitle(subtitle_string); - % xlabel("X") - % ylabel("Y") - % zlabel("Z") - % hold off - % - % figure; - % hold on - % plot3(x_mat, z_mat, dist_mat, ".r") - % plot3(test_point(1), test_point(3), distance(test_point(1), test_point(2), test_point(3), transformed_point), "*b") - % title("Distance from pinger point") - % subtitle_string = "Norm Distance: " + num2str(distance(test_point(1), test_point(2), test_point(3), transformed_point)); - % subtitle(subtitle_string); - % xlabel("X") - % ylabel("Z") - % zlabel("Dist") - % hold off - -end diff --git a/riptide_acoustics/src/UndeterminantMethod/estimate_pinger_position.asv b/riptide_acoustics/src/UndeterminantMethod/estimate_pinger_position.asv deleted file mode 100644 index 6cbf68f..0000000 --- a/riptide_acoustics/src/UndeterminantMethod/estimate_pinger_position.asv +++ /dev/null @@ -1,64 +0,0 @@ -function estimate = estimate_pinger_position(poses, delta_s, previous_estimate, hydrophone_base_vector) - - %transform so that the hydrophone base vector is parallel to the y axis - characteristic_distance = norm(hydrophone_base_vector); - - %from transform to positive y - unit_base_vector = hydrophone_base_vector / norm(hydrophone_base_vector); - cos_transform = sum(unit_base_vector .* [0,1,0]); - cross_transform = cross(unit_base_vector, [0,1,0]); - skew_sym_cross = [0, -cross_transform(3), cross_transform(2); - cross_transform(3), 0, -cross_transform(1); - -cross_transform(2), cross_transform(1),0]; - base_vector_rotm = eye(3) + skew_sym_cross + skew_sym_cross^2 / (1 + cos_transform); - - num_points = length(poses(1,:)); - - points = zeros(3, num_points); - - %weght function - weight_func = @(n) 1; - weights = zeros(1, num_points); - previous_weight = .01; - - - for counter = 1:num_points - - %from ([0,0,0,0,0,0,0]) to pose - rotm = quat2rotm(poses(4:7, counter)'); - transform = poses(1:3, counter); - - %transform so the origin is at ([0,0,0,0,0,0,0]) - previous_point_transformed = base_vector_rotm * rotm' * (previous_estimate - transform) - - [closest_point, dist] = calculate_distance_from_solve(delta_s(counter), characteristic_distance, previous_point_transformed); - closest_point = closest_point'; - points(:,counter) = (rotm * base_vector_rotm' * closest_point) + transform; - - weights(counter) = weight_func(dist); - end - - %find the center "mean" of all of the points - center_point = sum(points, 2) / num_points; - - %calculate the standard deviation (standard distance from the center) - sum_of_distance2 = 0; - distances_from_center = zeros(num_points, 1); - for counter = 1:num_points - distances_from_center(counter) = norm(center_point - points(:,counter)); - sum_of_distance2 = sum_of_distance2 + distances_from_center(counter)^2; - end - std_dev_from_center = sqrt(sum_of_distance2 / (num_points - 1)); - - %if greater than 1 std dev - do not include in estimate - weights = weights .* (std_dev_from_center > distances_from_center)'; - - % %plot points - % figure; - % plot3(points(1,:), points(2,:), points(3,:), "b*") - - - %find center of all points - estimate = (points * weights' + (previous_estimate) * previous_weight) / (sum(weights) + previous_weight); - -end \ No newline at end of file diff --git a/riptide_acoustics/src/UndeterminantMethod/estimate_pinger_position.m b/riptide_acoustics/src/UndeterminantMethod/estimate_pinger_position.m deleted file mode 100644 index b4573d3..0000000 --- a/riptide_acoustics/src/UndeterminantMethod/estimate_pinger_position.m +++ /dev/null @@ -1,64 +0,0 @@ -function estimate = estimate_pinger_position(poses, delta_s, previous_estimate, hydrophone_base_vector) - - %transform so that the hydrophone base vector is parallel to the y axis - characteristic_distance = norm(hydrophone_base_vector); - - %from transform to positive y - unit_base_vector = hydrophone_base_vector / norm(hydrophone_base_vector); - cos_transform = sum(unit_base_vector .* [0;1;0]); - cross_transform = cross(unit_base_vector, [0;1;0]); - skew_sym_cross = [0, -cross_transform(3), cross_transform(2); - cross_transform(3), 0, -cross_transform(1); - -cross_transform(2), cross_transform(1),0]; - base_vector_rotm = eye(3) + skew_sym_cross + skew_sym_cross^2 / (1 + cos_transform); - - num_points = length(poses(1,:)); - - points = zeros(3, num_points); - - %weght function - weight_func = @(n) 1; - weights = zeros(1, num_points); - previous_weight = .01; - - for counter = 1:num_points - - %from ([0,0,0,0,0,0,0]) to pose - rotm = quat2rotm(poses(4:7, counter)'); - transform = poses(1:3, counter); - - %transform so the origin is at ([0,0,0,0,0,0,0]) - previous_point_transformed = base_vector_rotm * rotm' * (previous_estimate - transform); - - [closest_point, dist] = calculate_distance_from_solve(delta_s(counter), characteristic_distance, previous_point_transformed); - closest_point = closest_point'; - points(:,counter) = (rotm * base_vector_rotm' * closest_point) + transform; - - weights(counter) = weight_func(dist); - end - - %find the center "mean" of all of the points - center_point = sum(points, 2) / num_points; - - %calculate the standard deviation (standard distance from the center) - sum_of_distance2 = 0; - distances_from_center = zeros(num_points, 1); - for counter = 1:num_points - distances_from_center(counter) = norm(center_point - points(:,counter)); - sum_of_distance2 = sum_of_distance2 + distances_from_center(counter)^2; - end - std_dev_from_center = sqrt(sum_of_distance2 / (num_points - 1)); - - %if greater than 1 std dev - do not include in estimate - % weights = weights .* (std_dev_from_center > distances_from_center)'; - - %find center of all points - estimate = (points * weights' + (previous_estimate) * previous_weight) / (sum(weights) + previous_weight); - - % %plot points - figure(1); - plot3(points(1,:), points(2,:), points(3,:), "b*", previous_estimate(1), previous_estimate(2), previous_estimate(3), "xr", estimate(1), estimate(2), estimate(3), "gx") - - - -end \ No newline at end of file diff --git a/riptide_acoustics/src/UndeterminantMethod/test1.csv b/riptide_acoustics/src/UndeterminantMethod/test1.csv deleted file mode 100644 index 2dc4bee..0000000 --- a/riptide_acoustics/src/UndeterminantMethod/test1.csv +++ /dev/null @@ -1,36 +0,0 @@ -number,time,delta_t,pos x 1,pos y 1,pos z 1,or w 1,or x 1,or y 1, or z 1,pos x 2,pos y 2,pos z 2,or w 2,or x 2,or y 2, or z 2 -1,1730602076.9605615,-0.00023910690471704902,0.17477051774202157,0.35526724639887375,0.9858247081319208,0.9999991883481831,-0.00030941976696492756,-0.00020121886321131685,0.0012194561705773605,0.17477051774202157,0.35526724639887375,0.9858247081319208,0.9999991883481831,-0.00030941976696492756,-0.00020121886321131685,0.0012194561705773605 -2,1730602078.9667318,-0.00023958979555123308,0.17345860967021187,0.3540682687286766,0.9809740862699532,0.9999987397548187,0.0004230851023879771,-0.000623163570459755,0.001397553195792181,0.17345860967021187,0.3540682687286766,0.9809740862699532,0.9999987397548187,0.0004230851023879771,-0.000623163570459755,0.001397553195792181 -3,1730602080.966533,-0.0002388495874544354,0.17483717066185328,0.355927146343053,0.9802572441585551,0.9999998968037208,-0.0004416842331025506,-0.00010073782498966525,3.4051086857910075e-05,0.17483717066185328,0.355927146343053,0.9802572441585551,0.9999998968037208,-0.0004416842331025506,-0.00010073782498966525,3.4051086857910075e-05 -4,1730602082.9712355,-0.00023941257498223103,0.17516954811031324,0.3541027266861226,0.9837919382535902,0.9999992478076677,0.00024717197072335225,3.100796699418819e-05,0.0012009698671241584,0.17516954811031324,0.3541027266861226,0.9837919382535902,0.9999992478076677,0.00024717197072335225,3.100796699418819e-05,0.0012009698671241584 -5,1730602084.9746716,-0.00023863820708125842,0.1750414084933162,0.35435604910995305,0.9840576722390639,0.9999999636524994,0.0001629940342028921,0.00010198172933705589,0.00018901764866265314,0.1750414084933162,0.35435604910995305,0.9840576722390639,0.9999999636524994,0.0001629940342028921,0.00010198172933705589,0.00018901764866265314 -6,1730602086.9681497,-0.00023915745126083633,0.17411772135577563,0.35464822014067027,0.9812986791837663,0.9999998353590444,-0.0001276187939803107,-0.0004907159973617589,0.0002686878069568441,0.17411772135577563,0.35464822014067027,0.9812986791837663,0.9999998353590444,-0.0001276187939803107,-0.0004907159973617589,0.0002686878069568441 -7,1730602088.9683607,-0.000238833529583436,0.17528023664455078,0.355235584416919,0.9831253755052829,0.9999999444069843,-0.00031540040670268826,-2.1628301252419562e-05,0.00010602277358149713,0.17528023664455078,0.355235584416919,0.9831253755052829,0.9999999444069843,-0.00031540040670268826,-2.1628301252419562e-05,0.00010602277358149713 -8,1730602090.9690344,-0.00023941608178562894,0.17526646836184295,0.35504694742199533,0.9849703278747103,0.9999992200511325,-0.0001414348480131425,-1.745818256528908e-05,0.0012408015644250602,0.17526646836184295,0.35504694742199533,0.9849703278747103,0.9999992200511325,-0.0001414348480131425,-1.745818256528908e-05,0.0012408015644250602 -9,1730602092.9703708,-0.0002394951921362415,0.17535911628723994,0.35459707221606707,0.9814910289161214,0.9999995501224405,0.0003087854734764475,0.0003472709585601525,0.0008269276445910311,0.17535911628723994,0.35459707221606707,0.9814910289161214,0.9999995501224405,0.0003087854734764475,0.0003472709585601525,0.0008269276445910311 -10,1730602094.9712648,-0.00023878828913218028,0.17490437564955694,0.3550755843765803,0.9805177758772854,0.9999998242940161,0.00012321582331858337,0.000125810530991369,0.0005660402000451104,0.17490437564955694,0.3550755843765803,0.9805177758772854,0.9999998242940161,0.00012321582331858337,0.000125810530991369,0.0005660402000451104 -11,1730602096.9651887,-0.0002389983471739289,0.17449848606204946,0.35578668260527746,0.9835136365982081,0.9999999452558151,-0.00026026695847683305,-0.00018312050417430666,-9.064412859775001e-05,0.17449848606204946,0.35578668260527746,0.9835136365982081,0.9999999452558151,-0.00026026695847683305,-0.00018312050417430666,-9.064412859775001e-05 -12,1730602098.9677374,-0.00023861968000385019,0.17523672558745987,0.3542890610484866,0.9872715425345799,0.9999997925733386,0.0003680067308857633,0.00019624886287545254,0.0004908265574917519,0.17523672558745987,0.3542890610484866,0.9872715425345799,0.9999997925733386,0.0003680067308857633,0.00019624886287545254,0.0004908265574917519 -13,1730602100.9710946,-0.00023890035560134018,0.17521591305199868,0.3546581917782677,0.9873188151868755,0.999999964264311,0.00016065918217922096,9.917171700903727e-06,-0.00021345175959599087,0.17521591305199868,0.3546581917782677,0.9873188151868755,0.999999964264311,0.00016065918217922096,9.917171700903727e-06,-0.00021345175959599087 -14,1730602102.9674559,-0.0002387520010607984,0.17507985614059032,0.3552776755797112,0.9838027042421368,0.9999999812267832,4.896899347367777e-05,0.00018236369435959004,4.34965953875846e-05,0.17507985614059032,0.3552776755797112,0.9838027042421368,0.9999999812267832,4.896899347367777e-05,0.00018236369435959004,4.34965953875846e-05 -15,1730602104.970081,-0.00023947379821559435,0.1737173275434046,0.3547664759948673,0.9850441483874908,0.9999988684000727,0.00020712660609594123,-0.0005733659981797373,0.0013753358045998938,0.1737173275434046,0.3547664759948673,0.9850441483874908,0.9999988684000727,0.00020712660609594123,-0.0005733659981797373,0.0013753358045998938 -16,1730602106.9679987,-0.00023852933988669989,0.17459786558139967,0.3549929292112845,0.9884943087971568,0.9999999672179588,2.8991724937339898e-05,-9.423100851987381e-05,-0.00023631351690418302,0.17459786558139967,0.3549929292112845,0.9884943087971568,0.9999999672179588,2.8991724937339898e-05,-9.423100851987381e-05,-0.00023631351690418302 -17,1730602108.9659603,-0.00023950819189385826,0.17431947329991643,0.35415530579555077,0.9845299433797811,0.9999993917169818,0.0001590372031228914,-0.00017590365806280728,0.0010771864915208517,0.17431947329991643,0.35415530579555077,0.9845299433797811,0.9999993917169818,0.0001590372031228914,-0.00017590365806280728,0.0010771864915208517 -18,1730602110.9644763,-0.00023966226061829322,0.17447077236287376,0.3559662721175278,0.9805980835420093,0.9999994280550956,-0.0005657306726253614,-0.00021435588225217816,0.0008819806367582533,0.17447077236287376,0.3559662721175278,0.9805980835420093,0.9999994280550956,-0.0005657306726253614,-0.00021435588225217816,0.0008819806367582533 -19,1730602112.971374,-0.0002446875439173555,0.0660843333711714,0.35505035955396336,0.9858507478343906,0.9999661777305295,7.720115837767317e-05,0.008213709164312412,0.000415230948086814,0.0660843333711714,0.35505035955396336,0.9858507478343906,0.9999661777305295,7.720115837767317e-05,0.008213709164312412,0.000415230948086814 -20,1730602114.9733787,-0.0002791140166842917,-0.4470946479847955,0.3540909917006062,1.0447844146760172,0.9989695117241044,0.00046144519347156526,0.04538345705544062,-0.00020866177102560747,-0.4470946479847955,0.3540909917006062,1.0447844146760172,0.9989695117241044,0.00046144519347156526,0.04538345705544062,-0.00020866177102560747 -21,1730602116.9665315,-0.0003162599332602687,-0.970256509581909,0.35373680136875885,1.0737710531423812,0.9992769090275209,-0.0002830823545975063,0.038009351338723546,0.0009317508217785118,-0.970256509581909,0.35373680136875885,1.0737710531423812,0.9992769090275209,-0.0002830823545975063,0.038009351338723546,0.0009317508217785118 -22,1730602118.9730878,-0.00033399703872171924,-1.207711916673252,0.44408844012201143,0.9828208566831272,0.9999936115663932,0.00317344884574182,-0.0016450072348369801,1.4959623928805418e-07,-1.207711916673252,0.44408844012201143,0.9828208566831272,0.9999936115663932,0.00317344884574182,-0.0016450072348369801,1.4959623928805418e-07 -23,1730602120.9634225,-0.0003691215677558217,-1.2853769922182812,1.0229603132549545,1.0009177405545453,0.999653578806899,0.025418360504437628,-0.006801685283811873,0.000605314071749189,-1.2853769922182812,1.0229603132549545,1.0009177405545453,0.999653578806899,0.025418360504437628,-0.006801685283811873,0.000605314071749189 -24,1730602122.9660347,-0.0003943946164293711,-1.3121946199196712,1.9234540408930478,1.1753650518091148,0.9982185728088924,0.059647213320038986,-0.001089718128976824,0.0008386637771986282,-1.3121946199196712,1.9234540408930478,1.1753650518091148,0.9982185728088924,0.059647213320038986,-0.001089718128976824,0.0008386637771986282 -25,1730602124.9647522,-0.0004037945431972722,-1.3458575233570151,2.406868512294075,1.212384835090746,0.9985968204017377,0.052918589400108684,0.0019773293704657934,0.0003214778425376011,-1.3458575233570151,2.406868512294075,1.212384835090746,0.9985968204017377,0.052918589400108684,0.0019773293704657934,0.0003214778425376011 -26,1730602126.967966,-0.00040708906153200884,-1.3584983879785515,2.573448252179173,1.1412939686450956,0.9994247287615419,0.03380745614661589,0.0020477552198020408,0.0017533246627500847,-1.3584983879785515,2.573448252179173,1.1412939686450956,0.9994247287615419,0.03380745614661589,0.0020477552198020408,0.0017533246627500847 -27,1730602128.971708,-0.00040806074219133727,-1.357064939791769,2.6647756786761647,1.08430485058301,0.9997822299691196,0.02077565107880742,0.001606737658886589,0.0011328522946685569,-1.357064939791769,2.6647756786761647,1.08430485058301,0.9997822299691196,0.02077565107880742,0.001606737658886589,0.0011328522946685569 -28,1730602130.9707863,-0.0004084979416342751,-1.3594705378747984,2.67836055534775,1.0353544663475671,0.9999404779925178,0.010781252924496729,0.000997760847145472,0.001345188003558663,-1.3594705378747984,2.67836055534775,1.0353544663475671,0.9999404779925178,0.010781252924496729,0.000997760847145472,0.001345188003558663 -29,1730602132.9623039,-0.0004083861324080657,-1.361951045196252,2.6906735546043663,1.0052461740355498,0.9999889658369797,0.004368917254852986,0.0004098944772908693,0.0016771263594692598,-1.361951045196252,2.6906735546043663,1.0052461740355498,0.9999889658369797,0.004368917254852986,0.0004098944772908693,0.0016771263594692598 -30,1730602134.971606,-0.0004082316128764602,-1.3577426907942822,2.700422672135693,0.9937708072419184,0.999998474271318,0.0016746051779782476,-0.00023254585456555367,0.00043940295801199545,-1.3577426907942822,2.700422672135693,0.9937708072419184,0.999998474271318,0.0016746051779782476,-0.00023254585456555367,0.00043940295801199545 -31,1730602136.9728036,-0.00040842323525591576,-1.3607768526676776,2.701118891804274,0.9867706461716464,0.9999992907387164,0.00029356118045768993,-6.184035214498375e-05,0.0011526142756239455,-1.3607768526676776,2.701118891804274,0.9867706461716464,0.9999992907387164,0.00029356118045768993,-6.184035214498375e-05,0.0011526142756239455 -32,1730602138.9738753,-0.0004080408535634715,-1.357350547391125,2.705735956868999,0.9784954488826209,0.9999996625972498,-0.0007154139937707934,-0.00010539908144492453,0.0003897168686109271,-1.357350547391125,2.705735956868999,0.9784954488826209,0.9999996625972498,-0.0007154139937707934,-0.00010539908144492453,0.0003897168686109271 -33,1730602140.9710107,-0.000408351601845452,-1.3581793011583123,2.7054241450450336,0.9759648420020786,0.9999993148573082,-0.0009650002900233382,-0.00015671634685985163,0.0006438162324049681,-1.3581793011583123,2.7054241450450336,0.9759648420020786,0.9999993148573082,-0.0009650002900233382,-0.00015671634685985163,0.0006438162324049681 -34,1730602142.9670005,-0.00040799146180058976,-1.3566049885819054,2.70583219114272,0.9772227938453851,0.999999611225209,-0.0007839807623736368,-0.0002887316346915098,0.00028205963608478595,-1.3566049885819054,2.70583219114272,0.9772227938453851,0.999999611225209,-0.0007839807623736368,-0.0002887316346915098,0.00028205963608478595 -35,1730602144.9683554,-0.000408348066079542,-1.3599165351918545,2.7027164519321696,0.9827807870870706,0.9999981139629581,-0.001220450448648613,0.0006018994470167279,0.0013857446679392607,-1.3599165351918545,2.7027164519321696,0.9827807870870706,0.9999981139629581,-0.001220450448648613,0.0006018994470167279,0.0013857446679392607 diff --git a/riptide_acoustics/src/UndeterminantMethod/test3.csv b/riptide_acoustics/src/UndeterminantMethod/test3.csv deleted file mode 100644 index df4dcc3..0000000 --- a/riptide_acoustics/src/UndeterminantMethod/test3.csv +++ /dev/null @@ -1,20 +0,0 @@ -number,time,delta_t,pos x 1,pos y 1,pos z 1,or w 1,or x 1,or y 1, or z 1,pos x 2,pos y 2,pos z 2,or w 2,or x 2,or y 2, or z 2 -1,1730606579.9802365,-0.0001253174677029977,-0.4163459671747822,0.5968035655233543,-0.13009170754716787,0.9996797023652668,0.006461941330008329,-0.023450988273185154,0.006984779320058735,-0.4156322472233951,1.2468224786111097,-0.12143704221311552,0.9996797023652668,0.006461941330008329,-0.023450988273185154,0.006984779320058735 -2,1730606581.9776223,-0.0001251644840117147,-0.4179850986623722,0.5967806670908133,-0.13012625665880345,0.9996800541537193,0.006533187693569448,-0.023409361541509927,0.007007751279572117,-0.4173030218089022,1.2467983900640658,-0.12138013798623497,0.9996800541537193,0.006533187693569448,-0.023409361541509927,0.007007751279572117 -3,1730606583.9795377,-0.00012521914510405066,-0.419616432675359,0.5967542522988211,-0.13032485875904767,0.9996780936847931,0.006813940737221492,-0.02340461501514544,0.007035852070671016,-0.41897936255841783,1.2467670215857867,-0.1212147596620875,0.9996780936847931,0.006813940737221492,-0.02340461501514544,0.007035852070671016 -4,1730606585.9722495,-0.00012510701653001928,-0.42124948425873543,0.5967287047536821,-0.13048799891217536,0.9996762942223656,0.007044266792109477,-0.023405168586172812,0.007062801048650141,-0.42065444110051303,1.2467372612949499,-0.12107936740820008,0.9996762942223656,0.007044266792109477,-0.023405168586172812,0.007062801048650141 -5,1730606587.972156,-0.00012510791402371317,-0.4229104169024931,0.5967186313880065,-0.13015997228803516,0.9996761234004469,0.006507179903331045,-0.02356441523833557,0.007072711448619975,-0.42231340924855504,1.2467368964166898,-0.12144798566263243,0.9996761234004469,0.006507179903331045,-0.02356441523833557,0.007072711448619975 -6,1730606589.9793305,-0.00012573835113941368,-0.3930678306723455,0.6038586364495109,-0.40652970499988694,0.9673025514229892,-0.0011557015035376628,0.25349261133366585,0.00811999777396901,-0.39494601270290997,1.253922416092197,-0.41021139332525924,0.9673025514229892,-0.0011557015035376628,0.25349261133366585,0.00811999777396901 -7,1730606591.9747446,-0.00014077767268860195,-0.27405960492570663,0.6105285826999282,-1.0007874319387178,0.9975713120363877,-0.0035568941513799248,0.06956153817234788,0.00013532587610615377,-0.2646535277559122,1.2605098634317726,-1.0067757881796024,0.9975713120363877,-0.0035568941513799248,0.06956153817234788,0.00013532587610615377 -8,1730606593.9788249,-0.00013911462769748914,-0.2032590714666731,0.6031998879739695,-0.9892140137723926,0.9998485061214814,-0.003932283808597593,0.016581788756566757,0.0035420661093946383,-0.19795357545544573,1.2532330026633849,-0.9945807184645078,0.9998485061214814,-0.003932283808597593,0.016581788756566757,0.0035420661093946383 -9,1730606595.9784057,-0.00015421170787955832,-0.16205597469502714,0.6031119269128142,-1.0316036576784025,0.999908936205534,-0.004634736641643484,0.0010544108170844532,-0.012630389162994879,-0.13564752976728106,1.2526239346262584,-1.037665495068887,0.999908936205534,-0.004634736641643484,0.0010544108170844532,-0.012630389162994879 -10,1730606597.9731364,-0.0001507145668751925,-0.012256016907346567,0.6049394091183984,-1.009169753056389,0.9999446132882163,-0.0038816473466427898,-0.009662977322558004,0.001526446533536237,-0.004193440804658083,1.2549480701308915,-1.0140416598016482,0.9999446132882163,-0.0038816473466427898,-0.009662977322558004,0.001526446533536237 -11,1730606599.97842,-0.00017441549370768663,0.5769791722630976,0.6077721500419295,-0.9892138874802936,0.9987557956860734,-0.0040956934980362,-0.04958145938979326,0.0034299800330018144,0.5827403359764699,1.257807624391154,-0.9937626323892503,0.9987557956860734,-0.0040956934980362,-0.04958145938979326,0.0034299800330018144 -12,1730606601.9789667,-0.0002855277072592073,1.4389062815266378,0.3432351939532685,-0.9827763774762791,0.9965118139182854,-0.02139823463545074,-0.08022211484833161,-0.008410265455735734,1.4619029577988203,0.9924147054945871,-1.0080175081048357,0.9965118139182854,-0.02139823463545074,-0.08022211484833161,-0.008410265455735734 -13,1730606603.9767227,-0.00039156238577056033,2.032189999942822,-0.3366907448514985,-0.9506967007037342,0.9978820143025261,-0.05010588412357974,-0.041475041352813435,-0.0008407452110572553,2.045947822807668,0.3100693403111179,-1.0148224720477494,0.9978820143025261,-0.05010588412357974,-0.041475041352813435,-0.0008407452110572553 -14,1730606605.9771225,-0.0004091697255268721,2.1353196690891325,-0.5673459636800655,-0.9485581375367372,0.9990499804463349,-0.03424341252172918,0.023798677076993607,-0.0126549689225031,2.1606815366830956,0.08065229103107868,-0.9938906596754523,0.9990499804463349,-0.03424341252172918,0.023798677076993607,-0.0126549689225031 -15,1730606607.9814677,-0.00041205963343828007,2.191563869763939,-0.6278439139769659,-0.9733444412757101,0.999797079141238,-0.018697379318775544,0.00702834225117261,-0.002609780155336798,2.2047839362870842,0.021637948950278707,-0.9978095096320301,0.999797079141238,-0.018697379318775544,0.00702834225117261,-0.002609780155336798 -16,1730606609.9771278,-0.00041278567022787243,2.2173207543314213,-0.8365530321600158,-0.9991215638073174,0.9998072260608244,-0.018224080069912742,-6.379096125897255e-05,0.007306815508710406,2.217824168721407,-0.18690805941193672,-1.022810294400148,0.9998072260608244,-0.018224080069912742,-6.379096125897255e-05,0.007306815508710406 -17,1730606611.9737067,-0.0004211143125894393,2.2013579542241986,-1.601488981622902,-0.9602122637260162,0.9988359607575722,-0.04802916479030291,-0.000957745330944172,-0.004359535631761697,2.2170781546380525,-0.954598698935752,-1.0225487495420074,0.9988359607575722,-0.04802916479030291,-0.000957745330944172,-0.004359535631761697 -18,1730606613.9787362,-0.00042406877741856756,2.196972526312652,-2.0617214901085608,-0.9580097535959066,0.9989405469254338,-0.04589544413627644,-0.0011434420389472718,-0.003175603273823597,2.211164431421146,-1.414535304433584,-1.0175801397023123,0.9989405469254338,-0.04589544413627644,-0.0011434420389472718,-0.003175603273823597 -19,1730606615.9805744,-0.00042650538273265415,2.210173591516032,-2.1371121601833827,-0.9681404530568304,0.9995345953228278,-0.0285877577228314,-0.006436451071234885,-0.008479679169586076,2.231428982167184,-1.4884339090686212,-1.0050827715948043,0.9995345953228278,-0.0285877577228314,-0.006436451071234885,-0.008479679169586076 diff --git a/riptide_acoustics/src/UndeterminantMethod/untitled2.asv b/riptide_acoustics/src/UndeterminantMethod/untitled2.asv deleted file mode 100644 index 40bcdc6..0000000 --- a/riptide_acoustics/src/UndeterminantMethod/untitled2.asv +++ /dev/null @@ -1,46 +0,0 @@ -close all; -clear; -clear global; - -data = readmatrix("test3.csv"); - -figure_count = 0; - -num_points = 17; -points = zeros(3, num_points); - -previous_estimate = [3; 2; -1]; - -%angle from correct -poses = data(1:num_points, 4:10)'; -delta_s = data(1:num_points, 3)' * 1500; - -poses = [ -0.1654 -0.0052 0.4661 0.7436 0.6187 0.0037 -0.7318 -1.0728 -1.1343 -1.1684 -1.1649 -1.0534 -0.5775 -0.3561 -0.2073 - 0.2949 0.2951 0.2949 0.2953 0.2943 0.2950 0.2949 0.2947 0.4725 0.6550 0.7949 0.7661 0.7213 0.6777 0.6443 - -0.9838 -0.9904 -0.9953 -0.9834 -0.9783 -0.9680 -0.9589 -0.9779 -0.9800 -0.9807 -0.9881 -0.9903 -0.9986 -0.9937 -0.9825 - 1.0000 0.9999 0.9995 1.0000 0.9999 0.9986 0.9969 0.9998 0.9999 0.9999 1.0000 1.0000 0.9994 0.9999 1.0000 - 0.0025 0.0024 0.0026 0.0024 0.0025 0.0025 0.0021 0.0024 0.0085 0.0113 0.0024 -0.0005 -0.0030 -0.0034 -0.0012 - -0.0002 -0.0102 -0.0325 0.0062 0.0157 0.0537 0.0787 0.0179 0.0098 0.0038 -0.0023 -0.0080 -0.0340 -0.0144 0.0077 - 0.0001 0.0002 -0.0001 -0.0007 0.0007 -0.0004 0.0003 0.0004 -0.0002 -0.0004 0.0004 -0.0003 0.0006 0.0009 0.0011] - -delta_s = [ -0.3577 - -0.3703 - -0.4121 - -0.4407 - -0.4273 - -0.3727 - -0.3183 - -0.2975 - -0.2748 - -0.2505 - -0.2323 - -0.2420 - -0.2728 - -0.2932 - -0.3080] - -for i = 1:100 - previous_estimate = estimate_pinger_position(poses, delta_s, previous_estimate, [-.01; .65; 0]) - %previous_estimate(3) = -1; - disp(i); -end diff --git a/riptide_acoustics/src/UndeterminantMethod/untitled2.m b/riptide_acoustics/src/UndeterminantMethod/untitled2.m deleted file mode 100644 index fa99326..0000000 --- a/riptide_acoustics/src/UndeterminantMethod/untitled2.m +++ /dev/null @@ -1,46 +0,0 @@ -close all; -clear; -clear global; - -data = readmatrix("test3.csv"); - -figure_count = 0; - -num_points = 5; -points = zeros(3, num_points); - -previous_estimate = [4.5; 2.1; -1.1]; - -%angle from correct -poses = data(1:num_points, 4:10)'; -delta_s = data(1:num_points, 3)' * 1500; - -poses = [ -0.1654 -0.0052 0.4661 0.7436 0.6187 0.0037 -0.7318 -1.0728 -1.1343 -1.1684 -1.1649 -1.0534 -0.5775 -0.3561 -0.2073 - 0.2949 0.2951 0.2949 0.2953 0.2943 0.2950 0.2949 0.2947 0.4725 0.6550 0.7949 0.7661 0.7213 0.6777 0.6443 - -0.9838 -0.9904 -0.9953 -0.9834 -0.9783 -0.9680 -0.9589 -0.9779 -0.9800 -0.9807 -0.9881 -0.9903 -0.9986 -0.9937 -0.9825 - 1.0000 0.9999 0.9995 1.0000 0.9999 0.9986 0.9969 0.9998 0.9999 0.9999 1.0000 1.0000 0.9994 0.9999 1.0000 - 0.0025 0.0024 0.0026 0.05024 0.0025 0.0025 0.0021 0.0024 0.0085 0.0113 0.0024 -0.0005 -0.0030 -0.0034 -0.0012 - -0.0002 -0.0102 -0.0325 0.0062 0.0157 0.0537 0.0787 0.0179 0.0098 0.0038 -0.0023 -0.0080 -0.0340 -0.0144 0.0077 - 0.0001 0.0002 -0.0001 -0.0007 0.0007 -0.0004 0.0003 0.0004 -0.0002 -0.0004 0.0004 -0.0003 0.0006 0.0009 0.0011] - -delta_s = [ -0.3577 - -0.3703 - -0.4121 - -0.4407 - -0.4273 - -0.3727 - -0.3183 - -0.2975 - -0.2748 - -0.2505 - -0.2323 - -0.2420 - -0.2728 - -0.2932 - -0.3080] - -for i = 1:1000 - previous_estimate = estimate_pinger_position(poses, delta_s, previous_estimate, [-.01; -.65; 0]) - %previous_estimate(3) = -1; - disp(i); -end diff --git a/riptide_acoustics/src/matlab/AcousticsNode.asv b/riptide_acoustics/src/matlab/AcousticsNode.asv deleted file mode 100644 index 59c1b32..0000000 --- a/riptide_acoustics/src/matlab/AcousticsNode.asv +++ /dev/null @@ -1,120 +0,0 @@ -clear; -clear global; - -%read from config -speed_of_sound = 1500; -pinger_depth = -1.0; - -%initialize node -global node; -node = ros2node("/acoustics_node"); - -%intialize tf2 -global tftree; -tftree = ros2tf(node); - -%initialize transform array -global measurements; - -%get acoustic pod transform -global port_to_origin_transform -global port_to_startboard_transform - -global pinger_pose -pinger_pose = [3.0,2.0,-1.0,0.0,0.0,0.0]; - -transforms_recieved = false; -while(transforms_recieved == false) - %loop until transforms become available - - try - port_to_origin_transform = getTransform(tftree, "talos/origin", "talos/acoustics_port_link"); - port_to_startboard_transform = getTransform(tftree, "talos/acoustics_starboard_link", "talos/acoustics_port_link"); - - transforms_recieved = true; - catch - pause(1); - disp("Waiting for acoustic transforms to become available!"); - end -end - - -%initialize publishers and subscribers -pub = ros2publisher(node, "/test_matlab", "std_msgs/UInt8"); - -delta_t_sub = ros2subscriber(node, "/talos/acoustics/delta_t", @delta_t_callback); - -x = 0; -while(x < 1) - if(length(measurements) > 2) - %select the two measurements to calculate with - m1 = measurements(end - 1); - m2 = measurements(end); - - %formatting... - p2o_translation = [port_to_origin_transform.transform.translation.x; port_to_origin_transform.transform.translation.y; port_to_origin_transform.transform.translation.z]; - p2o_quat = [port_to_origin_transform.transform.rotation.w; port_to_origin_transform.transform.rotation.x; port_to_origin_transform.transform.rotation.y; port_to_origin_transform.transform.rotation.z]; - p2o_rotation = quat2rotm(p2o_quat'); - - o2m1_translation = [m1.auv_origin.transform.translation.x; m1.auv_origin.transform.translation.y; m1.auv_origin.transform.translation.z]; - o2m1_quat = [m1.auv_origin.transform.rotation.w; m1.auv_origin.transform.rotation.x; m1.auv_origin.transform.rotation.y; m1.auv_origin.transform.rotation.z]; - o2m1_rotation = quat2rotm(o2m1_quat'); - - o2m2_translation = [m2.auv_origin.transform.translation.x; m2.auv_origin.transform.translation.y; m2.auv_origin.transform.translation.z]; - o2m2_quat = [m2.auv_origin.transform.rotation.w; m2.auv_origin.transform.rotation.x; m2.auv_origin.transform.rotation.y; m2.auv_origin.transform.rotation.z]; - o2m2_rotation = quat2rotm(o2m1_quat'); - - p2s_translation = [port_to_startboard_transform.transform.translation.x; port_to_startboard_transform.transform.translation.y; port_to_startboard_transform.transform.translation.z]; - - %transform each hydrophone into origin frame for each measurement - m1t = o2m1_rotation * p2o_translation + o2m1_translation - m1r = transpose(quatmultiply(p2o_quat', o2m1_quat')) - m2t = o2m2_rotation * p2o_translation + o2m2_translation - m2r = transpose(quatmultiply(p2o_quat', o2m2_quat')) - - [solve_x, solve_y] = solve_two_pulse_system_depth(m1.delta_t/speed_of_sound, m2.delta_t/speed_of_sound,[m1t;m1r],[m2t;m2r], p2s_translation, 1, [100,100], pinger_depth) - - pinger_location = o2m1_rotation * ([solve_x; solve_y; pinger_depth] + p2s_translation) + o2m1_translation - - timing_estimate_1 = GetTimingDIfference([m1t;m1r], p2s_translation, pinger_pose, speed_of_sound) - back_calc1 = GetTimingDIfference([m1t;m1r], p2s_translation, pinger_location, speed_of_sound) - m1.delta_t - - timing_estimate_2 = GetTimingDIfference([m2t;m2r], p2s_translation, pinger_pose, speed_of_sound) - back_calc2 = GetTimingDIfference([m2t;m2r], p2s_translation, pinger_location, speed_of_sound) - m2.delta_t - end - -end - - -function delta_t_callback(message) - %introduce global variables - global tftree; - global measurements; - - try - %get the transform at the time of the measurement - port_transform = getTransform(tftree, "map", 'talos/origin'); - - %fill out measurement data struct - measurement = struct(); - measurement.delta_t = message.vector.x; - measurement.frequency = message.vector.y; - measurement.stamp = message.header.stamp; - measurement.auv_origin = port_transform; - - if(length(measurements) == 0) - measurements = [measurement] - - else - measurements(end+1) = measurement; - end - - catch - disp("Failed to get transform") - - tftree.AvailableFrames - end - -end \ No newline at end of file diff --git a/riptide_acoustics/src/matlab/AcousticsNode.m b/riptide_acoustics/src/matlab/AcousticsNode.m deleted file mode 100644 index 9451126..0000000 --- a/riptide_acoustics/src/matlab/AcousticsNode.m +++ /dev/null @@ -1,130 +0,0 @@ -clear; -clear global; - -%read from config -speed_of_sound = 1500; -pinger_depth = -1.0; - -%initialize node -global node; -node = ros2node("/acoustics_node"); - -%intialize tf2 -global tftree; -tftree = ros2tf(node); - -%initialize transform array -global measurements; - -%get acoustic pod transform -global port_to_origin_transform -global port_to_startboard_transform - -global initial_pinger_estimate -initial_pinger_estimate = [3.0,2.0,-1.0,0.0,0.0,0.0]; - -transforms_recieved = false; -while(transforms_recieved == false) - %loop until transforms become available - - try - port_to_origin_transform = getTransform(tftree, "talos/origin", "talos/acoustics_port_link"); - port_to_startboard_transform = getTransform(tftree, "talos/acoustics_starboard_link", "talos/acoustics_port_link"); - - transforms_recieved = true; - catch - pause(1); - disp("Waiting for acoustic transforms to become available!"); - end -end - - -%initialize publishers and subscribers -pub = ros2publisher(node, "/test_matlab", "std_msgs/UInt8"); - -%add subscriber to add to measurements -delta_t_sub = ros2subscriber(node, "/talos/acoustics/delta_t", @delta_t_callback); - -%add rate function to analyze measurements -r = ros2rate(node, 1) -reset(r) - -x = 0 -while(x < 1) - run_analysis(port_to_startboard_transform, port_to_origin_transform, measurements, pinger_depth, initial_pinger_estimate, speed_of_sound); - waitfor(r) -end - -function [] = run_analysis(port_to_startboard_transform, port_to_origin_transform, measurements, pinger_depth, pinger_pose, speed_of_sound) - if(max(size((measurements))) > 5) - %select the two measurements to calculate with - m1 = measurements(5); - m2 = measurements(end); - - %formatting... - p2o_translation = [port_to_origin_transform.transform.translation.x; port_to_origin_transform.transform.translation.y; port_to_origin_transform.transform.translation.z]; - p2o_quat = [port_to_origin_transform.transform.rotation.w; port_to_origin_transform.transform.rotation.x; port_to_origin_transform.transform.rotation.y; port_to_origin_transform.transform.rotation.z]; - p2o_rotation = quat2rotm(p2o_quat'); - - o2m1_translation = [m1.auv_origin.transform.translation.x; m1.auv_origin.transform.translation.y; m1.auv_origin.transform.translation.z]; - o2m1_quat = [m1.auv_origin.transform.rotation.w; m1.auv_origin.transform.rotation.x; m1.auv_origin.transform.rotation.y; m1.auv_origin.transform.rotation.z]; - o2m1_rotation = quat2rotm(o2m1_quat'); - - o2m2_translation = [m2.auv_origin.transform.translation.x; m2.auv_origin.transform.translation.y; m2.auv_origin.transform.translation.z]; - o2m2_quat = [m2.auv_origin.transform.rotation.w; m2.auv_origin.transform.rotation.x; m2.auv_origin.transform.rotation.y; m2.auv_origin.transform.rotation.z]; - o2m2_rotation = quat2rotm(o2m1_quat'); - - p2s_translation = [port_to_startboard_transform.transform.translation.x; port_to_startboard_transform.transform.translation.y; port_to_startboard_transform.transform.translation.z]; - - %transform each hydrophone into origin frame for each measurement - m1t = o2m1_rotation * p2o_translation + o2m1_translation - m1r = transpose(quatmultiply(p2o_quat', o2m1_quat')) - m2t = o2m2_rotation * p2o_translation + o2m2_translation - m2r = transpose(quatmultiply(p2o_quat', o2m2_quat')) - - [solve_x, solve_y] = solve_two_pulse_system_depth(m1.delta_t/speed_of_sound, m2.delta_t/speed_of_sound,[m1t;m1r],[m2t;m2r], p2s_translation, 1, [100,100], pinger_depth) - - pinger_location = o2m1_rotation * ([solve_x; solve_y; pinger_depth] + p2s_translation) + o2m1_translation - - timing_estimate_1 = GetTimingDIfference([m1t;m1r], p2s_translation, pinger_pose, speed_of_sound) - back_calc1 = GetTimingDIfference([m1t;m1r], p2s_translation, pinger_location, speed_of_sound) - m1.delta_t - - timing_estimate_2 = GetTimingDIfference([m2t;m2r], p2s_translation, pinger_pose, speed_of_sound) - back_calc2 = GetTimingDIfference([m2t;m2r], p2s_translation, pinger_location, speed_of_sound) - m2.delta_t - end - -end - - -function delta_t_callback(message) - %introduce global variables - global tftree; - global measurements; - - try - %get the transform at the time of the measurement - port_transform = getTransform(tftree, "map", 'talos/origin'); - - %fill out measurement data struct - measurement = struct(); - measurement.delta_t = message.vector.x; - measurement.frequency = message.vector.y; - measurement.stamp = message.header.stamp; - measurement.auv_origin = port_transform; - - if(length(measurements) == 0) - measurements = [measurement]; - - else - measurements(end+1) = measurement; - end - - catch - disp("Failed to get transform") - - tftree.AvailableFrames - end - -end \ No newline at end of file diff --git a/riptide_acoustics/src/matlab/GetTimingDIfference.asv b/riptide_acoustics/src/matlab/GetTimingDIfference.asv deleted file mode 100644 index af3bdae..0000000 --- a/riptide_acoustics/src/matlab/GetTimingDIfference.asv +++ /dev/null @@ -1,9 +0,0 @@ -function [delta_t] = getTimingDifference(pose_1, hydrophone_base_vector, pinger_pos) - -%rotation matrix -rotm = quat2rotm([pose_1(4), pose_1(5), pose_1(6), pose_1(7)]); -hydrophone2_pos = getTimingDifference(1:3) + rotm * hydrophone_base_vector; - - - -end \ No newline at end of file diff --git a/riptide_acoustics/src/matlab/GetTimingDIfference.m b/riptide_acoustics/src/matlab/GetTimingDIfference.m deleted file mode 100644 index eb7edb0..0000000 --- a/riptide_acoustics/src/matlab/GetTimingDIfference.m +++ /dev/null @@ -1,12 +0,0 @@ -function [delta_t] = GetTimingDifference(pose_1, hydrophone_base_vector, pinger_pos, speed_of_sound) - -%rotation matrix -rotm = quat2rotm([pose_1(4), pose_1(5), pose_1(6), pose_1(7)]); -hydrophone2_pos = pose_1(1:3) + rotm * hydrophone_base_vector; - -time_1 = norm(pose_1(1:3) - pinger_pos) / speed_of_sound; -time_2 = norm(hydrophone2_pos - pinger_pos) / speed_of_sound; - -delta_t = time_2 - time_1; - -end \ No newline at end of file diff --git a/riptide_acoustics/src/matlab/data/test1.csv b/riptide_acoustics/src/matlab/data/test1.csv deleted file mode 100644 index 2dc4bee..0000000 --- a/riptide_acoustics/src/matlab/data/test1.csv +++ /dev/null @@ -1,36 +0,0 @@ -number,time,delta_t,pos x 1,pos y 1,pos z 1,or w 1,or x 1,or y 1, or z 1,pos x 2,pos y 2,pos z 2,or w 2,or x 2,or y 2, or z 2 -1,1730602076.9605615,-0.00023910690471704902,0.17477051774202157,0.35526724639887375,0.9858247081319208,0.9999991883481831,-0.00030941976696492756,-0.00020121886321131685,0.0012194561705773605,0.17477051774202157,0.35526724639887375,0.9858247081319208,0.9999991883481831,-0.00030941976696492756,-0.00020121886321131685,0.0012194561705773605 -2,1730602078.9667318,-0.00023958979555123308,0.17345860967021187,0.3540682687286766,0.9809740862699532,0.9999987397548187,0.0004230851023879771,-0.000623163570459755,0.001397553195792181,0.17345860967021187,0.3540682687286766,0.9809740862699532,0.9999987397548187,0.0004230851023879771,-0.000623163570459755,0.001397553195792181 -3,1730602080.966533,-0.0002388495874544354,0.17483717066185328,0.355927146343053,0.9802572441585551,0.9999998968037208,-0.0004416842331025506,-0.00010073782498966525,3.4051086857910075e-05,0.17483717066185328,0.355927146343053,0.9802572441585551,0.9999998968037208,-0.0004416842331025506,-0.00010073782498966525,3.4051086857910075e-05 -4,1730602082.9712355,-0.00023941257498223103,0.17516954811031324,0.3541027266861226,0.9837919382535902,0.9999992478076677,0.00024717197072335225,3.100796699418819e-05,0.0012009698671241584,0.17516954811031324,0.3541027266861226,0.9837919382535902,0.9999992478076677,0.00024717197072335225,3.100796699418819e-05,0.0012009698671241584 -5,1730602084.9746716,-0.00023863820708125842,0.1750414084933162,0.35435604910995305,0.9840576722390639,0.9999999636524994,0.0001629940342028921,0.00010198172933705589,0.00018901764866265314,0.1750414084933162,0.35435604910995305,0.9840576722390639,0.9999999636524994,0.0001629940342028921,0.00010198172933705589,0.00018901764866265314 -6,1730602086.9681497,-0.00023915745126083633,0.17411772135577563,0.35464822014067027,0.9812986791837663,0.9999998353590444,-0.0001276187939803107,-0.0004907159973617589,0.0002686878069568441,0.17411772135577563,0.35464822014067027,0.9812986791837663,0.9999998353590444,-0.0001276187939803107,-0.0004907159973617589,0.0002686878069568441 -7,1730602088.9683607,-0.000238833529583436,0.17528023664455078,0.355235584416919,0.9831253755052829,0.9999999444069843,-0.00031540040670268826,-2.1628301252419562e-05,0.00010602277358149713,0.17528023664455078,0.355235584416919,0.9831253755052829,0.9999999444069843,-0.00031540040670268826,-2.1628301252419562e-05,0.00010602277358149713 -8,1730602090.9690344,-0.00023941608178562894,0.17526646836184295,0.35504694742199533,0.9849703278747103,0.9999992200511325,-0.0001414348480131425,-1.745818256528908e-05,0.0012408015644250602,0.17526646836184295,0.35504694742199533,0.9849703278747103,0.9999992200511325,-0.0001414348480131425,-1.745818256528908e-05,0.0012408015644250602 -9,1730602092.9703708,-0.0002394951921362415,0.17535911628723994,0.35459707221606707,0.9814910289161214,0.9999995501224405,0.0003087854734764475,0.0003472709585601525,0.0008269276445910311,0.17535911628723994,0.35459707221606707,0.9814910289161214,0.9999995501224405,0.0003087854734764475,0.0003472709585601525,0.0008269276445910311 -10,1730602094.9712648,-0.00023878828913218028,0.17490437564955694,0.3550755843765803,0.9805177758772854,0.9999998242940161,0.00012321582331858337,0.000125810530991369,0.0005660402000451104,0.17490437564955694,0.3550755843765803,0.9805177758772854,0.9999998242940161,0.00012321582331858337,0.000125810530991369,0.0005660402000451104 -11,1730602096.9651887,-0.0002389983471739289,0.17449848606204946,0.35578668260527746,0.9835136365982081,0.9999999452558151,-0.00026026695847683305,-0.00018312050417430666,-9.064412859775001e-05,0.17449848606204946,0.35578668260527746,0.9835136365982081,0.9999999452558151,-0.00026026695847683305,-0.00018312050417430666,-9.064412859775001e-05 -12,1730602098.9677374,-0.00023861968000385019,0.17523672558745987,0.3542890610484866,0.9872715425345799,0.9999997925733386,0.0003680067308857633,0.00019624886287545254,0.0004908265574917519,0.17523672558745987,0.3542890610484866,0.9872715425345799,0.9999997925733386,0.0003680067308857633,0.00019624886287545254,0.0004908265574917519 -13,1730602100.9710946,-0.00023890035560134018,0.17521591305199868,0.3546581917782677,0.9873188151868755,0.999999964264311,0.00016065918217922096,9.917171700903727e-06,-0.00021345175959599087,0.17521591305199868,0.3546581917782677,0.9873188151868755,0.999999964264311,0.00016065918217922096,9.917171700903727e-06,-0.00021345175959599087 -14,1730602102.9674559,-0.0002387520010607984,0.17507985614059032,0.3552776755797112,0.9838027042421368,0.9999999812267832,4.896899347367777e-05,0.00018236369435959004,4.34965953875846e-05,0.17507985614059032,0.3552776755797112,0.9838027042421368,0.9999999812267832,4.896899347367777e-05,0.00018236369435959004,4.34965953875846e-05 -15,1730602104.970081,-0.00023947379821559435,0.1737173275434046,0.3547664759948673,0.9850441483874908,0.9999988684000727,0.00020712660609594123,-0.0005733659981797373,0.0013753358045998938,0.1737173275434046,0.3547664759948673,0.9850441483874908,0.9999988684000727,0.00020712660609594123,-0.0005733659981797373,0.0013753358045998938 -16,1730602106.9679987,-0.00023852933988669989,0.17459786558139967,0.3549929292112845,0.9884943087971568,0.9999999672179588,2.8991724937339898e-05,-9.423100851987381e-05,-0.00023631351690418302,0.17459786558139967,0.3549929292112845,0.9884943087971568,0.9999999672179588,2.8991724937339898e-05,-9.423100851987381e-05,-0.00023631351690418302 -17,1730602108.9659603,-0.00023950819189385826,0.17431947329991643,0.35415530579555077,0.9845299433797811,0.9999993917169818,0.0001590372031228914,-0.00017590365806280728,0.0010771864915208517,0.17431947329991643,0.35415530579555077,0.9845299433797811,0.9999993917169818,0.0001590372031228914,-0.00017590365806280728,0.0010771864915208517 -18,1730602110.9644763,-0.00023966226061829322,0.17447077236287376,0.3559662721175278,0.9805980835420093,0.9999994280550956,-0.0005657306726253614,-0.00021435588225217816,0.0008819806367582533,0.17447077236287376,0.3559662721175278,0.9805980835420093,0.9999994280550956,-0.0005657306726253614,-0.00021435588225217816,0.0008819806367582533 -19,1730602112.971374,-0.0002446875439173555,0.0660843333711714,0.35505035955396336,0.9858507478343906,0.9999661777305295,7.720115837767317e-05,0.008213709164312412,0.000415230948086814,0.0660843333711714,0.35505035955396336,0.9858507478343906,0.9999661777305295,7.720115837767317e-05,0.008213709164312412,0.000415230948086814 -20,1730602114.9733787,-0.0002791140166842917,-0.4470946479847955,0.3540909917006062,1.0447844146760172,0.9989695117241044,0.00046144519347156526,0.04538345705544062,-0.00020866177102560747,-0.4470946479847955,0.3540909917006062,1.0447844146760172,0.9989695117241044,0.00046144519347156526,0.04538345705544062,-0.00020866177102560747 -21,1730602116.9665315,-0.0003162599332602687,-0.970256509581909,0.35373680136875885,1.0737710531423812,0.9992769090275209,-0.0002830823545975063,0.038009351338723546,0.0009317508217785118,-0.970256509581909,0.35373680136875885,1.0737710531423812,0.9992769090275209,-0.0002830823545975063,0.038009351338723546,0.0009317508217785118 -22,1730602118.9730878,-0.00033399703872171924,-1.207711916673252,0.44408844012201143,0.9828208566831272,0.9999936115663932,0.00317344884574182,-0.0016450072348369801,1.4959623928805418e-07,-1.207711916673252,0.44408844012201143,0.9828208566831272,0.9999936115663932,0.00317344884574182,-0.0016450072348369801,1.4959623928805418e-07 -23,1730602120.9634225,-0.0003691215677558217,-1.2853769922182812,1.0229603132549545,1.0009177405545453,0.999653578806899,0.025418360504437628,-0.006801685283811873,0.000605314071749189,-1.2853769922182812,1.0229603132549545,1.0009177405545453,0.999653578806899,0.025418360504437628,-0.006801685283811873,0.000605314071749189 -24,1730602122.9660347,-0.0003943946164293711,-1.3121946199196712,1.9234540408930478,1.1753650518091148,0.9982185728088924,0.059647213320038986,-0.001089718128976824,0.0008386637771986282,-1.3121946199196712,1.9234540408930478,1.1753650518091148,0.9982185728088924,0.059647213320038986,-0.001089718128976824,0.0008386637771986282 -25,1730602124.9647522,-0.0004037945431972722,-1.3458575233570151,2.406868512294075,1.212384835090746,0.9985968204017377,0.052918589400108684,0.0019773293704657934,0.0003214778425376011,-1.3458575233570151,2.406868512294075,1.212384835090746,0.9985968204017377,0.052918589400108684,0.0019773293704657934,0.0003214778425376011 -26,1730602126.967966,-0.00040708906153200884,-1.3584983879785515,2.573448252179173,1.1412939686450956,0.9994247287615419,0.03380745614661589,0.0020477552198020408,0.0017533246627500847,-1.3584983879785515,2.573448252179173,1.1412939686450956,0.9994247287615419,0.03380745614661589,0.0020477552198020408,0.0017533246627500847 -27,1730602128.971708,-0.00040806074219133727,-1.357064939791769,2.6647756786761647,1.08430485058301,0.9997822299691196,0.02077565107880742,0.001606737658886589,0.0011328522946685569,-1.357064939791769,2.6647756786761647,1.08430485058301,0.9997822299691196,0.02077565107880742,0.001606737658886589,0.0011328522946685569 -28,1730602130.9707863,-0.0004084979416342751,-1.3594705378747984,2.67836055534775,1.0353544663475671,0.9999404779925178,0.010781252924496729,0.000997760847145472,0.001345188003558663,-1.3594705378747984,2.67836055534775,1.0353544663475671,0.9999404779925178,0.010781252924496729,0.000997760847145472,0.001345188003558663 -29,1730602132.9623039,-0.0004083861324080657,-1.361951045196252,2.6906735546043663,1.0052461740355498,0.9999889658369797,0.004368917254852986,0.0004098944772908693,0.0016771263594692598,-1.361951045196252,2.6906735546043663,1.0052461740355498,0.9999889658369797,0.004368917254852986,0.0004098944772908693,0.0016771263594692598 -30,1730602134.971606,-0.0004082316128764602,-1.3577426907942822,2.700422672135693,0.9937708072419184,0.999998474271318,0.0016746051779782476,-0.00023254585456555367,0.00043940295801199545,-1.3577426907942822,2.700422672135693,0.9937708072419184,0.999998474271318,0.0016746051779782476,-0.00023254585456555367,0.00043940295801199545 -31,1730602136.9728036,-0.00040842323525591576,-1.3607768526676776,2.701118891804274,0.9867706461716464,0.9999992907387164,0.00029356118045768993,-6.184035214498375e-05,0.0011526142756239455,-1.3607768526676776,2.701118891804274,0.9867706461716464,0.9999992907387164,0.00029356118045768993,-6.184035214498375e-05,0.0011526142756239455 -32,1730602138.9738753,-0.0004080408535634715,-1.357350547391125,2.705735956868999,0.9784954488826209,0.9999996625972498,-0.0007154139937707934,-0.00010539908144492453,0.0003897168686109271,-1.357350547391125,2.705735956868999,0.9784954488826209,0.9999996625972498,-0.0007154139937707934,-0.00010539908144492453,0.0003897168686109271 -33,1730602140.9710107,-0.000408351601845452,-1.3581793011583123,2.7054241450450336,0.9759648420020786,0.9999993148573082,-0.0009650002900233382,-0.00015671634685985163,0.0006438162324049681,-1.3581793011583123,2.7054241450450336,0.9759648420020786,0.9999993148573082,-0.0009650002900233382,-0.00015671634685985163,0.0006438162324049681 -34,1730602142.9670005,-0.00040799146180058976,-1.3566049885819054,2.70583219114272,0.9772227938453851,0.999999611225209,-0.0007839807623736368,-0.0002887316346915098,0.00028205963608478595,-1.3566049885819054,2.70583219114272,0.9772227938453851,0.999999611225209,-0.0007839807623736368,-0.0002887316346915098,0.00028205963608478595 -35,1730602144.9683554,-0.000408348066079542,-1.3599165351918545,2.7027164519321696,0.9827807870870706,0.9999981139629581,-0.001220450448648613,0.0006018994470167279,0.0013857446679392607,-1.3599165351918545,2.7027164519321696,0.9827807870870706,0.9999981139629581,-0.001220450448648613,0.0006018994470167279,0.0013857446679392607 diff --git a/riptide_acoustics/src/matlab/data/test2.csv b/riptide_acoustics/src/matlab/data/test2.csv deleted file mode 100644 index 3d88b8b..0000000 --- a/riptide_acoustics/src/matlab/data/test2.csv +++ /dev/null @@ -1,19 +0,0 @@ -number,time,delta_t,pos x 1,pos y 1,pos z 1,or w 1,or x 1,or y 1, or z 1,pos x 2,pos y 2,pos z 2,or w 2,or x 2,or y 2, or z 2 -1,1730606183.9822214,-0.0004049446010331419,-1.44818331372381,2.729122172380355,0.16447469907998422,0.9996439718404979,-0.005891315782799434,0.023512350475535227,0.011153086407656177,-1.44818331372381,2.729122172380355,0.16447469907998422,0.9996439718404979,-0.005891315782799434,0.023512350475535227,0.011153086407656177 -2,1730606185.978614,-0.0004048595658688002,-1.4464266503652385,2.72911422471794,0.16529892982310632,0.9996453243809861,-0.005700388419394981,0.023509993499049444,0.011136032541969262,-1.4464266503652385,2.72911422471794,0.16529892982310632,0.9996453243809861,-0.005700388419394981,0.023509993499049444,0.011136032541969262 -3,1730606187.9794643,-0.0004047982695540296,-1.4446275266997315,2.7293390766050543,0.16342223394915065,0.9996405590021175,-0.006146061219711183,0.023615575948105417,0.011103301403499738,-1.4446275266997315,2.7293390766050543,0.16342223394915065,0.9996405590021175,-0.006146061219711183,0.023615575948105417,0.011103301403499738 -4,1730606189.9816167,-0.0004047131798816803,-1.4427983524491972,2.7294270699991325,0.16362602898572473,0.9996364545547526,-0.006200093363465322,0.023785856070205978,0.011079287815376697,-1.4427983524491972,2.7294270699991325,0.16362602898572473,0.9996364545547526,-0.006200093363465322,0.023785856070205978,0.011079287815376697 -5,1730606191.9829905,-0.00040472188516482,-1.441067093081469,2.7294788007150315,0.16341365593629936,0.9996382825980462,-0.006185347903394746,0.023722443345314693,0.01105853143373917,-1.441067093081469,2.7294788007150315,0.16341365593629936,0.9996382825980462,-0.006185347903394746,0.023722443345314693,0.01105853143373917 -6,1730606193.9778209,-0.00040815079017685464,-1.4757537223195674,2.7222622651065036,0.09712637455239398,0.995473210093624,-0.0008116760643057892,-0.09462179852498112,0.008896314504340693,-1.4757537223195674,2.7222622651065036,0.09712637455239398,0.995473210093624,-0.0008116760643057892,-0.09462179852498112,0.008896314504340693 -7,1730606195.983692,-0.00041017851447404337,-1.4514179663061864,2.76113124628261,0.8571591137260269,0.9997483376423889,-9.890628782080295e-05,-0.02214686810668308,-0.003573210306068304,-1.4514179663061864,2.76113124628261,0.8571591137260269,0.9997483376423889,-9.890628782080295e-05,-0.02214686810668308,-0.003573210306068304 -8,1730606197.9891286,-0.0004112333212324556,-1.4789820499060147,2.750724095855923,0.9418069863898424,0.9999928231319808,0.001627135019988028,-0.003286417263314094,0.0009516184787096665,-1.4789820499060147,2.750724095855923,0.9418069863898424,0.9999928231319808,0.001627135019988028,-0.003286417263314094,0.0009516184787096665 -9,1730606199.9797802,-0.00041507892892091945,-1.5464585359197114,2.6811394135667284,0.9671956787992081,0.9999517767822987,8.59867674751213e-05,0.002463617118728545,0.009506172041995484,-1.5464585359197114,2.6811394135667284,0.9671956787992081,0.9999517767822987,8.59867674751213e-05,0.002463617118728545,0.009506172041995484 -10,1730606201.9832702,-0.0004068672756401804,-1.4930311454456546,2.2149640639809967,0.9306568594810922,0.9997275884952701,-0.023291403332014345,0.0011346247154914299,0.0009858798163705409,-1.4930311454456546,2.2149640639809967,0.9306568594810922,0.9997275884952701,-0.023291403332014345,0.0011346247154914299,0.0009858798163705409 -11,1730606203.9796584,-0.00038792189194332703,-1.5019731842600637,1.3072211982103648,0.8907994127436389,0.9981846247888271,-0.05997506346232245,0.0007835090179754385,0.005461932945170839,-1.5019731842600637,1.3072211982103648,0.8907994127436389,0.9981846247888271,-0.05997506346232245,0.0007835090179754385,0.005461932945170839 -12,1730606205.9803002,-0.0003232308396447451,-1.332108412861726,0.36207401326703664,0.961822281278685,0.9959573732678311,-0.08901467524896803,-0.0117906442680375,-0.0025057796206867395,-1.332108412861726,0.36207401326703664,0.961822281278685,0.9959573732678311,-0.08901467524896803,-0.0117906442680375,-0.0025057796206867395 -13,1730606207.9825418,-0.00020489405492753575,-0.7430562252439176,-0.29657880400565145,0.9969648611087392,0.9953320684872347,-0.07956339487390986,-0.054621099463337326,-0.0005245288145428069,-0.7430562252439176,-0.29657880400565145,0.9969648611087392,0.9953320684872347,-0.07956339487390986,-0.054621099463337326,-0.0005245288145428069 -14,1730606209.9806743,-0.0001671718758854756,-0.19801618269996957,-0.44045570501645587,1.0107209678952283,0.9973034118209274,-0.04494444739300005,-0.05801598201181403,-0.00021737083630478583,-0.19801618269996957,-0.44045570501645587,1.0107209678952283,0.9973034118209274,-0.04494444739300005,-0.05801598201181403,-0.00021737083630478583 -15,1730606211.9786365,-0.00014919102032094115,-0.01667539219850163,-0.5531548025930899,1.0037883317163254,0.999336416384447,-0.02282897914476973,-0.028380809412569156,-0.00030701170365328656,-0.01667539219850163,-0.5531548025930899,1.0037883317163254,0.999336416384447,-0.02282897914476973,-0.028380809412569156,-0.00030701170365328656 -16,1730606213.9846392,-0.00015347143110914244,0.0908327758412329,-0.5951806566066492,0.9892068628165283,0.9998369205121485,-0.010076477296627574,-0.012443051064079723,0.008352692158277778,0.0908327758412329,-0.5951806566066492,0.9892068628165283,0.9998369205121485,-0.010076477296627574,-0.012443051064079723,0.008352692158277778 -17,1730606215.980522,-0.0001446212059355562,0.11857213992520245,-0.588620405036433,0.9874957267047677,0.999972845200863,-0.006218312479291208,0.002642016737005114,-0.0029429913968840823,0.11857213992520245,-0.588620405036433,0.9874957267047677,0.999972845200863,-0.006218312479291208,0.002642016737005114,-0.0029429913968840823 -18,1730606217.9860191,-0.00014414287343270113,0.1374673538907063,-0.5897333885048757,0.8650125795394152,0.999897621880946,-0.0060957321213354065,0.012586032936664857,-0.003029782442844557,0.1374673538907063,-0.5897333885048757,0.8650125795394152,0.999897621880946,-0.0060957321213354065,0.012586032936664857,-0.003029782442844557 diff --git a/riptide_acoustics/src/matlab/data/test3.csv b/riptide_acoustics/src/matlab/data/test3.csv deleted file mode 100644 index df4dcc3..0000000 --- a/riptide_acoustics/src/matlab/data/test3.csv +++ /dev/null @@ -1,20 +0,0 @@ -number,time,delta_t,pos x 1,pos y 1,pos z 1,or w 1,or x 1,or y 1, or z 1,pos x 2,pos y 2,pos z 2,or w 2,or x 2,or y 2, or z 2 -1,1730606579.9802365,-0.0001253174677029977,-0.4163459671747822,0.5968035655233543,-0.13009170754716787,0.9996797023652668,0.006461941330008329,-0.023450988273185154,0.006984779320058735,-0.4156322472233951,1.2468224786111097,-0.12143704221311552,0.9996797023652668,0.006461941330008329,-0.023450988273185154,0.006984779320058735 -2,1730606581.9776223,-0.0001251644840117147,-0.4179850986623722,0.5967806670908133,-0.13012625665880345,0.9996800541537193,0.006533187693569448,-0.023409361541509927,0.007007751279572117,-0.4173030218089022,1.2467983900640658,-0.12138013798623497,0.9996800541537193,0.006533187693569448,-0.023409361541509927,0.007007751279572117 -3,1730606583.9795377,-0.00012521914510405066,-0.419616432675359,0.5967542522988211,-0.13032485875904767,0.9996780936847931,0.006813940737221492,-0.02340461501514544,0.007035852070671016,-0.41897936255841783,1.2467670215857867,-0.1212147596620875,0.9996780936847931,0.006813940737221492,-0.02340461501514544,0.007035852070671016 -4,1730606585.9722495,-0.00012510701653001928,-0.42124948425873543,0.5967287047536821,-0.13048799891217536,0.9996762942223656,0.007044266792109477,-0.023405168586172812,0.007062801048650141,-0.42065444110051303,1.2467372612949499,-0.12107936740820008,0.9996762942223656,0.007044266792109477,-0.023405168586172812,0.007062801048650141 -5,1730606587.972156,-0.00012510791402371317,-0.4229104169024931,0.5967186313880065,-0.13015997228803516,0.9996761234004469,0.006507179903331045,-0.02356441523833557,0.007072711448619975,-0.42231340924855504,1.2467368964166898,-0.12144798566263243,0.9996761234004469,0.006507179903331045,-0.02356441523833557,0.007072711448619975 -6,1730606589.9793305,-0.00012573835113941368,-0.3930678306723455,0.6038586364495109,-0.40652970499988694,0.9673025514229892,-0.0011557015035376628,0.25349261133366585,0.00811999777396901,-0.39494601270290997,1.253922416092197,-0.41021139332525924,0.9673025514229892,-0.0011557015035376628,0.25349261133366585,0.00811999777396901 -7,1730606591.9747446,-0.00014077767268860195,-0.27405960492570663,0.6105285826999282,-1.0007874319387178,0.9975713120363877,-0.0035568941513799248,0.06956153817234788,0.00013532587610615377,-0.2646535277559122,1.2605098634317726,-1.0067757881796024,0.9975713120363877,-0.0035568941513799248,0.06956153817234788,0.00013532587610615377 -8,1730606593.9788249,-0.00013911462769748914,-0.2032590714666731,0.6031998879739695,-0.9892140137723926,0.9998485061214814,-0.003932283808597593,0.016581788756566757,0.0035420661093946383,-0.19795357545544573,1.2532330026633849,-0.9945807184645078,0.9998485061214814,-0.003932283808597593,0.016581788756566757,0.0035420661093946383 -9,1730606595.9784057,-0.00015421170787955832,-0.16205597469502714,0.6031119269128142,-1.0316036576784025,0.999908936205534,-0.004634736641643484,0.0010544108170844532,-0.012630389162994879,-0.13564752976728106,1.2526239346262584,-1.037665495068887,0.999908936205534,-0.004634736641643484,0.0010544108170844532,-0.012630389162994879 -10,1730606597.9731364,-0.0001507145668751925,-0.012256016907346567,0.6049394091183984,-1.009169753056389,0.9999446132882163,-0.0038816473466427898,-0.009662977322558004,0.001526446533536237,-0.004193440804658083,1.2549480701308915,-1.0140416598016482,0.9999446132882163,-0.0038816473466427898,-0.009662977322558004,0.001526446533536237 -11,1730606599.97842,-0.00017441549370768663,0.5769791722630976,0.6077721500419295,-0.9892138874802936,0.9987557956860734,-0.0040956934980362,-0.04958145938979326,0.0034299800330018144,0.5827403359764699,1.257807624391154,-0.9937626323892503,0.9987557956860734,-0.0040956934980362,-0.04958145938979326,0.0034299800330018144 -12,1730606601.9789667,-0.0002855277072592073,1.4389062815266378,0.3432351939532685,-0.9827763774762791,0.9965118139182854,-0.02139823463545074,-0.08022211484833161,-0.008410265455735734,1.4619029577988203,0.9924147054945871,-1.0080175081048357,0.9965118139182854,-0.02139823463545074,-0.08022211484833161,-0.008410265455735734 -13,1730606603.9767227,-0.00039156238577056033,2.032189999942822,-0.3366907448514985,-0.9506967007037342,0.9978820143025261,-0.05010588412357974,-0.041475041352813435,-0.0008407452110572553,2.045947822807668,0.3100693403111179,-1.0148224720477494,0.9978820143025261,-0.05010588412357974,-0.041475041352813435,-0.0008407452110572553 -14,1730606605.9771225,-0.0004091697255268721,2.1353196690891325,-0.5673459636800655,-0.9485581375367372,0.9990499804463349,-0.03424341252172918,0.023798677076993607,-0.0126549689225031,2.1606815366830956,0.08065229103107868,-0.9938906596754523,0.9990499804463349,-0.03424341252172918,0.023798677076993607,-0.0126549689225031 -15,1730606607.9814677,-0.00041205963343828007,2.191563869763939,-0.6278439139769659,-0.9733444412757101,0.999797079141238,-0.018697379318775544,0.00702834225117261,-0.002609780155336798,2.2047839362870842,0.021637948950278707,-0.9978095096320301,0.999797079141238,-0.018697379318775544,0.00702834225117261,-0.002609780155336798 -16,1730606609.9771278,-0.00041278567022787243,2.2173207543314213,-0.8365530321600158,-0.9991215638073174,0.9998072260608244,-0.018224080069912742,-6.379096125897255e-05,0.007306815508710406,2.217824168721407,-0.18690805941193672,-1.022810294400148,0.9998072260608244,-0.018224080069912742,-6.379096125897255e-05,0.007306815508710406 -17,1730606611.9737067,-0.0004211143125894393,2.2013579542241986,-1.601488981622902,-0.9602122637260162,0.9988359607575722,-0.04802916479030291,-0.000957745330944172,-0.004359535631761697,2.2170781546380525,-0.954598698935752,-1.0225487495420074,0.9988359607575722,-0.04802916479030291,-0.000957745330944172,-0.004359535631761697 -18,1730606613.9787362,-0.00042406877741856756,2.196972526312652,-2.0617214901085608,-0.9580097535959066,0.9989405469254338,-0.04589544413627644,-0.0011434420389472718,-0.003175603273823597,2.211164431421146,-1.414535304433584,-1.0175801397023123,0.9989405469254338,-0.04589544413627644,-0.0011434420389472718,-0.003175603273823597 -19,1730606615.9805744,-0.00042650538273265415,2.210173591516032,-2.1371121601833827,-0.9681404530568304,0.9995345953228278,-0.0285877577228314,-0.006436451071234885,-0.008479679169586076,2.231428982167184,-1.4884339090686212,-1.0050827715948043,0.9995345953228278,-0.0285877577228314,-0.006436451071234885,-0.008479679169586076 diff --git a/riptide_acoustics/src/matlab/generatte_test_cases.asv b/riptide_acoustics/src/matlab/generatte_test_cases.asv deleted file mode 100644 index 1377e9a..0000000 --- a/riptide_acoustics/src/matlab/generatte_test_cases.asv +++ /dev/null @@ -1,57 +0,0 @@ - -close all -clear - -%speed of sound -speed_of_sound = 1500; - -%hydrophone base vector from 1 to 2 -base_vector = [0.01, 0.65, 0.0]; - -%initial pose of hydrophone 1 -%make sure to convert from ros quat to matlab quat -pose1_h1 = [-0.1644,0.295,-0.988,1,-0.0001,0.0002,0.001]; - -%second pose of hydrophone 2 -pose2_h1 = [-.1644,2.2512,-.9885,.9992,0.0402,0,0.0002]; - -%pinger pose -pose_pinger = [3, 2,-1,0,0,0,0]; - -%calculaete the second hydrophone poses -rotm1 = quat2rotm([pose1_h1(4), pose1_h1(5), pose1_h1(6), pose1_h1(7)]); -pose1_h2 = (rotm1 * base_vector' + pose1_h1(1:3)')'; - -rotm2 = quat2rotm([pose2_h1(4), pose2_h1(5), pose2_h1(6), pose2_h1(7)]); -pose2_h2 = (rotm2 * base_vector' + pose2_h1(1:3)')'; - -%calcate the distances for the four hydrophones from the pinger -dist1_h1 = norm(pose_pinger(1:3) - pose1_h1(1:3)); -dist1_h2 = norm(pose_pinger(1:3) - pose1_h2(1:3)); -dist2_h1 = norm(pose_pinger(1:3) - pose2_h1(1:3)); -dist2_h2 = norm(pose_pinger(1:3) - pose2_h2(1:3)); - -%calculate the time delta for each pose -delta_d1 = (dist1_h2 - dist1_h1); -delta_d2 = (dist2_h2 - dist2_h1); - -%calculate the distances othogonal from initial hydrophone base vector -pos1_mid = (pose1_h2 + pose1_h1(1:3)) / 2; -pos1_mid_to_pinger = [pose_pinger(1:2),0] - pos1_mid; - -%rotate to new frame -sol = rotm1 * pos1_mid_to_pinger'; - -%plot the senario -figure(1) -hold on; -plot3([pose1_h1(1);pose1_h2(1)], [pose1_h1(2);pose1_h2(2)],[pose1_h1(3);pose1_h2(3)], "b.-", [pose2_h1(1);pose2_h2(1)], [pose2_h1(2);pose2_h2(2)],[pose2_h1(3);pose2_h2(3)], "r.-") -plot3([pose_pinger(1)], [pose_pinger(2)], [pose_pinger(3)], "g*") -hold off - -%calcuate a1 and a2 values -disp("a1: " + num2str(norm(pose_pinger(1:2) - pose1_h1(1:2)))) -disp("a2: " + num2str(norm(pose_pinger(1:2) - pose2_h1(1:2)))) - -[solve_x_p, solve_y] = solve_two_pulse_system_depth(delta_d1, delta_d2, pose1_h1', pose2_h1', base_vector', 1, [100; 99], pose_pinger(3)); -[solve_x, solve_y] = solve_two_pulse_system_depth(delta_d1, delta_d2, pose1_h1', pose2_h1', base_vector', -1, [100; 99], pose_pinger(3)); diff --git a/riptide_acoustics/src/matlab/generatte_test_cases.m b/riptide_acoustics/src/matlab/generatte_test_cases.m deleted file mode 100644 index 768aeef..0000000 --- a/riptide_acoustics/src/matlab/generatte_test_cases.m +++ /dev/null @@ -1,57 +0,0 @@ - -close all -clear - -%speed of sound -speed_of_sound = 1500; - -%hydrophone base vector from 1 to 2 -base_vector = [0.2, 0.0, 0.0]; - -%initial pose of hydrophone 1 -%make sure to convert from ros quat to matlab quat -pose1_h1 = [0,0,0,1,-0.0001,0.0002,0.001]; - -%second pose of hydrophone 2 -pose2_h1 = [3.1644,0,0,.9992,0.0402,0,0]; - -%pinger pose -pose_pinger = [1, 7,-1,0,0,0,0]; - -%calculaete the second hydrophone poses -rotm1 = quat2rotm([pose1_h1(4), pose1_h1(5), pose1_h1(6), pose1_h1(7)]); -pose1_h2 = (rotm1 * base_vector' + pose1_h1(1:3)')'; - -rotm2 = quat2rotm([pose2_h1(4), pose2_h1(5), pose2_h1(6), pose2_h1(7)]); -pose2_h2 = (rotm2 * base_vector' + pose2_h1(1:3)')'; - -%calcate the distances for the four hydrophones from the pinger -dist1_h1 = norm(pose_pinger(1:3) - pose1_h1(1:3)); -dist1_h2 = norm(pose_pinger(1:3) - pose1_h2(1:3)); -dist2_h1 = norm(pose_pinger(1:3) - pose2_h1(1:3)); -dist2_h2 = norm(pose_pinger(1:3) - pose2_h2(1:3)); - -%calculate the time delta for each pose -delta_d1 = (dist1_h2 - dist1_h1); -delta_d2 = (dist2_h2 - dist2_h1); - -%calculate the distances othogonal from initial hydrophone base vector -pos1_mid = (pose1_h2 + pose1_h1(1:3)) / 2; -pos1_mid_to_pinger = [pose_pinger(1:2),0] - pos1_mid; - -%rotate to new frame -sol = rotm1 * pos1_mid_to_pinger'; - -%plot the senario -figure(1) -hold on; -plot3([pose1_h1(1);pose1_h2(1)], [pose1_h1(2);pose1_h2(2)],[pose1_h1(3);pose1_h2(3)], "b.-", [pose2_h1(1);pose2_h2(1)], [pose2_h1(2);pose2_h2(2)],[pose2_h1(3);pose2_h2(3)], "r.-") -plot3([pose_pinger(1)], [pose_pinger(2)], [pose_pinger(3)], "g*") -hold off - -%calcuate a1 and a2 values -disp("a1: " + num2str(norm(pose_pinger(1:2) - pose1_h1(1:2)))) -disp("a2: " + num2str(norm(pose_pinger(1:2) - pose2_h1(1:2)))) - -[solve_x_p, solve_y_p] = solve_two_pulse_system_depth(delta_d1, delta_d2, pose1_h1', pose2_h1', base_vector', 1, [7.41; 7.3], pose_pinger(3)); -[solve_x_n, solve_y_n] = solve_two_pulse_system_depth(delta_d1, delta_d2, pose1_h1', pose2_h1', base_vector', -1, [7.41; 7.3], pose_pinger(3)); diff --git a/riptide_acoustics/src/matlab/parse_measurements.asv b/riptide_acoustics/src/matlab/parse_measurements.asv deleted file mode 100644 index cae569e..0000000 --- a/riptide_acoustics/src/matlab/parse_measurements.asv +++ /dev/null @@ -1,38 +0,0 @@ -function pinger_location = parse_measurements(m1, m2, port_to_origin_transform, port_to_startboard_transform, speed_of_sound, pinger_depth) - - p2o_translation = [port_to_origin_transform.transform.translation.x; port_to_origin_transform.transform.translation.y; port_to_origin_transform.transform.translation.z]; - p2o_quat = [port_to_origin_transform.transform.rotation.w; port_to_origin_transform.transform.rotation.x; port_to_origin_transform.transform.rotation.y; port_to_origin_transform.transform.rotation.z]; - p2o_rotation = quat2rotm(p2o_quat'); - - %origin to world frame - o2m1_translation = [m1.auv_origin.transform.translation.x; m1.auv_origin.transform.translation.y; m1.auv_origin.transform.translation.z]; - o2m1_quat = [m1.auv_origin.transform.rotation.w; m1.auv_origin.transform.rotation.x; m1.auv_origin.transform.rotation.y; m1.auv_origin.transform.rotation.z]; - o2m1_rotation = quat2rotm(o2m1_quat'); - - - o2m2_translation = [m2.auv_origin.transform.translation.x; m2.auv_origin.transform.translation.y; m2.auv_origin.transform.translation.z]; - o2m2_quat = [m2.auv_origin.transform.rotation.w; m2.auv_origin.transform.rotation.x; m2.auv_origin.transform.rotation.y; m2.auv_origin.transform.rotation.z]; - o2m2_rotation = quat2rotm(o2m1_quat'); - - p2s_translation = [port_to_startboard_transform.transform.translation.x; port_to_startboard_transform.transform.translation.y; port_to_startboard_transform.transform.translation.z]; - - o2m1_rotation - o2m1_translation - - - %transform each hydrophone into world frame for each measurement - m1t = o2m1_rotation * p2o_translation + o2m1_translation; - m1r = transpose(quatmultiply(p2o_quat', o2m1_quat')); - m2t = o2m2_rotation * p2o_translation + o2m2_translation; - m2r = transpose(quatmultiply(p2o_quat', o2m2_quat')); - - o2m1_quat - p2o_quat - m1r - - %in world frame - [solve_x, solve_y, solve_depth] = solve_two_pulse_system_depth(m1.delta_t*speed_of_sound, m2.delta_t*speed_of_sound,[m1t;m1r],[m2t;m2r], p2s_translation, 3000, pinger_depth); - - pinger_location = ([solve_x; solve_y; pinger_depth]); - -end \ No newline at end of file diff --git a/riptide_acoustics/src/matlab/parse_measurements.m b/riptide_acoustics/src/matlab/parse_measurements.m deleted file mode 100644 index acd121b..0000000 --- a/riptide_acoustics/src/matlab/parse_measurements.m +++ /dev/null @@ -1,30 +0,0 @@ -function pinger_location = parse_measurements(m1, m2, port_to_origin_transform, port_to_startboard_transform, speed_of_sound, pinger_depth) - - p2o_translation = [port_to_origin_transform.transform.translation.x; port_to_origin_transform.transform.translation.y; port_to_origin_transform.transform.translation.z]; - p2o_quat = [port_to_origin_transform.transform.rotation.w; port_to_origin_transform.transform.rotation.x; port_to_origin_transform.transform.rotation.y; port_to_origin_transform.transform.rotation.z]; - p2o_rotation = quat2rotm(p2o_quat'); - - %origin to world frame - o2m1_translation = [m1.auv_origin.transform.translation.x; m1.auv_origin.transform.translation.y; m1.auv_origin.transform.translation.z]; - o2m1_quat = [m1.auv_origin.transform.rotation.w; m1.auv_origin.transform.rotation.x; m1.auv_origin.transform.rotation.y; m1.auv_origin.transform.rotation.z]; - o2m1_rotation = quat2rotm(o2m1_quat'); - - - o2m2_translation = [m2.auv_origin.transform.translation.x; m2.auv_origin.transform.translation.y; m2.auv_origin.transform.translation.z]; - o2m2_quat = [m2.auv_origin.transform.rotation.w; m2.auv_origin.transform.rotation.x; m2.auv_origin.transform.rotation.y; m2.auv_origin.transform.rotation.z]; - o2m2_rotation = quat2rotm(o2m1_quat'); - - p2s_translation = [port_to_startboard_transform.transform.translation.x; port_to_startboard_transform.transform.translation.y; port_to_startboard_transform.transform.translation.z]; - - %transform each hydrophone into world frame for each measurement - m1t = o2m1_rotation * p2o_translation + o2m1_translation; - m1r = transpose(quatmultiply(p2o_quat', o2m1_quat')); - m2t = o2m2_rotation * p2o_translation + o2m2_translation; - m2r = transpose(quatmultiply(p2o_quat', o2m2_quat')); - - %in world frame - [solve_x, solve_y, solve_depth] = solve_two_pulse_system_depth(m1.delta_t*speed_of_sound, m2.delta_t*speed_of_sound,[m1t;m1r],[m2t;m2r], p2s_translation, -1, [1;1], pinger_depth); - - pinger_location = ([solve_x; solve_y; pinger_depth]); - -end \ No newline at end of file diff --git a/riptide_acoustics/src/matlab/run_measurements_from_csv.asv b/riptide_acoustics/src/matlab/run_measurements_from_csv.asv deleted file mode 100644 index f1d1d1e..0000000 --- a/riptide_acoustics/src/matlab/run_measurements_from_csv.asv +++ /dev/null @@ -1,43 +0,0 @@ -clear; - -%constants -speed_of_sound = 1500; -pinger_depth = -1.0; - -%load in csv -filename = "data/test1.csv"; - -data = table2array(readtable(filename, "NumHeaderLines", 1)); - -%load into ros2 messages -transform = ros2message("geometry_msgs/TransformStamped"); - -line_1 = 2; -m1 = struct(); -m1.delta_t = data(line_1, 3); - -transform.transform.translation.x = data(line_1, 4); -transform.transform.translation.y = data(line_1, 5); -transform.transform.translation.z = data(line_1, 6); -transform.transform.rotation.w = data(line_1,7); -transform.transform.rotation.x = data(line_1,8); -transform.transform.rotation.y = data(line_1,9); -transform.transform.rotation.z = data(line_1,10); - -m1.auv_origin = transform; - -line_2 = 30; -m2 = struct(); -m2.delta_t = data(line_2, 3); - -transform.transform.translation.x = data(line_2, 4); -transform.transform.translation.y = data(line_2, 5); -transform.transform.translation.z = data(line_2, 6); -transform.transform.rotation.w = data(line_2,7); -transform.transform.rotation.x = data(line_2,8); -transform.transform.rotation.y = data(line_2,9); -transform.transform.rotation.z = data(line_2,10); - -m2.auv_origin = transform; - -parse_measurements(m1,m2,) diff --git a/riptide_acoustics/src/matlab/run_measurements_from_csv.m b/riptide_acoustics/src/matlab/run_measurements_from_csv.m deleted file mode 100644 index d1d284e..0000000 --- a/riptide_acoustics/src/matlab/run_measurements_from_csv.m +++ /dev/null @@ -1,42 +0,0 @@ -%run the node to use its initial parameters - -%load in csv -filename = "data/test3.csv"; - -data = table2array(readtable(filename, "NumHeaderLines", 1)); - -%load into ros2 messages -transform = ros2message("geometry_msgs/TransformStamped"); - -%pinger pose hardcoded -pinger_pose = [3.0,2.0,1.0,0.0,0.0,0.0]; - -line_1 = 7; -m1 = struct(); -m1.delta_t = data(line_1, 3); - -transform.transform.translation.x = data(line_1, 4); -transform.transform.translation.y = data(line_1, 5); -transform.transform.translation.z = data(line_1, 6); -transform.transform.rotation.w = data(line_1,7); -transform.transform.rotation.x = data(line_1,8); -transform.transform.rotation.y = data(line_1,9); -transform.transform.rotation.z = data(line_1,10); - -m1.auv_origin = transform; - -line_2 = 16; -m2 = struct(); -m2.delta_t = data(line_2, 3); - -transform.transform.translation.x = data(line_2, 4); -transform.transform.translation.y = data(line_2, 5); -transform.transform.translation.z = data(line_2, 6); -transform.transform.rotation.w = data(line_2,7); -transform.transform.rotation.x = data(line_2,8); -transform.transform.rotation.y = data(line_2,9); -transform.transform.rotation.z = data(line_2,10); - -m2.auv_origin = transform; - -parse_measurements(m1,m2,port_to_origin_transform, port_to_startboard_transform, speed_of_sound, pinger_depth) diff --git a/riptide_acoustics/src/matlab/solve_two_pulse_system_depth.asv b/riptide_acoustics/src/matlab/solve_two_pulse_system_depth.asv deleted file mode 100644 index 9d843dc..0000000 --- a/riptide_acoustics/src/matlab/solve_two_pulse_system_depth.asv +++ /dev/null @@ -1,126 +0,0 @@ -function [intersection_x, intersection_y, pinger_depth] = solve_two_pulse_system_depth(k1, k2, pose_1, pose_2, hydrophone_base_vector, estimated_distance, pinger_depth) -%k1: hydrophone 1 pulse time - hydrophone 2 pulse time for first pulse -%k2: hydrophone 1 pulse time - hydrophone 2 pulse time for second pulse -%pose_1: the pose of the front hydrophone at the time of the first pulse -%pose_2: the pose of the front hydrophone at the time of the second pulse -%hydrophone_base_vector: the position vector between hydrophone 1 and 2 -%estimated disance: the estimated distance from the front hydrophone and -%pinger -%pinger_depth: the estimated depth of the pinger - -%pose format = [x,y,z,rw,rx,ry,rz] - -pose_1(4:7) = quatmultiply(pose_1(4:7)', eul2quat([atan(hydrophone_base_vector(2)/hydrophone_base_vector(1)), 0, 0]))' -pose_2(4:7) = quatmultiply(pose_2(4:7)', eul2quat([atan(hydrophone_base_vector(2)/hydrophone_base_vector(1)), 0, 0]))'; -hydrophone_base_vector = [norm(hydrophone_base_vector(1:2)), 0, hydrophone_base_vector(3)]'; - -%base frame is hydrophone 1 pulse 1 = [0,0,0,0,0,0] -x1_1 = 0; -y1_1 = 0; -z1_1 = 0; - -pinger_depth = pinger_depth - pose_1(3); - -%calculate position of second hydrophone -%order [Z,Y,X] -eul_1 = quat2eul([pose_1(4), pose_1(5), pose_1(6), pose_1(7)]); -norm_matrix = eul2rotm([-eul_1(1), 0, 0]); -rot_1 = norm_matrix * quat2rotm([pose_1(4), pose_1(5), pose_1(6), pose_1(7)]); -pos2_1 = rot_1 * hydrophone_base_vector; - -pos1_2 = norm_matrix * [(pose_2(1) - pose_1(1)); pose_2(2) - pose_1(2); pose_2(3) - pose_1(3)]; - -%calculate position of second hydrophone in second pulse -rot_2 = norm_matrix * quat2rotm([pose_2(4), pose_2(5), pose_2(6), pose_2(7)]); -pos2_2 = rot_2 * hydrophone_base_vector + pos1_2; - -%base widths in x,y plane -h1 = norm([pos2_1(2),pos2_1(1)]); -h2 = norm([pos2_2(1) - pos1_2(1) , pos2_2(2) - pos1_2(2)]); - -%calculate delta positions -x1 = pos1_2(1) - x1_1; -x2 = pos2_2(1) - pos2_1(1); - -y1 = pos1_2(2) - y1_1; -y2 = pos2_2(2) - pos2_1(2); - -%changes in depth -dz1_1 = pinger_depth; -dz2_1 = pos2_1(3) + pinger_depth; -dz1_2 = pose_2(3) + pinger_depth; -dz2_2 = pos2_2(3) + pinger_depth; - -%calculate heading change -eul_2 = rotm2eul(rot_2); -heading_change = eul_2(1); - -%define core functions -K = @(a,k_bar,z1,z2) (-(2*a) + sqrt(4*a^2 + 4 * (k_bar^2 + 2 * k_bar * sqrt(a^2 + z1^2) + z1^2 - z2^2))) / 2; -Y = @(a,k,h) sqrt(a^2 + 2*a*k + k^2 - ((h^2 + 2*a*k +k^2)/(2*h))^2); -X = @(a,y,k,h) sqrt(a^2 - y^2) * -sign(k) - h / 2; - -norm1 = 5; -answer = [0;0]; -while(norm1 > .001) - - x0 = [rand() * estimated_distance;rand() * estimated_distance];% [estimated_distance; estimated_distance; estimated_distance; estimated_distance; k1 / 2 ; k2 / 2] * (.2 - rand()*.2); - - %equations of localization -% F = @(x) [sqrt(abs((x(1)+x(3))^2-x(5)^2))*sign((x(1)+x(3))^2-x(5)^2) + sqrt(abs((x(2)+x(4))^2-x(5)^2))*sign((x(1)+x(3))^2-x(5)^2) - sqrt(x1^2+y1^2); -% sqrt(abs((x(1)+x(3)+k1)^2-x(6)^2))*sign((x(1)+x(3))^2-x(5)^2) + sqrt(abs((x(2)+x(4)+k2)^2-x(6)^2))*sign((x(1)+x(3))^2-x(5)^2) - sqrt(x2^2+y2^2); -% (x(2)+x(4))^2 - (((x(1)+x(3))^2-(x(2)+x(4))^2-(x1^2+y1^2))/(-2*sqrt(x1^2+y1^2)))^2 - x(5)^2; -% (x(2)+x(4)+k2)^2 - (((x(1)+x(3)+k1)^2-(x(2)+x(4)+k2)^2-(x2^2+y2^2))/(-2*sqrt(x2^2+y2^2)))^2 - x(6)^2; -% .5*hydrophone_base_width^2 - k1^2 - 2*(k1+x(3))*x(3) - 2*k1*x(1) - 4*x(1)*x(3); -% .5*hydrophone_base_width^2 - k2^2 - 2*(k2+x(4))*x(4) - 2*k2*x(2) - 4*x(2)*x(4)]; - - - %cramers rule & determinant of rot matrix is one -% F = @(x) [cos(-heading_change)*(Y(x(1), k1, h1) - (y1 + y2) / 2) + (sin(-heading_change) * (X(x(1), Y(x(1), k1, h1), k1, h1) - (x1 + x2 - hydrophone_base_vector(1)) / 2)) - Y(x(2),k2, h2); -% (X(x(1), Y(x(1), k1, h1), k1, h1) - (x1 + x2 - hydrophone_base_vector(1)) / 2) * cos(-heading_change) - (Y(x(1), k1, h1) - (y1 + y2) / 2) * sin(-heading_change) - (X(x(2),Y(x(2),k2,h2), k2, h2))]; - F = @(x) [cos(-heading_change)*(Y(x(1), K(x(1), k1, dz1_1, dz2_1), h1) - (y1 + y2) / 2) + (sin(-heading_change) * (X(x(1), Y(x(1), K(x(1), k1, dz1_1, dz2_1), h1), K(x(1), k1, dz1_1, dz2_1), h1) - (x1 + x2) / 2)) - Y(x(2),K(x(2), k2, dz1_2, dz2_2), h2); - (X(x(1), Y(x(1), K(x(1), k1, dz1_1, dz2_1), h1), K(x(1), k1, dz1_1, dz2_1), h1) - (x1 + x2) / 2) * cos(-heading_change) - (Y(x(1), K(x(1), k1, dz1_1, dz2_1), h1) - (y1 + y2) / 2) * sin(-heading_change) - (X(x(2),Y(x(2),K(x(2), k2, dz1_2, dz2_2),h2), K(x(2), k2, dz1_2, dz2_2), h2))]; - - %options = optimoptions('fsolve','Display','iter', "OptimalityTolerance", 10e-12); - options = optimoptions("fsolve", 'Display', 'off', "OptimalityTolerance", 10e-6); - - %solve non-linear system - solve1 = fsolve(F, x0, options); - - norm1 = norm(F(solve1)); - answer = solve1; - -end - - -answer - -Y() - -%vector relative to pos 1 coordinate frame -%rotate to input frame -rel_vector = [X(answer(1),Y(answer(1), K(answer(1), k1, dz1_1, dz2_1), h1), K(answer(1), k1, dz1_1, dz2_1), h1); Y(answer(1), K(answer(1), k1, dz1_1, dz2_1), h1); 0] -global_vector = norm_matrix' * rel_vector + pose_1(1:3); - -%calculate position - -intersection_x = global_vector(1); -intersection_y = global_vector(2); - -%for quadratic fit -% intersection_x = (-(b2(2) - b1(2)) - sqrt(abs((b2(2) - b1(2))^2 - 4 * (b2(1) - b1(1))*(b2(3) - b1(3))))) / (2 * (b2(3) - b1(3))); -% intersection_y = b1(1) + b1(2)* intersection_x + b1(3) * intersection_x^2; - - -%plots for debug -% figure(1); -% plot(fx1(2,:),fy1, "or", rot_points(1,:), rot_points(2,:),"xg") - -% figure(2) -% w_vals = linspace(-1000,1000, 1000); -% h1_vals = b1(1) + b1(2) * w_vals + b1(3)*w_vals.^2; -% h2_vals = b2(2) * w_vals + b2(1) + b2(3) * w_vals.^2;%b2_intercept ; -% -% plot(w_vals, h1_vals,"r", w_vals, h2_vals, "g", [-1000], [1000], "bo") - - diff --git a/riptide_acoustics/src/matlab/solve_two_pulse_system_depth.m b/riptide_acoustics/src/matlab/solve_two_pulse_system_depth.m deleted file mode 100644 index 4d48998..0000000 --- a/riptide_acoustics/src/matlab/solve_two_pulse_system_depth.m +++ /dev/null @@ -1,130 +0,0 @@ -function [intersection_x, intersection_y, pinger_depth] = solve_two_pulse_system_depth(k1, k2, pose_1, pose_2, hydrophone_base_vector, y_sign, guess, pinger_depth) -%k1: hydrophone 1 pulse time - hydrophone 2 pulse time for first pulse -%k2: hydrophone 1 pulse time - hydrophone 2 pulse time for second pulse -%pose_1: the pose of the front hydrophone at the time of the first pulse -%pose_2: the pose of the front hydrophone at the time of the second pulse -%hydrophone_base_vector: the position vector between hydrophone 1 and 2 -%estimated disance: the estimated distance from the front hydrophone and -%pinger -%pinger_depth: the estimated depth of the pinger - -%pose format = [x,y,z,rw,rx,ry,rz] - -hydrophone_base_vector_rotm = eul2rotm([atan(hydrophone_base_vector(2)/hydrophone_base_vector(1)), 0, 0]) -pose_1(4:7) = quatmultiply(pose_1(4:7)', eul2quat([atan(hydrophone_base_vector(2)/hydrophone_base_vector(1)), 0, 0]))'; -pose_2(4:7) = quatmultiply(pose_2(4:7)', eul2quat([atan(hydrophone_base_vector(2)/hydrophone_base_vector(1)), 0, 0]))'; -hydrophone_base_vector = [norm(hydrophone_base_vector(1:2)), 0, hydrophone_base_vector(3)]'; - -%base frame is hydrophone 1 pulse 1 = [0,0,0,0,0,0] -x1_1 = 0; -y1_1 = 0; -z1_1 = 0; - -pinger_depth = pinger_depth - pose_1(3); - -%calculate position of second hydrophone -%order [Z,Y,X] -eul_1 = quat2eul([pose_1(4), pose_1(5), pose_1(6), pose_1(7)]); -norm_matrix = eul2rotm([-eul_1(1), 0, 0]); -rot_1 = norm_matrix * quat2rotm([pose_1(4), pose_1(5), pose_1(6), pose_1(7)]); -pos2_1 = rot_1 * hydrophone_base_vector; - -pos1_2 = norm_matrix * [(pose_2(1) - pose_1(1)); pose_2(2) - pose_1(2); pose_2(3) - pose_1(3)]; - -%calculate position of second hydrophone in second pulse -rot_2 = norm_matrix * quat2rotm([pose_2(4), pose_2(5), pose_2(6), pose_2(7)]); -pos2_2 = rot_2 * hydrophone_base_vector + pos1_2; - -%base widths in x,y plane -h1 = norm([pos2_1(2),pos2_1(1)]); -h2 = norm([pos2_2(1) - pos1_2(1) , pos2_2(2) - pos1_2(2)]); - -%calculate delta positions -x1 = pos1_2(1) - x1_1; -x2 = pos2_2(1) - pos2_1(1); - -y1 = pos1_2(2) - y1_1; -y2 = pos2_2(2) - pos2_1(2); - -%changes in depth -dz1_1 = pinger_depth; -dz2_1 = pos2_1(3) + pinger_depth; -dz1_2 = pose_2(3) + pinger_depth; -dz2_2 = pos2_2(3) + pinger_depth; - -%calculate heading change -eul_2 = rotm2eul(rot_2); -heading_change = eul_2(1); - -%define core functions -K = @(a,k_bar,z1,z2) (-(2*a) + sqrt(4*a^2 + 4 * (k_bar^2 + 2 * k_bar * sqrt(a^2 + z1^2) + z1^2 - z2^2))) / 2; -Y = @(a,k,h) sign(y_sign) * sqrt(a^2 + 2*a*k + k^2 - ((h^2 + 2*a*k +k^2)/(2*h))^2); -X = @(a,y,k,h) sqrt(a^2 - y^2) * -sign(k) - h / 2; - -norm1 = 5; -answer = [0;0]; -count = 0; -while(norm1 > .001 && count < 50) - - x0 = guess;% [estimated_distance; estimated_distance; estimated_distance; estimated_distance; k1 / 2 ; k2 / 2] * (.2 - rand()*.2); - - %equations of localization -% F = @(x) [sqrt(abs((x(1)+x(3))^2-x(5)^2))*sign((x(1)+x(3))^2-x(5)^2) + sqrt(abs((x(2)+x(4))^2-x(5)^2))*sign((x(1)+x(3))^2-x(5)^2) - sqrt(x1^2+y1^2); -% sqrt(abs((x(1)+x(3)+k1)^2-x(6)^2))*sign((x(1)+x(3))^2-x(5)^2) + sqrt(abs((x(2)+x(4)+k2)^2-x(6)^2))*sign((x(1)+x(3))^2-x(5)^2) - sqrt(x2^2+y2^2); -% (x(2)+x(4))^2 - (((x(1)+x(3))^2-(x(2)+x(4))^2-(x1^2+y1^2))/(-2*sqrt(x1^2+y1^2)))^2 - x(5)^2; -% (x(2)+x(4)+k2)^2 - (((x(1)+x(3)+k1)^2-(x(2)+x(4)+k2)^2-(x2^2+y2^2))/(-2*sqrt(x2^2+y2^2)))^2 - x(6)^2; -% .5*hydrophone_base_width^2 - k1^2 - 2*(k1+x(3))*x(3) - 2*k1*x(1) - 4*x(1)*x(3); -% .5*hydrophone_base_width^2 - k2^2 - 2*(k2+x(4))*x(4) - 2*k2*x(2) - 4*x(2)*x(4)]; - - - %cramers rule & determinant of rot matrix is one -% F = @(x) [cos(-heading_change)*(Y(x(1), k1, h1) - (y1 + y2) / 2) + (sin(-heading_change) * (X(x(1), Y(x(1), k1, h1), k1, h1) - (x1 + x2 - hydrophone_base_vector(1)) / 2)) - Y(x(2),k2, h2); -% (X(x(1), Y(x(1), k1, h1), k1, h1) - (x1 + x2 - hydrophone_base_vector(1)) / 2) * cos(-heading_change) - (Y(x(1), k1, h1) - (y1 + y2) / 2) * sin(-heading_change) - (X(x(2),Y(x(2),k2,h2), k2, h2))]; - F = @(x) [cos(-heading_change)*(Y(x(1), K(x(1), k1, dz1_1, dz2_1), h1) - (y1 + y2) / 2) + (sin(-heading_change) * (X(x(1), Y(x(1), K(x(1), k1, dz1_1, dz2_1), h1), K(x(1), k1, dz1_1, dz2_1), h1) - (x1 + x2) / 2)) - Y(x(2),K(x(2), k2, dz1_2, dz2_2), h2); - (X(x(1), Y(x(1), K(x(1), k1, dz1_1, dz2_1), h1), K(x(1), k1, dz1_1, dz2_1), h1) - (x1 + x2) / 2) * cos(-heading_change) - (Y(x(1), K(x(1), k1, dz1_1, dz2_1), h1) - (y1 + y2) / 2) * sin(-heading_change) - (X(x(2),Y(x(2),K(x(2), k2, dz1_2, dz2_2),h2), K(x(2), k2, dz1_2, dz2_2), h2))]; - - %options = optimoptions('fsolve','Display','iter', "OptimalityTolerance", 10e-12); - options = optimoptions("fsolve", 'Display', 'off', "OptimalityTolerance", 10e-6); - - %solve non-linear system - solve1 = fsolve(F, x0, options); - - norm1 = norm(F(solve1)); - answer = solve1; - - count = count + 1; -end - - - -answer - -%vector relative to pos 1 coordinate frame -%rotate to input frame -rel_vector = [X(answer(1),Y(answer(1), K(answer(1), k1, dz1_1, dz2_1), h1), K(answer(1), k1, dz1_1, dz2_1), h1); Y(answer(1), K(answer(1), k1, dz1_1, dz2_1), h1); 0]; -global_vector = norm_matrix' * rel_vector + pose_1(1:3); - -%account for hydrophone base vector -hydrophone_base_account = norm_matrix' * hydrophone_base_vector - -%calculate position -intersection_x = global_vector(1); -intersection_y = global_vector(2); - -%for quadratic fit -% intersection_x = (-(b2(2) - b1(2)) - sqrt(abs((b2(2) - b1(2))^2 - 4 * (b2(1) - b1(1))*(b2(3) - b1(3))))) / (2 * (b2(3) - b1(3))); -% intersection_y = b1(1) + b1(2)* intersection_x + b1(3) * intersection_x^2; - - -%plots for debug -% figure(1); -% plot(fx1(2,:),fy1, "or", rot_points(1,:), rot_points(2,:),"xg") - -% figure(2) -% w_vals = linspace(-1000,1000, 1000); -% h1_vals = b1(1) + b1(2) * w_vals + b1(3)*w_vals.^2; -% h2_vals = b2(2) * w_vals + b2(1) + b2(3) * w_vals.^2;%b2_intercept ; -% -% plot(w_vals, h1_vals,"r", w_vals, h2_vals, "g", [-1000], [1000], "bo") - - diff --git a/riptide_acoustics/src/matlab/two_pulse_depth_test.m b/riptide_acoustics/src/matlab/two_pulse_depth_test.m deleted file mode 100644 index acb581c..0000000 --- a/riptide_acoustics/src/matlab/two_pulse_depth_test.m +++ /dev/null @@ -1,31 +0,0 @@ -%should yeild (52,116.92) -distances = [-20, 41.465]; -pose1 = [0,0,0,1,0,0,0]; -pose2 = [141.89, 22.89,0, .9915,0,0,-.1300]; -pinger_depth = 0; - - -[slv_x,slv_y] = solve_two_pulse_system_depth(distances(1),distances(2),pose1, pose2, [50;0;0], 3000, pinger_depth) - -%should yeild (52,116.92) -distances = [-10.893, 23.1377]; -pose1 = [0,0,0,1,0,0,0]; -pose2 = [141.89, 22.89,0, .9915,0,0,-.1300]; -pinger_depth = -200; - - -[slv_x,slv_y] = solve_two_pulse_system_depth(distances(1),distances(2),pose1, pose2, [50;0;0], 100000, pinger_depth) - - -%should yeild (152,216.92) -distances = [-10.893, 23.1377]; -pose1 = [100,100,0,1,0,0,0]; -pose2 = [241.89, 122.89,0, .9915,0,0,-.1300]; -pinger_depth = -200; - - -[slv_x,slv_y] = solve_two_pulse_system_depth(distances(1),distances(2),pose1, pose2, [50;0;0], 100000, pinger_depth) - -%should yeild (2998.5,95.04) -% distances = [2,19]; -% transform = [-95.04, 8.49, -.38207]; From 6e9f44dbc19d5932ceba8908fcb2f4ba259f21fd Mon Sep 17 00:00:00 2001 From: zehdari Date: Fri, 10 Jul 2026 18:22:54 -0400 Subject: [PATCH 37/50] new topic --- riptide_acoustics/src/Acoustics.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/riptide_acoustics/src/Acoustics.cpp b/riptide_acoustics/src/Acoustics.cpp index b61f7bf..afa2d2e 100644 --- a/riptide_acoustics/src/Acoustics.cpp +++ b/riptide_acoustics/src/Acoustics.cpp @@ -26,7 +26,7 @@ class Acoustics : public rclcpp::Node this->declare_parameter("percentile", 95.0); ampSubscription = this->create_subscription( - "ivc/pinger/selected_amp", 10, std::bind(&Acoustics::ampCallback, this, _1)); + "ivc/pinger/selected_freq_amp_stream", 10, std::bind(&Acoustics::ampCallback, this, _1)); resultPublisher = this->create_publisher( "acoustics/buffer_0_closer", 10); From d9cea86acac0bc911f65b809162c20081adf9916 Mon Sep 17 00:00:00 2001 From: zehdari Date: Fri, 10 Jul 2026 18:39:50 -0400 Subject: [PATCH 38/50] Added acoustics to hw launches --- riptide_hardware/launch/hardware.launch.py | 11 +++++++++++ riptide_hardware/launch/hardware_fake_dvl.launch.py | 12 ++++++++++++ 2 files changed, 23 insertions(+) diff --git a/riptide_hardware/launch/hardware.launch.py b/riptide_hardware/launch/hardware.launch.py index b656b9c..f4bdaa1 100644 --- a/riptide_hardware/launch/hardware.launch.py +++ b/riptide_hardware/launch/hardware.launch.py @@ -35,6 +35,11 @@ "launch", "apriltag.launch.py" ) +acoustics_launch_file = os.path.join( + get_package_share_directory('riptide_acoustics'), + "launch", "acoustics.launch.py" +) + def generate_launch_description(): return LaunchDescription([ DeclareLaunchArgument('robot', default_value="tempest", @@ -80,6 +85,12 @@ def generate_launch_description(): ('robot', LC('robot')), ] ), + IncludeLaunchDescription( + AnyLaunchDescriptionSource(acoustics_launch_file), + launch_arguments=[ + ('robot', LC('robot')), + ] + ), Node( package='riptide_hardware2', executable='simple_actuator_interface.py', diff --git a/riptide_hardware/launch/hardware_fake_dvl.launch.py b/riptide_hardware/launch/hardware_fake_dvl.launch.py index 60f147e..c16e307 100644 --- a/riptide_hardware/launch/hardware_fake_dvl.launch.py +++ b/riptide_hardware/launch/hardware_fake_dvl.launch.py @@ -30,6 +30,11 @@ "launch", "apriltag.launch.py" ) +acoustics_launch_file = os.path.join( + get_package_share_directory('riptide_acoustics'), + "launch", "acoustics.launch.py" +) + # opbox_launch_file = os.path.join( # get_package_share_directory('opbox_ros_client'), # "launch", "opbox_ros_client.launch.py" @@ -84,6 +89,13 @@ def generate_launch_description(): ('robot', LC('robot')), ] ), + + IncludeLaunchDescription( + AnyLaunchDescriptionSource(acoustics_launch_file), + launch_arguments=[ + ('robot', LC('robot')), + ] + ), # IncludeLaunchDescription( # AnyLaunchDescriptionSource(opbox_launch_file), # launch_arguments=[ From 1a89fcfe5fc94505d267386512196be93d248083 Mon Sep 17 00:00:00 2001 From: osu-uwrt-bot2 Date: Sat, 11 Jul 2026 06:07:19 -0400 Subject: [PATCH 39/50] Slight tuning, camera and torpedo pose --- riptide_descriptions/config/talos.yaml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/riptide_descriptions/config/talos.yaml b/riptide_descriptions/config/talos.yaml index d22f5be..e9dfdc4 100644 --- a/riptide_descriptions/config/talos.yaml +++ b/riptide_descriptions/config/talos.yaml @@ -28,7 +28,7 @@ controller: drag_enable: [1, 1, 0, 0, 0, 0] feed_forward: - base_wrench: [-0.2, -0.1, -3.00, 0.25, 1.875, 0.2] + base_wrench: [-0.2, -0.1, -3.00, 0.25, 2.15, 0.0] # base_wrench: [-1.0, -1.0, 0.0, 0.0, 0.0, 0.0] auto_tune: @@ -194,9 +194,9 @@ controller: # d_gains: [5.0, 5.0, 90.0, 0.1, 0.1, 0.03] # ethan tune 1 - p_gains: [3.0, 18.0, 200.0, 2.0, 0.0, 5.0] - i_gains: [0.0, 0.0, 0.0, 0.01, 0.0, 0.01] - d_gains: [5.0, 5.0, 90.0, 1.0, 20.0, 0.9] + p_gains: [3.0, 18.0, 200.0, 2.0, 0.1, 6.0] + i_gains: [0.0, 0.0, 0.0, 0.01, 0.1, 0.01] + d_gains: [5.0, 5.0, 90.0, 1.0, 30.0, 0.9] # ethan tune 2 # p_gains: [3.0, 18.0, 200.0, 5.0, 5.0, 1.0] @@ -344,7 +344,7 @@ poker: pose: [0.2286, 0.0127, -0.17145, 0, 0, 0] torpedoes: - pose: [0.048, 0.18200625, -0.1524, 0.0, 0.0, 0.0] #real z: -0.1524, real y: 0.17700625 + pose: [0.048, 0.18200625, -0.1324, 0.0, 0.0, 0.0] #real z: -0.1524, real y: 0.17700625 baseline: 0.0365125 droppers: @@ -361,7 +361,7 @@ cameras: [ type: zed, name: ffc, baseline: 0.050, # m. Distance between cameras (manually measured) - pose: [-0.0195, -0.0889, 0.02, -0.01, 0.05, 0] + pose: [-0.0195, -0.0889, 0.02, -0.01, 0.05, 0.015] }, { type: zed, From 09d5c55746c0a6c2d39d4d486adb399c3802acb2 Mon Sep 17 00:00:00 2001 From: osu-uwrt-bot2 Date: Sat, 11 Jul 2026 06:10:42 -0400 Subject: [PATCH 40/50] Different cal and depth mode --- riptide_hardware/cfg/ffc_config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/riptide_hardware/cfg/ffc_config.yaml b/riptide_hardware/cfg/ffc_config.yaml index 748cade..53b2ceb 100644 --- a/riptide_hardware/cfg/ffc_config.yaml +++ b/riptide_hardware/cfg/ffc_config.yaml @@ -8,7 +8,7 @@ pub_resolution: "NATIVE" # The resolution used for image and depth map publishing. 'NATIVE' to use the same `general.grab_resolution` - `CUSTOM` to apply the `general.pub_downscale_factor` downscale factory to reduce bandwidth in transmission pub_downscale_factor: 2.0 # rescale factor used to rescale image before publishing when 'pub_resolution' is 'CUSTOM' # pub_frame_rate: 30.0 - optional_opencv_calibration_file: "/home/ros/zed_cals/tirpak_ffc_charuco_2_tan.yaml" + optional_opencv_calibration_file: "/home/ros/zed_cals/tirpak_ffc_charuco_3.yaml" grab_resolution: 'HD1200' grab_frame_rate: 15 self_calib: false @@ -17,7 +17,7 @@ # svo: # svo_path: "/home/ros/svos/prequal/prequal1.svo2" depth: - depth_mode: "NEURAL_LIGHT" + depth_mode: "NEURAL_PLUS" max_depth: 4.0 pos_tracking: publish_tf: false From 7c1d357b7cce746f662b3336722db9f566c624cb Mon Sep 17 00:00:00 2001 From: Effo12345 <4p7septipuses@gmail.com> Date: Sat, 11 Jul 2026 06:43:34 -0400 Subject: [PATCH 41/50] Don't pub result on multiple samples --- riptide_acoustics/src/Acoustics.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/riptide_acoustics/src/Acoustics.cpp b/riptide_acoustics/src/Acoustics.cpp index afa2d2e..50817c7 100644 --- a/riptide_acoustics/src/Acoustics.cpp +++ b/riptide_acoustics/src/Acoustics.cpp @@ -60,6 +60,7 @@ class Acoustics : public rclcpp::Node buffers[activeBuffer].clear(); sampling = true; + haveResult = false; response->success = true; response->message = "Sampling into buffer " + std::to_string(activeBuffer); From ba8b292b68821237840e35aa7157f022828012df Mon Sep 17 00:00:00 2001 From: osu-uwrt-bot2 Date: Mon, 13 Jul 2026 07:24:22 -0400 Subject: [PATCH 42/50] More aggressive z and pitch --- riptide_descriptions/config/talos.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/riptide_descriptions/config/talos.yaml b/riptide_descriptions/config/talos.yaml index e9dfdc4..102383f 100644 --- a/riptide_descriptions/config/talos.yaml +++ b/riptide_descriptions/config/talos.yaml @@ -158,7 +158,7 @@ controller: # lambda: [0.1, 0.1, 10.0, 10.0, 10.0, 0.35] #first order eta - eta_order_0: [0.8, 1.0, 1.0, 0.2, 0.2, 0.3] + eta_order_0: [0.8, 1.0, 2.0, 0.2, 0.2, 0.3] #second order eta eta_order_1: [0.04, 0.04, 0.3, 0.0, 0.0, 0.0] @@ -194,7 +194,7 @@ controller: # d_gains: [5.0, 5.0, 90.0, 0.1, 0.1, 0.03] # ethan tune 1 - p_gains: [3.0, 18.0, 200.0, 2.0, 0.1, 6.0] + p_gains: [3.0, 18.0, 200.0, 2.0, 5.0, 6.0] i_gains: [0.0, 0.0, 0.0, 0.01, 0.1, 0.01] d_gains: [5.0, 5.0, 90.0, 1.0, 30.0, 0.9] From 8a5993b01f10ac0b0b00615a26c2e837561a6de6 Mon Sep 17 00:00:00 2001 From: osu-uwrt-bot2 Date: Mon, 13 Jul 2026 17:49:48 -0400 Subject: [PATCH 43/50] Pinger broker for persistent frequency --- riptide_hardware/CMakeLists.txt | 8 +++- riptide_hardware/launch/hardware.launch.py | 6 +++ .../launch/hardware_fake_dvl.launch.py | 8 +++- riptide_hardware/src/pinger_broker.cpp | 44 +++++++++++++++++++ 4 files changed, 64 insertions(+), 2 deletions(-) create mode 100644 riptide_hardware/src/pinger_broker.cpp diff --git a/riptide_hardware/CMakeLists.txt b/riptide_hardware/CMakeLists.txt index ee07eda..93588a0 100644 --- a/riptide_hardware/CMakeLists.txt +++ b/riptide_hardware/CMakeLists.txt @@ -30,7 +30,9 @@ include_directories(include ${EIGEN3_INCLUDE_DIRS}) # Add executable for the DepthConverter add_executable(depth_converter src/depth_converter.cpp) -ament_target_dependencies(depth_converter +add_executable(pinger_broker src/pinger_broker.cpp) + +set(deps rclcpp geometry_msgs tf2_ros @@ -42,9 +44,13 @@ ament_target_dependencies(depth_converter nortek_dvl_msgs ) +ament_target_dependencies(depth_converter ${deps}) +ament_target_dependencies(pinger_broker ${deps}) + # Install the C++ executable install(TARGETS depth_converter + pinger_broker DESTINATION lib/${PROJECT_NAME} ) diff --git a/riptide_hardware/launch/hardware.launch.py b/riptide_hardware/launch/hardware.launch.py index f4bdaa1..76190ae 100644 --- a/riptide_hardware/launch/hardware.launch.py +++ b/riptide_hardware/launch/hardware.launch.py @@ -110,6 +110,12 @@ def generate_launch_description(): "imu_pwr_cycle.yaml" ) ] + ), + Node( + package='riptide_hardware2', + executable='pinger_broker', + name='pinger_broker', + output='screen' ) ], scoped=True) ]) diff --git a/riptide_hardware/launch/hardware_fake_dvl.launch.py b/riptide_hardware/launch/hardware_fake_dvl.launch.py index c16e307..cb75afb 100644 --- a/riptide_hardware/launch/hardware_fake_dvl.launch.py +++ b/riptide_hardware/launch/hardware_fake_dvl.launch.py @@ -113,6 +113,12 @@ def generate_launch_description(): executable='imu_power_cycle.py', name='imu_power_cycle', output='screen', - ) + ), + Node( + package='riptide_hardware2', + executable='pinger_broker', + name='pinger_broker', + output='screen' + ) ], scoped=True) ]) diff --git a/riptide_hardware/src/pinger_broker.cpp b/riptide_hardware/src/pinger_broker.cpp new file mode 100644 index 0000000..6a8c736 --- /dev/null +++ b/riptide_hardware/src/pinger_broker.cpp @@ -0,0 +1,44 @@ +#include "rclcpp/rclcpp.hpp" +#include "std_msgs/msg/int32.hpp" + +#include + + +using std::placeholders::_1; +using namespace std::chrono_literals; + +class PingerBroker : public rclcpp::Node { +public: + PingerBroker() : Node("pinger_broker") { + freqSelPub = this->create_publisher("ivc/pinger/set_freq_khz", 10); + freqSelSub = this->create_subscription("ivc/pinger/set_freq_broker_khz", 10, std::bind(&PingerBroker::freqSetCb, this, _1)); + + pubTimer = this->create_wall_timer(1000ms, std::bind(&PingerBroker::pubFreq, this)); + } + +private: + void freqSetCb(const std_msgs::msg::Int32& msg) { + freq_khz = msg.data; + } + + void pubFreq() { + std_msgs::msg::Int32 msg; + msg.data = freq_khz; + freqSelPub->publish(msg); + } + + rclcpp::Publisher::SharedPtr freqSelPub; + rclcpp::Subscription::SharedPtr freqSelSub; + + rclcpp::TimerBase::SharedPtr pubTimer; + + int freq_khz = 30; +}; + +int main(int argc, char * argv[]) { + rclcpp::init(argc, argv); + auto node = std::make_shared(); + rclcpp::spin(node); + rclcpp::shutdown(); + return 0; +} \ No newline at end of file From 14dd8055a96f24586f963068118b33de35315cce Mon Sep 17 00:00:00 2001 From: osu-uwrt-bot2 Date: Tue, 14 Jul 2026 21:32:52 -0400 Subject: [PATCH 44/50] Magnet link --- riptide_descriptions/urdf/robot_actuators.xacro | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/riptide_descriptions/urdf/robot_actuators.xacro b/riptide_descriptions/urdf/robot_actuators.xacro index 010ba49..3dc2cf9 100644 --- a/riptide_descriptions/urdf/robot_actuators.xacro +++ b/riptide_descriptions/urdf/robot_actuators.xacro @@ -114,4 +114,12 @@ parent="${namespace}/origin" pose="${config['claw']['pose']}" /> + + + + From f1498c1508a9a18ff64d29cb109e26f782aaa4d9 Mon Sep 17 00:00:00 2001 From: osu-uwrt-bot2 Date: Tue, 14 Jul 2026 21:33:37 -0400 Subject: [PATCH 45/50] Magnet link and semis 1 tunes --- riptide_descriptions/config/simulator.yaml | 68 ++++++++-------------- 1 file changed, 23 insertions(+), 45 deletions(-) diff --git a/riptide_descriptions/config/simulator.yaml b/riptide_descriptions/config/simulator.yaml index 82e16bb..55578a5 100644 --- a/riptide_descriptions/config/simulator.yaml +++ b/riptide_descriptions/config/simulator.yaml @@ -7,51 +7,29 @@ #Some of these may be depricated parameters from the vehicle! Even if it looks like a vehicle parameter, its not one! vehicle_properties: - talos: - base_wrench: [0.0, 0.0, -4.9, -0.95, 2.1, 0.0] - cod: [-0.148, 0.040, -0.055] - inertia3x3: [1.6315, -0.0599, -0.0330, # Ixx Ixy Ixz - -0.0599, 0.7341, 0.0427, # Iyx Iyy Iyz - -0.0330, 0.0427, 1.6332] # Izx Izy Izz kg*m^ - - #center of mass discrepancy - dcom: [0.01, 0.0, 0.0] - - # drag = (k1 + k2 * abs(v) + k3 * exp(abs(v) / k4) )* sign(v) - damping: [-38.7588, -28.1146, 37.3211, 0.5034, #x - -7.4651, 17.7641, 7.0013, 0.3067, #y - -7.0964, 27.6349, 6.9723, 0.3827, #z - -4.6881, 5.2662, 6.2630, 1.2739, #r - -18.1269, -3.1893, 17.3711, 2.0801, #p - -20.4148, -8.2547, 20.2604, 1.6177] #yaw - - thruster_max_force: 28.0 - - #sim discrepency - allows to test control when feed forward is not perfect - sim_discrepancy: [0.0, 1.0, -0.5, 0.1, -0.05, 0.0] - - env_force: [0.0, 0.7, 0.0] - - liltank: #TODO: measure or estimate all liltank parameters - cob: [0.0, 0.0, 0.15] - cod: [0.0, 0.0, -0.055] - inertia3x3: [1.6315, -0.0599, -0.0330, # Ixx Ixy Ixz - -0.0599, 0.7341, 0.0427, # Iyx Iyy Iyz - -0.0330, 0.0427, 1.6332] # Izx Izy Izz kg*m^2 - - # drag = (k1 + k2 * abs(v) + k3 * exp(abs(v) / k4) )* sign(v) - damping: [-38.7588, -28.1146, 37.3211, 0.5034, #x - -7.4651, 17.7641, 7.0013, 0.3067, #y - -7.0964, 27.6349, 6.9723, 0.3827, #z - -4.6881, 5.2662, 6.2630, 1.2739, #r - -18.1269, -3.1893, 17.3711, 2.0801, #p - -20.4148, -8.2547, 20.2604, 1.6177] #yaw - - thruster_max_force: 28.0 - - #sim discrepency - allows to test control when feed forward is not perfect - sim_discrepancy: [0.0, 0.0, 0.0, 0.0, 0.0, 0.0] - + base_wrench: [-0.35, -0.1, -3.00, 0.1, 2.4, 0.0] + cod: [-0.148, 0.040, -0.055] + inertia3x3: [1.6315, -0.0599, -0.0330, -0.0599, 0.7341, 0.0427, -0.0330, 0.0427, 1.6332] # Izx Izy Izz kg*m^ + + #center of mass discrepancy + dcom: [0.01, 0.0, 0.0] + + # drag = (k1 + k2 * abs(v) + k3 * exp(abs(v) / k4) )* sign(v) + damping: [-38.7588, -28.1146, 37.3211, 0.5034, #x + -7.4651, 17.7641, 7.0013, 0.3067, #y + -7.0964, 27.6349, 6.9723, 0.3827, #z + -4.6881, 5.2662, 6.2630, 1.2739, #r + -18.1269, -3.1893, 17.3711, 2.0801, #p + -20.4148, -8.2547, 20.2604, 1.6177] #yaw + + thruster_max_force: 28.0 + + + #sim discrepency - allows to test control when feed forward is not perfect +controller: + sim_discrepancy: [0.0, 1.0, -0.5, 0.1, -0.05, 0.0] + + env_force: [0.0, 0.0, 0.0] claw: fake_objects: #I've arbilitrarilly set these properties - measure at some point From d32ee3dbd83da7925dea64a030aff323aea189b1 Mon Sep 17 00:00:00 2001 From: osu-uwrt-bot2 Date: Tue, 14 Jul 2026 21:45:47 -0400 Subject: [PATCH 46/50] Semis1 tunes --- riptide_descriptions/config/talos.yaml | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/riptide_descriptions/config/talos.yaml b/riptide_descriptions/config/talos.yaml index 102383f..4a198f7 100644 --- a/riptide_descriptions/config/talos.yaml +++ b/riptide_descriptions/config/talos.yaml @@ -28,7 +28,7 @@ controller: drag_enable: [1, 1, 0, 0, 0, 0] feed_forward: - base_wrench: [-0.2, -0.1, -3.00, 0.25, 2.15, 0.0] + base_wrench: [-0.35, -0.1, -3.00, 0.1, 2.4, 0.0] # base_wrench: [-1.0, -1.0, 0.0, 0.0, 0.0, 0.0] auto_tune: @@ -158,13 +158,13 @@ controller: # lambda: [0.1, 0.1, 10.0, 10.0, 10.0, 0.35] #first order eta - eta_order_0: [0.8, 1.0, 2.0, 0.2, 0.2, 0.3] + eta_order_0: [0.8, 1.0, 4.0, 0.2, 0.2, 0.3] #second order eta - eta_order_1: [0.04, 0.04, 0.3, 0.0, 0.0, 0.0] + eta_order_1: [0.04, 0.04, 0.01, 0.0, 0.0, 0.0] #lambda - lambda: [0.2, 0.2, 1.0, 10.0, 10.0, 0.35] + lambda: [0.2, 0.2, 0.2, 10.0, 10.0, 0.35] #format: 'a, m' for each degree of freedom #where: @@ -356,12 +356,15 @@ hitch: claw: pose: [0.19, 0.0655, -0.3712, 0.0, 0.0, 0.0] +magnet: + pose: [-0.2946, 0.0475, -0.391, 0.0, 0.0, 0.0] + cameras: [ { type: zed, name: ffc, baseline: 0.050, # m. Distance between cameras (manually measured) - pose: [-0.0195, -0.0889, 0.02, -0.01, 0.05, 0.015] + pose: [-0.0195, -0.0889, 0.02, -0.01, 0.05, -0.0] }, { type: zed, From 80cd5e142c49ec62754e775cd390ac4b7eaf6184 Mon Sep 17 00:00:00 2001 From: Blake Date: Wed, 15 Jul 2026 02:12:42 -0400 Subject: [PATCH 47/50] remove percentile parameter in favor of mode parameter than can change between p95, avg, max, fallback to avg if max doesn't work well --- riptide_acoustics/launch/acoustics.launch.py | 6 +- riptide_acoustics/src/Acoustics.cpp | 75 +++++++++++++++----- 2 files changed, 59 insertions(+), 22 deletions(-) diff --git a/riptide_acoustics/launch/acoustics.launch.py b/riptide_acoustics/launch/acoustics.launch.py index 422a85f..0cc1d10 100644 --- a/riptide_acoustics/launch/acoustics.launch.py +++ b/riptide_acoustics/launch/acoustics.launch.py @@ -5,8 +5,8 @@ def generate_launch_description(): return LaunchDescription([ - DeclareLaunchArgument(name="percentile", default_value="95.0", - description="Percentile of each buffer used for the amplitude comparison"), + DeclareLaunchArgument(name="mode", default_value="max", + description="Mode of comparing buffer 0 and buffer 1 to find nearest pinger"), Node( package='riptide_acoustics', @@ -15,7 +15,7 @@ def generate_launch_description(): output='screen', parameters=[ { - "percentile": LaunchConfiguration("percentile") + "mode": LaunchConfiguration("mode") } ], respawn=True diff --git a/riptide_acoustics/src/Acoustics.cpp b/riptide_acoustics/src/Acoustics.cpp index 50817c7..0574fb1 100644 --- a/riptide_acoustics/src/Acoustics.cpp +++ b/riptide_acoustics/src/Acoustics.cpp @@ -9,6 +9,9 @@ #include "std_msgs/msg/bool.hpp" #include "std_srvs/srv/trigger.hpp" +#define MODE_MAX_FALLBACK_DIFF 1000.0f +#define P_VALUE 95.0f + using namespace std::placeholders; // 1) start_sample -> collect amplitudes for buffer 0 @@ -23,13 +26,13 @@ class Acoustics : public rclcpp::Node Acoustics() : Node("riptide_acoustics") { - this->declare_parameter("percentile", 95.0); + this->declare_parameter("mode", "avg"); ampSubscription = this->create_subscription( - "ivc/pinger/selected_freq_amp_stream", 10, std::bind(&Acoustics::ampCallback, this, _1)); + "/talos/ivc/pinger/selected_freq_amp_stream", 10, std::bind(&Acoustics::ampCallback, this, _1)); resultPublisher = this->create_publisher( - "acoustics/buffer_0_closer", 10); + "/talos/acoustics/buffer_0_closer", 10); resultTimer = this->create_wall_timer( std::chrono::seconds(1), std::bind(&Acoustics::publishResult, this)); @@ -84,7 +87,7 @@ class Acoustics : public rclcpp::Node if (activeBuffer == 0) { activeBuffer = 1; response->success = true; - response->message = "Buffer 0 closed with " + std::to_string(buffers[0].size()) + " samples"; + response->message = "With mode [" + this->get_parameter("mode").as_string() + "] Buffer 0 closed with " + std::to_string(buffers[0].size()) + " samples"; return; } @@ -103,17 +106,14 @@ class Acoustics : public rclcpp::Node return; } - float value0 = reduceBuffer(buffers[0]); - float value1 = reduceBuffer(buffers[1]); - - buffer0Closer = value0 >= value1; + reduceBuffers(buffers[0], buffers[1], this->get_parameter("mode").as_string()); haveResult = true; publishResult(); response->success = true; - response->message = "buffer " + std::to_string(buffer0Closer ? 0 : 1) + - " closer (buffer 0: " + std::to_string(value0) + - ", buffer 1: " + std::to_string(value1) + ")"; + response->message = "mode[" + this->get_parameter("mode").as_string() + "]buffer " + std::to_string(buffer0Closer ? 0 : 1) + + " closer (buffer 0: " + std::to_string(result_0) + + ", buffer 1: " + std::to_string(result_1) + ")"; RCLCPP_INFO(this->get_logger(), "%s", response->message.c_str()); } @@ -128,15 +128,49 @@ class Acoustics : public rclcpp::Node resultPublisher->publish(msg); } - float reduceBuffer(const std::vector & buffer) - { - double p = this->get_parameter("percentile").as_double(); + void compare_avg_buffers(const std::vector &buffer0, const std::vector &buffer1) { + result_0 = std::accumulate(buffer0.begin(), buffer0.end(), 0.0) / buffer0.size(); + result_1 = std::accumulate(buffer1.begin(), buffer1.end(), 0.0) / buffer1.size(); + buffer0Closer = result_0 >= result_1; + } + + void compare_p95_buffers(const std::vector &buffer0, const std::vector &buffer1) { + // it's c++ so sorting is O(n) right? (fire bar - balke) + std::vector sorted0 = buffer0; + std::vector sorted1 = buffer1; + size_t idx_0 = static_cast(std::clamp((double)P_VALUE, 0.0, 100.0) / 100.0 * (sorted0.size() - 1)); + size_t idx_1 = static_cast(std::clamp((double)P_VALUE, 0.0, 100.0) / 100.0 * (sorted1.size() - 1)); + std::nth_element(sorted0.begin(), sorted0.begin() + idx_0, sorted0.end()); + std::nth_element(sorted1.begin(), sorted1.begin() + idx_1, sorted1.end()); + result_0 = sorted0[idx_0]; + result_1 = sorted1[idx_1]; + buffer0Closer = result_0 >= result_1; + } + + void compare_max_buffers(const std::vector &buffer0, const std::vector &buffer1) { + result_0 = *std::max_element(buffer0.begin(), buffer0.end()); + result_1 = *std::max_element(buffer1.begin(), buffer1.end()); + if (std::abs(result_0 - result_1) < MODE_MAX_FALLBACK_DIFF) { + compare_avg_buffers(buffer0, buffer1); + } + } + + void reduceBuffers(const std::vector &buffer0, const std::vector &buffer1, std::string mode) { + if (mode == "avg") { + compare_avg_buffers(buffer0, buffer1); + } - // it's c++ so sorting is O(n) right? - std::vector sorted = buffer; - size_t idx = static_cast(std::clamp(p, 0.0, 100.0) / 100.0 * (sorted.size() - 1)); - std::nth_element(sorted.begin(), sorted.begin() + idx, sorted.end()); - return sorted[idx]; + if (mode == "max") { + result_0 = *std::max_element(buffer0.begin(), buffer0.end()); + result_1 = *std::max_element(buffer1.begin(), buffer1.end()); + if (std::abs(result_0 - result_1) < MODE_MAX_FALLBACK_DIFF) { + compare_avg_buffers(buffer0, buffer1); + } + } + + if (mode == "p95") { + compare_p95_buffers(buffer0, buffer1); + } } rclcpp::Subscription::SharedPtr ampSubscription; @@ -153,6 +187,9 @@ class Acoustics : public rclcpp::Node // last comparison result, invalid until the first compare completes bool buffer0Closer = false; bool haveResult = false; + // the results of the comparison run on the buffers + float result_0 = 0.0f; + float result_1 = 0.0f; }; From 6f2c67b1f1c2c149115c6471b99e6e93ee47da18 Mon Sep 17 00:00:00 2001 From: Blake Date: Wed, 15 Jul 2026 12:31:49 -0400 Subject: [PATCH 48/50] make response messages more clear --- riptide_acoustics/src/Acoustics.cpp | 40 ++++++++++++++++++++++------- 1 file changed, 31 insertions(+), 9 deletions(-) diff --git a/riptide_acoustics/src/Acoustics.cpp b/riptide_acoustics/src/Acoustics.cpp index 0574fb1..ec4faef 100644 --- a/riptide_acoustics/src/Acoustics.cpp +++ b/riptide_acoustics/src/Acoustics.cpp @@ -19,7 +19,6 @@ using namespace std::placeholders; // 3) start_sample -> collect ampltudes for buffer 1 // 4) stop_sample -> close buffer 1, then compare and publish once to buffer_0_closer -// Using p95 atm, but could be changed pretty easily class Acoustics : public rclcpp::Node { public: @@ -96,6 +95,29 @@ class Acoustics : public rclcpp::Node activeBuffer = 0; } + std::string make_comparison_response(const std::string &mode) { + auto summary = [&](const std::string &mode_label) { + return "mode => [" + mode_label + "], result => buffer " + + std::to_string(buffer0Closer ? 0 : 1) + + " closer [buffer 0: " + std::to_string(result_0) + + " buffer 1: " + std::to_string(result_1) + "]"; + }; + + std::string resp = ""; + if (mode == "avg") { + resp = summary("avg"); + } else if (mode == "max") { + if (did_fallback_on_max) { + resp += "INFO: [FELLBACK TO AVG ON MAX MODE] "; + } + resp += summary("max"); + } else if (mode == "p95") { + resp = summary("p95"); + resp = "UNRECOGNIZED MODE, CHECK LAUNCH FILE OR SOMETHING "; + } + return resp; + } + void compareBuffers(std_srvs::srv::Trigger::Response::SharedPtr response) { if (buffers[0].empty() || buffers[1].empty()) { @@ -111,9 +133,10 @@ class Acoustics : public rclcpp::Node publishResult(); response->success = true; - response->message = "mode[" + this->get_parameter("mode").as_string() + "]buffer " + std::to_string(buffer0Closer ? 0 : 1) + - " closer (buffer 0: " + std::to_string(result_0) + - ", buffer 1: " + std::to_string(result_1) + ")"; + response->message = make_comparison_response(this->get_parameter("mode").as_string()); + // response->message = "mode[" + this->get_parameter("mode").as_string() + "]buffer " + std::to_string(buffer0Closer ? 0 : 1) + + // " closer (buffer 0: " + std::to_string(result_0) + + // ", buffer 1: " + std::to_string(result_1) + ")"; RCLCPP_INFO(this->get_logger(), "%s", response->message.c_str()); } @@ -151,6 +174,7 @@ class Acoustics : public rclcpp::Node result_0 = *std::max_element(buffer0.begin(), buffer0.end()); result_1 = *std::max_element(buffer1.begin(), buffer1.end()); if (std::abs(result_0 - result_1) < MODE_MAX_FALLBACK_DIFF) { + did_fallback_on_max = true; compare_avg_buffers(buffer0, buffer1); } } @@ -161,11 +185,7 @@ class Acoustics : public rclcpp::Node } if (mode == "max") { - result_0 = *std::max_element(buffer0.begin(), buffer0.end()); - result_1 = *std::max_element(buffer1.begin(), buffer1.end()); - if (std::abs(result_0 - result_1) < MODE_MAX_FALLBACK_DIFF) { - compare_avg_buffers(buffer0, buffer1); - } + compare_max_buffers(buffer0, buffer1); } if (mode == "p95") { @@ -187,9 +207,11 @@ class Acoustics : public rclcpp::Node // last comparison result, invalid until the first compare completes bool buffer0Closer = false; bool haveResult = false; + bool did_fallback_on_max = false; // the results of the comparison run on the buffers float result_0 = 0.0f; float result_1 = 0.0f; + }; From eb5723854bc1568a82f47857c783ee52b7e4add9 Mon Sep 17 00:00:00 2001 From: osu-uwrt-bot2 Date: Wed, 15 Jul 2026 17:41:55 -0400 Subject: [PATCH 49/50] Slightly better tunes --- riptide_descriptions/config/talos.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/riptide_descriptions/config/talos.yaml b/riptide_descriptions/config/talos.yaml index 4a198f7..a6c6626 100644 --- a/riptide_descriptions/config/talos.yaml +++ b/riptide_descriptions/config/talos.yaml @@ -158,10 +158,10 @@ controller: # lambda: [0.1, 0.1, 10.0, 10.0, 10.0, 0.35] #first order eta - eta_order_0: [0.8, 1.0, 4.0, 0.2, 0.2, 0.3] + eta_order_0: [0.8, 1.0, 2.0, 0.2, 0.2, 0.3] #second order eta - eta_order_1: [0.04, 0.04, 0.01, 0.0, 0.0, 0.0] + eta_order_1: [0.04, 0.04, 0.3, 0.0, 0.0, 0.0] #lambda lambda: [0.2, 0.2, 0.2, 10.0, 10.0, 0.35] @@ -194,9 +194,9 @@ controller: # d_gains: [5.0, 5.0, 90.0, 0.1, 0.1, 0.03] # ethan tune 1 - p_gains: [3.0, 18.0, 200.0, 2.0, 5.0, 6.0] + p_gains: [3.0, 18.0, 200.0, 3.0, 5.0, 6.0] i_gains: [0.0, 0.0, 0.0, 0.01, 0.1, 0.01] - d_gains: [5.0, 5.0, 90.0, 1.0, 30.0, 0.9] + d_gains: [5.0, 5.0, 90.0, 5.0, 30.0, 0.9] # ethan tune 2 # p_gains: [3.0, 18.0, 200.0, 5.0, 5.0, 1.0] From ad2e881eda11546189408ab1ad42d796dc7408cd Mon Sep 17 00:00:00 2001 From: osu-uwrt-bot2 Date: Thu, 16 Jul 2026 00:26:48 -0400 Subject: [PATCH 50/50] Talos namespace --- riptide_acoustics/src/Acoustics.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/riptide_acoustics/src/Acoustics.cpp b/riptide_acoustics/src/Acoustics.cpp index ec4faef..cb452a2 100644 --- a/riptide_acoustics/src/Acoustics.cpp +++ b/riptide_acoustics/src/Acoustics.cpp @@ -37,10 +37,10 @@ class Acoustics : public rclcpp::Node std::chrono::seconds(1), std::bind(&Acoustics::publishResult, this)); startSampleService = this->create_service( - "acoustics/start_sample", std::bind(&Acoustics::startSample, this, _1, _2)); + "/talos/acoustics/start_sample", std::bind(&Acoustics::startSample, this, _1, _2)); stopSampleService = this->create_service( - "acoustics/stop_sample", std::bind(&Acoustics::stopSample, this, _1, _2)); + "/talos/acoustics/stop_sample", std::bind(&Acoustics::stopSample, this, _1, _2)); } private: