- 打卡等级:初来乍到
- 打卡总天数:4
- 最近打卡:2025-11-07 10:53:07
已绑定手机
新手上路
- 积分
- 27
|
发表于 2025-11-7 10:53:07
|
显示全部楼层
#include "control.h"
// 命中判定计数(用于激光开关的去抖)
static uint16 aim_cnt = 0;
// 命中判定阈值与保持时间(像素阈值、连续周期数)
const uint16 err_x_th = 6; // X 误差阈值(像素)
const uint16 err_y_th = 6; // Y 误差阈值(像素)
const uint16 hold_need = 5; // 连续满足阈值的周期数
// IMU 角速度与零偏(需要姿态时可用)
float angle_z_0off = 0,angle_z=0,angle_y_0off=0,angle_y=0;
float Yaw_Integral;
// PID 结构体与实例(用于 XY 轴速度控制)
typedef struct
{
float kp;
float ki;
float kd;
float integral;
float prev_error;
float integral_min;
float integral_max;
float out_min;
float out_max;
} pid_t;
// kp, ki, kd, integral, prev_error, integral_min/max, out_min/max
static pid_t pid_x = {0.40f, 0.00f, 0.02f, 0.0f, 0.0f, -200.0f, 200.0f, -80.0f, 80.0f};
static pid_t pid_y = {0.45f, 0.00f, 0.06f, 0.0f, 0.0f, -200.0f, 200.0f, -80.0f, 80.0f};
// 计算一步 PID 输出:像素误差 -> 期望转速(RPM),正负表示方向
static float pid_step(pid_t *pid, float error, float dt_s)
{
float derivative = (error - pid->prev_error) / dt_s;
pid->integral += error * dt_s;
if (pid->integral > pid->integral_max) pid->integral = pid->integral_max;
if (pid->integral < pid->integral_min) pid->integral = pid->integral_min;
float out = pid->kp * error + pid->ki * pid->integral + pid->kd * derivative;
if (out > pid->out_max) out = pid->out_max;
if (out < pid->out_min) out = pid->out_min;
pid->prev_error = error;
return out;
}
void gyroOffsetInit(void)
{
uint8 i = 100;
float temp_z[100]={0};
float temp=0;
while (i)
{
i--;
imu660ra_get_gyro();
temp_z[i]=((float)(imu660ra_gyro_z)) / 16.4;
temp += temp_z[i];
delay_ms(5);
}
angle_z_0off = temp / 100;
}
uint8 imu_init_flag = 100;
void Imu_Init(void)
{
if(imu660ra_init() == 0)
{
imu_init_flag = 1;
}
gyroOffsetInit();
Yaw_Integral = 0;
}
void Angle_Get(void)
{
imu660ra_get_gyro();
angle_z = ((float)(imu660ra_gyro_z)-angle_z_0off)/16.4;
angle_y = ((float)(imu660ra_gyro_y)-angle_y_0off)/16.4;
// angle_z = imu660ra_gyro_transition(((float)(imu660ra_gyro_z)-angle_z_0off));
}
void Angle_Integral(void)
{
Angle_Get();
Yaw_Integral += angle_z*dt;
}
// 基于图像坐标误差的二维云台跟踪(速度模式 + PID)
void gimbal_track_xy(uint16 real_x, uint16 real_y, uint16 target_x, uint16 target_y)
{
int32 x_err = (int32)target_x - (int32)real_x;
int32 y_err = (int32)target_y - (int32)real_y;
uint16 dead_zone = 5; // 像素死区,避免微抖
uint16 min_vel = 1; // 最小转速 RPM,克服静摩擦
uint16 max_vel = 60; // 最大转速 RPM,结合机械能力
// X 轴控制(电机1)
{
uint32 abs_x = (x_err >= 0) ? (uint32)x_err : (uint32)(-x_err);
if (abs_x <= dead_zone)
{
Emm_V5_Vel_Control_1(1, 0, 0, 10, 0); // 停止
}
else
{
float u = pid_step(&pid_x, (float)x_err, dt); // PID 输出
uint8 dir_x = (u < 0.0f) ? 1 : 0; // 负为右转,正为左转(与现有方向约定一致)
uint16 vel_x = (uint16)((u < 0.0f) ? -u : u);
if (vel_x < min_vel) vel_x = min_vel;
if (vel_x > max_vel) vel_x = max_vel;
Emm_V5_Vel_Control_1(1, dir_x, vel_x, 10, 0);
}
}
// Y 轴控制(电机2)
{
uint32 abs_y = (y_err >= 0) ? (uint32)y_err : (uint32)(-y_err);
if (abs_y <= dead_zone)
{
Emm_V5_Vel_Control_1(2, 0, 0, 10, 0); // 停止
}
else
{
float u = pid_step(&pid_y, (float)y_err, dt); // PID 输出
uint8 dir_y = (u > 0.0f) ? 1 : 0; // 正为上转,负为下转(与现有方向约定一致)
uint16 vel_y = (uint16)((u < 0.0f) ? -u : u);
if (vel_y < min_vel) vel_y = min_vel;
if (vel_y > max_vel) vel_y = max_vel;
Emm_V5_Vel_Control_1(2, dir_y, vel_y, 10, 0);
}
}
uint16 ax = (ex >= 0) ? (uint16)x_err : (uint16)(-x_err);
uint16 ay = (ey >= 0) ? (uint16)y_err : (uint16)(-y_err);
if (has_target && ax <= err_x_th && ay <= err_y_th)
{
if (aim_cnt < 1000) aim_cnt++;
}
else
{
aim_cnt = 0;
}
if (aim_cnt >= hold_need)
{
light_open();
}
else
{
light_close();
}
}
// 目标处理:有目标 -> 跟踪;无目标 -> 低速左右搜索(俯仰保持)
void gimbal_track_handle(uint8 has_target, uint16 real_x, uint16 real_y, uint16 target_x, uint16 target_y)
{
static uint16 scan_tick = 0; // 扫描计数器
static uint8 scan_dir = 0; // 0 左, 1 右
if (has_target)
{
// 发现目标:立即跟踪
gimbal_track_xy(real_x, real_y, target_x, target_y);
scan_tick = 0;
return;
}
// 无目标:低速左右扫描 yaw,俯仰停住
Emm_V5_Vel_Control_1(2, 0, 0, 10, 0); // 停止俯仰
if (scan_tick++ >= 150) // 每隔固定周期换向
{
scan_tick = 0;
scan_dir = (scan_dir == 0) ? 1 : 0;
}
Emm_V5_Vel_Control_1(1, scan_dir, 5, 10, 0);
}
|
|