PID算法在流量控制中的应用

目录

增量式或位置式

目录

增量式或位置式

PID控制周期

T1 时间

T2 约4ms

PID C代码


     

最近有小伙伴向我提问关于PID的问题:通过比例阀控制水流速度(流量),

  1. 使用增量式还是位置式 PID?
  2. 他的比例法驱动频率是500Hz那么PID的控制周期应该选多少?

增量式或位置式

借用知乎网友的一个回答:

通过带有调节阀的管路向水箱注水,控制输出u为阀门开度,如果控制目标是注水速度(流量),则采用增量控制,达到目标流量后阀门会保持;如果控制目标是水位,则采用位置式,达到目标水位后阀门会基本处于零位(积分作用下会保持一定开度)。

可以看出,当控制输出u,和控制目标是一一对应关系(一定阀门开度对应一定流量)时,采用增量式;当控制输出u,影响的是控制目标的速度(水位变化的速度,即流量)时,采用位置控制。

又比如,通过油门控制车速,也是增量控制,根据期望速度加减油门,速度到了保持住。

我们这里控制水恒流量,应该选用增量式。

PID控制周期

        

 周期 = T1(比例阀变化 -> 物理压力变化量) + T2(压力电压变化量 -> ADC量化时间 )

T1 时间

        这样具体要根据实际情况改变了,可以先按照100ms去调试。

T2 约4ms

可以查阅数据手册

压力传感器:

估算为2ms

MCU

算上软件滤波,ADC量化时间估算为2ms

咱们这里先采用100ms~200ms的PID控制周期去调PID参数。

PID C代码

/*This file has been prepared for Doxygen automatic documentation generation.*/
/*! \file *********************************************************************
 *
 * \brief Header file for pid.c.
 *
 * - File:               pid.h
 * - Compiler:           IAR EWAAVR 4.11A
 * - Supported devices:  All AVR devices can be used.
 * - AppNote:            AVR221 - Discrete PID controller
 *
 * \author               Atmel Corporation: http://www.atmel.com \n
 *                       Support email: avr@atmel.com
 *
 * $Name$
 * $Revision: 456 $
 * $RCSfile$
 * $Date: 2006-02-16 12:46:13 +0100 (to, 16 feb 2006) $
 *****************************************************************************/

#ifndef PID_H
#define PID_H

#include "stdint.h"

#define SCALING_FACTOR  128

/*! \brief PID Status
 *
 * Setpoints and data used by the PID control algorithm
 */
typedef struct PID_DATA{
  //! Last process value, used to find derivative of process value.
  int16_t lastProcessValue;
  //! Summation of errors, used for integrate calculations
  int32_t sumError;
  //! The Proportional tuning constant, multiplied with SCALING_FACTOR
  int16_t P_Factor;
  //! The Integral tuning constant, multiplied with SCALING_FACTOR
  int16_t I_Factor;
  //! The Derivative tuning constant, multiplied with SCALING_FACTOR
  int16_t D_Factor;
  //! Maximum allowed error, avoid overflow
  int16_t maxError;
  //! Maximum allowed sumerror, avoid overflow
  int32_t maxSumError;
} pidData_t;

/*! \brief Maximum values
 *
 * Needed to avoid sign/overflow problems
 */
// Maximum value of variables
#define MAX_INT         INT16_MAX
#define MAX_LONG        INT32_MAX
#define MAX_I_TERM      (MAX_LONG / 2)

// Boolean values
#define FALSE           0
#define TRUE            1

void pid_Init(int16_t p_factor, int16_t i_factor, int16_t d_factor, struct PID_DATA *pid);
int16_t pid_Controller(int16_t setPoint, int16_t processValue, struct PID_DATA *pid_st);
void pid_Reset_Integrator(pidData_t *pid_st);

#endif
/*This file has been prepared for Doxygen automatic documentation generation.*/
/*! \file *********************************************************************
 *
 * \brief General PID implementation for AVR.
 *
 * Discrete PID controller implementation. Set up by giving P/I/D terms
 * to Init_PID(), and uses a struct PID_DATA to store internal values.
 *
 * - File:               pid.c
 * - Compiler:           IAR EWAAVR 4.11A
 * - Supported devices:  All AVR devices can be used.
 * - AppNote:            AVR221 - Discrete PID controller
 *
 * \author               Atmel Corporation: http://www.atmel.com \n
 *                       Support email: avr@atmel.com
 *
 * $Name$
 * $Revision: 456 $
 * $RCSfile$
 * $Date: 2006-02-16 12:46:13 +0100 (to, 16 feb 2006) $
 *****************************************************************************/

#include "pid.h"
#include "stdint.h"

/*! \brief Initialisation of PID controller parameters.
 *
 *  Initialise the variables used by the PID algorithm.
 *
 *  \param p_factor  Proportional term.
 *  \param i_factor  Integral term.
 *  \param d_factor  Derivate term.
 *  \param pid  Struct with PID status.
 */
void pid_Init(int16_t p_factor, int16_t i_factor, int16_t d_factor, struct PID_DATA *pid)
// Set up PID controller parameters
{
  // Start values for PID controller
  pid->sumError = 0;
  pid->lastProcessValue = 0;
  // Tuning constants for PID loop
  pid->P_Factor = p_factor;
  pid->I_Factor = i_factor;
  pid->D_Factor = d_factor;
  // Limits to avoid overflow
  pid->maxError = MAX_INT / (pid->P_Factor + 1);
  pid->maxSumError = MAX_I_TERM / (pid->I_Factor + 1);
}


/*! \brief PID control algorithm.
 *
 *  Calculates output from setpoint, process value and PID status.
 *
 *  \param setPoint  Desired value.
 *  \param processValue  Measured value.
 *  \param pid_st  PID status struct.
 */
int16_t pid_Controller(int16_t setPoint, int16_t processValue, struct PID_DATA *pid_st)
{
  int16_t error, p_term, d_term;
  int32_t i_term, ret, temp;

  error = setPoint - processValue;

  // Calculate Pterm and limit error overflow
  if (error > pid_st->maxError){
    p_term = MAX_INT;
  }
  else if (error < -pid_st->maxError){
    p_term = -MAX_INT;
  }
  else{
    p_term = pid_st->P_Factor * error;
  }

  // Calculate Iterm and limit integral runaway
  temp = pid_st->sumError + error;
  if(temp > pid_st->maxSumError){
    i_term = MAX_I_TERM;
    pid_st->sumError = pid_st->maxSumError;
  }
  else if(temp < -pid_st->maxSumError){
    i_term = -MAX_I_TERM;
    pid_st->sumError = -pid_st->maxSumError;
  }
  else{
    pid_st->sumError = temp;
    i_term = pid_st->I_Factor * pid_st->sumError;
  }

  // Calculate Dterm
  d_term = pid_st->D_Factor * (pid_st->lastProcessValue - processValue);

  pid_st->lastProcessValue = processValue;

  ret = (p_term + i_term + d_term) / SCALING_FACTOR;
  if(ret > MAX_INT){
    ret = MAX_INT;
  }
  else if(ret < -MAX_INT){
    ret = -MAX_INT;
  }

  return((int16_t)ret);
}

/*! \brief Resets the integrator.
 *
 *  Calling this function will reset the integrator in the PID regulator.
 */
void pid_Reset_Integrator(pidData_t *pid_st)
{
  pid_st->sumError = 0;
}

/*This file has been prepared for Doxygen automatic documentation generation.*/
/*! \file *********************************************************************
 *
 * \brief Example of use of general PID implementation for AVR.
 *
 * Example of how to setup and use the general PID implementation in pid.c.
 *
 * - File:               main.c
 * - Compiler:           IAR EWAAVR 4.11A
 * - Supported devices:  All AVR devices can be used.
 * - AppNote:            AVR221 - Discrete PID controller
 *
 * \author               Atmel Corporation: http://www.atmel.com \n
 *                       Support email: avr@atmel.com
 *
 * $Name$
 * $Revision: 456 $
 * $RCSfile$
 * $Date: 2006-02-16 12:46:13 +0100 (to, 16 feb 2006) $
 *****************************************************************************/

#include <inavr.h>
#include <ioavr.h>
#include "stdint.h"
#include "pid.h"

/*! \brief P, I and D parameter values
 *
 * The K_P, K_I and K_D values (P, I and D gains)
 * need to be modified to adapt to the application at hand
 */
//! \xrefitem todo "Todo" "Todo list"
#define K_P     1.00
//! \xrefitem todo "Todo" "Todo list"
#define K_I     0.00
//! \xrefitem todo "Todo" "Todo list"
#define K_D     0.00

/*! \brief Flags for status information
 */
struct GLOBAL_FLAGS {
  //! True when PID control loop should run one time
  uint8_t pidTimer:1;
  uint8_t dummy:7;
} gFlags = {0, 0};

//! Parameters for regulator
struct PID_DATA pidData;

/*! \brief Sampling Time Interval
 *
 * Specify the desired PID sample time interval
 * With a 8-bit counter (255 cylces to overflow), the time interval value is calculated as follows:
 * TIME_INTERVAL = ( desired interval [sec] ) * ( frequency [Hz] ) / 255
 */
//! \xrefitem todo "Todo" "Todo list"
#define TIME_INTERVAL   157

/*! \brief Timer interrupt to control the sampling interval
 */
#pragma vector = TIMER0_OVF_vect
__interrupt void TIMER0_OVF_ISR( void )
{
  static uint16_t i = 0;
  if(i < TIME_INTERVAL)
    i++;
  else{
    gFlags.pidTimer = TRUE;
    i = 0;
  }
}

/*! \brief Init of PID controller demo
 */
void Init(void)
{
  pid_Init(K_P * SCALING_FACTOR, K_I * SCALING_FACTOR , K_D * SCALING_FACTOR , &pidData);

  // Set up timer, enable timer/counte 0 overflow interrupt
  TCCR0A = (1<<CS00);
  TIMSK0 = (1<<TOIE0);
  TCNT0 = 0;
}

/*! \brief Read reference value.
 *
 * This function must return the reference value.
 * May be constant or varying
 */
int16_t Get_Reference(void)
{
  return 8;
}

/*! \brief Read system process value
 *
 * This function must return the measured data
 */
int16_t Get_Measurement(void)
{
  return 4;
}

/*! \brief Set control input to system
 *
 * Set the output from the controller as input
 * to system.
 */
void Set_Input(int16_t inputValue)
{
  ;
}


/*! \brief Demo of PID controller
 */
void main(void)
{
  int16_t referenceValue, measurementValue, inputValue;
  Init();
  __enable_interrupt();

  while(1){

    // Run PID calculations once every PID timer timeout
    if(gFlags.pidTimer)
    {
      referenceValue = Get_Reference();
      measurementValue = Get_Measurement();

      inputValue = pid_Controller(referenceValue, measurementValue, &pidData);

      Set_Input(inputValue);

      gFlags.pidTimer = FALSE;
    }
  }
}

/*! \mainpage
 * \section Intro Introduction
 * This documents data structures, functions, variables, defines, enums, and
 * typedefs in the software for application note AVR221.
 *
 * \section CI Compilation Info
 * This software was written for the IAR Embedded Workbench 4.11A.
 *
 * To make project:
 * <ol>
 * <li> Add the file main.c and pid.c to project.
 * <li> Under processor configuration, select desired Atmel AVR device.
 * <li> Enable bit definitions in I/O include files
 * <li> High optimization on speed is recommended for best performance
 * </ol>
 *
 * \section DI Device Info
 * The included source code is written for all Atmel AVR devices.
 *
 * \section TDL ToDo List
 * \todo Put in own code in:
 * \ref Get_Reference(void), \ref Get_Measurement(void) and \ref Set_Input(int16_t inputValue)
 *
 * \todo Modify the \ref K_P (P), \ref K_I (I) and \ref K_D (D) gain to adapt to your application
 * \todo Specify the sampling interval time \ref TIME_INTERVAL
 */

这是一份AVR的官方增量式代码,也很容易移植到不同的MCU,这份代码我也有实际产品使用过,非常好。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

linux环境搭建

&#x1f388;个人主页:&#x1f388; :✨✨✨初阶牛✨✨✨ &#x1f43b;推荐专栏: &#x1f354;&#x1f35f;&#x1f32f;C语言进阶 &#x1f511;个人信条: &#x1f335;知行合一 &#x1f349;本篇简介:>:介绍学习如何使用云服务器搭建Linux的环境. 前言 linux介绍…

【计算机网络中ip概念总结】【平时我们说的ip 到底是什么】【计算机网络中 ip地址是什么】

专注 效率 记忆 预习 笔记 复习 做题 欢迎观看我的博客&#xff0c;如有问题交流&#xff0c;欢迎评论区留言&#xff0c;一定尽快回复&#xff01;&#xff08;大家可以去看我的专栏&#xff0c;是所有文章的目录&#xff09;   文章字体风格&#xff1a; 红色文字表示&#…

Linux系统之编译安装python3

Linux系统之编译安装python3 一、python3介绍1. python3简介2. python3特点 二、检查本地环境1. 检查本地操作系统版本2. 检查内核版本3. 检查当前python版本 三、安装前准备工作四、下载python最新版本源码包1. 访问python官网2. 创建下载目录3. 下载python源码包4. 解压pytho…

Redis 常见面试题

1. 认识Redis Redis是一个开源的内存数据结构存储&#xff0c;Redis是一个基于内存的数据库&#xff0c;对数据的读写都在内存中完成&#xff0c;因此数据读写速度非常快&#xff0c;常用于缓存&#xff0c;分布式锁等&#xff0c;MySQL的表数据都存储在 t_order.ibd&#xff…

国内可以免费使用的GPT

一、wetab新标签页 教程&#xff1a;https://diwlwltzssn.feishu.cn/docx/MnHhdvxATomBnMxfas2cm8wWnVd 装GPT界面&#xff1a;https://microsoftedge.microsoft.com/addons/detail/wetab%E5%85%8D%E8%B4%B9chatgpt%E6%96%B0%E6%A0%87%E7%AD%BE%E9%A1%B5/bpelnogcookhocnaokfp…

华为OD机试真题(Java),跳跃游戏 II(100%通过+复盘思路)

一、题目描述 给定一个长度为 n 的 0 索引整数数组 nums。初始位置为 nums[0]。 每个元素 nums[i] 表示从索引 i 向前跳转的最大长度。换句话说,如果你在 nums[i] 处,你可以跳转到任意 nums[i + j] 处: 0 <= j <= nums[i]0i + j <返回到达 nums[n - 1] 的最小跳跃…

C++学习笔记(四): 类、头文件、对象

一个类定义了一个类型&#xff0c;以及与其关联的一组操作。所谓类&#xff0c;是用户自定义的数据类型。 类机制是C最重要的特性之一。实际上&#xff0c;C最初的一个设计焦点就是能定义使用上像内置类型一样自然的类类型&#xff08;class type&#xff09;。 类的定义一般分…

Java之旅(三)

Java 输出&#xff1a;println()、print() 使用 println() 方法在 Java 中输出值或打印文本。 System.out.println("Hello World!"); println() 可以根据需要添加任意数量的方法。请注意&#xff0c;它将为每个方法添加一个新行&#xff1a; System.out.println(&…

Unity2D骨骼动画制作之单张图片编辑

1、打开骨骼制作面板 在Sprite Editor左侧选项&#xff0c;选择Skinning Editor 2、 &#xff08;1&#xff09;骨骼制作 Preview Pose 预览模式&#xff0c;可以预览动作并不会真正的改变设置 Reset Pose 将角色骨骼和关节恢复到原始位置 Edit Bone 编辑骨骼&#xff0c;…

SUSE系统上安装HANA

一:安装SUSE操作系统 1.1 准备安装镜像 SLE-15-SP1-安装程序-DVD-x86_64-GM-DVD1 SLE-15-SP1-软件包-x86_64-GM-DVD1 SAP HANA安装文件 IMDB_SERVER20_032_0-80002031.SAR 1.2 引导系统 1.3 选择要安装的产品 SUSE Linux Enterprise Server for SAP Applications 15 SP…

【MySQL】MySQL间隙锁--幻读解决原理

文章目录 一、间隙锁概念二、测试间隙锁范围加锁三、测试等值间隙锁 一、间隙锁概念 当我们用范围条件而不是相等条件检索数据&#xff0c; 并请求共享或排他锁时&#xff0c;InnoDB 会给符合条件的已有数据记录的索引项加锁&#xff1b;对于键值在条件范围内但并不存在的记录…

chatgpt赋能python:用Python做股票分析

用Python做股票分析 在当今的股市中&#xff0c;数据分析和预测已经变得十分重要。Python作为最流行的编程语言之一&#xff0c;不仅易于学习&#xff0c;还有非常强大的数据处理和分析能力。在本文中&#xff0c;我们将探讨如何用Python进行股票分析。 数据收集 要进行股票…

如何利用宝塔面板快速搭建Wordpress网站?

本章教程&#xff0c;主要介绍一下&#xff0c;如何利用宝塔面板快速搭建Wordpress网站。 目录 一、 前置条件 二、 打开宝塔面板 三、解析域名 四、安装界面 五、主题安装 六、网站预览 一、 前置条件 需要准备一台Linux服务器&#xff0c;系统版本使用centos 7.X。 使用…

【项目】ROS下使用乐视深度相机LeTMC-520

本文主要记录如何在ros下使用乐视深度相机。乐视三合一体感摄像头LeTMC-520其实就是奥比中光摄像头&#xff08;Orbbec Astra Pro&#xff09; 系统&#xff1a;Ubuntu20.04 这款相机使用uvc输入彩色信息&#xff0c;需要使用libuvc、libuvc_ros才能在ROS上正常使用彩色功能。…

Vue.js 中的父子组件通信方式

Vue.js 中的父子组件通信方式 在 Vue.js 中&#xff0c;组件是构建应用程序的基本单元。当我们在应用程序中使用组件时&#xff0c;组件之间的通信是非常重要的。在 Vue.js 中&#xff0c;父子组件通信是最常见的组件通信方式之一。在本文中&#xff0c;我们将讨论 Vue.js 中的…

spring boot使用elasticsearch分词,排序,分页,高亮简单示例

目录 1. 创建ES实体2. 创建查询实体3. 查询方法实现3.1 核心代码3.2 构建查询条件3.2.1 关键词分词 3.3 高亮处理 4.完整查询代码展示 记&#xff0c;写一个简单的es分词demo,es版本6.8.12 如果使用es7有些方法可能会有所改变&#xff0c;请参考7的文档 es安装教程&#xff1a;…

隔离驱动芯片SLMi332兼容光耦隔离驱动器时的注意事项

数明深力科SLMi33x系列SLMi332是一款兼容光耦带DESAT保护功能的IGBT/SiC隔离驱动器。内置快速去饱和(DESAT) 故障检测功能、米勒钳位功能、漏极开路故障反馈、软关断功能以及可选择的自恢复模式&#xff0c;兼容光耦隔离驱动器。 SLMi33x系列SLMi332的DESAT阈值为6.5V&#xf…

大数据---聚类分析概述及聚类评估

聚类概述: 什么是聚类&#xff1f; 是把数据对象集合按照相似性划分成多个子集的过程。每个子集是一个簇&#xff08;cluster&#xff09;&#xff0c;分类的最终效果&#xff1a;使得簇中的对象彼此相似&#xff0c;但与其他簇中的对象相异。聚类是无监督学习&#xff0c;因…

缓存被穿透了怎么办?

首先来了解几个概念&#xff1a; 缓存穿透&#xff1a;大量请求根本不存在的key 缓存雪崩&#xff1a;redis中大量key集体过期 缓存击穿&#xff1a;redis中一个热点key过期&#xff08;大量用户访问该热点key&#xff0c;但是热点key过期&#xff09; 穿透解决方案 对空值…

如何有效和快速清理C盘

电脑在运行过程中会产生磁盘碎片&#xff0c;时间一长垃圾文件就会越多。而且我们平常不敢乱清理C盘中的文件&#xff0c;以免因为误删导致系统出现故障&#xff0c;所以垃圾文件才肆意占用系统盘空间。不过我们可以选择系统自带的“磁盘清理”功能“制服”它&#xff0c;给C盘…