polarimeter_software/User/app/pm_meas.c
2025-09-30 10:37:23 +08:00

559 lines
20 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* @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;
}