mirror of
https://github.com/PhotonVision/photonvision
synced 2026-06-21 01:01:41 +00:00
83 lines
5.5 KiB
Markdown
83 lines
5.5 KiB
Markdown
# Photon Serde Autocode
|
|
|
|
Like Rosmsg. But worse.
|
|
|
|

|
|
|
|
## Goals
|
|
|
|
- As fast as possible (only slightly slower than packed structs, ideally)
|
|
- Support for variable length arrays and optional types
|
|
- Allow deserialization into user-defined, possibly nested, types. See [ResultList](src/targeting/resultlist.h) for an example of this.
|
|
|
|
## Design
|
|
|
|
The code for a single type is split across 3 files. Let's look at PnpResult:
|
|
- [The struct definition](src/struct/pnpresult_struct.h): This is the data the object holds. Auto-generated. The data this object holds can be primitives or other, fully-deserialized types (like Vec2)
|
|
- [The user class](src/targeting/pnpresult_struct.h): This is the fully-deserialized PnpResult type. This contains extra functions users might need to expose like `Amgiguity`, or other computed helper things.
|
|
- [The serde interface](src/serde/pnpresult_struct.h): This is a template specialization for converting the user class to/from bytes
|
|
|
|
## Prior art
|
|
|
|
- Protobuf: slow on embedded platforms (at least quickbuf is)
|
|
- Wpi's struct: no VLAs/optionals
|
|
- Rosmsg: I'm not using ros, but I'm stealing their message hash idea
|
|
|
|
## Deviations from WPI's Struct Schema Typestrings
|
|
|
|
- Enum types are disallowed
|
|
- Bitfields and bit packing are disallowed
|
|
- Only variable length arrays are supported (no fixed-length arrays)
|
|
- Arrays must be no more than 127 elements long
|
|
- Members can be either VLAs or optional, but not both
|
|
- A top-level NT topic type shall be a single type (eg TargetCorner), and cannot an array of types (eg TargetCorner[] or TargetCorner[?])
|
|
- `float` and `double` types will be replaced with float32/float64 when generating message schema strings. This means that `float32 x;` and `float x;` will result in the same message hash.
|
|
|
|
For example, this is a valid PhotonStruct schema. Note the WPILib `Transform3d`, the Photon-defined `TargetCorner`, optional prefix, and VLA suffix.
|
|
|
|
```
|
|
float64 poseAmbiguity;
|
|
optional Transform3d altCameraToTarget;
|
|
TargetCorner:16f6ac0dedc8eaccb951f4895d9e18b6 minAreaRectCorners[?];
|
|
```
|
|
|
|
## Dynamic Decoding
|
|
|
|
Dynamic decoding is facilitated by publishing schemas to the `.schema` table in NT, and by encoding the `message_uuid` as a property on a `photonstruct` publisher. Schema names in the .schema table shall be formatted as `photonstruct:{Type Name}:{Message UUID}`. For example, here I've published Photon results to `/photonvision/WPI2024/rawBytes`. This topic has the typestring `photonstruct:PhotonPipelineResult:ed36092eb95e9fc254ebac897e2a74df`, with properties `{message_uuid': 'ed36092eb95e9fc254ebac897e2a74df'}`. It shall be legal to have published multiple versions of the same message, as long as their UUIDs are unique (which they'd better be).
|
|
|
|
| Topic Name | Type | Type String |
|
|
|------|------|-------|
|
|
| /.schema/photonstruct:PhotonPipelineResult:ed36092eb95e9fc254ebac897e2a74df | kRaw | photonstructschema |
|
|
| /.schema/photonstruct:PhotonTrackedTarget:4387ab389a8a78b7beb4492f145831b4 | kRaw | photonstructschema |
|
|
| /.schema/photonstruct:TargetCorner:16f6ac0dedc8eaccb951f4895d9e18b6 | kRaw | photonstructschema |
|
|
| /.schema/photonstruct:MultiTargetPNPResult:af2056aaab740eeb889a926071cae6ee | kRaw | photonstructschema |
|
|
| /.schema/photonstruct:PnpResult:ae4d655c0a3104d88df4f5db144c1e86 | kRaw | photonstructschema |
|
|
| /.schema/photonstruct:PhotonPipelineMetadata:626e70461cbdb274fb43ead09c255f4e | kRaw | photonstructschema |
|
|
| /.schema/proto:geometry3d.proto | kRaw | proto:FileDescriptorProto |
|
|
| /.schema/proto:photon.proto | kRaw | proto:FileDescriptorProto |
|
|
|
|
The struct definition for PhotonPipelineResult we retrieved from the struct schema database shown above (via the command `python.exe scripts/catnt.py --echo /.schema/photonstruct:PhotonPipelineResult:ed36092eb95e9fc254ebac897e2a74df`) is:
|
|
|
|
```
|
|
PhotonPipelineMetadata:626e70461cbdb274fb43ead09c255f4e metadata;
|
|
PhotonTrackedTarget:4387ab389a8a78b7beb4492f145831b4[?] targets;
|
|
MultiTargetPNPResult:af2056aaab740eeb889a926071cae6ee? multitagResult;
|
|
```
|
|
|
|
If we were decoding this, we'd go retrieve the struct definitions for all our nested types. For example, `PhotonTrackedTarget:4387ab389a8a78b7beb4492f145831b4` is defined by it's .schema table entry be the following. This type also demonstrates a mix of WPILib struct types (such as Transform3d), intrinsic types (such as float64), and Photon struct types (such as TargetCorner).
|
|
|
|
```
|
|
float64 yaw;
|
|
float64 pitch;
|
|
float64 area;
|
|
float64 skew;
|
|
int32 fiducialId;
|
|
int32 objDetectId;
|
|
float32 objDetectConf;
|
|
Transform3d bestCameraToTarget;
|
|
Transform3d altCameraToTarget;
|
|
float64 poseAmbiguity;
|
|
TargetCorner:16f6ac0dedc8eaccb951f4895d9e18b6[?] minAreaRectCorners;
|
|
TargetCorner:16f6ac0dedc8eaccb951f4895d9e18b6[?] detectedCorners;
|
|
```
|