【モータ制御】ベクトル制御を実装する上で必要な処理の実用的なプログラム構成例を紹介【クラーク変換/パーク変換/空間ベクトル変調/PI制御】

以前下記記事にて永久磁石同期モータ(PMSM)のベクトル制御のプログラムを紹介しましたが、その中でも使用しているベクトル制御を実装する上で必要となる処理について解説いたします

【STM32(nucleo)】永久磁石同期モータ(PMSM)のエンコーダ付きベクトル制御のプログラム紹介(GitHubで公開中)【PlatformIO】

モータ制御を現役でバリバリやっているような人には少し物足りない内容かもしれませんが、ひとつの実装例として参考になれば幸いです

なお、今回紹介するプログラムは一般的なC言語で記述されたものですが、浮動小数点演算を使用する前提の構成となります

また、モータドライバ(三相インバータ)を駆動したりモータ電流をフィードバックするための相補PWMやA/Dコンバータなど、モータ制御向けの機能を有するマイコンで実装する必要があります

本ブログでは浮動小数点演算器(FPU)を搭載していてモータ制御に必要な機能を一通り搭載しているSTマイクロのnucleo-G474REというマイコンボードを使用しております

クラーク変換(U/V/W→α/β変換)のプログラム例

ベクトル制御では永久磁石モータの三相ステータコイルに流れる三相電流(U/V/W軸)を検出してロータの磁束方向およびそれに直交する方向からなる座標系(d/q軸)に座標変換を行います

その前段階の処理として三相固定座標系(U/V/W軸)を二相固定座標系(α/β)に変換します

U
U
V
V
W
W
α
α
β
β
三相固定座標系
三相固定座標系
二相固定座標系
二相固定座標系
Text is not SVG – cannot display

この変換処理を一般的にクラーク変換と呼びます

クラーク変換には変換前後で正弦波の振幅(ベクトルの大きさ)を不変とする相対変換と電力(各相の電流×電圧の総和)を不変とする絶対変換の二種類が存在しますが、今回は絶対変換を紹介します

void uvw2ab( float u, float v, float w, float *a, float *b )
{
  *a = ( 0.816496580928f ) * ( u - ( ( v + w ) * (0.5f) ) );
  *b = ( 0.7071067811866f ) * ( v - w );
}

引数u,v,wにて変換元の三相固定座標系の数値を指定し、引数a,bに変換後の二相固定座標系の数値を格納する変数のアドレスを指定します

パーク変換(α/β→d/q変換)のプログラム例

クラーク変換で二相固定座標系(α/β軸)に変換したモータ電流を回転座標系(d/q軸)に変換する処理を一般的にパーク変換と呼びます

α
α
二相固定座標系
二相固定座標系
二相回転座標系
二相回転座標系
β
β
d
d
q
q
α(U)
α(U)
θ
θ
ω
ω
Text is not SVG – cannot display

回転座標系はロータの磁束方向(d軸)とそれに直交する方向(q軸)となりますので当然ながら変換するためにはロータの位相情報が必要となります

ロータの位相は一般的にU軸(α軸)を基準としたd軸の位相として定義され、一般的にはエンコーダなどの位置センサを用いて検出する必要があります

パーク変換には三角関数(sin,cos)の演算が必要となりますが今回は数学演算ライブラリ(math.h)のsinf関数およびcosf関数にて数値演算しています

sinテーブルを作成してテーブル参照で三角関数の演算を代用する方法もありますが、最近のマイコンは性能が良いのでほとんど使わなくなったような気がします・・・

void ab2dq( float a, float b, float *d, float *q, float phs )
{
	float sin, cos;

	sin = sinf(phs);
	cos = cosf(phs);

	*d = cos*a + sin*b;
	*q = cos*b - sin*a;
}

引数a,bにて変換前の二相固定座標系の数値を指定し、引数d,qにて変換後の回転座標系の数値を格納する変数のアドレスを指定します

また、引数phsにロータの位相を指定します

PI制御のプログラム例

回転座標系(d/q軸)に変換されたモータ電流はそれぞれ個別に制御されます

d軸電流は励磁電流と呼ばれ、ロータの磁束を強め/弱める方向の磁束を発生させます

q軸電流はトルク電流と呼ばれ、ロータの磁束と作用してマグネットトルクを発生させる方向の磁束を発生させます

基本的にはマグネットトルク発生に寄与しないd軸電流を0Aに制御し、q軸電流にてトルク制御を行います

電流フィードバック制御は一般的にPI制御にて構成します

PI制御は観測値と目標値の偏差を入力値として出力値が制御対象への入力値(今回の場合は三相ステータコイルに印可するd/q軸電圧)となります

P(比例制御)とI(積分制御)を組み合わせた制御で、それぞれの制御のゲイン(比例ゲイン、積分ゲイン)がパラメータとなります

また、処置が飽和したときの対策として積分処理にはリミッタをかけるのが一般的かと思います

typedef struct
{
	float sum;
	float out;
	float max;
	float min;
	float kp;
	float ki;
} pi_t;

uint8_t pi( pi_t *hdl, float err )
{
	uint8_t ret = 0;
	float out;

	hdl->sum = hdl->sum + err * hdl->ki;

	if(hdl->sum > hdl->max)
	{
		hdl->sum = hdl->max;
		ret = 1;
	}
	else if(hdl->sum < hdl->min)
	{
		hdl->sum = hdl->min;
		ret = 1;
	}

	out = hdl->sum + err * hdl->kp;

	if(out > hdl->max)
	{
		out = hdl->max;
		ret = 1;
	}
	else if(out < hdl->min)
	{
		out = hdl->min;
		ret = 1;
	}

	hdl->out = out;

	return ret;
}

pi_t型の引数hdlにて比例ゲイン(kp)、積分ゲイン(ki)、PI制御の出力リミット値(max/min)を指定し、引数errにて観測値と目標値の偏差を指定します

積分処理の積算値は引数hdlのsumが更新されるので制御を初期化する場合はsumをゼロクリアすればOKです

また、sumもPI制御の出力リミット値(max/min)でリミットされます

上記関数には戻り値が設定されており、PI制御が飽和した場合に1を返すようにしています

今回作成したプログラムではこの機能は使用していませんが、例えば電流PI制御の場合はリミッタ値をDCリンク電圧を元に可変するようにして飽和時は弱め界磁制御に移行するといった使い方が想定できます

逆パーク変換(d/q→α/β変換)のプログラム例

電流PI制御にて出力されたd/q軸電圧は永久磁石同期モータのステータコイルに印可すべきリファレンス電圧となります

実際にはモータドライバ回路(三相インバータ)にて出力する必要があるので、最終的にはPWM Dutyに変換する必要があります

その第一段階としてd/q軸回転座標系をα/β二相固定座標系に変換します

ちょうど先に紹介したパーク変換の逆変換処理となります

void dq2ab( float d, float q, float *a, float *b, float phs )
{
	float sin, cos;

	sin = sinf(phs);
	cos = cosf(phs);

	*a = cos*d - sin*q;
	*b = cos*q + sin*d;
}

引数d,qにて変換前の回転座標系の数値を指定し、引数a,bにて変換後の二相固定座標系の数値を格納する変数のアドレスを指定します

また、引数phsにロータの位相を指定します

ロータの位相は、厳密には今回指定したd/q軸電圧がPWMとして出力されるタイミングの位相にする必要があります

PWMキャリアの谷でモータ電流をA/D変換してから一連の処置を行ったとした場合、例えば次のPWMキャリアの谷でPWM Dutyが反映されるとしたらA/D変換時の位相からPWM周期で1~2周期分進み位相となるので平均して1.5周期分進めた位相を指定します

ちなみに今回作成したベクトル制御のプログラムは手を抜いて進み位相は考慮していません・・・

あくまでも厳密にはといったところなのでそこまでしなくても普通に動作します

逆クラーク変換(α/β→U/V/W変換)のプログラム例

逆パーク変換と同様にクラーク変換逆変換となります

逆クラーク変換も相対変換と絶対変換が存在しますのでこちらも絶対変換にて構成してます

void ab2uvw( float a, float b, float *u, float *v, float *w )
{
  a *= (0.40824892046f);
  b *= (0.70710678118f);
  *u = 2.0f * a;
  *v = -a + b;
  *w = -a - b;
}

引数a,bにて変換元の固定二相座標系の数値を指定し、引数u,v,wにて変換先の固定三相座標系の数値を格納する変数のアドレスを指定します

空間ベクトル変調(SVM)のプログラム例

U/V/W相のリファレンス電圧が算出出来たらこれらを各相のPWMに変換します

リファレンス電圧をそのままPWMに変換した場合、電圧利用率が低くなってしまうので、まず空間ベクトル変調(SVM)という手法を使ってリファレンス電圧に手を加えます

void svm( float *u, float *v, float *w)
{
	float max, min, typ;

	max = min = *u;
	if(*v > max) max = *v;
	if(*w > max) max = *w;
	if(*v < min) min = *v;
	if(*w < min) min = *w;
	typ = (max + min) * 0.5;

	*u -= typ;
	*v -= typ;
	*w -= typ;
}

SVMは3つのリファレンス電圧に対し、最大のものと最小のものの平均値を中間電圧とし、中間電圧を各相リファレンス電圧から引き算します

こうすることで正弦波のピークが抑えられ、より高い変調率を選択することが出来るので電圧利用率が増加します

リファレンス電圧は正弦波状ではなくなってしまいますが、モータに印可される電圧は変わらないため問題ありません(同じ大きさの中間電圧分シフトしているだけなので、モータに印可される線間電圧の相対的な大きさは変わらない)

PWM変調率を算出するプログラム例

SVMによって最終的に出力するリファレンス電圧が決定したら電圧値を変調率に変換します

変調率はDCリンク電圧(Input)とリファレンス電圧(Output)の比率で、上アームと下アームのPWM ON Dutyを表します

具体的には、変調率1.0のときは上アームが100% ON、変調率が-1.0のときは下アームが 100% ON、変調率は0.0のときは上アーム、下アーム共に50% ONといった感じです

float rVBus = 1.41421356f/vBus;
float uMod = vURef*rVBus;
float vMod = vVRef*rVBus;
float wMod = vWRef*rVBus;
uint16_t pwmRef = htim1.Init.Period>>1;
__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, pwmRef - pwmRef*uMod );
__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_2, pwmRef - pwmRef*vMod );
__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_3, pwmRef - pwmRef*wMod );

uMod、vMod、wModが変調率です

変調率を求めた後のコード例はマイコンやペリフェラルの設定によっても異なると思いますが、変調率の定義に従って上下アームのON Dutyを設定してやればOKです

まとめ

ベクトル制御を実装する上で必要となる処理のプログラム例を紹介してきました

これらの処理はどれも使いまわしが効く処理なので一度実装してしまえば基本的に調整することはないと思います

もしかしたらもっと良いコーディングの方法があるかもしれませんが、ひとつの実装例として参考になれば幸いです

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA