当前位置: 首页 > news >正文

平衡车 -- PID

🌈个人主页:羽晨同学

💫个人格言:“成为自己未来的主人~” 

PID

PID是用来改善控制系统的。

接下来,我们来完成PID控制器的具体的代码。

我们创建一个PID的结构体来长期保存PID的数据。

typedef struct
{float Kp;//比例系数float Ki;//积分项的系数float Kd;//微分项的系数float SP;//用户的设定值
}PID_TypeDef;

接下来,我们创建两个函数便于初始化PID和对PID的SP的值进行设定。

//
// @简介:对PID控制器进行初始化
// @参数 Kp - 比例系数
// @参数 Ki - 积分系数
// @参数 kd - 微分系数
//
void PID_Init(PID_TypeDef*PID,float Kp,float Ki,float Kd)
{PID->Kd = Kd;PID->Ki = Ki;PID->Kp = Kp;PID->SP = 0.0f;
}
//
// @简介:改变设定值SP
// @参数 SP - 新的设定值
//
void PID_ChangeSP(PID_TypeDef*PID,float SP)
{PID->SP = SP;
}

接下来,我们来完成另外一个函数,来完成PID的运算,由于需要用到积分项和微分项,所以,我们需要额外保存几个变量。

	uint64_t t_k_1;//t[k-1] 上次运行PID的时间float err_k_1;//err[k-1],上次运行PID时的误差float err_int_k_1;//err_int[k-1],上次运行的积分值

然后,我们在PID的初始化函数中,对于这几个变量进行初始化。

void PID_Init(PID_TypeDef*PID,float Kp,float Ki,float Kd)
{PID->Kd = Kd;PID->Ki = Ki;PID->Kp = Kp;PID->SP = 0.0f;PID->err_int_k_1 = 0.0f;PID->err_k_1 = 0.0f;PID->t_k_1 = 0.0f;
}
//
// @简介:执行一次PID运算
// @参数 FB - 反馈的值,也就是传感器采回的值
// @返回值:PID控制器计算的结果
//
float PID_Compute(PID_TypeDef*PID,float FB)
{float err = PID->SP - FB;uint64_t t_k = GetUs();float deltaT = (t_k - PID->t_k_1)*1.0e-6f;float err_dev = (err - PID->err_k_1)/deltaT;float err_int = PID->err_int_k_1 + (err + PID->err_k_1)*deltaT*0.5f;float COp = PID->Kp*err;float COi = PID->Ki*err_int;float COd = PID->Kd*err_dev;float CO = COp+COi+COd;//更新PID->err_int_k_1 = err_int;PID->err_k_1 = err;PID->t_k_1 = t_k;return CO;
}

PID测试

我们接下来,对左右电机进行一个测试。

我们创建两个struct对象,分别代表左右电机

static PID_TypeDef pid_motor_l;//左电机调速系统的PID控制器
static PID_TypeDef pid_motor_r;//右电机调速系统的PID控制器
//
// @简介:电机的初始化函数
//
void App_Motor_Init(void)
{	PID_Init(&pid_motor_l,0.5,7,0);//PI控制器PID_Init(&pid_motor_r,0.5,7,0);//PI控制器
}
//
// @简介:电机调速系统的进程函数
//
void App_Motor_Proc(void)
{PERIODIC(1)// #1. 通过编码器获取左右电机旋转的角速度float omega_l = App_Encoder_GetSpeed_L();float omega_r = App_Encoder_GetSpeed_R();// #2. 计算PID控制器的输出float ua_l = PID_Compute(&pid_motor_l,omega_l);float ua_r = PID_Compute(&pid_motor_r,omega_r);// #3. 将电压ua设置到点击两端float vbat = App_Bat_Get();float duty_l = ua_l/vbat * 100.0f;float duty_r = ua_r/vbat * 100.0f;App_Pwm_Set_L(duty_l);App_Pwm_Set_R(duty_r);}

然后我们创建两个函数,分别来控制左右电机的转速

//
// @简介:用来设置左电机的转速omega的值
//
void App_Motor_SetOmega_L(float omega)
{PID_ChangeSP(&pid_motor_l,omega);
}
//
// @简介:用来设置左电机的转速omega的值
//
void App_Motor_SetOmega_R(float omega)
{PID_ChangeSP(&pid_motor_r,omega);
}

PID改进

输出限幅

所以为了防止这种情况,我们在结构体中增加两个变量。

typedef struct
{float Kp;//比例系数float Ki;//积分项的系数float Kd;//微分项的系数float SP;//用户的设定值uint64_t t_k_1;//t[k-1] 上次运行PID的时间float err_k_1;//err[k-1],上次运行PID时的误差float err_int_k_1;//err_int[k-1],上次运行的积分值float UpperLimit;//上限值float LowerLimit;//下限值
}PID_TypeDef;
//
// @简介:对PID控制器进行初始化
// @参数 Kp - 比例系数
// @参数 Ki - 积分系数
// @参数 kd - 微分系数
//
void PID_Init(PID_TypeDef*PID,float Kp,float Ki,float Kd)
{PID->Kd = Kd;PID->Ki = Ki;PID->Kp = Kp;PID->SP = 0.0f;PID->err_int_k_1 = 0.0f;PID->err_k_1 = 0.0f;PID->t_k_1 = 0.0f;PID->UpperLimit = +3.4e+38f;PID->LowerLimit = -3.4e+38f;
}
//
// @简介:设置PID控制器门限
// @参数:Upper - 上限值
// @参数:Lower - 下限值
//
void PID_LimitConfig(PID_TypeDef*PID,float Upper,float Lower)
{PID -> LowerLimit = Lower;PID -> UpperLimit = Upper;
}

这样子,我们就可以在点击初始化函数中,配置PID的上下限了

//
// @简介:电机的初始化函数
//
void App_Motor_Init(void)
{	PID_Init(&pid_motor_l,0.5,7,0);//PI控制器PID_LimitConfig(&pid_motor_l,+8.4f,-8.4f);PID_Init(&pid_motor_r,0.5,7,0);//PI控制器PID_LimitConfig(&pid_motor_r,+8.4f,-8.4f);
}

这样,我们就对其上下限进行了限制。

//
// @简介:执行一次PID运算
// @参数 FB - 反馈的值,也就是传感器采回的值
// @返回值:PID控制器计算的结果
//
float PID_Compute(PID_TypeDef*PID,float FB)
{float err = PID->SP - FB;uint64_t t_k = GetUs();float deltaT = (t_k - PID->t_k_1)*1.0e-6f;float err_dev = (err - PID->err_k_1)/deltaT;float err_int = PID->err_int_k_1 + (err + PID->err_k_1)*deltaT*0.5f;float COp = PID->Kp*err;float COi = PID->Ki*err_int;float COd = PID->Kd*err_dev;float CO = COp+COi+COd;//更新PID->err_int_k_1 = err_int;PID->err_k_1 = err;PID->t_k_1 = t_k;if(CO > PID->UpperLimit) CO = PID->UpperLimit;if(CO<PID->LowerLimit) CO = PID->LowerLimit;return CO;
}

这样就实现了输出限幅

积分限幅

	if(PID->err_int_k_1>PID->UpperLimit) PID->err_int_k_1 = PID->UpperLimit;if(PID->err_int_k_1<PID->LowerLimit)PID->err_int_k_1 = PID->LowerLimit;

PID复位

//
// @简介:对PID控制器进行复位
//
void PID_Reset(PID_TypeDef*PID)
{PID->err_int_k_1 = 0.0f;PID->err_k_1 = 0.0f;PID->t_k_1 = 0;
}
//
// @简介:开关电机
// @参数 on 控制电机的开关,0 - 关闭,非零 - 开启
//
void App_Motor_Cmd(uint8_t on)
{App_Pwm_Cmd(on);//在开关电机的同时需要对PID控制器进行复位PID_Reset(&pid_motor_l);PID_Reset(&pid_motor_r);
}
//
// @简介: 按键回调函数
//
static void OnUserKey_Clicked(uint8_t clicks)
{if(clicks == 1){pwm_on^=1;App_Motor_Cmd(pwm_on);}
}

首次运行时忽略积分和微分项

//
// @简介:执行一次PID运算
// @参数 FB - 反馈的值,也就是传感器采回的值
// @返回值:PID控制器计算的结果
//
float PID_Compute(PID_TypeDef*PID,float FB)
{float err = PID->SP - FB;uint64_t t_k = GetUs();float deltaT = (t_k - PID->t_k_1)*1.0e-6f;float err_dev = 0.0f;float err_int = 0.0f;if(PID->t_k_1 !=0){err_dev = (err - PID->err_k_1)/deltaT;err_int = PID->err_int_k_1 + (err + PID->err_k_1)*deltaT*0.5f;}float COp = PID->Kp*err;float COi = PID->Ki*err_int;float COd = PID->Kd*err_dev;float CO = COp+COi+COd;//更新PID->err_int_k_1 = err_int;PID->err_k_1 = err;PID->t_k_1 = t_k;if(CO > PID->UpperLimit) CO = PID->UpperLimit;if(CO<PID->LowerLimit) CO = PID->LowerLimit;if(PID->err_int_k_1>PID->UpperLimit) PID->err_int_k_1 = PID->UpperLimit;if(PID->err_int_k_1<PID->LowerLimit)PID->err_int_k_1 = PID->LowerLimit;return CO;
}


文章转载自:

http://xzAdRhD6.pftjj.cn
http://SNcOhR9N.pftjj.cn
http://krVZEVTl.pftjj.cn
http://KoPRW4Kf.pftjj.cn
http://BKs5Yym7.pftjj.cn
http://UfHHSQLp.pftjj.cn
http://WhL3ncUV.pftjj.cn
http://oqNxmCNv.pftjj.cn
http://hporkQj2.pftjj.cn
http://hfnvhfwT.pftjj.cn
http://xO7JfkeY.pftjj.cn
http://NAAurtlC.pftjj.cn
http://qXMosq27.pftjj.cn
http://hmXDvV9D.pftjj.cn
http://gOKkANjg.pftjj.cn
http://9QmtlumV.pftjj.cn
http://MhpnuuGT.pftjj.cn
http://uRK7rTgv.pftjj.cn
http://TCQhugNP.pftjj.cn
http://KzDIiw93.pftjj.cn
http://ugYiQacN.pftjj.cn
http://GUqVr71g.pftjj.cn
http://LZATfKlu.pftjj.cn
http://6SzNjwjw.pftjj.cn
http://O1NMvnM5.pftjj.cn
http://GR3MddVY.pftjj.cn
http://ITYHYDdS.pftjj.cn
http://UDwlseRv.pftjj.cn
http://FNti0ZDC.pftjj.cn
http://TOSk6OCn.pftjj.cn
http://www.dtcms.com/a/375856.html

相关文章:

  • 【ComfyUI】Flux Krea 微调完美真实照片生成
  • dp类相关问题(1):区间dp
  • TensorFlow 2.x 核心 API 与模型构建:从入门到实践
  • 华清远见25072班网络编程学习day2
  • 【论文写作】--网络与信息安全顶刊顶会
  • 【人工智能99问】如何基于QWen3进行LoRA微调?(38/99)
  • JAVA Predicate
  • 自动驾驶中的传感器技术41——Radar(2)
  • Netty HandlerContext 和 Pipeline
  • Stuns in Singapore!中新赛克盛大亮相ISS World Asia 2025
  • 开始 ComfyUI 的 AI 绘图之旅-LoRA(五)
  • 字符函数和字符串函数 last part
  • win安装多个mysql,免安装mysql
  • 开源项目_强化学习股票预测
  • Shell 脚本基础:从语法到实战全解析
  • Nginx如何部署HTTP/3
  • 解一元三次方程
  • A股大盘数据-20250909分析
  • 05-Redis 命令行客户端(redis-cli)实操指南:从连接到返回值解析
  • shell函数+数组+运算+符号+交互
  • 群晖Lucky套件高级玩法-——更新证书同步更新群晖自带证书
  • 照明控制设备工程量计算 -图形识别超方便
  • Matlab通过FFT快速傅里叶变换提取频率
  • iis 高可用
  • 有趣的数学 贝塞尔曲线和毕加索
  • 基于STM32的智能宠物小屋设计
  • STM32之RS485与ModBus详解
  • DCDC输出
  • GitHub 项目提交完整流程(含常见问题与解决办法)
  • Day39 SQLite数据库操作与文本数据导入