第一次提交

This commit is contained in:
2026-05-29 17:36:34 +08:00
commit 5b3333125d
1318 changed files with 722705 additions and 0 deletions
+801
View File
@@ -0,0 +1,801 @@
// app
#include "robot_def.h"
#include "robot_cmd.h"
// module
#include "controller.h"
#include "remote_control.h"
#include "ins_task.h"
#include "master_process.h"
#include "message_center.h"
#include "general_def.h"
#include "dji_motor.h"
#include "bmi088.h"
#include "vofa.h"
#include "board_comm.h"
// bsp
#include "bsp_dwt.h"
#include "bsp_log.h"
#include "opencv.h"
#include "step_motor.h"
#include "kalman_filter.h"
#include "arm_math.h"
#define mapping(x, in_min, in_max, out_min, out_max) \
((x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min)
// 添加卡尔曼滤波器实例
static KalmanFilter_t yaw_error_kf;
static KalmanFilter_t pitch_error_kf;
static float filtered_yaw_error = 0.0f;
static float filtered_pitch_error = 0.0f;
// 私有宏,自动将编码器转换成角度值
#define YAW_ALIGN_ANGLE (YAW_CHASSIS_ALIGN_ECD * ECD_ANGLE_COEF_DJI) // 对齐时的角度,0-360
#define PTICH_HORIZON_ANGLE (PITCH_HORIZON_ECD * ECD_ANGLE_COEF_DJI) // pitch水平时电机的角度,0-360
#define yaw_control_kxmax 0.001f
#define yaw_control_kxmin 0.0005f
#define pitch_control_kxmax 0.001f
#define pitch_control_kxmin 0.0004f
/* cmd应用包含的模块实例指针和交互信息存储*/
#ifdef GIMBAL_BOARD // 对双板的兼容,条件编译
#include "can_comm.h"
static CANCommInstance *cmd_can_comm; // 双板通信
#endif
#ifdef ONE_BOARD
static Publisher_t *chassis_cmd_pub; // 底盘控制消息发布者
static Subscriber_t *chassis_feed_sub; // 底盘反馈信息订阅者
#endif // ONE_BOARD
static Chassis_Ctrl_Cmd_s chassis_cmd_send; // 发送给底盘应用的信息,包括控制信息和UI绘制相关
static Chassis_Upload_Data_s chassis_fetch_data; // 从底盘应用接收的反馈信息信息,底盘功率枪口热量与底盘运动状态等
static RC_ctrl_t *rc_data; // 遥控器数据,初始化时返回
static Vision_Recv_s *vision_recv_data; // 视觉接收数据指针,初始化时返回
static Vision_Send_s vision_send_data; // 视觉发送数据
static board_comm_data_t *board_comm_data; // 板间通信数据指针,初始化时返回
static Publisher_t *gimbal_cmd_pub; // 云台控制消息发布者
static Subscriber_t *gimbal_feed_sub; // 云台反馈信息订阅者
static Gimbal_Ctrl_Cmd_s gimbal_cmd_send; // 传递给云台的控制信息
static Gimbal_Upload_Data_s gimbal_fetch_data; // 从云台获取的反馈信息
static Publisher_t *shoot_cmd_pub; // 发射控制消息发布者
static Subscriber_t *shoot_feed_sub; // 发射反馈信息订阅者
static Shoot_Ctrl_Cmd_s shoot_cmd_send; // 传递给发射的控制信息
static Shoot_Upload_Data_s shoot_fetch_data; // 从发射获取的反馈信息
static Robot_Status_e robot_state; // 机器人整体工作状态
PIDInstance yaw_tracking_pid; // 云台yaw跟随pid
PIDInstance pitch_tracking_pid; // 云台pitch跟随pid
static float yaw_tracking_pid_output; // 云台yaw跟随pid输出
static float pitch_tracking_pid_output; // 云台pitch跟随pid输出
static float vofa_debug[7];
// #define PITCH_HORIZON_ECD 6800 // 俯仰水平时的电机编码器
// 在RobotCMDInit()中添加卡尔曼滤波器初始化
static void VisionKalmanFilterInit()
{
// yaw误差卡尔曼滤波器初始化
Kalman_Filter_Init(&yaw_error_kf, 2, 0, 1); // 状态2维(位置,速度), 控制0维, 观测1维
static float P_init_yaw[4] = {
5.0f, 0.0f, // 减小初始不确定性
0.0f, 5.0f};
memcpy(yaw_error_kf.P_data, P_init_yaw, sizeof(P_init_yaw));
// 状态转移矩阵F (dt = 1/200 = 0.005s, 200Hz控制频率)
static float F_init_yaw[4] = {
1.0f, 0.005f, // 修正时间步长
0.0f, 1.0f};
memcpy(yaw_error_kf.F_data, F_init_yaw, sizeof(F_init_yaw));
// 过程噪声协方差矩阵Q
static float Q_init_yaw[4] = {
0.1f, 0.0f, // 增大位置过程噪声
0.0f, 1.0f // 增大速度过程噪声
};
memcpy(yaw_error_kf.Q_data, Q_init_yaw, sizeof(Q_init_yaw));
// 观测矩阵H
static uint8_t measurement_map_yaw[1] = {1}; // 观测第1个状态(位置)
static float measurement_degree_yaw[1] = {1.0f};
static float mat_R_diagonal_yaw[1] = {1.0f}; // 减小观测噪声,增加对观测的信任
static float state_min_variance_yaw[2] = {0.001f, 0.001f}; // 减小最小方差
yaw_error_kf.UseAutoAdjustment = 1;
memcpy(yaw_error_kf.MeasurementMap, measurement_map_yaw, sizeof(measurement_map_yaw));
memcpy(yaw_error_kf.MeasurementDegree, measurement_degree_yaw, sizeof(measurement_degree_yaw));
memcpy(yaw_error_kf.MatR_DiagonalElements, mat_R_diagonal_yaw, sizeof(mat_R_diagonal_yaw));
memcpy(yaw_error_kf.StateMinVariance, state_min_variance_yaw, sizeof(state_min_variance_yaw));
// pitch误差卡尔曼滤波器初始化
Kalman_Filter_Init(&pitch_error_kf, 2, 0, 1); // 状态2维(位置,速度), 控制0维, 观测1维
static float P_init_pitch[4] = {
5.0f, 0.0f, // 减小初始不确定性
0.0f, 5.0f};
memcpy(pitch_error_kf.P_data, P_init_pitch, sizeof(P_init_pitch));
// 状态转移矩阵F (dt = 1/200 = 0.005s, 200Hz控制频率)
static float F_init_pitch[4] = {
1.0f, 0.005f, // 修正时间步长
0.0f, 1.0f};
memcpy(pitch_error_kf.F_data, F_init_pitch, sizeof(F_init_pitch));
// 过程噪声协方差矩阵Q
static float Q_init_pitch[4] = {
0.1f, 0.0f, // 增大位置过程噪声
0.0f, 1.0f // 增大速度过程噪声
};
memcpy(pitch_error_kf.Q_data, Q_init_pitch, sizeof(Q_init_pitch));
// 观测矩阵H
static uint8_t measurement_map_pitch[1] = {1}; // 观测第1个状态(位置)
static float measurement_degree_pitch[1] = {1.0f};
static float mat_R_diagonal_pitch[1] = {1.0f}; // 减小观测噪声,增加对观测的信任
static float state_min_variance_pitch[2] = {0.001f, 0.001f}; // 减小最小方差
pitch_error_kf.UseAutoAdjustment = 1;
memcpy(pitch_error_kf.MeasurementMap, measurement_map_pitch, sizeof(measurement_map_pitch));
memcpy(pitch_error_kf.MeasurementDegree, measurement_degree_pitch, sizeof(measurement_degree_pitch));
memcpy(pitch_error_kf.MatR_DiagonalElements, mat_R_diagonal_pitch, sizeof(mat_R_diagonal_pitch));
memcpy(pitch_error_kf.StateMinVariance, state_min_variance_pitch, sizeof(state_min_variance_pitch));
}
void RobotCMDInit()
{
rc_data = RemoteControlInit(&huart5); // 修改为对应串口,注意如果是自研板dbus协议串口需选用添加了反相器的那个
gimbal_cmd_pub = PubRegister("gimbal_cmd", sizeof(Gimbal_Ctrl_Cmd_s));
gimbal_feed_sub = SubRegister("gimbal_feed", sizeof(Gimbal_Upload_Data_s));
shoot_cmd_pub = PubRegister("shoot_cmd", sizeof(Shoot_Ctrl_Cmd_s));
shoot_feed_sub = SubRegister("shoot_feed", sizeof(Shoot_Upload_Data_s));
chassis_cmd_pub = PubRegister("chassis_cmd", sizeof(Chassis_Ctrl_Cmd_s));
chassis_feed_sub = SubRegister("chassis_feed", sizeof(Chassis_Upload_Data_s));
static PID_Init_Config_s yaw_tracking_pid_config = {
.Kp = 1.5f,
.Ki = 3.0f,
.Kd = 0.07f,
.IntegralLimit = 100.0f,
.CoefB = 20.0f,
.CoefA = 20.0f,
// .Output_LPF_RC = 0.002f, // 100Hz
.Improve = PID_Trapezoid_Intergral | PID_Integral_Limit | PID_Derivative_On_Measurement | PID_ChangingIntegrationRate,
.MaxOut = 200.0f,
};
static PID_Init_Config_s pitch_tracking_pid_config = {
.Kp = 1.0f,
.Ki = 2.0f,
.Kd = 0.05f,
.IntegralLimit = 100.0f,
.CoefB = 10.0f,
.CoefA = 30.0f,
// .Output_LPF_RC = 0.002f, // 100Hz
.Improve = PID_Trapezoid_Intergral | PID_Integral_Limit | PID_Derivative_On_Measurement | PID_ChangingIntegrationRate,
.MaxOut = 200.0f,
};
PIDInit(&yaw_tracking_pid, &yaw_tracking_pid_config);
PIDInit(&pitch_tracking_pid, &pitch_tracking_pid_config);
VisionKalmanFilterInit();
Vision_Uart_Init(&huart10);
board_comm_data = BoardCommInit(&huart1); // 初始化板间通信数据指针
robot_state = ROBOT_READY; // 启动时机器人进入工作模式,后续加入所有应用初始化完成之后再进入
}
static uint8_t first_circle_flag = 1;
static float circle_progress = 0; // 画圆进度 100%为一圈
static float circle_radius = 60.0f; // 画圆半径
static float circle_angle = 0.0f; // 当前圆周角度
static float target_x = 0.0f; // 圆周上目标点x坐标
static float target_y = 0.0f; // 圆周上目标点y坐标
/**
* @brief 控制输入为遥控器(调试时)的模式和控制量设置
*
*/
static void RemoteControlSet()
{
chassis_cmd_send.chassis_mode = CHASSIS_NO_FOLLOW;
gimbal_cmd_send.gimbal_mode = GIMBAL_GYRO_MODE;
if (!switch_is_down(rc_data[TEMP].rc.switch_left))
{
if (Vison_revc_flag && switch_is_mid(rc_data[TEMP].rc.switch_left))
{
LIMIT_MIN_MAX(opencv_Vision_data[0], -100.0f, 100.0f);
LIMIT_MIN_MAX(opencv_Vision_data[1], -100.0f, 100.0f);
LIMIT_MIN_MAX(opencv_Vision_data[2], 500.0f, 1560.0f);
yaw_error_kf.MeasuredVector[0] = opencv_Vision_data[0];
float *yaw_filtered = Kalman_Filter_Update(&yaw_error_kf);
filtered_yaw_error = yaw_filtered[0];
pitch_error_kf.MeasuredVector[0] = opencv_Vision_data[1];
float *pitch_filtered = Kalman_Filter_Update(&pitch_error_kf);
filtered_pitch_error = pitch_filtered[0];
yaw_tracking_pid_output = PIDCalculate(&yaw_tracking_pid, filtered_yaw_error, 0);
pitch_tracking_pid_output = PIDCalculate(&pitch_tracking_pid, filtered_pitch_error, 0);
gimbal_cmd_send.yaw -= yaw_tracking_pid_output * mapping(opencv_Vision_data[2], 500.0f, 1560.0f, yaw_control_kxmax, yaw_control_kxmin); // yaw角度控制
gimbal_cmd_send.pitch += 0.001f * pitch_tracking_pid_output; // pitch角度控制
vofa_debug[0] = opencv_Vision_data[0]; // 调试用
vofa_debug[1] = filtered_yaw_error; // 调试用
vofa_debug[2] = yaw_tracking_pid.Err; // 调试用
vofa_debug[3] = yaw_tracking_pid.Pout; // 调试用
vofa_debug[4] = yaw_tracking_pid.Iout; // 调试用
vofa_debug[5] = yaw_tracking_pid.Dout; // 调试用
vofa_debug[6] = yaw_tracking_pid.Output; // 调试用
vofa_justfloat_output(vofa_debug, 7, &huart7);
first_circle_flag = 1; // 重置画圆标志
}
else if (switch_is_up(rc_data[TEMP].rc.switch_left))
{
if (first_circle_flag)
{
circle_progress = 0;
circle_angle = 0.0f;
first_circle_flag = 0;
}
LIMIT_MIN_MAX(opencv_Vision_data[0], -100.0f, 100.0f);
LIMIT_MIN_MAX(opencv_Vision_data[1], -100.0f, 100.0f);
LIMIT_MIN_MAX(opencv_Vision_data[2], 500.0f, 1560.0f);
yaw_error_kf.MeasuredVector[0] = opencv_Vision_data[0];
float *yaw_filtered = Kalman_Filter_Update(&yaw_error_kf);
filtered_yaw_error = yaw_filtered[0];
pitch_error_kf.MeasuredVector[0] = opencv_Vision_data[1];
float *pitch_filtered = Kalman_Filter_Update(&pitch_error_kf);
filtered_pitch_error = pitch_filtered[0];
// 画圆模式 500hz 16s画完整圆
circle_progress += 0.000125f; // 每次增加1/16
if (circle_progress >= 1.0f)
{
circle_progress = 1.0f; // 限制在100%
}
circle_angle = 2.0f * PI * circle_progress; // 计算当前圆周角度
target_x = circle_radius * cosf(circle_angle); // 计算圆周上目标点x坐标
target_y = circle_radius * sinf(circle_angle); // 计算圆周上目标点y坐标
yaw_tracking_pid_output = PIDCalculate(&yaw_tracking_pid, filtered_yaw_error, target_x);
pitch_tracking_pid_output = PIDCalculate(&pitch_tracking_pid, filtered_pitch_error, target_y);
gimbal_cmd_send.yaw -= yaw_tracking_pid_output * mapping(opencv_Vision_data[2], 500.0f, 1560.0f, yaw_control_kxmax, yaw_control_kxmin); // yaw角度控制
gimbal_cmd_send.pitch += pitch_tracking_pid_output * mapping(opencv_Vision_data[2], 500.0f, 1560.0f, pitch_control_kxmax, pitch_control_kxmin); // pitch角度控制
}
else
{
first_circle_flag = 1; // 重置画圆标志
gimbal_cmd_send.yaw -= 0.0025f * (float)rc_data[TEMP].rc.rocker_r_; // yaw角度控制
gimbal_cmd_send.pitch += 0.00125f * (float)rc_data[TEMP].rc.rocker_r1; // pitch角度控制
}
}
else
{
gimbal_cmd_send.yaw -= 0.0025f * (float)rc_data[TEMP].rc.rocker_r_; // yaw角度控制
gimbal_cmd_send.pitch += 0.00125f * (float)rc_data[TEMP].rc.rocker_r1; // pitch角度控制
}
if (gimbal_cmd_send.pitch > 30.0f) // 限制pitch角度
gimbal_cmd_send.pitch = 30.0f;
else if (gimbal_cmd_send.pitch < -30.0f)
gimbal_cmd_send.pitch = -30.0f;
}
void laser_on()
{
HAL_GPIO_WritePin(POWER_5V_GPIO_Port, POWER_5V_Pin, GPIO_PIN_SET);
}
void laser_off()
{
HAL_GPIO_WritePin(POWER_5V_GPIO_Port, POWER_5V_Pin, GPIO_PIN_RESET);
}
enum YunTaiState_e
{
YUNT_IDLE = 0, // 云台无任务
YUNT_NO1, // 云台题号1准备
YUNT_NO2_WAITING,
YUNT_NO2,
YUNT_NO3_WAITING,
YUNT_NO3_TURING,
YUNT_NO3_SEARCHING,
YUNT_NO3_TRACKING,
YUNT_NO4_1_WAITING,
YUNT_NO4_1_TURING,
YUNT_NO4_1_TRACKING,
YUNT_NO4_1_TRACKING_RUNNING,
YUNT_NO4_3_WAITING,
YUNT_NO4_3_TURING,
YUNT_NO4_3_TRACKING,
YUNT_NO4_3_TRACKING_RUNNING,
} YunTaiState = YUNT_IDLE; // 云台状态机
static float yuntai_no3_yawtarget = 0.0f; // 云台题号3目标yaw角度
static float yuntai_no3_searching_yaw = 0.0f; // 云台题号3搜索yaw角度
static float yuntai_no3_searching_yaw_max = 60.0f; // 云台yaw题号3搜索最大yaw角度
static float yuntai_no3_searching_yaw_min = -60.0f; // 云台yaw题号3搜索最小yaw角度
static float yuntai_no3_searching_step = 0.2f; // 云台题号3搜索步长,每次0.18度
static uint8_t yuntai_no3_searching_dir = 0; // 云台题号3搜索方向, 0: -, 1:+
static int16_t yuntai_no3_searching_count = 50; // 3计数器
static uint8_t no4_turning_count = 0; // 题号4转向计数器
static uint8_t no4_turning_status = 0; // 题号4转向状态
static uint8_t no4_turning_feedforward = 0.05;
static float timestart_timeline = 0.0f; // 题号开始时间
/**
* @brief 题号状态机
*
*/
static void AutoSet()
{
yaw_tracking_pid.Ki = 3.0f;
switch (board_comm_data->rx_int_data[0]) // 根据板间通信数据的第一个整数值来判断题号
{
case 1:
YunTaiState = YUNT_NO1;
break;
case 2:
if (board_comm_data->rx_int_data[1] == 0)
{
YunTaiState = YUNT_NO2_WAITING;
}
else
{
YunTaiState = YUNT_NO2; // 进入题号2状态
}
break;
case 3:
if (board_comm_data->rx_int_data[1] == 0)
{
YunTaiState = YUNT_NO3_WAITING;
}
else if (YunTaiState == YUNT_NO3_WAITING)
{
YunTaiState = YUNT_NO3_TURING; // 进入题号3转向状态
}
break;
case 4:
case 5:
if (board_comm_data->rx_int_data[1] == 0)
{
YunTaiState = YUNT_NO4_1_WAITING; // 进入题号4-1等待状态
}
else if (board_comm_data->rx_int_data[1] == 1 && YunTaiState == YUNT_NO4_1_WAITING)
{
YunTaiState = YUNT_NO4_1_TURING; // 进入题号4-1转向状态
}
else if (board_comm_data->rx_int_data[1] == 2)
{
YunTaiState = YUNT_NO4_1_TRACKING_RUNNING;
}
break;
case 6:
if (board_comm_data->rx_int_data[1] == 0)
{
YunTaiState = YUNT_NO4_3_WAITING; // 进入题号4-3等待状态
}
else if (board_comm_data->rx_int_data[1] == 1 && YunTaiState == YUNT_NO4_3_WAITING)
{
YunTaiState = YUNT_NO4_3_TURING; // 进入题号4-3状态
}
else if (board_comm_data->rx_int_data[1] == 2)
{
YunTaiState = YUNT_NO4_3_TRACKING_RUNNING;
}
default:
break;
}
switch (YunTaiState)
{
case YUNT_NO1:
gimbal_cmd_send.gimbal_mode = GIMBAL_ZERO_FORCE;
chassis_cmd_send.chassis_mode = CHASSIS_ZERO_FORCE;
gimbal_cmd_send.yaw = gimbal_fetch_data.gimbal_imu_data.YawTotalAngle;
gimbal_cmd_send.pitch = gimbal_fetch_data.gimbal_imu_data.Pitch;
laser_off();
break;
case YUNT_NO2_WAITING:
gimbal_cmd_send.gimbal_mode = GIMBAL_ZERO_FORCE;
gimbal_cmd_send.yaw = gimbal_fetch_data.gimbal_imu_data.YawTotalAngle;
gimbal_cmd_send.pitch = gimbal_fetch_data.gimbal_imu_data.Pitch;
timestart_timeline = DWT_GetTimeline_ms(); // 记录开始时间
laser_off();
break;
case YUNT_NO2:
gimbal_cmd_send.gimbal_mode = GIMBAL_GYRO_MODE;
if (DWT_GetTimeline_ms() - timestart_timeline >= 1500.0f)
{
laser_on();
}
else
{
if (Vison_revc_flag)
{
LIMIT_MIN_MAX(opencv_Vision_data[0], -100.0f, 100.0f);
LIMIT_MIN_MAX(opencv_Vision_data[1], -100.0f, 100.0f);
LIMIT_MIN_MAX(opencv_Vision_data[2], 500.0f, 1560.0f);
yaw_error_kf.MeasuredVector[0] = opencv_Vision_data[0];
float *yaw_filtered = Kalman_Filter_Update(&yaw_error_kf);
filtered_yaw_error = yaw_filtered[0];
pitch_error_kf.MeasuredVector[0] = opencv_Vision_data[1];
float *pitch_filtered = Kalman_Filter_Update(&pitch_error_kf);
filtered_pitch_error = pitch_filtered[0];
yaw_tracking_pid_output = PIDCalculate(&yaw_tracking_pid, filtered_yaw_error, 0);
pitch_tracking_pid_output = PIDCalculate(&pitch_tracking_pid, filtered_pitch_error, 0);
gimbal_cmd_send.yaw -= yaw_tracking_pid_output * mapping(opencv_Vision_data[2], 500.0f, 1560.0f, yaw_control_kxmax, yaw_control_kxmin); // yaw角度控制
gimbal_cmd_send.pitch += 0.001f * pitch_tracking_pid_output;
}
}
break;
case YUNT_NO3_WAITING:
gimbal_cmd_send.gimbal_mode = GIMBAL_ZERO_FORCE;
gimbal_cmd_send.yaw = gimbal_fetch_data.gimbal_imu_data.YawTotalAngle;
gimbal_cmd_send.pitch = gimbal_fetch_data.gimbal_imu_data.Pitch;
// 计算0度对应最接近的多圈yaw
yuntai_no3_yawtarget = roundf(gimbal_fetch_data.gimbal_imu_data.YawTotalAngle / 360.0f) * 360.0f; // 计算最近的多圈yaw
laser_off();
timestart_timeline = DWT_GetTimeline_ms(); // 记录开始时间
break;
case YUNT_NO3_TURING:
gimbal_cmd_send.gimbal_mode = GIMBAL_GYRO_MODE;
float yaw_diff = gimbal_fetch_data.gimbal_imu_data.YawTotalAngle - yuntai_no3_yawtarget; // 计算当前yaw与目标yaw的差值
float step_size = 0.8f; // 90度每秒, 500hz控制频率下每次0.18度
if (fabsf(yaw_diff) > step_size) // 如果差值大于180度,则调整到最小角度
{
if (yaw_diff > 0)
gimbal_cmd_send.yaw -= step_size;
else
gimbal_cmd_send.yaw += step_size;
}
if (fabsf(yaw_diff) <= 5.0f) // 如果差值小于5度,则认为转向完成
{
if (Vison_revc_flag)
{
YunTaiState = YUNT_NO3_TRACKING; // 进入跟踪状态
}
else
{
YunTaiState = YUNT_NO3_SEARCHING; // 进入搜索状态
}
}
gimbal_cmd_send.pitch = 0;
break;
case YUNT_NO3_TRACKING:
gimbal_cmd_send.gimbal_mode = GIMBAL_GYRO_MODE;
if (DWT_GetTimeline_ms() - timestart_timeline >= 3500.0f) // 如果超过3.5秒,则认为跟踪完成
{
laser_on();
}
else
{
if (Vison_revc_flag && Vison_Frequency >= 20.0f) // 如果视觉数据可用且频率大于20Hz
{
LIMIT_MIN_MAX(opencv_Vision_data[0], -100.0f, 100.0f);
LIMIT_MIN_MAX(opencv_Vision_data[1], -100.0f, 100.0f);
LIMIT_MIN_MAX(opencv_Vision_data[2], 500.0f, 1560.0f);
yaw_error_kf.MeasuredVector[0] = opencv_Vision_data[0];
float *yaw_filtered = Kalman_Filter_Update(&yaw_error_kf);
filtered_yaw_error = yaw_filtered[0];
pitch_error_kf.MeasuredVector[0] = opencv_Vision_data[1];
float *pitch_filtered = Kalman_Filter_Update(&pitch_error_kf);
filtered_pitch_error = pitch_filtered[0];
yaw_tracking_pid_output = PIDCalculate(&yaw_tracking_pid, filtered_yaw_error, 0);
pitch_tracking_pid_output = PIDCalculate(&pitch_tracking_pid, filtered_pitch_error, 0);
gimbal_cmd_send.yaw -= yaw_tracking_pid_output * mapping(opencv_Vision_data[2], 500.0f, 1560.0f, yaw_control_kxmax, yaw_control_kxmin); // yaw角度控制
gimbal_cmd_send.pitch += 0.001f * pitch_tracking_pid_output;
}
else
{
YunTaiState = YUNT_NO3_SEARCHING; // 如果视觉数据不可用,则进入搜索状态
}
}
break;
case YUNT_NO3_SEARCHING:
gimbal_cmd_send.gimbal_mode = GIMBAL_GYRO_MODE;
if (yuntai_no3_searching_dir == 0) // 如果当前是向左搜索
{
gimbal_cmd_send.yaw += yuntai_no3_searching_step; // 向左搜索
if (gimbal_cmd_send.yaw > yuntai_no3_searching_yaw_max + yuntai_no3_yawtarget) // 如果超过最小值,则切换方向
{
yuntai_no3_searching_dir = 1; // 切换到向右搜索
}
}
else // 当前是向右搜索
{
gimbal_cmd_send.yaw -= yuntai_no3_searching_step; // 向右搜索
if (gimbal_cmd_send.yaw < yuntai_no3_searching_yaw_min + yuntai_no3_yawtarget) // 如果超过最大值,则切换方向
{
yuntai_no3_searching_dir = 0; // 切换到向左搜索
}
}
gimbal_cmd_send.pitch = 0.0f; // 保持pitch角度为0
if (Vison_revc_flag && Vison_Frequency >= 20.0f)
{
yuntai_no3_searching_count--;
}
else
{
yuntai_no3_searching_count = 100; // 如果视觉数据可用但频率低于20Hz,则重置计数器
}
if (yuntai_no3_searching_count <= 0)
{
YunTaiState = YUNT_NO3_TRACKING; // 如果视觉数据可用,则进入跟踪状态
}
break;
case YUNT_NO4_1_WAITING:
gimbal_cmd_send.gimbal_mode = GIMBAL_ZERO_FORCE;
gimbal_cmd_send.yaw = gimbal_fetch_data.gimbal_imu_data.YawTotalAngle;
gimbal_cmd_send.pitch = gimbal_fetch_data.gimbal_imu_data.Pitch;
// 计算0度对应最接近的多圈yaw
yuntai_no3_yawtarget = roundf(gimbal_fetch_data.gimbal_imu_data.YawTotalAngle / 360.0f) * 360.0f; // 计算最近的多圈yaw
no4_turning_count = 0; // 题号4转向计数器
no4_turning_status = 0; // 题号4转向状态
timestart_timeline = DWT_GetTimeline_ms(); // 记录开始时间
laser_off();
break;
case YUNT_NO4_1_TURING:
gimbal_cmd_send.gimbal_mode = GIMBAL_GYRO_MODE;
gimbal_cmd_send.yaw = yuntai_no3_yawtarget - 45.0f;
gimbal_cmd_send.pitch = 0.0f; // 保持pitch角度为0
if (fabsf(gimbal_fetch_data.gimbal_imu_data.YawTotalAngle - yuntai_no3_yawtarget + 45.0f) < 5.0f)
{
YunTaiState = YUNT_NO4_1_TRACKING; // 进入跟踪状态
}
break;
case YUNT_NO4_1_TRACKING:
gimbal_cmd_send.gimbal_mode = GIMBAL_GYRO_MODE;
if (Vison_revc_flag)
{
LIMIT_MIN_MAX(opencv_Vision_data[0], -100.0f, 100.0f);
LIMIT_MIN_MAX(opencv_Vision_data[1], -100.0f, 100.0f);
LIMIT_MIN_MAX(opencv_Vision_data[2], 500.0f, 1560.0f);
yaw_error_kf.MeasuredVector[0] = opencv_Vision_data[0];
float *yaw_filtered = Kalman_Filter_Update(&yaw_error_kf);
filtered_yaw_error = yaw_filtered[0];
pitch_error_kf.MeasuredVector[0] = opencv_Vision_data[1];
float *pitch_filtered = Kalman_Filter_Update(&pitch_error_kf);
filtered_pitch_error = pitch_filtered[0];
yaw_tracking_pid_output = PIDCalculate(&yaw_tracking_pid, filtered_yaw_error, 0);
pitch_tracking_pid_output = PIDCalculate(&pitch_tracking_pid, filtered_pitch_error, 0);
gimbal_cmd_send.yaw -= yaw_tracking_pid_output * mapping(opencv_Vision_data[2], 500.0f, 1560.0f, yaw_control_kxmax, yaw_control_kxmin); // yaw角度控制
gimbal_cmd_send.pitch += 0.001f * pitch_tracking_pid_output;
}
break;
case YUNT_NO4_1_TRACKING_RUNNING:
gimbal_cmd_send.gimbal_mode = GIMBAL_GYRO_MODE;
if (board_comm_data->rx_float_data[1] >= 600.0f)
{
no4_turning_status = 1; // 题号4转向状态
if (board_comm_data->rx_float_data[0] >= 0.0f && board_comm_data->rx_float_data[0] <= 12.5f)
{
no4_turning_count = 1;
}
else if (board_comm_data->rx_float_data[0] > 12.5f && board_comm_data->rx_float_data[0] <= 37.5f)
{
no4_turning_count = 2;
}
else if (board_comm_data->rx_float_data[0] > 37.5f && board_comm_data->rx_float_data[0] <= 62.5f)
{
no4_turning_count = 3;
}
else if (board_comm_data->rx_float_data[0] > 62.5f && board_comm_data->rx_float_data[0] <= 87.5f)
{
no4_turning_count = 4;
}
}
else
{
no4_turning_status = 0; // 题号4转向状态
}
if (Vison_revc_flag)
{
LIMIT_MIN_MAX(opencv_Vision_data[0], -100.0f, 100.0f);
LIMIT_MIN_MAX(opencv_Vision_data[1], -100.0f, 100.0f);
LIMIT_MIN_MAX(opencv_Vision_data[2], 500.0f, 1560.0f);
yaw_error_kf.MeasuredVector[0] = opencv_Vision_data[0];
float *yaw_filtered = Kalman_Filter_Update(&yaw_error_kf);
filtered_yaw_error = yaw_filtered[0];
pitch_error_kf.MeasuredVector[0] = opencv_Vision_data[1];
float *pitch_filtered = Kalman_Filter_Update(&pitch_error_kf);
filtered_pitch_error = pitch_filtered[0];
yaw_tracking_pid_output = PIDCalculate(&yaw_tracking_pid, filtered_yaw_error, 0);
pitch_tracking_pid_output = PIDCalculate(&pitch_tracking_pid, filtered_pitch_error, 0);
gimbal_cmd_send.pitch += 0.001f * pitch_tracking_pid_output;
if (no4_turning_status == 1 && (no4_turning_count >=1 && no4_turning_count <=4)) // 如果题号4转向状态为1且转向计数器为1到4
{
yaw_tracking_pid.Kp = 3.0f; // 关闭yaw比例
yaw_tracking_pid.Ki = 0.0f; // 关闭yaw积分
yaw_tracking_pid.Iout = 0.0f; // 清零yaw积分输出
gimbal_cmd_send.yaw -= yaw_tracking_pid_output * mapping(opencv_Vision_data[2], 500.0f, 1560.0f, yaw_control_kxmax, yaw_control_kxmin); // yaw角度控制
}
else
{
yaw_tracking_pid.Kp = 2.0f; // 关闭yaw比例
yaw_tracking_pid.Ki = 4.0f;
gimbal_cmd_send.yaw -= yaw_tracking_pid_output * mapping(opencv_Vision_data[2], 500.0f, 1560.0f, yaw_control_kxmax, yaw_control_kxmin); // yaw角度控制
}
laser_on();
}
break;
case YUNT_NO4_3_WAITING:
gimbal_cmd_send.gimbal_mode = GIMBAL_ZERO_FORCE;
gimbal_cmd_send.yaw = gimbal_fetch_data.gimbal_imu_data.YawTotalAngle;
gimbal_cmd_send.pitch = gimbal_fetch_data.gimbal_imu_data.Pitch;
no4_turning_count = 0; // 题号4转向计数器
no4_turning_status = 0; // 题号4转向状态
// 计算0度对应最接近的多圈yaw
yuntai_no3_yawtarget = roundf(gimbal_fetch_data.gimbal_imu_data.YawTotalAngle / 360.0f) * 360.0f; // 计算最近的多圈yaw
timestart_timeline = DWT_GetTimeline_ms(); // 记录开始时间
laser_off();
break;
case YUNT_NO4_3_TURING:
gimbal_cmd_send.gimbal_mode = GIMBAL_GYRO_MODE;
gimbal_cmd_send.yaw = yuntai_no3_yawtarget - 45.0f;
gimbal_cmd_send.pitch = 0.0f; // 保持pitch角度为0
if (fabsf(gimbal_fetch_data.gimbal_imu_data.YawTotalAngle - yuntai_no3_yawtarget + 45.0f) < 5.0f)
{
YunTaiState = YUNT_NO4_3_TRACKING; // 进入跟踪状态
}
break;
case YUNT_NO4_3_TRACKING:
gimbal_cmd_send.gimbal_mode = GIMBAL_GYRO_MODE;
if (Vison_revc_flag)
{
LIMIT_MIN_MAX(opencv_Vision_data[0], -100.0f, 100.0f);
LIMIT_MIN_MAX(opencv_Vision_data[1], -100.0f, 100.0f);
LIMIT_MIN_MAX(opencv_Vision_data[2], 500.0f, 1560.0f);
yaw_error_kf.MeasuredVector[0] = opencv_Vision_data[0];
float *yaw_filtered = Kalman_Filter_Update(&yaw_error_kf);
filtered_yaw_error = yaw_filtered[0];
pitch_error_kf.MeasuredVector[0] = opencv_Vision_data[1];
float *pitch_filtered = Kalman_Filter_Update(&pitch_error_kf);
filtered_pitch_error = pitch_filtered[0];
yaw_tracking_pid_output = PIDCalculate(&yaw_tracking_pid, filtered_yaw_error, 60.0f); // 题号4-3目标error为60
pitch_tracking_pid_output = PIDCalculate(&pitch_tracking_pid, filtered_pitch_error, 0);
gimbal_cmd_send.yaw -= yaw_tracking_pid_output * mapping(opencv_Vision_data[2], 500.0f, 1560.0f, yaw_control_kxmax, yaw_control_kxmin); // yaw角度控制
gimbal_cmd_send.pitch += 0.001f * pitch_tracking_pid_output;
}
break;
case YUNT_NO4_3_TRACKING_RUNNING:
gimbal_cmd_send.gimbal_mode = GIMBAL_GYRO_MODE;
if (board_comm_data->rx_float_data[1] >= 600.0f)
{
no4_turning_status = 1; // 题号4转向状态
if (board_comm_data->rx_float_data[0] >= 0.0f && board_comm_data->rx_float_data[0] <= 12.5f)
{
no4_turning_count = 1;
}
else if (board_comm_data->rx_float_data[0] > 12.5f && board_comm_data->rx_float_data[0] <= 37.5f)
{
no4_turning_count = 2;
}
else if (board_comm_data->rx_float_data[0] > 37.5f && board_comm_data->rx_float_data[0] <= 62.5f)
{
no4_turning_count = 3;
}
else if (board_comm_data->rx_float_data[0] > 62.5f && board_comm_data->rx_float_data[0] <= 87.5f)
{
no4_turning_count = 4;
}
}
else
{
no4_turning_status = 0; // 题号4转向状态
}
if (Vison_revc_flag)
{
LIMIT_MIN_MAX(opencv_Vision_data[0], -100.0f, 100.0f);
LIMIT_MIN_MAX(opencv_Vision_data[1], -100.0f, 100.0f);
LIMIT_MIN_MAX(opencv_Vision_data[2], 500.0f, 1560.0f);
yaw_error_kf.MeasuredVector[0] = opencv_Vision_data[0];
float *yaw_filtered = Kalman_Filter_Update(&yaw_error_kf);
filtered_yaw_error = yaw_filtered[0];
pitch_error_kf.MeasuredVector[0] = opencv_Vision_data[1];
float *pitch_filtered = Kalman_Filter_Update(&pitch_error_kf);
filtered_pitch_error = pitch_filtered[0];
// 画圆模式 500hz 16s画完整圆
circle_progress = board_comm_data->rx_float_data[0] / 100.0f; // 画圆进度,从板间通信数据获取
if (circle_progress >= 1.0f)
{
circle_progress = 1.0f; // 限制在100%
}
circle_angle = 2.0f * PI * circle_progress; // 计算当前圆周角度
target_x = circle_radius * cosf(circle_angle); // 计算圆周上目标点x坐标
target_y = circle_radius * sinf(circle_angle); // 计算圆周上目标点y坐标
yaw_tracking_pid_output = PIDCalculate(&yaw_tracking_pid, filtered_yaw_error, target_x);
pitch_tracking_pid_output = PIDCalculate(&pitch_tracking_pid, filtered_pitch_error, target_y);
gimbal_cmd_send.pitch += pitch_tracking_pid_output * mapping(opencv_Vision_data[2], 500.0f, 1560.0f, pitch_control_kxmax, pitch_control_kxmin); // pitch角度控制
if (no4_turning_status == 1 && (no4_turning_count == 2 || no4_turning_count == 3)) // 如果题号4转向状态为1且转向计数器为2或3
{
yaw_tracking_pid.Kp = 3.0f; // 关闭yaw比例
yaw_tracking_pid.Ki = 0.0f; // 关闭yaw积分
yaw_tracking_pid.Iout = 0.0f; // 清零yaw积分输出
gimbal_cmd_send.yaw -= yaw_tracking_pid_output * mapping(opencv_Vision_data[2], 500.0f, 1560.0f, yaw_control_kxmax, yaw_control_kxmin); // yaw角度控制
}
else
{
yaw_tracking_pid.Kp = 1.5f; // 关闭yaw比例
yaw_tracking_pid.Ki = 3.0f;
gimbal_cmd_send.yaw -= yaw_tracking_pid_output * mapping(opencv_Vision_data[2], 500.0f, 1560.0f, yaw_control_kxmax, yaw_control_kxmin); // yaw角度控制
}
}
laser_on();
break;
default:
break;
}
}
/**
* @brief 紧急停止,包括遥控器左上侧拨轮打满/重要模块离线/双板通信失效等
* 停止的阈值'300'待修改成合适的值,或改为开关控制.
*
* @todo 后续修改为遥控器离线则电机停止(关闭遥控器急停),通过给遥控器模块添加daemon实现
*
*/
static void EmergencyHandler()
{
// 拨轮的向下拨超过一半进入急停模式.注意向打时下拨轮是正
if (switch_is_down(rc_data[TEMP].rc.switch_right) || robot_state == ROBOT_STOP) // 还需添加重要应用和模块离线的判断
{
robot_state = ROBOT_STOP;
gimbal_cmd_send.gimbal_mode = GIMBAL_ZERO_FORCE;
chassis_cmd_send.chassis_mode = CHASSIS_ZERO_FORCE;
gimbal_cmd_send.yaw = gimbal_fetch_data.gimbal_imu_data.YawTotalAngle;
gimbal_cmd_send.pitch = gimbal_fetch_data.gimbal_imu_data.Pitch;
LOGERROR("[CMD] emergency stop!");
}
// 遥控器右侧开关为[上],恢复正常运行
if (!switch_is_down(rc_data[TEMP].rc.switch_right) && robot_state == ROBOT_STOP)
{
robot_state = ROBOT_READY;
LOGINFO("[CMD] reinstate, robot ready");
}
}
/* 机器人核心控制任务,200Hz频率运行(必须高于视觉发送频率) */
void RobotCMDTask()
{
// 从其他应用获取回传数据
#ifdef ONE_BOARD
SubGetMessage(chassis_feed_sub, (void *)&chassis_fetch_data);
#endif // ONE_BOARD
#ifdef GIMBAL_BOARD
chassis_fetch_data = *(Chassis_Upload_Data_s *)CANCommGet(cmd_can_comm);
#endif // GIMBAL_BOARD
SubGetMessage(shoot_feed_sub, &shoot_fetch_data);
SubGetMessage(gimbal_feed_sub, &gimbal_fetch_data);
// 根据遥控器左侧开关,确定当前使用的控制模式为遥控器调试还是键鼠
if (switch_is_mid(rc_data[TEMP].rc.switch_right))
{
RemoteControlSet();
}
else if (switch_is_up(rc_data[TEMP].rc.switch_right) || !RemoteControlIsOnline())
{
AutoSet();
}
// AutoSet();
EmergencyHandler(); // 处理模块离线和遥控器急停等紧急情况
#ifdef ONE_BOARD
PubPushMessage(chassis_cmd_pub, (void *)&chassis_cmd_send);
#endif // ONE_BOARD
#ifdef GIMBAL_BOARD
CANCommSend(cmd_can_comm, (void *)&chassis_cmd_send);
#endif // GIMBAL_BOARD
PubPushMessage(shoot_cmd_pub, (void *)&shoot_cmd_send);
PubPushMessage(gimbal_cmd_pub, (void *)&gimbal_cmd_send);
}
+18
View File
@@ -0,0 +1,18 @@
#ifndef ROBOT_CMD_H
#define ROBOT_CMD_H
/**
* @brief 机器人核心控制任务初始化,会被RobotInit()调用
*
*/
void RobotCMDInit();
/**
* @brief 机器人核心控制任务,200Hz频率运行(必须高于视觉发送频率)
*
*/
void RobotCMDTask();
#endif // !ROBOT_CMD_H
+50
View File
@@ -0,0 +1,50 @@
# robot_cmd
<p align='right'>neozng1@hnu.edu.cn</p>
## 运行流程
运行流程可以很直观的从`RobotCMDTask()`中看出。
1. 首先通过消息订阅机制,获取其他应用的反馈信息
2. 使用`CalcOffsetAngle()`计算底盘和云台的offset angle(使得底盘始终获知当前的正方向)
3. 接着根据当前是通过键鼠or遥控器控制,调用对应的函数,将控制指令量化为具体的控制信息
4. 得到控制信息之后,先不急着发布,而是检测重要的模块和应用是否掉线或出现异常,以及遥控器是否进入紧急停止模式,如果以上情况发生,那么将所有的控制信息都置零,即让电机和其他执行单元保持静止。
5. 最后通过pubsub机制,把具体的控制信息发布到对应话题,让其他应用获取。若为双板,则将原本要推送给底盘的信息通过CANComm进行发送。
### 遥控器控制模式:
拨轮向下打到底进入紧急停止模式(后续改为关闭遥控器停止,利用daemon);拨轮向上打开启摩擦轮,超过一半开始发射(速度环,连发)
左侧开关:
- 上:键鼠控制
- 中:视觉控制(没有识别到目标的时候仍然可以使用遥控器控制云台)
- 下:遥控器控制
右侧开关:
- 上:弹舱开
- 中:底盘云台分离(底盘不旋转,全向移动)
- 下:底盘跟随云台
### 键鼠控制模式:
遥控器左侧开关拨到最上方,进入键鼠控制模式,此时不会响应遥控器遥感和拨轮的输入.
前后左右:WSAD
开关弹舱盖:R
小陀螺:Q
发射:鼠标左键
自瞄:鼠标右键
+140
View File
@@ -0,0 +1,140 @@
#include "kalman_filter.h"
// 添加卡尔曼滤波器实例
static KalmanFilter_t yaw_error_kf;
static KalmanFilter_t pitch_error_kf;
static float filtered_yaw_error = 0.0f;
static float filtered_pitch_error = 0.0f;
// 在RobotCMDInit()中添加卡尔曼滤波器初始化
static void VisionKalmanFilterInit()
{
// yaw误差卡尔曼滤波器初始化
Kalman_Filter_Init(&yaw_error_kf, 2, 0, 1); // 状态2维(位置,速度), 控制0维, 观测1维
// 减小初始协方差矩阵P,增加收敛速度
static float P_init_yaw[4] = {
5.0f, 0.0f, // 减小初始不确定性
0.0f, 5.0f
};
memcpy(yaw_error_kf.P_data, P_init_yaw, sizeof(P_init_yaw));
// 状态转移矩阵F (dt = 1/200 = 0.005s, 200Hz控制频率)
static float F_init_yaw[4] = {
1.0f, 0.005f, // 修正时间步长
0.0f, 1.0f
};
memcpy(yaw_error_kf.F_data, F_init_yaw, sizeof(F_init_yaw));
// 增大过程噪声协方差矩阵Q,允许更快的状态变化
static float Q_init_yaw[4] = {
0.1f, 0.0f, // 增大位置过程噪声
0.0f, 1.0f // 增大速度过程噪声
};
memcpy(yaw_error_kf.Q_data, Q_init_yaw, sizeof(Q_init_yaw));
// 观测矩阵H
static uint8_t measurement_map_yaw[1] = {1}; // 观测第1个状态(位置)
static float measurement_degree_yaw[1] = {1.0f};
static float mat_R_diagonal_yaw[1] = {1.0f}; // 减小观测噪声,增加对观测的信任
static float state_min_variance_yaw[2] = {0.001f, 0.001f}; // 减小最小方差
yaw_error_kf.UseAutoAdjustment = 1;
memcpy(yaw_error_kf.MeasurementMap, measurement_map_yaw, sizeof(measurement_map_yaw));
memcpy(yaw_error_kf.MeasurementDegree, measurement_degree_yaw, sizeof(measurement_degree_yaw));
memcpy(yaw_error_kf.MatR_DiagonalElements, mat_R_diagonal_yaw, sizeof(mat_R_diagonal_yaw));
memcpy(yaw_error_kf.StateMinVariance, state_min_variance_yaw, sizeof(state_min_variance_yaw));
// pitch误差卡尔曼滤波器初始化 - 使用相同的优化参数
Kalman_Filter_Init(&pitch_error_kf, 2, 0, 1);
static float P_init_pitch[4] = {
5.0f, 0.0f,
0.0f, 5.0f
};
memcpy(pitch_error_kf.P_data, P_init_pitch, sizeof(P_init_pitch));
static float F_init_pitch[4] = {
1.0f, 0.005f,
0.0f, 1.0f
};
memcpy(pitch_error_kf.F_data, F_init_pitch, sizeof(F_init_pitch));
static float Q_init_pitch[4] = {
0.1f, 0.0f,
0.0f, 1.0f
};
memcpy(pitch_error_kf.Q_data, Q_init_pitch, sizeof(Q_init_pitch));
static uint8_t measurement_map_pitch[1] = {1};
static float measurement_degree_pitch[1] = {1.0f};
static float mat_R_diagonal_pitch[1] = {1.0f};
static float state_min_variance_pitch[2] = {0.001f, 0.001f};
pitch_error_kf.UseAutoAdjustment = 1;
memcpy(pitch_error_kf.MeasurementMap, measurement_map_pitch, sizeof(measurement_map_pitch));
memcpy(pitch_error_kf.MeasurementDegree, measurement_degree_pitch, sizeof(measurement_degree_pitch));
memcpy(pitch_error_kf.MatR_DiagonalElements, mat_R_diagonal_pitch, sizeof(mat_R_diagonal_pitch));
memcpy(pitch_error_kf.StateMinVariance, state_min_variance_pitch, sizeof(state_min_variance_pitch));
}
// 更新RemoteControlSet()函数中的视觉处理部分
static void RemoteControlSet()
{
chassis_cmd_send.chassis_mode = CHASSIS_NO_FOLLOW;
gimbal_cmd_send.gimbal_mode = GIMBAL_GYRO_MODE;
if (switch_is_up(rc_data[TEMP].rc.switch_left))
{
if (Vison_revc_flag)
{
// 限制视觉数据范围
LIMIT_MIN_MAX(opencv_Vision_data[0], -100.0f, 100.0f);
LIMIT_MIN_MAX(opencv_Vision_data[1], -100.0f, 100.0f);
// 更新卡尔曼滤波器观测值
yaw_error_kf.MeasuredVector[0] = opencv_Vision_data[0];
pitch_error_kf.MeasuredVector[0] = opencv_Vision_data[1];
}
else
{
// 无视觉数据时,观测值设为0(表示无效)
yaw_error_kf.MeasuredVector[0] = 0.0f;
pitch_error_kf.MeasuredVector[0] = 0.0f;
}
// 执行卡尔曼滤波更新
float *yaw_filtered = Kalman_Filter_Update(&yaw_error_kf);
float *pitch_filtered = Kalman_Filter_Update(&pitch_error_kf);
filtered_yaw_error = yaw_filtered[0];
filtered_pitch_error = pitch_filtered[0];
// 使用滤波后的误差进行PID控制
yaw_tracking_pid_output = PIDCalculate(&yaw_tracking_pid, filtered_yaw_error, 0);
pitch_tracking_pid_output = PIDCalculate(&pitch_tracking_pid, filtered_pitch_error, 0);
gimbal_cmd_send.yaw += 0.001f * yaw_tracking_pid_output;
gimbal_cmd_send.pitch -= 0.001f * pitch_tracking_pid_output;
// 调试输出
vofa_debug[0] = filtered_yaw_error;
vofa_debug[1] = filtered_pitch_error;
vofa_debug[2] = yaw_tracking_pid.Err;
vofa_debug[3] = yaw_tracking_pid.dt;
vofa_debug[4] = yaw_tracking_pid.Iout;
vofa_debug[5] = yaw_tracking_pid.Dout;
vofa_debug[6] = yaw_tracking_pid.Output;
vofa_justfloat_output(vofa_debug, 7, &huart7);
}
else
{
gimbal_cmd_send.yaw += 0.0025f * (float)rc_data[TEMP].rc.rocker_r_;
gimbal_cmd_send.pitch -= 0.00125f * (float)rc_data[TEMP].rc.rocker_r1;
}
if (gimbal_cmd_send.pitch > 30.0f)
gimbal_cmd_send.pitch = 30.0f;
else if (gimbal_cmd_send.pitch < -30.0f)
gimbal_cmd_send.pitch = -30.0f;
}