# Curtis 电机控制器集成指南 ## 概述 本指南介绍如何在 AGV 路径跟踪项目中集成柯蒂斯(Curtis)1298+1220C 电机控制器的 CAN 通讯功能。 **协议版本:** V4.0 **波特率:** 125 kbps **厂商:** 上海诺力智能科技有限公司 --- ## 目录 1. [快速开始](#快速开始) 2. [文件结构](#文件结构) 3. [编译和运行](#编译和运行) 4. [API 参考](#api-参考) 5. [完整调用流程](#完整调用流程) 6. [示例程序说明](#示例程序说明) 7. [故障排查](#故障排查) 8. [最佳实践](#最佳实践) --- ## 快速开始 ### 1. 最简示例(5行代码) ```cpp #include "include/can/CurtisMotorController.h" CurtisMotorController curtis; curtis.Connect(); // 连接 CurtisCommand cmd; cmd.driveSpeed = CurtisMotorController::SpeedPercentToCAN(30.0f); // 前进30% curtis.SendCommand(cmd); // 发送命令 curtis.ReceiveStatus(); // 接收状态 curtis.PrintStatus(); // 打印状态 curtis.Disconnect(); // 断开 ``` ### 2. 基本控制循环 ```cpp CurtisMotorController curtis; curtis.Connect(); CurtisCommand cmd; cmd.driveSpeed = CurtisMotorController::SpeedPercentToCAN(30.0f); cmd.steerAngle = CurtisMotorController::AngleDegreeToCAN(15.0f); while (running) { curtis.ReceiveStatus(); // 接收状态 curtis.SendCommand(cmd); // 发送命令(≤20ms周期) Sleep(15); // 15ms } curtis.Disconnect(); ``` --- ## 文件结构 ### 核心文件 ``` agv_path_tracking/ ├── include/can/ │ └── CurtisMotorController.h # Curtis 控制器头文件 ├── src/can/ │ └── CurtisMotorController.cpp # Curtis 控制器实现 ├── examples/ │ ├── curtis_demo.cpp # 键盘控制示例 │ └── curtis_path_tracking_demo.cpp # 路径跟踪示例 ├── lib/ │ ├── ControlCAN.h # CAN 接口库头文件 │ └── ControlCAN.dll # CAN 接口库 └── docs/curtis/ └── CURTIS_INTEGRATION_GUIDE.md # 本文档 ``` ### 技能文档 ``` .claude/skills/ ├── can-protocol.md # USB-CAN 接口函数库参考 └── curtis-motor-protocol.md # Curtis 协议详细说明 ``` --- ## 编译和运行 ### 1. 编译 ```bash cd build cmake .. make ``` **编译目标:** - `curtis_demo` - 键盘控制演示 - `curtis_path_tracking_demo` - 路径跟踪演示 ### 2. 运行示例 **键盘控制演示:** ```bash ./curtis_demo ``` **路径跟踪演示:** ```bash ./curtis_path_tracking_demo ``` ### 3. 在您的项目中使用 **在 CMakeLists.txt 中添加:** ```cmake add_executable(your_program your_main.cpp src/can/CurtisMotorController.cpp ) target_link_libraries(your_program ${CMAKE_SOURCE_DIR}/lib/ControlCAN.dll ) ``` **在代码中包含头文件:** ```cpp #include "include/can/CurtisMotorController.h" ``` --- ## API 参考 ### 核心类:CurtisMotorController #### 构造函数 ```cpp CurtisMotorController(DWORD deviceType = VCI_USBCAN2, DWORD deviceIndex = 0, DWORD canChannel = 0); ``` **参数:** - `deviceType`: CAN 设备类型(默认 VCI_USBCAN2 = 4) - `deviceIndex`: 设备索引(默认 0 = 第一个设备) - `canChannel`: CAN 通道(默认 0 = CAN1) #### 连接管理 ```cpp bool Connect(); // 连接到 Curtis 控制器 void Disconnect(); // 断开连接 bool IsConnected() const; // 检查是否已连接 ``` #### 命令发送 ```cpp bool SendCommand(const CurtisCommand& cmd); // 发送控制命令 bool SendStopCommand(); // 发送停止命令 void EmergencyStop(); // 紧急停车 ``` **重要:** 必须以 ≤20ms 周期持续发送命令,否则控制器会超时保护。 #### 状态接收 ```cpp bool ReceiveStatus(); // 接收状态反馈 const CurtisStatus& GetStatus() const; // 获取当前状态 void PrintStatus() const; // 打印状态到控制台 ``` #### 故障检测 ```cpp bool HasError() const; // 检查是否有故障 bool IsTimeout(uint32_t timeoutMs) const; // 检查通讯超时 std::string GetErrorString() const; // 获取故障描述 ``` #### 工具函数(静态) ```cpp static int16_t SpeedPercentToCAN(float percent); // 速度百分比 → CAN值 static float SpeedCANToPercent(int16_t canValue); // CAN值 → 速度百分比 static int16_t AngleDegreeToCAN(float degrees); // 角度 → CAN值 static float AngleCANToDegree(int16_t canValue); // CAN值 → 角度 ``` ### 数据结构 #### CurtisCommand(控制命令) ```cpp struct CurtisCommand { int16_t driveSpeed; // 行走速度 -4096~4095 int16_t liftSpeedLeft; // 左侧提升速度 -4096~4095 int16_t liftSpeedRight; // 右侧提升速度 -4096~4095 int16_t steerAngle; // 转向角度 -900~900 uint8_t controlBits0; // 控制位0 uint8_t controlBits1; // 控制位1(自动设置AGV标识) }; ``` #### CurtisStatus(状态反馈) ```cpp struct CurtisStatus { uint8_t driveError; // 行走故障代码 uint8_t steerError; // 转向故障代码 float currentAngle; // 当前转向角度(度) int16_t motorSpeed; // 电机转速 uint8_t batteryLevel; // 电量 0-100% bool isAutoMode; // 是否自动模式 bool isEmergencyStop; // 是否急停状态 uint32_t lastUpdateTime; // 最后更新时间戳 }; ``` --- ## 完整调用流程 ### 流程图 ``` ┌─────────────────────────────────────────────┐ │ 1. 创建 CurtisMotorController 对象 │ └─────────────────┬───────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────┐ │ 2. Connect() │ │ ├─ VCI_OpenDevice() 打开CAN设备 │ │ ├─ VCI_InitCAN() 初始化(125kbps) │ │ ├─ VCI_StartCAN() 启动CAN通道 │ │ └─ VCI_ClearBuffer() 清空缓冲区 │ └─────────────────┬───────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────┐ │ 3. 主循环(≤15ms 周期) │ │ ┌──────────────────────────────────────┐ │ │ │ ① ReceiveStatus() │ │ │ │ └─ 接收并解析 0x260 状态帧 │ │ │ │ │ │ │ │ ② 检查故障和急停 │ │ │ │ ├─ HasError() │ │ │ │ ├─ IsTimeout() │ │ │ │ └─ 如有异常,执行 EmergencyStop() │ │ │ │ │ │ │ │ ③ 设置控制命令 │ │ │ │ ├─ SpeedPercentToCAN() │ │ │ │ └─ AngleDegreeToCAN() │ │ │ │ │ │ │ │ ④ SendCommand() │ │ │ │ ├─ 发送 0x1E5 行走/提升命令 │ │ │ │ └─ 发送 0x2E5 转向命令 │ │ │ │ │ │ │ │ ⑤ Sleep(15) │ │ │ └──────────────────────────────────────┘ │ └─────────────────┬───────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────┐ │ 4. Disconnect() │ │ ├─ SendStopCommand() 发送停止命令 │ │ └─ VCI_CloseDevice() 关闭设备 │ └─────────────────────────────────────────────┘ ``` ### 详细步骤说明 #### 步骤1:创建对象 ```cpp // 使用默认参数(设备类型=VCI_USBCAN2, 索引=0, 通道=0) CurtisMotorController curtis; // 或指定参数 CurtisMotorController curtis(VCI_USBCAN2, 0, 0); ``` #### 步骤2:连接 ```cpp if (!curtis.Connect()) { std::cerr << "连接失败!" << std::endl; return -1; } ``` **内部执行:** 1. `VCI_OpenDevice()` - 打开 USB-CAN 设备 2. `VCI_InitCAN()` - 配置 125kbps 波特率 3. `VCI_StartCAN()` - 启动 CAN 通道 4. `VCI_ClearBuffer()` - 清空接收缓冲区 #### 步骤3:主循环 ```cpp CurtisCommand cmd; DWORD lastTime = GetTickCount(); while (running) { // 3.1 接收状态 curtis.ReceiveStatus(); const CurtisStatus& status = curtis.GetStatus(); // 3.2 检查故障 if (curtis.HasError()) { std::cout << curtis.GetErrorString() << std::endl; } // 3.3 检查急停 if (status.isEmergencyStop) { cmd.driveSpeed = 0; cmd.steerAngle = 0; } // 3.4 设置命令 cmd.driveSpeed = CurtisMotorController::SpeedPercentToCAN(30.0f); cmd.steerAngle = CurtisMotorController::AngleDegreeToCAN(15.0f); // 3.5 发送命令(15ms周期) if (GetTickCount() - lastTime >= 15) { curtis.SendCommand(cmd); lastTime = GetTickCount(); } Sleep(5); } ``` **关键点:** - **命令周期:** 必须 ≤20ms,推荐 10-15ms - **状态接收:** 每次循环都调用 `ReceiveStatus()` - **故障检测:** 实时检查故障和急停状态 - **数值转换:** 使用工具函数进行单位转换 #### 步骤4:断开连接 ```cpp curtis.Disconnect(); ``` **内部执行:** 1. 发送停止命令(所有速度归零) 2. 等待 50ms 确保命令发送 3. 调用 `VCI_CloseDevice()` 关闭设备 --- ## 示例程序说明 ### 示例1:键盘控制(curtis_demo.cpp) **功能:** - 键盘控制 AGV 运动 - 实时状态监控 - 故障和急停检测 **控制键:** - `W/S` - 前进/后退(30%) - `A/D` - 左转/右转(30度) - `Q/E` - 提升/下降(50%) - `H` - 鸣喇叭 - `T` - 切换龟速模式 - `空格` - 全部停止 - `P` - 打印状态 - `ESC` - 退出 **运行:** ```bash cd build ./curtis_demo ``` **核心代码:** ```cpp // 主循环 while (running) { curtis.ReceiveStatus(); // 接收状态 if (_kbhit()) { char ch = _getch(); switch (ch) { case 'w': cmd.driveSpeed = SpeedPercentToCAN(30.0f); break; // ... 其他键处理 } } if (GetTickCount() - lastSendTime >= 15) { curtis.SendCommand(cmd); // 15ms周期 lastSendTime = GetTickCount(); } Sleep(5); } ``` ### 示例2:路径跟踪(curtis_path_tracking_demo.cpp) **功能:** - 矩形路径跟踪 - 圆形路径跟踪 - 实时位置控制 - 故障自动中止 **运行:** ```bash cd build ./curtis_path_tracking_demo ``` **核心代码:** ```cpp for (size_t i = 0; i < path.size(); i++) { double targetX = path[i].x; double targetY = path[i].y; while (true) { curtis.ReceiveStatus(); // 计算距离和方向 double dx = targetX - currentX; double dy = targetY - currentY; double distance = sqrt(dx*dx + dy*dy); if (distance < 0.1) break; // 到达 // 计算转向角度和速度 double targetAngle = atan2(dy, dx); cmd.steerAngle = AngleDegreeToCAN(angleError); cmd.driveSpeed = SpeedPercentToCAN(30.0f); curtis.SendCommand(cmd); Sleep(15); } } ``` --- ## 故障排查 ### 常见问题 #### 1. 连接失败 **症状:** `Connect()` 返回 false **解决方案:** - 检查 USB-CAN 设备是否连接 - 检查驱动是否正确安装 - 确认 `ControlCAN.dll` 在正确位置 - 检查设备索引是否正确(通常为 0) **诊断命令:** ```cpp VCI_BOARD_INFO info; if (VCI_ReadBoardInfo(VCI_USBCAN2, 0, &info) == 1) { cout << "设备序列号: " << info.str_Serial_Num << endl; } ``` #### 2. 控制器无响应 **症状:** 发送命令后车辆不动 **检查清单:** - [ ] AGV 标识位是否设置(Byte1 Bit0 = 1) - [ ] 命令发送频率是否 ≤20ms - [ ] 手动模式下 J9/SW_3 是否闭合 - [ ] 自动模式下 J24/SW_1 是否为 ON - [ ] 波特率是否为 125kbps **诊断代码:** ```cpp const CurtisCommand& lastCmd = curtis.GetLastCommand(); cout << "最后命令: " << endl; cout << " driveSpeed = " << lastCmd.driveSpeed << endl; cout << " steerAngle = " << lastCmd.steerAngle << endl; cout << " controlBits1 = 0x" << hex << (int)lastCmd.controlBits1 << endl; ``` #### 3. 频繁超时 **症状:** `IsTimeout()` 频繁返回 true **原因:** - 命令发送周期 > 20ms - CAN 总线连接不稳定 - 控制器参数设置不正确 **解决方案:** ```cpp // 检查发送周期 DWORD lastTime = GetTickCount(); curtis.SendCommand(cmd); DWORD elapsed = GetTickCount() - lastTime; cout << "发送耗时: " << elapsed << "ms" << endl; // 应 < 20ms ``` #### 4. 转向不准确 **症状:** 转向角度与预期不符 **解决方案:** - 确认 1220C 参数配置: ``` can steer left stop to centre = -900 can steer right stop to centre = 900 ``` - 检查角度转换: ```cpp float degrees = 30.0f; int16_t canValue = CurtisMotorController::AngleDegreeToCAN(degrees); cout << "30° → CAN值 = " << canValue << endl; // 应为 300 ``` #### 5. 收到故障代码 **症状:** `HasError()` 返回 true **诊断:** ```cpp const CurtisStatus& status = curtis.GetStatus(); cout << "行走故障: 0x" << hex << (int)status.driveError << endl; cout << "转向故障: 0x" << hex << (int)status.steerError << endl; cout << curtis.GetErrorString() << endl; ``` **处理:** - 查阅 Curtis 故障代码手册 - 检查电机连接和电源 - 检查控制器参数配置 --- ## 最佳实践 ### 1. 命令发送频率 **推荐做法:** ```cpp const int SEND_PERIOD_MS = 15; // 15ms周期 DWORD lastSendTime = GetTickCount(); while (running) { if (GetTickCount() - lastSendTime >= SEND_PERIOD_MS) { curtis.SendCommand(cmd); lastSendTime = GetTickCount(); } Sleep(5); } ``` **避免:** - ❌ 周期 > 20ms(会超时) - ❌ 不规律的发送间隔 - ❌ 只发送一次就停止 ### 2. 故障处理 **推荐做法:** ```cpp curtis.ReceiveStatus(); const CurtisStatus& status = curtis.GetStatus(); // 实时检查故障 if (curtis.HasError()) { cout << "[故障] " << curtis.GetErrorString() << endl; curtis.EmergencyStop(); // 记录日志 logError(status.driveError, status.steerError); // 通知上层 notifyError(); } // 检查急停 if (status.isEmergencyStop) { cout << "[急停] 停止所有运动" << endl; cmd.driveSpeed = 0; cmd.steerAngle = 0; curtis.SendCommand(cmd); } ``` ### 3. 数值范围检查 **推荐做法:** ```cpp // 使用工具函数自动限制范围 float speedPercent = 120.0f; // 超出范围 int16_t canSpeed = CurtisMotorController::SpeedPercentToCAN(speedPercent); // 自动限制到 100% → 4095 // 或手动检查 cmd.driveSpeed = canSpeed; if (cmd.driveSpeed > CURTIS_DRIVE_SPEED_MAX) { cmd.driveSpeed = CURTIS_DRIVE_SPEED_MAX; } ``` ### 4. 状态回调 **推荐做法:** ```cpp curtis.SetStatusCallback([](const CurtisStatus& status) { // 状态更新时自动调用 if (status.batteryLevel < 20) { cout << "[警告] 电量低: " << (int)status.batteryLevel << "%" << endl; } }); ``` ### 5. 安全断开 **推荐做法:** ```cpp // 程序退出前 cout << "正在安全停止..." << endl; curtis.SendStopCommand(); Sleep(100); // 等待停止生效 curtis.Disconnect(); ``` ### 6. 异常处理 **推荐做法:** ```cpp try { CurtisMotorController curtis; if (!curtis.Connect()) { throw runtime_error("连接失败"); } // 主循环... } catch (const exception& e) { cerr << "异常: " << e.what() << endl; curtis.Disconnect(); return -1; } ``` ### 7. 日志记录 **推荐做法:** ```cpp void logStatus(const CurtisStatus& status) { ofstream log("curtis_log.txt", ios::app); log << GetTickCount() << "," << (int)status.driveError << "," << (int)status.steerError << "," << status.currentAngle << "," << status.motorSpeed << "," << (int)status.batteryLevel << endl; } // 定期调用 if (GetTickCount() - lastLogTime >= 1000) { logStatus(curtis.GetStatus()); lastLogTime = GetTickCount(); } ``` --- ## 附录 ### A. CAN ID 映射表 | CAN ID | 方向 | 描述 | 数据内容 | |--------|------|------|----------| | 0x1E5 | AGV → Curtis | 控制命令 | 行走速度、提升速度、控制位 | | 0x2E5 | AGV → Curtis | 转向命令 | 转向角度 | | 0x260 | Curtis → AGV | 状态反馈 | 故障、角度、转速、电量、状态 | | 0x360 | Curtis → AGV | 保留 | 保留字段 | ### B. 控制位定义 **控制位0(Byte0):** | Bit | 功能 | 0 | 1 | |-----|------|---|---| | 0 | 调速开关 | 有效 | 无效 | | 1 | 紧急反向 | 无效 | 有效 | | 2 | 龟速模式 | 无效 | 有效 | | 3 | 喇叭 | 无效 | 有效 | | 4 | 左提升 | 无效 | 有效 | | 5 | 左下降 | 无效 | 有效 | | 6 | 右提升 | 无效 | 有效 | | 7 | 右下降 | 无效 | 有效 | **控制位1(Byte1 - AGV专用):** | Bit | 功能 | 0 | 1 | |-----|------|---|---| | 0 | AGV标识 | 无效 | 连接(必须) | | 1 | 紧急停车 | 无效 | 急停 | | 2 | 找中完成 | - | 完成 | | 3-7 | 保留 | - | - | ### C. 数据范围表 | 参数 | 最小值 | 最大值 | 单位 | 说明 | |------|--------|--------|------|------| | 行走速度 | -4096 | 4095 | - | 负值=后退 | | 提升速度 | -4096 | 4095 | - | 负值=下降 | | 转向角度(命令) | -900 | 900 | 0.1° | -900=-90° | | 转向角度(反馈) | -16383 | 16383 | 0.1° | -16383=-90° | | 电量 | 0 | 100 | % | 百分比 | ### D. 相关文档 - **can-protocol.md** - USB-CAN 接口函数库参考 - **curtis-motor-protocol.md** - Curtis 协议详细说明 - **CAN_Protocol.pdf** - CAN 通讯协议基础 - **柯蒂斯通信协议.pdf** - 原始协议文档 --- ## 技术支持 如有问题,请参考: 1. `.claude/skills/curtis-motor-protocol.md` - 完整协议说明 2. `.claude/skills/can-protocol.md` - CAN 接口库文档 3. 示例程序源码 **厂商技术支持:** 上海诺力智能科技有限公司 --- **文档版本:** 1.0 **最后更新:** 2024-11-15 **作者:** AGV 路径跟踪项目组