refactor: migrate from DJI motors to DM motors, simplify to gimbal-only control

- Rewrite DM motor driver based on DM_6220 reference protocol (MIT/PosVel/Velocity modes)
- Use velocity control mode: MCU runs angle PID with IMU feedback, motor handles speed loop
- Rewrite vision protocol with 3 frame types (0xA5 auto data, 0xA6 manual data, 0xA7 command)
- Decouple serial parsing (opencv module) from mode switching (robot_cmd module)
- Implement dual-loop architecture: vision PID outer loop → angle ref → motor PID inner loop
- Add KF/PID reset on mode switch transitions
- Merge motor_reverse_flag and feedback_reverse_flag into single output_reverse flag
- Remove DMMotorEnable/DMMotorStop (motor always runs, controlled by angle ref)
- Remove unused modules: DJI/LK/HT/step/servo motors, chassis, shoot, referee
- Add ExitRun0Mode() for STM32H723 power supply configuration
- Clean up CMakeLists: 115 → 76 C source files
This commit is contained in:
2026-05-29 23:37:36 +08:00
parent 5b3333125d
commit 4d1702b577
17 changed files with 790 additions and 1485 deletions
+57 -81
View File
@@ -1,99 +1,75 @@
#include "opencv.h"
#include "daemon.h"
#define FRAME_SOF 0xA5
#define FRAME_PAYLOAD_LEN (2+2+4*12) // 52
#define FRAME_FULL_LEN (1+1+FRAME_PAYLOAD_LEN) //54
float opencv_Vision_data[12];
VisionData_t vision_data;
uint8_t Vison_revc_flag = 0; // 视觉数据接收标志位
uint8_t vision_used_flag = 0; // 视觉数据使用标志位,用于判断是否需要使用视觉数据
float Vison_Frequency; // 视觉数据接收频率,单位Hz
static USARTInstance *cv_usart_instance;
static DaemonInstance *OPENCV_vision_daemon_instance;
static DaemonInstance *vision_daemon_instance;
/**
* @brief 接收解包回调函数,将在bsp_usart.c中被usart rx callback调用
* @todo 1.提高可读性,将get_protocol_info的第四个参数增加一个float类型buffer
* 2.添加标志位解码
*/
static void DecodeVision()
static uint8_t calc_checksum(uint8_t *buf, uint8_t len)
{
DaemonReload(OPENCV_vision_daemon_instance); // 喂狗,防止离线
static uint32_t counter;
float dt = DWT_GetDeltaT(&counter);
Vison_Frequency = 1.0f / dt; // 计算频率
const uint16_t len = cv_usart_instance->recv_buff_size;
uint16_t flag_register;
uint8_t *buf = cv_usart_instance->recv_buff;
if (len != FRAME_FULL_LEN) {
// 长度不符
LOGERROR("Vision frame length error");
return;
}
if (buf[0] != FRAME_SOF) {
// SOF 错误
LOGERROR("Vision frame SOF error");
return;
}
uint8_t payload_len = buf[1];
if (payload_len != FRAME_PAYLOAD_LEN) {
// Payload 长度不符
LOGERROR("Vision frame payload length error");
return;
}
// 解析命令号和 flags(小端)
uint16_t cmd_id = buf[2] | (buf[3] << 8);
uint16_t flags = buf[4] | (buf[5] << 8);
// 解析 12 个 float
for (int i = 0; i < 12; i++) {
// 从 buf[6 + 4*i] 连续读 4 字节
memcpy(&opencv_Vision_data[i], buf + 6 + 4*i, sizeof(float));
}
// // 解析任务标志位(拟合最接近的int)
// int last_task_cmd = Vison_task_cmd;
// Vison_task_cmd = (int)(Vison_data[10] + 0.5f);
// if (Vison_task_cmd != last_task_cmd) {
// Vison_task_changed = 1; // 任务命令变化
// }
Vison_revc_flag = 1; // 设置接收标志位
// vision_used_flag = 1; // 设置视觉数据使用标志位
//解析暂停标志位
// run = (int)(Vison_data[11] + 0.5f);
// 处理...
// printf("Got frame: cmd=0x%04X flags=0x%04X\n", cmd_id, flags);
// for (int i = 0; i < 12; i += 2) {
// printf(" coord %d: (%.3f, %.3f)\n",
// i/2, data[i], data[i+1]);
// float a = data[0];
// TODO: code to resolve flag_register;
uint8_t sum = 0;
for (uint8_t i = 0; i < len - 1; i++)
sum += buf[i];
return sum;
}
void VisionOfflineCallback(void *owner_id)
static void DecodeVision()
{
Vison_revc_flag = 0; // 清除接收标志位
DaemonReload(vision_daemon_instance);
static uint32_t counter;
float dt = DWT_GetDeltaT(&counter);
vision_data.frequency = 1.0f / dt;
uint8_t *buf = cv_usart_instance->recv_buff;
uint16_t len = cv_usart_instance->recv_buff_size;
uint8_t sof = buf[0];
if (sof == VISION_SOF_AUTO && len == VISION_AUTO_LEN)
{
if (calc_checksum(buf, VISION_AUTO_LEN) != buf[VISION_AUTO_LEN - 1])
return;
memcpy(&vision_data.error_x, buf + 1, 4);
memcpy(&vision_data.error_y, buf + 5, 4);
vision_data.auto_updated = 1;
}
else if (sof == VISION_SOF_MANUAL && len == VISION_MANUAL_LEN)
{
if (calc_checksum(buf, VISION_MANUAL_LEN) != buf[VISION_MANUAL_LEN - 1])
return;
vision_data.zero_force = buf[1];
memcpy(&vision_data.yaw_ref, buf + 2, 4);
memcpy(&vision_data.pitch_ref, buf + 6, 4);
vision_data.manual_updated = 1;
}
else if (sof == VISION_SOF_CMD && len == VISION_CMD_LEN)
{
if (calc_checksum(buf, VISION_CMD_LEN) != buf[VISION_CMD_LEN - 1])
return;
vision_data.cmd = buf[1];
vision_data.cmd_pending = 1;
}
}
static void VisionOfflineCallback(void *id)
{
(void)id;
USARTServiceInit(cv_usart_instance);
}
void Vision_Uart_Init(UART_HandleTypeDef *_handle)
{
USART_Init_Config_s conf;
conf.module_callback = DecodeVision;
conf.recv_buff_size = 54;
conf.usart_handle = _handle;
USART_Init_Config_s conf = {
.module_callback = DecodeVision,
.recv_buff_size = 64,
.usart_handle = _handle,
};
cv_usart_instance = USARTRegister(&conf);
// 为master process注册daemon,用于判断视觉通信是否离线
Daemon_Init_Config_s daemon_conf = {
.callback = VisionOfflineCallback, // 离线时调用的回调函数,会重启串口接收
.owner_id = NULL,
.callback = NULL,
.owner_id = NULL,
.reload_count = 10,
};
OPENCV_vision_daemon_instance = DaemonRegister(&daemon_conf);
}
vision_daemon_instance = DaemonRegister(&daemon_conf);
}
+63 -10
View File
@@ -1,22 +1,75 @@
#ifndef OPENCV_H
#define OPENCV_H
#include "bsp_usart.h"
#include "opencv.h"
#include "bsp_log.h"
#include "bsp_dwt.h"
#include "bsp_log.h"
#include "usart.h"
#include "robot_def.h"
#include "daemon.h"
#include <string.h>
#include <stdio.h>
/*
* 上位机串口协议 — 三种帧类型, 通过帧头区分
*
* ---- 自动/视觉追踪数据帧 0xA5 (10 字节) ----
* [SOF:0xA5][error_x:4][error_y:4][checksum:1]
* error_x/y: float, 归一化误差, 视觉PID外环反馈值
*
* ---- 手动控制数据帧 0xA6 (11 字节) ----
* [SOF:0xA6][zero_force:1][yaw_ref:4][pitch_ref:4][checksum:1]
* zero_force: 0=正常角度控制, 1=零力/保持当前位置
* yaw_ref/pitch_ref: float, 角度参考值 (degrees)
*
* ---- 命令帧 0xA7 (3 字节) ----
* [SOF:0xA7][cmd:1][checksum:1]
* cmd: 0x01=切换到自动模式, 0x02=切换到手动模式, 0x03=激光开, 0x04=激光关
*
* checksum: 帧头到数据末尾前一字节累加和取低8位
*/
#define VISION_SOF_AUTO 0xA5
#define VISION_SOF_MANUAL 0xA6
#define VISION_SOF_CMD 0xA7
#define VISION_AUTO_LEN 10
#define VISION_MANUAL_LEN 11
#define VISION_CMD_LEN 3
/* 命令码 */
#define VISION_CMD_SWITCH_AUTO 0x01
#define VISION_CMD_SWITCH_MANUAL 0x02
#define VISION_CMD_LASER_ON 0x03
#define VISION_CMD_LASER_OFF 0x04
/* 工作模式 */
typedef enum
{
VISION_MODE_AUTO = 0, // 自动/视觉追踪
VISION_MODE_MANUAL = 1, // 手动
} Vision_Mode_e;
typedef struct
{
/* 自动模式数据 */
float error_x;
float error_y;
uint8_t auto_updated; // 收到新的自动数据帧标志
/* 手动模式数据 */
uint8_t zero_force;
float yaw_ref;
float pitch_ref;
uint8_t manual_updated; // 收到新的手动数据帧标志
/* 命令 */
uint8_t cmd; // 最新命令码
uint8_t cmd_pending; // 有待处理命令标志
float frequency; // 接收频率 (Hz)
} VisionData_t;
extern VisionData_t vision_data;
void Vision_Uart_Init(UART_HandleTypeDef *_handle);
extern float opencv_Vision_data[12]; // 视觉数据,12个float
extern uint8_t Vison_revc_flag; // 视觉数据接收标志位
extern uint8_t vision_used_flag; // 视觉数据使用标志位,用于判断是否需要使用视觉数据
extern float Vison_Frequency; // 视觉数据频率
#endif // !OPENCV_H
#endif // OPENCV_H