12
5

【MATLAB】真理値表で直接回る!ブラシレスモータ

Last updated at Posted at 2023-12-11

はじめに

待ちに待ったクリスマス、街ゆく人たちは嬉しそうな声色で「メリークリスマス!」と挨拶を交わしあいます。

一方で私はと言うと、毎年クリスマスを迎える時期に下記のような挨拶をするのでした。

『MATLAB/SimulinkはarduinoやRaspberryPiといった、ハードウェアと連携することが可能です。連携方法としてはMATLABと連携する方法、Simulinkと連携する方法が準備されています。』2019年版)(2022年版

ということで、毎度おなじみのSimulink Support Packageを用いたMATLAB芸です。
https://jp.mathworks.com/matlabcentral/fileexchange/40312-simulink-support-package-for-arduino-hardware

そして、タイトルを見ただけで中身が大筋分かったアナタ! MATLAB通ですね~。そうです、私が変な芸人ですお察しの通りStateflow芸の披露となります。
(Stateflowって何?な人はStateflow 入門へどうぞ。Onrampは神コンテンツ)

Stateflow単体でもMATLAB芸として十分に成立することは、Stateflow テトリスのモデルを頑張って解読するを一読することでご理解頂けると思いますが、何せこの芸は競技者人口が少ない。そこにSimulink Support Packageを組み合わせた芸は、なんかもう絶滅危惧種かってぐらいの希少さになりますので珍しがって読んで頂ければと思います。珍しがってくれ、頼む

前置きはさておきとして、Simulink Support PackageとStateflowの組み合わせで、下記のようにモータをぎゅんぎゅん回す方法について本記事では解説していきます。

1. ブラシレスモータのハードウェア特性

以降、ほぼほぼ東芝の120度通電解説資料を使っての説明になります。サンキュー東芝! ブラシレスモータ、120度通電についてご存じの方は飛ばして頂いて構いません。

1.2 何を/どう操作してモータが回るか

所謂「ブラシレスモータ」は3相インバータと永久磁石同期モータの組み合わせによって構成されます。3相インバータはU相、V相、W相がそれぞれ上下2つのスイッチング素子から構成されます。(下記図にてU相であれば$Q_{U-H}$と$Q_{U-L}$から構成される)
image.png
120度通電における上下2つのスイッチング素子の動作方法はいくつかありますが、本記事では下記3つの動作状態のみを用いるものとします。

上側SW 下側SW 本記事における出力状態の呼称
PWM 上側反転 PWM
OFF ON GND
OFF OFF OFF

なお、上記3状態を用いた120度通電は東芝資料にて片アームPWM・上下相補スイッチングに分類されます。(さらに厳密に言うと片アームPWM・上下相補スイッチング・下ON。)PWM、OFF、GNDの具体的なイメージは下記ピンク枠内を参照。
image.png

PWMでは、上側SWのオンDutyを0~100%の間にて調整することでインバータ出力電圧を0VからDCリンク電圧の間に操作することが出来ますが、本記事では詳細説明は省略します。なんか調整できる要素があるんだなぐらいに思って下さい。(乱暴)

3相インバータを構成するU、V、Wの3相それぞれが上記3つの出力状態を取る事が出来ますが、実際の120度通電では各動作状態ごと1相にしか適用されません。このため組み合わせの総数としては3×2×1=6通りとなります。

U相SW出力 V相SW出力 W相SW出力 本記事における出力状態の呼称
PWM GND OFF
PWM OFF GND
OFF PWM GND
GND PWM OFF
GND OFF PWM
OFF GND PWM

動作状態1ではU相-V相間に電圧が印可されることで電流が流れます。動作状態2ではU相-W相間、動作状態3ではV-W相間に…というように、状態1~6を随時切り替えていくことで電流が流れる相が変化、回転磁界が発生することでモータが回転します。

1.3 動作状態をどうやって決定するか

状態1~6の随時切り替えにはホールセンサが用いられます。ホールセンサの詳しい説明は東芝の120度通電解説資料を参照して頂きたいですが、重要なポイントとしては下記です。

・120度ごとに配置される
・モータ磁極位置に応じてON/OFFを出力する

下記図は、U相ホールセンサのON/OFF境界を赤線、V相を青線、W相を緑線として表した図になりますが、3本の境界線によってモータ磁極位置は60度ごとに切り替わる6領域に分割されます。

image.png

上記①~⑥領域におけるU,V,W相ホールセンサ状態は下記となります。

本記事における領域の呼称 U相センサ状態 V相センサ状態 W相センサ状態
ON OFF ON
ON OFF OFF
ON ON OFF
OFF ON OFF
OFF ON ON
OFF OFF ON

天下り的に答えを言ってしまうと、領域①にて出力状態1を、領域②にて出力状態2を出力することを決定することで120度通電によるブラシレスモータ駆動が実現されます。

2. 真理値表で表現する120度通電

1.3に記載したU,V,W相センサ状態表と、1.2に記載したU,V,W相SW出力表を突き合わせることで120度通電に必要な真理値表が完成します。なお、ここでセンサ状態はON=True、OFF=Falseとして書き換えるものとします。

領域 U相センサ状態 V相センサ状態 W相センサ状態   U相SW出力 V相SW出力 W相SW出力
True False True PWM GND OFF
True False False PWM OFF GND
True True False OFF PWM GND
False True False GND PWM OFF
False True True GND OFF PWM
False False True OFF GND PWM

東芝の120度通電解説資料だと下記表にて表現されています。センサ状態とSW状態が左右逆に書かれている点、SW出力が上側と下側で別々に記載されている点などに相違がありますが、やっている事は同じです。
image.png

3. MATLAB/Simulinkにおける真理値表の実装

皆様お待たせしました、やっと本論に入ります。と言ったのも束の間、真理値表の実装はStateflowの真理値表ブロックを使うだけなので本論が一瞬で終わります。

Mathworks公式の真理値表のプログラミングになんかいっぱい書いてありますが、要するにやることは…

Simulinkライブラリブラウザを開いてTruth Tableをドラッグ&ドロップしーの
image.png

2章で作成した真理値表を入力しーの
image.png

Ardino ハードウェア連携の入出力ブロックをいい感じに繋げーの
image.png

するだけです。
ね、 簡単でしょう?






…と言われても困りますよね。
ということで、要点だけに絞って下記解説します。

3.1 真理値表とTruth Tableとの対応

下記色分けされた領域のように対応しています。なお、真理値表をTruth Tableに実装する際に行と列を入れ替えている点注意下さい。(TFの組み合わせを行ごとに書くほうが一般的な気がするが…Truth Tableは列ごとに記載する仕様のためこうなる)

image.png
オレンジ:入力条件の説明部分。単に説明のため、記載内容は振る舞いへ影響しない。

青:True/Falseの組み合わせ。なお真理値表にない組み合わせとしてTruth Table7つめに、3入力とも「-」となる条件を追加しているがこれはデフォルトを指す。今回の場合、3入力ともTrue、および3入力ともFalseの場合にデフォルトが選択される。

緑:所定のTrue/Falseの組み合わせにおいて実行されるアクションの番号、およびその具体的内容。

3.2 Simulinkモデルにおける入出力設定

入出力設定の前段としてハードウェア構成、入出力を説明します。

3.2.1 ハードウェア構成、入出力

image.png

滅茶苦茶ざっくり説明ですが、
①ブラシレスモータ(ホールセンサ付き)
②3相インバータ
③マイコン
④ボリューム

にて構成されます。

②3相インバータはX-NUCLEO-IHM07M1を使用しています。このインバータにて使用されているドライバIC L6230は、U,V,W各相の上下SWを1つのPWM端子(下記IN1~IN3端子)のみで相補PWMにて操作する特徴を有すると共に、イネーブル端子(下記EN1~EN3端子)によって上下SWを両OFFとする特徴を有しています。

image.png

このため③マイコンから②3相インバータへの出力としては、PWM出力(U,V,W)と出力イネーブル(U,V,W)の6出力が必要となります。

①ブラシレスモータは②3相インバータより供給される3相電圧によって回転し、回転角度に応じてホールセンサのON/OFFが変化、これが③マイコンへと取り込まれ、Simulinkモデル上のTruth Tableへと入力されます。

最後に④ボリュームはPWMの調整用で、ボリュームをねじることで最終的にブラシレスモータへと出力される電圧が調整されるようモデルの作成を行っています。

3.2.2 モデル入出力設定

上記より
入力:ホールセンサ値(U,V,W)、ボリューム
出力:イネーブル(U,V,W)、PWM(U,V,W)
として定義されるため、モデルにて下記のように配置・配線します。
image.png

なお、状態遷移図における出力状態とイネーブル、PWM出力との関係は下記になります。

出力状態 イネーブル PWM出力
PWM ON ボリュームと連携
GND ON 0%
OFF OFF 0%

上記表に基づき、Truth Tableのアクションテーブル欄を記入しています。再掲になりますが、領域①のU,V,W相SW出力は下記にて定義しました。

領域 U相SW出力 V相SW出力 W相SW出力
PWM GND OFF

Truth Tableのアクションテーブル欄の記入結果は下記です。ここで、EN_xが各相のイネーブル端子、PWM_xが各相のPWM端子へと繋がっています。また、dutyはTruth Table入力端子にてボリュームと繋がっています。
image.png

領域②~⑥についても同様に記入することで、120度通電に必要な出力を得ることが出来ます。
StateflowおよびTruth Tableの入力に慣れていないと面倒なようにも感じますが、状態遷移図そのものを作成することでプログラミングが完了する恩恵は非常に大きいと感じました。

4.改めて、実装結果

冒頭の動画とやっている事は基本同じですが、途中で紹介したハードウェア構成全体が見える動画を撮り直したので。

おまけ C言語における120度通電の実装

完全に趣味の領域の話ですが(記事全般を通して趣味の話しかしてないので何を今更だが)本記事のネタを作成する数か月前に120度通電をCコードで実装していたので、その際のコードを引き合いにすることで真理値表を直接書けてしまうことのメリットを強調してみます。


コードが滅茶苦茶長いので、折り畳み。

当然ながらCコードでは真理値表を直接書けないので、switch caseを駆使することになりますが下記のような構成によって実装する必要があります。(なお、Cコードの実装に当たってはCQ出版の 高トルク&高速応答!センサレス・モータ制御技術をおおいに参照しています。 )

A. ホールセンサ入力を領域①~⑥に変換するコード
B. 領域①~⑥に対応してU,V,W相出力がPWMか、GNDか、OFFかを決定するコード
C. U,V,W相出力に応じてイネーブルを決定するコード
D. 出力がPWMの場合、ボリュームに応じて出力Dutyを決定するコード

実際のコードを下記に記載します。内容を追う必要は全くありません、あくまで実際にコードを書くと滅茶苦茶大変だというのが伝われば良いので。
(ただしこのコードの長さは可読性重視によるところが大きく、可読性を無視すればもっと短くできる余地はあります。Truth Tableは可読性が高く、ブロックが1つで済む点が秀逸。)

「A. ホールセンサ入力を領域①~⑥に変換するコード」の実際

static uint8_t calcVoltageMode(uint8_t* Hall){
	uint8_t hallInput;
	uint8_t voltageMode = 0;

	// Convert hall input to digital signal
	hallInput = (Hall[2] << 2) + (Hall[1] << 1) + Hall[0];

	// Decode digital signal to voltage mode
	switch(hallInput){
	  case 3:
		voltageMode = 3;
		break;
	  case 2:
		voltageMode = 4;
		break;
	  case 6:
		voltageMode = 5;
		break;
	  case 4:
		voltageMode = 6;
		break;
	  case 5:
		voltageMode = 1;
		break;
	  case 1:
		voltageMode = 2;
		break;
	  default :
		voltageMode = 0;
	  break;
	}
	return voltageMode;
}

「B. 領域①~⑥に対応してU,V,W相出力がPWMか、GNDか、OFFかを決定するコード」の実際

static void calcOutputMode(uint8_t voltageMode, int8_t* outputMode){
		// Decide output mode
		// OUTPUTMODE_OPEN = 0
  		// OUTPUTMODE_POSITIVE = 1
  		// OUTPUTMODE_NEGATIVE = -1
  
		switch(voltageMode){
		  case 3:
			outputMode[0] = OUTPUTMODE_OPEN;
			outputMode[1] = OUTPUTMODE_POSITIVE;
			outputMode[2] = OUTPUTMODE_NEGATIVE;
			break;
		  case 4:
			outputMode[0] = OUTPUTMODE_NEGATIVE;
			outputMode[1] = OUTPUTMODE_POSITIVE;
			outputMode[2] = OUTPUTMODE_OPEN;
			break;
		  case 5:
			outputMode[0] = OUTPUTMODE_NEGATIVE;
			outputMode[1] = OUTPUTMODE_OPEN;
			outputMode[2] = OUTPUTMODE_POSITIVE;
			break;
		  case 6:
			outputMode[0] = OUTPUTMODE_OPEN;
			outputMode[1] = OUTPUTMODE_NEGATIVE;
			outputMode[2] = OUTPUTMODE_POSITIVE;
			break;
		  case 1:
			outputMode[0] = OUTPUTMODE_POSITIVE;
			outputMode[1] = OUTPUTMODE_NEGATIVE;
			outputMode[2] = OUTPUTMODE_OPEN;
			break;
		  case 2:
			outputMode[0] = OUTPUTMODE_POSITIVE;
			outputMode[1] = OUTPUTMODE_OPEN;
			outputMode[2] = OUTPUTMODE_NEGATIVE;
			break;
		  default :
			outputMode[0] = OUTPUTMODE_OPEN;
			outputMode[1] = OUTPUTMODE_OPEN;
			outputMode[2] = OUTPUTMODE_OPEN;
		  break;
		}
}

「C. U,V,W相出力に応じてイネーブルを決定するコード」の実際

void writeOutputMode(int8_t* outputMode){

	// if the outputMode is OPEN, set Enable Pin to RESET.
	if(outputMode[0] == OUTPUTMODE_OPEN )
		HAL_GPIO_WritePin(EN1_GPIO_Port, EN1_Pin, GPIO_PIN_RESET);
	else
		HAL_GPIO_WritePin(EN1_GPIO_Port, EN1_Pin, GPIO_PIN_SET);

	if(outputMode[1] == OUTPUTMODE_OPEN )
		HAL_GPIO_WritePin(EN2_GPIO_Port, EN2_Pin, GPIO_PIN_RESET);
	else
		HAL_GPIO_WritePin(EN2_GPIO_Port, EN2_Pin, GPIO_PIN_SET);

	if(outputMode[2] == OUTPUTMODE_OPEN )
		HAL_GPIO_WritePin(EN3_GPIO_Port, EN3_Pin, GPIO_PIN_RESET);
	else
		HAL_GPIO_WritePin(EN3_GPIO_Port, EN3_Pin, GPIO_PIN_SET);
}

「D. 出力がPWMの場合、ボリュームに応じて出力Dutyを決定するコード」の実際

static void calcDuty(int8_t* outputMode, float DutyRef, float* Duty){

	if( outputMode[0] > 0)
		Duty[0] = (float)(outputMode[0] * DutyRef;
    else
    	Duty[0] = 0;
	if( outputMode[1] > 0)
		Duty[1] = (float)(outputMode[1] * DutyRef;
    else
    	Duty[1] = 0;
	if( outputMode[2] > 0)
		Duty[2] = (float)(outputMode[2] * DutyRef;
    else
    	Duty[2] = 0;

}

おわりに

ちなみに、上記動画と同じ内容の実機デモを会社でやったのですが反応がイマイチでした。
やはりモータ制御は人類には早すぎたか…。

12
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
5