Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
d506115
Add sonar SLAM pose, map and shot messages
follesoe Nov 3, 2026
51ba7f1
Add the sonar's field of view and range to SlamShot
follesoe Nov 3, 2026
8f5a099
Make the SLAM messages sensor-neutral, publish the map on the SLAM st…
follesoe Nov 3, 2026
8e2d416
Add requests to start and stop the SLAM, and its state
follesoe Nov 4, 2026
79bc1ea
Add the SLAM sensor's settings, starting with a 3D sonar's mode and r…
follesoe Nov 4, 2026
01a706f
Add the drone's pose and the mount's tilt to the SLAM estimate
follesoe Nov 4, 2026
3fffcde
Say what the SLAM's poses, settings and start mean exactly
follesoe Nov 4, 2026
18f8f0a
SLAM recording and frequency reset messages
follesoe Nov 4, 2026
4cad2a1
SLAM map frame and the drone's pose on keyframes
follesoe Nov 4, 2026
efae40b
SLAM poses are the platform's, with each sensor's mount and id
follesoe Nov 5, 2026
14750cc
DvlVelocity: the velocity's y is right, not left
follesoe Nov 6, 2026
06903a2
Keep SONAR_3D in the sonar frequency mode's values for protolint
follesoe Nov 6, 2026
4e32958
CI: prereleases of the TypeScript package from sonar3d-slam
follesoe Nov 7, 2026
ba63462
SLAM: loop closures in the map updates and the map
follesoe Nov 7, 2026
1bd6957
SLAM: sensor device ids, scan times, settings per sensor
follesoe Nov 7, 2026
f55364d
SLAM: review fixes
follesoe Nov 8, 2026
382f291
SLAM: no reserved fields before the first release
follesoe Nov 10, 2026
2714407
SLAM: a map in parts, packed moves, and room to replace and remove
follesoe Nov 10, 2026
f08fa80
SLAM: renumber the fields after the dropped ones
follesoe Nov 10, 2026
ab0fe42
SlamSensor: a sensor keeps its id while it is connected
follesoe Nov 10, 2026
69e4406
SLAM: catch up from recent updates, submap content, frame in the status
follesoe Nov 10, 2026
32827e4
SLAM: sensor_type, where the SLAM publishes, whole submaps in parts
follesoe Nov 10, 2026
7eed67c
SLAM: map_id is 0 when the SLAM has no map
follesoe Nov 10, 2026
94c0f49
SLAM: SlamSubmap numbered in order, no reserved sensor type
follesoe Nov 11, 2026
c3078c0
SLAM: status sequence and aiding, pose keyframe, why a request failed
follesoe Nov 11, 2026
572a9f0
SLAM: a sensor's new settings keep the map
follesoe Nov 11, 2026
5414815
SLAM: an empty sensor id fits only one sensor of the kind
follesoe Nov 11, 2026
62c4a00
SLAM: NO_SUCH_SENSOR's comment on its own line (protolint)
follesoe Nov 11, 2026
ec1a924
SLAM: a map's singular fields come from its first part; a breaking-ch…
follesoe Nov 13, 2026
21ad076
SLAM: every part of a map has its map_id, sequence and part_count
follesoe Nov 13, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions .github/workflows/ci-build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,23 @@ jobs:
with:
configDirectory: .

# Messages already out keep their fields' numbers and types: a renumbered,
# retyped or removed field fails against master (buf.yaml). Additions pass.
buf-breaking:
runs-on: ubuntu-latest
steps:
- name: Checkout source
uses: actions/checkout@v4
- name: Install buf
uses: bufbuild/buf-action@v1
with:
setup_only: true
- name: Check for breaking changes against master
run: |
git fetch --no-tags --depth=1 origin master
git worktree add --detach "$RUNNER_TEMP/master" FETCH_HEAD
buf breaking --against "$RUNNER_TEMP/master/protobuf_definitions"

build-linux:
runs-on: ubuntu-latest
steps:
Expand Down
23 changes: 19 additions & 4 deletions .github/workflows/ci-typescript.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ on:
push:
branches:
- master
# Prereleases for the SLAM pull requests (p2-django builds against
# them); remove before merging.
- sonar3d-slam
pull_request:

permissions:
Expand Down Expand Up @@ -75,12 +78,24 @@ jobs:
cp README.npm.md README.md
VERSION=$(pnpm pkg get version | tr -d '"')
SHORT_SHA=${GITHUB_SHA::8}
pnpm version --no-git-tag-version "${VERSION}-${SHORT_SHA}"
pnpm publish --no-git-checks --access public --tag latest
echo "PROTOCOL_VERSION=${VERSION}-${SHORT_SHA}" >> "$GITHUB_ENV"
# master is latest; another branch publishes a prerelease named and
# tagged after it, which leaves latest alone.
if [ "$GITHUB_REF_NAME" = master ]; then
RELEASE="${VERSION}-${SHORT_SHA}"
TAG=latest
else
TAG="${GITHUB_REF_NAME//\//-}"
# "g" before the hash: an all-digit one with a leading zero is not a
# valid SemVer prerelease identifier.
RELEASE="${VERSION}-${TAG}.g${SHORT_SHA}"
fi
pnpm version --no-git-tag-version "$RELEASE"
pnpm publish --no-git-checks --access public --tag "$TAG"
echo "PROTOCOL_VERSION=${RELEASE}" >> "$GITHUB_ENV"

- name: Trigger protocol bump in blueye-ts
if: github.event_name == 'push'
# Releases only: a branch's prerelease is not for blueye-ts.
if: github.event_name == 'push' && github.ref_name == 'master'
env:
GH_TOKEN: ${{ secrets.BLUEYE_PROTOCOL_BUMP_PAT }}
run: |
Expand Down
3 changes: 2 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,10 +72,11 @@ All proto definitions are in `protobuf_definitions/`:
- **req_rep.proto** — Request/response message patterns
- **mission_planning.proto** — Mission planning messages
- **aquatroll.proto** — AquaTroll sensor-specific messages
- **slam.proto** — SLAM state, poses, map and sensors (its `Slam*Tel` messages are in telemetry.proto and its requests in req_rep.proto)

## CI/CD Pipeline

- **ci-build.yaml** — Protolint validation (all pushes and PRs)
- **ci-build.yaml** — Protolint validation, and `buf breaking` against master (`buf.yaml`) so a renumbered, retyped or removed field fails (all pushes and PRs)
- **ci-typescript.yaml** — Generate TS, compile, smoke-test the packed tarball (PRs and master); publish to npm and trigger the blueye-ts bump (master only)
- **ci-dotnet.yaml** — Build and publish NuGet package (all pushes)
- **ci-python.yaml** — Triggers `blueye.protocol` repo update via repository dispatch (master, when proto files change)
Expand Down
8 changes: 8 additions & 0 deletions buf.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# buf config for the breaking-change check in CI (ci-build.yaml): a field that is
# renumbered, retyped or removed against master fails the build. Additions pass.
version: v2
modules:
- path: protobuf_definitions
breaking:
use:
- FILE
26 changes: 25 additions & 1 deletion protobuf_definitions/message_formats.proto
Original file line number Diff line number Diff line change
Expand Up @@ -738,7 +738,7 @@ message DvlVelocity {
int32 status = 2; // Vendor-specific status of the DVL.
float delta_time = 3; // Time since last velocity measurement (ms).
float fom = 4; // Figure of merit, a measure of the accuracy of the velocities (m/s).
Vector3 velocity = 5; // Velocity, x forward, y left, z down (m/s).
Vector3 velocity = 5; // Velocity, x forward, y right, z down (m/s).
bool is_water_tracking = 6; // Water tracking status.
repeated DvlTransducer transducers = 7; // List of transducers.
}
Expand Down Expand Up @@ -2083,3 +2083,27 @@ message Annotation {
google.protobuf.Duration duration = 7; // Optional span; omit for a point-in-time marker.
AnnotationSource source = 8; // Origin of the annotation; the app sets APP, the drone sets DRONE for its own.
}

// Unit quaternion describing a rotation (Hamilton convention: w + xi + yj + zk).
//
// Where it is a body's orientation, it rotates vectors from the body's frame to
// the frame the body is placed in.
message Quaternion {
double x = 1; // X-component of the vector part.
double y = 2; // Y-component of the vector part.
double z = 3; // Z-component of the vector part.
double w = 4; // Scalar part.
}

// Points packed for use as typed arrays without decoding them one by one.
//
// The frame the positions are in is the one the enclosing message names.
//
// count says how many points the bytes hold: positions is exactly count * 12 bytes,
// and intensities either empty or count bytes. A cloud whose bytes disagree with
// count is malformed, and is not used.
message PointCloud {
uint32 count = 1; // Number of points.
bytes positions = 2; // count x, y, z triplets of little-endian float32 (m): 12 bytes per point.
bytes intensities = 3; // count signal strengths, one byte each (0 to 255), or empty when unknown.
}
125 changes: 125 additions & 0 deletions protobuf_definitions/req_rep.proto
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ package blueye.protocol;
import "google/protobuf/any.proto";
import "message_formats.proto";
import "mission_planning.proto";
import "slam.proto";

option csharp_namespace = "Blueye.Protocol.Protobuf";
option java_multiple_files = true;
Expand Down Expand Up @@ -186,6 +187,26 @@ message SetLogFrequencyRep {
bool success = 1; // True if message name valid and frequency successfully updated.
}

// Request to reset the publishing frequency of a message to the drone's default.
message ResetPubFrequencyReq {
string message_type = 1; // Message name, f. ex. "AttitudeTel"; empty resets every message.
}

// Response after resetting the publishing frequency.
message ResetPubFrequencyRep {
bool success = 1; // True if the message name was valid and its frequency reset.
}

// Request to reset the logging frequency of a message to the drone's default.
message ResetLogFrequencyReq {
string message_type = 1; // Message name, f. ex. "AttitudeTel"; empty resets every message.
}

// Response after resetting the logging frequency.
message ResetLogFrequencyRep {
bool success = 1; // True if the message name was valid and its frequency reset.
}

// Request to get latest telemetry data
message GetTelemetryReq {
string message_type = 1; // Message name, f. ex. "AttitudeTel".
Expand Down Expand Up @@ -294,3 +315,107 @@ message GetMultibeamColorFilterReq {
message GetMultibeamColorFilterRep {
MultibeamColorFilter color_filter = 1; // The current color filter. Empty rgb means none is set (grayscale).
}

// Request to catch up on the SLAM map.
//
// Nothing comes in the reply but what is coming: the SLAM publishes it on its own
// publisher, to the clients subscribed there. The SLAM keeps its latest map
// updates: when map_id is the current map and the updates after have_sequence are
// still kept, it publishes those again (SlamMapUpdateTel, as they were, so a client
// that has them already skips them by their sequence); otherwise it publishes the
// whole map (SlamMapTel, in parts when it is large).
message GetSlamMapReq {
uint32 map_id = 1; // The map the client holds; 0 when it holds none.
uint64 have_sequence = 2; // The sequence of the last update the client applied to that map.
}

// Response after asking to catch up on the SLAM map: what is published for it.
message GetSlamMapRep {
bool available = 1; // True if the SLAM is running and answered.
uint32 map_id = 2; // The current map (0 when the SLAM has no map).
// The client has the map at this sequence once it has what is published. The whole map may be one
// made earlier (its parts say their sequence): the updates after it are published again after it.
uint64 sequence = 3;
oneof catch_up {
// The updates from from_sequence to sequence are published again (none when from_sequence is past
// sequence: the client has the map already).
uint64 from_sequence = 4;
uint32 part_count = 5; // The whole map is published, in this many SlamMapTel parts.
}
}

// Request to discard the SLAM map and start a new one from the current pose.
message ResetSlamReq {
// The map to discard: the SLAM resets only if it is still the current map, so that two clients
// asking at once start one new map, not two. 0 (map ids never are): only if the SLAM has no map yet.
uint32 map_id = 1;
}

// Response after resetting the SLAM map.
message ResetSlamRep {
bool available = 1; // True if the SLAM is running and answered.
// Identifier of the map now (the new one, or the current one when the request's map_id was not
// the current map's); 0 when the SLAM has no map yet (it starts one with its next scan).
uint32 map_id = 2;
bool reset = 3; // True if the map was discarded.
SlamRequestError error = 4; // Why the map was not discarded, when it was not.
}

// Request to start or stop the SLAM.
//
// Starting switches the sensor on (for a sonar, its acoustics) and begins a new
// map from the current pose. Stopping switches the sensor off and keeps the map,
// so it can still be viewed. Asking for the state the SLAM is in already changes
// nothing (ResetSlamReq begins a new map). The SLAM reports its state in
// SlamStatusTel.
message SetSlamActiveReq {
bool active = 1; // True to start the SLAM, false to stop it.
}

// Response after starting or stopping the SLAM.
message SetSlamActiveRep {
bool available = 1; // True if the SLAM is running and answered.
bool active = 2; // True if the SLAM is now mapping (false if its sensor did not switch on).
SlamRequestError error = 3; // Why the SLAM is not in the state asked for, when it is not.
}

// Request to start or stop recording the SLAM.
//
// A recording needs the SLAM to be mapping, and stopping the SLAM stops it. It is
// stored with the drone's other media (picture, video and multibeam recordings).
// Like a multibeam recording it holds the drone's DroneInfoTel, at its start and
// when it changes, which names the dive log it belongs with (log_name).
// Asking for the state the recording is in already changes nothing.
message SetSlamRecordingReq {
bool record = 1; // True to start recording, false to stop.
bool sensor_data = 2; // Also the sensor's raw data, and the dive log faster, for running the SLAM offline.
StorageLocation storage_location = 3; // Where to store the recording.
}

// Response after starting or stopping a SLAM recording.
message SetSlamRecordingRep {
bool available = 1; // True if the SLAM is running and answered.
SlamRecordingState recording = 2; // The recording after the request.
SlamRequestError error = 3; // Why the recording is not in the state asked for, when it is not.
}

// Request to change the settings of a SLAM sensor.
//
// The settings go to the sensor named by sensor_id. Without one, they go to the
// SLAM's sensor of their kind when it has exactly one; with several, the request
// must name one (SLAM_REQUEST_ERROR_NO_SUCH_SENSOR otherwise). Settings left
// unspecified stay as they are. The map goes on at the resolution it was made
// with; the next new map (a start, or ResetSlamReq) takes the resolution the
// sensor's range then gives.
message SetSlamSensorSettingsReq {
SlamSensorSettings settings = 1; // The settings to change.
string sensor_id = 2; // The sensor to change (SlamSensor.id); empty: the SLAM's only sensor of the settings' kind.
}

// Response after changing the settings of a SLAM sensor.
message SetSlamSensorSettingsRep {
bool available = 1; // True if the SLAM is running and answered.
bool applied = 2; // True if the sensor took the settings.
SlamSensorSettings settings = 3; // The sensor's settings after the request.
SlamRequestError error = 4; // Why the settings were not applied, when they were not.
}
Loading
Loading