PID算法
#define LimitMax(input, max) \ { \ if (input > max) \ { \ input = max; \ } \ else if (input < -max) \ { \ input = -max; \ } \ } enum PID_MODE { PID_POSITION = 0, PID_DELTA }; typedef struct { //PID运算模式 uint8_t mode; //PID 三个基本参数 float Kp; float Ki; float Kd; float max_out; //PID最大输出 float max_iout; //PID最大积分输出 float set; //PID目标值 float fdb; //PID当前值 float out; //三项叠加输出 float Pout; //比例项输出 float Iout; //积分项输出 float Dout; //微分项输出 //微分项最近三个值 0最新 1上一次 2上上次 float Dbuf[3]; //误差项最近三个值 0最新 1上一次 2上上次 float error[3]; } pid_type_def; void PID_init(pid_type_def *pid, uint8_t mode, const float PID[3], float max_out, float max_iout) { if (pid == NULL || PID == NULL) { return; } pid->mode = mode; pid->Kp = PID[0]; pid->Ki = PID[1]; pid->Kd = PID[2]; pid->max_out = max_out; pid->max_iout = max_iout; pid->Dbuf[0] = pid->Dbuf[1] = pid->Dbuf[2] = 0.0f; pid->error[0] = pid->error[1] = pid->error[2] = pid->Pout = pid->Iout = pid->Dout = pid->out = 0.0f; } float PID_calc(pid_type_def *pid, float ref, float set) { //判断传入的PID指针不为空 if (pid == NULL) { return 0.0f; } //存放过去两次计算的误差值 pid->error[2] = pid->error[1]; pid->error[1] = pid->error[0]; //设定目标值和当前值到结构体成员 pid->set = set; pid->fdb = ref; //计算最新的误差值 pid->error[0] = set - ref; //判断PID设置的模式 if (pid->mode == PID_POSITION) { //位置式PID //比例项计算输出 pid->Pout = pid->Kp * pid->error[0]; //积分项计算输出 pid->Iout += pid->Ki * pid->error[0]; //存放过去两次计算的微分误差值 pid->Dbuf[2] = pid->Dbuf[1]; pid->Dbuf[1] = pid->Dbuf[0]; //当前误差的微分用本次误差减去上一次误差来计算 pid->Dbuf[0] = (pid->error[0] - pid->error[1]); //微分项输出 pid->Dout = pid->Kd * pid->Dbuf[0]; //对积分项进行限幅 LimitMax(pid->Iout, pid->max_iout); //叠加三个输出到总输出 pid->out = pid->Pout + pid->Iout + pid->Dout; //对总输出进行限幅 LimitMax(pid->out, pid->max_out); } else if (pid->mode == PID_DELTA) { //增量式PID //以本次误差与上次误差的差值作为比例项的输入带入计算 pid->Pout = pid->Kp * (pid->error[0] - pid->error[1]); //以本次误差作为积分项带入计算 pid->Iout = pid->Ki * pid->error[0]; //迭代微分项的数组 pid->Dbuf[2] = pid->Dbuf[1]; pid->Dbuf[1] = pid->Dbuf[0]; //以本次误差与上次误差的差值减去上次误差与上上次误差的差值作为微分项的输入带入计算 pid->Dbuf[0] = (pid->error[0] - 2.0f * pid->error[1] + pid->error[2]); pid->Dout = pid->Kd * pid->Dbuf[0]; //叠加三个项的输出作为总输出 pid->out += pid->Pout + pid->Iout + pid->Dout; //对总输出做一个先限幅 LimitMax(pid->out, pid->max_out); } return pid->out; } void PID_clear(pid_type_def *pid) { if (pid == NULL) { return; } //当前误差清零 pid->error[0] = pid->error[1] = pid->error[2] = 0.0f; //微分项清零 pid->Dbuf[0] = pid->Dbuf[1] = pid->Dbuf[2] = 0.0f; //输出清零 pid->out = pid->Pout = pid->Iout = pid->Dout = 0.0f; //目标值和当前值清零 pid->fdb = pid->set = 0.0f; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话