Files
agv-control-slam/docs/curtis/CURTIS_INTEGRATION_GUIDE.md
2025-11-15 16:05:59 +08:00

19 KiB
Raw Blame History

Curtis 电机控制器集成指南

概述

本指南介绍如何在 AGV 路径跟踪项目中集成柯蒂斯Curtis1298+1220C 电机控制器的 CAN 通讯功能。

协议版本: V4.0 波特率: 125 kbps 厂商: 上海诺力智能科技有限公司


目录

  1. 快速开始
  2. 文件结构
  3. 编译和运行
  4. API 参考
  5. 完整调用流程
  6. 示例程序说明
  7. 故障排查
  8. 最佳实践

快速开始

1. 最简示例5行代码

#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. 基本控制循环

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. 编译

cd build
cmake ..
make

编译目标:

  • curtis_demo - 键盘控制演示
  • curtis_path_tracking_demo - 路径跟踪演示

2. 运行示例

键盘控制演示:

./curtis_demo

路径跟踪演示:

./curtis_path_tracking_demo

3. 在您的项目中使用

在 CMakeLists.txt 中添加:

add_executable(your_program your_main.cpp
    src/can/CurtisMotorController.cpp
)

target_link_libraries(your_program
    ${CMAKE_SOURCE_DIR}/lib/ControlCAN.dll
)

在代码中包含头文件:

#include "include/can/CurtisMotorController.h"

API 参考

核心类CurtisMotorController

构造函数

CurtisMotorController(DWORD deviceType = VCI_USBCAN2,
                      DWORD deviceIndex = 0,
                      DWORD canChannel = 0);

参数:

  • deviceType: CAN 设备类型(默认 VCI_USBCAN2 = 4
  • deviceIndex: 设备索引(默认 0 = 第一个设备)
  • canChannel: CAN 通道(默认 0 = CAN1

连接管理

bool Connect();              // 连接到 Curtis 控制器
void Disconnect();           // 断开连接
bool IsConnected() const;    // 检查是否已连接

命令发送

bool SendCommand(const CurtisCommand& cmd);  // 发送控制命令
bool SendStopCommand();                      // 发送停止命令
void EmergencyStop();                        // 紧急停车

重要: 必须以 ≤20ms 周期持续发送命令,否则控制器会超时保护。

状态接收

bool ReceiveStatus();                   // 接收状态反馈
const CurtisStatus& GetStatus() const;  // 获取当前状态
void PrintStatus() const;               // 打印状态到控制台

故障检测

bool HasError() const;                         // 检查是否有故障
bool IsTimeout(uint32_t timeoutMs) const;      // 检查通讯超时
std::string GetErrorString() const;            // 获取故障描述

工具函数(静态)

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控制命令

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状态反馈

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创建对象

// 使用默认参数(设备类型=VCI_USBCAN2, 索引=0, 通道=0
CurtisMotorController curtis;

// 或指定参数
CurtisMotorController curtis(VCI_USBCAN2, 0, 0);

步骤2连接

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主循环

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断开连接

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 - 退出

运行:

cd build
./curtis_demo

核心代码:

// 主循环
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

功能:

  • 矩形路径跟踪
  • 圆形路径跟踪
  • 实时位置控制
  • 故障自动中止

运行:

cd build
./curtis_path_tracking_demo

核心代码:

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

诊断命令:

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

诊断代码:

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 总线连接不稳定
  • 控制器参数设置不正确

解决方案:

// 检查发送周期
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
    
  • 检查角度转换:
    float degrees = 30.0f;
    int16_t canValue = CurtisMotorController::AngleDegreeToCAN(degrees);
    cout << "30° → CAN值 = " << canValue << endl;  // 应为 300
    

5. 收到故障代码

症状: HasError() 返回 true

诊断:

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. 命令发送频率

推荐做法:

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. 故障处理

推荐做法:

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. 数值范围检查

推荐做法:

// 使用工具函数自动限制范围
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. 状态回调

推荐做法:

curtis.SetStatusCallback([](const CurtisStatus& status) {
    // 状态更新时自动调用
    if (status.batteryLevel < 20) {
        cout << "[警告] 电量低: " << (int)status.batteryLevel << "%" << endl;
    }
});

5. 安全断开

推荐做法:

// 程序退出前
cout << "正在安全停止..." << endl;
curtis.SendStopCommand();
Sleep(100);  // 等待停止生效
curtis.Disconnect();

6. 异常处理

推荐做法:

try {
    CurtisMotorController curtis;
    if (!curtis.Connect()) {
        throw runtime_error("连接失败");
    }

    // 主循环...

} catch (const exception& e) {
    cerr << "异常: " << e.what() << endl;
    curtis.Disconnect();
    return -1;
}

7. 日志记录

推荐做法:

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. 控制位定义

控制位0Byte0

Bit 功能 0 1
0 调速开关 有效 无效
1 紧急反向 无效 有效
2 龟速模式 无效 有效
3 喇叭 无效 有效
4 左提升 无效 有效
5 左下降 无效 有效
6 右提升 无效 有效
7 右下降 无效 有效

控制位1Byte1 - 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 路径跟踪项目组