Skip to content

Latest commit

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

VirtualCAN-ROS2-Interface

  • ROS2 interface for virtual CAN (vcan0), for developing and testing CAN logic without vehicle hardware
  • Type a signal name and a value in the terminal, and it is encoded into a CAN frame from the .dbc definition
  • The received frame is decoded back into physical values and published as one topic per signal, so the round trip is visible end to end
terminal input vcan0 ROS2 topic
Vehicle_speed 80 ──► 711 [8] 00 50 00 ... ──► /can/Vehicle_speed { value: 80.0, unit: kph }

Environment

  • Ubuntu 22.04
  • ROS2 Humble
  • CAN : SocketCAN virtual interface (vcan0)

Package

PackageDescription
can_interfaceBus (raw SocketCAN socket), DbcLoader (.dbc parser), DBC (signal encode/decode)
can_msgsFrame.msg (raw frame), Signal.msg (one decoded signal), ParsedMessage.msg (one message)
socketcan_interfaceReceives frames from vcan0, publishes raw frames and decoded signals
virtual_canEncodes values typed in the terminal and writes them to vcan0

Virtual CAN Setup

sudo modprobe vcan # Load the vcan kernel module
sudo ip link add dev vcan0 type vcan # Create the interface
sudo ip link set up vcan0 # Bring it up
  • The setting is lost on reboot, so it must be re-applied
ip link show vcan0 # Check the interface
candump vcan0 # Check reception

DBC Pull

DBC files live in a separate private repository (IVSP-DBC), registered as a submodule at dbc/.

git clone --recursive git@github.com:dev-muuu/VirtualCAN-ROS2-Interface.git
git submodule update --init --recursive # If dbc/ is empty
git submodule update --remote dbc # Pull the latest DBC
  • Access to the private repository is required — without it the clone succeeds but dbc/ stays empty

Signal Database

DbcLoader parses BO_ (message) and SG_ (signal) lines of a .dbc file directly. Bit layout follows the DBC convention.

Byte orderstart_bitExtraction
Little endian (@1)LSB positionShift the 8-byte little-endian value
Big endian (@0)MSB positionCollect bits in Motorola order

Resolution order for the DBC path:

PrioritySourceExample
1Command line argumentros2 run virtual_can virtual_can_sender dbc/Sportage_CAN_ver01.dbc
2CAN_DBC_PATH environment variableexport CAN_DBC_PATH=$PWD/dbc/Sportage_CAN_ver01.dbc
3Defaultdbc/Sportage_CAN_ver01.dbc (relative to the working directory)

Frame Generation

Inject arbitrary values into the bus without vehicle hardware, to verify the receive → parse → log path end to end.

  • Values are given in physical units (km/h, deg) — bit packing (start bit, length, byte order, sign, factor/offset) is resolved from the signal database
  • Symmetric with extract_signal, so encode → send → receive → decode can be verified in a single loop
  • Reproduces specific conditions (e.g. speed 80 km/h) on demand, which is hard to trigger in an actual vehicle
MethodInputOutput
create_frame_by_signal_nameOne signal name and valueOne frame, message ID resolved automatically
create_frameMessage ID and multiple signal valuesOne frame
create_frames_by_signal_namesMultiple signal values across messagesMultiple frames, grouped by message ID
#include"can_interface/dbc.h"
can_interface::DBCdbc("dbc/Sportage_CAN_ver01.dbc");
// 1. One signal — message ID is resolved from the signal nameauto frame = dbc.create_frame_by_signal_name("EPS_command", 90.0);
if (frame) bus.send(*frame);
// 2. Multiple signals within one messageauto frame = dbc.create_frame(343, {
{"EPS_command", 90.0},
{"SCC_command", 1.5},
});
// 3. Multiple signals across messages — grouped by message ID automaticallyauto frames = dbc.create_frames_by_signal_names({
{"EPS_command", 90.0},
{"Alive_count", 7.0},
});
for (constauto& f : frames) bus.send(f);
  • Signal names must match the SG_ names in the DBC exactly — check with print_signal_map()
  • create_frame returns std::nullopt if the message ID is unknown, or if a signal does not belong to that message
  • create_frames_by_signal_names silently skips unknown signal names, so verify the returned count

For values entered at runtime, use the sender directly — see Interactive Command. Define a generator in can_frame_generator.h only for fixed patterns that are repeated often.

namespacecan_frame_generator
{
can_frame generate_velocity()
{
can_interface::DBC dbc;
auto frame_opt = dbc.create_frame_by_signal_name("EPS_command", 90.0);
return frame_opt ? *frame_opt : can_frame{};
}
}

Build

colcon build --symlink-install
source install/setup.bash

Execute Command

Run from the workspace root — both executables resolve the DBC through a relative path.

  1. ROS2 Interface — reads vcan0, publishes raw frames and decoded signals
source install/setup.bash
ros2 run socketcan_interface socketcan_interface
  1. Frame Sender — encodes the values typed in the terminal
source install/setup.bash
ros2 run virtual_can virtual_can_sender
  1. Monitor — see Monitoring
ros2 topic echo /can/Vehicle_speed

Parameters of the ROS2 interface:

ParameterDefaultDescription
interfacevcan0SocketCAN interface name — set to can0 for a real bus
dbc_path(empty)Falls back to CAN_DBC_PATH, then dbc/Sportage_CAN_ver01.dbc
parsed_prefix/canNamespace of the decoded topics
publish_groupedfalseAlso publish one topic per message, carrying all of its signals
ros2 run socketcan_interface socketcan_interface --ros-args \
-p interface:=vcan0 \
-p dbc_path:=$PWD/dbc/Sportage_CAN_ver01.dbc \
-p publish_grouped:=true

Interactive Command

Type a signal name and a value in the sender terminal, and the frame is encoded and sent at that moment. No rebuild is needed to change a value.

CommandDescription
<signal> <value> [...]Send once — the default
hold <signal> <value> [...]Repeat at 10 Hz until changed, like a real bus
showPrint the values currently being repeated
clearStop repeating
listPrint every message and signal defined in the DBC
testSend the fixed test frame (0x123) once
quitExit
EPS_command 90 # One frame, right now
EPS_command 90 SCC_command 1.5 # Two signals of one message -> one frame
Alive_count 7 # Different message -> separate frame
hold EPS_command 90 # Start repeating at 10 Hz
hold EPS_command 120 # Change the value while repeating
clear # Stop repeating
  • Signals are grouped by message ID automatically, so signals from different messages can be given on one line
  • A signal name that is not in the DBC is rejected before sending, with the name printed
  • Nothing is sent until an input arrives — hold is the only command that keeps sending

Topic

TopicTypeDescription
/frame_can0can_msgs/FrameRaw CAN frame, every ID including those absent from the DBC
/can/<signal>can_msgs/SignalOne topic per signal — header, name, value, unit
/can/<message>/<signal>can_msgs/SignalUsed only when the same signal name appears in more than one message
/can/<message>can_msgs/ParsedMessageAll signals of one message at once — only with publish_grouped:=true
  • Every topic in the DBC is created at startup, so ros2 topic echo resolves the type before the first frame arrives
  • value is a single scalar, so it can be plotted directly in rqt_plot or Foxglove

Monitoring

ros2 topic list | grep ^/can # Every decoded signal
ros2 topic echo /can/Vehicle_speed # One signal
ros2 topic echo /frame_can0 # Raw frame
candump vcan0 # Check at the SocketCAN layer
  • Start echobefore typing into the sender — a single-shot send is gone by the time it starts
  • Use hold to keep a value on the bus while inspecting it

About

ROS2-CAN interface using Virtual CAN

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages