|
如题,空载时我就发现我的有功功率会大幅度跳变,但是频率因数还好,就没放在心上,结果接上信号就甚至开始正负跳变,百思不得其解,希望有大佬相助
我用的是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; // 计算成功
}
|
|