1.1 概述
比例(Proportion)積分(Integral)微分(Differential)控制器(PID控制器或三項控制器)是一種采用反饋的控制回路機(jī)制,廣泛應(yīng)用于工業(yè)控制系統(tǒng)和需要連續(xù)調(diào)制控制的各種其他應(yīng)用。
PID控制器連續(xù)計算誤差值 e(t) 作為所需設(shè)定點(SP) 和測量過程變量(PV)之間的差值,并應(yīng)用基于比例、積分和導(dǎo)數(shù)項(分別表示為P、I和D)的校正,因此得名。


r(t) 是期望的過程值或設(shè)定點(SP),y(t) 是測量的過程值(PV)。
1.2 歷史發(fā)展
1911年,第一個PID控制器是由Elmer Sperry開發(fā)的。
1922 年,俄裔美國工程師尼古拉斯·米諾斯基 ( Nicolas Minorsky)才首次利用理論分析制定了我們現(xiàn)在所說的 PID 或三項控制的正式控制律。米諾斯基當(dāng)時正在為美國海軍研究和設(shè)計自動船舶轉(zhuǎn)向系統(tǒng),他的分析基于對舵手的觀察。
他指出,舵手不僅根據(jù)當(dāng)前航向誤差,還根據(jù)過去的誤差以及當(dāng)前的變化率來駕駛船舶;然后 Minorsky 對此進(jìn)行了數(shù)學(xué)處理。他的目標(biāo)是穩(wěn)定,而不是一般控制,這大大簡化了問題。
1933年,TIC(泰勒儀器公司)實現(xiàn)了完全可調(diào)節(jié)的前氣動控制器。幾年后,控制工程師通過將末端返回到一些假值,直到誤差不為零,消除了比例控制器中發(fā)現(xiàn)的穩(wěn)態(tài)誤差。這個返回包含了誤差,這被稱為比例積分控制器。
1940年,第一個氣動PID控制器通過導(dǎo)數(shù)動作開發(fā),以減少超調(diào)問題。
1942年,Ziegler & Nichols引入了調(diào)諧規(guī)則,由工程師發(fā)現(xiàn)和設(shè)置PID控制器的合適參數(shù)。
20世紀(jì)50年代中期,自動PID控制器在工業(yè)上得到了廣泛的應(yīng)用。工業(yè)中大多數(shù)現(xiàn)代 PID 控制都是作為DCS、PLC 或單片機(jī)程序來實現(xiàn)的。
1.3 應(yīng)用
?火箭的姿態(tài)控制
?無人機(jī)懸??刂频?/p>
?相機(jī)穩(wěn)定器、相機(jī)云臺
?平衡小車
?汽車的定速巡航控制、轉(zhuǎn)向控制
?發(fā)動機(jī)轉(zhuǎn)速控制
?3D打印機(jī)上的溫度控制器
?工業(yè)自動化領(lǐng)域,大約95%的閉環(huán)操作使用PID控制器。
1.4 與 ON/OFF 型控制器對比
像PID控制器這樣的閉環(huán)系統(tǒng)包括一個反饋控制系統(tǒng)。該系統(tǒng)利用一個固定點對反饋變量進(jìn)行評估,從而產(chǎn)生誤差信號。在此基礎(chǔ)上,它改變系統(tǒng)輸出。這個過程將繼續(xù),直到誤差達(dá)到零,否則反饋變量的值就等于一個固定點。
與ON/OFF型控制器相比,該控制器提供了良好的效果。在開/關(guān)型控制器中,只需兩個條件即可管理系統(tǒng)。大多數(shù)暖通空調(diào)系統(tǒng)、冰箱都采用這種方法。
例如,在冰箱中,它會冷卻內(nèi)部直到達(dá)到所需溫度,然后關(guān)閉冷卻器,直到達(dá)到高于所需溫度的設(shè)定值。一旦工藝值低于固定點,則開啟。
類似地,一旦該值高于固定值,它將關(guān)閉。這種控制器的輸出不穩(wěn)定,在不動點的區(qū)域內(nèi)振蕩頻繁。然而,與ON/OFF型控制器相比,PID 控制器更加穩(wěn)定和準(zhǔn)確。

1.6 響應(yīng)類型
由PID控制器驅(qū)動的系統(tǒng)通常具有三種類型的響應(yīng):欠阻尼、過阻尼和臨界阻尼。


?欠阻尼響應(yīng)在穩(wěn)定之前圍繞參考值振蕩。
?過阻尼響應(yīng)上升緩慢并且不會超過參考值。
?臨界阻尼響應(yīng)具有最快的上升時間,且不會超過參考值。
公式
2.1 PID 系統(tǒng)定義與公式

r(t) setpoint, reference,是期望的過程值或設(shè)定值(SP);
y(t) output, process variable,是測量的過程值,輸出值(PV);
e(t) error,是偏差;
u(t) control effort,是控制量;
PID控制器的顯著特點是能夠利用比例、積分和微分這三個控制項對控制器輸出的影響來進(jìn)行精確和最優(yōu)的控制。
PID 控制器,不斷計算誤差值e(t) 作為所需設(shè)定點之間的差異SP=r(t) 和測量的過程變量PV=y(t):e(t)=r(t)?y(t) ,并應(yīng)用基于比例、積分和導(dǎo)數(shù)項的修正。
控制器嘗試通過調(diào)整控制變量來最小化隨時間變化的誤差u(t)。manipulated variable (MV)。



2.2 PID 數(shù)字公式
由于計算機(jī)控制是一種采樣控制,它只能根據(jù)采樣時刻的偏差計算控制量,而不能像模擬控制那樣連續(xù)輸出控制量,進(jìn)行連續(xù)控制。由于這一特點,(式 1-1)中的積分項和微分項不能直接使用,必須進(jìn)行離散化處理。
離散化處理的方法為:以τ作為采樣周期,k作為采樣序號,則離散采樣時間kτ對應(yīng)著連續(xù)時間t,用矩形法數(shù)值積分近似代替積分,用一階后向差分近似代替微分,可作如下近似變換:

2.3 位置式 PID 算法
將(式 2-1)代入(式 1-1),就可以得到離散的 PID 表達(dá)式為

將(式 2-1)代入(式 1-2),就可以得到離散的PID 表達(dá)式為

積分系數(shù)、微分系數(shù)做如下替換:
注意:必須使τ為定值,或者變化小到可以忽略,這樣P、I、D才是固定常數(shù),才可能調(diào)節(jié)


2.4 增量式 PID 算法

增量式 PID 控制算法可以通過(式 2-2)推導(dǎo)出。由(式 2-2)可以得到控制器的第 k-1 個采樣時刻的輸出值為:

由(式 2-3)可以得到控制器的第 k-1 個采樣時刻的輸出值為:

用(式 2-3)減去(式 2-7)相減并整理,就可以得到增量式 PID 控制算法公式:

由(式 2-8)可以看出,如果計算機(jī)控制系統(tǒng)采用恒定的采樣周期τ,一旦確定 A、 B、 C,只要使用前后三次測量的偏差值,就可以由(式 2-8)求出控制量。
增量式 PID 控制算法與位置式 PID 算法(式 2-3)相比,只需要保持當(dāng)前時刻以前三個時刻的偏差值即可,累計誤差較小,計算量小的多,因此在實際中得到廣泛的應(yīng)用。
而位置式 PID 控制算法也可以通過增量式控制算法推出遞推計算公式:

(式 2-9)就是目前在計算機(jī)控制中廣泛應(yīng)用的數(shù)字遞推 PID 控制算法。
調(diào)試技巧

代碼實現(xiàn)
import numpy as np
import matplotlib.pyplot as plt
class PositionPID(object):
"""位置式PID算法實現(xiàn)"""
def __init__(self, target, cur_val, dt, max, min, p, i, d) -> None:
self.dt = dt # 循環(huán)時間間隔
self._max = max # 最大輸出限制,規(guī)避過沖
self._min = min # 最小輸出限制
self.k_p = p # 比例系數(shù)
self.k_i = i # 積分系數(shù)
self.k_d = d # 微分系數(shù)
self.target = target # 目標(biāo)值
self.cur_val = cur_val # 算法當(dāng)前PID位置值,第一次為設(shè)定的初始位置
self._pre_error = 0 # t-1 時刻誤差值
self._integral = 0 # 誤差積分值
def calculate(self):
"""
計算t時刻PID輸出值cur_val
"""
error = self.target - self.cur_val # 計算當(dāng)前誤差
# 比例項
p_out = self.k_p * error
# 積分項
self._integral += (error * self.dt)
i_out = self.k_i * self._integral
# 微分項
derivative = (error - self._pre_error) / self.dt
d_out = self.k_d * derivative
# t 時刻pid輸出
output = p_out + i_out + d_out
# 限制輸出值
if output > self._max:
output = self._max
elif output < self._min:
? ? ? ? ? ?output = self._min
? ? ? ?
? ? ? ?self._pre_error = error
? ? ? ?self.cur_val = output
? ? ? ?return self.cur_val
? ?def fit_and_plot(self, count = 200):
? ? ? ?"""
? ? ? ?使用PID擬合setPoint
? ? ? ?"""
? ? ? ?counts = np.arange(count)
? ? ? ?outputs = []
? ? ? ?for i in counts:
? ? ? ? ? ?outputs.append(self.calculate())
? ? ? ? ? ?print('Count %3d: output: %f' % (i, outputs[-1]))
? ? ? ?print('Done')
? ? ? ?# print(outputs)
? ? ? ?
? ? ? ?plt.figure()
? ? ? ?plt.axhline(self.target, c='red')
? ? ? ?plt.plot(counts, np.array(outputs), 'b.')
? ? ? ?plt.ylim(min(outputs) - 0.1 * min(outputs), max(outputs) + 0.1 * max(outputs))
? ? ? ?plt.plot(outputs)
? ? ? ?plt.show()
pid = PositionPID(10, -5, 0.5, 100, -100, 0.2, 0.1, 0.01)
pid.fit_and_plot(150)

c/c++
//首先定義PID結(jié)構(gòu)體用于存放一個PID的數(shù)據(jù)
typedef struct
{
float kp,ki,kd;//三個系數(shù)
float error,lastError;//誤差、上次誤差
float integral,maxIntegral;//積分、積分限幅
float output,maxOutput;//輸出、輸出限幅
}PID;
//用于初始化pid參數(shù)的函數(shù)
void PID_Init(PID *pid,float p,float i,float d,float maxI,float maxOut)
{
pid->kp=p;
pid->ki=i;
pid->kd=d;
pid->maxIntegral=maxI;
pid->maxOutput=maxOut;
}
//進(jìn)行一次pid計算
//參數(shù)為(pid結(jié)構(gòu)體,目標(biāo)值,反饋值),計算結(jié)果放在pid結(jié)構(gòu)體的output成員中
void PID_Calc(PID *pid,float reference,float feedback)
{
//更新數(shù)據(jù)
pid->lastError=pid->error;//將舊error存起來
pid->error=reference-feedback;//計算新error
//計算微分
float dout=(pid->error-pid->lastError)*pid->kd;
//計算比例
float pout=pid->error*pid->kp;
//計算積分
pid->integral+=pid->error*pid->ki;
//積分限幅
if(pid->integral > pid->maxIntegral) pid->integral=pid->maxIntegral;
else if(pid->integral < -pid->maxIntegral) pid->integral=-pid->maxIntegral;
//計算輸出
pid->output=pout+dout+pid->integral;
//輸出限幅
if(pid->output > pid->maxOutput) pid->output=pid->maxOutput;
else if(pid->output < -pid->maxOutput) pid->output=-pid->maxOutput;
}
PID mypid;//創(chuàng)建一個PID結(jié)構(gòu)體變量
int main()
{
//...這里有些其他初始化代碼
PID_Init(&mypid,10,1,5,800,1000);//初始化PID參數(shù)
while(1)//進(jìn)入循環(huán)運行
{
float feedbackValue=...;//這里獲取到被控對象的反饋值
float targetValue=...;//這里獲取到目標(biāo)值
PID_Calc(&mypid,targetValue,feedbackValue);//進(jìn)行PID計算,結(jié)果在output成員變量中
設(shè)定執(zhí)行器輸出大小(mypid.output);
delay(10);//等待一定時間再開始下一次循環(huán)
}
}
單環(huán)效果


串級PID的C語言代碼
//此處需要插入上面的單級PID相關(guān)代碼
//串級PID的結(jié)構(gòu)體,包含兩個單級PID
typedef struct
{
PID inner;//內(nèi)環(huán)
PID outer;//外環(huán)
float output;//串級輸出,等于inner.output
}CascadePID;
//串級PID的計算函數(shù)
//參數(shù)(PID結(jié)構(gòu)體,外環(huán)目標(biāo)值,外環(huán)反饋值,內(nèi)環(huán)反饋值)
void PID_CascadeCalc(CascadePID *pid,float outerRef,float outerFdb,float innerFdb)
{
PID_Calc(&pid->outer,outerRef,outerFdb);//計算外環(huán)
PID_Calc(&pid->inner,pid->outer.output,innerFdb);//計算內(nèi)環(huán)
pid->output=pid->inner.output;//內(nèi)環(huán)輸出就是串級PID的輸出
}
CascadePID mypid;//創(chuàng)建串級PID結(jié)構(gòu)體變量
int main()
{
//...其他初始化代碼
PID_Init(&mypid.inner,10,0,0,0,1000);//初始化內(nèi)環(huán)參數(shù)
PID_Init(&mypid.outer,5,0,5,0,100);//初始化外環(huán)參數(shù)
while(1)//進(jìn)入循環(huán)運行
{
float outerTarget=...;//獲取外環(huán)目標(biāo)值
float outerFeedback=...;//獲取外環(huán)反饋值
float innerFeedback=...;//獲取內(nèi)環(huán)反饋值
PID_CascadeCalc(&mypid,outerTarget,outerFeedback,innerFeedback);//進(jìn)行PID計算
設(shè)定執(zhí)行機(jī)構(gòu)輸出大小(mypid.output);
delay(10);//延時一段時間
}
}
雙環(huán)效果

雙環(huán)控制
串聯(lián)
如果電機(jī)控制既要控制速度又要控制位置,因為速度和位置相關(guān),所以需要串聯(lián)。

并聯(lián)
姿態(tài)角度與速度間無相關(guān)性,各自單獨算一路控制

示例
循跡小車
可見小車的循跡效果。
野火中步進(jìn)電機(jī)位置速度雙環(huán)控制
步進(jìn)電機(jī)速度環(huán)控制實現(xiàn)和 10. 步進(jìn)電機(jī)位置環(huán)控制實現(xiàn)介紹了單環(huán)控制已經(jīng)能很好地提高電機(jī)的性能了,但是仍有其局限性。
使用速度環(huán)精確控制了電機(jī)的轉(zhuǎn)速,但是停止的位置難以精確控制;
使用位置環(huán)精確控制了電機(jī)轉(zhuǎn)過的角度,卻不得不人為限制速度來防止堵轉(zhuǎn)。
位置環(huán)和速度環(huán)雙環(huán)控制,既實現(xiàn)位置的精確調(diào)節(jié)又實現(xiàn)速度的自動控制。

該控制下,編碼器不僅起到了反饋位置的作用,也起到了反饋速度的作用。
調(diào)參技巧:在PID參數(shù)整定時,采取先內(nèi)環(huán)再外環(huán)的方法,也就是先單獨使用速度環(huán)控制,得到滿意的參數(shù)后, 再把位置環(huán)套在外面,整定位置環(huán)參數(shù),最后根據(jù)整體效果對速度環(huán)參數(shù)進(jìn)行微調(diào)。
bsp_pid.h
/*pid*/
typedef struct
{
float target_val; //目標(biāo)值
float actual_val; //實際值
float err; //定義當(dāng)前偏差值
float err_next; //定義下一個偏差值
float err_last; //定義上一個偏差值
float Kp, Ki, Kd; //定義比例、積分、微分系數(shù)
}_pid;
bsp_stepper_ctrl.h
/*宏定義*/
/*******************************************************/
#define TIM_STEP_FREQ (SystemCoreClock/TIM_PRESCALER) // 頻率ft值
/*電機(jī)單圈參數(shù)*/
#define STEP_ANGLE 1.8f //步進(jìn)電機(jī)的步距角 單位:度
#define FSPR (360.0f/STEP_ANGLE) //步進(jìn)電機(jī)的一圈所需脈沖數(shù)
#define MICRO_STEP 32 //細(xì)分器細(xì)分?jǐn)?shù)
#define SPR (FSPR*MICRO_STEP) //細(xì)分后一圈所需脈沖數(shù)
#define PULSE_RATIO (float)(SPR/ENCODER_TOTAL_RESOLUTION)//步進(jìn)電機(jī)單圈脈沖數(shù)與編碼器單圈脈沖的比值
#define SAMPLING_PERIOD 50 //PID采樣頻率,單位Hz
#define MOVE_CTRL 0.1f //啟用速度環(huán)控制量
#define TARGET_DISP 20 //步進(jìn)電機(jī)運動時的目標(biāo)圈數(shù),單位:轉(zhuǎn)
#define TARGET_SPEED_MAX 800 // 目標(biāo)速度的最大值
typedef struct {
unsigned char stepper_dir : 1; //步進(jìn)電機(jī)方向
unsigned char stepper_running : 1; //步進(jìn)電機(jī)運行狀態(tài)
unsigned char MSD_ENA : 1; //驅(qū)動器使能狀態(tài)
}__SYS_STATUS;
bsp_stepper_ctrl.c-增量式PID算法實現(xiàn)-增量式PID
/** * @brief 增量式PID算法實現(xiàn) * @param val:當(dāng)前實際值 * @note 無 * @retval 通過PID計算后的輸出 */ float PID_realize(_pid *pid, float temp_val) { /*傳入實際值*/ pid->actual_val = temp_val; /*計算目標(biāo)值與實際值的誤差*/ pid->err=pid->target_val-pid->actual_val; /*PID算法實現(xiàn)*/ float increment_val = pid->Kp*(pid->err - pid->err_next) + pid->Ki*pid->err + pid->Kd*(pid->err - 2 * pid->err_next + pid->err_last); /*傳遞誤差*/ pid->err_last = pid->err_next; pid->err_next = pid->err; /*返回增量值*/ return increment_val; }
bsp_stepper_ctrl.c-步進(jìn)電機(jī)位置速度雙閉環(huán)控制
/** * @brief 步進(jìn)電機(jī)位置速度雙閉環(huán)控制 * @retval 無 * @note 基本定時器中斷內(nèi)調(diào)用 */ void Stepper_Ctrl(void) { /* 編碼器相關(guān)變量 */ static __IO float last_count = 0; __IO float capture_count = 0; __IO float capture_per_unit = 0; /* 經(jīng)過pid計算后的期望值 */ static __IO float speed_cont_val = 0.0f; static __IO float move_cont_val = 0.0f; static int cont_val = 0; /* 當(dāng)電機(jī)運動時才啟動pid計算 */ if((sys_status.MSD_ENA == 1) && (sys_status.stepper_running == 1)) { /* 計算編碼器脈沖數(shù) */ capture_count = (int)__HAL_TIM_GET_COUNTER(&TIM_EncoderHandle) + (encoder_overflow_count * ENCODER_TIM_PERIOD); /* 計算速度環(huán)的傳入值 */ capture_per_unit = capture_count - last_count; last_count = capture_count; /* 編碼器脈沖累計值作為實際值傳入位置環(huán)pid控制器 */ move_cont_val += PID_realize_move(&move_pid, (float)capture_count);// 進(jìn)行 PID 計算 /* 判斷運動方向 */ move_cont_val > 0 ? (MOTOR_DIR(CW)) : (MOTOR_DIR(CCW)); /* 判斷是否啟用速度環(huán) */ if (fabsf(move_cont_val) >= MOVE_CTRL) { /* 傳遞位置環(huán)計算值,便于計算*/ cont_val = move_cont_val; /* 目標(biāo)速度上限處理 */ if (cont_val > TARGET_SPEED_MAX) { cont_val = TARGET_SPEED_MAX; } else if (cont_val < -TARGET_SPEED_MAX) ? ? ? { ? ? ? ? cont_val = -TARGET_SPEED_MAX; ? ? ? } #if defined(PID_ASSISTANT_EN) ? ? ? int32_t temp = cont_val; ? ? ? set_computer_value(SEED_TARGET_CMD, CURVES_CH2, &temp, 1); ? ? // 給通道 2 發(fā)送目標(biāo)值 #endif ? ? ? /* 設(shè)定速度的目標(biāo)值 */ ? ? ? set_pid_target(&speed_pid, cont_val); ? ? ? /* 單位時間內(nèi)的編碼器脈沖數(shù)作為實際值傳入速度環(huán)pid控制器 */ ? ? ? speed_cont_val += PID_realize_speed(&speed_pid, (float)capture_per_unit);// 進(jìn)行 PID 計算 ? ? ? /* 由于OC_Pulse_num為uint16_t變量,取速度環(huán)輸出值的絕對值進(jìn)行后續(xù)計算*/ ? ? ? cont_val = fabsf(speed_cont_val); ? ? ? /* 計算比較計數(shù)器的值 */ ? ? ? OC_Pulse_num = ((uint16_t)(TIM_STEP_FREQ / (cont_val * PULSE_RATIO * SAMPLING_PERIOD))) >> 1; } else { /* 計算比較計數(shù)器的值 */ OC_Pulse_num = ((uint16_t)(TIM_STEP_FREQ / ((float)move_cont_val * PULSE_RATIO))) >> 1; } #if PID_ASSISTANT_EN int Temp_ch2 = capture_per_unit; // 上位機(jī)需要整數(shù)參數(shù),轉(zhuǎn)換一下 int Temp_ch1 = capture_count; set_computer_value(SEED_FACT_CMD, CURVES_CH2, &Temp_ch2, 1); // 給通道 1 發(fā)送實際值 // 給通道 2 發(fā)送實際值 set_computer_value(SEED_FACT_CMD, CURVES_CH1, &Temp_ch1, 1); // 給通道 1 發(fā)送實際值 #else printf("實際值:%d,目標(biāo)值:%.0f ", capture_per_unit, pid.target_val);// 打印實際值和目標(biāo)值 #endif } else { /*停機(jī)狀態(tài)所有參數(shù)清零*/ last_count = 0; speed_cont_val = 0; move_cont_val = 0; speed_pid.actual_val = 0; speed_pid.err = 0; speed_pid.err_last = 0; speed_pid.err_next = 0; move_pid.actual_val = 0; move_pid.err = 0; move_pid.err_last = 0; move_pid.err_next = 0; } }
main
/**
* @brief 主函數(shù)
* @param 無
* @retval 無
*/
int main(void)
{
/* 初始化系統(tǒng)時鐘為168MHz */
SystemClock_Config();
/*初始化USART 配置模式為 115200 8-N-1,中斷接收*/
DEBUG_USART_Config();
printf("歡迎使用野火 電機(jī)開發(fā)板 步進(jìn)電機(jī)位置速度雙環(huán)控制 例程
");
printf("按下按鍵3啟動和停止電機(jī)
");
/* 初始化時間戳 */
HAL_InitTick(5);
/*按鍵中斷初始化*/
Key_GPIO_Config();
/*led初始化*/
LED_GPIO_Config();
/* 初始化基本定時器定時,20ms產(chǎn)生一次中斷 */
TIMx_Configuration();
/* 編碼器接口初始化 */
Encoder_Init();
/*步進(jìn)電機(jī)初始化*/
stepper_Init();
/* 上電默認(rèn)停止電機(jī) */
Set_Stepper_Stop();
/* PID算法參數(shù)初始化 */
PID_param_init();
// MOTOR_DIR(CW);
/* 目標(biāo)位置轉(zhuǎn)換為編碼器的脈沖數(shù)作為pid目標(biāo)值 */
move_pid.target_val = TARGET_DISP * ENCODER_TOTAL_RESOLUTION;
int32_t Temp = TARGET_DISP * ENCODER_TOTAL_RESOLUTION;
#if PID_ASSISTANT_EN
set_computer_value(SEED_STOP_CMD, CURVES_CH1, NULL, 0); // 同步上位機(jī)的啟動按鈕狀態(tài)
set_computer_value(SEED_TARGET_CMD, CURVES_CH1, &Temp, 1);// 給通道 1 發(fā)送目標(biāo)值
#endif
while(1)
{
/* 掃描KEY1,啟動電機(jī) */
if( Key_Scan(KEY1_GPIO_PORT,KEY1_PIN) == KEY_ON )
{
#if PID_ASSISTANT_EN
Set_Stepper_Start();
set_computer_value(SEED_START_CMD, CURVES_CH1, NULL, 0);// 同步上位機(jī)的啟動按鈕狀態(tài)
#else
Set_Stepper_Start();
#endif
}
/* 掃描KEY2,停止電機(jī) */
if( Key_Scan(KEY2_GPIO_PORT,KEY2_PIN) == KEY_ON )
{
#if PID_ASSISTANT_EN
Set_Stepper_Stop();
set_computer_value(SEED_STOP_CMD, CURVES_CH1, NULL, 0);// 同步上位機(jī)的啟動按鈕狀態(tài)
#else
Set_Stepper_Stop();
#endif
}
/* 掃描KEY3,增大目標(biāo)位置*/
if( Key_Scan(KEY3_GPIO_PORT,KEY3_PIN) == KEY_ON )
{
/* 目標(biāo)位置增加48000,對應(yīng)電機(jī)位置增加20圈 */
move_pid.target_val += 48000;
#if PID_ASSISTANT_EN
int temp = move_pid.target_val;
set_computer_value(SEED_TARGET_CMD, CURVES_CH1, &temp, 1);// 給通道 1 發(fā)送目標(biāo)值
#endif
}
/* 掃描KEY4,減小目標(biāo)位置 */
if( Key_Scan(KEY4_GPIO_PORT,KEY4_PIN) == KEY_ON )
{
/* 目標(biāo)位置減小48000,對應(yīng)電機(jī)位置減少20圈 */
move_pid.target_val -= 48000;
#if PID_ASSISTANT_EN
int temp = move_pid.target_val;
set_computer_value(SEED_TARGET_CMD, CURVES_CH1, &temp, 1);// 給通道 1 發(fā)送目標(biāo)值
#endif
}
}
}
補(bǔ)充知識點
模擬量數(shù)字化
實際數(shù)字化應(yīng)用中,PID 系統(tǒng)中的積分項和微分項需要進(jìn)行離散化處理。
類似的典型應(yīng)用有數(shù)字示波器。對于數(shù)字示波器來說它無法直接量化模擬信號,替代的辦法就是持續(xù)周期性采樣,然后將得到的一系列采樣點顯示出來,當(dāng)采樣速率越高,顯示的圖像越真實,這就是數(shù)學(xué)中極限的與微分的思想。

香農(nóng)(Shannon) 采樣定律

?定理內(nèi)容
香農(nóng)取樣定理是針對有限帶寬函數(shù)的,為了不失真地恢復(fù)模擬信號,采樣頻率應(yīng)該不小于模擬信號頻譜中最高頻率的2倍。
審核編輯:劉清
-
嵌入式
+關(guān)注
關(guān)注
5198文章
20442瀏覽量
333986 -
plc
+關(guān)注
關(guān)注
5051文章
14592瀏覽量
486881 -
DCS
+關(guān)注
關(guān)注
20文章
657瀏覽量
53819 -
PID算法
+關(guān)注
關(guān)注
2文章
172瀏覽量
25716 -
PID控制器
+關(guān)注
關(guān)注
2文章
173瀏覽量
19701 -
工業(yè)控制系統(tǒng)
+關(guān)注
關(guān)注
3文章
118瀏覽量
17185
原文標(biāo)題:嵌入式PID算法理論+實踐分析
文章出處:【微信號:嵌入式開發(fā)愛好者,微信公眾號:嵌入式開發(fā)愛好者】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
【最新課程推薦】PID控制算法
【杭州】誠聘嵌入式算法工程師
嵌入式小波編碼算法的原理是什么?
PID算法增量式算法
嵌入式系統(tǒng)編程教學(xué)分析
嵌入式系統(tǒng)中FFT算法分析及設(shè)計方案
pid算法_什么是pid算法
嵌入式開發(fā)的crc算法知識精選
基于KCF的目標(biāo)跟蹤算法研究及嵌入式系統(tǒng)實現(xiàn)
嵌入式系統(tǒng)的應(yīng)用控制理論
linux嵌入式系統(tǒng)算法,嵌入式Linux操作系統(tǒng)調(diào)度算法研究
PID算法與直流電機(jī)的PWM技術(shù)控制
什么是嵌入式PID算法?嵌入式PID算法分析
評論