センサレス クローズドループ矩形波制御 その1 ーブラシレスモータ駆動への道2ー
前回はブラシレスモータをいじくりつつ、駆動ドライバ基板を製作して矩形波駆動でいわゆるオープンループ制御でのモータ回転を楽しみました。
ここではモータの回転をセンサレスでフィードバックするクローズドループ矩形波制御での駆動を目指します。
目次
参考書籍
ここからは以下の電子書籍を参考に進めます。Kindle Unlimitedメンバーでしたら無料で読むことができます。
ブラシレスレスモータの制御方法が各種紹介されPICマイコンでの実例が記載されております。
原理説明も明確になされているのでかなり理解が深まりました。
書籍のChapter8 センサレス 制御(120 °通電方式) を参考に回転制御の実現を目指します。
モータ誘起電圧
ここではエンコーダなどの外部センサを用いずに回転位置を把握しながらの矩形波回転制御を目指します。
モータは前回に引き続き以下のセンサのないものを使用します。
センサレスの場合はブラシレスモータが回転したときに発生する端子の誘起電圧から回転位置を読み取る必要があります。
参考書籍のブラシレスモータ誘起電圧観測回路を製作し、実際の回転時の発生する電圧を観てみます。
以下が製作した誘起電圧観測用フィルタ回路です。
$v_u, v_v, v_w$でモータ端子の誘起電圧を観測します。
実動作時にはドライバによるPWMオン電圧がモータ端子には印可されますので、誘起電圧はフィルタを介して観測します。
参考書籍にならってフィルタのカットオフ周波数は1.78kHzとしました。
モータ回転制御時のPWM周波数は20kHzとしますので十分スイッチングの影響はカットされるはずです。
$v_n$は各端子間を抵抗で分圧してモータコイルの中間の電圧(中性点電圧)を観測します。
モータを電動ドライバで回してフィルタ回路を介して誘起電圧を観測してみました。
ブラシレスモータの誘起電圧を観測
電動ドライバで回して観測#ブラシレスモータ駆動への道 pic.twitter.com/uahgh5MaBV
— HomeMadeGarbage (@H0meMadeGarbage) April 22, 2022
以下が観測波形。それぞれ120°位相が異なる電圧が観られました。
回転位置検出
次にモータに前回製作した駆動ドライバを接続した状態(電源はOFF)で電動ドライバでモータを回して、
誘起電圧$v_u$と中性点電圧$v_n$を観ました。
誘起電圧$v_u$と中性点電圧$v_n$と一致する点がコイルが永久磁石による磁束と直行せずに電磁誘導が起きない位置にあることを示しており、更にモータ端子の誘起電圧が$v_n$より大きいか小さいかでモータの位置を分類することができます。
フィルタ回路の出力をESP32のアナログ入力ピンに接続して、各誘起電圧と中性点電圧$v_n$を比較することでモータの回転位置を1周6分割で把握することができました。
コントローラをESP32に変更
回転速度検出したい#ブラシレスモータ駆動への道 pic.twitter.com/3XjUZ1LCoI— HomeMadeGarbage (@H0meMadeGarbage) April 23, 2022
各誘起電圧が中性点電圧$v_n$より大きければ1を出力し、各状態を分類することで6つの位置ステートに分けれました。
中性点との比較にはヒステリシスのあるコンパレータなどは用いず、ESP32アナログピンによるanalogReadで実施しました。
若干ノイズ的な比較出力もありますが$v_u > v_n、v_v < v_n、v_w > v_n$ならモータ位置①などと明確な場合分けによって6分割できました。
ステートの時間を観測すれば回転速度も把握できることになります。
以上、誘起電圧と中性点電圧比較によってモータの位置と回転速度を把握できるようになりました!
センサレス クローズドループ矩形波制御
いよいよモータ位置と回転速度に基づいて制御を実施します。
構成
ここでの駆動システムの構成は以下の通りです。
スイッチでモータの正転・逆転を変えれるようにしました。
またここではモータの回転速度ではなくドライバのハイサイドON時の20kHz PWMデューティを可変抵抗で指定するようにいたしました。
動作
制御の詳細説明の前に動作をご覧ください。
ブラシレスモータ センサレス クローズドループ矩形波制御でけた
正転・逆転の切り替えもスムーズ回転速度指定ではなくハイサイドOnのPWMデューティを可変抵抗で指定してるだけだから負荷によって回転速度変わる。
回転速度指定->PWM変換ブロック実装を目指す。#ブラシレスモータ駆動への道#EP32 pic.twitter.com/uZ6Cs8w5Lt
— HomeMadeGarbage (@H0meMadeGarbage) April 25, 2022
無事に回転制御できました!
可変抵抗による速度制御やスイッチによる正転・逆転切り替えも動作しました。
以下は動作時の誘起電圧$v_u$と中性点電圧$v_n$です。
スイッチングノイズの影響を受けておりますが、電動ドライバでモータを動かしたときと同様の波形で制御出来ております。
Arduinoコード
ESP32用に制作したプログラムを元に制御方法説明します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 |
#define Bottun 39 #define uHin 25 #define uLin 5 #define vHin 26 #define vLin 17 #define wHin 27 #define wLin 16 #define Vu 34 #define Vv 35 #define Vw 32 #define Vn 33 #define StateVar 14 #define StateOn 12 #define vol 13 int uPWMCH = 0; int vPWMCH = 1; int wPWMCH = 2; int State = 0, StateOld = 0, IniOK = 0; int span = 10; int vVu = 0; int vVv = 0; int vVw = 0; int vVn = 0; int U = 0; int V = 0; int W = 0; int BottunState = 0; unsigned long oldTime = 0, nowTime; unsigned long rotTime[6] = {0,}; float rotTimeAve; int volPWM; //Core0 void rotPosition(void *pvParameters) { while(IniOK == 0){ //初期設定待ち delay(1); } nowTime = micros(); for (;;){ disableCore0WDT(); vVu = analogRead(Vu); vVv = analogRead(Vv); vVw = analogRead(Vw); vVn = analogRead(Vn); BottunState = digitalRead(Bottun); if(vVu >= vVn){ U = 1; }else{ U = 0; } if(vVv >= vVn){ V = 1; }else{ V = 0; } if(vVw >= vVn){ W = 1; }else{ W = 0; } if(U == 1 && V == 0 && W == 1){ if(BottunState){ State = 1; }else{ State = 6; } }else if(U == 1 && V == 0 && W == 0){ if(BottunState){ State = 2; }else{ State = 1; } }else if(U == 1 && V == 1 && W == 0){ if(BottunState){ State = 3; }else{ State = 2; } }else if(U == 0 && V == 1 && W == 0){ if(BottunState){ State = 4; }else{ State = 3; } }else if(U == 0 && V == 1 && W == 1){ if(BottunState){ State = 5; }else{ State = 4; } }else if(U == 0 && V == 0 && W == 1){ if(BottunState){ State = 6; }else{ State = 5; } } /* Serial.print(U); Serial.print(", "); Serial.print(V); Serial.print(", "); Serial.print(W); Serial.print(", "); Serial.print(State); */ if(StateOld != State){ digitalWrite(StateVar, HIGH); nowTime = micros(); rotTime[State - 1] = nowTime - oldTime; oldTime = nowTime; StateOld = State; } rotTimeAve = (rotTime[0] + rotTime[1] + rotTime[2] + rotTime[3] + rotTime[4] + rotTime[5]) / 6.0; //Serial.print(", "); //Serial.println(rotTimeAve / 1000.0); } } void setup() { Serial.begin(115200); pinMode(Bottun, INPUT); pinMode(uLin, OUTPUT); pinMode(vLin, OUTPUT); pinMode(wLin, OUTPUT); digitalWrite(uLin, LOW); digitalWrite(vLin, LOW); digitalWrite(wLin, LOW); pinMode(StateVar, OUTPUT); digitalWrite(StateVar, LOW); attachInterrupt(StateOn, rot, RISING); ledcSetup(uPWMCH, 20000, 10); ledcAttachPin(uHin, uPWMCH); ledcWrite(uPWMCH, 0); ledcSetup(vPWMCH, 20000, 10); ledcAttachPin(vHin, vPWMCH); ledcWrite(vPWMCH, 0); ledcSetup(wPWMCH, 20000, 10); ledcAttachPin(wHin, wPWMCH); ledcWrite(wPWMCH, 0); //回転位置検出 タスク xTaskCreatePinnedToCore( rotPosition , "rotPosition" // A name just for humans , 4096 // This stack size can be checked & adjusted by reading the Stack Highwater , NULL , 1 // Priority, with 3 (configMAX_PRIORITIES - 1) being the highest, and 0 being the lowest. , NULL , 0); //State 5 ledcWrite(uPWMCH, 0); digitalWrite(uLin, HIGH); ledcWrite(vPWMCH, 0); digitalWrite(vLin, LOW); ledcWrite(wPWMCH, 128); digitalWrite(wLin, LOW); delay(100); //State 6 ledcWrite(uPWMCH, 0); digitalWrite(uLin, LOW); ledcWrite(vPWMCH, 0); digitalWrite(vLin, HIGH); ledcWrite(wPWMCH, 128); digitalWrite(wLin, LOW); delay(100); IniOK = 1; } void loop() { } void rot(){ digitalWrite(StateVar, LOW); volPWM = analogRead(vol) / 8; //Serial.println(volPWM); delay(int(rotTimeAve / 2.0)); if(State == 1){ ledcWrite(uPWMCH, volPWM); digitalWrite(uLin, LOW); ledcWrite(vPWMCH, 0); digitalWrite(vLin, HIGH); ledcWrite(wPWMCH, 0); digitalWrite(wLin, LOW); }else if(State == 2){ ledcWrite(uPWMCH, volPWM); digitalWrite(uLin, LOW); ledcWrite(vPWMCH, 0); digitalWrite(vLin, LOW); ledcWrite(wPWMCH, 0); digitalWrite(wLin, HIGH); }else if(State == 3){ ledcWrite(uPWMCH, 0); digitalWrite(uLin, LOW); ledcWrite(vPWMCH, volPWM); digitalWrite(vLin, LOW); ledcWrite(wPWMCH, 0); digitalWrite(wLin, HIGH); }else if(State == 4){ ledcWrite(uPWMCH, 0); digitalWrite(uLin, HIGH); ledcWrite(vPWMCH, volPWM); digitalWrite(vLin, LOW); ledcWrite(wPWMCH, 0); digitalWrite(wLin, LOW); }else if(State == 5){ ledcWrite(uPWMCH, 0); digitalWrite(uLin, HIGH); ledcWrite(vPWMCH, 0); digitalWrite(vLin, LOW); ledcWrite(wPWMCH, volPWM); digitalWrite(wLin, LOW); }else if(State == 6){ ledcWrite(uPWMCH, 0); digitalWrite(uLin, LOW); ledcWrite(vPWMCH, 0); digitalWrite(vLin, HIGH); ledcWrite(wPWMCH, volPWM); digitalWrite(wLin, LOW); } } |
モータ回転時の誘起電圧を検出できないと回転位置の把握ができないため、
コントローラ起動時にモータを少し回して誘起電圧を発生させています (L. 185-201)。
デュアルコアのcore0でモータの位置と速度を算出しています (L. 43-141)。
モータの位置ステートの切り替わり時にIO14をHIGHに立ち上げてIO12でそれを受けて割り込みでドライバを駆動するようにいたしました (L. 159)。
駆動するためのドライバのトランジスタON/OFFステートはスイッチ(IO39)のH/Lで正転用・逆転用に切り替えています (L. 80-116)。
回転速度rotTimeAve は各位置ステートの時間の平均で割り出しています (L. 128-136)。
ステートの切り替えの割り込みでドライバ駆動関数rot() (L. 210-263)が呼ばれます。
rotTimeAveの半分待った後 (L. 216) に位置ステートと正転/逆転に基づいたトランジスタON/OFF設定が実行されます。
位置ステートの時間の真ん中でモータが駆動して次の位置ステートに移行し回転が継続されます。
トランジスタのハイサイドONはledcWriteを用いて20kHz PWMで印可します。分解能は10ビット(0~1023)でオンデューティ設定します (L. 161-171)。
オンデューティは可変抵抗(IO13)で設定するようにしました (L. 224)。
おわりに
センサレス クローズドループ矩形波制御が実現できました。
モータの誘導起電力で回転位置や速度を判定しているため、低速では起電力が小さくモータを駆動できませんでした。
モータ端子の誘導起電力で位置検知してるから
低速だと電圧小さくて制御できない。#ブラシレスモータ駆動への道#ESP32 pic.twitter.com/0B1UVj4mf1— HomeMadeGarbage (@H0meMadeGarbage) April 25, 2022
またここではモータを駆動するドライバのハイサイドトランジスタのPWMオンデューティを指定して制御しましたが、この場合モータの負荷によって速度は変わってしまいます。
次回は回転速度を指定するようにして、実回転速度からPWMオンデューティをコントロールするようにしたいと思います。