【嵌入式】STM32利用arm-dsp库进行PID调节控制

目录

    • 一、实验简介
      • 1.原理
      • 2.所用外设
    • 二、代码
      • 1.PWM输出配置
      • 2.定时器触发的DMA传输的ADC
      • 3.主体代码
    • 三、实验结果

在工程实际中,应用最为广泛的调节器控制规律为比例、积分、微分控制,简称 PID 控制,又称 PID调节。其原理介绍教科书以及网上已经有大量资料,本文着重介绍在嵌入式设备中,如何快速上手进行PID控制,并通过简单的单片机外设进行验证。

一、实验简介

1.原理

我们知道,可以通过调节PWM的占空比来起到调节等效电压的效果。比如U=D*Ud,其中U为输出的等效电压,D为占空比,Ud为单极性PWM的峰值电压,这常用在电机控制中。
当要维持输出电压U为某一确定值Us时,往往要形成回路以准确得控制U的大小,这就要用到ADC来回采电压Us,利用某种控制手段使得输出的电压U与确定值Us误差维持在一定范围内。其中,这种控制方法最为常用的就是PID控制。
以经典反馈控制回路为例:
【嵌入式】STM32利用arm-dsp库进行PID调节控制_第1张图片
其中给定值即为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输出通道:
【嵌入式】STM32利用arm-dsp库进行PID调节控制_第2张图片
然后分频到50Hz,同时设置PWM极性高,Pulse随便设置即可,为了最大程度体现PID的效果,我们初始设置为0,在PWM极性高时,Pulse为0意味着PWM通道总是输出低,即PWM初始的等效电压为0.
【嵌入式】STM32利用arm-dsp库进行PID调节控制_第3张图片

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控制的实用价值所在,至于调节速度、震荡速度、允许误差等,可以通过实际工程需求进行整定、调整。
【嵌入式】STM32利用arm-dsp库进行PID调节控制_第4张图片
最后,贴一张调节稳定后的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调节控制_第5张图片

你可能感兴趣的:(算法,嵌入式,单片机,stm32,嵌入式)