From 4769244a043934edc36189f303fb3bf47017937b Mon Sep 17 00:00:00 2001 From: Will Stuckey Date: Mon, 10 Aug 2026 17:00:02 -0400 Subject: [PATCH 1/2] initial commit of vision protocol docs --- docs-source/_static/site.css | 18 ++ docs-source/conf.py | 10 + docs-source/protocol.md | 84 ++++++- .../diagrams/coordinate-system-dark.svg | 37 +++ .../diagrams/coordinate-system-light.svg | 37 +++ .../protocol/diagrams/message-hierarchy.mmd | 26 +++ .../protocol/diagrams/multi-camera-merge.mmd | 21 ++ .../diagrams/multicast-join-sequence.mmd | 29 +++ .../protocol/diagrams/multicast-join.mmd | 17 ++ ...twork_fanout_truss_vision_highlight.drawio | 220 ++++++++++++++++++ docs-source/protocol/gamecontroller.md | 10 + docs-source/protocol/referee.md | 5 +- docs-source/protocol/teamclient.md | 7 - docs-source/protocol/vision.md | 164 ++++++++++++- 14 files changed, 670 insertions(+), 15 deletions(-) create mode 100644 docs-source/protocol/diagrams/coordinate-system-dark.svg create mode 100644 docs-source/protocol/diagrams/coordinate-system-light.svg create mode 100644 docs-source/protocol/diagrams/message-hierarchy.mmd create mode 100644 docs-source/protocol/diagrams/multi-camera-merge.mmd create mode 100644 docs-source/protocol/diagrams/multicast-join-sequence.mmd create mode 100644 docs-source/protocol/diagrams/multicast-join.mmd create mode 100644 docs-source/protocol/diagrams/ssl_field_network_fanout_truss_vision_highlight.drawio create mode 100644 docs-source/protocol/gamecontroller.md delete mode 100644 docs-source/protocol/teamclient.md diff --git a/docs-source/_static/site.css b/docs-source/_static/site.css index 4736d8e..09a77f9 100644 --- a/docs-source/_static/site.css +++ b/docs-source/_static/site.css @@ -36,3 +36,21 @@ .two-col-diagram-grid .mermaid-container { width: 100%; } + +/* For diagrams rendered with useMaxWidth: false (native size instead of + shrink-to-fit-column) — scroll instead of overflowing the page if it's + wider than the column. */ +.wide-mermaid pre.mermaid, +.wide-mermaid .mermaid-container { + overflow-x: auto; +} + +/* Scale the whole rendered diagram up, text included. Mermaid's own + actorFontSize/messageFontSize/noteFontSize sequence config keys don't + reach the rendered elements in the pinned mermaid version — they + stay hardcoded at 16px regardless. `zoom` (not `transform: scale`) + correctly reflows the layout box, so it doesn't overlap the content + below it. */ +.wide-mermaid svg { + zoom: 2; +} diff --git a/docs-source/conf.py b/docs-source/conf.py index 8fe8096..40cd321 100644 --- a/docs-source/conf.py +++ b/docs-source/conf.py @@ -71,6 +71,7 @@ DUAL_THEME_DIAGRAMS = [ "field/network/diagrams/ssl_field_network_fanout_truss.drawio", "field/network/diagrams/ssl_field_network_fanout_direct.drawio", + "protocol/diagrams/ssl_field_network_fanout_truss_vision_highlight.drawio", ] @@ -182,3 +183,12 @@ def setup(app): # Intel Core i7-7567U spec sheet — verified 2026-08-09 r"^https://www\.intel\.com/content/www/us/en/products/sku/97541/intel-core-i77567u-processor-4m-cache-up-to-4-00-ghz/specifications\.html$", ] + +# The docs link to many individual files in github.com/RoboCup-SSL/ssl-protocol-defs +# (one per proto). Anonymous requests to github.com get secondary-rate-limited fast, +# and linkcheck's default backoff (linkcheck_rate_limit_timeout=300s) means a single +# rate-limited host can stall the whole linkcheck run for many minutes per retry, +# looking like a hang rather than a check failure. Fail fast instead so a +# rate-limited link shows up as a normal report line — re-run later once the limit +# resets, or authenticate (linkcheck_auth) if this becomes a frequent CI problem. +linkcheck_rate_limit_timeout = 15.0 diff --git a/docs-source/protocol.md b/docs-source/protocol.md index ba27bee..77d0382 100644 --- a/docs-source/protocol.md +++ b/docs-source/protocol.md @@ -1,12 +1,94 @@ # League Protocols and Communication +[Just looking for the connection summary table?](#connection-summary-table) + +"How do I connect to an SSL field?" is one of the first questions a software team member will ask. This page and its +sub-pages document the process of connecting up: interfacing to the field, transport protocols, and data formats. If you +aren't already roughly familiar with the SSL field, checkout the [field documentation](field.md) to learn about the +layout and network structure. + +## What does a SSL field provide? + +RoboCup SSL fields provide two primary services _to teams_. + +1. Robot and Ball Localization (where are the robots and balls?) +1. Game State (game half, timeouts, penalties, etc.) + +This article first covers vision (field geometry, robot and ball localization), then game controller (game state). +Conceptually, teams first want to receive robot positions. It does not matter what the game state is if you can't see +anything. Ensuring rules and game state compliance comes next. + +## How do I connect to an SSL Field? + +1. **A team is never required to reply to the field.** Teams can and do play full matches only _receiving_ field vision + and game controller data. The only input required from a team is it's keeper number which is entered via the Game + Controller Operator or the Remote Control, neither of which is provided by the team. +1. **Field data delivery is not guaranteed.** It's sent at a high rate so **teams are expected to tolerate occasional + dropped packets without re-transmission.** Typically a re-transmission would take a few ms to execute, by which time + the next data frame is probably ready anyway. +1. **All required field data is sent via [multicast](https://en.wikipedia.org/wiki/Multicast) or + [broadcast]().** This allows the networking equipment to + duplicate the data stream as many times as needed without the league software managing dozens of connections. It also + means the team's software does not need to know the addresses or hostnames of any specific field computers in order + to connect. The team software simply needs to know the standard multicast addresses and ports of vision and game + controller, and you'll be able to connect to any standard field around the world. Specifically, these data streams + are [UDP](https://en.wikipedia.org/wiki/User_Datagram_Protocol) /[IPv4](https://en.wikipedia.org/wiki/IPv4) + +[IGMP](https://en.wikipedia.org/wiki/Internet_Group_Management_Protocol). +1. **All league packet data is defined by protocol buffers.** You can find the definitions in the + [ssl-protocol-defs repository](https://github.com/RoboCup-SSL/ssl-protocol-defs). + +The actual protocols and data formats are documented in the sections below. + +## Vision + +A detailed description of the vision protocol is provided in a dedicated article. + +[Vision Protocol](protocol/vision.md) + +## Game Controller + +A detailed description of the game controller protocol is provided in a dedicated article. + +[Game Controller Protocol](protocol/gamecontroller.md) + +## Connection Summary Table + +Below is the summary table of protocol addressing and ports. The GC address can be found by listening to the GC +multicast group and reading the [source address](https://en.wikipedia.org/wiki/IPv4#Source_address) field of the IP +layer, rather than knowing it statically. + +| Protocol | Protobuf | Type | Address | Port | +| ------------------------ | -------------------------------------------------------------------------------------------------------------------------------- | ------------- | ---------- | ----- | +| SSL-Game-Controller (GC) | [Referee](https://github.com/RoboCup-SSL/ssl-protocol-defs/blob/main/proto/gc/ssl_gc_referee_message.proto) | UDP Multicast | 224.5.23.1 | 10003 | +| SSL-Vision Detections | [SSL_WrapperPacket](https://github.com/RoboCup-SSL/ssl-protocol-defs/blob/main/proto/vision/ssl_vision_wrapper.proto) | UDP Multicast | 224.5.23.2 | 10006 | +| AutoRef -> GC | [AutoRef](https://github.com/RoboCup-SSL/ssl-protocol-defs/blob/main/proto/gc/ssl_gc_rcon_autoref.proto) | TCP | GC | 10007 | +| AutoRef -> GC | [AutoRef](https://github.com/RoboCup-SSL/ssl-protocol-defs/blob/main/proto/gc/ssl_gc_rcon_autoref.proto) | TCP + SSL | GC | 10107 | +| Team -> GC | [Team](https://github.com/RoboCup-SSL/ssl-protocol-defs/blob/main/proto/gc/ssl_gc_rcon_team.proto) | TCP | GC | 10008 | +| Team -> GC | [Team](https://github.com/RoboCup-SSL/ssl-protocol-defs/blob/main/proto/gc/ssl_gc_rcon_team.proto) | TCP + SSL | GC | 10108 | +| Remote Control -> GC | [Remote Control](https://github.com/RoboCup-SSL/ssl-protocol-defs/blob/main/proto/gc/ssl_gc_rcon_remotecontrol.proto) | TCP | GC | 10011 | +| Remote Control -> GC | [Remote Control](https://github.com/RoboCup-SSL/ssl-protocol-defs/blob/main/proto/gc/ssl_gc_rcon_remotecontrol.proto) | TCP + SSL | GC | 10111 | +| SSL-Vision-Tracker | [TrackerWrapperPacket](https://github.com/RoboCup-SSL/ssl-protocol-defs/blob/main/proto/vision/ssl_vision_wrapper_tracked.proto) | UDP Multicast | 224.5.23.2 | 10010 | +| Simulation Control | [SimulationControl](https://github.com/RoboCup-SSL/ssl-protocol-defs/blob/main/proto/simulation/ssl_simulation_control.proto) | UDP | Simulator | 10300 | +| Robot Control Blue | [RobotControl](https://github.com/RoboCup-SSL/ssl-protocol-defs/blob/main/proto/simulation/ssl_simulation_robot_control.proto) | UDP | Simulator | 10301 | +| Robot Control Yellow | [RobotControl](https://github.com/RoboCup-SSL/ssl-protocol-defs/blob/main/proto/simulation/ssl_simulation_robot_control.proto) | UDP | Simulator | 10302 | + +## Additional Resources + +- Unicast +- Multicast +- Broadcast +- TCP +- UDP +- IPv4 +- IGMPv4 + ```{toctree} --- maxdepth: 1 caption: Contents --- protocol/vision.md +protocol/gamecontroller.md protocol/referee.md -protocol/teamclient.md protocol/simulator.md ``` diff --git a/docs-source/protocol/diagrams/coordinate-system-dark.svg b/docs-source/protocol/diagrams/coordinate-system-dark.svg new file mode 100644 index 0000000..496f6e4 --- /dev/null +++ b/docs-source/protocol/diagrams/coordinate-system-dark.svg @@ -0,0 +1,37 @@ + + + + + + + + SSL Vision Coordinate System (schematic, not to scale) + + + + + + + + +X + +Y + + + + origin (0, 0) + position units: mm + + + + + + + + + + + θ + + robot + orientation θ: radians, measured counter-clockwise from +X + diff --git a/docs-source/protocol/diagrams/coordinate-system-light.svg b/docs-source/protocol/diagrams/coordinate-system-light.svg new file mode 100644 index 0000000..5b3418d --- /dev/null +++ b/docs-source/protocol/diagrams/coordinate-system-light.svg @@ -0,0 +1,37 @@ + + + + + + + + SSL Vision Coordinate System (schematic, not to scale) + + + + + + + + +X + +Y + + + + origin (0, 0) + position units: mm + + + + + + + + + + + θ + + robot + orientation θ: radians, measured counter-clockwise from +X + diff --git a/docs-source/protocol/diagrams/message-hierarchy.mmd b/docs-source/protocol/diagrams/message-hierarchy.mmd new file mode 100644 index 0000000..5f28b0c --- /dev/null +++ b/docs-source/protocol/diagrams/message-hierarchy.mmd @@ -0,0 +1,26 @@ +--- +config: + layout: elk + elk: + nodePlacementStrategy: NETWORK_SIMPLEX +--- +flowchart TD + Wrapper["SSL_WrapperPacket
one of detection / geometry present"] + Wrapper --> Detection["SSL_DetectionFrame
camera_id, frame_number,
t_capture, t_sent"] + Wrapper --> Geometry["SSL_GeometryData"] + + Detection --> Balls["balls[]
SSL_DetectionBall"] + Detection --> RY["robots_yellow[]
SSL_DetectionRobot"] + Detection --> RB["robots_blue[]
SSL_DetectionRobot"] + + Geometry --> Field["field
SSL_GeometryFieldSize"] + Geometry --> Calib["calib[]
SSL_GeometryCameraCalibration"] + + classDef slate fill:#f8fafc,stroke:#475569,color:#0f172a; + classDef cyan fill:#ecfeff,stroke:#0891b2,color:#164e63; + classDef purple fill:#faf5ff,stroke:#9333ea,color:#581c87; + classDef amber fill:#fffbeb,stroke:#d97706,color:#78350f; + class Wrapper slate; + class Detection,Geometry cyan; + class Balls,RY,RB purple; + class Field,Calib amber; diff --git a/docs-source/protocol/diagrams/multi-camera-merge.mmd b/docs-source/protocol/diagrams/multi-camera-merge.mmd new file mode 100644 index 0000000..61654e6 --- /dev/null +++ b/docs-source/protocol/diagrams/multi-camera-merge.mmd @@ -0,0 +1,21 @@ +--- +config: + layout: elk + elk: + nodePlacementStrategy: NETWORK_SIMPLEX +--- +flowchart LR + subgraph Cams["Cameras"] + C0["Camera 0"] + C1["Camera 1"] + CN["Camera N"] + end + + C0 -- "SSL_DetectionFrame
camera_id=0" --> Client["Team Client
merge by camera_id,
reconcile t_capture"] + C1 -- "camera_id=1" --> Client + CN -- "camera_id=N" --> Client + + classDef amber fill:#fffbeb,stroke:#d97706,color:#78350f; + classDef cyan fill:#ecfeff,stroke:#0891b2,color:#164e63; + class C0,C1,CN amber; + class Client cyan; diff --git a/docs-source/protocol/diagrams/multicast-join-sequence.mmd b/docs-source/protocol/diagrams/multicast-join-sequence.mmd new file mode 100644 index 0000000..24be954 --- /dev/null +++ b/docs-source/protocol/diagrams/multicast-join-sequence.mmd @@ -0,0 +1,29 @@ +%%{init: {"sequence": {"useMaxWidth": false, "actorMargin": 220, "messageMargin": 80}}}%% +sequenceDiagram + participant Team as Team Client + participant Net as Switch / Router
(multicast group 224.5.23.2:10006) + participant Vision as Vision Processor + + Team->>Net: IGMP Join 224.5.23.2:10006 + Note over Team,Vision: No handshake — Team is now another multicast receiver + + Vision->>Net: SSL_WrapperPacket
detection (frame 1) + Net->>Team: SSL_WrapperPacket
detection (frame 1) + + Vision->>Net: SSL_WrapperPacket
detection (frame 2) + Net->>Team: SSL_WrapperPacket
detection (frame 2) + + Note over Vision,Team: Geometry publisher re-sends field geometry every 1s + + Vision->>Net: SSL_WrapperPacket
geometry + Net->>Team: SSL_WrapperPacket
geometry + + Vision->>Net: SSL_WrapperPacket
detection (frame 3) + Net->>Team: SSL_WrapperPacket
detection (frame 3) + + Vision->>Net: SSL_WrapperPacket
detection (frame 4) + Net--xTeam: SSL_WrapperPacket
detection (frame 4) — dropped + Note over Team: No retransmission — team just waits for the next frame + + Vision->>Net: SSL_WrapperPacket
detection (frame 5) + Net->>Team: SSL_WrapperPacket
detection (frame 5) diff --git a/docs-source/protocol/diagrams/multicast-join.mmd b/docs-source/protocol/diagrams/multicast-join.mmd new file mode 100644 index 0000000..b6b2a22 --- /dev/null +++ b/docs-source/protocol/diagrams/multicast-join.mmd @@ -0,0 +1,17 @@ +--- +config: + layout: elk + elk: + nodePlacementStrategy: NETWORK_SIMPLEX +--- +flowchart TD + Team["Team Client
IGMP join only"] -- "IGMP Join
224.5.23.2:10006" --> Net["Switch / Router
duplicates stream"] + Vision["Vision Processor"] -- "SSL_WrapperPacket
UDP multicast, continuous, no ack" --> Net + Net -- "SSL_WrapperPacket" --> Team + + classDef purple fill:#faf5ff,stroke:#9333ea,color:#581c87; + classDef cyan fill:#ecfeff,stroke:#0891b2,color:#164e63; + classDef gray fill:#f1f5f9,stroke:#64748b,color:#1e293b; + class Team purple; + class Vision cyan; + class Net gray; diff --git a/docs-source/protocol/diagrams/ssl_field_network_fanout_truss_vision_highlight.drawio b/docs-source/protocol/diagrams/ssl_field_network_fanout_truss_vision_highlight.drawio new file mode 100644 index 0000000..a04ad25 --- /dev/null +++ b/docs-source/protocol/diagrams/ssl_field_network_fanout_truss_vision_highlight.drawio @@ -0,0 +1,220 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs-source/protocol/gamecontroller.md b/docs-source/protocol/gamecontroller.md new file mode 100644 index 0000000..a0eb2c8 --- /dev/null +++ b/docs-source/protocol/gamecontroller.md @@ -0,0 +1,10 @@ +# Game Controller Protocol + +The league's game management software, [Game Controller](https://github.com/RoboCup-SSL/ssl-game-controller), manages +game state and publishes it as this protocol. + +## Transport + +## Message Format + +## League Software diff --git a/docs-source/protocol/referee.md b/docs-source/protocol/referee.md index 8793b19..0b97c67 100644 --- a/docs-source/protocol/referee.md +++ b/docs-source/protocol/referee.md @@ -1,10 +1,9 @@ # Referee Protocol +Probably mostly document the vision filter data stream? + ## Transport ## Message Format ## League Software - -The league's game management software, [Game Controller](https://github.com/RoboCup-SSL/ssl-game-controller), manages -game state and publishes it as this protocol. diff --git a/docs-source/protocol/teamclient.md b/docs-source/protocol/teamclient.md deleted file mode 100644 index cc13712..0000000 --- a/docs-source/protocol/teamclient.md +++ /dev/null @@ -1,7 +0,0 @@ -# Team Client Protocol - -## Transport - -## Message Format - -## League Software diff --git a/docs-source/protocol/vision.md b/docs-source/protocol/vision.md index b03d690..dc4c08c 100644 --- a/docs-source/protocol/vision.md +++ b/docs-source/protocol/vision.md @@ -1,12 +1,168 @@ # Vision Protocol -## Transport - -## Message Format +This page describes the layers that deliver and decode the vision data stream from the field. ## League Software -There are two pieces of league maintained software that interpret camera data: +There are two pieces of league maintained software that interpret camera data to produce a vision data stream: - (Recommended) [Vision Processor](https://github.com/TIGERs-Mannheim/vision-processor) - (Legacy) [SSL Vision](https://github.com/RoboCup-SSL/ssl-vision) + +If you're actively trying to connect to a field, you'll need to make sure at least one is running and has a valid field +geometry calibration. More info about running each tool can be found in the repo linked above. + +## Transport + +The [transport layer](https://en.wikipedia.org/wiki/Transport_layer) is responsible for delivering raw data to the team +computers from the field. It doesn't say anything about the data it's delivering, but this is the first step of field +connectivity. As shown in the diagram below, a team computer connects to the field's vision data stream by joining the +vision multicast group. The league defines this multicast group as having an address of `224.5.23.2` at port `10006`. +You join a multicast group just like you connect to any IP address and port combo, though you may need to pass some +additional options when binding the socket. Consult the documentation for the language of your choosing. + +```{mermaid} diagrams/multicast-join.mmd +``` + +Once you've joined, you should expect to receive messages on a regular interval. Robot and Ball detections will arrive +at the framerate of the field camera(s), usually 60-75Hz. You will also receive field geometry data every few seconds. +**Do not assume the dimensions of the field in your software.** No field is perfect and your software/AI should always +operate on the real physically constructed field dimensions. These will be loaded into the league vision software, +calibration will occur, and final field geometry will be sent on the vision data channel. Always use this data as your +source of truth for the field. Competition fields are often off by up to a few cm and aspect ratio is not preserved. + +Shown in the join sequence diagram below is also a dropped detection frame. Note that re-transmission does not occur. +Packet loss should be _well below_ 1%. If it's not, network quality investigation is warranted. + +````{container} wide-mermaid +```{mermaid} diagrams/multicast-join-sequence.mmd +``` +```` + +### Connections Table + +The following table contains all relevant connection information related to vision data streams. + +| Protocol | Protobuf | Type | Address | Port | +| --------------------- | --------------------------------------------------------------------------------------------------------------------- | ------------- | ---------- | ----- | +| SSL-Vision Detections | [SSL_WrapperPacket](https://github.com/RoboCup-SSL/ssl-protocol-defs/blob/main/proto/vision/ssl_vision_wrapper.proto) | UDP Multicast | 224.5.23.2 | 10006 | + +## Message Format + +Now that a data stream is received, the raw data needs to be decoded. The league uses +[google protobuf](https://protobuf.dev/) to define the data format actually sent via multicast. The interface +definitions are available in the [league protobuf repository](https://github.com/RoboCup-SSL/ssl-protocol-defs). The +definition file there are needed to decode the raw data. The image below shows the hierarchy of the data structure send +in the data stream. + +```{mermaid} diagrams/message-hierarchy.mmd +``` + +There are two top level data structures, SSL_DetectionFrame and SSL_GeometryData. These correspond to the packet types +seen in the join sequence diagram above. SSL_DetectionFrame is the meat of the data stream, and as seen in the diagram, +contains only the positions and orientations of friendly robots, opponent robots, and balls. It also includes timing +information so you can tell if a packet has been lost or has arrived out of order. SSL_GeometryData is sent on a regular +1s interval. Most teams will only care about the SSL_GeometryFieldSize which contains information about the real +physical field dimensions. These will differ from the idealized values and teams should use the real ones. +SSL_GeometryCameraCalibration contains information about the camera calibration result. Many teams do not use the camera +calibration data, only the field size data. + +The table below contains links to the specific proto files needed to decode the mentioned data types. It's recommended +you include the entire [ssl-protocol-defs repository](https://github.com/RoboCup-SSL/ssl-protocol-defs) as a submodule +in your code, rather than copying the files directly. + +| Message | Proto File | +| ----------------------------- | -------------------------------------------------------------------------------------------------------------------------------- | +| SSL_WrapperPacket | [ssl_vision_wrapper.proto](https://github.com/RoboCup-SSL/ssl-protocol-defs/blob/main/proto/vision/ssl_vision_wrapper.proto) | +| SSL_DetectionFrame | [ssl_vision_detection.proto](https://github.com/RoboCup-SSL/ssl-protocol-defs/blob/main/proto/vision/ssl_vision_detection.proto) | +| SSL_DetectionBall | [ssl_vision_detection.proto](https://github.com/RoboCup-SSL/ssl-protocol-defs/blob/main/proto/vision/ssl_vision_detection.proto) | +| SSL_DetectionRobot | [ssl_vision_detection.proto](https://github.com/RoboCup-SSL/ssl-protocol-defs/blob/main/proto/vision/ssl_vision_detection.proto) | +| SSL_GeometryData | [ssl_vision_geometry.proto](https://github.com/RoboCup-SSL/ssl-protocol-defs/blob/main/proto/vision/ssl_vision_geometry.proto) | +| SSL_GeometryFieldSize | [ssl_vision_geometry.proto](https://github.com/RoboCup-SSL/ssl-protocol-defs/blob/main/proto/vision/ssl_vision_geometry.proto) | +| SSL_GeometryCameraCalibration | [ssl_vision_geometry.proto](https://github.com/RoboCup-SSL/ssl-protocol-defs/blob/main/proto/vision/ssl_vision_geometry.proto) | + +## Coordinate System + +At this point, a team should have valid decoded field geometry, robot positions, and ball positions. This section +discusses the actual units and coordinate frame of the decoded. Position units are in millimeters (mm) and angles are in +radians measured counter-clockwise from +X. Likewise, field geometry is in mm. + +Timestamps are of type `double` and are unix timestamps in seconds. The "frame_number" field is a monotonic counter. + +```{image} diagrams/coordinate-system-light.svg +--- +class: only-light +alt: SSL vision coordinate system, origin at field center, +X/+Y axes, and + orientation angle convention +--- +``` + +```{image} diagrams/coordinate-system-dark.svg +--- +class: only-dark +alt: SSL vision coordinate system, origin at field center, +X/+Y axes, and + orientation angle convention +--- +``` + +The coordinate frame is centered in the middle of the field, with +Y axis going up and the +X axis going right. When +facing the field from the side table, nominally the negative most corner will be at the viewers bottom left and the +positive most corner in the upper right. + +| Field | Unit | +| ----------------------------------------- | ---------------------------- | +| `x`, `y` (position) | mm | +| `orientation` | rad, CCW from +X | +| `t_capture`, `t_sent`, `t_capture_camera` | s (unix timestamp, `double`) | +| `frame_number` | monotonic counter, unitless | +| `confidence` | unitless, `[0-1]` | + +## Multi-camera Handling and Filtering + +SSL_DetectionsFrames carry only positions as seen by **one camera**. This means if you play on any field with more than +one camera, **you will receive a detection per camera**. When robots/balls overlap cameras, the data stream will contain +multiple detections of the object and the timestamps and positions may disagree slightly. This is intentional behavior, +as teams may disagree on the approach to handle detections at boundaries. For example, when kicking off on a 4 camera +field, all four cameras are likely to see the ball and robots at the center and you will receive 4 detections for each +entity. This is visualized in the graphic below. + +```{mermaid} diagrams/multi-camera-merge.mmd +``` + +There is no single agreed upon way to resolve these overlaps. An additional challenge is filtering position in velocity. +Teams will see noise in this derivative without filtering. Again, there are many approaches to solve this. The committee +notes that is a challenging problem in the SSL and robotics in general. Since 2023, the automatic referee software +packages in use by the game controller publish their filtered game state. This is so teams can inspect the data +interpretation behind issued penalties and infractions. It's also intended to be a "decent" or better implementation of +a shared data filter. Teams are allowed, and even encouraged, to use this filtered data in their AI. Eventually a team +may need to write a custom filter, but its expected that years would pass for a new team before the autoref filter is a +limiting factor. Many teams have written about +[vision filters in the SSL](https://tdpsearch.com/search?q=vision+filter&league=soccer_smallsize). + +**The [referee protocol](./referee.md) page documents how teams can use the filtered vision data from the automatic +referees.** + +## Network Flow + +The diagram below is the same competition network fan-out shown in the +[field network documentation](../field/network/compnetwork.md), with the machines involved in the vision stream +highlighted and everything else (game state / referee / broadcast only) muted. Cameras and the Truss NUC produce it, the +Vision Computer publishes the merged `224.5.23.2:10006` stream, and team PCs (plus, optionally, a streaming PC) consume +it. The router and switch are highlighted too — they maintain the multicast group (IGMP snooping/querier) the whole +stream depends on, even though they never decode the vision data stream. + +```{image} diagrams/ssl_field_network_fanout_truss_vision_highlight.light.svg +--- +class: only-light +alt: SSL field network fan-out diagram with vision-producing and + vision-consuming machines highlighted, everything else muted +--- +``` + +```{image} diagrams/ssl_field_network_fanout_truss_vision_highlight.dark.svg +--- +class: only-dark +alt: SSL field network fan-out diagram with vision-producing and + vision-consuming machines highlighted, everything else muted +--- +``` From 4a25ceb0caf0bf081136a2996be1e28d28c6117e Mon Sep 17 00:00:00 2001 From: Will Stuckey Date: Mon, 10 Aug 2026 18:17:38 -0400 Subject: [PATCH 2/2] add gc docs --- docs-source/_static/site.css | 35 ++- docs-source/conf.py | 1 + .../diagrams/multicast-join-sequence.mmd | 2 +- .../diagrams/rcon-handshake-sequence.mmd | 15 ++ .../diagrams/referee-broadcast-sequence.mmd | 18 ++ .../protocol/diagrams/referee-broadcast.mmd | 17 ++ .../protocol/diagrams/referee-hierarchy.mmd | 23 ++ ...d_network_fanout_truss_gc_highlight.drawio | 220 ++++++++++++++++++ docs-source/protocol/gamecontroller.md | 151 +++++++++++- docs-source/protocol/referee.md | 13 ++ docs-source/protocol/vision.md | 4 + 11 files changed, 493 insertions(+), 6 deletions(-) create mode 100644 docs-source/protocol/diagrams/rcon-handshake-sequence.mmd create mode 100644 docs-source/protocol/diagrams/referee-broadcast-sequence.mmd create mode 100644 docs-source/protocol/diagrams/referee-broadcast.mmd create mode 100644 docs-source/protocol/diagrams/referee-hierarchy.mmd create mode 100644 docs-source/protocol/diagrams/ssl_field_network_fanout_truss_gc_highlight.drawio diff --git a/docs-source/_static/site.css b/docs-source/_static/site.css index 09a77f9..ef20ba1 100644 --- a/docs-source/_static/site.css +++ b/docs-source/_static/site.css @@ -50,7 +50,38 @@ reach the rendered elements in the pinned mermaid version — they stay hardcoded at 16px regardless. `zoom` (not `transform: scale`) correctly reflows the layout box, so it doesn't overlap the content - below it. */ -.wide-mermaid svg { + below it. + + sphinxcontrib-mermaid's own CSS (pre.mermaid > svg, specificity 0,1,2) + forces every diagram's SVG to a fixed height (mermaid_height, default + 500px). For most diagrams that's load-bearing — it's what keeps a large + flowchart's raw viewBox from rendering at full intrinsic height, via + letterboxing (preserveAspectRatio: xMidYMid meet). It only becomes a + problem here because useMaxWidth:false + zoom already size these + diagrams from their own content, so the fixed-height letterbox just adds + blank bars on top. Scoped to wide-mermaid only — applying this globally + regressed other diagrams that rely on the fixed-height cap. */ +.wide-mermaid pre.mermaid > svg { + height: auto; zoom: 2; } + +/* Same letterbox fix as above (see comment), without the zoom bump — for + flowcharts whose aspect ratio is wide/flat enough that the fixed-height + box wastes a lot of it as blank top/bottom bars, but don't need bigger + text. Only apply to diagrams confirmed wide via their rendered viewBox + (mermaid-cli) — a portrait diagram would instead grow uncontrolled + without the fixed-height cap, since width:100% + auto height scales + height up from the full container width. */ +.no-letterbox-mermaid pre.mermaid > svg { + height: auto; +} + +/* furo's generic `.container` class (applied to every MyST {container} block, + including this one) adds fixed margin/padding that doesn't scale with the + diagram — a bigger fraction of a short diagram's height than a tall one, so + it reads as excess whitespace above/below on shorter sequence diagrams. */ +.wide-mermaid { + margin: 0; + padding: 0; +} diff --git a/docs-source/conf.py b/docs-source/conf.py index 40cd321..0e434ce 100644 --- a/docs-source/conf.py +++ b/docs-source/conf.py @@ -72,6 +72,7 @@ "field/network/diagrams/ssl_field_network_fanout_truss.drawio", "field/network/diagrams/ssl_field_network_fanout_direct.drawio", "protocol/diagrams/ssl_field_network_fanout_truss_vision_highlight.drawio", + "protocol/diagrams/ssl_field_network_fanout_truss_gc_highlight.drawio", ] diff --git a/docs-source/protocol/diagrams/multicast-join-sequence.mmd b/docs-source/protocol/diagrams/multicast-join-sequence.mmd index 24be954..205dd47 100644 --- a/docs-source/protocol/diagrams/multicast-join-sequence.mmd +++ b/docs-source/protocol/diagrams/multicast-join-sequence.mmd @@ -1,4 +1,4 @@ -%%{init: {"sequence": {"useMaxWidth": false, "actorMargin": 220, "messageMargin": 80}}}%% +%%{init: {"sequence": {"useMaxWidth": false, "actorMargin": 220, "messageMargin": 40}}}%% sequenceDiagram participant Team as Team Client participant Net as Switch / Router
(multicast group 224.5.23.2:10006) diff --git a/docs-source/protocol/diagrams/rcon-handshake-sequence.mmd b/docs-source/protocol/diagrams/rcon-handshake-sequence.mmd new file mode 100644 index 0000000..69170cb --- /dev/null +++ b/docs-source/protocol/diagrams/rcon-handshake-sequence.mmd @@ -0,0 +1,15 @@ +%%{init: {"sequence": {"useMaxWidth": false, "actorMargin": 220, "messageMargin": 40}}}%% +sequenceDiagram + participant Team as Team Client + participant GC as Game Controller + + Team->>GC: TCP connect :10008 (or :10108 for TLS) + Team->>GC: TeamRegistration (team_name[, signature]) + GC->>Team: ControllerReply (status_code=OK, next_token) + Note over Team,GC: Connection stays open — one registration, then repeated exchanges.
next_token must be echoed back in the next Signature if using secure comms. + + Team->>GC: TeamToController (ping) + GC->>Team: ControllerToTeam (controller_reply) + + Team->>GC: TeamToController (desired_keeper) + GC->>Team: ControllerToTeam (controller_reply) diff --git a/docs-source/protocol/diagrams/referee-broadcast-sequence.mmd b/docs-source/protocol/diagrams/referee-broadcast-sequence.mmd new file mode 100644 index 0000000..58cd2ee --- /dev/null +++ b/docs-source/protocol/diagrams/referee-broadcast-sequence.mmd @@ -0,0 +1,18 @@ +%%{init: {"sequence": {"useMaxWidth": false, "actorMargin": 220, "messageMargin": 40}}}%% +sequenceDiagram + participant Team as Team Client + participant Net as Switch / Router
(multicast group 224.5.23.1:10003) + participant GC as Game Controller + + Team->>Net: IGMP Join 224.5.23.1:10003 + Note over Team,GC: No handshake — Team is now another multicast receiver + + GC->>Net: Referee (stage, command, scores) + Net->>Team: Referee (stage, command, scores) + + GC->>Net: Referee + Net--xTeam: Referee — dropped + Note over Team: No retransmission — team just waits for the next packet + + GC->>Net: Referee + Net->>Team: Referee diff --git a/docs-source/protocol/diagrams/referee-broadcast.mmd b/docs-source/protocol/diagrams/referee-broadcast.mmd new file mode 100644 index 0000000..8d01a00 --- /dev/null +++ b/docs-source/protocol/diagrams/referee-broadcast.mmd @@ -0,0 +1,17 @@ +--- +config: + layout: elk + elk: + nodePlacementStrategy: NETWORK_SIMPLEX +--- +flowchart TD + Team["Team Client
IGMP join only"] -- "IGMP Join
224.5.23.1:10003" --> Net["Switch / Router
duplicates stream"] + GC["Game Controller"] -- "Referee
UDP multicast, continuous, no ack" --> Net + Net -- "Referee" --> Team + + classDef purple fill:#faf5ff,stroke:#9333ea,color:#581c87; + classDef cyan fill:#ecfeff,stroke:#0891b2,color:#164e63; + classDef gray fill:#f1f5f9,stroke:#64748b,color:#1e293b; + class Team purple; + class GC cyan; + class Net gray; diff --git a/docs-source/protocol/diagrams/referee-hierarchy.mmd b/docs-source/protocol/diagrams/referee-hierarchy.mmd new file mode 100644 index 0000000..6f28b1a --- /dev/null +++ b/docs-source/protocol/diagrams/referee-hierarchy.mmd @@ -0,0 +1,23 @@ +--- +config: + layout: elk + elk: + nodePlacementStrategy: NETWORK_SIMPLEX +--- +flowchart TD + Referee["Referee
one packet per broadcast"] + Referee --> Stage["stage, command,
packet_timestamp"] + Referee --> Yellow["yellow
Referee.TeamInfo"] + Referee --> Blue["blue
Referee.TeamInfo"] + Referee --> Pos["designated_position
Referee.Point"] + Referee --> Events["game_events[]
GameEvent"] + Referee --> Proposals["game_event_proposals[]
GameEventProposalGroup"] + + classDef slate fill:#f8fafc,stroke:#475569,color:#0f172a; + classDef cyan fill:#ecfeff,stroke:#0891b2,color:#164e63; + classDef purple fill:#faf5ff,stroke:#9333ea,color:#581c87; + classDef amber fill:#fffbeb,stroke:#d97706,color:#78350f; + class Referee slate; + class Stage cyan; + class Yellow,Blue purple; + class Pos,Events,Proposals amber; diff --git a/docs-source/protocol/diagrams/ssl_field_network_fanout_truss_gc_highlight.drawio b/docs-source/protocol/diagrams/ssl_field_network_fanout_truss_gc_highlight.drawio new file mode 100644 index 0000000..8ada836 --- /dev/null +++ b/docs-source/protocol/diagrams/ssl_field_network_fanout_truss_gc_highlight.drawio @@ -0,0 +1,220 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs-source/protocol/gamecontroller.md b/docs-source/protocol/gamecontroller.md index a0eb2c8..1481215 100644 --- a/docs-source/protocol/gamecontroller.md +++ b/docs-source/protocol/gamecontroller.md @@ -1,10 +1,155 @@ # Game Controller Protocol -The league's game management software, [Game Controller](https://github.com/RoboCup-SSL/ssl-game-controller), manages -game state and publishes it as this protocol. +This page describes the layers that deliver and decode the game state protocol to and from the field. This protocol is +partly bidirectional — teams may optionally talk back to the +[Game Controller](https://github.com/RoboCup-SSL/ssl-game-controller) to request a keeper change, a timeout, or a +substitution. + +## League Software + +There is one piece of league maintained software that manages game state and produces this protocol: + +- [Game Controller](https://github.com/RoboCup-SSL/ssl-game-controller) + +If you're actively trying to connect to a field, you'll need to make sure it's running. Automatic referee +implementations are separate, optional software that talk to the Game Controller over the same protocol described below +— see the [referee protocol](./referee.md) page for how their filtered game state gets used. ## Transport +**A team is never required to reply to the Game Controller.** Teams can and do play full matches only _receiving_ game +state. The Game Controller talks to teams two ways: a one-way broadcast every team should listen to, and an optional +two-way TCP connection only needed if a team wants to actively request something. + +### Referee Broadcast + +The Game Controller publishes overall game state (stage, command, scores, cards) as a one-way UDP multicast stream. The +league defines this multicast group as having an address of `224.5.23.1` at port `10003`. You join a multicast group +just like you connect to any IP address and port combo, though you may need to pass some additional options when binding +the socket. + +```{mermaid} diagrams/referee-broadcast.mmd +``` + +Once you've joined, you should expect to receive a `Referee` packet on a regular interval, independent of any camera +framerate. **Do not assume the game is running the current command until you've received a `Referee` packet saying so.** +Field data delivery is not guaranteed here either — a dropped packet is not re-transmitted, and packet loss should be +_well below_ 1%. + +````{container} wide-mermaid +```{mermaid} diagrams/referee-broadcast-sequence.mmd +``` +```` + +### Remote Control "rcon" Channels (Optional) + +If a team's software/AI wants to request a keeper change, respond to an advantage choice, or request a substitution, it +opens a TCP connection to the Game Controller and registers. Again, many of these functions can be done by the human +robot handler and physical remote control provided by the event organizer/field. There are three separate rcon channels, +each on its own port: one for teams, one for autoRefs, and one for physical remote controls. Registration and signing +are described in +[ssl_gc_rcon.proto](https://github.com/RoboCup-SSL/ssl-protocol-defs/blob/main/proto/gc/ssl_gc_rcon.proto); signatures +are optional and only needed for secure/verified communication. + +Unlike the broadcast channel, opening a TCP connection requires knowing the Game Controller's actual IP. **You don't +need to guess this or hardcode a hostname** — read it off the +[source address](https://en.wikipedia.org/wiki/IPv4#Source_address) field of the IP packets carrying the `Referee` +broadcast you're already receiving. Whatever machine that traffic comes from is the Game Controller. + +````{container} wide-mermaid +```{mermaid} diagrams/rcon-handshake-sequence.mmd +``` +```` + +The connection stays open — registration happens once, then the team sends further requests and receives replies over +the same socket for as long as it stays connected. If a team never connects, it simply never receives a +`ControllerReply` and never gets to make requests; the game continues based on the referee's broadcast state alone. + +### Connections Table + +The following table contains connection information for the broadcast channel and the team/remote-control rcon channels. +AutoRef connections use the same rcon flow but are documented on the [referee protocol](./referee.md#connections-table) +page instead, alongside the rest of the autoRef-specific content. + +| Protocol | Protobuf | Type | Address | Port | +| ------------------------ | --------------------------------------------------------------------------------------------------------------------- | ------------- | ---------- | ----- | +| SSL-Game-Controller (GC) | [Referee](https://github.com/RoboCup-SSL/ssl-protocol-defs/blob/main/proto/gc/ssl_gc_referee_message.proto) | UDP Multicast | 224.5.23.1 | 10003 | +| Team -> GC | [Team](https://github.com/RoboCup-SSL/ssl-protocol-defs/blob/main/proto/gc/ssl_gc_rcon_team.proto) | TCP | GC | 10008 | +| Team -> GC | [Team](https://github.com/RoboCup-SSL/ssl-protocol-defs/blob/main/proto/gc/ssl_gc_rcon_team.proto) | TCP + SSL | GC | 10108 | +| Remote Control -> GC | [Remote Control](https://github.com/RoboCup-SSL/ssl-protocol-defs/blob/main/proto/gc/ssl_gc_rcon_remotecontrol.proto) | TCP | GC | 10011 | +| Remote Control -> GC | [Remote Control](https://github.com/RoboCup-SSL/ssl-protocol-defs/blob/main/proto/gc/ssl_gc_rcon_remotecontrol.proto) | TCP + SSL | GC | 10111 | + ## Message Format -## League Software +The league uses [google protobuf](https://protobuf.dev/) to define the data format for this protocol too. The interface +definitions are in the same [league protobuf repository](https://github.com/RoboCup-SSL/ssl-protocol-defs) used for +vision. The image below shows the hierarchy of the broadcast `Referee` message — the one every team decodes, whether or +not it ever opens an rcon connection. + +````{container} no-letterbox-mermaid +```{mermaid} diagrams/referee-hierarchy.mmd +``` +```` + +`Referee` is the single top level structure sent on the broadcast channel. It carries the coarse game `stage` and +fine-grained `command` teams must obey, a `Referee.TeamInfo` for each of `yellow` and `blue` (score, cards, timeouts, +keeper id), an optional `designated_position` for ball placement, and any `game_events`/`game_event_proposals` the +active autoRef has reported. + +The table below contains links to the specific proto files needed to decode the broadcast message and the three rcon +channels. It's recommended you include the entire +[ssl-protocol-defs repository](https://github.com/RoboCup-SSL/ssl-protocol-defs) as a submodule in your code, rather +than copying the files directly. + +| Message | Proto File | +| ------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- | +| Referee, Referee.TeamInfo, Referee.Point | [ssl_gc_referee_message.proto](https://github.com/RoboCup-SSL/ssl-protocol-defs/blob/main/proto/gc/ssl_gc_referee_message.proto) | +| GameEvent, GameEventProposalGroup | [ssl_gc_game_event.proto](https://github.com/RoboCup-SSL/ssl-protocol-defs/blob/main/proto/gc/ssl_gc_game_event.proto) | +| ControllerReply, Signature | [ssl_gc_rcon.proto](https://github.com/RoboCup-SSL/ssl-protocol-defs/blob/main/proto/gc/ssl_gc_rcon.proto) | +| TeamRegistration, TeamToController, ControllerToTeam | [ssl_gc_rcon_team.proto](https://github.com/RoboCup-SSL/ssl-protocol-defs/blob/main/proto/gc/ssl_gc_rcon_team.proto) | +| AutoRefRegistration, AutoRefToController, ControllerToAutoRef | [ssl_gc_rcon_autoref.proto](https://github.com/RoboCup-SSL/ssl-protocol-defs/blob/main/proto/gc/ssl_gc_rcon_autoref.proto) | +| RemoteControlRegistration, RemoteControlToController, ControllerToRemoteControl, RemoteControlTeamState | [ssl_gc_rcon_remotecontrol.proto](https://github.com/RoboCup-SSL/ssl-protocol-defs/blob/main/proto/gc/ssl_gc_rcon_remotecontrol.proto) | +| Team, RobotId, Division | [ssl_gc_common.proto](https://github.com/RoboCup-SSL/ssl-protocol-defs/blob/main/proto/gc/ssl_gc_common.proto) | + +## Team Sides + +This protocol shares its coordinate frame and units with the vision protocol — see +[vision's coordinate system](./vision.md#coordinate-system) for the full breakdown (mm, radians, CCW from +X). The only +game-controller-specific piece is `blue_team_on_positive_half`, which tells you which side of the field's +X axis the +blue team currently defends. **This flips between halves — do not hardcode a side.** `designated_position` (used for +ball placement) is a `Referee.Point` in the same field frame. + +## Registration Channels + +The Game Controller's rcon interface is split into three independent TCP channels — team, autoRef, and remote control — +each on its own port, each with its own registration message and message types. This means if your software wants to act +as more than one role (for example, a team client that is also a remote control), it needs a separate TCP connection and +registration per role; they are not multiplexed over one socket. + +**The [referee protocol](./referee.md) page documents how teams can use the `game_events` an automatic referee reports +over this protocol.** + +## Network Flow + +The diagram below is the same competition network fan-out shown in the +[field network documentation](../field/network/compnetwork.md), with the machines involved in the game controller +protocol highlighted and the vision-only machines (cameras, Truss NUC, Vision Computer) muted. The Game Controller +publishes the `224.5.23.1:10003` broadcast and serves the three rcon channels; team PCs, remotes, the status board, and +the officiating/broadcast machines that read referee state all consume it. The router and switch are highlighted too — +they maintain the multicast group and carry the rcon TCP connections. + +```{image} diagrams/ssl_field_network_fanout_truss_gc_highlight.light.svg +--- +class: only-light +alt: SSL field network fan-out diagram with game-controller-producing and + game-controller-consuming machines highlighted, vision-only machines muted +--- +``` + +```{image} diagrams/ssl_field_network_fanout_truss_gc_highlight.dark.svg +--- +class: only-dark +alt: SSL field network fan-out diagram with game-controller-producing and + game-controller-consuming machines highlighted, vision-only machines muted +--- +``` diff --git a/docs-source/protocol/referee.md b/docs-source/protocol/referee.md index 0b97c67..5273046 100644 --- a/docs-source/protocol/referee.md +++ b/docs-source/protocol/referee.md @@ -4,6 +4,19 @@ Probably mostly document the vision filter data stream? ## Transport +### Connections Table + +The following table contains connection information for automatic referee (autoRef) implementations talking to the +[Game Controller](https://github.com/RoboCup-SSL/ssl-game-controller). See the +[game controller protocol](./gamecontroller.md#remote-control-rcon-channels-optional) page for the shared rcon +registration/signing flow these channels use, including +[how to find the Game Controller's IP](./gamecontroller.md#remote-control-rcon-channels-optional) without guessing it. + +| Protocol | Protobuf | Type | Address | Port | +| ------------- | -------------------------------------------------------------------------------------------------------- | --------- | ------- | ----- | +| AutoRef -> GC | [AutoRef](https://github.com/RoboCup-SSL/ssl-protocol-defs/blob/main/proto/gc/ssl_gc_rcon_autoref.proto) | TCP | GC | 10007 | +| AutoRef -> GC | [AutoRef](https://github.com/RoboCup-SSL/ssl-protocol-defs/blob/main/proto/gc/ssl_gc_rcon_autoref.proto) | TCP + SSL | GC | 10107 | + ## Message Format ## League Software diff --git a/docs-source/protocol/vision.md b/docs-source/protocol/vision.md index dc4c08c..56ddd84 100644 --- a/docs-source/protocol/vision.md +++ b/docs-source/protocol/vision.md @@ -55,8 +55,10 @@ definitions are available in the [league protobuf repository](https://github.com definition file there are needed to decode the raw data. The image below shows the hierarchy of the data structure send in the data stream. +````{container} no-letterbox-mermaid ```{mermaid} diagrams/message-hierarchy.mmd ``` +```` There are two top level data structures, SSL_DetectionFrame and SSL_GeometryData. These correspond to the packet types seen in the join sequence diagram above. SSL_DetectionFrame is the meat of the data stream, and as seen in the diagram, @@ -126,8 +128,10 @@ as teams may disagree on the approach to handle detections at boundaries. For ex field, all four cameras are likely to see the ball and robots at the center and you will receive 4 detections for each entity. This is visualized in the graphic below. +````{container} no-letterbox-mermaid ```{mermaid} diagrams/multi-camera-merge.mmd ``` +```` There is no single agreed upon way to resolve these overlaps. An additional challenge is filtering position in velocity. Teams will see noise in this derivative without filtering. Again, there are many approaches to solve this. The committee