# 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 路径跟踪项目组
