Using UAVCAN as a general purpose CAN(FD) Embedded DCPS Middleware?

Hi!

I would like to use UAVCAN on a project that is nothing to do with drones or avionics, but does involve a lot of nodes on a CAN(FD) bus which need to be able to talk to each other in a flexible and reliable way.

In “big” control systems, I am used to the concept of Data-Centric Publish-Subscribe (DCPS), where nodes can publish arbitrary ‘topics’ (data structures) on the network, to which they can write ‘samples’ i.e. serialized data packets, which are replicated across the network. This is used by ROS 2.0 for example and there are several implementations e.g. OpenSplice, RTI OpenDDS etc which work over a UDP network.

However, I need something like this but suitable for an embedded (STM32) platform with limited RAM and Flash - I am running Zephyr RTOS. I have already got it to build libcanard and send a few test messages.

My requirements are:

  • I want a hierarchical data structure of key/value pairs. This must be iterable and introspectable in the code.
    • For example, if node0 has some data describing a temperature sensor, I would like that to be visible as node0.sensors.temperature
  • I want a mixture of publish/subscribe and request/response. For example, names of objects, configuration objects etc would only be updated on request, while others would be periodically transmitted. If possible, it would be nice if any object could be requested to be periodically transmitted, or transmitted on update.
  • Keys should be stored in flash where possible, e.g. local data structures in flash, subscriptions to remote topics in RAM.
  • Internally to the firmware, I want to be able to use this data structure to pass data around between threads, like a shared memory. So it needs to be thread-safe.
  • I don’t want to have to maintain incompatible versions of the protocol. If I add some data to a topic or make a new topic, i should still be able to see the values on nodes running old versions of the protocol. I understand this may not be possible with ROM-based data structures - so as a minimum I’d want to have a warning/error if a particular topic is incompatible.
    • Nodes with more memory (e.g. running python) should be able to decode the protocol without any prior knowledge of the data structures.

Is UAVCAN a good fit for this? Am I asking too much?
From the specification, it seems as if UAVCAN can do most of this.
The part I am having trouble with is making a flexible tree of DSDL. Is it possible for DSDL structures to inherit from eachother?

1 Like

Hi Tom,

In general, it seems like UAVCAN might work for you, but it may not be able to satisfy all of your requirements. I presume you have read the Guide and studied the demos in C and Python already; if not, consider doing so as it should add clarity.

UAVCAN is, essentially, three things: 1. several transport layers (UAVCAN/CAN, UAVCAN/serial, etc.); 2. data serialization format with a custom interface definition language (DSDL); 3. a simple standard application layer to suit most applications. Practically this translates to shipping serialized data structures around the network in a very low-level manner compared to more sophisticated frameworks like DDS you referenced. UAVCAN consciously trades off features for simplicity on the assumption that proper design provided, one can attain the same high-level architectural goals with UAVCAN by bending the design of the resulting decentralized data distribution system slightly.

One issue I see in your list of desired features is that you seem to be assuming that UAVCAN provides a shared object abstraction similar to what DDS does, but this is not so. In UAVCAN, your application is responsible for commencing data transactions through the network, and as such, there is no abstraction like “object” or “data ownership”. There are only topics (we call them “subjects” usually) that you can publish to (manually) and subscribe to. Topics are segregated using arbitrary numerical identifiers (subject-IDs) instead of names that you might be used to. On each node that may publish/subscribe to a topic there is a name associated with that topic; say, sensors.temperature, sensors.pressure, etc, but different nodes may name the same topic differently (e.g., two nodes may refer to the same temperature as sensor.temperature and sensor_readout; this is not a problem as long as they agree on the numerical subject-ID).

DSDL is also a rather pedestrian format designed for simplicity and time-predictable handling in high-integrity systems. It supports extensibility and structural subtyping but does so in a rather minimalist way – the Guide provides some examples. You can add/remove fields without breaking wire compatibility as long as you follow a set of simple rules, but there is at the moment no finished solution for runtime type compatibility checking. There was a proposal from @VadimZ that was kind of abandoned but I would very much welcome any effort towards its resurrection:

https://forum.opencyphal.org/t/type-safety-enhancement-proposal/1416

The proposal relies on querying each node’s data type information at runtime in order to determine if it is compatible with the topics it is interacting with. There is a related issue intended to bring the type information to the transport layer in order to make the protocol fully resilient to type mismatch errors:

Decoding data exchanges without any prior knowledge as you requested is possible but not out-of-the-box. Nodes may publish the optional type introspection message called uavcan.node.port.List that contains information on the topics and services the node is configured to interact with at the moment. It allows the Yakut CLI tool to render the connectivity matrix like this (at the bottom):

Having the list of topics and services, one can read the type information for each topic/service from the standard port type registers like uavcan.pub.sensor.temperature.type:

Having the type information, your Python node can automatically deserialize the data. This can be easily implemented in a few dozen lines of Python code with PyUAVCAN.


In general, if you compare it against DDS, UAVCAN comes without many batteries included, but in the kinds of systems it is designed for this is often a very desirable property. You can implement higher-level abstractions by layering additional logic atop the bare bones offered by UAVCAN.

2 Likes