Files
H100PowerManger/src/pMotor/protocol/MotorCan.h
T
zjk 873a1a9b09
build-test-deploy / ci (push) Failing after 32m37s
新增 pMotor 推进电机操作程序:经 pCanBridge(CAN1) 与电机控制器交互
- 协议模块(protocol/MotorCan):12 条 CAN ID、指令帧 0x18EF2010 编码、
  状态帧(转速/母线电压/温度)与支路一/二故障报警帧解码,故障位按协议
  逐位映射中文描述(docs/推进电机通信协议20260321.docx)
- 主程序(MOOS 应用):订阅 pCanBridge 透传 CAN_0x* 解码电机状态;
  500ms 周期下发指令帧(CAN_TX_0x18EF2010,m_sSrcAux=CAN1),
  可选 red_channel 冗余下发 0x18EF2011;复位位保持 reset_hold_sec
- 网页(18081):按协议指令帧字段独立下发(使能命令 Byte0 bit0 /
  复位命令 Byte0 bit1 / 期望转速 Byte2/3,无联动按钮);
  WebSocket 1Hz 推送转速/电压/温度/故障告警/链路统计
- 配置接入:missions/h100.moos 增加 pMotor 配置块(can_channel=CAN1)
- 测试:pmotorTest 92 项(指令帧字节级对照协议示例、解码/故障表)
- 部署:scripts/build-board.sh 增加 pMotor 产物拷贝 + pMotor.service;
  .gitignore 补 bin/pMotor、bin/pmotorTest
- 联调验证:MOOSDB+pCanBridge+假CANET 全链路,下行帧 01 00 E8 03
  与协议文档示例一致,复位位 3s 保持行为正确
2026-09-02 10:50:28 +08:00

155 lines
7.2 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#ifndef PMOTOR_CAN_H
#define PMOTOR_CAN_H
#include <cstdint>
#include <string>
#include <vector>
namespace motor {
//============================================================================
// 推进电机控制器 CAN 通信协议(docs/推进电机通信协议20260321.docx)
//
// 数据来源:pCanBridge 透传的 MOOS 消息
// 变量名 CAN_0x%08X = CAN ID(29 位扩展帧),m_sVal = 二进制数据域(8 字节),
// m_dfVal2 = 原始帧信息字节。本模块只负责按报文 ID 解析/编码数据域字段。
//
// 物理/链路层:CAN2.0B 扩展帧,250kbps(默认),数据域 8 字节,
// 多字节数值采用 Intel 编码(低字节在前,高字节在后)。
//
// 【报文清单】
// 下行(pMotor -> 电机控制器,周期 500ms):
// 0x18EF2010 主控指令帧(冗余 CAN 0x18EF2011)
// 0x18EF2110 主控限制帧(冗余 CAN 0x18EF2111,全预留,暂不发送)
// 上行(电机控制器 -> pMotor):
// 0x18EF1020 支路一故障报警状态帧1(200ms,冗余 CAN 0x18EF1021)
// 0x18EF1120 支路二故障报警状态帧2(200ms,冗余 CAN 0x18EF1121)
// 0x18EF1220 状态帧3:反馈转速/母线电压(100ms,冗余 0x18EF1221)
// 0x18EF1320 状态帧4:温度(100ms,冗余 0x18EF1321)
//
// 【指令帧 0x18EF2010】数据域 8 字节:
// byte0 bit0 = 使能命令(0 无效 1 有效,IGBT 开关;需先预充后使能)
// byte0 bit1 = 复位命令(0 无效 1 有效,仅故障状态下有效,故障支路复位)
// byte0 bit2 = 支路一通信状态(0 正常 1 中断)
// byte0 bit3 = 支路二通信状态(0 正常 1 中断)
// byte0 bit4~7 / byte1 / byte4~7 = 预留(发 0)
// byte2/3 = (转速模式)期望转速 s16(偏移 0,分辨率 1,Intel)
// 文档示例:启动并给定 1000 转速 -> 01 00 E8 03 00 00 00 00
// 停机 -> 00 00 00 00 00 00 00 00
// 复位 -> 02 00 00 00 00 00 00 00
//
// 【状态帧3 0x18EF1220】byte0/1 反馈转速 s16(Intel),byte2/3 母线电压 u16(Intel)
//
// 【状态帧4 0x18EF1320】byte0~5 依次为 A/B/C 相绕组、逆变组件底板、
// 输出滤波器、筒内空气温度,均为 s8(偏移 0,分辨率 1)
//
// 【故障帧 0x18EF1020/0x18EF1120】4 个 16 位字(byte0|1、byte2|3、byte4|5、byte6|7),
// 字内 bit 定义见 MotorCan.cpp 中描述表;前两帧仅支路一/支路二
// byte0|1 的 bit7 不同(支路一=筒内空气过温故障,支路二=预留)。
//============================================================================
// CAN ID 定义(29 位扩展帧)
enum MotorCanId : uint32_t {
MOTOR_CAN_CMD = 0x18EF2010u, // 主控指令帧(pMotor -> 电机)
MOTOR_CAN_CMD_RED = 0x18EF2011u, // 主控指令帧 冗余 CAN
MOTOR_CAN_LIMIT = 0x18EF2110u, // 主控限制帧(全预留,暂不使用)
MOTOR_CAN_LIMIT_RED = 0x18EF2111u, // 主控限制帧 冗余 CAN
MOTOR_CAN_FAULT1 = 0x18EF1020u, // 支路一故障报警状态帧1
MOTOR_CAN_FAULT1_RED = 0x18EF1021u, // 支路一 冗余 CAN
MOTOR_CAN_FAULT2 = 0x18EF1120u, // 支路二故障报警状态帧2
MOTOR_CAN_FAULT2_RED = 0x18EF1121u, // 支路二 冗余 CAN
MOTOR_CAN_STATE3 = 0x18EF1220u, // 状态帧3(转速/母线电压)
MOTOR_CAN_STATE3_RED = 0x18EF1221u, // 状态帧3 冗余 CAN
MOTOR_CAN_STATE4 = 0x18EF1320u, // 状态帧4(温度)
MOTOR_CAN_STATE4_RED = 0x18EF1321u, // 状态帧4 冗余 CAN
};
// 指令动作(网页 /api/motor_cmd -> 队列 -> Iterate 应用)
// 与协议指令帧字段一一对应,独立下发、无联动:
enum MotorAction {
MOTOR_ACT_ENABLE = 1, // 使能命令=1(Byte0 bit0,保持当前期望转速)
MOTOR_ACT_DISABLE = 2, // 使能命令=0(Byte0 bit0,协议"停机"示例:期望转速清 0)
MOTOR_ACT_SPEED = 3, // 期望转速(Byte2/3,speed 参数,使能状态保持不变)
MOTOR_ACT_RESET = 4, // 复位命令=1(Byte0 bit1,保持高 resetHold 秒,仅故障态有效)
};
// 网页下发的控制指令
struct MotorCmd {
int action = 0; // MotorAction
int16_t speed = 0; // MOTOR_ACT_SPEED / MOTOR_ACT_START 有效
};
// 单条电机上行消息的新鲜度 + 原始数据
struct MotorBranchState {
int branch = 0; // 1 / 2
uint16_t word[4] = {0, 0, 0, 0}; // 4 个 16 位字(故障1/故障2/故障3/报警)
std::vector<std::string> faults; // 当前置位的故障描述(word0~2)
std::vector<std::string> alarms; // 当前置位的报警描述(word3)
bool valid = false; // 是否收到过该支路故障帧
double lastRxTime = 0; // 最近收到时刻(MOOSTime,调用方设置)
};
// 电机最新合成状态(各帧部分更新合并)
struct MotorState {
// 状态帧3 0x18EF1220
int16_t feedbackSpeed = 0; // 反馈转速
uint16_t busVoltage = 0; // 母线电压
bool state3Valid = false;
double state3LastRx = 0;
// 状态帧4 0x18EF1320(s8,偏移 0)
int8_t tempA = 0, tempB = 0, tempC = 0; // A/B/C 相绕组温度
int8_t tempInv = 0, tempFilter = 0, tempAir = 0; // 逆变底板/滤波器/筒内空气
bool state4Valid = false;
double state4LastRx = 0;
// 故障/报警
MotorBranchState branch1;
MotorBranchState branch2;
// 计数
uint32_t rxCount = 0; // 命中电机报文的帧计数
};
// 是否为电机协议定义的 CAN ID(含冗余 CAN 变体)
inline bool isCanMotorId(uint32_t canId) {
switch (canId) {
case MOTOR_CAN_CMD:
case MOTOR_CAN_CMD_RED:
case MOTOR_CAN_LIMIT:
case MOTOR_CAN_LIMIT_RED:
case MOTOR_CAN_FAULT1:
case MOTOR_CAN_FAULT1_RED:
case MOTOR_CAN_FAULT2:
case MOTOR_CAN_FAULT2_RED:
case MOTOR_CAN_STATE3:
case MOTOR_CAN_STATE3_RED:
case MOTOR_CAN_STATE4:
case MOTOR_CAN_STATE4_RED:
return true;
default:
return false;
}
}
// 故障/报警字索引(帧内):0=byte0|1 1=byte2|3 2=byte4|5 3=byte6|7(报警)
extern const char* kMotorWordName[4];
// 按支路(1/2)与字索引查故障/报警描述表,把 bits 中置位的描述填入 out;
// 返回描述条数。wordIdx 非法或无置位返回 0。
int motorFaultTexts(int branch, int wordIdx, uint16_t bits,
std::vector<std::string>& out);
// 编码指令帧数据域(8 字节,经 MOOS CAN_TX_0x%08X 下发 pCanBridge 发送):
// [0] = enable(bit0) | reset(bit1)(支路通信状态位恒 0)
// [1] = 0 [2]/[3] = speed s16 Intel(停用时调用方传 0) [4..7] = 0
void buildMotorCmdData(bool enable, bool reset, int16_t speed, uint8_t out[8]);
// 解析一帧 CAN 数据域;命中电机上行报文返回 true 并部分更新 st:
// 故障帧 -> 对应支路 word[4] + 故障/报警描述
// 状态帧3 -> feedbackSpeed / busVoltage
// 状态帧4 -> 6 路温度
// 非电机报文或 data/dlc 非法返回 false。lastRxTime 由调用方设置。
bool decodeMotorFrame(uint32_t canId, const uint8_t* data, int dlc, MotorState& st);
} // namespace motor
#endif // PMOTOR_CAN_H