使用PID算法实现DAC模拟量输出的快速调节

目录

概述

1 系统框架和算法

1.1 框架结构介绍

1.2 PID算法实现

1.2.1 理论介绍

1.2.2  离散化位置式PID

1.2.3 位置式PID算法

2 STM32Cube 配置项目

2.1 配置参数

2.2 GENERATE项目

3 功能实现

3.1 ADC采样数据功能

3.2 DAC数据转换

3.3  PID相关的调制函数

4 测试

4.1 编写测试代码

4.2 运行结果


源代码下载地址:

使用PID算法实现DAC模拟量输出的快速调节资源-CSDN文库

概述

本文主要介绍一个PID在实际项目应用的案例,通过ADC采样DAC的输出结果,调整使其快速到达期望的结果,系统基于STM32F103RC,使用DAC输出模拟量,ADC采集该模拟量作为反馈信号,以实现系统的闭环控制。

1 系统框架和算法

1.1 框架结构介绍

MCU内核处理数据: 设置DAC的初始值,读取ADC模块反馈回来的数值,内部进行PID运算,并将运算结果发送给DAC模块,重新输出数据。

DAC输出模拟量: 经过MCU计算后,将运行结果通道DAC模块输出

ADC采集输出数据:ADC模块采集DAC输出的模拟量,将其转换为数据值,MCU应用该值和输出量进行比较,以确定是否还需要继续调节输出数值

1.2 PID算法实现

1.2.1 理论介绍

PID 算法是闭环控制系统中常用的算法, PID 分别是 Proportion(比例)、 Integral(积分)、
Differential(微分)的首字母缩写。它是一种结合比例、积分和微分三个环节于一体的闭环控制算法。具体的控制流程如下图所示:


通过将输入目标值和实际输出值进行偏差的计算,然后把计算结果输入到 PID控制算法中,经过比例、积分和微分三个环节的运算,运算后的输出作用于执行器,从而让系统的实际值逐渐靠近目标值。连续控制的理想 PID 控制规律:

• Kp      —— 比例增益, Kp 与比例度成倒数关系
• Tt       —— 积分时间常数
• TD     —— 微分时间常数
• u(t)—— PID 控制器的输出信号
• e(t)—— 给定值 r(t)与测量值误差

1.2.2  离散化位置式PID

对离散式的PID公式进行运算,则 u(k) 可表示为:

进一步进行简化,则得出PID的标准公式:

参数介绍

• k                        采样的序号
• err(k)                 第 k 次的误差
• u(k)                    输出量
• Kp                      比例参数
• Ki=Kp*T/Ti         积分参数
• Kd=Kp*Td/T       微分参数

1.2.3 位置式PID算法

typedef struct
{
    float target_val;       //目标值
    float actual_val;       //实际值
    float err;              //定义偏差值
    float err_last;         //定义上一个偏差值
    float Kp,Ki,Kd;         //定义比例、积分、微分系数
    float integral;         //定义积分值
}_pid;


_pid pid_location;

void pid_param_init(void)
{
    /* 位置相关初始化参数 */
    pid_location.target_val = 20;
    pid_location.actual_val=0.0;
    pid_location.err=0.0;
    pid_location.err_last=0.0;
    pid_location.integral=0.0;

    pid_location.Kp = 0.045;
    pid_location.Ki = 0.0;
    pid_location.Kd = 0.0;

} 

float pid_location_realize(_pid *pid, float actual_val)
{
    /*计算目标值与实际值的误差*/
    pid->err=pid->target_val-actual_val;

    /* 设定闭环死区 */
    if((pid->err >= -20) && (pid->err <= 20))
    {
        pid->err = 0;
        pid->integral = 0;
    }

    pid->integral += pid->err;    // 误差累积

    /*PID算法实现*/
    pid->actual_val = pid->Kp*pid->err+pid->Ki*pid->integral+pid->Kd*(pid->err-pid->err_last);

    /*误差传递*/
    pid->err_last=pid->err;

    /*返回当前实际值*/
    return pid->actual_val;
}

2 STM32Cube 配置项目

2.1 配置参数

1)配置DAC参数,使用DAC2模块,选择PA5作为输出IO

DAC相关参数配置

2)配置ADC参数,选择ADC1,通道号为IN2

 ADC1参数配置

ADC1对应的DMA配置

 使能DMA1中断

 2.2 GENERATE项目

配置完成参数后,点击GENERATE生成项目,具体目录结构如下:

3 功能实现

端口介绍:

PA2: 模拟量ADC的输入端口

PA5: 模拟量DAC的输出端口

 3.1 ADC采样数据功能

实现功能: 

1)使能ADC模块

2)数据处理

3)DMA调用ADC相关的回调函数

typedef struct
{
   uint8_t sta         : 2;      //00: IELD, 01: Ready   02: Fail   11: unuse
   uint8_t res         : 6;
} Status_bit;

typedef struct
{
    uint16_t adcRaw;
    union{
        uint8_t status;
        Status_bit  status_bit;
    };
    
}Stru_GetAdc;


static uint32_t adcDMAValue[2];
Stru_GetAdc struc_GetAdc;

void ADC_ConvInit( ADC_HandleTypeDef* hadc )
{
    HAL_ADC_Start_DMA(hadc, adcDMAValue, 4);
}

void ADC_HandleDataPackets( void )
{
    uint16_t adcvalueList[4];
    uint32_t value=0;
    
    adcvalueList[0] = adcDMAValue[0]&0x0000ffff;
    adcvalueList[1] = (adcDMAValue[0]>>16)&0x0000ffff;
    
    adcvalueList[2] = adcDMAValue[1]&0x0000ffff;
    adcvalueList[3] = (adcDMAValue[1]>>16)&0x0000ffff;
    
    value = 0;
    for( int i =0;  i< 4; i++ ){
        value +=  adcvalueList[i]; 
    }
    
    struc_GetAdc.adcRaw =  value>>2;
    struc_GetAdc.status_bit.sta = 1;
}

void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
    if(hadc->Instance == ADC1)
    {
        HAL_ADC_Stop_DMA(hadc);                     // stop to convert adc value 
        
        ADC_HandleDataPackets();
        
        HAL_ADC_Start_DMA(hadc, adcDMAValue, 4);    // start to convert adc value
    }
}

3.2 DAC数据转换

功能介绍:

设置对应通道需要转换的数字量

void DAC_SetConvert(DAC_HandleTypeDef *hdac, uint32_t Channel, uint16_t value)
{
   HAL_DAC_SetValue( hdac, Channel, DAC_ALIGN_12B_R,value );
}

3.3  PID相关的调制函数

代码73行: 获取当前ADC的值

代码74行:获取PID的期望值

代码75行: 比较期望值和实际值的误差

代码78行:使用当前的ADC值进行PID运算

代码80行:设置DAC的输出值

详细源代码:

void ADC_DAC_MatchByPID( void )
{
    bool is_match;
    uint32_t current_val,cal_value;
    
    {
        current_val = struc_GetAdc.adcRaw;
        cal_value = pid_get_target(&pid_speed);
        is_match = abs((int)((current_val- cal_value))) > 5 ? false : true;
        if( !is_match )
        {
            cal_value = pid_speed_realize(&pid_speed, current_val);
            cal_value = cal_value > MAX_VALE ? MAX_VALE: cal_value;
            DAC_SetConvert( &hdac, DAC_CHANNEL_2, cal_value );
        }
        
        printf("ADC Raw Count: %04d , voltage: %0.2f V \r\n",current_val, (current_val*3.3)/4096  );
    }
}

4 测试

4.1 编写测试代码

代码96行:初始化PID数据结构

代码97行:设置期望值

代码97行:设置P,I,D三个参数的值

详细源码:

void voltage_InitPara( void )
{
    pid_param_init();
    pid_set_target( &pid_speed, 3400);
    pid_set_para(&pid_speed, 0.5, 0.1, 0.1);
}

4.2 运行结果

编译代码,下载到板卡中运行.

测试1: 期望值RAW = 3400,误差delta < 5对应实际电压值V = 2.74V

稳定前的值:

稳定后的值:

测试2: 期望值RAW = 1400,误差delta < 5,对应实际电压值V = 1.12V 

void voltage_InitPara( void )
{
    pid_param_init();
    pid_set_target( &pid_speed, 1400);
    pid_set_para(&pid_speed, 0.5, 0.1, 0.1);
}

稳定前的值:

稳定后的值:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值