From 5cf7a61bae16086d8155eebb5c3ed4de6ec23a80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Folles=C3=B8?= Date: Wed, 30 Sep 2026 15:11:52 +0200 Subject: [PATCH 1/6] Add Reach Robotics Reach Alpha 5 manipulator device id Co-Authored-By: Claude Opus 5.5 --- protobuf_definitions/message_formats.proto | 1 + 1 file changed, 1 insertion(+) diff --git a/protobuf_definitions/message_formats.proto b/protobuf_definitions/message_formats.proto index 72aff06c..10d6cd0a 100644 --- a/protobuf_definitions/message_formats.proto +++ b/protobuf_definitions/message_formats.proto @@ -1234,6 +1234,7 @@ enum GuestPortDeviceID { GUEST_PORT_DEVICE_ID_CYGNUS_MINI_ROV_THICKNESS_GAUGE_RS232 = 49; GUEST_PORT_DEVICE_ID_WATERLINKED_DVL_A100 = 50; // Waterlinked DVL A100. GUEST_PORT_DEVICE_ID_OUTLAND_TECHNOLOGY_MP200 = 51; // Outland Technology MP-200 Manipulator. + GUEST_PORT_DEVICE_ID_REACH_ROBOTICS_REACH_ALPHA_5 = 52; // Reach Robotics Reach Alpha 5 Manipulator. } // Guest port number. From 59c71fcc8e2ffb8ff153f1b742aac02d7b645169 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Folles=C3=B8?= Date: Wed, 30 Sep 2026 15:11:52 +0200 Subject: [PATCH 2/6] Add manipulator arm control and telemetry messages ManipulatorCtrl moves a manipulator arm by velocity, read in a control mode: cylindrical (the drone default) or cartesian in the drone frame. The arm velocities use the names and signs of MotionInput, so the same stick can drive the drone or the arm, and a speed modifier scales the drone's default speed down or up. The jaw and wrist velocities are the existing GripperVelocities; ManipulatorCtrl supersedes GripperCtrl while it keeps arriving, so clients that only send GripperCtrl keep working. ManipulatorPoseCtrl sends the arm to a named pose, stowed or deployed. ManipulatorTel reports the joint positions, the end position in the drone frame, and whether a joint limit, a collision or a fault stops the arm. Co-Authored-By: Claude Opus 5.5 --- protobuf_definitions/control.proto | 14 +++++++ protobuf_definitions/message_formats.proto | 43 ++++++++++++++++++++++ protobuf_definitions/telemetry.proto | 5 +++ 3 files changed, 62 insertions(+) diff --git a/protobuf_definitions/control.proto b/protobuf_definitions/control.proto index d4da5cd4..9988b37b 100644 --- a/protobuf_definitions/control.proto +++ b/protobuf_definitions/control.proto @@ -166,6 +166,20 @@ message GripperCtrl { GripperVelocities gripper_velocities = 1; // The desired gripping and rotation velocity. } +// Issue a command to move a manipulator arm. +// +// Supersedes GripperCtrl while it keeps arriving. The arm stops when the commands stop. +message ManipulatorCtrl { + ManipulatorVelocities velocities = 1; // The desired arm, gripping and rotation velocities. +} + +// Issue a command to move a manipulator arm to a named pose. +// +// A ManipulatorCtrl that asks for movement cancels it. +message ManipulatorPoseCtrl { + ManipulatorPose pose = 1; // The pose to move to. +} + // Issue a command to set a generic servo value. message GenericServoCtrl { GenericServo servo = 1; // Message with the desired servo value. diff --git a/protobuf_definitions/message_formats.proto b/protobuf_definitions/message_formats.proto index 10d6cd0a..bdb93672 100644 --- a/protobuf_definitions/message_formats.proto +++ b/protobuf_definitions/message_formats.proto @@ -265,6 +265,49 @@ message GripperVelocities { float rotate_velocity = 2; // The rotating velocity (-1.0..1.0). } +// How the arm velocities of ManipulatorVelocities are read. +enum ManipulatorControlMode { + MANIPULATOR_CONTROL_MODE_UNSPECIFIED = 0; // Drone default (cylindrical). + MANIPULATOR_CONTROL_MODE_CYLINDRICAL = 1; // Swing about the base, reach out from it, and heave. + MANIPULATOR_CONTROL_MODE_CARTESIAN = 2; // Surge, sway and heave of the end of the arm in the drone frame. +} + +// Manipulator velocity values. +// +// The arm velocities (-1.0..1.0) are scaled by the drone to its configured maximum speeds, and use +// the names and signs of MotionInput, so the same stick can drive the drone or the arm. +message ManipulatorVelocities { + ManipulatorControlMode mode = 1; // How to read the arm velocities. + float surge = 2; // Cartesian: forward (positive). Cylindrical: reach out from the base (positive). + float sway = 3; // Cartesian: right (positive). Unused in cylindrical mode. + float heave = 4; // Descend (positive) and ascend (negative). + float yaw = 5; // Cylindrical: swing clockwise (positive) about the base. Unused in cartesian mode. + GripperVelocities gripper_velocities = 6; // The desired gripping and rotation velocity. + // Speed of the arm relative to the drone default (-1.0..1.0): negative is slower, positive is faster, + // and 0 is the default speed. + float speed_modifier = 7; +} + +// Named manipulator poses. +enum ManipulatorPose { + MANIPULATOR_POSE_UNSPECIFIED = 0; // No pose. + MANIPULATOR_POSE_STOWED = 1; // Folded for transport, launch and recovery. + MANIPULATOR_POSE_DEPLOYED = 2; // Unfolded and ready to work. +} + +// Manipulator state. +message Manipulator { + ManipulatorControlMode mode = 1; // Mode of the last command. + // Joint positions from the jaws to the base: jaws opening (m), then joint angles (rad). + repeated float joint_positions = 2; + Vector3 end_position = 3; // Position of the base of the jaws in the drone frame (m). + bool at_limit = 4; // A joint at the end of its range keeps the arm from going further. + bool collision = 5; // The arm stopped itself short of an obstacle or of itself. + bool holding = 6; // The jaws closed on something. + bool fault = 7; // A joint reports a hardware fault. + ManipulatorPose moving_to = 8; // Named pose the arm is moving to, if any. +} + // Information about a remote client. message ClientInfo { string type = 1; // The type of client (such as Blueye App, Observer App, SDK, etc). diff --git a/protobuf_definitions/telemetry.proto b/protobuf_definitions/telemetry.proto index afe62869..391697dd 100644 --- a/protobuf_definitions/telemetry.proto +++ b/protobuf_definitions/telemetry.proto @@ -268,6 +268,11 @@ message MultibeamServoTel { float set_point = 2; // Servo set point (-30..30). } +// State of a manipulator arm, published while one is connected. +message ManipulatorTel { + Manipulator manipulator = 1; // Manipulator state. +} + // GuestPort current readings. message GuestPortCurrentTel { GuestPortCurrent current = 1; // Guest port current readings. From 20de6fb0f0919091f8f2098f5410821e632383e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Folles=C3=B8?= Date: Wed, 30 Sep 2026 15:27:13 +0200 Subject: [PATCH 3/6] Move arm input into GripperCtrl as a oneof Following review: one command path instead of a separate ManipulatorCtrl, so there is no arbitration between gripper and manipulator commands. GripperCtrl keeps gripper_velocities at field 1 and gains an arm_input oneof, CartesianManipulatorInput (x, y, z in the drone body frame) or CylindricalManipulatorInput (radial, vertical, azimuth about the arm's base axis), and speed_modifier. The chosen input says how the command is read, so the separate control mode field goes; ManipulatorControlMode stays for telemetry only. Without arm input the arm does not move. Co-Authored-By: Claude Opus 5.5 --- protobuf_definitions/control.proto | 20 +++++++---- protobuf_definitions/message_formats.proto | 40 ++++++++++++---------- 2 files changed, 34 insertions(+), 26 deletions(-) diff --git a/protobuf_definitions/control.proto b/protobuf_definitions/control.proto index 9988b37b..72cc58b3 100644 --- a/protobuf_definitions/control.proto +++ b/protobuf_definitions/control.proto @@ -162,20 +162,26 @@ message SystemTimeCtrl { } // Issue a command to control the gripper. +// +// On a manipulator arm the command can also move the arm. Without arm input the arm does not move, +// and the arm stops when the commands stop, as the gripper does. message GripperCtrl { GripperVelocities gripper_velocities = 1; // The desired gripping and rotation velocity. -} -// Issue a command to move a manipulator arm. -// -// Supersedes GripperCtrl while it keeps arriving. The arm stops when the commands stop. -message ManipulatorCtrl { - ManipulatorVelocities velocities = 1; // The desired arm, gripping and rotation velocities. + // The desired velocity of the end of a manipulator arm, if any. + oneof arm_input { + CartesianManipulatorInput cartesian = 2; // In the drone body frame. + CylindricalManipulatorInput cylindrical = 3; // About the axis of the arm's base joint. + } + + // Speed of the arm relative to the drone default (-1.0..1.0): negative is slower, positive is faster, + // and 0 is the default speed. + float speed_modifier = 4; } // Issue a command to move a manipulator arm to a named pose. // -// A ManipulatorCtrl that asks for movement cancels it. +// A GripperCtrl that asks for any movement cancels it. message ManipulatorPoseCtrl { ManipulatorPose pose = 1; // The pose to move to. } diff --git a/protobuf_definitions/message_formats.proto b/protobuf_definitions/message_formats.proto index bdb93672..f2d0d6bd 100644 --- a/protobuf_definitions/message_formats.proto +++ b/protobuf_definitions/message_formats.proto @@ -265,27 +265,29 @@ message GripperVelocities { float rotate_velocity = 2; // The rotating velocity (-1.0..1.0). } -// How the arm velocities of ManipulatorVelocities are read. -enum ManipulatorControlMode { - MANIPULATOR_CONTROL_MODE_UNSPECIFIED = 0; // Drone default (cylindrical). - MANIPULATOR_CONTROL_MODE_CYLINDRICAL = 1; // Swing about the base, reach out from it, and heave. - MANIPULATOR_CONTROL_MODE_CARTESIAN = 2; // Surge, sway and heave of the end of the arm in the drone frame. +// Cartesian velocity of the end of a manipulator arm, in the drone body frame. +// +// Each value is normalized (-1.0..1.0) and scaled by the drone to its configured maximum speed. +message CartesianManipulatorInput { + float x = 1; // Forward (positive) and backward (negative). + float y = 2; // Right (positive) and left (negative). + float z = 3; // Down (positive) and up (negative). } -// Manipulator velocity values. +// Cylindrical velocity of the end of a manipulator arm, about the axis of the arm's base joint. // -// The arm velocities (-1.0..1.0) are scaled by the drone to its configured maximum speeds, and use -// the names and signs of MotionInput, so the same stick can drive the drone or the arm. -message ManipulatorVelocities { - ManipulatorControlMode mode = 1; // How to read the arm velocities. - float surge = 2; // Cartesian: forward (positive). Cylindrical: reach out from the base (positive). - float sway = 3; // Cartesian: right (positive). Unused in cylindrical mode. - float heave = 4; // Descend (positive) and ascend (negative). - float yaw = 5; // Cylindrical: swing clockwise (positive) about the base. Unused in cartesian mode. - GripperVelocities gripper_velocities = 6; // The desired gripping and rotation velocity. - // Speed of the arm relative to the drone default (-1.0..1.0): negative is slower, positive is faster, - // and 0 is the default speed. - float speed_modifier = 7; +// Each value is normalized (-1.0..1.0) and scaled by the drone to its configured maximum speed. +message CylindricalManipulatorInput { + float radial = 1; // Out from the base axis (positive) and in towards it (negative). + float vertical = 2; // Along the base axis: down (positive) and up (negative), as in the drone body frame. + float azimuth = 3; // Swing about the base axis: clockwise seen from above (positive). +} + +// How the arm input of the last command was read, as reported in telemetry. +enum ManipulatorControlMode { + MANIPULATOR_CONTROL_MODE_UNSPECIFIED = 0; // No arm input. + MANIPULATOR_CONTROL_MODE_CYLINDRICAL = 1; // CylindricalManipulatorInput. + MANIPULATOR_CONTROL_MODE_CARTESIAN = 2; // CartesianManipulatorInput. } // Named manipulator poses. @@ -297,7 +299,7 @@ enum ManipulatorPose { // Manipulator state. message Manipulator { - ManipulatorControlMode mode = 1; // Mode of the last command. + ManipulatorControlMode mode = 1; // How the arm input of the last command was read. // Joint positions from the jaws to the base: jaws opening (m), then joint angles (rad). repeated float joint_positions = 2; Vector3 end_position = 3; // Position of the base of the jaws in the drone frame (m). From 96d7b41ab16d2814b8c31b7eaf16a074c291f2f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Folles=C3=B8?= Date: Wed, 30 Sep 2026 15:30:02 +0200 Subject: [PATCH 4/6] Say what a pose move and the speed modifier apply to A pose move runs to the end by itself; a nonzero arm_input or a lapse of the client watchdog cancels it, gripper and rotation velocities do not. speed_modifier scales arm_input only. The telemetry end position is in the drone body frame, as the cartesian input is. Co-Authored-By: Claude Opus 5.5 --- protobuf_definitions/control.proto | 8 +++++--- protobuf_definitions/message_formats.proto | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/protobuf_definitions/control.proto b/protobuf_definitions/control.proto index 72cc58b3..e6eb3224 100644 --- a/protobuf_definitions/control.proto +++ b/protobuf_definitions/control.proto @@ -174,14 +174,16 @@ message GripperCtrl { CylindricalManipulatorInput cylindrical = 3; // About the axis of the arm's base joint. } - // Speed of the arm relative to the drone default (-1.0..1.0): negative is slower, positive is faster, - // and 0 is the default speed. + // Speed of the arm input relative to the drone default (-1.0..1.0): negative is slower, positive is + // faster, and 0 is the default speed. Scales arm_input only, not gripper_velocities. float speed_modifier = 4; } // Issue a command to move a manipulator arm to a named pose. // -// A GripperCtrl that asks for any movement cancels it. +// The move runs to the end by itself; the client does not need to keep sending commands. A GripperCtrl +// with a nonzero arm_input cancels it, as does a lapse of the client watchdog (WatchdogCtrl). +// Gripper and rotation velocities do not cancel it. message ManipulatorPoseCtrl { ManipulatorPose pose = 1; // The pose to move to. } diff --git a/protobuf_definitions/message_formats.proto b/protobuf_definitions/message_formats.proto index f2d0d6bd..d01dc5a2 100644 --- a/protobuf_definitions/message_formats.proto +++ b/protobuf_definitions/message_formats.proto @@ -302,7 +302,7 @@ message Manipulator { ManipulatorControlMode mode = 1; // How the arm input of the last command was read. // Joint positions from the jaws to the base: jaws opening (m), then joint angles (rad). repeated float joint_positions = 2; - Vector3 end_position = 3; // Position of the base of the jaws in the drone frame (m). + Vector3 end_position = 3; // Position of the base of the jaws in the drone body frame (m). bool at_limit = 4; // A joint at the end of its range keeps the arm from going further. bool collision = 5; // The arm stopped itself short of an obstacle or of itself. bool holding = 6; // The jaws closed on something. From 08b3f6dbff0cdd130840a47fe6e5b9d7e52ff272 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Folles=C3=B8?= Date: Wed, 30 Sep 2026 17:59:45 +0200 Subject: [PATCH 5/6] Replace ManipulatorPoseCtrl with a hold-to-run known-pose arm input Moving the arm to a named pose becomes an explicit, continuous action: KnownPoseManipulatorInput is a third arm_input of GripperCtrl, holding the pose and a velocity along the drone's path to it (0..1). The arm moves only while the velocity is above zero and resumes from wherever it stopped, so releasing the control stops it, as for the other inputs. ManipulatorPoseCtrl and its separate cancellation rules go away, and the telemetry mode gains KNOWN_POSE. The GripperCtrl comment now matches how clients send it, on change: each command stands until the next one, and the arm stops on a command without arm input or when the client watchdog lapses. Co-Authored-By: Claude Opus 5.5 --- protobuf_definitions/control.proto | 14 +++----------- protobuf_definitions/message_formats.proto | 12 +++++++++++- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/protobuf_definitions/control.proto b/protobuf_definitions/control.proto index e6eb3224..cede7312 100644 --- a/protobuf_definitions/control.proto +++ b/protobuf_definitions/control.proto @@ -163,8 +163,8 @@ message SystemTimeCtrl { // Issue a command to control the gripper. // -// On a manipulator arm the command can also move the arm. Without arm input the arm does not move, -// and the arm stops when the commands stop, as the gripper does. +// On a manipulator arm the command can also move the arm. Each command stands until the next one: the +// arm stops on a command without arm input, or when the client watchdog (WatchdogCtrl) lapses. message GripperCtrl { GripperVelocities gripper_velocities = 1; // The desired gripping and rotation velocity. @@ -172,6 +172,7 @@ message GripperCtrl { oneof arm_input { CartesianManipulatorInput cartesian = 2; // In the drone body frame. CylindricalManipulatorInput cylindrical = 3; // About the axis of the arm's base joint. + KnownPoseManipulatorInput known_pose = 5; // Towards a named pose, such as stowed or deployed. } // Speed of the arm input relative to the drone default (-1.0..1.0): negative is slower, positive is @@ -179,15 +180,6 @@ message GripperCtrl { float speed_modifier = 4; } -// Issue a command to move a manipulator arm to a named pose. -// -// The move runs to the end by itself; the client does not need to keep sending commands. A GripperCtrl -// with a nonzero arm_input cancels it, as does a lapse of the client watchdog (WatchdogCtrl). -// Gripper and rotation velocities do not cancel it. -message ManipulatorPoseCtrl { - ManipulatorPose pose = 1; // The pose to move to. -} - // Issue a command to set a generic servo value. message GenericServoCtrl { GenericServo servo = 1; // Message with the desired servo value. diff --git a/protobuf_definitions/message_formats.proto b/protobuf_definitions/message_formats.proto index d01dc5a2..aafcac90 100644 --- a/protobuf_definitions/message_formats.proto +++ b/protobuf_definitions/message_formats.proto @@ -283,11 +283,21 @@ message CylindricalManipulatorInput { float azimuth = 3; // Swing about the base axis: clockwise seen from above (positive). } +// Movement of a manipulator arm towards a named pose, along a path the drone chooses. +// +// Hold-to-run: the arm moves only while the velocity is above zero, and resumes from wherever it +// stopped. +message KnownPoseManipulatorInput { + ManipulatorPose pose = 1; // The pose to move towards. + float velocity = 2; // Speed along the path (0.0..1.0): 0 holds the arm where it is. +} + // How the arm input of the last command was read, as reported in telemetry. enum ManipulatorControlMode { MANIPULATOR_CONTROL_MODE_UNSPECIFIED = 0; // No arm input. MANIPULATOR_CONTROL_MODE_CYLINDRICAL = 1; // CylindricalManipulatorInput. MANIPULATOR_CONTROL_MODE_CARTESIAN = 2; // CartesianManipulatorInput. + MANIPULATOR_CONTROL_MODE_KNOWN_POSE = 3; // KnownPoseManipulatorInput. } // Named manipulator poses. @@ -307,7 +317,7 @@ message Manipulator { bool collision = 5; // The arm stopped itself short of an obstacle or of itself. bool holding = 6; // The jaws closed on something. bool fault = 7; // A joint reports a hardware fault. - ManipulatorPose moving_to = 8; // Named pose the arm is moving to, if any. + ManipulatorPose moving_to = 8; // Named pose the arm is moving towards, if any. } // Information about a remote client. From 224adaf8e06e328bf2b40a6e7b09f2bf4af12313 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Folles=C3=B8?= Date: Thu, 1 Oct 2026 12:31:53 +0200 Subject: [PATCH 6/6] Rename ManipulatorTel to GripperTel To match GripperCtrl: GripperTel carries a Gripper, the state of the gripper and, on a manipulator arm, of the arm. Co-Authored-By: Claude Opus 5.5 --- protobuf_definitions/message_formats.proto | 4 ++-- protobuf_definitions/telemetry.proto | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/protobuf_definitions/message_formats.proto b/protobuf_definitions/message_formats.proto index aafcac90..7d9a7dcd 100644 --- a/protobuf_definitions/message_formats.proto +++ b/protobuf_definitions/message_formats.proto @@ -307,8 +307,8 @@ enum ManipulatorPose { MANIPULATOR_POSE_DEPLOYED = 2; // Unfolded and ready to work. } -// Manipulator state. -message Manipulator { +// Gripper state: of its arm too, when the gripper is a manipulator arm. +message Gripper { ManipulatorControlMode mode = 1; // How the arm input of the last command was read. // Joint positions from the jaws to the base: jaws opening (m), then joint angles (rad). repeated float joint_positions = 2; diff --git a/protobuf_definitions/telemetry.proto b/protobuf_definitions/telemetry.proto index 391697dd..9219f987 100644 --- a/protobuf_definitions/telemetry.proto +++ b/protobuf_definitions/telemetry.proto @@ -268,9 +268,9 @@ message MultibeamServoTel { float set_point = 2; // Servo set point (-30..30). } -// State of a manipulator arm, published while one is connected. -message ManipulatorTel { - Manipulator manipulator = 1; // Manipulator state. +// State of a gripper, published while one is connected. +message GripperTel { + Gripper gripper = 1; // Gripper state. } // GuestPort current readings.