Open Source
    Microcontroller
    Automatic Control
    Coding Notes

2014年8月19日 星期二

Arduino〈倒單擺 Inverted Pendulum〉(4) 狀態回授控制

下午6:21 Posted by Unknown , , , No comments
狀態回授(State Feedback)是典型負回授系統中會使用的控制策略,方法非常直觀,就是將量測到的狀態(x)乘上增益(K)後回授,以此為新的控制命令(u),再輸入到控制對象(plant)中,如此反覆迭代,若增益選取得當,系統為穩定,狀態回授值會趨向參考值,此時控制命令趨近於零,而系統輸出(y)進入穩態(Steady State)。

來複習一下課本的小知識...

下面是一個典型的二階線性系統,彈簧(k) ─ 阻尼(c) ─ 質量塊(m)系統及其對應的運動方程式(equation of motion):



u為系統的施力,在控制方塊與硬體中,也可對應為控制命令或是馬達轉動所需的電壓。
由運動方程式可推導系統的特徵方程式,其方程式的根就是特徵值(eigenvalues),也是系統的極點(poles),這些抽象的值實際上決定了控制系統表現的行為,極點的實部表示衰減行為,虛部顯示振盪行為,這些皆可由系統參數(m、c、k)推導而得其量值。
考慮施加一控制力u,質量塊會由a點移動到b點,(b - a) 為位移 ( x ), a 到 b 之間的運動過程被稱為暫態響應(transient response),其運動的方式被極點決定,例如多快到達b點(settling time),運動是否會過頭(overshoot),振動的量值等等。若對這些行為不滿意,便可以透過控制的手段來改善,這時候狀態回授的方法便派上用場。

將感測器量到的狀態(位移量、速度等)乘以一個係數回授加到新的控制命令中,使得:



代入原來方程式變成

整裡可得


可以發現透過狀態回授的方法,可以改變原方程式的係數,也就像是改變系統的參數,比如說原機構中的彈簧,其彈性係數由 k 變成 k + K1,而阻尼也有類似的效果。當參數不同,便會產生新的特徵方程式與特徵值,那麼極點的位置也就隨之變化,藉此可以改善系統的行為,像是反應速度變快或是變穩定等等。

了解這個原理之後,再回到倒單擺。倒單擺的數學模型當然沒這麼簡單,基本上是非線性的,然而利用泰勒展開的概念可以推導其 Jacobian 矩陣,那麼就可以將原運動方程在平衡點進行線性化,得到類似上面的結果。

不過在程式撰寫方面,模型有沒有線性化並不是那麼重要,實際上程式有很大一部分都在處理和數學與控制無關的事......
下面是一個運用狀態回授進行倒單擺控制的Arduino程式碼範例,由於內容包含了自訂的資源庫(libary),相關數值的設定也必須搭配實際的硬體,所以要套用到其他的模組,程式必須得在作調整。

#include <Wire.h>
#include <SPI.h>
#include "InvPenSensor.h"
#include "InvPenActuator.h"

#define MIN_COMMAND 80
#define GAIN_ANGLE 2150
#define GAIN_OMEGA 20
#define REF 0.175

InvPenSensor sensor;
InvPenActuator actuator;

int command, forwardCount = 0, backwardCount = 0;
float omega, lastOmega = 0, pseudoOmega,
      angle, lastAngle = 0, pseudoAngle, 
      dt, reference = 0.175;
unsigned long time = 0, steady = 0;

void setup() {
  Serial.begin(9600);
  sensor.init();
  actuator.init();
  while(lastAngle == 0) {
    time = micros();
    lastAngle = sensor.getAngle();
  }
  steady = millis();
  steadyProcess();
}

void steadyProcess() {
  while( (millis() - steady) < 2000) {
    pseudoAngle = sensor.getAngle();
    dt = 0.000001 * (micros() - time);
    time = micros();
    sensor.getGY(&pseudoOmega);
    omega = pseudoOmega * PI / 2587.5;
    if( pseudoAngle != 0) {
      angle = 0.5 * (lastAngle + omega * dt) + 0.5 * pseudoAngle;
    } else {
      angle = lastAngle + omega * dt;
    }  
    lastAngle = angle;
  }
  sensor.getGY(&pseudoOmega);
  lastOmega = pseudoOmega * PI / 2587.5;
}

int counter = 0;

void loop() {
  while(1) {
    command = MIN_COMMAND;
    pseudoAngle = sensor.getAngle();
    dt = 0.000001 * (micros() - time);
    time = micros();
    sensor.getGY(&pseudoOmega);
    omega = 2 * pseudoOmega * PI / 2587.5 - lastOmega;
    if( pseudoAngle != 0) {
      angle =  0.98 * (lastAngle + omega * dt) + 0.02 * pseudoAngle;
    } else {
      angle = lastAngle + 1.5 * omega * dt;
    }  
    lastAngle = angle;
    lastOmega = omega;
    
/////////////////////////////////////////////////////////////////////////
  
      if( (angle - reference) > 0.0) {      
          command += GAIN_ANGLE * (angle - reference);
          if(omega > 0.00) {
            command += GAIN_OMEGA * omega;
          }
          command = constrain(command, MIN_COMMAND, 255);
          actuator.driveBackward(command);
          forwardCount = 0;
          backwardCount ++;
//          Serial.println("BACK"); 
  
      } else if( (angle - reference) < -0.0) {        
          command -= GAIN_ANGLE * (angle - reference);
          if(omega < -0.00) {
            command -= GAIN_OMEGA * omega;
          }
          command = constrain(command, MIN_COMMAND, 255);
          actuator.driveForward(command);
          backwardCount = 0;
          forwardCount ++;
//          Serial.println("FRONT");
      }
      
      reference = REF + 0.00002 * (forwardCount - backwardCount);

  } // while(1)
    
}

這裡是一個實際的demo影片




在理論分析的階段,我們依著數學推導的結果來
設計控制器,但是撰寫程式碼,完全是不同的思維,例如在規畫控制方塊的時候,並不會有判斷邏輯敘述( if(...){...} )的概念,但是這在程式碼中卻是稀鬆平常,又例如在計算控制命令時,很少關注 u 的正負符號與上下限問題,因為方程式總是能自動滿足,然而實際上,驅動電壓最高可能就5v,也不會有負電壓,因此這些數值上的對應細節都只能靠程式來調整,只要有一個環節不正確,那麼連帶控制理論就不成立,哪怕那是多高等的控制技巧都將無力施展。也因此,程式中常需要利用許多"非線性"的寫法來達成原本可能只是"線性"的控制策略。例如:

1. 由於馬達驅動有最低與最高電壓限制,對應的控制命令就必須要有最小與最大值。
2. 每次計算控制命令時,皆由最小值向上累加,以確保命令在可驅動範圍內。
3.在感測的角度變號時,控制命令的計算也要變號,以確保其值始終為正(才有辦法做analogWrite),但仍要可以區分其方向,所以會利用 if 判斷式,將不同計算式限制在不同的角度區間內。
4. 與角度同方向的角速度變化才做回授。
5. 須對參考值(reference)做回授校正。

最後一點其實很重要也是設計控制器時經常忽略的地方,在理論分析回授增益時,都會將參考值設為零,表示要使倒單擺控制到平衡點。當此值不為零,則表示要使倒單擺向某一側維持一個傾斜角,這時倒單擺需要不斷加速才能維持平衡。因此,如果要讓倒單擺乖乖留在原地,便要確保參考值維持在零點,也就是要設定reference在平衡點的角度上,然而受限於硬體安裝偏差或是配重的影響,這個角度很難事先量測,因此在程式中會先給一個近似的估測值(REF值),再透過回授的方式來持續校正到平衡點,校正的方式則類似積分控制器。
至於那些增益到底要設多少,理論分析當然可以計算出個值,但不如動手多做嘗試來得有感覺。

0 意見:

張貼留言