polarimeter_software/User/app/pm_meas.c

559 lines
20 KiB
C
Raw Permalink Normal View History

2025-09-30 02:37:23 +00:00
/*
* @Author: mypx
* @Date: 2025-08-11 08:30:45
* @LastEditTime: 2025-09-24 16:12:52
* @LastEditors: mypx mypx_coder@163.com
* @Description:
*/
#include "pm_meas.h"
#include "data_process.h"
#include "data_sampling.h"
#include "et_waveform_analysis.h"
#include "etk_diff.h"
#include "etk_ringbuffer.h"
#include "servo.h"
#include <float.h>
#include <rtthread.h>
#include <stdio.h>
static pm_fsm_t *pm_fsm = NULL;
pm_fsm_t *get_pm_fsm(void)
{
return pm_fsm;
}
void fsm_state_toggle(void)
{
pm_fsm_t *fsm = get_pm_fsm();
if (!fsm)
return;
fsm->fz_fsm_state = !fsm->fz_fsm_state;
}
MSH_CMD_EXPORT_ALIAS(fsm_state_toggle, fsm, calculate state toggle);
void calibrate_mode_toggle(void)
{
pm_fsm_t *fsm = get_pm_fsm();
if (!fsm)
return;
fsm->fz_fsm_state = false;
fsm->meas_mode = fsm->meas_mode == PM_MEAS_NORMAL_Mode ? PM_MEAS_TEST_Mode : PM_MEAS_NORMAL_Mode;
}
MSH_CMD_EXPORT_ALIAS(calibrate_mode_toggle, cali, calibrate mode toggle);
void trend_handle_init(trend_handle_t *handle)
{
handle->last_val = NAN;
handle->inc_cnt = 0;
handle->dec_cnt = 0;
}
TrendStatus get_data_trend(trend_handle_t *handle, double new_val)
{
const double EPS = 1e-6; // 浮点数比较精度(避免误差)
// 1. 首次调用:无历史数据,返回“未知”并初始化上次值
if (isnan(handle->last_val))
{
handle->last_val = new_val;
return TREND_UNKNOWN;
}
// 2. 比较本次与上次数据,更新计数
if (new_val > handle->last_val + EPS)
{
// 数据上升
handle->inc_cnt++;
handle->dec_cnt = 0;
}
else if (new_val < handle->last_val - EPS)
{
// 数据下降
handle->dec_cnt++;
handle->inc_cnt = 0;
}
handle->last_val = new_val;
// 3. 判断是否达到“连续6次”的触发阈值原逻辑cnt > 5
if (handle->inc_cnt > TREND_MAX_CNT)
{
//inc_cnt = 0; // 触发后清零,重新开始计数
return TREND_INCREASE;
}
if (handle->dec_cnt > TREND_MAX_CNT)
{
//dec_cnt = 0; // 触发后清零,重新开始计数
return TREND_DECREASE;
}
return TREND_STABLE;
}
void trend_handle_reset(trend_handle_t *handle)
{
handle->last_val = NAN;
handle->inc_cnt = 0;
handle->dec_cnt = 0;
}
void measurement_info_print(pm_meas_t *fz)
{
ET_WARN("------------------------Calibration Info Start----------------------------");
ET_INFO("Find zero time: %lu ms", fz->calib_info[0].end_time - fz->calib_info[0].start_time);
float min_angle = FLT_MAX;
float max_angle = -FLT_MAX;
for (int i = 0; i < fz->calib_cnt; i++)
{
#if (ENABLE_CALIB_DOUBLE_DIR == 1)
if (fz->calib_info[i].m_dir == -1)
fz->calib_info[i].cali_angle = fz->calib_info[i].angle - CALIB_OFFSET;
else
#endif
fz->calib_info[i].cali_angle = fz->calib_info[i].angle;
if (fz->calib_info[i].cali_angle < min_angle)
{
min_angle = fz->calib_info[i].cali_angle;
}
if (fz->calib_info[i].cali_angle > max_angle)
{
max_angle = fz->calib_info[i].cali_angle;
}
// ET_INFO("[%d]: Used time:%d ms, start:%.3f, end zero pos:%.3f, cali_angle:%.2f, power:%.3f", i,
// fz->calib_info[i].end_time - fz->calib_info[i].start_time, fz->calib_info[i].target_angle,
// fz->calib_info[i].angle, fz->calib_info[i].cali_angle, fz->calib_info[i].power);
ET_INFO("[%d]: Used time:%d ms, start:%.3f, end zero pos:%.3f, power:%.3f", i,
fz->calib_info[i].end_time - fz->calib_info[i].start_time, fz->calib_info[i].target_angle,
fz->calib_info[i].angle, fz->calib_info[i].power);
}
ET_INFO("Final min_angle: %.3f, max_angle: %.3f", min_angle, max_angle);
if (min_angle != FLT_MAX && max_angle != FLT_MIN)
{
float accuracy = (max_angle - min_angle) / ENCODER_TO_POLARIZER_RATIO;
ET_INFO("Accuracy: %.3f (max_angle-min_angle=%.3f, ratio=%d)", accuracy, max_angle - min_angle,
ENCODER_TO_POLARIZER_RATIO);
}
else
{
ET_WARN("Accuracy not calculated: min_angle=%.3f, max_angle=%.3f", min_angle, max_angle);
}
ET_WARN("------------------------Calibration Info End----------------------------");
}
void accuracy_auto_measurement_once(pm_meas_t *fz, float angle)
{
ET_INFO("Start to calibrate again:%d", fz->calib_cnt);
#if (CALIB_TO_FIX_STAGE == 1)
float diff = angle - fz->stage_info[ZF_PREPARE_ENTER_STAGE].angle;
#else
float diff = CALIB_OSCI_ANGLE;
#endif
ET_INFO("diff:%.2f", diff);
float target_angle = 0.0f;
#if (ENABLE_CALIB_DOUBLE_DIR == 1)
if (fz->calib_cnt % 2 == 0)
{
target_angle = angle - diff;
ET_INFO("Odd times, turn back");
}
else
{
target_angle = angle + diff;
ET_INFO("Even times, turn forward");
}
#else
target_angle = angle + diff * CALIB_ONE_DIR;
#endif
uint32_t delay = convert_encoder_angle_rpm_to_to_time(diff, RUN_OFFSET_ANGLE_RPM);
ET_DBG("wait %d ms to target:%.2f", delay, target_angle);
int speed = (target_angle > angle) ? -RUN_OFFSET_ANGLE_RPM
: RUN_OFFSET_ANGLE_RPM; // FIXME:note encoder dir and motor dir
sv630p_speed_mode_start(&sv630p_handle, speed);
fz->target_dir = (speed > 0) ? -1 : 1;
ET_WARN("SPEED:%d", speed);
while (1)
{
angle = get_real_time_angle_degree();
//ET_DBG("angle:%.2f", angle);
// 根据方向判断是否达到目标角度,处理负数角度情况
if (((speed > 0) && (target_angle > angle)) || ((speed < 0) && (target_angle < angle)))
{
sv630p_speed_mode_stop(&sv630p_handle);
ET_INFO("Set angle:%.3f, reached:%.3f", target_angle, angle);
#if (CALIB_TO_FIX_STAGE == 1)
fz->state = ZF_PREPARE_ENTER_STAGE;
ET_WARN("Back to ZF_PREPARE_ENTER_STAGE state");
#else
fz->state = ZF_FAST_CLOSE_STAGE;
ET_WARN("Back to ZF_FAST_CLOSE_STAGE state");
#endif
uint8_t index = fz->calib_cnt % FZ_CALI_MAX_COUNT;
fz->calib_info[index].target_angle = target_angle;
ET_INFO("update start calib index:%d", index);
fz->calib_info[index].start_time = rt_tick_get_millisecond();
//fz->target_dir *= -1; // reverse dir
break;
}
}
}
// 饱和高噪声状态判断,不会误判为其他状态,任何情形都无需从这个状态反转
StageResult is_saturate_starting(pm_meas_t *fz)
{
if (fabs(fz->slope) < FZ_SATURATED_SLOPE_MAX && fz->p50 < FZ_SATURATED_POWER_MAX && (fz->freq < 10))
{
//fz->state = ZF_SATURATED_STAGE;
return STAGE_RESULT_OK;
}
else
return STAGE_RESULT_NO;
}
int goto_saturated_stage(pm_meas_t *fz)
{
fz->state = ZF_SATURATED_STAGE;
ET_WARN("power state: ZF_SATURATED_STAGE");
return sv630p_speed_update(&sv630p_handle, FZ_SATURATED_STAGE_RPM * fz->target_dir);
}
StageResult is_wave_enter_stage(pm_meas_t *fz)
{
if (fz->slope > 100000)
return STAGE_RESULT_OK;
else
return STAGE_RESULT_NO;
}
StageResult is_prepare_enter_stage(pm_meas_t *fz)
{
if (fz->p50 > 100000 && fz->trend == TREND_DECREASE && fz->state != ZF_WAVE_LEAVE_STAGE)
return STAGE_RESULT_OK;
else
return STAGE_RESULT_NO;
}
int goto_prepare_enter_stage(pm_meas_t *fz, double p50, double slope, float freq, double angle)
{
fz->state = ZF_PREPARE_ENTER_STAGE;
if (fz->stage_info[ZF_PREPARE_ENTER_STAGE].is_updated == 0)
{
fz->stage_info[ZF_PREPARE_ENTER_STAGE].angle = angle;
fz->stage_info[ZF_PREPARE_ENTER_STAGE].time = rt_tick_get_millisecond();
fz->stage_info[ZF_PREPARE_ENTER_STAGE].p50 = p50;
fz->stage_info[ZF_PREPARE_ENTER_STAGE].slope = slope;
fz->stage_info[ZF_PREPARE_ENTER_STAGE].freq = freq;
fz->stage_info[ZF_PREPARE_ENTER_STAGE].is_updated = 1;
}
ET_WARN("enter ZF_PREPARE_ENTER_STAGE angle:%.3f", angle);
return sv630p_speed_update(&sv630p_handle, FZ_PREPARE_STAGE_RPM * fz->target_dir);
}
int goto_prepare_leave_stage(pm_meas_t *fz, double angle)
{
fz->target_dir = fz->target_dir * -1;
if (fz->stage_info[ZF_PREPARE_LEAVE_STAGE].is_updated == 0)
{
fz->stage_info[ZF_PREPARE_LEAVE_STAGE].is_updated = 1;
fz->stage_info[ZF_PREPARE_LEAVE_STAGE].angle = angle;
fz->stage_info[ZF_PREPARE_LEAVE_STAGE].time = rt_tick_get_millisecond();
fz->stage_info[ZF_PREPARE_LEAVE_STAGE].p50 = fz->p50;
fz->stage_info[ZF_PREPARE_LEAVE_STAGE].slope = fz->slope;
fz->stage_info[ZF_PREPARE_LEAVE_STAGE].freq = fz->freq;
}
ET_WARN("ZF_PREPARE_LEAVE_STAGE, angle:%.3f", angle);
return sv630p_speed_update(&sv630p_handle, FZ_PREPARE_STAGE_RPM * fz->target_dir);
}
StageResult is_fast_close_enter(pm_meas_t *fz)
{
if (fz->p50 <= 100000 && fz->trend == TREND_DECREASE && fz->state == ZF_PREPARE_ENTER_STAGE)
return STAGE_RESULT_OK;
else
return STAGE_RESULT_NO;
}
int goto_fast_close_stage(pm_meas_t *fz, double p50, double slope, float freq, double angle)
{
fz->state = ZF_FAST_CLOSE_STAGE;
if (fz->stage_info[ZF_FAST_CLOSE_STAGE].is_updated == 0)
{
fz->stage_info[ZF_FAST_CLOSE_STAGE].is_updated = 1;
fz->stage_info[ZF_FAST_CLOSE_STAGE].angle = angle;
fz->stage_info[ZF_FAST_CLOSE_STAGE].time = rt_tick_get_millisecond();
fz->stage_info[ZF_FAST_CLOSE_STAGE].p50 = p50;
fz->stage_info[ZF_FAST_CLOSE_STAGE].slope = slope;
fz->stage_info[ZF_FAST_CLOSE_STAGE].freq = freq;
}
ET_WARN("FAST_CLOSE, angle:%.3f\r\n", angle);
return sv630p_speed_update(&sv630p_handle, FZ_FAST_CLOSE_STAGE_RPM * fz->target_dir);
}
StageResult is_zero_stage(pm_meas_t *fz)
{
if (fabs(fz->slope) < 0.5 && fz->p50 < ZERO_POS_THRESHOLD && (fz->freq >= 99.95 && fz->freq < 100.3f)
&& fz->trend == TREND_STABLE)
return STAGE_RESULT_OK;
else
return STAGE_RESULT_NO;
}
StageResult is_fast_leave_stage(pm_meas_t *fz)
{
if ((fz->p50 <= 100000) && (fz->slope > 10000)
&& (fz->freq > FZ_FAST_CLOSE_ENTER_FREQ_MIN && fz->freq < FZ_FAST_CLOSE_ENTER_FREQ_MAX)
&& fz->state != ZF_SATURATED_STAGE)
return STAGE_RESULT_OK;
else
return STAGE_RESULT_NO;
}
StageResult is_prepare_leave_stage(pm_meas_t *fz)
{
if (fz->p50 >= 100000 && fz->trend == TREND_INCREASE
&& (fz->state == ZF_FAST_CLOSE_STAGE || fz->state == ZF_ZERO_STAGE))
return STAGE_RESULT_OK;
else
return STAGE_RESULT_NO;
}
StageResult is_wave_leave_stage(pm_meas_t *fz)
{
if ((fz->slope < -100000) || (fz->freq > 52 && fz->freq < 98 && fz->p50 < 1000 && fz->trend == TREND_DECREASE)
|| (fz->freq < 1.0 && fz->trend == TREND_DECREASE))
return STAGE_RESULT_OK;
else
return STAGE_RESULT_NO;
}
ZeroFindState get_find_zero_state(pm_meas_t *fz)
{
return fz->state;
}
void calculate_stage(pm_meas_t *fz)
{
if (is_saturate_starting(fz) == STAGE_RESULT_OK && fz->stage_info[ZF_SATURATED_STAGE].is_print == false)
{
ET_WARN("enter saturated");
fz->stage_info[ZF_SATURATED_STAGE].is_print = true;
fz->state = ZF_SATURATED_STAGE;
}
if (is_wave_enter_stage(fz) == STAGE_RESULT_OK && fz->stage_info[ZF_WAVE_ENTER_STAGE].is_print == false)
{
ET_WARN("enter 50hz started");
fz->stage_info[ZF_WAVE_ENTER_STAGE].is_print = true;
fz->stage_info[ZF_SATURATED_STAGE].is_print = false;
fz->state = ZF_WAVE_ENTER_STAGE;
}
if (is_prepare_enter_stage(fz) == STAGE_RESULT_OK && fz->stage_info[ZF_PREPARE_ENTER_STAGE].is_print == false)
{
ET_WARN("enter prepare stage");
fz->stage_info[ZF_PREPARE_ENTER_STAGE].is_print = true;
fz->state = ZF_PREPARE_ENTER_STAGE;
fz->stage_info[ZF_WAVE_ENTER_STAGE].is_print = false;
}
if (is_fast_close_enter(fz) == STAGE_RESULT_OK && fz->stage_info[ZF_FAST_CLOSE_STAGE].is_print == false)
{
ET_WARN("enter fast close stage, dir:%d, target dir :%d", sv630p_get_dir(&sv630p_handle), fz->target_dir);
fz->stage_info[ZF_FAST_CLOSE_STAGE].is_print = true;
fz->stage_info[ZF_PREPARE_ENTER_STAGE].is_print = false;
fz->state = ZF_FAST_CLOSE_STAGE;
}
if (is_zero_stage(fz) == STAGE_RESULT_OK && fz->stage_info[ZF_ZERO_STAGE].is_print == false)
{
ET_WARN("enter zero state");
fz->stage_info[ZF_ZERO_STAGE].is_print = true;
if (fz->state == ZF_UNKNOWN_STAGE)
fz->state = ZF_ZERO_STAGE; // 开机判断
}
else
{
fz->stage_info[ZF_ZERO_STAGE].is_print = false;
}
if (is_fast_leave_stage(fz) == STAGE_RESULT_OK && fz->stage_info[ZF_FAST_LEAVE_STAGE].is_print == false)
{
ET_WARN("fast leave stage");
fz->stage_info[ZF_FAST_LEAVE_STAGE].is_print = true;
//fz->stage_info[ZF_PRECISE_ADJUST_LEAVE_STAGE].is_print = false;
fz->state = ZF_FAST_LEAVE_STAGE;
}
if (is_prepare_leave_stage(fz) == STAGE_RESULT_OK && fz->stage_info[ZF_PREPARE_LEAVE_STAGE].is_print == false)
{
ET_WARN("prepare leave stage");
fz->stage_info[ZF_PREPARE_LEAVE_STAGE].is_print = true;
fz->stage_info[ZF_FAST_LEAVE_STAGE].is_print = false;
fz->stage_info[ZF_WAVE_LEAVE_STAGE].is_print = false;
fz->state = ZF_PREPARE_LEAVE_STAGE;
}
if (is_wave_leave_stage(fz) == STAGE_RESULT_OK && fz->stage_info[ZF_WAVE_LEAVE_STAGE].is_print == false)
{
ET_WARN("small 50hz leave stage");
fz->stage_info[ZF_WAVE_LEAVE_STAGE].is_print = true;
fz->stage_info[ZF_PREPARE_LEAVE_STAGE].is_print = false;
fz->state = ZF_WAVE_LEAVE_STAGE;
}
}
int find_zero_process(pm_meas_t *fz, const uint16_t *adc_buf, uint32_t len)
{
pm_fsm_t *fsm = get_pm_fsm();
if (!fsm)
{
ET_ERR("fsm is not init");
return 0;
}
double rpm = 0.0;
//static uint8_t wait_time = 0;
float angle;
fz->p50 = et_goertzel_compute_power_u16(&fz->cfg, adc_buf);
fz->slope = etk_slope_update_slope(&fz->slope_handle, fz->p50);
// if (wait_time++ < BUFFER_SIZE)
// return 0;
angle = get_real_time_angle_degree();
fz->freq = et_zero_cross_freq_u16(&fz->zc, adc_buf, len, sampling_rate, DC_OFFSET_ADC_VALUE);
fz->trend = get_data_trend(&fz->trend_handle, fz->p50);
calculate_stage(fz);
ET_DBG("[%d]tr:%d, s:%.2f, p50:%.2f, freq:%.2f, angle:%.2f", fz->state, fz->trend, fz->slope, fz->p50, fz->freq,
angle);
if (fsm->fz_fsm_state)
{
return 0;
}
if (fz->calib_info[0].start_time == 0)
{
fz->calib_info[0].start_time = rt_tick_get_millisecond();
fz->calib_info[0].angle = 0;
fz->calib_info[0].target_angle = 0;
}
switch (fz->state)
{
case ZF_UNKNOWN_STAGE:
case ZF_SATURATED_STAGE:
fz->target_dir = MEASURE_DEF_DIR;
sv630p_speed_update(&sv630p_handle, FZ_UNKNOWN_STAGE_RPM * fz->target_dir);
break;
case ZF_WAVE_ENTER_STAGE:
sv630p_speed_update(&sv630p_handle, FZ_WAVE_ENTER_STAGE_RPM * fz->target_dir);
break;
case ZF_PREPARE_ENTER_STAGE:
sv630p_speed_update(&sv630p_handle, FZ_PREPARE_STAGE_RPM * fz->target_dir);
break;
case ZF_FAST_CLOSE_STAGE:
// 优化方向反转逻辑,减少过零点超调
if (get_data_trend(&fz->trend_handle, fz->p50) == TREND_INCREASE && fz->slope > 200 && fz->p50 < 40000)
{
fz->target_dir *= -1;
etk_position_pid_reset(&fz->pid); // 重置PID避免累积误差影响
trend_handle_reset(&fz->trend_handle); // 防止很快的重复进来
ET_WARN("passed the zero point and rotated in the opposite direction (slope=%.1f, p50=%.0f)", fz->slope,
fz->p50);
}
if (fz->p50 < ZERO_POS_THRESHOLD)
{
fz->zero_stable_count++;
if (fz->zero_stable_count > ZC_COUNT_STABLE_TIMES || fz->p50 < 0.03)
{
fz->state = ZF_ZERO_STAGE;
fz->calib_info[fz->calib_cnt].m_dir = sv630p_get_last_rpm(&sv630p_handle) > 0 ? 1 : -1;
sv630p_speed_mode_stop(&sv630p_handle);
uint8_t index = fz->calib_cnt % FZ_CALI_MAX_COUNT;
fz->calib_info[index].angle = angle;
fz->calib_info[index].end_time = rt_tick_get_millisecond();
fz->calib_info[index].power = fz->p50;
ET_INFO("update END calib index:%d", index);
ET_WARN("Find zero done:%.3f", angle);
fz->calib_cnt++;
etk_position_pid_reset(&fz->pid);
fz->target_dir = MEASURE_DEF_DIR;
}
}
else
{
fz->zero_stable_count = 0;
#if 1
// 根据p50值大小采用三级控制策略优化不同阶段的控制效果
if (fz->p50 > 40000)
{
rpm = 850.0f;
rpm = rpm * (fz->target_dir);
}
else if (fz->p50 > 10000)
{
rpm = etk_position_pid_update(&fz->pid, fz->p50, 0.535, 0.7f);
rpm = rpm * fz->target_dir;
}
else
#endif
{
// p50较小时使用保守PID控制降低微分滤波系数以提高稳定性
rpm = etk_position_pid_update(&fz->pid, fz->p50, 0.535, 0.9f);
if (rpm > 350.0f)
rpm = 350.0f;
if (rpm < -350.0f)
rpm = -350.0f;
rpm = rpm * fz->target_dir;
}
if (fabs(rpm) < FZ_PRECISE_ADJUST_MIN_RPM)
{
rpm = (rpm >= 0) ? FZ_PRECISE_ADJUST_MIN_RPM : -FZ_PRECISE_ADJUST_MIN_RPM;
}
ET_DBG("rpm:%.2f", rpm);
sv630p_speed_update(&sv630p_handle, rpm);
}
break;
case ZF_ZERO_STAGE:
if (fsm->meas_mode == PM_MEAS_TEST_Mode && fz->calib_cnt < FZ_CALI_MAX_COUNT)
{
fz->calib_info[fz->calib_cnt].start_time = rt_tick_get_millisecond();
accuracy_auto_measurement_once(fz, angle);
}
else
{
sv630p_speed_mode_stop(&sv630p_handle);
measurement_info_print(fz);
fsm->fz_fsm_state = true; // test use
fz->calib_cnt = 0;
fsm->meas_mode = PM_MEAS_NORMAL_Mode;
fz->target_dir = MEASURE_DEF_DIR;
//ET_WARN("Mesaure time:%d ms", rt_tick_get_millisecond() - fz->calib_start_time);
}
break;
case ZF_FAST_LEAVE_STAGE:
fz->target_dir *= -1;
fz->state = ZF_FAST_CLOSE_STAGE;
sv630p_speed_update(&sv630p_handle, FZ_FAST_CLOSE_STAGE_RPM * fz->target_dir);
break;
case ZF_PREPARE_LEAVE_STAGE:
goto_prepare_leave_stage(fz, angle);
fz->state = ZF_PREPARE_ENTER_STAGE;
break;
case ZF_WAVE_LEAVE_STAGE:
fz->target_dir *= -1;
fz->state = ZF_WAVE_ENTER_STAGE;
break;
case ZF_STATE_MAX:
break;
default:
ET_INFO("not process state %d", fz->state);
break;
}
return 0;
}
int pm_fsm_init(pm_fsm_t *fsm)
{
pm_fsm = fsm;
return 0;
}