Homepage GitHub

Sending sequence of frames via CAN

I need to send a radar frame which basically is a pointcloud and the amount of data is greater than 8 or 64 bytes, usually around 500 bytes. So at the moment, I am using SocketAPI from Linux and I am using magic word to obtain the end of the frame (so I keep reading the CAN bus until I get the Magic word of 8 bytes and therefore I know that the sequence has finished).

I was wondering, how can I implement this in UAVCAN? Should I use PyUAVCAN or Libcanard? I am not using embedded system on receiving side, rather a full ARM64 Linux system.

Also is it possible to throw away the magic word implementation and rather dump 500 bytes to UAVCAN and expect the receiver using UAVCAN to figure our where is the end of this 500 bytes?

This is the only way to do it. Please read the Guide.

PyUAVCAN is probably easier to use because it offers a higher-level API.

Ok I am just scared that Python will throw some exceptions but from your own experience, would you say that it will be safe to use PyUAVCAN?

Yes, I am reading the guide and I have a question regarding that. Am I correct saying that if I expect no more than 500 bytes, I just need to create a message with data[<500] and create a subscriber that uses this message?

Python is ill-suited for embedded systems, but you said that the receiving end is not an embedded system, so it might be adequate. If not, use Libcanard.

Not quite. You seem to be reasoning at the level of bytes. You need to discard this mindset and start thinking in terms of data types. What do these bytes represent? What exactly does the radar give you, at the application level?

Yes, the receving machine is Linux. The sending machine is embedded. I assume this is totally okay to use PyUAVCAN on Linux and Libcanard on Embedded?

I am actually happy to disregard the bytes as I am not an embedded engineer. Ok, in that case it really is like ROS and I think I will not have problems with that. Radar is sending a header and a data, that’s it. I guess I can create a message with some header fields like timestamp and just data field which is an array.

Of course; otherwise, what would be the point of everything.

Yes, it is just like ROS. I recommend keeping in mind that what you are dealing with ultimately is not some bytes but quantities modeling the real world. For example, if the radar yields an array of distance measurements in polar coordinates, then model it like float32[100] distance, etc. instead of uint8[500] radar_output.

You have here a nice API for the libcanard:

Can I use it together with DSDL to start my project quicker? As I am on Linux machine, I am not required to manually allocate heap and etc.

Yes, but! The DSDL generator is currently in a sort of half-finished state:

You can either serialize your types manually (it’s very easy to do using the helper functions from canard_dsdl.h), or you can help us finalize that PR to merge it quicker (it’s 99% done).

Ok but first I am just trying to grasp this new way of communication for me :slight_smile:
So I know getting more and more familiar. I have a specific code question if you do not mind.

Suppose I have created my uavcan message called Distance and after serialization I have ID for example 56.
Now, when I want to subscribe to the topic with such message, I have to pass a CanardTransfer when accepting a message from the topic. As far as I understood, when I finally receive a message, this transfer will hold my message and the payload. So in my case, will this be sufficient to first declare a member of struct like this:

const CanardTransfer distance_transfer = {
    .timestamp_usec = transmission_deadline,      // Zero if transmission deadline is not limited.
    .priority       = CanardPriorityNominal,
    .transfer_kind  = CanardTransferKindMessage,
    .port_id        = 1234,                       // This is the subject-ID.
    .remote_node_id = CANARD_NODE_ID_UNSET,       // Messages cannot be unicast, so use UNSET.
    .transfer_id    = my_message_transfer_id,
    .payload_size   = 100, // My maximum number of distance points
    .payload        =  **Not sure what to declare here;**
};

And after receiving a payload I just need to deserialize it.

Sorry, I don’t understand your question.

What does this mean?

No. To subscribe, you call canardRxSubscribe(...). You don’t need a transfer for that.

Yes. The library will create an instance of CanardTransfer for you, whose payload you then deserialize and process. You do not need to instantiate CanardTransfer unless you want to publish a message.

Ah okay now it makes sense! I was just a bit lost with the guide + github + C++ API, sorry.

Regarding the ID, when we create a subscriber, we have to pass const CanardPortID port_id which I believe is Subject ID of the message. So what I mean is that if I want to create a custom message, I need to give my message an ID correct? Like Heartbeat has its own ID 32085. So In order to give an ID, I have to serialize my message first.

Btw, if I want to use this message for example: https://github.com/UAVCAN/public_regulated_data_types/blob/master/uavcan/primitive/array/Integer32.1.0.uavcan
Do I need to deserialize them to obtain the Subject ID?

Serialization and subject-ID assignment are independent processes. You assign your subject identifiers at the system definition time, like you do with topics in ROS. Say, you might proclaim that “let there be radar readings published at subject-ID 56”, and there will be radar readings at subject-ID 56. No messages needed, see? You just decide it and it’s done.

Now, when actually publishing a new radar message, you serialize it, and when invoking canardTxPush(...) you pass your serialized data along with your 56 into it (packed into an instance of CanardTransfer).

When subscribing, you already know that you should be on a lookout for messages published with the subject-ID of 56.

Subject-ID == topic name.

Yes makes sense.

When I want to accept a frame using canardRxAccept function, how can I know which message I received? For example I already have 3 subscriptions for 3 radar devices that share the same message:

(void) canardRxSubscribe(&ins_, CanardTransferKindMessage, 201, 100, CANARD_DEFAULT_TRANSFER_ID_TIMEOUT_USEC, &radar1_subscription_);
(void) canardRxSubscribe(&ins_, CanardTransferKindMessage, 202, 100, CANARD_DEFAULT_TRANSFER_ID_TIMEOUT_USEC, &radar2_subscription_);
(void) canardRxSubscribe(&ins_, CanardTransferKindMessage, 203, 100, CANARD_DEFAULT_TRANSFER_ID_TIMEOUT_USEC, &radar3_subscription_);

But when I try to accept it, can it be any of the 3 messages? As I understood there could be only 1 CanardInstance

I mean I can just take the ID of the transfer (I mean my ID which I am sending from the Radar so I can differentiate) and compare but is there a “correct” way?

You just look at the field port_id of the received CanardTransfer.

Replying because I also want to know if there is a “recommended” way of consuming the canard RX frames other than switch.

Right now, the only information in the Canard transfer frame that identifies its structure is port_id. So if I have a consumer task, say processReceivedTransfer(const CanardTransfer* const rxf), the task would have a dirty switch( rxf->port_id ){} which will call the respective deserializer functions (which I’m also patiently waiting for…).

Which made me think that it must mean that all DSDL datatypes that will be used at an application-level must be labeled with port_ids. Hmm, interesting.

Your preferred methods of data handling are entirely out of the scope of the library (and Specification). If you’re dealing with a large number of ports, declarative approaches based on a handler registry might be preferred, where you define a dictionary mapping port-ID to handler functions, and traverse it whenever you receive a new transfer. Up to you.

No.

A port-ID identifies a specific service within the system, like, say, airspeed measurement, or a radar sensor, or a motor drive. The service requires a data type definition to facilitate data transactions. A data type may be used with different service instances (e.g., multiple cameras) or even completely different services (e.g., the same type can be used to report an estimation and to command a setpoint). Excepting few edge cases, most of which are already addressed by the standard data type set (uavcan.*), data types defined by vendors/developers will not need a fixed port-ID at all. Instead, the mapping between subjects/services and their identifiers will be established according to the design of the system at a later stage. As I wrote earlier, it helps to reason about subject-IDs as topics, they are basically identical in their purpose.

There is a short explanation in Choosing Message and Service IDs. If you are building a UAVCAN-powered system, you really need to read the Guide, where this question is explored in-depth.

1 Like

Got it. Is my understanding correct that the handling of Canard frames after it was released from the RX buffer (given that memory is freed afterwards) is up to the user of the library?

Thank you for the explanation. I was actually on the brink of making the mistake of tying the semantics to the data-types.

The allocation of port IDs and deciding on which datatypes to use confused me because I was acting both as a vendor and the integrator; basically I’m designing the whole system with no external components.

I think you mean not frames but transfers, CanardTransfer (a frame is a transport-layer entity, a transfer is something you deal with at the presentation layer). If so, yes, your understanding is correct.

Yes, I mean CanardTransfer, not transport frames. Thanks for clarifying!