PID學習筆記5-雙環PID

在學習江協科技PID課程時,做一些筆記,對應視頻3-1,對應代碼:13

13-雙環PID定速定位置控制-代碼封裝

main.c:

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "LED.h"
#include "Timer.h"
#include "Key.h"
#include "RP.h"
#include "Motor.h"
#include "Encoder.h"
#include "Serial.h"
#include "PID.h"uint8_t KeyNum;int16_t Speed, Location;		//速度,位置/*定義PID結構體變量*/
PID_t Inner = {					//內環PID結構體變量,定義的時候同時給部分成員賦初值.Kp = 0.3,					//比例項權重.Ki = 0.3,					//積分項權重.Kd = 0,					//微分項權重.OutMax = 100,				//輸出限幅的最大值.OutMin = -100,				//輸出限幅的最小值
};PID_t Outer = {					//外環PID結構體變量,定義的時候同時給部分成員賦初值.Kp = 0.3,					//比例項權重.Ki = 0,					//積分項權重.Kd = 0.4,					//微分項權重.OutMax = 20,				//輸出限幅的最大值.OutMin = -20,				//輸出限幅的最小值
};int main(void)
{/*模塊初始化*/OLED_Init();		//OLED初始化Key_Init();			//非阻塞式按鍵初始化Motor_Init();		//電機初始化Encoder_Init();		//編碼器初始化RP_Init();			//電位器旋鈕初始化Serial_Init();		//串口初始化,波特率9600Timer_Init();		//定時器初始化,定時中斷時間1ms/*OLED打印一個標題*/OLED_Printf(0, 0, OLED_8X16, "2*PID Control");OLED_Update();while (1){/*按鍵修改目標值*//*解除以下注釋后,記得屏蔽電位器旋鈕修改目標值的代碼*//*調節內環目標值使用Inner.Target,調節外環目標值使用Outer.Target*/
//		KeyNum = Key_GetNum();		//獲取鍵碼
//		if (KeyNum == 1)			//如果K1按下
//		{
//			Inner.Target += 10;		//目標值加10
//		}
//		if (KeyNum == 2)			//如果K2按下
//		{
//			Inner.Target -= 10;		//目標值減10
//		}
//		if (KeyNum == 3)			//如果K3按下
//		{
//			Inner.Target = 0;		//目標值歸0
//		}/*解除下面一段代碼的注釋,進行內環PID調參*//*進行內環PID調參時,請注釋掉外環控制內環的部分代碼*//*RP_GetValue函數返回電位器旋鈕的AD值,范圍:0~4095*//* 除4095.0可以把AD值歸一化,再乘上一個系數,可以調整到一個合適的范圍*/
//		Inner.Kp = RP_GetValue(1) / 4095.0 * 2;				//修改Kp,調整范圍:0~2
//		Inner.Ki = RP_GetValue(2) / 4095.0 * 2;				//修改Ki,調整范圍:0~2
//		Inner.Kd = RP_GetValue(3) / 4095.0 * 2;				//修改Kd,調整范圍:0~2
//		Inner.Target = RP_GetValue(4) / 4095.0 * 300 - 150;	//修改目標值,調整范圍:-150~150
//		
//		/*OLED顯示*/
//		OLED_Printf(0, 16, OLED_8X16, "Kp:%4.2f", Inner.Kp);			//顯示Kp
//		OLED_Printf(0, 32, OLED_8X16, "Ki:%4.2f", Inner.Ki);			//顯示Ki
//		OLED_Printf(0, 48, OLED_8X16, "Kd:%4.2f", Inner.Kd);			//顯示Kd
//		
//		OLED_Printf(64, 16, OLED_8X16, "Tar:%+04.0f", Inner.Target);	//顯示目標值
//		OLED_Printf(64, 32, OLED_8X16, "Act:%+04.0f", Inner.Actual);	//顯示實際值
//		OLED_Printf(64, 48, OLED_8X16, "Out:%+04.0f", Inner.Out);		//顯示輸出值
//		
//		OLED_Update();	//OLED更新,調用顯示函數后必須調用此函數更新,否則顯示的內容不會更新到OLED上
//		
//		Serial_Printf("%f,%f,%f\r\n", Inner.Target, Inner.Actual, Inner.Out);	//串口打印目標值、實際值和輸出值
//																	//配合SerialPlot繪圖軟件,可以顯示數據的波形/*解除下面一段代碼的注釋,進行外環PID調參*//*內環PID調參完成后,加上外環控制內環的部分代碼,再進行外環PID調參*//*RP_GetValue函數返回電位器旋鈕的AD值,范圍:0~4095*//* 除4095.0可以把AD值歸一化,再乘上一個系數,可以調整到一個合適的范圍*/
//		Outer.Kp = RP_GetValue(1) / 4095.0 * 2;				//修改Kp,調整范圍:0~2
//		Outer.Ki = RP_GetValue(2) / 4095.0 * 2;				//修改Ki,調整范圍:0~2
//		Outer.Kd = RP_GetValue(3) / 4095.0 * 2;				//修改Kd,調整范圍:0~2Outer.Target = RP_GetValue(4) / 4095.0 * 816 - 408;	//修改目標值,調整范圍:-408~408/*OLED顯示*/OLED_Printf(0, 16, OLED_8X16, "Kp:%4.2f", Outer.Kp);			//顯示KpOLED_Printf(0, 32, OLED_8X16, "Ki:%4.2f", Outer.Ki);			//顯示KiOLED_Printf(0, 48, OLED_8X16, "Kd:%4.2f", Outer.Kd);			//顯示KdOLED_Printf(64, 16, OLED_8X16, "Tar:%+04.0f", Outer.Target);	//顯示目標值OLED_Printf(64, 32, OLED_8X16, "Act:%+04.0f", Outer.Actual);	//顯示實際值OLED_Printf(64, 48, OLED_8X16, "Out:%+04.0f", Outer.Out);		//顯示輸出值OLED_Update();	//OLED更新,調用顯示函數后必須調用此函數更新,否則顯示的內容不會更新到OLED上Serial_Printf("%f,%f,%f\r\n", Outer.Target, Outer.Actual, Outer.Out);	//串口打印目標值、實際值和輸出值//配合SerialPlot繪圖軟件,可以顯示數據的波形}
}void TIM1_UP_IRQHandler(void)
{/*定義靜態變量(默認初值為0,函數退出后保留值和存儲空間)*/static uint16_t Count1, Count2;		//分別用于內環和外環的計次分頻if (TIM_GetITStatus(TIM1, TIM_IT_Update) == SET){/*每隔1ms,程序執行到這里一次*/Key_Tick();			//調用按鍵的Tick函數/*內環計次分頻*/Count1 ++;				//計次自增if (Count1 >= 40)		//如果計次40次,則if成立,即if每隔40ms進一次{Count1 = 0;			//計次清零,便于下次計次/*獲取實際速度值和實際位置值*//*Encoder_Get函數,可以獲取兩次讀取編碼器的計次值增量*//*此值正比于速度,所以可以表示速度,但它的單位并不是速度的標準單位*//*此處每隔40ms獲取一次計次值增量,電機旋轉一周的計次值增量約為408*//*因此如果想轉換為標準單位,比如轉/秒*//*則可將此句代碼改成Speed = Encoder_Get() / 408.0 / 0.04;*/Speed = Encoder_Get();		//獲取編碼器增量,得到實際速度Location += Speed;			//實際速度累加,得到實際位置/*以下進行內環PID控制*//*內環獲取實際值*/Inner.Actual = Speed;		//內環為速度環,實際值為速度值/*PID計算及結構體變量值更新*/PID_Update(&Inner);			//調用封裝好的函數,一步完成PID計算和更新/*內環執行控制*//*內環輸出值給到電機PWM*/Motor_SetPWM(Inner.Out);}/*外環計次分頻*/Count2 ++;				//計次自增if (Count2 >= 40)		//如果計次40次,則if成立,即if每隔40ms進一次{Count2 = 0;			//計次清零,便于下次計次/*以下進行外環PID控制*//*外環獲取實際值*/Outer.Actual = Location;		//外環為位置環,實際值為位置值/*PID計算及結構體變量值更新*/PID_Update(&Outer);			//調用封裝好的函數,一步完成PID計算和更新/*外環執行控制*//*外環的輸出值作用于內環的目標值,組成串級PID結構*/Inner.Target = Outer.Out;}TIM_ClearITPendingBit(TIM1, TIM_IT_Update);}
}

PID.c:

#include "stm32f10x.h"                  // Device header
#include "PID.h"/*** 函    數:PID計算及結構體變量值更新* 參    數:PID_t * 指定結構體的地址* 返 回 值:無*/
void PID_Update(PID_t *p)
{/*獲取本次誤差和上次誤差*/p->Error1 = p->Error0;					//獲取上次誤差p->Error0 = p->Target - p->Actual;		//獲取本次誤差,目標值減實際值,即為誤差值/*外環誤差積分(累加)*//*如果Ki不為0,才進行誤差積分,這樣做的目的是便于調試*//*因為在調試時,我們可能先把Ki設置為0,這時積分項無作用,誤差消除不了,誤差積分會積累到很大的值*//*后續一旦Ki不為0,那么因為誤差積分已經積累到很大的值了,這就導致積分項瘋狂輸出,不利于調試*/if (p->Ki != 0)					//如果Ki不為0{p->ErrorInt += p->Error0;	//進行誤差積分}else							//否則{p->ErrorInt = 0;			//誤差積分直接歸0}/*PID計算*//*使用位置式PID公式,計算得到輸出值*/p->Out = p->Kp * p->Error0+ p->Ki * p->ErrorInt+ p->Kd * (p->Error0 - p->Error1);/*輸出限幅*/if (p->Out > p->OutMax) {p->Out = p->OutMax;}	//限制輸出值最大為結構體指定的OutMaxif (p->Out < p->OutMin) {p->Out = p->OutMin;}	//限制輸出值最小為結構體指定的OutMin
}

PID.h:

#ifndef __PID_H
#define __PID_Htypedef struct {float Target;float Actual;float Out;float Kp;float Ki;float Kd;float Error0;float Error1;float ErrorInt;float OutMax;float OutMin;
} PID_t;void PID_Update(PID_t *p);#endif

一、雙環 PID 實現原理

  1. 結構

    • 外環(位置環)
      直接對位置誤差(目標位置 ? 實際位置)進行 PID 計算,輸出的結果不是直接驅動電機,而是作為內環速度環的目標值

    • 內環(速度環)
      對速度誤差(目標速度 ? 實際速度)進行 PID 控制,輸出 PWM 信號驅動電機。

  2. 工作流程

    • 外環根據位置誤差計算一個期望速度(正負代表轉動方向和快慢)。

    • 內環將期望速度作為目標,與實時測得的速度比較后,用 PID 算法快速調整 PWM,使實際速度跟隨期望速度。

    • 最終實現位置精確控制的同時,速度變化過程平滑、快速。

  3. 代碼對應關系

1.外環

Outer.Actual = Location;
PID_Update(&Outer);
Inner.Target = Outer.Out; // 外環輸出作為內環目標速度

2.內環:

Inner.Actual = Speed;
PID_Update(&Inner);
Motor_SetPWM(Inner.Out);

二、雙環 PID 的優點(相對單環 PID)

  1. 穩態精度高

    • 外環處理位置誤差,能消除位置的長期偏差(穩態誤差小)。

    • 內環快速消除速度誤差,讓外環的輸出更快收斂到目標。

  2. 動態性能好

    • 內環抑制了速度的突變,提高系統響應速度。

    • 外環不直接控制 PWM,避免了位置環大幅度輸出導致的振蕩。

  3. 抗干擾能力強

    • 內環對速度的快速調節可以抵消外部擾動(負載變化、摩擦力變化等)。

  4. 抑制超調與振蕩

    • 外環輸出受內環限幅(OutMax / OutMin)保護,避免因位置誤差大而直接全速輸出造成沖過頭。


三、調參技巧(先內環后外環)

1. 為什么要先調內環

  • 內環(速度環)是系統快速響應部分,直接影響外環效果。

  • 如果內環速度跟隨不準,外環即使參數再好,也會出現震蕩、超調甚至失穩。

2. 調內環步驟

  1. 關掉外環(直接給內環一個固定的速度目標值)。

  2. 先調 Kp:從小到大增加,直到速度響應快且基本無振蕩。

  3. 再調 Ki:消除速度穩態誤差,但不要太大,防止低頻振蕩。

  4. 視情況加 Kd:抑制速度快速變化的振蕩。

  5. 確保內環在目標速度變化時能快速平穩跟隨。

3. 調外環步驟

  1. 開啟外環,內環保持調好的參數。

  2. 外環先調 Kp:從小到大增加,觀察位置響應速度和超調情況。

  3. 若存在位置穩態誤差,可適當增加 Ki(位置環 Ki 一般很小)。

  4. 外環 Kd 用于抑制位置變化過快時的超調(相當于對速度的進一步約束)。

  5. 外環輸出限幅 (OutMax) 要合理,一般設為電機中速范圍,防止位置誤差大時直接給滿速。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/93089.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/93089.shtml
英文地址,請注明出處:http://en.pswp.cn/web/93089.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

C#vb.net中Interlocked類實現原子操作加減計算,涵蓋狀態切換、計數控制等常見場景

以下是 C# 中使用 int 類型結合 Interlocked 類實現原子操作的完整示例&#xff0c;涵蓋狀態切換、計數控制等常見場景&#xff1a; 完整代碼示例csharp using System; using System.Threading;/// <summary> /// 基于整數類型的原子操作工具類&#xff08;線程安全&am…

RCL 2025 | LLM采樣機制的新視角:來自處方性偏移的解釋

1. 導讀 大型語言模型&#xff08;Large Language Models, LLMs&#xff09;在自主決策場景中的應用日益廣泛&#xff0c;它們需要在龐大的行動空間中進行響應采樣&#xff08;response sampling&#xff09;。然而&#xff0c;驅動這一采樣過程的啟發式機制仍缺乏深入研究。本…

08 ABP Framework Blazor UI

ABP Framework Blazor UI 架構 overview ABP Blazor UI 系統構建在 Blazorise 組件庫之上&#xff0c;為構建數據驅動應用提供結構化方法&#xff0c;包含 CRUD 操作、主題和本地化的一致模式。 #mermaid-svg-QAvWlELsLhZgYXHu {font-family:"trebuchet ms",verdana,…

JUC學習筆記-----LinkedBlockingQueueConcurrentLinkedQueueCopyOnWriteArrayList

LinkedBlockingQueue基本的入隊出隊初始化public class LinkedBlockingQueue<E> extends AbstractQueue<E>implements BlockingQueue<E>, java.io.Serializable {// 靜態內部類 Node&#xff0c;用于存儲隊列元素及維護節點間關系static class Node<E>…

小杰python高級(six day)——pandas庫

1.數據可視化用于繪制 DataFrame 數據圖形&#xff0c;它允許用戶直接從 DataFrame 創建各種類型的圖表&#xff0c;而不需要使用其他繪圖庫&#xff08;底層實際上使用了 Matplotlib&#xff09;。&#xff08;1&#xff09;plotDataFrame.plot(*args, **kwargs)功能&#xff…

第十六屆藍橋杯青少組C++省賽[2025.8.9]第二部分編程題(1 、慶典隊列)

參考程序&#xff1a;#include <iostream> using namespace std;int main() {int n, A;cin >> n >> A; // 輸入&#xff1a;n 和 A&#xff0c;用空格隔開cout << n / A; // 整數相除&#xff0c;自動向下取整return 0; }

C++進階:智能指針

目錄1. RAII與智能指針2. C庫中的智能指針2.1 智能指針auto_ptr2.2 智能指針unique_ptr2.3 智能指針shared_ptr3. shared_ptr的循環引用4. 智能指針的定值刪除器1. RAII與智能指針 上一篇文章學習了異常相關的知識&#xff0c;其中遺留了一個異常安全相關的問題。那就是異常的拋…

Tkinter 實現按鈕鼠標懸浮提示:兩種方案(繼承Frame與不繼承)

在 Tkinter 桌面應用開發中&#xff0c;為按鈕添加“鼠標懸浮提示”是提升用戶體驗的常用功能——無需點擊&#xff0c;只需將鼠標挪到按鈕上方&#xff0c;就能自動顯示按鈕功能說明。本文將詳細介紹兩種實現方案&#xff1a;不繼承 Frame 類&#xff08;快速簡潔版&#xff0…

20250814 最小生成樹總結

引子 啊啊額&#xff0c;從一張圖里抽出幾條邊&#xff0c;組成一棵樹&#xff0c;無環n?1n-1n?1條邊&#xff0c;就是生成樹。那么邊權和最小的生成樹就叫最小生成樹&#xff0c;最大生成樹同理。 kruskal最小生成樹 要求kruskal最小生成樹&#xff0c;我們首先用結構體數組…

數據大集網:實體店獲客引流的數字化引擎,解鎖精準拓客新密碼?

?在實體店面臨流量焦慮、獲客成本攀升的當下&#xff0c;實體店獲客引流工具的重要性愈發凸顯。如何在激烈的市場競爭中精準觸達目標客戶、構建可持續的客流增長模式&#xff1f;數據大集網憑借其創新的智能獲客體系與全鏈路服務能力&#xff0c;正成為萬千實體店突破增長瓶頸…

nginx --ssl證書生成mkcert

github https://github.com/FiloSottile/mkcert/releases網盤下載地址 https://pan.baidu.com/s/1XI0879pqu7HXZMnmQ9ztaw 提取碼: 1111windows使用示例

守拙以致遠:個人IP的長青之道|創客匠人

2025年被認為是AI應用全面爆發的一年。各種人工智能工具在寫作、制圖、剪輯等領域廣泛使用&#xff0c;大大提升了個人和團隊的工作效率。對于個人IP而言&#xff0c;這類工具的出現確實帶來了新的機會&#xff0c;但也伴隨著一種現象——一些人開始過度依賴甚至神化AI&#xf…

USB 3.0 LTSSM 狀態機

USB2.0在電源供應后&#xff0c;通過Pull Up D-來決定枚舉LS&#xff0c;Pull Up D有一個USB高速握手過程&#xff0c;來決定HS FS。USB3.0則會通過鏈路訓練&#xff08;Link Training&#xff09;&#xff0c;來準備USB3.0通信。每當我們插上USB線的時候&#xff0c;對于3.0的…

MySQL窗口函數與PyMySQL以及SQL注入

MySQL窗口函數與PyMySQL實戰指南&#xff1a;從基礎到安全編程 引言 在數據處理和分析領域&#xff0c;MySQL作為最流行的關系型數據庫之一&#xff0c;其窗口函數功能為數據分析提供了強大的支持。同時&#xff0c;Python作為數據分析的主要語言&#xff0c;通過PyMySQL庫與My…

高級項目——基于FPGA的串行FIR濾波器

給大家安利一個 AI 學習神站&#xff01;在這個 AI 卷成紅海的時代&#xff0c;甭管你是硬核開發者還是代碼小白&#xff0c;啃透 AI 技能樹都是剛需。這站牛逼之處在于&#xff1a;全程用 "變量名式" 幽默 生活化類比拆解 AI&#xff0c;從入門到入土&#xff08;啊…

JPrint免費的Web靜默打印控件:PDF打印中文亂碼異常解決方案

文章目錄JPrint是什么&#xff1f;中文亂碼&#xff08;Using fallback font xxx for xxxx&#xff09;1.字體嵌入2.客戶機字體安裝開源地址相關目錄導航使用文檔端口號修改代理使用場景打印服務切換中文亂碼解決方案 JPrint是什么&#xff1f; JPrint是一個免費開源的可視化靜…

MFT 在零售行業的實踐案例與場景:加速文件集成與業務協作的高效方案

零售行業競爭激烈、數字化轉型迭代迅速&#xff0c;業務對數據與檔案的傳輸、處理和整合要求極高。無論是新品上市市場數據&#xff0c;還是供應鏈物流單據&#xff0c;集成方式不論是通過API或是檔案傳輸, 對于傳輸的穩定性,安全性與性能, 都會直接影響決策效率與顧客體驗。MF…

OSG+Qt —— 筆記1 - Qt窗口加載模型(附源碼)

?? OSG/OsgEarth 相關技術、疑難雜癥文章合集(掌握后可自封大俠 ?_?)(記得收藏,持續更新中…) OSG+Qt所用版本皆為: Vs2017+Qt5.12.4+Osg3.6.5+OsgQt(master) 效果 代碼(需將cow.osg、reflect.rgb拷貝至工程目錄下) OsgForQt.ui main.cpp

開源安全云盤存儲:Hoodik 實現端到端數據加密,Docker快速搭建

以下是對 Hoodik 的簡單介紹&#xff1a; Hoodik 是一個使用 Rust 和 Vue 開發的輕量級自托管安全云存儲解決方案采用了非對稱RSA密鑰對和AES混合加密策略&#xff0c;從文件存儲加密到數據鏈路加密&#xff0c;全程保證數據安全支持Docker一鍵私有部署&#xff0c;數據和服務…

[C++] Git 使用教程(從入門到常用操作)

1. Git 簡介 Git 是一款分布式版本控制系統&#xff0c;用來跟蹤文件變化、協作開發、管理項目版本。 它是開源的&#xff0c;由 Linus Torvalds 在 2005 年開發&#xff0c;廣泛用于開源與企業項目中。 2. 安裝 Git Windows 前往 Git 官網 下載并安裝。 安裝時建議勾選 Git…