From d81d8a8d7d6809dbdbd2b3a39f57ad411da08edf Mon Sep 17 00:00:00 2001 From: ZhangChuqing Date: Mon, 15 Dec 2025 21:51:04 +0800 Subject: [PATCH 1/3] =?UTF-8?q?=E8=A3=81=E5=88=A4=E7=B3=BB=E7=BB=9F?= =?UTF-8?q?=E9=80=9A=E4=BF=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- GSRL/Device/inc/dvc_referee.hpp | 299 ++++++++++++++++++++++++++ GSRL/Device/src/dvc_referee.cpp | 369 ++++++++++++++++++++++++++++++++ REFEREE_DEV_DOC.md | 89 ++++++++ 3 files changed, 757 insertions(+) create mode 100644 GSRL/Device/inc/dvc_referee.hpp create mode 100644 GSRL/Device/src/dvc_referee.cpp create mode 100644 REFEREE_DEV_DOC.md diff --git a/GSRL/Device/inc/dvc_referee.hpp b/GSRL/Device/inc/dvc_referee.hpp new file mode 100644 index 0000000..04a03ed --- /dev/null +++ b/GSRL/Device/inc/dvc_referee.hpp @@ -0,0 +1,299 @@ +/** + ****************************************************************************** + * @file : dvc_referee.hpp + * @brief : 裁判系统解析头文件 + ****************************************************************************** + * @attention + * + * Copyright (c) 2025 GMaster + * All rights reserved. + * + ****************************************************************************** + */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __DVC_REFEREE_HPP +#define __DVC_REFEREE_HPP + +/* Includes ------------------------------------------------------------------*/ +#include +#include + +/* Exported types ------------------------------------------------------------*/ +// ================= 协议结构体定义 ================= + +#pragma pack(push, 1) + +// 0x0001 比赛状态 +struct GameStatus +{ + uint8_t game_type : 4; + uint8_t game_progress : 4; + uint16_t stage_remain_time; + uint64_t sync_timestamp; +}; + +// 0x0002 比赛结果 +struct GameResult +{ + uint8_t winner; +}; + +// 0x0003 比赛机器人血量数据 +struct GameRobotHP +{ + uint16_t red_1_robot_HP; + uint16_t red_2_robot_HP; + uint16_t red_3_robot_HP; + uint16_t red_4_robot_HP; + uint16_t red_5_robot_HP; + uint16_t red_7_robot_HP; + uint16_t red_outpost_HP; + uint16_t red_base_HP; + uint16_t blue_1_robot_HP; + uint16_t blue_2_robot_HP; + uint16_t blue_3_robot_HP; + uint16_t blue_4_robot_HP; + uint16_t blue_5_robot_HP; + uint16_t blue_7_robot_HP; + uint16_t blue_outpost_HP; + uint16_t blue_base_HP; +}; + +// 0x0201 机器人性能体系数据 +struct RobotStatus +{ + uint8_t robot_id; + uint8_t robot_level; + uint16_t current_HP; + uint16_t maximum_HP; + uint16_t shooter_barrel_cooling_value; + uint16_t shooter_barrel_heat_limit; + uint16_t chassis_power_limit; + uint8_t power_management_gimbal_output : 1; + uint8_t power_management_chassis_output : 1; + uint8_t power_management_shooter_output : 1; +}; + +// 0x0202 能量 & 热量数据 +struct PowerHeatData +{ + uint16_t chassis_volt; + uint16_t chassis_current; + float chassis_power; + uint16_t chassis_power_buffer; + uint16_t shooter_17mm_1_barrel_heat; + uint16_t shooter_17mm_2_barrel_heat; + uint16_t shooter_42mm_barrel_heat; +}; + +// 0x0203 机器人位置 +struct RobotPos +{ + float x; + float y; + float angle; // 度,正北 = 0 +}; + +// 0x0204 增益 & 能量反馈 +struct Buff +{ + uint8_t recovery_buff; + uint16_t cooling_buff; + uint8_t defence_buff; + uint8_t vulnerability_buff; + uint16_t attack_buff; + uint8_t remaining_energy; +}; + +// 0x0206 受伤信息 +struct HurtData +{ + uint8_t armor_id : 4; + uint8_t HP_deduction_reason : 4; +}; + +// 0x0207 射击信息 +struct ShootData +{ + uint8_t bullet_type; + uint8_t shooter_number; + uint8_t launching_frequency; + float initial_speed; +}; + +// 0x0208 允许发弹量 +struct ProjectileAllowance +{ + uint16_t projectile_allowance_17mm; + uint16_t projectile_allowance_42mm; + uint16_t remaining_gold_coin; + uint16_t projectile_allowance_fortress; +}; + +// 0x0209 RFID 状态 +struct RfidStatus +{ + uint32_t rfid_status; + uint8_t rfid_status_2; +}; + +// 0x0303 小地图点击 +struct MapCommand +{ + float target_position_x; + float target_position_y; + uint8_t cmd_keyboard; + uint8_t target_robot_id; + uint16_t cmd_source; +}; + +// 0x0305 雷达 → 小地图机器人坐标 +struct MapRobotData +{ + uint16_t hero_position_x; + uint16_t hero_position_y; + uint16_t engineer_position_x; + uint16_t engineer_position_y; + uint16_t infantry_3_position_x; + uint16_t infantry_3_position_y; + uint16_t infantry_4_position_x; + uint16_t infantry_4_position_y; + uint16_t infantry_5_position_x; + uint16_t infantry_5_position_y; + uint16_t sentry_position_x; + uint16_t sentry_position_y; +}; + +// 0x0307 路径数据 +struct MapPathData +{ + uint8_t intention; + uint16_t start_position_x; + uint16_t start_position_y; + int8_t delta_x[49]; + int8_t delta_y[49]; + uint16_t sender_id; +}; + +// 0x0308 自定义文字 +struct CustomInfo +{ + uint16_t sender_id; + uint16_t receiver_id; + uint8_t user_data[30]; // UTF-16 原始字节 +}; + +// 0x0304 键鼠数据(图传链路) +struct RemoteControl +{ + int16_t mouse_x; + int16_t mouse_y; + int16_t mouse_z; + int8_t left_button_down; + int8_t right_button_down; + uint16_t keyboard_value; + uint16_t reserved; +}; + +#pragma pack(pop) + +// ================= 全部信息聚合 ================= + +struct RefereeInfo +{ + // 比赛 & 队伍 + GameStatus gameStatus{}; + GameResult gameResult{}; + GameRobotHP gameRobotHP{}; + + // 本机 + RobotStatus robotStatus{}; + PowerHeatData powerHeat{}; + RobotPos robotPos{}; + Buff buff{}; + HurtData hurt{}; + ShootData shoot{}; + ProjectileAllowance projectileAllowance{}; + RfidStatus rfidStatus{}; + + // 小地图 / 文本 + MapCommand mapCommand{}; + MapRobotData mapRobotData{}; + MapPathData mapPathData{}; + CustomInfo customInfo{}; + + // 图传键鼠 + RemoteControl remoteControl{}; + + // 简单状态标志 + bool hasGameStatus = false; + bool hasRobotStatus = false; + bool hasPowerHeat = false; + bool hasRobotPos = false; + bool hasBuff = false; + bool hasRemoteControl = false; +}; + +// 链路类型:常规链路 or 图传链路 +enum class RefereeLink : uint8_t +{ + Main, // 常规链路(电源管理 User 串口) + Vision // 图传链路(图传 UART) +}; + +// ================= 主解析类 ================= + +class RefereeParser +{ +public: + explicit RefereeParser(RefereeLink link); + + // 串口每收到一个字节,就调用一次 + void onByteReceived(uint8_t byte); + + // 获取解析结果 + const RefereeInfo& getInfo() const { return info_; } + + // 可选:重置状态机 + void reset(); + + // CRC + uint8_t calcCRC8(const uint8_t* msg, uint32_t len, uint8_t crc) const; + uint16_t calcCRC16(const uint8_t* msg, uint32_t len, uint16_t crc) const; + +private: + // 状态机阶段 + enum class Step : uint8_t + { + WaitSOF = 0, + ReadHeader, + ReadBody + }; + + struct Context + { + Step step = Step::WaitSOF; + uint16_t dataLen = 0; + uint16_t index = 0; + uint16_t frameLen = 0; + uint8_t buffer[256] = {0}; + }; + + void parseByte(uint8_t byte); + bool checkHeaderCRC() const; + bool checkFrameCRC() const; + void handleCompleteFrame(); + + void dispatchCmd(uint16_t cmd_id, const uint8_t* data, uint16_t len); + + bool verifyCRC8(const uint8_t* msg, uint32_t len) const; + bool verifyCRC16(const uint8_t* msg, uint32_t len) const; + +private: + RefereeLink link_; + Context ctx_; + RefereeInfo info_; +}; + +#endif /* __DVC_REFEREE_HPP */ diff --git a/GSRL/Device/src/dvc_referee.cpp b/GSRL/Device/src/dvc_referee.cpp new file mode 100644 index 0000000..7fd0afa --- /dev/null +++ b/GSRL/Device/src/dvc_referee.cpp @@ -0,0 +1,369 @@ +/** + ****************************************************************************** + * @file : dvc_referee.cpp + * @brief : 裁判系统解析源文件 + ****************************************************************************** + * @attention + * + * Copyright (c) 2025 GMaster + * All rights reserved. + * + ****************************************************************************** + */ +/* Includes ------------------------------------------------------------------*/ +#include "dvc_referee.hpp" +#include +/* Typedef -------------------------------------------------------------------*/ + +/* Define --------------------------------------------------------------------*/ +namespace +{ + constexpr uint8_t CRC8_INIT = 0xff; + constexpr uint16_t CRC16_INIT = 0x0000; + + const uint8_t CRC8_TAB[256] = { + 0x00, 0x5e, 0xbc, 0xe2, 0x61, 0x3f, 0xdd, 0x83, 0xc2, 0x9c, 0x7e, 0x20, 0xa3, 0xfd, 0x1f, 0x41, + 0x9d, 0xc3, 0x21, 0x7f, 0xfc, 0xa2, 0x40, 0x1e, 0x5f, 0x01, 0xe3, 0xbd, 0x3e, 0x60, 0x82, 0xdc, + 0x23, 0x7d, 0x9f, 0xc1, 0x42, 0x1c, 0xfe, 0xa0, 0xe1, 0xbf, 0x5d, 0x03, 0x80, 0xde, 0x3c, 0x62, + 0xbe, 0xe0, 0x02, 0x5c, 0xdf, 0x81, 0x63, 0x3d, 0x7c, 0x22, 0xc0, 0x9e, 0x1d, 0x43, 0xa1, 0xff, + 0x46, 0x18, 0xfa, 0xa4, 0x27, 0x79, 0x9b, 0xc5, 0x84, 0xda, 0x38, 0x66, 0xe5, 0xbb, 0x59, 0x07, + 0xdb, 0x85, 0x67, 0x39, 0xba, 0xe4, 0x06, 0x58, 0x19, 0x47, 0xa5, 0xfb, 0x78, 0x26, 0xc4, 0x9a, + 0x65, 0x3b, 0xd9, 0x87, 0x04, 0x5a, 0xb8, 0xe6, 0xa7, 0xf9, 0x1b, 0x45, 0xc6, 0x98, 0x7a, 0x24, + 0xf8, 0xa6, 0x44, 0x1a, 0x99, 0xc7, 0x25, 0x7b, 0x3a, 0x64, 0x86, 0xd8, 0x5b, 0x05, 0xe7, 0xb9, + 0x8c, 0xd2, 0x30, 0x6e, 0xed, 0xb3, 0x51, 0x0f, 0x4e, 0x10, 0xf2, 0xac, 0x2f, 0x71, 0x93, 0xcd, + 0x11, 0x4f, 0xad, 0xf3, 0x70, 0x2e, 0xcc, 0x92, 0xd3, 0x8d, 0x6f, 0x31, 0xb2, 0xec, 0x0e, 0x50, + 0xaf, 0xf1, 0x13, 0x4d, 0xce, 0x90, 0x72, 0x2c, 0x6d, 0x33, 0xd1, 0x8f, 0x0c, 0x52, 0xb0, 0xee, + 0x32, 0x6c, 0x8e, 0xd0, 0x53, 0x0d, 0xef, 0xb1, 0xf0, 0xae, 0x4c, 0x12, 0x91, 0xcf, 0x2d, 0x73, + 0xca, 0x94, 0x76, 0x28, 0xab, 0xf5, 0x17, 0x49, 0x08, 0x56, 0xb4, 0xea, 0x69, 0x37, 0xd5, 0x8b, + 0x57, 0x09, 0xeb, 0xb5, 0x36, 0x68, 0x8a, 0xd4, 0x95, 0xcb, 0x29, 0x77, 0xf4, 0xaa, 0x48, 0x16, + 0xe9, 0xb7, 0x55, 0x0b, 0x88, 0xd6, 0x34, 0x6a, 0x2b, 0x75, 0x97, 0xc9, 0x4a, 0x14, 0xf6, 0xa8, + 0x74, 0x2a, 0xc8, 0x96, 0x15, 0x4b, 0xa9, 0xf7, 0xb6, 0xe8, 0x0a, 0x54, 0xd7, 0x89, 0x6b, 0x35, + }; + + const uint16_t CRC16_TAB[256] = { + 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf, + 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7, + 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e, + 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876, + 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd, + 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5, + 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c, + 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974, + 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb, + 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3, + 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a, + 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72, + 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9, + 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1, + 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738, + 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70, + 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7, + 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff, + 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036, + 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e, + 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5, + 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd, + 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134, + 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c, + 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3, + 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb, + 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232, + 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a, + 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1, + 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9, + 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330, + 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78 + }; +} + +// ================= RefereeParser 实例 ================= +RefereeParser::RefereeParser(RefereeLink link) + : link_(link) +{ + reset(); +} + +void RefereeParser::reset() +{ + ctx_.step = Step::WaitSOF; + ctx_.index = 0; + ctx_.dataLen = 0; + ctx_.frameLen = 0; + // info_ 可以保留历史值,不一定清空 +} + +void RefereeParser::onByteReceived(uint8_t byte) +{ + parseByte(byte); +} + +void RefereeParser::parseByte(uint8_t byte) +{ + auto& c = ctx_; + + switch (c.step) + { + case Step::WaitSOF: + if (byte == 0xA5) + { + c.buffer[0] = byte; + c.index = 1; + c.step = Step::ReadHeader; + } + break; + + case Step::ReadHeader: + c.buffer[c.index++] = byte; + if (c.index >= 5) // SOF + data_length(2) + seq + CRC8 + { + if (!checkHeaderCRC()) + { + // CRC8 错,丢包 + c.step = Step::WaitSOF; + c.index = 0; + break; + } + + // data_length 小端 + uint16_t len = static_cast(c.buffer[1]) | + (static_cast(c.buffer[2]) << 8); + + c.dataLen = len; + c.frameLen = 5 + 2 + c.dataLen + 2; // header + cmd_id + data + CRC16 + + if (c.frameLen > sizeof(c.buffer) || c.frameLen < 9) + { + // 长度异常 + c.step = Step::WaitSOF; + c.index = 0; + break; + } + + c.step = Step::ReadBody; + } + break; + + case Step::ReadBody: + c.buffer[c.index++] = byte; + if (c.index >= c.frameLen) + { + // 收到完整一帧 + if (checkFrameCRC()) + { + handleCompleteFrame(); + } + // 不管对错都回到等待 + c.step = Step::WaitSOF; + c.index = 0; + } + break; + } +} + +bool RefereeParser::checkHeaderCRC() const +{ + // 前 5 字节是 frame_header + return verifyCRC8(ctx_.buffer, 5); +} + +bool RefereeParser::checkFrameCRC() const +{ + return verifyCRC16(ctx_.buffer, ctx_.frameLen); +} + +void RefereeParser::handleCompleteFrame() +{ + const auto& c = ctx_; + + // cmd_id 在 [5,6] + uint16_t cmd_id = static_cast(c.buffer[5]) | + (static_cast(c.buffer[6]) << 8); + + const uint8_t* data = &c.buffer[7]; + uint16_t len = c.dataLen; + + dispatchCmd(cmd_id, data, len); +} + +// ================= CRC 实现 ================= + +uint8_t RefereeParser::calcCRC8(const uint8_t* msg, uint32_t len, uint8_t crc) const +{ + while (len--) + { + uint8_t index = crc ^ (*msg++); + crc = CRC8_TAB[index]; + } + return crc; +} + +bool RefereeParser::verifyCRC8(const uint8_t* msg, uint32_t len) const +{ + if (!msg || len < 2) return false; + uint8_t expected = calcCRC8(msg, len - 1, CRC8_INIT); + return (expected == msg[len - 1]); +} + +uint16_t RefereeParser::calcCRC16(const uint8_t* msg, uint32_t len, uint16_t crc) const +{ + if (!msg) return 0xFFFF; + while (len--) + { + uint8_t ch = *msg++; + uint8_t idx = (crc ^ ch) & 0xff; + crc = (crc >> 8) ^ CRC16_TAB[idx]; + } + return crc; +} + +bool RefereeParser::verifyCRC16(const uint8_t* msg, uint32_t len) const +{ + if (!msg || len <= 2) return false; + uint16_t expected = calcCRC16(msg, len - 2, CRC16_INIT); + return ((expected & 0xff) == msg[len - 2] && + ((expected >> 8) & 0xff) == msg[len - 1]); +} + +// ================= cmd_id 分发 ================= + +void RefereeParser::dispatchCmd(uint16_t cmd_id, const uint8_t* data, uint16_t len) +{ + // 小端平台,协议也是小端,直接 memcpy 即可 + switch (cmd_id) + { + // -------- 全局类 -------- + case 0x0001: // 比赛状态 + if (len == sizeof(GameStatus)) + { + std::memcpy(&info_.gameStatus, data, sizeof(GameStatus)); + info_.hasGameStatus = true; + } + break; + + case 0x0002: // 比赛结果 + if (len == sizeof(GameResult)) + { + std::memcpy(&info_.gameResult, data, sizeof(GameResult)); + } + break; + + case 0x0003: // 己方血量 + if (len == sizeof(GameRobotHP)) + { + std::memcpy(&info_.gameRobotHP, data, sizeof(GameRobotHP)); + } + break; + + // -------- 本机器人 -------- + case 0x0201: // 状态 + if (len == sizeof(RobotStatus)) + { + std::memcpy(&info_.robotStatus, data, sizeof(RobotStatus)); + info_.hasRobotStatus = true; + } + break; + + case 0x0202: // 能量 & 热量 + if (len == sizeof(PowerHeatData)) + { + std::memcpy(&info_.powerHeat, data, sizeof(PowerHeatData)); + info_.hasPowerHeat = true; + } + break; + + case 0x0203: // 位置 + if (len == sizeof(RobotPos)) + { + std::memcpy(&info_.robotPos, data, sizeof(RobotPos)); + info_.hasRobotPos = true; + } + break; + + case 0x0204: // Buff + if (len == sizeof(Buff)) + { + std::memcpy(&info_.buff, data, sizeof(Buff)); + info_.hasBuff = true; + } + break; + + case 0x0206: // 受伤 + if (len == sizeof(HurtData)) + { + std::memcpy(&info_.hurt, data, sizeof(HurtData)); + } + break; + + case 0x0207: // 射击 + if (len == sizeof(ShootData)) + { + std::memcpy(&info_.shoot, data, sizeof(ShootData)); + } + break; + + case 0x0208: // 发弹量 + if (len == sizeof(ProjectileAllowance)) + { + std::memcpy(&info_.projectileAllowance, data, sizeof(ProjectileAllowance)); + } + break; + + case 0x0209: // RFID + if (len == sizeof(RfidStatus)) + { + std::memcpy(&info_.rfidStatus, data, sizeof(RfidStatus)); + } + break; + + // -------- 小地图 / 文本 -------- + case 0x0303: // 小地图点击 + if (len == sizeof(MapCommand)) + { + std::memcpy(&info_.mapCommand, data, sizeof(MapCommand)); + } + break; + + case 0x0305: // 雷达发的敌方坐标 + if (len == sizeof(MapRobotData)) + { + std::memcpy(&info_.mapRobotData, data, sizeof(MapRobotData)); + } + break; + + case 0x0307: // 路径规划 + if (len == sizeof(MapPathData)) + { + std::memcpy(&info_.mapPathData, data, sizeof(MapPathData)); + } + break; + + case 0x0308: // 自定义文字 + if (len == sizeof(CustomInfo)) + { + std::memcpy(&info_.customInfo, data, sizeof(CustomInfo)); + } + break; + + // -------- 图传键鼠 -------- + case 0x0304: // 键鼠 + if (link_ == RefereeLink::Vision && + len == sizeof(RemoteControl)) + { + std::memcpy(&info_.remoteControl, data, sizeof(RemoteControl)); + info_.hasRemoteControl = true; + } + break; + + default: + // 其它 cmd_id 暂不处理,需要时再加 + break; + } +} + +/* Macro ---------------------------------------------------------------------*/ + +/* Variables -----------------------------------------------------------------*/ + +/* Function prototypes -------------------------------------------------------*/ + +/* User code -----------------------------------------------------------------*/ diff --git a/REFEREE_DEV_DOC.md b/REFEREE_DEV_DOC.md new file mode 100644 index 0000000..0bdc16a --- /dev/null +++ b/REFEREE_DEV_DOC.md @@ -0,0 +1,89 @@ +# 裁判系统驱动开发文档 (Referee System Driver Docs) + +## 1. 概述 +本驱动用于解析 RoboMaster 裁判系统串口协议(V1.6.1),支持数据接收、校验、解包及数据结构化存储。 + +* **适用硬件**: STM32F407 (或其他支持 UART 的 MCU) +* **通信接口**: USART6 (RX: PG9, TX: PG14) +* **波特率**: 115200, 8N1 +* **接收方式**: DMA + 空闲中断 (Idle Line Detection) + +## 2. 协议说明 +遵循 RoboMaster 串口协议 V1.6.1 标准。 + +### 2.1 数据帧结构 +| SOF (1B) | DataLength (2B) | Seq (1B) | CRC8 (1B) | CmdID (2B) | Data (nB) | CRC16 (2B) | +| :--- | :--- | :--- | :--- | :--- | :--- | :--- | +| 0xA5 | 数据段长度 | 包序号 | 帧头校验 | 命令ID | 数据内容 | 整帧校验 | + +### 2.2 关键配置 +* **CRC8 初始值**: `0xFF` +* **CRC16 初始值**: `0x0000` (注意:非 0xFFFF,2024年这版大疆给的经测试发现不对) +* **多字节序**: Little Endian (小端模式) + +## 3. 接口使用 + +### 3.1 初始化 +在任务初始化阶段调用: +```cpp +// 缓冲区建议 >= 128 字节,防止长包截断 +UART_Init(&huart6, refereeITCallback, 128); +``` + +### 3.2 中断回调 +在串口中断回调函数中将数据喂给解析器: +```cpp +// 定义全局解析器实例 +RefereeParser referee(RefereeLink::Main); + +extern "C" void refereeITCallback(uint8_t *Buffer, uint16_t Length) +{ + for (uint16_t i = 0; i < Length; i++) + { + referee.onByteReceived(Buffer[i]); + } +} +``` + +### 3.3 数据获取 +解析后的数据存储在 `RefereeInfo` 结构体中,可直接访问: + +```cpp +const RefereeInfo& info = referee.getInfo(); + +// 1. 检查数据是否更新 +if (info.hasRobotStatus) { + // 2. 读取具体字段 + uint8_t my_id = info.robotStatus.robot_id; + uint16_t hp = info.robotStatus.current_HP; + float power = info.powerHeat.chassis_power; +} + +// 3. 比赛状态(注意:单机调试时可能没有此包) +if (info.hasGameStatus) { + int stage = info.gameStatus.game_progress; +} +``` + +## 4. 支持的数据包列表 + +| Cmd ID | 描述 | 对应结构体 | 备注 | +| :--- | :--- | :--- | :--- | +| **0x0001** | 比赛状态 | `GameStatus` | 包含倒计时、比赛阶段 | +| **0x0002** | 比赛结果 | `GameResult` | 胜负判定 | +| **0x0003** | 机器人血量 | `GameRobotHP` | 红蓝双方所有单位血量 | +| **0x0201** | 机器人状态 | `RobotStatus` | ID、血量、功率限制、热量限制 | +| **0x0202** | 功率热量 | `PowerHeatData` | 实时功率、枪口热量 | +| **0x0203** | 机器人位置 | `RobotPos` | x, y, angle | +| **0x0204** | 增益状态 | `Buff` | 补给站Buff、防御Buff | +| **0x0206** | 伤害数据 | `HurtData` | 装甲板ID、伤害类型 | +| **0x0207** | 射击数据 | `ShootData` | 射速、弹丸初速 | +| **0x0208** | 弹丸余量 | `ProjectileAllowance` | 剩余发弹量 | +| **0x0303** | 小地图命令 | `MapCommand` | 选手端点击坐标 | +| **0x0304** | 键鼠数据 | `RemoteControl` | **仅图传链路有效** | + +## 5. 常见问题排查 + +1. **完全无数据**: 检查 `board_config.h` 是否定义 `USE_USART6`,检查 RX/TX 线序。 +2. **校验错误 (CRC Fail)**: 确认 `dvc_referee.cpp` 中 `CRC16_INIT` 为 `0x0000`。 +3. **长度错误 (Len Mismatch)**: 确认 `dvc_referee.hpp` 中的结构体定义是否与 V1.6.1 协议一致。 From 29b7f0fd036a89fa0faec5f03ec61130973c5351 Mon Sep 17 00:00:00 2001 From: ZhangChuqing Date: Tue, 6 Jan 2026 15:37:12 +0800 Subject: [PATCH 2/3] =?UTF-8?q?=E5=8D=8F=E8=AE=AE=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- GSRL/Device/inc/dvc_referee.hpp | 60 ++++++++++++++++++++++++++++++++- GSRL/Device/src/dvc_referee.cpp | 50 +++++++++++++++++++++++++++ REFEREE_DEV_DOC.md | 36 ++++++++++++++++++-- 3 files changed, 142 insertions(+), 4 deletions(-) diff --git a/GSRL/Device/inc/dvc_referee.hpp b/GSRL/Device/inc/dvc_referee.hpp index 04a03ed..5dcf6d2 100644 --- a/GSRL/Device/inc/dvc_referee.hpp +++ b/GSRL/Device/inc/dvc_referee.hpp @@ -184,7 +184,7 @@ struct CustomInfo uint8_t user_data[30]; // UTF-16 原始字节 }; -// 0x0304 键鼠数据(图传链路) +//0x0304 键鼠数据(图传链路) struct RemoteControl { int16_t mouse_x; @@ -196,6 +196,50 @@ struct RemoteControl uint16_t reserved; }; +// ----------[RM2026 V1.1.0] 新增:雷达无线链路数据结构--------- + +// 0x0A01 敌方机器人位置坐标 +struct RadarEnemyPos +{ + float x[7]; // 1~5号步兵 + 工程 + 哨兵 + float y[7]; +}; + +// 0x0A02 敌方机器人血量信息 +struct RadarEnemyHP +{ + uint16_t hp[7]; +}; + +// 0x0A03 敌方机器人剩余发弹量 +struct RadarEnemyAmmo +{ + uint16_t ammo_17mm[7]; + uint16_t ammo_42mm[7]; +}; + +// 0x0A04 敌方队伍宏观状态 +struct RadarTeamStatus +{ + uint8_t team_level; + uint16_t total_HP; + uint16_t total_ammo; +}; + +// 0x0A05 敌方机器人增益效果 +struct RadarEnemyBuff +{ + uint8_t recovery_buff[7]; + uint8_t defence_buff[7]; + uint8_t attack_buff[7]; +}; + +// 0x0A06 敌方干扰波密钥 +struct RadarJammerKey +{ + uint8_t key[6]; +}; + #pragma pack(pop) // ================= 全部信息聚合 ================= @@ -233,6 +277,20 @@ struct RefereeInfo bool hasRobotPos = false; bool hasBuff = false; bool hasRemoteControl = false; + + // 雷达无线链路数据 + RadarEnemyPos radarEnemyPos{}; + RadarEnemyHP radarEnemyHP{}; + RadarEnemyAmmo radarEnemyAmmo{}; + RadarTeamStatus radarTeamStatus{}; + RadarEnemyBuff radarEnemyBuff{}; + RadarJammerKey radarJammerKey{}; + bool hasRadarEnemyPos = false; + bool hasRadarEnemyHP = false; + bool hasRadarEnemyAmmo = false; + bool hasRadarTeamStatus = false; + bool hasRadarEnemyBuff = false; + bool hasRadarJammerKey = false; }; // 链路类型:常规链路 or 图传链路 diff --git a/GSRL/Device/src/dvc_referee.cpp b/GSRL/Device/src/dvc_referee.cpp index 7fd0afa..9067848 100644 --- a/GSRL/Device/src/dvc_referee.cpp +++ b/GSRL/Device/src/dvc_referee.cpp @@ -354,6 +354,56 @@ void RefereeParser::dispatchCmd(uint16_t cmd_id, const uint8_t* data, uint16_t l } break; + // -------- 雷达无线链路数据 -------- + // [RM2026 V1.1.0] 新增命令码:雷达无线链路 + + case 0x0A01: // 敌方位置 + if (len == sizeof(RadarEnemyPos)) + { + std::memcpy(&info_.radarEnemyPos, data, sizeof(RadarEnemyPos)); + info_.hasRadarEnemyPos = true; + } + break; + + case 0x0A02: // 敌方血量 + if (len == sizeof(RadarEnemyHP)) + { + std::memcpy(&info_.radarEnemyHP, data, sizeof(RadarEnemyHP)); + info_.hasRadarEnemyHP = true; + } + break; + case 0x0A03: // 敌方发弹量 + if (len == sizeof(RadarEnemyAmmo)) + { + std::memcpy(&info_.radarEnemyAmmo, data, sizeof(RadarEnemyAmmo)); + info_.hasRadarEnemyAmmo = true; + } + break; + + case 0x0A04: // 敌方队伍状态 + if (len == sizeof(RadarTeamStatus)) + { + std::memcpy(&info_.radarTeamStatus, data, sizeof(RadarTeamStatus)); + info_.hasRadarTeamStatus = true; + } + break; + + case 0x0A05: // 敌方增益 + if (len == sizeof(RadarEnemyBuff)) + { + std::memcpy(&info_.radarEnemyBuff, data, sizeof(RadarEnemyBuff)); + info_.hasRadarEnemyBuff = true; + } + break; + + case 0x0A06: // 干扰波密钥 + if (len == sizeof(RadarJammerKey)) + { + std::memcpy(&info_.radarJammerKey, data, sizeof(RadarJammerKey)); + info_.hasRadarJammerKey = true; + } + break; + default: // 其它 cmd_id 暂不处理,需要时再加 break; diff --git a/REFEREE_DEV_DOC.md b/REFEREE_DEV_DOC.md index 0bdc16a..5a3f65a 100644 --- a/REFEREE_DEV_DOC.md +++ b/REFEREE_DEV_DOC.md @@ -1,7 +1,15 @@ # 裁判系统驱动开发文档 (Referee System Driver Docs) +作者:张楚清 +版本:v1.1.0 +最后更新日期:2026-1-6 + +## 版本信息(Version Info) +本次更新同步官方协议 V1.1.0,新增对 雷达无线链路(CmdID 0x0A01~0x0A06) 的解析支持,并对文档结构进行了优化。 +👉 详细更新日志见文末《附录 A:更新日志》。 + ## 1. 概述 -本驱动用于解析 RoboMaster 裁判系统串口协议(V1.6.1),支持数据接收、校验、解包及数据结构化存储。 +本驱动用于解析 RoboMaster 裁判系统串口协议(V1.1.0),支持数据接收、校验、解包及数据结构化存储。 * **适用硬件**: STM32F407 (或其他支持 UART 的 MCU) * **通信接口**: USART6 (RX: PG9, TX: PG14) @@ -9,7 +17,7 @@ * **接收方式**: DMA + 空闲中断 (Idle Line Detection) ## 2. 协议说明 -遵循 RoboMaster 串口协议 V1.6.1 标准。 +遵循 RoboMaster 串口协议 V1.1.0 标准。 ### 2.1 数据帧结构 | SOF (1B) | DataLength (2B) | Seq (1B) | CRC8 (1B) | CmdID (2B) | Data (nB) | CRC16 (2B) | @@ -81,9 +89,31 @@ if (info.hasGameStatus) { | **0x0208** | 弹丸余量 | `ProjectileAllowance` | 剩余发弹量 | | **0x0303** | 小地图命令 | `MapCommand` | 选手端点击坐标 | | **0x0304** | 键鼠数据 | `RemoteControl` | **仅图传链路有效** | +| **0x0A01** | 敌方机器人位置坐标 | `RadarEnemyPos` | 10Hz,信号发射源→雷达 | +| **0x0A02** | 敌方机器人血量信息 | `RadarEnemyHP` | 同上 | +| **0x0A03** | 敌方机器人剩余发弹量 | `RadarEnemyAmmo` | 同上 | +| **0x0A04** | 敌方队伍宏观状态 | `RadarTeamStatus` | 同上 | +| **0x0A05** | 敌方机器人增益效果 | `RadarEnemyBuff` | 同上 | +| **0x0A06** | 敌方干扰波密钥 | `RadarJammerKey` | 同上 | ## 5. 常见问题排查 1. **完全无数据**: 检查 `board_config.h` 是否定义 `USE_USART6`,检查 RX/TX 线序。 2. **校验错误 (CRC Fail)**: 确认 `dvc_referee.cpp` 中 `CRC16_INIT` 为 `0x0000`。 -3. **长度错误 (Len Mismatch)**: 确认 `dvc_referee.hpp` 中的结构体定义是否与 V1.6.1 协议一致。 +3. **长度错误 (Len Mismatch)**: 确认 `dvc_referee.hpp` 中的结构体定义是否与 V1.1.0 协议一致。 + + +## 更新日志(Changelog) + +### v1.1.0(2026-01-06) +**新增** +- 新增对裁判系统 *雷达无线链路* 的解析支持,包含 CmdID `0x0A01` ~ `0x0A06`。 +- 新增雷达数据结构体与对应解析流程。 + +**文档** +- 文档版本号同步至官方协议 V1.1.0。 +- 补充雷达链路说明,完善协议字段解释。 +- 优化章节结构与排版。 + +**兼容性** +- 保持与原有常规链路、图传链路接口兼容,无破坏性变更。 \ No newline at end of file From b81046ec3c1c1684a69f68d1881ee19acf782208 Mon Sep 17 00:00:00 2001 From: ZhangChuqing Date: Tue, 6 Jan 2026 17:39:17 +0800 Subject: [PATCH 3/3] new file: GSRL/Documentation/Device/REFEREE_DEV_DOC.md --- GSRL/Documentation/Device/REFEREE_DEV_DOC.md | 119 +++++++++++++++++++ 1 file changed, 119 insertions(+) create mode 100644 GSRL/Documentation/Device/REFEREE_DEV_DOC.md diff --git a/GSRL/Documentation/Device/REFEREE_DEV_DOC.md b/GSRL/Documentation/Device/REFEREE_DEV_DOC.md new file mode 100644 index 0000000..3be0466 --- /dev/null +++ b/GSRL/Documentation/Device/REFEREE_DEV_DOC.md @@ -0,0 +1,119 @@ +# 裁判系统驱动开发文档 (Referee System Driver Docs) + +作者:张楚清 +版本:v1.1.0 +最后更新日期:2026-1-6 + +## 版本信息(Version Info) +本次更新同步官方协议 V1.1.0,新增对 雷达无线链路(CmdID 0x0A01~0x0A06) 的解析支持,并对文档结构进行了优化。 +👉 详细更新日志见文末《更新日志》。 + +## 1. 概述 +本驱动用于解析 RoboMaster 裁判系统串口协议(V1.1.0),支持数据接收、校验、解包及数据结构化存储。 + +* **适用硬件**: STM32F407 (或其他支持 UART 的 MCU) +* **通信接口**: USART6 (RX: PG9, TX: PG14) +* **波特率**: 115200, 8N1 +* **接收方式**: DMA + 空闲中断 (Idle Line Detection) + +## 2. 协议说明 +遵循 RoboMaster 串口协议 V1.1.0 标准。 + +### 2.1 数据帧结构 +| SOF (1B) | DataLength (2B) | Seq (1B) | CRC8 (1B) | CmdID (2B) | Data (nB) | CRC16 (2B) | +| :--- | :--- | :--- | :--- | :--- | :--- | :--- | +| 0xA5 | 数据段长度 | 包序号 | 帧头校验 | 命令ID | 数据内容 | 整帧校验 | + +### 2.2 关键配置 +* **CRC8 初始值**: `0xFF` +* **CRC16 初始值**: `0x0000` (注意:非 0xFFFF,2024年这版大疆给的经测试发现不对) +* **多字节序**: Little Endian (小端模式) + +## 3. 接口使用 + +### 3.1 初始化 +在任务初始化阶段调用: +```cpp +// 缓冲区建议 >= 128 字节,防止长包截断 +UART_Init(&huart6, refereeITCallback, 128); +``` + +### 3.2 中断回调 +在串口中断回调函数中将数据喂给解析器: +```cpp +// 定义全局解析器实例 +RefereeParser referee(RefereeLink::Main); + +extern "C" void refereeITCallback(uint8_t *Buffer, uint16_t Length) +{ + for (uint16_t i = 0; i < Length; i++) + { + referee.onByteReceived(Buffer[i]); + } +} +``` + +### 3.3 数据获取 +解析后的数据存储在 `RefereeInfo` 结构体中,可直接访问: + +```cpp +const RefereeInfo& info = referee.getInfo(); + +// 1. 检查数据是否更新 +if (info.hasRobotStatus) { + // 2. 读取具体字段 + uint8_t my_id = info.robotStatus.robot_id; + uint16_t hp = info.robotStatus.current_HP; + float power = info.powerHeat.chassis_power; +} + +// 3. 比赛状态(注意:单机调试时可能没有此包) +if (info.hasGameStatus) { + int stage = info.gameStatus.game_progress; +} +``` + +## 4. 支持的数据包列表 + +| Cmd ID | 描述 | 对应结构体 | 备注 | +| :--- | :--- | :--- | :--- | +| **0x0001** | 比赛状态 | `GameStatus` | 包含倒计时、比赛阶段 | +| **0x0002** | 比赛结果 | `GameResult` | 胜负判定 | +| **0x0003** | 机器人血量 | `GameRobotHP` | 红蓝双方所有单位血量 | +| **0x0201** | 机器人状态 | `RobotStatus` | ID、血量、功率限制、热量限制 | +| **0x0202** | 功率热量 | `PowerHeatData` | 实时功率、枪口热量 | +| **0x0203** | 机器人位置 | `RobotPos` | x, y, angle | +| **0x0204** | 增益状态 | `Buff` | 补给站Buff、防御Buff | +| **0x0206** | 伤害数据 | `HurtData` | 装甲板ID、伤害类型 | +| **0x0207** | 射击数据 | `ShootData` | 射速、弹丸初速 | +| **0x0208** | 弹丸余量 | `ProjectileAllowance` | 剩余发弹量 | +| **0x0303** | 小地图命令 | `MapCommand` | 选手端点击坐标 | +| **0x0304** | 键鼠数据 | `RemoteControl` | **仅图传链路有效** | +| **0x0A01** | 敌方机器人位置坐标 | `RadarEnemyPos` | 10Hz,信号发射源→雷达 | +| **0x0A02** | 敌方机器人血量信息 | `RadarEnemyHP` | 同上 | +| **0x0A03** | 敌方机器人剩余发弹量 | `RadarEnemyAmmo` | 同上 | +| **0x0A04** | 敌方队伍宏观状态 | `RadarTeamStatus` | 同上 | +| **0x0A05** | 敌方机器人增益效果 | `RadarEnemyBuff` | 同上 | +| **0x0A06** | 敌方干扰波密钥 | `RadarJammerKey` | 同上 | + +## 5. 常见问题排查 + +1. **完全无数据**: 检查 `board_config.h` 是否定义 `USE_USART6`,检查 RX/TX 线序。 +2. **校验错误 (CRC Fail)**: 确认 `dvc_referee.cpp` 中 `CRC16_INIT` 为 `0x0000`。 +3. **长度错误 (Len Mismatch)**: 确认 `dvc_referee.hpp` 中的结构体定义是否与 V1.1.0 协议一致。 + + +## 更新日志(Changelog) + +### v1.1.0(2026-01-06) +**新增** +- 新增对裁判系统 *雷达无线链路* 的解析支持,包含 CmdID `0x0A01` ~ `0x0A06`。 +- 新增雷达数据结构体与对应解析流程。 + +**文档** +- 文档版本号同步至官方协议 V1.1.0。 +- 补充雷达链路说明,完善协议字段解释。 +- 优化章节结构与排版。 + +**兼容性** +- 保持与原有常规链路、图传链路接口兼容,无破坏性变更。 \ No newline at end of file