Skip to content

Repository files navigation

CANopenLinux CiA 402 主从 Demo — 环境搭建与使用文档

本文档说明如何在 Linux 上用 虚拟 CAN 总线(vcan) 搭建、编译并运行两个 CANopen 电机 demo:

  • motor402_slave — CiA 402 模拟伺服驱动器(从站,实现 402 状态机 + 位置/速度模式)
  • motor402_master — CANopen 主站(NMT 主站 + SDO 客户端 + 心跳监控),自动控制从站往复运动

不需要任何 CAN 硬件,全部在虚拟总线上完成收发调试。

note:files at demos/


1. 环境要求

项目 要求
操作系统 Linux(Debian/Ubuntu 系最方便,本文以 Ubuntu 为例)
内核 支持 SocketCAN / CAN 子系统(主流发行版默认支持)
工具链 gccmakecmake(≥3.10)
权限 root(仅创建 vcan 接口时需要)
可选 can-utilscandump/cansend 等,强烈建议安装,方便调试)

检查内核 CAN 支持:

# 应能正常加载(或提示已加载)
sudo modprobe can
sudo modprobe vcan

如果 modprobe vcan 报错,说明内核未编译 vcan 模块(罕见,可 sudo apt install linux-modules-extra-$(uname -r) 或换内核)。

2. 环境搭建

2.1 安装依赖

sudo apt update
sudo apt install build-essential cmake can-utils

2.2 创建虚拟 CAN 总线 vcan0

sudo modprobe vcan
sudo ip link add dev vcan0 type vcan
sudo ip link set up vcan0

# 验证
ip link show vcan0        # 应看到 vcan0: <NOARP,UP,LOWER_UP> ... link/can

注意:vcan0 重启后消失,每次开机需重新执行(可选:见 2.3 开机自动创建)。

2.3 (可选)开机自动创建 vcan0

创建 systemd 服务 /etc/systemd/system/vcan0.service

[Unit]
Description=Virtual CAN interface vcan0
After=network.target

[Service]
Type=oneshot
ExecStart=/sbin/ip link add dev vcan0 type vcan
ExecStart=/sbin/ip link set up vcan0
RemainAfterExit=yes

[Install]
WantedBy=multi-user.target
sudo systemctl enable --now vcan0

3. 编译(CMake + Unix Makefiles)

在项目根目录:

cd CANopenLinux
cmake -S . -B build -G "Unix Makefiles"
cmake --build build -j

生成三个可执行文件(build/ 目录下):

目标 说明
build/canopend 标准 CANopen 设备 + commander(与 make 一致,OD 为示例 DS301)
build/motor402_slave CiA 402 模拟电机从站(自定义 402 对象字典 + 电机仿真)
build/motor402_master CANopen 主站,自动控制 402 从站往复运动

语言标准:C11(-std=gnu11)、C++14(-std=gnu++14,工程已启用 C/C++ 双语言, 可自行添加 .cpp 源文件复用 libcanopenlinux_core.a 栈库)。

常用构建选项(配置时传入):

cmake -S . -B build -DCO_SINGLE_THREAD=ON   # 单线程模式(默认开,匹配 Makefile)
cmake -S . -B build -DCO_CONFIG_DEBUG=ON    # 开启栈调试日志

make 原生命令也可用(构建 canopend),但两个 402 demo 只能通过 CMake 构建。

4. 运行

4.1 启动从站(终端 1)

./build/motor402_slave vcan0 -i 1

参数(继承自 canopend):

参数 说明
<can device> CAN 接口名,如 vcan0
-i <NodeId> CANopen 节点号 1..127(默认 LSS 未配置 0xFF,会等 LSS 分配)
-c <接口> CiA 309 ASCII 网关:stdio / local-<路径> / tcp-<端口>(调试用)
-T <ms> 网关 socket 超时
-r 允许 NMT reset 后重启系统

启动后输出:

[slave] motor_app: communication reset
... (syslog: CANopen device, Node ID = 0x01, running ...)

说明:demo 从站已禁用非易失存储(CO_CONFIG_STORAGE=0),因此首次启动 不会出现 *.persist 文件缺失的 EMCY 告警,NMT 稳定在 operational。

4.2 启动主站(终端 2)

./build/motor402_master vcan0 -i 2 -s 1 -m pp --amp 80000
参数 说明 默认
<can device> CAN 接口名
-i <NodeId> 主站节点号 2
-s <NodeId> 从站节点号 1
-m <mode> pp 位置模式 / pv 速度模式 pp
-n <moves> 往复次数 4
--amp <v> 运动幅度(increments) 100000
--pdo 改用 RPDO1 发送控制字与目标(默认 SDO)

运行流程(自动):NMT 启动从站 → 等心跳 → 设模式 → 0x6040=06/07/0F 使能 → 写目标位置 → 轮询状态字等 target reached → 读回实际位置 → 反向 → 循环。

4.3 三种演示方式

# 1) 位置模式(pp):往复 2 次,幅度 60000
./build/motor402_master vcan0 -i 2 -s 1 -m pp -n 2 --amp 60000

# 2) 速度模式(pv):正反转各保持 2.5s
./build/motor402_master vcan0 -i 2 -s 1 -m pv -n 2 --amp 30000

# 3) PDO 控制:控制字+目标经 RPDO1(0x201) 发送
./build/motor402_master vcan0 -i 2 -s 1 -m pp --pdo -n 2 --amp 60000

4.4 日志解读

从站(命令接收 + 每秒位置变化):

[slave] received controlword 0x0006              ← Shutdown
[slave] received controlword 0x0007              ← Switch on
[slave] received controlword 0x000F              ← Enable operation
[slave] received modes of operation 1            ← 0x6060 = pp
[slave] received target position 80000           ← 0x607A
[slave] DRIVE state: 1 -> 2 -> 3 -> 4            ← 402 状态机迁移
[slave] pp: target 80000 reached
[slave] pos=80000 (demand 80000), vel=0, state=4, sw=0x0637   ← 每秒

主站(SDO 收发,读值变化时才打印):

[master] SDO TX write 0x6060:0 = 01
[master] SDO TX write 0x6040:0 = 06 00
[master] SDO RX ack write 0x6040:0
[master] SDO TX write 0x607A:0 = 80 38 01 00    ← 80000 (小端 0x00013880)
[master] SDO TX read 0x6041:0
[master] SDO RX read 0x6041:0 = 37 02           ← 状态字变化
[master] moving to 80000
[master] target reached: statusword=0x0637 (operation enabled) target-reached
[master] position actual = 80000

4.5 完整运行输出示例(一次往复,--amp 80000 -n 1

从站(终端 1)

[slave] motor_app: communication reset
[slave] received controlword 0x0006              ← shutdown
[slave] DRIVE state: 1 -> 2                      ← switch on disabled -> ready to switch on
[slave] received controlword 0x0007
[slave] DRIVE state: 2 -> 3                      ← switched on
[slave] received controlword 0x000F
[slave] DRIVE state: 3 -> 4                      ← operation enabled
[slave] received modes of operation 1            ← pp 模式
[slave] received target position 80000
[slave] pos=12500 (demand 12500), vel=50000, state=4, sw=0x0237   ← 正在加速/匀速
[slave] pos=80000 (demand 80000), vel=0, state=4, sw=0x0637       ← 到位(bit10 置位)
[slave] pp: target 80000 reached
[slave] received target position -80000
[slave] pos=45000 (demand 45000), vel=-50000, state=4, sw=0x0237  ← 反向运动中
[slave] pos=0 (demand 0), vel=0, state=4, sw=0x0637               ← 回到 0
[slave] pp: target -80000 reached

主站(终端 2)

NMT 'enter operational' sent to node 1
slave 1 is operational
[master] SDO TX write 0x6060:0 = 01
[master] SDO RX ack write 0x6060:0
[master] SDO TX write 0x6040:0 = 06 00
[master] SDO RX ack write 0x6040:0
[master] SDO TX write 0x6040:0 = 07 00
[master] SDO RX ack write 0x6040:0
[master] SDO TX write 0x6040:0 = 0F 00
[master] SDO RX ack write 0x6040:0
[master] drive enabled, mode pp, starting moves
[master] SDO TX write 0x607A:0 = 80 38 01 00    ← +80000
[master] SDO TX read 0x6041:0
[master] SDO RX read 0x6041:0 = 37 02           ← 状态字变化
[master] moving to 80000
[master] target reached: statusword=0x0637 (operation enabled) target-reached
[master] SDO TX read 0x6064:0
[master] SDO RX read 0x6064:0 = 80 38 01 00
[master] position actual = 80000
[master] SDO TX write 0x607A:0 = 80 C7 FE FF    ← -80000 (补码 0xFFFEC780)
[master] moving to -80000
[master] target reached: statusword=0x0637 (operation enabled) target-reached
[master] SDO TX read 0x6064:0
[master] position actual = 0
[master] all 1 moves completed

主站内部相位机:NMT启动 → 等心跳 → 设模式 → cw=06→07→0F → 写目标 → 轮询状态字(bit10) → 读回位置 → 反向 → 往复

5. 调试工具

5.1 candump — 观察总线上所有报文

candump -td -a vcan0                       # 全部报文(带时间戳)
candump vcan0,181:7FF,281:7FF,701:7FF      # 只看从站 TPDO1/TPDO2/心跳
candump vcan0,601:7FF,581:7FF              # 只看 SDO 请求/响应

常见 COB-ID 速查(节点 1 = 从站,节点 2 = 主站):

COB-ID 含义
0x000 NMT 命令
0x081 从站 EMCY
0x181 从站 TPDO1:状态字(u16)+位置(i32),每 100ms
0x281 从站 TPDO2:模式显示(i8)+速度(i32),每 100ms
0x201 主站 RPDO1→从站:控制字(u16)+目标位置(i32)
0x581/0x601 从站 SDO 响应/请求
0x701 从站心跳(0x05=operational),每 500ms

5.2 cansend — 手动发送命令

# 向节点 1 写 0x6040:0 = 0x000F(enable operation,SDO 快速下载)
cansend vcan0 601 2F 40 60 00 0F 00 00 00

# 向节点 1 写 0x607A:0 = 80000(目标位置,小端 0x13880 → 80 38 01 00)
cansend vcan0 601 23 7A 60 00 80 38 01 00

5.3 CiA 309 网关 — 交互式控制(从站)

./build/motor402_slave vcan0 -i 1 -c "stdio"
1 w 0x6060 0 u8 1          # pp 模式
1 w 0x6040 0 u16 0x06      # shutdown
1 w 0x6040 0 u16 0x07      # switch on
1 w 0x6040 0 u16 0x0F      # enable operation
1 w 0x607A 0 i32 80000     # 运动到 80000
1 r 0x6064 0 i32           # 读实际位置
1 r 0x6041 0 u16           # 读状态字

或本地 socket 接口配合 cocomm

./build/motor402_slave vcan0 -i 1 -c "local-/tmp/CO_command_socket"
cd cocomm && make && ./cocomm "1 r 0x6064 0 i32"

6. CiA 402 快速参考

6.1 驱动状态机(本 demo 实现)

graph TD
    A[Switch on disabled] -->|Shutdown 0x06| B[Ready to switch on]
    B -->|Switch on 0x07| C[Switched on]
    C -->|Enable operation 0x0F| D[Operation enabled]
    D -->|bit2=0 快速停止| E[Quick stop active]
    E -->|bit2=1 且 bit3=1| D
    D -->|bit1=0 禁用电压| A
    B -->|bit1=0 或 bit2=0| A
    C -->|bit1=0 或 bit2=0| A
    E -->|bit1=0| A
    D -.->|故障 跟随误差超限| F[Fault reaction active]
    F -->|100ms| G[Fault]
    G -->|bit7 上升沿 故障复位| A
Loading

6.2 控制字 0x6040(RW)

bit 名称 说明
0 Switch on 1 = 接通
1 Enable voltage 1 = 使能电压
2 Quick stop 1 = 允许运行(0 触发快速停止)
3 Enable operation 1 = 使能运行
4-6 保留 置 0
7 Fault reset 0→1 上升沿复位故障
8 Halt 1 = 暂停(按 0x6084 减速停止)

常用命令值:0x06 Shutdown、0x07 Switch on、0x0F Enable operation、0x80 Fault reset。

6.3 状态字 0x6041(RO)

bit 名称 说明
0 Ready to switch on 就绪
1 Switched on 已接通
2 Operation enabled 运行使能
3 Fault 故障(需 bit7 复位)
4 Voltage enabled 电压使能
5 Quick stop 快速停止激活
6 Switch on disabled 禁止接通
7 Warning 警告
9 Remote 远程控制(恒 1)
10 Target reached 到位(窗口 0x6065 内持续 0x6067)
12 Set-point acknowledge pp 模式目标已接收

典型值:0x0250 禁止接通 / 0x0221 就绪 / 0x0223 已接通 / 0x0237 运行使能 / 0x0208 故障。

6.4 对象字典速查表(demos/motor402_slave/OD.c

索引 名称 类型 访问 默认值
0x603F Error code u16 RO 0
0x6040 Controlword u16 RW 0
0x6041 Statusword u16 RO 0x0250
0x605A Quick stop option code u16 RW 1
0x605B Shutdown option code u16 RW 0
0x605C Disable operation option code u16 RW 1
0x605D Halt option code u16 RW 1
0x605E Fault reaction option code u16 RW 0
0x6060 Modes of operation(1=pp, 3=pv) i8 RW 0
0x6061 Modes of operation display i8 RO 0
0x6062 Position demand value i32 RO 0
0x6063 Position actual (internal) i32 RO 0
0x6064 Position actual value i32 RO 0
0x6065 Position window(到位判据) u32 RW 1
0x6067 Position window time u16 RW 50 ms
0x606B Velocity demand value i32 RO 0
0x606C Velocity actual value i32 RO 0
0x607A Target position i32 RW 0
0x6080 Max motor speed u32 RW 100000
0x6081 Profile velocity u32 RW 50000
0x6083 Profile acceleration u32 RW 100000
0x6084 Profile deceleration u32 RW 100000
0x6085 Quick stop deceleration u32 RW 200000
0x6086 Motion profile type(0=线性) i16 RW 0
0x6098 Homing method(未实现,置 0) i8 RW 0
0x6099 Homing speeds(sub1/2) rec RW -
0x609A Homing acceleration u32 RW 10000
0x60F4 Following error actual i32 RO 0
0x60FF Target velocity i32 RW 0
0x6502 Supported drive modes(pp+pv) u32 RO 0x03
0x2000 Encoder increments/rev(仿真) u32 RW 10000
0x2001 Motor inertia(仿真,tau=值×1ms) u32 RW 100
0x2002 Friction(仿真) u32 RW 0
0x2003 Rated torque(仿真) u32 RW 100 mNm
0x2004 Load torque(仿真) i32 RW 0
0x2005 Max velocity(仿真限速) u32 RW 100000
0x2006 Following error limit(超限→故障) u32 RW 20000

单位约定:位置 = increments,速度 = inc/s,加速度 = inc/s²。

6.5 PDO 映射

PDO COB-ID 方向 映射 触发
RPDO1 0x200+从站号(0x201) 主站→从站 0x6040:0:16 + 0x607A:0:32(6 字节) 异步(type 0xFE)
TPDO1 0x180+从站号(0x181) 从站→主站 0x6041:0:16 + 0x6064:0:32(6 字节) 事件定时 100ms
TPDO2 0x280+从站号(0x281) 从站→主站 0x6061:0:8 + 0x606C:0:32(5 字节) 事件定时 100ms

6.6 CAN 报文逐字节解析

SDO 写 0x6040:0 = 0x000F(enable operation):

601  2F 40 60 00 0F 00 00 00
│    │  │  │  │  └─────── 数据区 8 字节,有效 1 字节:0x0F
│    │  │  │  └────────── subindex = 0x00
│    │  │  └───────────── index = 0x6040(小端 40 60)
│    │  └──────────────── 命令 0x2F = 快速下载(1 字节数据)
└──── 请求 COB-ID = 0x600 + 节点号 1
581  60 40 60 00 00 00 00 00
│    └ 0x60 = 下载响应(成功)
└ 响应 COB-ID = 0x580 + 节点号 1

其他典型帧:

含义
000 01 01 NMT:节点 1 进入 operational
701 05 节点 1 心跳,operational
181 37 02 80 38 01 00 TPDO1:状态字 0x0237 + 位置 80000
081 00 FF 01 2F 04 00 00 00 节点 1 EMCY(0xFF00 设备特定,错误位 0x2F=非易失存储,info=0x04)

7. 常见问题(FAQ)

Q1:Error: CAN device 'vcan0' not found A:vcan0 不存在。先执行第 2.2 节创建接口(重启后需重建)。

Q2:主站 ERROR: heartbeat of slave 1 not seen A:依次检查:

  • 从站是否在运行且带 -i 1(不带则节点未配置,不发心跳);
  • candump vcan0,701:7FF 是否能看到 701 [1] 05(05=operational);
  • 主从是否在同一网络命名空间(unshare/容器场景)。

Q3:SDO error 0x0000 A:SDO 请求无响应。确认从站在运行;用 candump 观察 0x601/0x581 是否有往返。

Q4:位置只增不减 / 往复数值不对 A:旧版本 profilePositionStep 有速度方向 bug(已修复)。重新 cmake --build build

Q5:启动时 EMCY errorCode=0x5000, errorBit=0x2F A:这是"非易失存储文件缺失"告警。demo 从站已禁用存储,正常不会出现;若出现说明构建时未带 CO_CONFIG_STORAGE=0

Q6:想恢复从站参数持久化 A:去掉 CMakeLists.txtmotor402_slave 目标的 CO_CONFIG_STORAGE=0 重新构建即可(注意首次运行会产生 EMCY 且 NMT 可能降级为 pre-operational)。

Q7:两个 demo 的 CiA 402 对象字典在哪? A:demos/motor402_slave/OD.c / OD.h(DS301 + 402 + 厂商仿真参数,手写,格式同 CANopenEditor V4 生成文件)。

8. 目录结构

CANopenLinux/
├── CMakeLists.txt              # C11/C++14,Unix Makefiles 生成器
├── CO_driver.c ...             # Linux SocketCAN 驱动
├── CO_main_basic.c             # 通用主程序(canopend 与从站共用)
├── CANopenNode/                # CANopenNode 栈(submodule)
├── cocomm/                     # CiA309 命令行工具
└── demos/
    ├── README.md               # 本文档
    ├── motor402_slave/         # 402 从站:OD.c/h、motor_app.c/h、README
    └── motor402_master/        # 402 主站:main.c、README

参考实现(开源电机项目):

About

canopenlinux ,master slave simulator , slave sim pp mode run in loop

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages