PID控制算法介绍及使用举例

PID 控制算法是一种常用的反馈控制算法,用于控制系统的稳定性和精度。PID 分别代表比例(Proportional)、积分(Integral)和微分(Derivative),通过组合这三个部分来调节控制输出,以使系统的实际输出值尽可能接近预期的参考值。

算法介绍

PID(比例-积分-微分)控制算法是一种广泛使用的控制算法,用于调节系统的输出以匹配所需的参考输入。PID 控制器基于三个基本组件来计算其控制输出:比例(Proportional)、积分(Integral)和微分(Derivative)。

  1. 比例(P)控制
    • 比例控制是最简单的控制形式。控制器的输出是输入误差的一个比例。误差是参考输入与实际系统输出之间的差异。
    • 比例控制可以快速响应误差,但它不能消除稳态误差(即系统稳定后仍然存在的误差)。
    • 如果比例系数设置得太大,系统可能会变得不稳定,出现振荡。
  2. 积分(I)控制
    • 积分控制考虑了过去的误差。它通过对误差进行积分来消除稳态误差。
    • 积分控制有助于减小系统稳定后的误差,但也可能导致系统响应变慢,并可能增加超调(即系统输出超过参考输入的情况)。
    • 如果积分系数设置得太大,系统可能变得对扰动非常敏感,甚至可能产生积分饱和现象(即积分项累积到过大,导致系统响应异常)。
  3. 微分(D)控制
    • 微分控制基于误差的变化率来预测未来的误差,并据此调整控制输出。
    • 微分控制有助于加快系统的响应速度,减少超调,并增加系统的稳定性。
    • 然而,微分控制对噪声非常敏感,因为噪声通常会导致误差的突然变化,这可能被误认为是误差的变化率。

PID 的基本公式:

Output = K_p * Error + K_i * Integral(Error) + K_d * Derivative(Error)

其中,Error 表示期望值与实际值之间的偏差,(K_p)、(K_i) 和 (K_d) 分别表示比例、积分和微分部分的系数。如何使用 PID 控制算法:

  1. 确定系统模型和参数 在应用 PID 控制算法前,需要确定控制对象的数学模型和相关参数,例如比例系数 (K_p)、积分时间 (T_i)、微分时间 (T_d) 等。

  2. 实现 PID 控制器 在代码中实现 PID 控制器,通常需要记录上一次的误差值以及积分值,以便计算出下一次的控制输出。

 

PID 控制器的调整

调整 PID 控制器的参数(即 K_pK_i 和 K_d)是 PID 控制中的关键任务。这通常涉及到一些试验和误差调整,或者使用更先进的调优方法。

对不同工况和“场景”下往往需要设置不同的PID形式,不同的PID参数达到预期控制效果,其实就是需要人工经验设定规则来适应不同工况,所以不能算是自动的控制;相信用过PID的都知道一套口诀,在实际调参依赖口诀调参,然后调出一个理想的控制曲线,调参过程依赖人工。

也可以借助野火的 PID调试助手工具来调试pid的参数。

代码示例 

class PIDController {
public:
    double compute(double setpoint, double measurement) {
        double error = setpoint - measurement;
        integral += error * dt;
        derivative = (error - prevError) / dt;
        output = Kp * error + Ki * integral + Kd * derivative;
        prevError = error;
        return output;
    }

private:
    double Kp = 1.0;
    double Ki = 0.1;
    double Kd = 0.01;

    double integral = 0.0;
    double derivative = 0.0;
    double prevError = 0.0;
    double output = 0.0;
    double dt = 0.1; // 采样时间
};
  1. 调试和调整参数 调试 PID 控制器需要不断调整参数,观察实际输出值与期望值之间的偏差,逐步优化参数,以实现系统的稳定控制。

  2. 实时应用 在实际控制系统中,将计算得到的 PID 控制器的输出值应用到相应的执行机构或系统中,以实现期望的控制效果。

以下是一个简单的 C++ 示例,演示如何实现一个基本的 PID 控制器,并在一个简单的模拟系统中应用该控制器。这个例子中,模拟一个以恒定速度运动的小车,通过 PID 控制器调节小车的速度,使其尽快达到期望速度。注意,这只是一个简单的演示,实际系统中可能需要更复杂的控制逻辑和参数调整。

#include <iostream>


class PIDController {
public:
    PIDController(double kp, double ki, double kd, double dt) : Kp(kp), Ki(ki), Kd(kd), dt(dt) {}

    double compute(double setpoint, double measurement) {
        double error = setpoint - measurement;
        integral += error * dt;
        derivative = (error - prevError) / dt;
        output = Kp * error + Ki * integral + Kd * derivative;
        prevError = error;
        return output;
    }

private:
    double Kp;
    double Ki;
    double Kd;
    double dt;

    double integral = 0.0;
    double derivative = 0.0;
    double prevError = 0.0;
    double output = 0.0;
};

class Car {
public:
    void setSpeed(double speed) {
        currentSpeed = speed;
    }

    void update() {
        // 模拟小车运动,这里假设小车以固定加速度加速到目标速度
        currentSpeed += acceleration;
        std::cout << "Current speed: " << currentSpeed << std::endl;
    }

    double getSpeed() const {
        return currentSpeed;
    }

private:
    double currentSpeed = 0.0;
    double acceleration = 0.1;
};

int main() {
    PIDController pid(0.5, 0.01, 0.1, 0.1); // 设置 PID 控制器的参数
    Car car;

    double targetSpeed = 10.0; // 设置目标速度

    for (int i = 0; i < 100; ++i) {
        double speedError = targetSpeed - car.getSpeed();
        double controlOutput = pid.compute(targetSpeed, car.getSpeed());
        std::cout << "speedError: " << speedError << std::endl;
        std::cout << "controlOutput: " << controlOutput << std::endl;
        car.setSpeed(9+i/50.0); // 应用控制输出调节小车速度
        car.update();
    }

    return 0;
}

在这个简单的示例中,创建了一个 PIDController 类来模拟 PID 控制器的行为,然后创建了一个 Car 类来模拟一个运动的小车。在主函数中,设置 PID 控制器的参数,并循环调用 PID 控制器来调节小车的速度,直到达到目标速度为止。

注意,实际的 PID 控制器需要根据具体的控制对象和系统需求进行调整和优化。

c语言实现

#include "pid.h"
/**************************************************************
 *批量复位PID函数
 * @param[in] 
 * @param[out] 
 * @return     
 ***************************************************************/	
void pidRest(PidObject **pid,const uint8_t len)
{
	uint8_t i;
	for(i=0;i<len;i++)
	{
	  	pid[i]->integ = 0;
	    pid[i]->prevError = 0;
	    pid[i]->out = 0;
			pid[i]->offset = 0;
	}
}

/**************************************************************
 * Update the PID parameters.
 *
 * @param[in] pid         A pointer to the pid object.
 * @param[in] measured    The measured value
 * @param[in] updateError Set to TRUE if error should be calculated.
 *                        Set to False if pidSetError() has been used.
 * @return PID algorithm output
 ***************************************************************/	
void pidUpdate(PidObject* pid,const float dt)
{
	 float error;
	 float deriv;
	
    error = pid->desired - pid->measured + pid->offset; //当前角度与实际角度的误差

    pid->integ += error * dt;	 //误差积分累加值
	
	//  pid->integ = LIMIT(pid->integ,pid->IntegLimitLow,pid->IntegLimitHigh); //进行积分限幅

    deriv = (error - pid->prevError)/dt;  //前后两次误差做微分
	
    pid->out = pid->kp * error + pid->ki * pid->integ + pid->kd * deriv;//PID输出
	
		//pid->out = LIMIT(pid->out,pid->OutLimitLow,pid->OutLimitHigh); //输出限幅
		
    pid->prevError = error;  //更新上次的误差
		
}

/**************************************************************
 *  CascadePID
 * @param[in] 
 * @param[out] 
 * @return     
 ***************************************************************/	
void CascadePID(PidObject* pidRate,PidObject* pidAngE,const float dt)  //串级PID
{	 
	pidUpdate(pidAngE,dt);    //先计算外环
	pidRate->desired = pidAngE->out;
	pidUpdate(pidRate,dt);    //再计算内环	
}

应用示例

#include "pid.h"
#include <stdio.h>

int main()
{
	
	printf("hello pid test \n");
	float target_water_level = 100.0;  // 目标水位10L
    float feedback_water_level = 0.0;  // 初始水位0L
    
    PidObject pidRate;
	
	PidObject *pPidObject[]={&pidRate};
	pidRest(pPidObject,1);
	
    pidRate.kp = 0.1f;  // 比例系数
	pidRate.ki = 0.01f; // 积分系数
	pidRate.kd = 0.05f; // 微分系数
	
	float dt = 0.003;// 3ms 采样时间
	
	pidRate.measured = 0;
	pidRate.desired = 100.00;  //控制期望值 10L
	// 模拟控制循环
    for (int i = 0; i < 100; i++) {      // 模拟水位反馈
	
		pidUpdate(&pidRate,dt);   //pid处理
		
		feedback_water_level = 91+i/50.00;  // 模拟每次循环水位波动 (传感器水位采集反馈值)
		
        pidRate.measured = feedback_water_level;
		//pidRate.desired = pidRate.out;
        // 输出当前水位和阀门开度补偿值
        printf("Feedback Water Level: %.2fL, Valve Openness: %.2f\n", feedback_water_level, pidRate.out);
    }
	return 0;
}

为什么pwm可以调速

pwm占空比就是一个脉冲周期内有效电平在整个周期所占的比例。

通过调节PWM的占空比就能调节IO口上电压的持续性变化,因此也能够控制外设的功率进行持续性变化,也就能控制直流电机的转速快慢。

一种情况,对于电阻,直流电机来说,有占空比虽然从微观来说是波,但从宏观来说,就相当于将输入电压打个折扣再输出,输入5伏,占空比是50%,那么输出就是2.5伏,一般来说,直流电机的转速是和其输入电压成正比的。

还有种情况,就是通过连续改变PWM的占空比,将直流电切成大小不一,有规律的波形,宏观上形成不同频率的正弦波,这就叫斩波。通过斩波可以产生任意频率的交流电。

其他资源

PID算法详解及实例分析_pid控制原理详解及实例说明-CSDN博客

一文搞懂PID控制算法_pid算法-CSDN博客

什么是PID控制?

PWM原理 PWM频率与占空比详解-CSDN博客

电机控制进阶——PID速度控制 - 知乎

通俗易懂!讲解PID! - 知乎

编码器计数原理与电机测速原理——多图解析 - 知乎

电机PID控制补充篇-野火上位机串口协议介绍 - 知乎

电机控制进阶——PID速度控制

野火串口调试助手PID功能(文末有工程链接)_野火多功能调试助手-CSDN博客

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

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

相关文章

【成都信息工程大学】只考程序设计!成都信息工程大学计算机考研考情分析!

成都信息工程大学&#xff08;Chengdu University of Information Technology&#xff09;&#xff0c;简称“成信大”&#xff0c;由中国气象局和四川省人民政府共建&#xff0c;入选中国首批“卓越工程师教育培养计划”、“2011计划”、“中西部高校基础能力建设工程”、四川…

eNSP学习——VRRP基础配置、多备份组、跟踪接口及认证

目录 主要命令 实验一、VRRP基础配置 原理概述 实验目的 实验内容 实验拓扑 实验编址 实验步骤 1、基本配置 2、部署OSPF网络 3、配置VRRP协议 4、验证VRRP主备切换 实验二、配置VRRP多备份组 原理概述 实验目的 实验内容 实验拓扑 实验编址 实验步骤 1、基本…

LeetCode-82. 删除排序链表中的重复元素 II【链表 双指针】

LeetCode-82. 删除排序链表中的重复元素 II【链表 双指针】 题目描述&#xff1a;解题思路一&#xff1a;用一个cur即可实现去重cur.next cur.next.next背诵版&#xff1a;解题思路三&#xff1a;0 题目描述&#xff1a; 给定一个已排序的链表的头 head &#xff0c; 删除原始…

数据结构:模拟栈

数据结构&#xff1a;模拟栈 题目描述参考代码 题目描述 输入样例 10 push 5 query push 6 pop query pop empty push 4 query empty输出样例 5 5 YES 4 NO参考代码 #include <iostream>using namespace std;const int N 1000010;int m, x; int q[N]; string op; int…

2024年数字化经济与智慧金融国际会议(ICDESF 2024)

2024 International Conference on Digital Economy and Smart Finance 【1】大会信息 大会时间&#xff1a;2024-07-22 大会地点&#xff1a;中国成都 截稿时间&#xff1a;2024-07-10(以官网为准&#xff09; 审稿通知&#xff1a;投稿后2-3日内通知 会议官网&#xff1a;h…

使用libgpiod库中的事件方式监测多个输入事件

Linux下要同时检测多个GPIO输入的方法有很多&#xff0c;这里我使用libgpiod库中的API实现多个GPIO输入检测&#xff0c;可以达到类似STM32利用外部中断实现输入事件检测的效果&#xff0c;示例代码如下所示&#xff1a; /* 示例使用的libgpiod库版本为V1.2.1 */ //示例功能是…

前端逆向之下载canvas引用的图片

前端逆向之下载canvas引用的图片 一、来源二、解决三、如果在Network这里也找不到呢&#xff1f; 一、来源 当我们用dom检查器的时候无法选中想要扒下来的图片&#xff0c;只能选中canvas&#xff0c;这种时候该怎么办呢&#xff1f; 二、解决 这个时候应该换个脑子&#xf…

SAP PP学习笔记17 - MTS(Make-to-Stock) 按库存生产(策略70)

上几章讲了几种策略&#xff0c;策略10&#xff0c;11&#xff0c;30&#xff0c;40。 SAP PP学习笔记14 - MTS&#xff08;Make-to-Stock) 按库存生产&#xff08;策略10&#xff09;&#xff0c;以及生产计划的概要-CSDN博客 SAP PP学习笔记15 - MTS&#xff08;Make-to-St…

MySQL的联合索引及案例分析

1. 联合索引 关于联合索引的详解参考博客【Mysql-----联合索引和最左匹配】&#xff0c;包含讲解 最左匹配 联合索引失效的情况 不遵循最左匹配原则范围查询右边失效原理like索引失效原理 比较关注的点在于&#xff1a; 对A、B、C三个字段创建一个联合索引&#xff08;A, …

计算机SCI期刊,中科院3区,对国人相当友好

一、期刊名称 Image and Vision Computing 二、期刊简介概况 期刊类型&#xff1a;SCI 学科领域&#xff1a;计算机科学 影响因子&#xff1a;4.7 中科院分区&#xff1a;3区 三、期刊征稿范围 图像和视觉计算的主要目标是为图像解释和计算机视觉各个方面的高质量理论和应…

跟着大佬学RE(四)

几个API函数 [ACTF新生赛2020]Universe_final_answer 一个很多方程组的函数&#xff0c;还有一个嗯&#xff0c;对input进行一些操作的函数 嗯&#xff0c;确实方程解出来得到 key 直接运行就可以得到 flag 了&#xff0c;不过还是去分析了一下。 v22 __readfsqword(0x28u);…

2024.6.5

1、react原理学习&#xff0c; hook、fiber 2、瀑布流组件完善 3、代码随想录二刷

6.4分享会

介绍 严格意义上讲&#xff0c;我们在 JavaScript 阶段学习的知识绝大部分属于 ECMAScript 的知识体系&#xff0c;ECMAScript 简称 ES 它提供了一套语言标准规范&#xff0c;如变量、数据类型、表达式、语句、函数等语法规则都是由 ECMAScript 规定的。浏览器将 ECMAScript 大…

opencv快速安装以及各种查看版本命令

安装opencv并查看其版本&#xff0c;直接通过一个可执行文件实现。 #!/bin/bashwget https://codeload.github.com/opencv/opencv/zip/3.4 -O opencv-3.4.zip && unzip opencv-3.4.zip && cd opencv-3.4 && \mkdir build && cd build &&a…

亮数据——全球网络数据一站式平台

在我们日常的项目开发和研究中&#xff0c;数据获取总是一个让人既爱又恨的话题。找到一个既高效又安全的工具&#xff0c;简直就像是在茫茫沙漠中找到绿洲。近期&#xff0c;我测评了&#xff0c;数十家数据获取工具&#xff0c;最后锁定了&#xff0c;亮数据&#xff0c;本篇…

攻防世界—webbaby详解

1.ssrf注入漏洞 ssrf&#xff08;服务端请求伪造&#xff09;是一种安全漏洞&#xff0c;攻击者通过该漏洞向受害服务器发出伪造的请求&#xff0c;从而访问并获取服务器上的资源&#xff0c;常见的ssrf攻击场景包括访问内部网络的服务&#xff0c;执行本地文件系统命令&#…

如何在 Java 中使用 JOptionPane 显示消息对话框

在 Java 开发中&#xff0c;JOptionPane 是一个非常实用的类&#xff0c;可以用来显示各种类型的对话框&#xff0c;例如信息对话框、警告对话框、错误对话框等。今天&#xff0c;我们将深入探讨如何使用 JOptionPane.showMessageDialog 方法来显示消息对话框&#xff0c;以及如…

【Unity | Editor强化工具】资产快速访问工具

经常在Project窗口中翻找资产相对麻烦&#xff0c;Unity自带的Favorite功能又和Project窗口强绑定&#xff0c;且只能在双列视图下使用&#xff0c;故制作了一个可以在独立窗口中列举常用资产的小工具&#xff1a; Unity Asset Quick Access 。 CSDN弄了个Github加速计划&…

天润融通,荣获2024中国AI应用层创新企业

AI技术发展日新月异&#xff0c;可谓“AI一天&#xff0c;人间一年”。 从2023年到2024年&#xff0c;短短一年的时间&#xff0c;大模型技术的发展就已经逐步从追求“技术突破”转向了追求“应用落地”。如何将大模型的技术与企业的生产、运营、销售等场景结合起来&#xff0…

C语言的printf输出问题

看到这段代码的时候&#xff0c;想到这个printf输出的值是多少? 若您想到的答案是1-2&#xff0c;真的是这样吗&#xff1f; #include <stdio.h>int main(int argc, char *argv[]) {int i 1;printf("%d-%d\r\n", i, i);return 0; }先了解一个知识点&#xf…