硬汉嵌入式论坛

 找回密码
 立即注册
查看: 122|回复: 5
收起左侧

最近在做一个检测50hz正弦波的有功功率和功率因数,结果数据最后一直无规律跳变

[复制链接]

1

主题

3

回帖

6

积分

新手上路

积分
6
发表于 3 天前 | 显示全部楼层 |阅读模式
如题,空载时我就发现我的有功功率会大幅度跳变,但是频率因数还好,就没放在心上,结果接上信号就甚至开始正负跳变,百思不得其解,希望有大佬相助

我用的是f103c8t6所以受限于性能我就只设置了采样点为256,又因为之前移植dsp库没有成功我就直接使用了自制的fft函数
我的采样频率设置的是1kHz
代码如下

#include "main.h"
#include "adc.h"
#include "dma.h"
#include "i2c.h"
#include "tim.h"
#include "gpio.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "oled.h"
#include <math.h>
#include <stdlib.h>
#include <string.h>

/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
typedef struct {
    float real;
    float imag;
} compx;

compx EE(compx b1, compx b2) {
    compx b3;
    b3.real = b1.real * b2.real - b1.imag * b2.imag;
    b3.imag = b1.real * b2.imag + b1.imag * b2.real;
    return b3;
}

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
#define N 256
#define FS 1000.0f  //采样频率
#define F_BASE 50.0f//被采样频率基波
// 电压互感器(PT)参数
#define PT_RATIO        (226.0f / 1.0f)  // 变比(例如:220V/0.1V)
#define PT_BURDEN_RES   (1.0f)        // 负载电阻(Ω)

// 电流互感器(CT)参数
#define CT_RATIO        (226.0f / 0.07f)  // 变比(例如:100A/0.1A)
#define CT_BURDEN_RES   (1.0f)          // 负载电阻(Ω)

// ADC参数
#define ADC_RESOLUTION  (4095.0f)        // ADC分辨率(12位ADC)
#define ADC_REF_VOLT    (3.3f)           // ADC参考电压(V)
#define ADC_CHANNELS      2           // 通道数量
#define ADC_BUFFER_SIZE N     //定义ADC缓冲区大小宏
/* 相位补偿参数 */
#define PHASE_COMPENSATION_DEG  30.0f  // 补偿角度(单位:度),根据实际测量填写
#define DEG_TO_RAD(deg)         (deg * M_PI / 180.0f)  // 角度转弧度
/* 偏置 */
#define ADC_VOLTAGE_BIAS 1.200f
#define ADC_CURRENT_BIAS 1.200f
/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
uint16_t v_raw[N], i_raw[N];       // ADC原始数据
uint16_t v_buffer[N], i_buffer[N];       // ADC原始数据
compx v_fft[N], i_fft[N];          // FFT输入数据(复数形式)
volatile uint8_t new_data_ready = 0;  // 标志新数据是否准备好
/* 双缓冲区定义 */
uint16_t adc_buffer1[ADC_CHANNELS * ADC_BUFFER_SIZE];
uint16_t adc_buffer2[ADC_CHANNELS * ADC_BUFFER_SIZE];

/* 缓冲区状态标志 */
volatile uint8_t buffer_index = 0;      // 当前活动缓冲区索引
volatile uint8_t buffer_ready[2] = {0}; // 缓冲区就绪标志
float power_params[2] = {0};            // 功率参数数组

void SystemClock_Config(void);
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc) {
    if (hadc->Instance == ADC1) {
        buffer_index = (buffer_index + 1) % 2;  // 循环切换缓冲区
        buffer_ready[buffer_index] = 1;         // 标记当前缓冲区就绪
    }
}



void FFT(compx *xin, int Num);
float adc_to_voltage(uint16_t adc_value);
float adc_to_current(uint16_t adc_value);
void apply_hanning_window(float *data) ;
int calculate_power_to_array(compx *v_fft, compx *i_fft, int Num, float fs, float f_base, float result[2]);
int main(void)
{

  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_ADC1_Init();
  MX_I2C1_Init();
  MX_TIM3_Init();
  /* USER CODE BEGIN 2 */
  OLED_Init();
  HAL_ADCEx_Calibration_Start(&hadc1);
  if (HAL_ADC_Start_DMA(&hadc1, (uint32_t*)adc_buffer1,
                        ADC_CHANNELS * ADC_BUFFER_SIZE) != HAL_OK)
  {
      Error_Handler();
  }

  HAL_TIM_Base_Start(&htim3);  // 启动定时器触发ADC
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
           if (buffer_ready[0] || buffer_ready[1])
           {
                uint16_t *current_buffer = (buffer_index == 0) ? adc_buffer2 : adc_buffer1;

                // 在主循环的数据提取阶段修改
                float v_time[N], i_time[N];  // 时域电压/电流缓冲区

                for (int i = 0; i < N; i++)
                {
                    // 1. 转换为真实电压/电流(含偏置处理)
                    v_time = adc_to_voltage(current_buffer[i*2]);
                    i_time = adc_to_current(current_buffer[i*2+1]);

                    // 2. 应用汉宁窗(仅对时域实部加窗)
                    float hanning = 0.5f * (1 - cosf(2 * M_PI * i / (N - 1)));
                    v_time *= hanning;
                    i_time *= hanning;

                    // 3. 赋值给FFT输入(虚部保持0)
                    v_fft.real = v_time;
                    v_fft.imag = 0;
                    i_fft.real = i_time;
                    i_fft.imag = 0;
                }




                // 执行FFT
                FFT(v_fft, N);
                FFT(i_fft, N);

                // 计算功率
                if (calculate_power_to_array(v_fft,
                                                                     i_fft,
                                                                                 N,
                                                                                 FS,
                                                                                 F_BASE,
                                                                                 power_params) == 0)
                {
                        OLED_NewFrame();
                        OLED_ShowData(0, 0,&font16x16,OLED_COLOR_NORMAL, "P:%.3f",power_params[0] );
                        OLED_ShowData(0, 16,&font16x16,OLED_COLOR_NORMAL, "PF:%.3f",power_params[1] );
                        OLED_ShowFrame();  // OLED显示结果
                }

                buffer_ready[buffer_index ? 0 : 1] = 0;  // 标记缓冲区为已处理
            }

  }
  /* USER CODE END 3 */
}
void FFT(compx *xin, int Num)
{
    int f, m, LH, nm, i, k, j, L;
    double p, ps;
    int le, B, ip;
    float pi;
    compx w, t;

    LH = Num / 2;
    f  = Num;
    for (m=1; (f=f/2)!=1; m++)
    {
        for (L=m; L>=1; L--)
        {
            le = (int)pow(2, L);
            B  = le / 2;
            pi = 3.14159265358979323846;
            for (j=0; j<=B-1; j++)
            {
                p  = pow(2, m-L) * j;
                ps = 2*pi/Num*p;
                w.real = cos(ps);
                w.imag = -sin(ps);
                for (i=j; i<=Num-1; i=i+le)
                {
                    ip = i+B;
                    t  = xin;
                    xin.real = xin.real + xin[ip].real;
                    xin.imag = xin.imag + xin[ip].imag;
                    xin[ip].real = xin[ip].real - t.real;
                    xin[ip].imag = xin[ip].imag - t.imag;
                    xin[ip] = EE(xin[ip], w);
                }
            }
        }
    }

    nm = Num - 2;
    j  = Num / 2;
    for(i=1;i<=nm;i++)
    {
        if(i<j){t=xin[j];xin[j]=xin;xin=t;}
        k=LH;
        while(j>=k){j=j-k;k=k/2;}
        j=j+k;
    }
}


// 将ADC原始值转换为实际电压(PT侧)
float adc_to_voltage(uint16_t adc_value) {
    float voltage_raw = (adc_value / ADC_RESOLUTION) * ADC_REF_VOLT;//转换为电压互感器次边电压
    float voltage_handled=( voltage_raw -ADC_VOLTAGE_BIAS)* PT_RATIO * PT_BURDEN_RES;
    return voltage_handled ;
}

// 将ADC原始值转换为实际电流(CT侧)
float adc_to_current(uint16_t adc_value) {
    float current_v_raw = (adc_value / ADC_RESOLUTION) * ADC_REF_VOLT;//电流互感器次边电压
    float current_handled = (current_v_raw-ADC_CURRENT_BIAS)/ ( CT_RATIO * CT_BURDEN_RES);
    return current_handled;
}
// 计算有功功率和功率因数,结果存入数组
// 参数:result[0]=有功功率,result[1]=功率因数,fs:采样频率,f_base:基波频率
// 返回值:0=成功,非0=错误码
int calculate_power_to_array(compx *v_fft, compx *i_fft, int Num, float fs, float f_base, float result[2])
{
    // 输入参数校验
    if (result == NULL) {
        return -1;  // 结果数组指针为空
    }

    float freq_res = fs / Num;
    if (f_base < freq_res || f_base > fs/2) {
        result[0] = 0.0f;  // 有功功率
        result[1] = 0.0f;  // 功率因数
        return -2;  // 频率超出范围
    }


    // 计算基波索引
    int k = round(f_base * Num / fs);
    if (k >= Num/2) k = N/2 - 1;

    // 提取并归一化基波分量
    compx Vk = v_fft[k];
    compx Ik = i_fft[k];
    Vk.real /= (Num / 2.0f);
    Vk.imag /= (Num / 2.0f);
    Ik.real /= (Num / 2.0f);
    Ik.imag /= (Num / 2.0f);

    /* 相位补偿(针对电流信号) */
    float phase_shift = DEG_TO_RAD(PHASE_COMPENSATION_DEG);
    // 对电流基波进行相位旋转(超前补偿角度)
    float ik_real = Ik.real * cosf(phase_shift) - Ik.imag * sinf(phase_shift);
    float ik_imag = Ik.real * sinf(phase_shift) + Ik.imag * cosf(phase_shift);
    Ik.real = ik_real;  // 更新补偿后的电流实部
    Ik.imag = ik_imag;  // 更新补偿后的电流虚部

    // 计算相位差和功率因数
    float theta_v = atan2f(Vk.imag, Vk.real);  //电压相位
    float theta_i = atan2f(Ik.imag, Ik.real);  //电流相位补偿过的相位
    float theta = theta_v - theta_i;                   //相位差
    result[1] = cosf(theta);  // 功率因数存入数组[1]

    // 计算有效值和有功功率
    float V_rms = sqrtf(Vk.real * Vk.real + Vk.imag * Vk.imag) / sqrtf(2.0f);
    float I_rms = sqrtf(Ik.real * Ik.real + Ik.imag * Ik.imag) / sqrtf(2.0f);
    result[0] = V_rms * I_rms * result[1];  // 有功功率存入数组[0]

    return 0;  // 计算成功
}










回复

使用道具 举报

1

主题

9

回帖

12

积分

新手上路

积分
12
发表于 3 天前 | 显示全部楼层
虽然不是很清楚这个,但是如果你是监测的标准的电网的话,我的建议是可以考虑试用一下国产的计量芯片,成本大约是0.5~1.5元之间,而且有两路,只需要一个uart或者spi口即可,而且可以监测的非常的准确和省事。FAE会提供例程。
回复

使用道具 举报

1

主题

3

回帖

6

积分

新手上路

积分
6
 楼主| 发表于 前天 01:13 | 显示全部楼层
我现在通过初步的分析我觉得应该要先解决空载的时候有功功率跳变的问题,功率因数不变纯粹就是因为空载时他俩电压都差不多大,有一点怀疑是因为加窗,因为看有的老师说加窗会影响幅值
回复

使用道具 举报

7

主题

148

回帖

169

积分

初级会员

积分
169
发表于 前天 08:16 | 显示全部楼层
为什么要加窗,只为了加而加,加窗主要是谐波那块,你求功率因素连FFT都不需要,知道RMS 有效值 均方根值不,直接累加开根
回复

使用道具 举报

1

主题

3

回帖

6

积分

新手上路

积分
6
 楼主| 发表于 前天 13:39 | 显示全部楼层
mygod 发表于 2025-7-3 08:16
为什么要加窗,只为了加而加,加窗主要是谐波那块,你求功率因素连FFT都不需要,知道RMS 有效值 均方根值不 ...

我也发现这个了,但是我后面还要求thd,所以我想试试加窗来着
回复

使用道具 举报

1

主题

3

回帖

6

积分

新手上路

积分
6
 楼主| 发表于 前天 13:42 | 显示全部楼层
jiafangshi 发表于 2025-7-2 22:58
虽然不是很清楚这个,但是如果你是监测的标准的电网的话,我的建议是可以考虑试用一下国产的计量芯片,成本 ...

谢谢您的建议,这个我和队友也商量过,但是时间比较紧,所以就尽量能搞出来一点是一点
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|小黑屋|Archiver|手机版|硬汉嵌入式论坛

GMT+8, 2025-7-5 03:32 , Processed in 0.379697 second(s), 24 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2023, Tencent Cloud.

快速回复 返回顶部 返回列表