Interfacing
This page documents the communication interface between the high-level control software and the motor controllers. The system uses CAN bus as the primary communication protocol, bridging ROS2 nodes running on a laptop with embedded motor controllers (STM32-based FOC drivers and commercial actuators).
Architecture Overview
The interfacing layer consists of three main components:
┌─────────────────────────────────────────────────────────────┐
│ ROS2 Control Layer │
│ (joint_command, trajectory planning, IK/FK) │
└─────────────────────┬───────────────────────────────────────┘
│ ROS2 topics (MotorCmd, MotorFeedback)
┌─────────────────────▼───────────────────────────────────────┐
│ CAN Node (ROS2) │
│ • Message encoding/decoding (DBC) │
│ • SocketCAN / SLCAN interface │
│ • Topic-to-CAN mapping │
└─────────────────────┬───────────────────────────────────────┘
│ CAN bus (physical layer)
┌─────────────────────▼───────────────────────────────────────┐
│ Motor Controllers │
│ • STM32 FDCAN (custom FOC controllers) │
│ • Commercial motors (AK80-9, AK10-9, GL40 II) │
└─────────────────────────────────────────────────────────────┘
Note
This interface layer bridges the high-level motion planning (ROS2) with low-level motor control, providing real-time command and feedback over CAN bus.
CAN Bus Protocol
Bus Configuration
Parameter |
Value |
|---|---|
Bus type |
CAN 2.0 (Classic CAN) |
Bitrate |
500 kbps (configurable: 10k - 1M) |
Frame format |
Standard (11-bit ID) or Extended (29-bit ID) |
Max payload |
8 bytes per frame |
Termination |
120 Ω resistors at both ends |
Warning
Always ensure proper CAN bus termination with 120 Ω resistors at both ends to prevent signal reflections and communication errors.
Hardware Interface Options
The system supports two types of CAN interfaces:
SocketCAN (native Linux CAN)
Direct CAN interface (e.g.,
can0,can1)Lowest latency
Requires hardware CAN adapter
SLCAN (Serial Line CAN)
CAN-over-USB adapter (e.g., CANable via
/dev/ttyACM0)Automatically configured via setup script
Bitrate mapping to SLCAN codes (see
can_core.cpp:262-286)
Message Structure
CAN messages follow the standard CAN 2.0 frame format:
struct CanMessage {
uint32_t id; // CAN message ID (11 or 29 bits)
std::vector<uint8_t> data; // Payload (max 8 bytes)
uint8_t dlc; // Data Length Code
bool is_extended_id; // Extended frame format flag
bool is_remote_frame; // Remote transmission request
uint64_t timestamp_us; // Timestamp in microseconds
}
Reference: humanoid/autonomy/interfacing/can/include/can_core.hpp:8-19
DBC-Based Message Encoding
The CAN node uses DBC (Database CAN) files to define message formats and signal mappings. This allows for:
Structured message definition
Automatic encoding/decoding of physical values
Signal scaling and offset handling
Multi-byte signal packing
Reference: humanoid/autonomy/interfacing/can/include/can_node.hpp:32-36
ROS2 CAN Interface
CAN Core (CanCore class)
Low-level CAN interface abstraction that handles:
Initialization (can_core.cpp:21-34)
Interface configuration (SocketCAN vs SLCAN)
Socket creation and binding
Non-blocking I/O setup
Transmission (can_core.cpp:54-117)
CAN frame construction
Extended ID and RTR flag handling
Error checking and logging
Reception (can_core.cpp:119-177)
Non-blocking frame reception
ID mask extraction (standard vs extended)
Timeout handling
Setup Methods
setupSocketCan()- Native Linux CAN interface (can_core.cpp:179-236)setupSlcan()- USB CAN adapter via external script (can_core.cpp:238-308)
CAN Node (CanNode class)
ROS2 node that bridges ROS topics and CAN messages:
Key Features:
Topic subscription:
MotorCmdmessages from joint controllersMessage publishing:
MotorFeedbackfor motor stateDBC integration: Signal encoding/decoding using
dbcppplibraryPeriodic reception: Timer-based polling for incoming CAN messages
Message Flow:
Command Path (ROS → CAN):
MotorCmd topic → motorCMDCallback() → encodeSignal() → sendMessage() → CAN bus
Feedback Path (CAN → ROS):
CAN bus → receiveMessage() → DBC decode → MotorFeedback topic
Tip
Use ros2 topic echo /motor_feedback to monitor real-time motor telemetry during development.
Reference: humanoid/autonomy/interfacing/can/include/can_node.hpp:26-68
ROS2 Message Types
Motor Command (MotorCmd)
Position, velocity, torque setpoints
Control mode selection
Device ID targeting
Motor Feedback (MotorFeedback)
Current position, velocity
Motor current, temperature
Error flags
Reference: humanoid/autonomy/interfacing/can/include/can_node.hpp:18-24
Embedded (STM32) FDCAN Driver
STM32G4 FDCAN Peripheral
The custom FOC motor controllers use the STM32G4’s FDCAN peripheral for CAN communication.
Hardware Configuration (FDCAN_STM32.cpp:25-58)
Parameter |
Value |
|---|---|
Instance |
FDCAN2 |
Frame format |
Classic CAN |
Mode |
Normal |
Nominal bitrate |
~1 Mbps (calculated from prescaler/segments) |
GPIO pins |
PB12 (RX), PB13 (TX) |
Alternate function |
AF9 (FDCAN2) |
Interrupts |
FDCAN2_IT0, FDCAN2_IT1 |
Bus-Off Recovery (FDCAN_STM32.cpp:7-23)
Automatic detection of bus-off state
Recovery by clearing INIT bit in CCCR register
Error status callback handling
Motor Control Loop Integration
The FDCAN driver is integrated into the FOC control loop:
Setup Phase (foc.cpp:11-16)
Initialize PWM for motor phases
Configure angle encoder (MT6835)
Initialize current sensing
Configure motor driver
Set PID parameters and call
motor.initFOC()Initialize FDCAN and enable RX interrupts
Loop Phase (foc.cpp:25-29)
Call
motor.loopFOC()andmotor.move()Check ring buffer for CAN messages
Process motor commands
Send telemetry data back over CAN
Reference: humanoid/embedded/STM32/app/src/foc.cpp
Hardware Setup
Physical Connections
All motors connect to a shared CAN bus with the following topology:
[Laptop] --USB-- [CANable] --CAN_H/CAN_L-- [Motor 1] -- [Motor 2] -- ... -- [Motor 7] --[120Ω]
| | | |
[120Ω termination] XT30 XT30 XT30
Note
Each motor has an XT30 connector for CAN data. Power is supplied separately via XT60 (high-voltage motors) or XT30 (low-voltage motors). See Electrical Documentation for details.
See Electrical Documentation for detailed wiring diagrams.
Interface Configuration
Example: SocketCAN setup
CanConfig config;
config.interface_name = "can0";
config.bustype = "socketcan";
config.bitrate = 500000;
config.receive_timeout_ms = 100;
Example: SLCAN setup (CANable)
CanConfig config;
config.interface_name = "can0";
config.device_path = "/dev/ttyACM0";
config.bustype = "slcan";
config.bitrate = 500000; // Maps to "-s6" for slcand
Reference: humanoid/autonomy/interfacing/can/include/can_core.hpp:21-28
Development & Testing
Launch Files
Start the CAN interface node:
ros2 launch can can.launch.py
Reference: humanoid/autonomy/interfacing/can/launch/can.launch.py
Testing Tools
Controller test (test_controller.cpp)
Automated testing of CAN message encoding/decoding
Validation of DBC signal mapping
Performance benchmarking
Reference: humanoid/autonomy/interfacing/can/test/test_controller.cpp
Debugging
Enable verbose logging in can_core.cpp:
Uncomment lines 62-75 for transmitted message logging
Use
RCLCPP_DEBUGlevel for received messages
Monitor CAN traffic (Linux):
# View raw CAN messages
candump can0
# Send test message
cansend can0 123#DEADBEEF
Future Enhancements
CAN-FD support: Increase bandwidth for higher-rate telemetry
Multi-bus architecture: Separate buses for different limbs
Hardware filtering: Reduce CPU load by filtering messages at hardware level
Time-stamping: Precise synchronization with external sensors
References
SocketCAN documentation: https://www.kernel.org/doc/html/latest/networking/can.html
DBC format: https://github.com/stefanhoelzl/CANpy