这个问题我是在运行自己写的foc闭环速度程序时碰到的。
首先,对传感器获取的速度进行滤波的重要性这里不过多叙述。如果你的foc程序里没有添加低通滤波器来对传感器获取的角度处理得出的速度进行滤波的话,那么最终你程序运行起来的效果大概是,无刷电机虽然有照你给的目标速度值进行转动,但是会产生非常大的噪声,并且电机的转动非常卡顿,并且电机抖动的非常厉害(厉害到桌子都在晃的那种),只有对速度进行滤波,无刷电机才能平滑且无声的转动(当然,加了svpwm会更加平滑)。
下面先给出我的lowpass_fillter.c文件:
#include "lowpass_fillter.h"
#include "main.h"
float LowPass_Fillter(lowpass_fillter *name,float x)
{
unsigned long timestamp = micros();
float dt = (timestamp - name->timestamp_prev)*1e-6f;
if (dt < 0.0f ) dt = 1e-3f;
else if(dt > 0.3f) {
name->y_prev = x;
name->timestamp_prev = timestamp;
return x;
}
float alpha = name->Tf/(name->Tf + dt);
float y = alpha*name->y_prev + (1.0f - alpha)*x;
name->y_prev = y;
name->timestamp_prev = timestamp;
return y;
}
然后是lowpass_fillter.h文件:
#ifndef __lowpass_fillter_H
#define __lowpass_fillter_H
typedef struct LowPassFillter{
float Tf;
float timestamp_prev;
float y_prev;
}lowpass_fillter;
float LowPass_Fillter(lowpass_fillter *name,float x);
#endif
这里提醒两个注意事项:
第一,不要把timestamp_prev和y_prev定义在lowpass_fillter.c文件中当成全局变量来使用
如果你这么做的话,对于foc闭环速度程序的话还行,因为你只需要对速度进行滤波,也就是你的滤波对象只有一个。如果到后面需要引入电流闭环的话,这样的程序一定会出大问题。因为当电流也需要滤波,速度也需要滤波,两个滤波函数是一起共用timestamp_prev和y_prev这两个变量的,如果这两个滤波器的函数不是同时执行的话,比如速度滤波函数刚执行完后,更新了timestamp_prev,而过了一会,又要用到电流滤波器的函数,这个函数就会调用刚更新的timestamp_prev,而不是之前的timestamp_prev,这样程序不就崩溃了吗?所以,一定要把timestamp_prev和y_prev这两个变量放入结构体中进行定义,这样就会各自调用各自结构体里面的值,不会互相干扰了。
第二,不要在LowPass_Fillter(lowpass_fillter *name,float x)函数中进行结构体直接赋值,而是要通过指针进行赋值
什么意思呢?就是LowPass_Fillter(lowpass_fillter *name,float x)这个函数中你要传入的是*name而不是name,传入的是name的话,在函数里面进行调用就是name.Tf而不是name->Tf,像Tf是一个定值还没有问题,但像结构体里的y_prev,这个值可是需要随时变化的,如果你用直接赋值name.y_prev = y;而不是name->y_prev = y;,这样就会导致下一次进入这个函数时,y_prev还是初始值,因为上一次的值在上一轮函数执行完后就失效了,所以必须采用指针赋值。
其实,PID文件和lowpass_fillter文件是一个意思的,接下来给出我的PID.C和PID.H:
#include "PID.h"
#include "main.h"
#define _constrain(amt,low,high) ((amt)<(low)?(low):((amt)>(high)?(high):(amt)))
float PID(PID_Struct *x,float error)
{
uint32_t now_time = micros();
float Ts = (now_time-x->last_time)*1e-6f;
if(Ts <= 0 || Ts > 0.5f) Ts = 1e-3f;
x->last_time =now_time;
float P = x->kp*error;
x->I+=x->ki*Ts*(error+x->last_error)*0.5f;
x->I = _constrain(x->I,-x->lim_ki,x->lim_ki);
float D = x->kd*(error-x->last_error)/Ts;
x->last_error = error;
float output = P + x->I + D;
output = _constrain(output,-x->lim_output,x->lim_output);
float output_rate = (output - x->last_output)/Ts;
if (output_rate > x->output_ramp)
output = x->last_output + x->output_ramp*Ts;
else if (output_rate < -x->output_ramp)
output = x->last_output - x->output_ramp*Ts;
x->last_output = output;
return output;
}
#ifndef __PID_H
#define __PID_H
typedef struct PID_HANDLE
{
float kp;
float ki;
float kd;
float I;
float lim_output; // 输出限幅使能
float output_ramp; //输出值变化速度限幅
float lim_ki; // 误差积分限幅
float last_time;
float last_error;
float last_output;
}PID_Struct;
float PID(PID_Struct *x,float error);
#endif