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

news2024/12/23 6:45:45

目录

概述

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);
}

稳定前的值:

稳定后的值:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1887826.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

黄子韬vs徐艺洋卫生间风波

【热搜爆点】黄子韬VS徐艺洋&#xff1a;卫生间风波背后的职场与友情界限探讨在这个充满欢笑与意外的综艺时代&#xff0c;《跟我出游吧》再次以它独有的魅力&#xff0c;引爆了一个既尴尬又引人深思的话题——“黄子韬要上徐艺洋的卫生间&#xff1f;”这不仅仅是一句简单的调…

CSS|03 尺寸样式属性文本与字体属性

尺寸样式属性 height:元素高度height的值&#xff1a;auto 自动length 使用px定义高度% 基于包含它的块级对象的百分比高度 width&#xff1a;元素的宽度width的值与height一样span标签可以设置宽度、高度吗&#xff1f; 答&#xff1a;不可以&#xff0c;因为span标签是一个行…

mysql-sql-第十四周

学习目标&#xff1a; sql 学习内容&#xff1a; 40.查询学过「哈哈」老师授课的同学的信息 Select * from students left join score on students.stunmscore.stunm where counm (select counm from teacher left join course on teacher.teanmcourse.teanm where teache…

DCU整体硬件架构

DCU整体硬件架构 DCU整体硬件架构 首先&#xff0c;DCU通过PCI-E总线与CPU处理器相连&#xff0c;它是CPU主机系统的一个硬件扩展&#xff0c;其存在的目的是为了对程序某些模块或者函数进行加速。虽然DCU是原硬件系统的一个扩展&#xff0c;接受CPU调度指挥&#xff0c;但是在…

西部智慧健身小程序+华为运动健康服务

1、 应用介绍 西部智慧健身小程序为用户提供一站式全流程科学健身综合服务。用户通过登录微信小程序&#xff0c;可享用健康筛查、运动风险评估、体质检测评估、运动处方推送、个人运动数据监控与评估等公益服务。 2、 体验介绍西部智慧健身小程序华为运动健康服务核心体验如…

认识流式处理框架Apache Flink

目录 一、Apache Flink 的基础概念 1.1 Apache Flink是什么&#xff1f; 1.2 Flink的定义 二、Apache Flink 的发展史 2.1 Flink前身Stratosphere 2.2 Flink发展时间线及重大变更 三、Flink核心特性 3.1 批流一体化 3.2 同时支持高吞吐、低延迟、高性能 3.3 支持事件时…

前端接入chatgpt,实现流式文字的显示

前端接入chatgpt,实现流式文字的显示 业务需求&#xff1a; 项目需要接入chatgpt提供的api&#xff0c;后端返回流式的字符&#xff0c;前端接收并实时显示。 相关技术原理&#xff1a; 1. JS中的Stream流: 在JavaScript中&#xff0c;使用Stream流通常指的是处理数据流的…

react native中使用@react-navigation/native进行自定义头部

react native中使用react-navigation/native进行自定义头部 效果示例图实例代码 效果示例图 实例代码 /* eslint-disable react-native/no-inline-styles */ /* eslint-disable react/no-unstable-nested-components */ import React, { useLayoutEffect } from react; import…

ripro子主题eeesucai-child集成后台美化包(适用于设计素材站+资源下载站等)

模板介绍 最新RiPro子主题模板&#xff0c;Eeesucai-child模板后台美化包&#xff0c;使用该子主题前需要安装WordPress程序和RiPro模板。 安装教程 第一种&#xff0c;在wordpress后台上传主题&#xff0c;上传之后点启动 第二种&#xff0c;上传到wordpress主题目录/wp-con…

MatLab 二维图像绘制基础

MatLab 二维图像绘制基础 plot 描点绘图 %% % 二维绘图 &#xff0c;plot进行描点&#xff0c;步长越小&#xff0c;越平滑 x [1:9]; y [0.1:0.2:1.7]; X x y*i; % 复数 plot(X)plot绘制矩阵 %% % 当X Y 为矩阵时&#xff0c;对应矩阵中的元素依次绘制 t 0:0.01:2*pi; …

将多个Excel工作表合并成一个工作表,1分钟轻松搞定!

1. 案例展示 2. 视频详解 多个工作表合并成一个工作表 3. 图文详解 第一步&#xff1a;相同格式&#xff08;表头&#xff09;的表格&#xff0c;并将所有表格都放在一个文件夹内“将多个工作表合并成一个工作表”&#xff08;自己定义文件名&#xff09; 第二步&#xff1a;新…

Linux 【线程池】【单例模式】【读者写者问题】

&#x1f493;博主CSDN主页:麻辣韭菜&#x1f493;   ⏩专栏分类&#xff1a;Linux初窥门径⏪   &#x1f69a;代码仓库:Linux代码练习&#x1f69a;   &#x1f339;关注我&#x1faf5;带你学习更多Linux知识   &#x1f51d; 目录 &#x1f3f3;️‍&#x1f308;前言 …

VSCode打开其它IDE项目注释显示乱码的解决方法

问题描述&#xff1a;VSCode打开Visual Studio&#xff08;或其它IDE&#xff09;工程&#xff0c;注释乱码&#xff0c;如下图所示&#xff1a; 解决方法&#xff1a;点击VSCode右下角的UTF-8&#xff0c;根据提示点击“通过编码重新打开”&#xff0c;再选择GB2312&#xff0…

JDBC链接kerberos认证的impala数据库报错问题解决

先上代码 public static Connection connectToImpala() {try {log.info("ketTabPath:" ketTabPath);log.info("krb5Path:" krb5Path);System.setProperty("java.security.krb5.conf", krb5Path);System.setProperty("sun.security.krb5.…

python如何输出list

直接输出list_a中的元素三种方法&#xff1a; list_a [1,2,3,313,1] 第一种 for i in range(len(list_a)):print(list_a[i]) 1 2 3 313 1 第二种 for i in list_a:print(i) 1 2 3 313 1 第三种&#xff0c;使用enumerate输出list_a方法&#xff1a; for i&#xff0c;j in enum…

线程池666666

1. 作用 线程池内部维护了多个工作线程&#xff0c;每个工作线程都会去任务队列中拿取任务并执行&#xff0c;当执行完一个任务后不是马上销毁&#xff0c;而是继续保留执行其它任务。显然&#xff0c;线程池提高了多线程的复用率&#xff0c;减少了创建和销毁线程的时间。 2…

【FFmpeg】avformat_find_stream_info函数

【FFmpeg】avformat_find_stream_info 1.avformat_find_stream_info1.1 初始化解析器&#xff08;av_parser_init&#xff09;1.2 查找探测解码器&#xff08;find_probe_decoder&#xff09;1.3 尝试打开解码器&#xff08;avcodec_open2&#xff09;1.4 读取帧&#xff08;re…

Redis的使用(二)redis的命令总结

1.概述 这一小节&#xff0c;我们主要来研究一下redis的五大类型的基本使用&#xff0c;数据类型如下&#xff1a; redis我们接下来看一看这八种类型的基本使用。我们可以在redis的官网查询这些命令:Commands | Docs,同时我们也可以用help 数据类型查看命令的帮助文档。 2. 常…

新鲜出炉!恭喜这 5 位同学中选 NebulaGraph 社区 2024 开源之夏项目!

开源之夏是中国科学院软件研究所发起的“开源软件供应链点亮计划”系列暑期活动&#xff0c;旨在鼓励高校学生积极参与开源软件的开发维护&#xff0c;促进优秀开源软件社区的蓬勃发展。活动联合各大开源社区&#xff0c;针对重要开源软件的开发与维护提供项目开发任务&#xf…

【JPCS出版,PSESG 2024,8月16-18】2024年电力系统工程与智能电网国际学术会议

2024年电力系统工程与智能电网国际学术会议(PSESG 2024)于2024年8月16-18日在中国北京隆重召开。 会议旨在为从事“电力系统工程”、“智能电网”、“储能技术”等领域的专家学者、工程技术人员、研发人员提供一个共享科研成果和前沿技术&#xff0c;了解学术发展趋势&#xf…