A simple ros2 driver to get human-readable data from a J1939 sensor
Reading data from a J1939 sensor is kind of annoying.
You can collect logs and use some third party software to decode after-the-fact, or hardcode some parser for real-time translation for a specific sensor. But we thought it'd be nice to load a dbc and start reading data through ROS2. So we whipped up a simple driver for ROS2 that integrated with ros2_socketcan to decode incoming CAN frames live.
All you need is a device and a DBC!
The whole J1939 standard dbc will work - this takes care of using the correct messages for your device
-
Get a J1939 device (like some sensor)
-
Physically connect and power the sensor
-
Set up a CAN port on the host machine
-
For a PEAK CAN device:
sudo ip link set can0 up type can bitrate 250000 dbitrate 2000000 fd on fd-non-iso on
-
For a Lawicel CANusb:
sudo slcand -o -c -f -s5 /dev/ttyUSB0 can0
sudo ifconfig can0 up
-
For a Virtual CAN network:
ip link add dev vcan0 type vcan
ip link set up vcan0
-
-
Modify
generic_can_driver/config/socketcan_params.yaml
for your interface (e.g.can0
) -
Modify
generic_can_driver/config/generic_can_params.yaml
for your device's id- This is the last two digits in the identifier of the sensor's CAN frame (e.g. E5) converted to decimal
-
Launch
ros2 launch generic_can_driver generic_can.launch.py
- on_configure()
- setupDatabase()
- dbc_pgns_
- configurePublishers()
- setupDatabase()
- on_activate()
- activatePublishers()
- subscribe to can bus
- bind rxFrame()
- rxFrame() loop
-
Can you launch this node multiple times?
Yes. Make sure to edit the deivce name, frame, and ID in each node's config so that it parses the correct device
-
What if I have multiple CAN lines?
You can launch multiple instances of ros2_socketcan. Just make sure that you give each node the correct name for its CAN interface (e.g. can0, can1, can2, ...)
-
What if I have multiple devices on one CAN line?
You can launch multiple instances of the generic_can_driver, and leave the interface in the node config (e.g can0) the same. However, make sure you update the device names, frames, and IDs in each node's config.
-
What message type does this publish out?
This publishes a
j1939_interfaces/msg/can_data.hpp
message. The message definition can be found underros2_j1939/j1939_interfaces
. This is a key-value message type, kinda likediagnostic_msgs/msg/DiagnosticArray.msg
but instead of being astring-string
key-value pair, this is astring-float64
key-value pair. -
Why does it use this custom message?
Again, the motivation for this simple driver was to create a quick way to bring in human- readable data from a J1939 sensor into ROS2. For a specific implementation, which outputs a "correct" message type (like an IMU sensor message), have a look at device-specific driver implementations.
-
How are the publishers set up?
On configure, the driver parses the (user-provided) dbc file and spins up one publisher per dbc message defined within that file. So if you have three dbc messages, this creates three publishers, with the topics automatically named according to
/device_name/message_name
-
The values of some of my data are constant and REALLY large, why?
Sometimes if a field is empty or erroring, that field ("signal") of the message will simply hold the max possible value. So if your temperature is in a range of
[0,202]
, an error (such as no sensor, sensor malfunction, etc.) will populate that temperature field with202
.
-
Socket Can refuses to change lifecycle state
Make sure you have a can bus enabled and that its name is correct in the launch file/params
-
Error receiving CAN message: can0 - CAN Receive Timeout
ros2_socketcan expects a certain frequency of incoming CAN frames. If this frequency is missed, after a certain amount of time it alerts the user. This timeout length is configurable, however if your devices are active (just publishing at a slow frequency) this can also just be ignored.