嵌入式STM32利用arm-dsp库进行PID调节控制
Posted 菜老越
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了嵌入式STM32利用arm-dsp库进行PID调节控制相关的知识,希望对你有一定的参考价值。
在工程实际中,应用最为广泛的调节器控制规律为比例、积分、微分控制,简称 PID 控制,又称 PID调节。其原理介绍教科书以及网上已经有大量资料,本文着重介绍在嵌入式设备中,如何快速上手进行PID控制,并通过简单的单片机外设进行验证。
一、实验简介
1.原理
我们知道,可以通过调节PWM的占空比来起到调节等效电压的效果。比如U=D*Ud,其中U为输出的等效电压,D为占空比,Ud为单极性PWM的峰值电压,这常用在电机控制中。
当要维持输出电压U为某一确定值Us时,往往要形成回路以准确得控制U的大小,这就要用到ADC来回采电压Us,利用某种控制手段使得输出的电压U与确定值Us误差维持在一定范围内。其中,这种控制方法最为常用的就是PID控制。
以经典反馈控制回路为例:
其中给定值即为Us;控制规律为PID;执行器即为占空比的调整;过程即为PWM输出;被控变量即为U;传感器即为ADC。本系统采用PID控制时,可作以下描述:PWM输出的电压值U被ADC采样后,回环送入调节器与给定值Us相比较,获得一个偏差送入PID调节器,通过PID来控制PWM占空比的大小从而起到调节电压U的效果,最终使得电压U与给定值Us之间的误差稳定在某个设定的范围阈值之内。
2.所用外设
我们利用一个单片机核心板即可进行上述的PID调节PWM的实验。
其中,PWM由定时器输出;通过片内ADC进行电压回采、处理;通过arm-dsp库进行PID调节。
综上,本实验用到以下外设:
1.定时器的PWM输出;
2.定时器触发的ADC采样;
3.ADC采样的DMA传输;
二、代码
依旧用到了STM32CubeMX来进行驱动代码的生成。
这里规定了几个重要的参数:
1.STM32F407,配置主频160MHz;
1.PWM载波50Hz;
2.ADC采样率4000Hz;
1.PWM输出配置
使用TIMER3来输出PWM,160MHz主频下,TIMER3的时钟为80MHz,将在这个基础上分频到50Hz;
首先配置TIM3的PWM输出通道:
然后分频到50Hz,同时设置PWM极性高,Pulse随便设置即可,为了最大程度体现PID的效果,我们初始设置为0,在PWM极性高时,Pulse为0意味着PWM通道总是输出低,即PWM初始的等效电压为0.
2.定时器触发的DMA传输的ADC
这个完全参考我之前的博客即可,【嵌入式】STM32F4的ADC采样——多通道、DMA、定时器触发,唯一的区别在于此处只用了一通道,更简单了。
生成代码后,硬件上将PWM通道直接怼到ADC1的0通道即可。
3.主体代码
#include "main.h"
#include "stm32f4xx_hal.h"
#include "adc.h"
#include "dma.h"
#include "tim.h"
#include "gpio.h"
#include "arm_math.h"
void SystemClock_Config(void);
#define BUF_LEN 400 //采样数组长度
#define PWM_PERIOD_CCR1 8000 //PWM周期-计数值
#define DES_VOL 1.0 //目标电压
#define ERR_LIMIT 0.05 //误差限制
typedef struct{
arm_pid_instance_f32 S;
float out;
}PidCtrlTypedef; //pidt调节结构体
volatile uint8_t dma_cpl_flag = 0; //dma传输完成标志
uint16_t adc_raw[BUF_LEN] = {0}; //adc原始采样值
uint16_t adc_raw_copy[BUF_LEN] = {0}; //adc原始采样值备份
float cur_vol = 0; //当前电压
PidCtrlTypedef pidCtrl; //pid调节实例
/* ADC-DMA全传输完成回调 */
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc)
{
UNUSED(hadc);
memcpy((void *)adc_raw_copy,(void *)adc_raw,sizeof(adc_raw));
dma_cpl_flag = 1;
}
/* 根据原始采样值计算PWM等效电压值 */
float calVol()
{
int i;
float res;
float sum = 0;
float tempVal;
for(i = 0;i < BUF_LEN; i++)
{
tempVal = adc_raw_copy[i] * 3.3 / 4095;
sum += tempVal;
}
res = sum / BUF_LEN;
cur_vol = res;
return res;
}
/* PID初始化 */
void pidInit()
{
pidCtrl.S.Kp = 0.1;
pidCtrl.S.Ki=0.1;
pidCtrl.S.Kd = 0.1;
arm_pid_init_f32(&pidCtrl.S,1);
pidCtrl.out = 0;
}
/* PID执行 */
static void pidExecu(float vol)
{
float pidErr;
pidErr = DES_VOL - vol;
//误差不在允许范围内
if(fabs(pidErr) > ERR_LIMIT)
{
pidCtrl.out = arm_pid_f32(&pidCtrl.S,pidErr);
// vol pidCtrl.out
// —————————— = ————————————
// 当前占空比 调节后占空比
htim3.Instance->CCR1 = (uint32_t)(pidCtrl.out * (htim3.Instance->CCR1 + 1) / vol);
}
}
/* USER CODE END 0 */
/**
* @brief The application entry point.
*
* @retval None
*/
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_DMA_Init();
MX_ADC1_Init();
MX_TIM2_Init();
MX_TIM3_Init();
//pid初始化
pidInit();
//开启AD转换时钟
HAL_TIM_Base_Start(&htim2);
//开启PWM输出
HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1);
//开启ADC-DMA传输
HAL_ADC_Start_DMA(&hadc1, (uint32_t *)adc_raw, BUF_LEN);
while (1)
{
if(dma_cpl_flag == 1)
{
dma_cpl_flag = 0;
pidExecu(calVol());
}
}
}
以上代码即为主体逻辑代码。流程如下:
1.外设初始化;
2.PID初始化;
3.开启AD转换时钟;
4.开启PWM输出;
5.开启ADC-DMA采样;
6.每次DMA传输完成中断后,进行PWM有效值计算、PID控制、PWM占空比调节。
同时,程序中有几个关键点:
1.设置DMA的长度为400,即采样率4000Hz时、PWM载波50Hz时,采样100ms进一次DMA传输完成中断,共采了5周期的PWM,从而能直接对400个采样点取平均,得到PWM的有效值。即采样长度一定要为PWM载波的整数倍,且采样率要远大于PWM载波频率;
2.利用arm-dsp库进行PID控制要在工程中加入arm-dsp的lib库,具体方法请参考博客【嵌入式】利用arm-DSP库进行FFT计算,获得信号的频谱、幅值及相位(上)的二-1小节;
3.arm_pid_f32函数声明如下:
static __INLINE float32_t arm_pid_f32(
arm_pid_instance_f32 * S,
float32_t in)
其中S为arm_pid_instance_f32 实例指针,在arm_pid_init_f32函数中实例化。要注意的是变量in,这个in是给定值与反馈值的误差,其在arm_math.h中做了阐述:
The PID controller calculates an "error" value as the difference between
* the measured output and the reference input.
* The controller attempts to minimize the error by adjusting the process control inputs.
* The proportional value determines the reaction to the current error,
* the integral value determines the reaction based on the sum of recent errors,
* and the derivative value determines the reaction based on the rate at which the error has been changing.
了解了这几点,再配合上边注释详细的程序,即可理解实验过程以及PID调节的使用方法。具体Kp、Ki、Kd参数的调节,还需要根据不同场景来进行耐心调节。
三、实验结果
程序中定义了变量cur_vol以存储当前采到的PWM有效值,为了直观的体现PID对PWM的调节过程,使用STM32Studio进行变量实时调试,具体使用方法请自行百度。
可以看到,在设置期望值1V、初始PWM输出为0V、最大允许误差0.05V时,PID控制器可在2秒内平滑稳定得将输出值调节到误差0.05V之内。且人为地多次随机插拔采样通道使得采样值扰动、PWM失稳后,PID控制器总能再次平稳得将输出值再次调节到1V±0.05V之内。
这就体现了PID控制的实用价值所在,至于调节速度、震荡速度、允许误差等,可以通过实际工程需求进行整定、调整。
最后,贴一张调节稳定后的PWM实测波形,可以看到其占空比约为6.489/20 = 32.445%,以AD基准电压3.3V来算,其输出电压为3.3*0.32445 = 1.070685V,这个值由于逻辑分析仪的占空比采样误差、3.3V基准误差的存在,使得与1V有一定的偏差。同时程序里也规定了0.95~1.05V范围内是期望范围。
综上,PID调节起到了应有的作用。
以上是关于嵌入式STM32利用arm-dsp库进行PID调节控制的主要内容,如果未能解决你的问题,请参考以下文章
STM32cubeMX--增量式PID调节电机速度(霍尔编码器)