# canopen **Repository Path**: wwwzhanglong/canopen ## Basic Information - **Project Name**: canopen - **Description**: 本项目实现了一个基于 CANopen 协议的伺服电机控制系统,集成了 TCP 服务器,支持远程控制和实时状态监控。系统运行在 ALIENTEK 战舰开发板 V3,通过 CANopen 协议与 Nimotion 公司的一体化低压伺服电机通信。用户可以通过 TCP 客户端发送指令,控制电机的运行模式(位置、速度、转矩)、目标位置、速度或清零位置,同时接收电机的实时状态(如位置、速度、转矩)。 - **Primary Language**: C/C++ - **License**: GPL-3.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 3 - **Created**: 2025-07-08 - **Last Updated**: 2026-02-25 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # CANopen 伺服电机控制项目 ## 项目概述 本项目实现了一个基于 **CANopen 协议** 的伺服电机控制系统,集成了 **TCP 服务器**,支持远程控制和实时状态监控。系统运行在 ALIENTEK 战舰开发板 V3(基于 STM32F103 微控制器),通过 CANopen 协议与 Nimotion 公司的一体化低压伺服电机通信。用户可以通过 TCP 客户端发送指令,控制电机的运行模式(位置、速度、转矩)、目标位置、速度或清零位置,同时接收电机的实时状态(如位置、速度、转矩)。 项目目标是提供一个模块化、可扩展的控制框架,适用于工业自动化、机器人技术和精密运动控制场景。核心功能包括: - 通过 TCP 接收客户端指令并解析为电机控制操作。 - 使用 CANopen 协议(SDO 和 PDO)与伺服电机通信。 - 实时监控电机状态并通过 TCP 反馈到客户端。 - 支持多种运行模式(位置、速度、转矩)和功能(如原点回归、故障清除)。 ## 项目组件 项目由以下四个主要代码文件组成: 1. **`nimotion.c`**:CANopen 通信核心模块,提供 SDO 和 PDO 处理、电机状态管理和运动控制接口。 2. **`nimotiontest.c`**:测试模块,验证电机在不同模式下的性能。 3. **`tcp_server_demo.c`**:TCP 服务器模块,处理客户端指令和状态反馈。 4. **`timer.c`**:定时器模块,配置中断并解析 PDO 数据以更新电机状态。 ## CANopen 协议详解 CANopen 是一种基于 CAN(控制器局域网)总线的高层通信协议,遵循 CiA 301 标准,并结合 CiA 402 伺服驱动器配置文件,广泛应用于工业自动化和运动控制系统。它通过标准化设备间的通信方式,确保不同厂商设备之间的互操作性。 与本项目相关的 CANopen 关键概念包括: ### 1. 对象字典(Object Dictionary, OD) - **定义**:每个 CANopen 设备维护一个对象字典,包含配置参数、状态信息和过程数据的数据库。对象字典通过 16 位索引和 8 位子索引访问。 - **作用**:提供标准化的参数和变量访问接口,例如运行模式(`0x6060`)、目标速度(`0x60FF`)、当前位置(`0x6064`)。 - **项目中的应用**: - 通过 SDO 读写对象字典,配置电机参数或读取状态。 - 示例:`NiM_writeParam(nAddr, 0x60FF, 0x00, &nVelocity, 4)` 写入目标速度。 ### 2. SDO(服务数据对象) - **定义**:SDO 用于访问和修改对象字典中的条目,采用请求-响应机制,支持可靠传输。 - **特点**: - 客户端-服务器模型,主机发送请求,设备返回数据或确认。 - 支持可变长度数据(通过分段传输),适合非实时任务。 - 报文由两个 CAN 帧组成(请求和响应),例如 `StdId = 0x600 + nAddr`。 - **作用**: - 配置电机参数(如运行模式、加速度)。 - 写入控制指令(如目标位置、速度)。 - 读取状态信息(如错误码、状态字)。 - **项目中的应用**: - 设置运行模式(`0x6060`)、目标位置(`0x607A`)、目标速度(`0x60FF`)。 - 清零位置(`0x2031`)、读取错误码(`0x603F`)。 - 示例:在 `tcp_server_demo.c` 中,`NiM_moveVelocity` 通过 SDO 写入 `0x60FF` 设置速度。 ### 3. PDO(过程数据对象) - **定义**:PDO 用于实时传输过程数据(如传感器值、控制指令),优化了速度和效率。 - **特点**: - 单向传输,单帧最多 8 字节,无需确认机制。 - 分为 TPDO(设备发送到主机)和 RPDO(主机发送到设备)。 - 支持同步(由 SYNC 消息触发)、异步(事件触发)或按需触发。 - 数据内容通过对象字典映射(`0x1600~0x1BFF`),可自定义传输参数。 - **作用**: - 实时监控设备状态(如位置、速度、转矩)。 - 周期性下发控制指令(如目标速度)。 - **项目中的应用**: - **TPDO**:接收电机实时状态,如 `current_position`、`current_speed`、`current_torque`。 - **RPDO**:可能隐式用于下发控制指令(未明确实现)。 - 示例:在 `timer.c` 中,解析 TPDO 数据更新 `current_position[Motor1]` 和 `current_speed[Motor1]`。 ### 4. NMT(网络管理) - **定义**:NMT 控制网络和节点的状态(如初始化、预操作、操作、停止)。 - **作用**:确保设备正常运行和网络同步。 - **项目中的应用**: - 使用 `NiM_NMT(nAddr, OPERATIONAL)` 将电机设置为操作状态,启用 PDO 通信。 ### SDO 与 PDO 的对比 | 特性 | SDO | PDO | |---------------------|----------------------------------|----------------------------------| | **用途** | 配置、参数设置、状态读取 | 实时数据交换 | | **传输方式** | 请求-响应,确认机制 | 单向传输,高效但无确认 | | **数据长度** | 可变,支持分段传输 | 固定(通常 8 字节) | | **优先级** | 较低,适合非实时任务 | 较高,适合时间关键型应用 | | **触发机制** | 主机主动请求 | 同步、异步或按需触发 | | **项目中的应用** | 设置运行模式、目标值 | 接收实时位置、速度、转矩 | ## 代码结构与功能分析 ### 1. nimotion.c **功能**:实现 CANopen 通信层,提供与伺服电机交互的核心接口。 - **SDO 通信**: - `NiM_readParam`:读取对象字典数据(如错误码 `0x603F`)。 - `NiM_writeParam`:写入对象字典数据(如目标速度 `0x60FF`)。 - **PDO 处理**: - 在 `CAN_Master_Handler` 中,接收 TPDO 数据并存储在 `tpdo2`、`tpdo3`、`tpdo4` 中。 - 未解析数据,需结合 `timer.c` 的解析逻辑。 - **电机状态管理**: - `NiM_isMotorOnline`:检查电机在线状态(`0x200C:02`)。 - `NiM_getMotorInfo`:获取序列号(`0x1018:04`)和软件版本(`0x1018:03`)。 - `NiM_clearErrorState`:清除故障(`0x6040`)。 - **运动控制**: - `NiM_moveAbsolute`:绝对位置运动(`0x607A`)。 - `NiM_moveRelative`:相对 chroma运动(`0x607A`)。 - `NiM_moveVelocity`:速度模式(`0x60FF`)。 - `NiM_moveTorque`:转矩模式(`0x6071`)。 - `NiM_goHome`:原点回归(`0x6098`)。 - **CANopen 应用**: - SDO 用于配置和控制,PDO 用于接收状态。 - 示例:`NiM_moveVelocity` 通过 SDO 写入 `0x60FF` 设置速度。 ### 2. nimotiontest.c **功能**:提供测试函数,验证电机在不同模式下的性能。 - **`NiM_Position_Mode`**: - 测试绝对和相对位置运动,通过 SDO 设置目标位置并用 PDO 验证 `current_position`。 - 示例:移动到 0 位置并检查 `current_position[nAddr] == 0`。 - **`NiM_Velocity_Mode`**: - 测试速度控制,通过 SDO 设置目标速度并验证 `current_speed`。 - 示例:设置 1200 RPM 并检查速度误差。 - **`NiM_Torque_Mode`**: - 测试转矩控制,通过 SDO 设置目标转矩并验证 `current_torque`。 - **CANopen 应用**: - SDO 设置控制参数,PDO 获取实时反馈。 - 示例:`current_position[nAddr]` 通过 PDO 更新,用于验证位置控制。 ### 3. tcp_server_demo.c **功能**:实现 TCP 服务器,处理客户端指令和状态反馈。 - **指令接收与解析**: - 接收 16 字节数据包(帧头 `0xEB 0x90 0x00 0x0D`,10 字节命令,帧尾 `0x55 0xFF`)。 - 解析命令(`command_10B`),支持: - 选择模式(`mode`):位置或速度模式。 - 设置速度(`SetVelocity`):通过 SDO 写入 `0x60FF`。 - 绝对/相对位置(`AbsolutePosition`/`RelativePosition`):通过 SDO 写入 `0x607A`。 - 清零位置(`Gotozero`):通过 SDO 写入 `0x2031`。 - **电机控制**: - 调用 `NiM_Position_Mode1` 或 `NiM_Velocity_Mode1`,通过 SDO 配置电机。 - 示例:`NiM_moveVelocity(Motor1, Velocity)` 设置速度。 - **状态反馈**: - 通过 PDO 获取的 `current_position` 等数据通过 TCP 发送。 - 示例:交替发送位置数据和提示信息(如“速度模式开启”)。 - **CANopen 应用**: - SDO 用于控制指令下发,PDO 用于状态反馈。 - 示例:速度控制通过 SDO 写入 `0x60FF`,位置通过 PDO 从 `tpdo2` 获取。 ### 4. timer.c **功能**:配置定时器并处理中断,解析 PDO 数据以更新电机状态。 - **定时器初始化**: - `TIM3_Int_Init`:配置定时器 3,每 10ms 增加 `lwip_localtime`,用于 lwIP 协议栈时间管理。 - `TIM2_Int_Init`:配置定时器 2,每 1ms 触发中断,用于 PDO 解析和状态更新。 - **PDO 数据解析**: - 在 `TIM2_IRQHandler` 中,解析 TPDO 数据: - 如果 `tpdo2.StdId == 0x280 + Motor1`(TPDO2),解析 4 字节数据为 `current_position[Motor1]`: ```c current_position[Motor1] = (int32_t)((tpdo2.Data[3] << 24) + (tpdo2.Data[2] << 16) + (tpdo2.Data[1] << 8) + tpdo2.Data[0]); ``` - 如果 `tpdo3.StdId == 0x380 + Motor1`(TPDO3),解析 4 字节数据为 `current_speed[Motor1]`: ```c current_speed[Motor1] = (int32_t)((tpdo3.Data[3] << 24) + (tpdo3.Data[2] << 16) + (tpdo3.Data[1] << 8) + tpdo3.Data[0]); ``` - 每 1000ms(`timer2_cnt % 1000 == 0`)通过串口打印位置和速度: ```c printf("电机位置:%d\r\n", current_position[Motor1]); printf("电机速度:%d\r\n", current_speed[Motor1]); ``` - **CANopen 应用**: - PDO 用于实时更新电机状态(位置和速度)。 - 示例:TPDO2(`0x280 + Motor1`)映射 `0x6064`(当前位置),TPDO3(`0x380 + Motor1`)映射 `0x606C`(实际速度)。 ## 系统集成与工作流程 系统的工作流程如下: 1. **初始化**: - `timer.c` 初始化定时器,启用 1ms 中断(TIM2)解析 PDO 数据。 - `tcp_server_demo.c` 启动 TCP 服务器,监听客户端连接。 - `nimotion.c` 初始化 CANopen 通信,配置电机节点。 2. **客户端指令**: - TCP 客户端发送 16 字节指令(如 `SetVelocity 720` 表示 120 RPM)。 - `tcp_server_demo.c` 解析指令,提取参数(如速度值 720 × 278 ≈ 200160 计数/秒)。 3. **CANopen 控制**: - `tcp_server_demo.c` 调用 `nimotion.c` 函数,通过 SDO 写入对象字典: - 速度:`NiM_moveVelocity` 写入 `0x60FF`。 - 位置:`NiM_moveAbsolute` 写入 `0x607A`。 - 清零:`NiM_writeParam` 写入 `0x2031`。 - `nimotion.c` 使用 `NiM_NMT` 启用 PDO 通信。 4. **实时反馈**: - 电机通过 TPDO 发送实时状态,`timer.c` 解析 `tpdo2` 和 `tpdo3`,更新 `current_position` 和 `current_speed`。 - `tcp_server_demo.c` 通过 TCP 将状态(`current_position` 或提示信息)返回客户端。 5. **用户界面**: - LCD 显示服务器 IP、客户端连接状态和指令。 - 串口打印调试信息(如位置、速度)。 ## CANopen 协议在项目中的应用 ### 1. PDO(过程数据对象) - **功能**:实时传输过程数据,如电机状态。 - **应用**: - **TPDO**:电机发送当前位置(`0x6064`)、速度(`0x606C`)、转矩(`0x6077`)。 - 在 `timer.c` 中,TPDO2(`0x280 + Motor1`)解析为 `current_position[Motor1]`,TPDO3(`0x380 + Motor1`)解析为 `current_speed[Motor1]`。 - 示例: ```c current_position[Motor1] = (int32_t)((tpdo2.Data[3] << 24) + (tpdo2.Data[2] << 16) + (tpdo2.Data[1] << 8) + tpdo2.Data[0]); ``` - **RPDO**:可能隐式用于下发控制指令(如目标速度 `0x60FF`),但代码未明确实现。 - 在 `nimotiontest.c` 中,`current_position` 用于验证位置控制结果。 - 在 `tcp_server_demo.c` 中,`current_position` 通过 TCP 发送到客户端。 - **映射**: - TPDO2 映射 `0x6064`(当前位置,4 字节)。 - TPDO3 映射 `0x606C`(实际速度,4 字节)。 ### 2. SDO(服务数据对象) - **功能**:配置参数、设置目标值、读取状态。 - **应用**: - **配置**:设置运行模式(`0x6060`)、控制字(`0x6040`)、清零位置(`0x2031`)。 - **控制**:写入目标位置(`0x607A`)、速度(`0x60FF`)、转矩(`0x6071`)。 - **状态读取**:读取错误码(`0x603F`)、状态字(`0x6041`)。 - 示例: - 在 `tcp_server_demo.c` 中,`NiM_moveVelocity` 通过 SDO 写入 `0x60FF`: ```c error = NiM_writeParam(nAddr, 0x60FF, 0x00, (uint8_t *)(&nVelocity), 4); ``` - 在 `nimotiontest.c` 中,`NiM_Position_Mode` 通过 SDO 设置目标位置。 - **实现**: - `nimotion.c` 的 `NiM_writeParam` 构造 SDO 请求,等待响应确认。 ### 3. NMT(网络管理) - **功能**:管理节点状态。 - **应用**: - `NiM_NMT(nAddr, OPERATIONAL)` 启用 PDO 通信。 - 示例:在 `tcp_server_demo.c` 中,`NiM_Velocity_Mode1` 调用 `NiM_NMT`。 ### 4. 对象字典 - **功能**:提供参数访问接口。 - **应用**: - 关键索引: - `0x6060`:运行模式(位置、速度、转矩)。 - `0x60FF`:目标速度。 - `0x607A`:目标位置。 - `0x6064`:当前位置(PDO)。 - `0x606C`:实际速度(PDO)。 - 示例:`NiM_writeParam(nAddr, 0x2031, 0x01, &temp3, 2)` 清零位置。 ## 单位换算与协议细节 - **速度换算**: - 代码中 `1000000/5 = 200000` 对应 120 RPM,表明速度单位为计数/秒,一圈为 100000 计数。 - 客户端速度值(如 720 度/秒)乘以 278,转换为计数/秒(720 × 278 ≈ 200160 ≈ 120 RPM)。 - 换算公式:\(\frac{100000}{360} \approx 277.7778 \approx 278\),表示 1 度/秒 ≈ 278 计数/秒。 - **位置换算**: - 位置值乘以 278,将客户端输入的度转换为计数(1 度 ≈ 278 计数)。 - 示例:360 度 × 278 ≈ 100080 计数 ≈ 1 圈。 - **协议约定**: - 系数 278 是整数近似,简化嵌入式计算,误差约 0.08%。 ## 潜在改进建议 1. **PDO 解析完善**: - 扩展 `timer.c` 的解析逻辑,支持 `current_torque` 和多电机(`Motor1` 以外的节点)。 - 示例: ```c if (tpdo4.StdId == 0x480 + Motor1) current_torque[Motor1] = (int16_t)((tpdo4.Data[1] << 8) + tpdo4.Data[0]); ``` 2. **RPDO 实现**: - 配置 RPDO 映射(`0x1600~0x17FF`),通过 PDO 下发目标速度(`0x60FF`),提高实时性。 3. **错误处理**: - 解析 SDO 错误码(`sdo_recv_msg.Data[4~7]`),通过 TCP 反馈到客户端。 4. **多电机支持**: - 使用数组或结构体管理多电机状态,支持动态节点 ID。 5. **实时性优化**: - 替换固定延时(如 `delay_ms(1000)`)为状态标志(如到位标志)。 6. **安全性**: - 添加 TCP 通信的身份验证或加密机制。 7. **数据反馈**: - 实现 `Datasend` 命令,发送 `current_speed` 和 `current_torque`。 ## 参考资料 - 《一体化低压伺服电机 CANopen 通信用户手册》:提供 Nimotion 电机的对象字典和协议细节。 - [CANopen 标准规范 (CiA 301)](https://www.can-cia.org/can-knowledge/canopen/canopen-standard/):CANopen 协议规范。 - [伺服驱动器设备配置文件 (CiA 402)](https://www.can-cia.org/can-knowledge/canopen/application-profile/cia-402/):伺服驱动器标准。 - [CAN in Automation (CiA)](https://www.can-cia.org/):CANopen 资源。