1.什么自动控制系统是
在无人直接参与下可使生产过程按预定程序进行的控制系统。
一个控制系统包括控制器、传感器、变送器、执行机构、输入输出接口。 具体过程是:控制器的输出经过输出接口、执行机构,加到被控系统上;控制系统的被控量,经过传感器、变送器,通过输入接口送到控制器。
自动控制系统可分为开环控制系统和闭环控制系统。
开环控制系统(open-loop control system) :输出对于输入没有影响。在开环控制系统中,系统输出只受输入的控制,控制精度和抑制干扰的特性都比较差。开环控制系统中,基于按时序进行逻辑控制的称为顺序控制系统;由顺序控制装置、检测元件、执行机构和被控工业对象所组成。
闭环控制系统(closed-loop control system): 输出会通过反馈电路返送回来影响输入,形成一个或多个闭环的控制系统。把控制系统输出量的一部分或全部,通过一定方法和装置反送回系统的输入端,然后将反馈信息与原输入信息进行比较,再将比较的结果施加于系统进行控制,避免系统偏离预定目标。闭环控制系统利用的是负反馈。
2.什么是PID控制
将偏差的比例(Proportion)、积分(Integral)和微分(Differential)通过线性组合构成控制量,用这一控制量对被控对象进行控制,这样的控制器称PID控制器。
当被控对象的结构和参数不能完全掌握,或得不到精确的数学模型时,控制理论的其它技术难以采用时,系统控制器的结构和参数必须依靠经验和现场调试来确定,这时应用PID控制技术最为方便。即当我们不完全了解一个系统和被控对象,或不能通过有效的测量手段来获得系统参数时,最适合用PID控制技术。 PID控制,实际中也有PI和PD控制。PID控制器就是根据系统的误差,利用比例、积分、微分计算出控制量进行控制的。
列子:以电机转速调节为例。调节PWM占空比可以实现电机调速,编码器可以检测当前电机转速。那现在我需要控制电机转速为3圈/s(目标速度),并且是控制在不同负载时电机都运行在这个速度。 以下开始电机处于停止状态此时PWM占空比为0,开始时候我们不知道给多少占空比合适,就给占空比为45%,电机旋转,通过编码器我们得到当前的速度只有2.5圈/s(实际速度)(数值是假设的,可能跟实际有所偏差),此时我们需要加大占空比,给到50%,编码器得到速度才2.8圈/s;没办法,我们还需要再加占空比,改为55%,编码器得到3.1圈/s,给大了,再调,改为54%,这次刚刚好了,编码器速度在3圈/s左右变动,勉强满足要求。 如果现在给电机加了一些负载,本来占空比54%有3圈/s的速度的,现在下降为2.3圈/s了,现在为达到3圈/s速度,又要类似上面的尝试修改过程,改为60%,只有2.5圈/s,改为80%,超了,到了3.2圈/s,改为77%,差一点点,改为78%,效果还不错。 现在,又增加或者减少一点负载,又要运行类似上面的占空比调整过程… … 我们会思考,有没有办法编个程序让STM32实现上面的电机控制过程呢??
解析:不管增加负载还是减小负载,让程序自己调整占空比从而使电机转速控制在目标速度。用一个数学公式。该公式有一个变量(当前速度与目标速度的差值)。而公式的结果是占空比。所以PID算法就是解决这个问题的数学公式。有时我们不仅希望在PID算法程序中实现占空比的自动调节,并且希望在很短时间内快速调节到目标值。一般PID算法要实现:稳、准、快。
1.P(比列运算)
比例控制是一种最简单的控制方式。其控制器的输出与输入信号误差成比例关系。当仅有比例控制时系统输出存在稳态误差(Steady-state error),假设输入误差为0,那么输出也为0,显然是不行的。 比例控制考虑当前误差,误差值和一个正值的常数Kp(比例系数)相乘。Kp只是在控制器的输出和误差成比例的时候成立。注意在误差是0的时候,控制器的输出也是0。 比例控制的输出如下:

若比例增益大,在相同误差量下,会有较大的输出,但若比例增益太大,会使系统不稳定。 相反的,若比例增益小,若在相同误差量下,其输出较小,因此控制器会较不敏感的。若比例增益太小,当有干扰出现时,其控制信号可能不够大,无法修正干扰的影响。
比列运算是存在稳态误差(在比列控制中,经过一定时间后误差稳定在一定值),稳态误差和比例增益(Kp)成正比,和受控系统本身的增益(Pout)成反比。通过调节KP来达到目标值。
下面是不同KP的曲线:

怎么消除稳态误差?
1.加入偏置(在Pout = Kp e(t)后面加一个偏置值。 2.引入积分控制
2.I(积分控制)
在积分控制中,控制器的输出与输入误差信号的积分成正比关系。对一个自动控制系统,如果在进入稳态后存在稳态误差,则称这个控制系统是有稳态误差的或简称有差系统(System with Steady-state Error)。为了消除稳态误差,在控制器中必须引入“积分项”。积分项对误差取决于时间的积分,随着时间的增加,积分项会增大。这样,即便误差很小,积分项也会随着时间的增加而加大,它推动控制器的输出增大使稳态误差进一步减小,直到等于零。因此,比例+积分(PI)控制器,可以使系统在进入稳态后无稳态误差。

积分控制考虑过去误差,将过去一段时间误差值和(误差和)乘以一个正值的常数Ki。Ki从过去的平均误差值来找到系统的输出结果和预定值的平均误差。一个简单的比例系统会震荡,会在预定值的附近来回变化,因为系统无法消除多余的纠正。通过加上累计的平均误差值,平均系统误差值就会渐渐减少。所以,最终这个PID回路系统会在设定值稳定下来。积分控制的输出如下:

积分控制会加速系统趋近设定值的过程,并且消除纯比例控制器会出现的稳态误差。 积分增益越大,趋近设定值的速度越快,不过因为积分控制会累计过去所有的误差,可能会使输出出现过冲的情形。
3.D(微分控制)
在微分控制中,控制器的输出与输入误差信号的微分(即误差的变化率)成正比关系。
在控制器中仅引入“比例”项往往是不够的,比例项的作用仅是放大误差的幅值,而目前需要增加的是“微分项”,它能预测误差变化的趋势。在误差没出现之前就抑制住。微分考虑的是未来的误差。这样,具有比例+微分的控制器,就能够提前使抑制误差的控制作用等于零,甚至为负值,从而避免了被控量的严重超调。所以对有较大惯性或滞后的被控对象,比例+微分(PD)控制器能改善系统在调节过程中的动态特性。

微分控制考虑将来误差,计算误差的一阶导数,并和一个正值的常数Kd相乘。 这个导数的控制会对系统的改变作出反应。导数的结果越大(说明系统误差变化率大),那么控制系统就对输出结果作出更快速的反应。这个Kd参数也是PID被称为可预测的控制器的原因。 Kd参数对减少控制器短期的改变很有帮助。 微分控制的输出如下:

微分控制可以提升整定时间及系统稳定性。不过因为纯微分器不是因果系统,因此在PID系统实现时,一般会为微分控制加上一个低通滤波以限制高频增益及噪声。 实际上较少用到微分控制,估计PID控制器中只有约20%有用到微分控制。使用PID算法,P是最基本的,可以衍生出来的算法有:P、PI、PD。
总结:P(比列)考虑的是当前误差(存在稳态误差)。I(积分)考虑的是过去误差(对过去误差的积累随着时间消除稳态误差)。D(微分)考虑的是未来误差(对未来误差的预测从而提前去消除)。当有较大惯性组件(环节)或有滞后(delay)组件时才考虑用。
模拟PID框图:

r(t)是给定值,y(t)是系统的实际输出值,给定值与实际输出值构成控制偏差e(t)。


上面是模拟PID的公式:
Ki = KP/ Ti Kd = KP / Td
比例系数𝐾𝑝越大,控制作用越强,则过渡过程越快,控制过程的静态偏差也就越小; 但是越大,也越容易产生振荡,破坏系统的稳定性。 积分时间𝑇𝑖越大,积分的积累作用越弱,这时系统在过渡时不会产生振荡;增大积分时间会减慢静态误差的消除过程,消除偏差所需的时间也较长,但可以减少超调量,提高系统的稳定性。当𝑇𝑖较小时,则积分的作用较强,这时系统过渡时间中有可能产生振荡,不过消除偏差所需的时间较短。
𝑇𝑑越大时,则它抑制偏差e(t)变化的作用越强;𝑇𝑑越小时,则它反抗偏差e(t)变化的作用越弱。微分环节的作用是阻止偏差的变化。它是根据偏差的变化趋势(变化速度)进行控制。偏差变化的越快,微分控制器的输出就越大,并能在偏差值变大之前进行修正。但微分的作用对输入信号的噪声很敏感,对那些噪声较大的系统一般不用微分,或在微分起作用之前先对输入信号进行滤波。

而如果要在程序中实现不能用模拟的PID公式,需要转换成数字PID(由于计算机控制是一种采样控制,它只能根据采样时刻的偏差计算控制量,而不能像模拟控制那样连续输出控制量,进行连续控制)对模拟PID进行离散化处理转换成数字PID。(离散化相当于ADC的采样处理)
数字PID分为:位置PID与增量PID。
1.位置PID
数字系统中,离散化处理的方法为:以T作为采样周期,k作为采样序号,则离散采样时间kT对应着连续时间t,用矩形法数值积分近似代替积分,用一阶后向差分近似代替微分。

上式中,为了表示的方便,将类似于e(kT)简化成ek等,然后代入模拟PID公式得到数字PID:

但是需要注意的是Ki与Kd跟模拟PID不同。
ki = KP* T/ Ti Kd = KP * Td / T
如果采样周期足够小(采样频率大),则数字PID公式的近似计算可以获得足够精确的结果,离散控制过程与连续过程十分接近。
数字位置式PID由于全量输出(其结果直接控制被控制量),所以每次输出均与过去状态有关,计算时要对ek进行累加,工作量大;并且,因为计算机输出的uk对应的是执行机构的实际位置,如果计算机出现故障,输出的uk将大幅度变化,会引起执行机构的大幅度变化,有可能因此造成严重的生产事故。引出数字增量式PID。
打个比方来说明数字位置式PID与数字增量式PID。如果我的目标值需要60%的占空比。那么直接用改成60%的占空比。如果是增量式当前没达到目标值的占空比是50%,那么我要达到60%占空比目标值。直接在50%占空比上加10%来达到目标值。
2.增量式PID
增量式PID指数字控制器的输出只是控制量的增量∆uk。当执行机构需要的控制量是增量,而不是位置量的绝对数值时,可以使用增量式PID控制算法进行控制.
增量式PID控制算法可以通过位置式PID公式推导出。由位置式PID公式可以得到控制器的第k-1个采样时刻的输出值为
用K时刻的位置式减去K-1时刻位置式。
∆uk = uk−uk−1 得到如下公式:
由增量式PID公式可以看出,如果计算机控制系统采用恒定的采样周期T,一旦确定A、B、C,只要使用前后三次测量的偏差值(ek,ek-1,ek-2),就可以求出控制量。
而在代码中简化为: KP*ek + Ki*ek-1 + Kd*ek-2
而位置式PID控制算法也可以通过增量式控制算法推出递推计算公式:

总结:以上就是数字PID的介绍以及为什么要引入PID算法,以及PID参数的整定到达目标值的快速方法。直流有刷电机的位置(控制转多少圈),速度(每分钟转多少圈或每秒转多少圈)。
3.学习列子:
1.速度环-----增量式PID

有刷电机带有编码器,需要一个定时器做编码器模式。用一个高级定时器产生PWM并且是互补的。用PWM控制电机需要根据电机驱动板的硬件电路图。
编码器.h
#ifndef __BSP_ENCODER_H__ #define __BSP_ENCODER_H__ #define ENCODER_TIMx TIM3 #define ENCODER_TIM_RCC_CLK_ENABLE() __HAL_RCC_TIM3_CLK_ENABLE() #define ENCODER_TIM_RCC_CLK_DISABLE() __HAL_RCC_TIM3_CLK_DISABLE() #define ENCODER_TIM_GPIO_CLK_ENABLE() __HAL_RCC_GPIOC_CLK_ENABLE() #define ENCODER_TIM_CH1_PIN GPIO_PIN_6 // PC6 CH1 #define ENCODER_TIM_CH1_GPIO GPIOC #define ENCODER_TIM_CH2_PIN GPIO_PIN_7 // PC7 CH2 #define ENCODER_TIM_CH2_GPIO GPIOC #define TIM_ENCODERMODE_TIx TIM_ENCODERMODE_TI12 #define ENCODER_TIM_IRQn TIM3_IRQn #define ENCODER_TIM_IRQHANDLER TIM3_IRQHandler // 定义定时器预分频,定时器实际时钟频率为:84MHz/(ENCODER_TIMx_PRESCALER+1) #define ENCODER_TIM_PRESCALER 0 // 定义定时器周期,当定时器开始计数到ENCODER_TIMx_PERIOD值是更新定时器并生成对应事件和中断 #define ENCODER_TIM_PERIOD 0xFFFF // 使用32bits 的计数器作为编码器计数,F4系列的TIM2,TIM5 定义定时器周期,当定时器开始计数到ENCODER_TIMx_PERIOD值是更新定时器并生成对应事件和中断 //#define ENCODER_TIM_PERIOD 0xFFFFFFFF //#define CNT_MAX #define USE_16CNT // 使用定时器3是16bits 的计数器作为编码器计数,F4系列的TIM3,TIM4 #define ENCODER_TIM_PERIOD 0xFFFF #define CNT_MAX ((int32_t)65536) // 计数值从0~65535 /* 扩展变量 ------------------------------------------------------------------*/ extern TIM_HandleTypeDef htimx_Encoder; // 编码器初始化结构体 extern int32_t OverflowCount ;//定时器溢出次数 /* 函数声明 ------------------------------------------------------------------*/ void ENCODER_TIMx_Init(void);
讯享网
编码器.c
讯享网#include "encoder/bsp_encoder.h" int32_t OverflowCount = 0;//记录定时器溢出次数 TIM_HandleTypeDef htimx_Encoder; // 定时器结初始化构体 TIM_Encoder_InitTypeDef sEncoderConfig; // 编码器结构体 void ENCODER_TIMx_Init(void) { ENCODER_TIM_RCC_CLK_ENABLE(); htimx_Encoder.Instance = ENCODER_TIMx; // 定时器基地址 htimx_Encoder.Init.Prescaler = ENCODER_TIM_PRESCALER; // 预分频器 htimx_Encoder.Init.CounterMode = TIM_COUNTERMODE_UP; // 计数模式 htimx_Encoder.Init.Period = ENCODER_TIM_PERIOD; // ARR值 htimx_Encoder.Init.ClockDivision=TIM_CLOCKDIVISION_DIV1; // 时钟分频因子 sEncoderConfig.EncoderMode = TIM_ENCODERMODE_TIx; // 编码器模式 sEncoderConfig.IC1Polarity = TIM_ICPOLARITY_RISING; // 输入捕获1极性选择 sEncoderConfig.IC1Selection = TIM_ICSELECTION_DIRECTTI; // 输入捕获1选择 sEncoderConfig.IC1Prescaler = TIM_ICPSC_DIV1; // 输入捕获1的预分频器 sEncoderConfig.IC1Filter = 13; // 输入捕获1滤波器 sEncoderConfig.IC2Polarity = TIM_ICPOLARITY_RISING; // 输入捕获2极性选择 sEncoderConfig.IC2Selection = TIM_ICSELECTION_DIRECTTI; // 输入捕获2选择 sEncoderConfig.IC2Prescaler = TIM_ICPSC_DIV1; // 输入捕获2的预分频器 sEncoderConfig.IC2Filter = 13; // 输入捕获2滤波器 __HAL_TIM_SET_COUNTER(&htimx_Encoder,0); // 设置计数值为0 HAL_TIM_Encoder_Init(&htimx_Encoder, &sEncoderConfig); __HAL_TIM_CLEAR_IT(&htimx_Encoder, TIM_IT_UPDATE); // 清除更新中断标志位 __HAL_TIM_URS_ENABLE(&htimx_Encoder); // 仅允许计数器溢出才产生更新中断 __HAL_TIM_ENABLE_IT(&htimx_Encoder,TIM_IT_UPDATE); // 使能更新中断 HAL_NVIC_SetPriority(ENCODER_TIM_IRQn, 0, 0); HAL_NVIC_EnableIRQ(ENCODER_TIM_IRQn); HAL_TIM_Encoder_Start(&htimx_Encoder, TIM_CHANNEL_ALL); } / * 函数功能: 基本定时器硬件初始化配置 * 输入参数: htim_base:基本定时器句柄类型指针 * 返 回 值: 无 * 说 明: 该函数被HAL库内部调用 */ void HAL_TIM_Encoder_MspInit(TIM_HandleTypeDef* htim_base) { GPIO_InitTypeDef GPIO_InitStruct; if(htim_base->Instance==ENCODER_TIMx) { /* 基本定时器外设时钟使能 */ ENCODER_TIM_GPIO_CLK_ENABLE(); /* 定时器通道1功能引脚IO初始化 */ GPIO_InitStruct.Pin = ENCODER_TIM_CH1_PIN; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull=GPIO_PULLDOWN; GPIO_InitStruct.Alternate = GPIO_AF2_TIM4; // 复用推挽 HAL_GPIO_Init(ENCODER_TIM_CH1_GPIO, &GPIO_InitStruct); /* 定时器通道2功能引脚IO初始化 */ GPIO_InitStruct.Pin = ENCODER_TIM_CH2_PIN; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull = GPIO_PULLDOWN; GPIO_InitStruct.Alternate = GPIO_AF2_TIM4; // 复用推挽 HAL_GPIO_Init(ENCODER_TIM_CH2_GPIO, &GPIO_InitStruct); } } / * 函数功能: 定时器更新中断 * 输入参数: *htim,定时器句柄 * 返 回 值: 无 * 说 明: 编码器捕获溢出计数 */ void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if(__HAL_TIM_IS_TIM_COUNTING_DOWN(&htimx_Encoder)) OverflowCount--; //向下计数溢出 else OverflowCount++; //向上计数溢出 }
PWM输出定时器.h
#ifndef __BDCMOTOR_TIM_H__ #define __BDCMOTOR_TIM_H__ #include "stm32f4xx_hal.h" #define BDCMOTOR_TIMx TIM1 #define BDCMOTOR_TIM_RCC_CLK_ENABLE() __HAL_RCC_TIM1_CLK_ENABLE() #define BDCMOTOR_TIM_RCC_CLK_DISABLE() __HAL_RCC_TIM1_CLK_DISABLE() #define BDCMOTOR_TIM_CH1_GPIO_CLK_ENABLE() __HAL_RCC_GPIOA_CLK_ENABLE() #define BDCMOTOR_TIM_CH1_PORT GPIOA #define BDCMOTOR_TIM_CH1_PIN GPIO_PIN_8 // CH1 #define BDCMOTOR_TIM_CH1N_GPIO_CLK_ENABLE() __HAL_RCC_GPIOB_CLK_ENABLE() #define BDCMOTOR_TIM_CH1N_PORT GPIOB #define BDCMOTOR_TIM_CH1N_PIN GPIO_PIN_13 // CHN1 #define SHUTDOWN_GPIO_CLK_ENABLE() __HAL_RCC_GPIOH_CLK_ENABLE() #define SHUTDOWN_PORT GPIOH #define SHUTDOWN_PIN GPIO_PIN_6 // SD(驱动板的PWM使能端) #define ENABLE_MOTOR() HAL_GPIO_WritePin(SHUTDOWN_PORT,SHUTDOWN_PIN,GPIO_PIN_RESET) #define SHUTDOWN_MOTOR() HAL_GPIO_WritePin(SHUTDOWN_PORT,SHUTDOWN_PIN,GPIO_PIN_SET) #define BDCMOTOR_TIM_CC_IRQx TIM1_CC_IRQn #define BDCMOTOR_TIM_CC_IRQxHandler TIM1_CC_IRQHandler // 定义定时器预分频,定时器实际时钟频率为:168MHz/(BDCMOTOR_TIMx_PRESCALER+1) #define BDCMOTOR_TIM_PRESCALER 1 // 实际时钟频率为:84MHz // 定义定时器周期,PWM频率为:168MHz/(BDCMOTOR_TIMx_PRESCALER+1)/(BDCMOTOR_TIM_PERIOD+1) #define BDCMOTOR_TIM_PERIOD 4199 // PWM频率为84MHz/(4199+1)=20KHz #define BDCMOTOR_DUTY_ZERO (0) // 0%占空比 #define BDCMOTOR_DUTY_FULL (BDCMOTOR_TIM_PERIOD-100) // 100%占空比 但大不到驱动板有自举电路的存在 #define BDDCMOTOR_DIR_CW() {HAL_TIM_PWM_Stop(&htimx_BDCMOTOR,TIM_CHANNEL_1);\ HAL_TIMEx_PWMN_Start(&htimx_BDCMOTOR,TIM_CHANNEL_1);} #define BDDCMOTOR_DIR_CCW() {HAL_TIM_PWM_Start(&htimx_BDCMOTOR,TIM_CHANNEL_1);\ HAL_TIMEx_PWMN_Stop(&htimx_BDCMOTOR,TIM_CHANNEL_1);} #define MOTOR_DIR_CW 1 // 电机方向: 顺时针 #define MOTOR_DIR_CCW (-1) // 电机方向: 逆时针 // 定义高级定时器重复计数寄存器值 // 实际PWM频率为:168MHz/(BDCMOTOR_TIMx_PRESCALER+1)/(BDCMOTOR_TIM_PERIOD+1)/(BDCMOTOR_TIM_REPETITIONCOUNTER+1) #define BDCMOTOR_TIM_REPETITIONCOUNTER 0 /* 扩展变量 ------------------------------------------------------------------*/ extern TIM_HandleTypeDef htimx_BDCMOTOR; extern __IO int32_t PWM_Duty; /* 函数声明 ------------------------------------------------------------------*/ void BDCMOTOR_TIMx_Init(void); void SetMotorDir(int16_t Dir); void SetMotorSpeed(int16_t Duty); #endif /* __BDCMOTOR_TIM_H__ */
PWM输出定时器.c
讯享网 #include "DCMotor/bsp_BDCMotor.h" TIM_HandleTypeDef htimx_BDCMOTOR; __IO int32_t PWM_Duty=BDCMOTOR_DUTY_ZERO; // 占空比:PWM_Duty/BDCMOTOR_TIM_PERIOD*100% void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim) { /* BDCMOTOR相关GPIO初始化配置 */ if(htim == &htimx_BDCMOTOR) { GPIO_InitTypeDef GPIO_InitStruct; /* 引脚端口时钟使能 */ __HAL_RCC_GPIOE_CLK_ENABLE(); BDCMOTOR_TIM_CH1_GPIO_CLK_ENABLE(); BDCMOTOR_TIM_CH1N_GPIO_CLK_ENABLE(); SHUTDOWN_GPIO_CLK_ENABLE(); /* BDCMOTOR输出脉冲控制引脚IO初始化 */ GPIO_InitStruct.Pin = BDCMOTOR_TIM_CH1_PIN; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; GPIO_InitStruct.Alternate = GPIO_AF1_TIM1; HAL_GPIO_Init(BDCMOTOR_TIM_CH1_PORT, &GPIO_InitStruct); GPIO_InitStruct.Pin = BDCMOTOR_TIM_CH1N_PIN; HAL_GPIO_Init(BDCMOTOR_TIM_CH1N_PORT, &GPIO_InitStruct); __HAL_RCC_GPIOE_CLK_ENABLE(); GPIO_InitStruct.Pin = GPIO_PIN_11; HAL_GPIO_Init(GPIOE, &GPIO_InitStruct); GPIO_InitStruct.Pin = SHUTDOWN_PIN; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; GPIO_InitStruct.Alternate = 0; HAL_GPIO_Init(SHUTDOWN_PORT, &GPIO_InitStruct); /* 使能电机控制引脚 */ ENABLE_MOTOR(); } } / * 函数功能: BDCMOTOR定时器初始化 * 输入参数: 无 * 返 回 值: 无 * 说 明: 无 */ void BDCMOTOR_TIMx_Init(void) { TIM_ClockConfigTypeDef sClockSourceConfig; // 定时器时钟 TIM_OC_InitTypeDef sConfigOC; /* 基本定时器外设时钟使能 */ BDCMOTOR_TIM_RCC_CLK_ENABLE(); /* 定时器基本环境配置 */ htimx_BDCMOTOR.Instance = BDCMOTOR_TIMx; // 定时器编号 htimx_BDCMOTOR.Init.Prescaler = BDCMOTOR_TIM_PRESCALER; // 定时器预分频器 htimx_BDCMOTOR.Init.CounterMode = TIM_COUNTERMODE_UP; // 计数方向:向上计数 htimx_BDCMOTOR.Init.Period = BDCMOTOR_TIM_PERIOD; // 定时器周期 htimx_BDCMOTOR.Init.ClockDivision=TIM_CLOCKDIVISION_DIV1; // 时钟分频 htimx_BDCMOTOR.Init.RepetitionCounter = BDCMOTOR_TIM_REPETITIONCOUNTER;// 重复计数器 /* 初始化定时器比较输出环境 */ HAL_TIM_PWM_Init(&htimx_BDCMOTOR); /* 定时器时钟源配置 */ sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL; // 使用内部时钟源 HAL_TIM_ConfigClockSource(&htimx_BDCMOTOR, &sClockSourceConfig); /* 定时器比较输出配置 */ sConfigOC.OCMode = TIM_OCMODE_PWM1; // 比较输出模式:PWM1模式 sConfigOC.Pulse = PWM_Duty; // 初始占空比 sConfigOC.OCPolarity = TIM_OCPOLARITY_LOW; // 输出极性 sConfigOC.OCNPolarity = TIM_OCNPOLARITY_LOW; // 互补通道输出极性 sConfigOC.OCFastMode = TIM_OCFAST_DISABLE; // 快速模式 sConfigOC.OCIdleState = TIM_OCIDLESTATE_RESET; // 空闲电平 sConfigOC.OCNIdleState = TIM_OCNIDLESTATE_RESET; // 互补通道空闲电平 HAL_TIM_PWM_ConfigChannel(&htimx_BDCMOTOR, &sConfigOC, TIM_CHANNEL_1); /* 启动定时器 */ HAL_TIM_Base_Start(&htimx_BDCMOTOR); } / * 函数功能: 设置电机速度 * 输入函数: Duty,输出脉冲占空比 * 返 回 值: 无 * 说 明: 无 */ void SetMotorSpeed(int16_t Duty) { __HAL_TIM_SET_COMPARE(&htimx_BDCMOTOR,TIM_CHANNEL_1,Duty); // 设置捕获/比较寄存器的值从而改变占空比 } / * 函数功能: 设置电机转动方向 * 输入函数: Dir,电机转动方向 * 返 回 值: 无 * 说 明: 无 */ void SetMotorDir(int16_t Dir) { if(Dir == MOTOR_DIR_CW) { HAL_TIM_PWM_Start(&htimx_BDCMOTOR,TIM_CHANNEL_1); HAL_TIMEx_PWMN_Stop(&htimx_BDCMOTOR,TIM_CHANNEL_1); // 停止输出 } else { HAL_TIM_PWM_Stop(&htimx_BDCMOTOR,TIM_CHANNEL_1); HAL_TIMEx_PWMN_Start(&htimx_BDCMOTOR,TIM_CHANNEL_1); } }
main.c
#include "stm32f4xx_hal.h" #include "key/bsp_key.h" #include "encoder/bsp_encoder.h" #include "usart/bsp_usartx.h" #include "DCMotor/bsp_BDCMotor.h" #include "led/bsp_led.h" /* 私有类型定义 --------------------------------------------------------------*/ typedef struct { __IO int32_t SetPoint; //设定目标 Desired Value __IO float Proportion; //比例常数 Proportional Const __IO float Integral; //积分常数 Integral Const __IO float Derivative; //微分常数 Derivative Const __IO int LastError; //Error[-1] // 上次误差 __IO int PrevError; //Error[-2] // 上上次误差 }PID_TypeDef; /* 四舍五入 */ //将浮点数x四舍五入为int32_t #define ROUND_TO_INT32(x) ((int32_t)(x)+0.5f)>=(x)? ((int32_t)(x)):((uint32_t)(x)+1) /*/ // 定义PID相关宏 // 这三个参数设定对电机运行影响非常大 // PID参数跟采样时间息息相关 /*/ #define SPD_P_DATA 5.5f // P参数 #define SPD_I_DATA 1.56f // I参数 #define SPD_D_DATA 0.0f // D参数 #define TARGET_SPEED 50.0f // 目标速度 表示一分钟转50圈 #define ENCODER 11 // 编码器线数 #define SPEEDRATIO 30 // 电机减速比 // 电机旋转一圈对应的编码器脉冲数(因为是减速型电机实际转30圈,而减速后才转一圈)减速比 1:30 #define PPR (SPEEDRATIO*ENCODER*4) // Pulse/r 每圈可捕获的脉冲数 #define MAX_SPEED 380 // 空载满速380r/m __IO uint8_t Start_flag = 0; // PID 开始标志 int32_t Motor_Dir = MOTOR_DIR_CW; // 电机方向 __IO int32_t LastSpd_Pulse= 0; // 编码器捕获值 Pulse extern __IO uint32_t uwTick; /* PID结构体 */ PID_TypeDef sPID; // 定义PID参数结构体 PID_TypeDef *ptr = &sPID; void PID_ParamInit(void); int32_t SpdPIDCalc(float NextPoint); void StopMotor(void); void StartMotor(void); /* 函数体 --------------------------------------------------------------------*/ / * 函数功能: 系统时钟配置 * 输入参数: 无 * 返 回 值: 无 * 说 明: 无 */ void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct; RCC_ClkInitTypeDef RCC_ClkInitStruct; __HAL_RCC_PWR_CLK_ENABLE(); // 使能PWR时钟 __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1); // 设置调压器输出电压级别1 RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE; // 外部晶振,8MHz RCC_OscInitStruct.HSEState = RCC_HSE_ON; // 打开HSE RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; // 打开PLL RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; // PLL时钟源选择HSE RCC_OscInitStruct.PLL.PLLM = 8; // 8分频MHz RCC_OscInitStruct.PLL.PLLN = 336; // 336倍频 RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2; // 2分频,得到168MHz主时钟 RCC_OscInitStruct.PLL.PLLQ = 7; // USB/SDIO/随机数产生器等的主PLL分频系数 HAL_RCC_OscConfig(&RCC_OscInitStruct); RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2; RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; // 系统时钟:168MHz RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; // AHB时钟: 168MHz RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4; // APB1时钟:42MHz RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2; // APB2时钟:84MHz HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5); HAL_RCC_EnableCSS(); // 使能CSS功能,优先使用外部晶振,内部时钟源为备用 // HAL_RCC_GetHCLKFreq()/1000 1ms中断一次 // HAL_RCC_GetHCLKFreq()/ 10us中断一次 // HAL_RCC_GetHCLKFreq()/ 1us中断一次 HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000); // 配置并启动系统滴答定时器 /* 系统滴答定时器时钟源 */ HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK); /* 系统滴答定时器中断优先级配置 */ HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0); } / * 函数功能: 主函数. * 输入参数: 无 * 返 回 值: 无 * 说 明: 无 */ int main(void) { /* 复位所有外设,初始化Flash接口和系统滴答定时器 */ HAL_Init(); /* 配置系统时钟 */ SystemClock_Config(); /* 串口初始化 */ MX_USARTx_Init(); /* LED初始化 */ LED_GPIO_Init(); /* 按键初始化 */ KEY_GPIO_Init(); /* 编码器初始化及使能编码器模式 */ ENCODER_TIMx_Init(); /* 高级控制定时器初始化并配置PWM输出功能 */ BDCMOTOR_TIMx_Init(); /* 设定占空比 */ PWM_Duty = 0; SetMotorSpeed(PWM_Duty); // 0% 占空比 电机一开始是停止的 /* PID 参数初始化 */ PID_ParamInit(); /* 无限循环 */ while (1) { /* 启动按钮 */ if(KEY1_StateRead()==KEY_DOWN) { StartMotor(); } if(KEY2_StateRead()==KEY_DOWN) { StopMotor(); } if(KEY3_StateRead()==KEY_DOWN)//加速 { sPID.SetPoint += 2; // 改变目标值 if(sPID.SetPoint >= MAX_SPEED) // 限制速度范围 r/m sPID.SetPoint = MAX_SPEED; } if(KEY4_StateRead()==KEY_DOWN)//减速 { sPID.SetPoint -= 2; if(sPID.SetPoint <=-MAX_SPEED) sPID.SetPoint = -MAX_SPEED; } } } / * 函数功能:启动电机转动 * 输入参数:无 * 返 回 值:无 * 说 明:无 */ void StartMotor(void) { if(sPID.SetPoint > 0) { Motor_Dir = MOTOR_DIR_CCW; BDDCMOTOR_DIR_CCW(); } else { Motor_Dir = MOTOR_DIR_CW; BDDCMOTOR_DIR_CW(); } Start_flag = 1; ENABLE_MOTOR(); // 使能电机 } / * 函数功能:停止电机转动 * 输入参数:无 * 返 回 值:无 * 说 明:无 */ void StopMotor(void) { SHUTDOWN_MOTOR(); HAL_TIM_PWM_Stop(&htimx_BDCMOTOR,TIM_CHANNEL_1); HAL_TIMEx_PWMN_Stop(&htimx_BDCMOTOR,TIM_CHANNEL_1); // 停止输出 /* 复位数据 */ Start_flag = 0; PWM_Duty = 0; sPID.PrevError = 0; sPID.LastError = 0; SetMotorSpeed(PWM_Duty); } / * 函数功能: 系统滴答定时器中断回调函数 * 输入参数: 无 * 返 回 值: 无 * 说 明: 每发生一次滴答定时器中断进入该回调函数一次 */ void HAL_SYSTICK_Callback(void) { __IO int32_t Spd_Pulse = 0; // 编码器捕获值 Pulse __IO int32_t Spd_PPS = 0; // 速度值 Pulse/Sample __IO float Spd_RPM = 0; // 速度值 r/m __IO int32_t FB_Speed = 0; // 用于反馈速度值到上位机 /* 速度环周期100ms */ if(uwTick % 100 == 0) { Spd_Pulse = (OverflowCount*CNT_MAX) + (int32_t)__HAL_TIM_GET_COUNTER(&htimx_Encoder); // 100ms内编码器的计数值 Spd_PPS = Spd_Pulse - LastSpd_Pulse; // 编码器增量 脉冲数 LastSpd_Pulse = Spd_Pulse ; // 保存上一次编码器的计数值 /* 11线编码器,30减速比,一圈脉冲信号是11*30*4 */ /* 放大10倍计算r/s,放大60则是计算r/m*/ Spd_RPM = ((((float)Spd_PPS/(float)PPR)*10.0f)*(float)60); // 100ms内转多少圈 PPR:一圈所需的脉冲数 *10 化成秒 *60 每分钟旋转多少圈 /* 计算PID结果 */ if(Start_flag == 1) { // PWM_Duty += SpdPIDCalc(Spd_RPM); // Spd_RPM:实际的转速 PWM_Duty = PWM_Duty + SpdPIDCalc(Spd_RPM); // 上次占空比 + 增量的占空比 (因为是增量式PID) /* 判断当前运动方向 */ if(PWM_Duty < 0) { Motor_Dir = MOTOR_DIR_CW; BDDCMOTOR_DIR_CW(); /* 限制占空比 */ if(PWM_Duty < -BDCMOTOR_DUTY_FULL) PWM_Duty = -BDCMOTOR_DUTY_FULL; /* 直接修改占空比 */ SetMotorSpeed(-PWM_Duty); } else { Motor_Dir = MOTOR_DIR_CCW; BDDCMOTOR_DIR_CCW(); if(PWM_Duty > BDCMOTOR_DUTY_FULL) PWM_Duty = BDCMOTOR_DUTY_FULL; SetMotorSpeed(PWM_Duty); } } printf("Spd:%d Pulse -- Spd: %.2f r/m \n",Spd_PPS,Spd_RPM); //编码器增量 脉冲数 增量的占空比(一分钟转多少圈) } } / PID 控制设计 */ / * 函数功能: PID参数初始化 * 输入参数: 无 * 返 回 值: 无 * 说 明: 无 */ void PID_ParamInit() { sPID.LastError = 0; // Error[-1] sPID.PrevError = 0; // Error[-2] sPID.Proportion = SPD_P_DATA; // 比例常数 Proportional Const sPID.Integral = SPD_I_DATA; // 积分常数 Integral Const sPID.Derivative = SPD_D_DATA; // 微分常数 Derivative Const sPID.SetPoint = TARGET_SPEED; // 设定目标Desired Value } / * 函数名称:速度闭环PID控制设计 * 输入参数:当前控制量 * 返 回 值:目标控制量 * 说 明:无 */ int32_t SpdPIDCalc(float NextPoint) { float iError,iIncpid; iError = (float)sPID.SetPoint - NextPoint; //偏差 = 目标值-当前值 /* 给恒定的占空比的时候,0.5r/m 的跳动范围是正常的 */ if((iError<0.5f )&& (iError>-0.5f)) // 如果偏差很小就不改变(很合适) iError = 0.0f; /*简化的公式 = KP*ek + Ki*ek-1 + Kd*ek-2 */ iIncpid=(sPID.Proportion * iError) //E[k]项 (当前误差) -(sPID.Integral * sPID.LastError) //E[k-1]项 +(sPID.Derivative * sPID.PrevError); //E[k-2]项 sPID.PrevError = sPID.LastError; //把上次误差赋给上上次误差 sPID.LastError = iError; // 当前误差赋给上次误差 return(ROUND_TO_INT32(iIncpid)); //返回增量值 }
速度环用滴答定时器100ms来计时间。来反馈当前速度。
2.位置环-----位置式PID
让电机维持转多少圈。产生PWM定时器与定时器编码器上个列子都一样。只是main.c不同。
讯享网#include "stm32f4xx_hal.h" #include "key/bsp_key.h" #include "encoder/bsp_encoder.h" #include "usart/bsp_usartx.h" #include "DCMotor/bsp_BDCMotor.h" #include "led/bsp_led.h" /* 私有类型定义 --------------------------------------------------------------*/ typedef struct { __IO int32_t SetPoint; //设定目标 Desired Value __IO float SumError; //误差累计 __IO float Proportion; //比例常数 Proportional Const __IO float Integral; //积分常数 Integral Const __IO float Derivative; //微分常数 Derivative Const __IO int LastError; //Error[-1] }PID_TypeDef; /*/ // 定义PID相关宏 // 这三个参数设定对电机运行影响非常大 // PID参数跟采样时间息息相关 /*/ #define SPD_P_DATA 1.025f // P参数 #define SPD_I_DATA 0.215f // I参数 #define SPD_D_DATA 0.1f // D参数 #define TARGET_LOC 6600 // 目标速度 脉冲数:PPR (一圈的脉冲数) #define ENCODER 11 // 编码器线数 #define SPEEDRATIO 30 // 电机减速比 #define PPR (SPEEDRATIO*ENCODER*4) // Pulse/r 每圈可捕获的脉冲数 #define MAX_SPEED 380 // 空载满速380r/m #define FB_USE_GRAPHIC // 使用图像曲线作为反馈 /* 私有变量 ------------------------------------------------------------------*/ __IO uint8_t Start_flag = 0; // PID 开始标志 int32_t Motor_Dir = MOTOR_DIR_CW; // 电机方向 __IO int32_t Loc_Pulse; // 编码器捕获值 Pulse /* 扩展变量 ------------------------------------------------------------------ */ extern __IO uint32_t uwTick; /* PID结构体 */ PID_TypeDef sPID; // PID参数结构体 PID_TypeDef *ptr = &sPID; /* 扩展变量 ------------------------------------------------------------------*/ /* 私有函数原形 --------------------------------------------------------------*/ void PID_ParamInit(void) ; int32_t LocPIDCalc(int32_t NextPoint); void StopMotor(void); void StartMotor(void); /* 函数体 --------------------------------------------------------------------*/ / * 函数功能: 系统时钟配置 * 输入参数: 无 * 返 回 值: 无 * 说 明: 无 */ void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct; RCC_ClkInitTypeDef RCC_ClkInitStruct; __HAL_RCC_PWR_CLK_ENABLE(); // 使能PWR时钟 __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1); // 设置调压器输出电压级别1 RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE; // 外部晶振,8MHz RCC_OscInitStruct.HSEState = RCC_HSE_ON; // 打开HSE RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; // 打开PLL RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; // PLL时钟源选择HSE RCC_OscInitStruct.PLL.PLLM = 8; // 8分频MHz RCC_OscInitStruct.PLL.PLLN = 336; // 336倍频 RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2; // 2分频,得到168MHz主时钟 RCC_OscInitStruct.PLL.PLLQ = 7; // USB/SDIO/随机数产生器等的主PLL分频系数 HAL_RCC_OscConfig(&RCC_OscInitStruct); RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2; RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; // 系统时钟:168MHz RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; // AHB时钟: 168MHz RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4; // APB1时钟:42MHz RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2; // APB2时钟:84MHz HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5); HAL_RCC_EnableCSS(); // 使能CSS功能,优先使用外部晶振,内部时钟源为备用 // HAL_RCC_GetHCLKFreq()/1000 1ms中断一次 // HAL_RCC_GetHCLKFreq()/ 10us中断一次 // HAL_RCC_GetHCLKFreq()/ 1us中断一次 HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000); // 配置并启动系统滴答定时器 /* 系统滴答定时器时钟源 */ HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK); /* 系统滴答定时器中断优先级配置 */ HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0); } / * 函数功能: 主函数. * 输入参数: 无 * 返 回 值: 无 * 说 明: 无 */ int main(void) { /* 复位所有外设,初始化Flash接口和系统滴答定时器 */ HAL_Init(); /* 配置系统时钟 */ SystemClock_Config(); /* 串口初始化 */ MX_USARTx_Init(); /* LED初始化 */ LED_GPIO_Init(); /* 按键初始化 */ KEY_GPIO_Init(); /* 编码器初始化及使能编码器模式 */ ENCODER_TIMx_Init(); /* 高级控制定时器初始化并配置PWM输出功能 */ BDCMOTOR_TIMx_Init(); /* 设定占空比 */ PWM_Duty = 0; SetMotorSpeed(PWM_Duty); // 0% 占空比 /* PID 参数初始化 */ PID_ParamInit(); /* 无限循环 */ while (1) { /* 启动按钮 */ if(KEY1_StateRead()==KEY_DOWN) { StartMotor(); } if(KEY2_StateRead()==KEY_DOWN) { StopMotor(); } if(KEY3_StateRead()==KEY_DOWN)//加速 { sPID.SetPoint += PPR; // +1 r 加一圈 +1320 } if(KEY4_StateRead()==KEY_DOWN)//减速 { sPID.SetPoint -= PPR; // -1 r 减一圈 -1320 } } } / * 函数功能:启动电机转动 * 输入参数:无 * 返 回 值:无 * 说 明:无 */ void StartMotor(void) { if(sPID.SetPoint > 0) { Motor_Dir = MOTOR_DIR_CCW; BDDCMOTOR_DIR_CCW(); } else { Motor_Dir = MOTOR_DIR_CW; BDDCMOTOR_DIR_CW(); } Start_flag = 1; ENABLE_MOTOR(); } / * 函数功能:停止电机转动 * 输入参数:无 * 返 回 值:无 * 说 明:无 */ void StopMotor(void) { SHUTDOWN_MOTOR(); HAL_TIM_PWM_Stop(&htimx_BDCMOTOR,TIM_CHANNEL_1); HAL_TIMEx_PWMN_Stop(&htimx_BDCMOTOR,TIM_CHANNEL_1); // 停止输出 /* 复位数据 */ Start_flag = 0; PWM_Duty = 0; sPID.SumError = 0; sPID.LastError = 0; SetMotorSpeed(PWM_Duty); } / * 函数功能: 系统滴答定时器中断回调函数 * 输入参数: 无 * 返 回 值: 无 * 说 明: 每发生一次滴答定时器中断进入该回调函数一次 */ void HAL_SYSTICK_Callback(void) { int32_t tmpPWM_Duty = 0; /* 速度环周期100ms */ if(uwTick % 100 == 0) { Loc_Pulse = (OverflowCount*CNT_MAX) + (int32_t)__HAL_TIM_GET_COUNTER(&htimx_Encoder); /* 计算PID结果 */ if(Start_flag == 1) { PWM_Duty = LocPIDCalc(Loc_Pulse); // 位置式PID if(PWM_Duty >= BDCMOTOR_DUTY_FULL/2) // 2分之1 PWM_Duty = BDCMOTOR_DUTY_FULL/2; if(PWM_Duty <= -BDCMOTOR_DUTY_FULL/2) PWM_Duty = -BDCMOTOR_DUTY_FULL/2; /* 判断当前运动方向 */ if(PWM_Duty < 0) { Motor_Dir = MOTOR_DIR_CW; BDDCMOTOR_DIR_CW(); tmpPWM_Duty = -PWM_Duty; } else { Motor_Dir = MOTOR_DIR_CCW; BDDCMOTOR_DIR_CCW(); tmpPWM_Duty = PWM_Duty; } /* 输出PWM */ SetMotorSpeed( tmpPWM_Duty ); } printf("Target:%d / Loc: %d (Pulse) = %.2f (r) \n",sPID.SetPoint, Loc_Pulse, (float)Loc_Pulse/(float)PPR); // 目标值 反馈值 控制旋转多少圈 } } / PID 控制设计 */ / * 函数功能: PID参数初始化 * 输入参数: 无 * 返 回 值: 无 * 说 明: 无 */ void PID_ParamInit() { sPID.LastError = 0; // Error[-1] sPID.SumError = 0; // 累积误差 sPID.Proportion = SPD_P_DATA; // 比例常数 Proportional Const sPID.Integral = SPD_I_DATA; // 积分常数 Integral Const sPID.Derivative = SPD_D_DATA; // 微分常数 Derivative Const sPID.SetPoint = TARGET_LOC; // 设定目标Desired Value } / * 函数名称:位置闭环PID控制设计 * 输入参数:当前控制量 * 返 回 值:目标控制量 * 说 明:无 */ int32_t LocPIDCalc(int32_t NextPoint) { int32_t iError,dError; iError = sPID.SetPoint - NextPoint; //偏差 if( (iError<50) && (iError>-50) ) // 偏差50个脉冲就忽略 iError = 0; /* 限定积分区域 */ if((iError<400 )&& (iError>-400)) { sPID.SumError += iError; //积分 /* 设定积分上限 */ if(sPID.SumError >= (TARGET_LOC*10)) sPID.SumError = (TARGET_LOC*10); if(sPID.SumError <= -(TARGET_LOC*10)) sPID.SumError = -(TARGET_LOC*10); } dError = iError - sPID.LastError; //微分 sPID.LastError = iError; return (int32_t)( (sPID.Proportion * (float)iError) //比例项 + (sPID.Integral * (float)sPID.SumError) //积分项 + (sPID.Derivative * (float)dError) ); //微分项 }
总结:位置式PID与增量式PID都是根据公式来写的。怎么快速稳定达到目标值。需要调节KP,Ki,Kd。调节有不同的方法。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/74599.html