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) │
└─────────────────────────────────────────────────────────────┘
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 |
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
- Direct CAN interface (e.g.,
-
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)
- CAN-over-USB adapter (e.g., CANable via
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 controllers - Message publishing:
MotorFeedbackfor motor state - DBC integration: Signal encoding/decoding using
dbcppplibrary - Periodic 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
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
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
- Electrical wiring diagram
- Motor selection
- SocketCAN documentation: https://www.kernel.org/doc/html/latest/networking/can.html
- DBC format: https://github.com/stefanhoelzl/CANpy