はじめに
こんにちは。(株) 日立製作所の Lumada Data Science Lab. の松本茂紀です。
先日掲載しました私の紹介記事(データサイエンティストの仕事~日立のデータサイエンティストに聞いてみた!~シリーズ3~)で、オフの時間に生活の役に立つIoTデバイスを作っていることを少し紹介しました。
前回の「IoTプラレールを作ってみました(その1:IoT電池の作成)」に引き続き、今回の「IoTプラレールを作ってみました(その2:IoT電池の作成)」では、モーターを制御するところから、実際に自動運転(周期的な動作)でプラレールを走らせるところまでを紹介します。
その他の記事は以下から参照できますのでよろしければどうぞ。
IoTプラレールを作ってみました(その1:IoT電池の作成 前編)
IoTプラレールを作ってみました(その3:IoTサイクルメータを作ろう 前編)
IoTプラレールを作ってみました(その4:IoTサイクルメータを作ろう 後編)
モーター制御の仕組みを知ろう
フィットネスバイクを漕ぐ速さに応じて、プラレールの速度を変化させるためには、モーターの回転速度を制御する必要があります。モーターの回転速度は、掛ける電圧を上げれば速く、下げれば遅くなりますので、電圧の調整が必要になります。
しかし、今回扱うESP32には、電圧を調整する機能がないため、パルス幅変調(PWM)という仕組みを使って制御していきます。
PWMがどんな仕組みかを図を使って簡単に紹介します。まず、モーターに電圧を掛けている状態をON、電圧が掛かっていない状態をOFFとします。
通常、プラレールに電池を入れて走らせる場合は、常にONの状態になっており、電池の電圧に応じたモーターの回転速度が得られます。
これに対し、PWMは下図のように一定時間の間にONとOFFの状態を繰り返す制御を行います。ONの瞬間は先程と同じですが、OFFの状態ではモーターは止まります(正確には、すぐに止まらず回生電力が生じる状態になります)。このONとOFFの時間を一定の比率に調整すると、モーターには平均の電圧が掛かったような状態になり、結果的に常にONのときよりも低速でモーターを回転させる事ができます。
では、PWMを使って回転速度の制御を行うためには何を調整するか?というと、ON/OFFの時間の比率(デューティ比)と、周波数になります。
まず、ON/OFFのセットが1秒間に何回行われるかが周波数の設定で、今回のモータードライバの仕様では0~250 kHzとなっています。1つの周期の中で、ONとOFFの切り替え比率を何段階で調整するかを、8ビットや10ビットといったビット単位で決めていきます。8ビットは256段階、10ビットは1024段階といった具合です。どれだけ細かく制御したいかでビットを選択することになります。
今回、自転車の回転数に応じたモーター制御を考えていますが、1秒に3回転はかなり頑張って自転車漕ぐペースですので、現実的な上限は180 rpmであると仮定します。今回は2本の電池を繋いでいるため電圧は合計2.4 Vですので、256段階中の180であれば、デューティ比が70%となり約1.7 Vの出力になります。通常プラレールを動かす単3電池は1.5 Vですので、すごく頑張って自転車を漕げば、いつものプラレールの速度くらいになる、ということです。
データサイエンティストらしくモータ制御パラメータを決めよう
PWMでモーターを制御するためには、デューティ比と周波数を決める必要があります。デューティ比は自転車の回転速度との兼ね合いで、8ビットがちょうど良さそうなことがわかりました。では、周波数はどの様に決めたら良いでしょうか?
周波数は、モーターの動作効率にも影響するので、電池の減り具合に影響を及ぼしそうです。長く遊ぶためには、なるべく効率よく動作できる条件を決めたいと思います。
そこで、まずはモーターに流れる電流量を取得して、最適条件を決定していきます。なお、電流量の測定には、Raspberry Piと電流センサモジュールを使い、ニセ電池のプラス側の配線を流れる電流値を測定しました。測定方法と条件は以下のとおりです。
- 測定機器:Raspberry Pi 4 Model B
- 使用モジュール:INA219(電流センサ)
- 測定条件:
- Shunt ohms=0.1Ω
- Voltage_range=16V
- Bus ADC=16SAMP(16サンプル平均、ADC解像度12 bit, 処理間隔8.51ms)
- Shunt ADC=16SAMP(同上)
- 測定時間:10秒間
PWMでモーターを駆動させる際に、電力損失や機器負荷にも繋がるノイズ(リップル電流)が生じます。特に、影響が大きく出るというデューティ比50%に固定し、周波数を変えた際の電流値と10秒間の電力量を計測しました。1Hzから2000Hzまで変えた結果が図のとおりです。
1Hzだと特に大きく電流が流れた後、指数関数的に減衰しています。周波数が高くなるにつれ飛び上がる成分が減少していき、安定した電流値になっているように見られます。また、電力も周波数が上がるにつれて低減している様子が見られます。このことから、周波数を上げるほどエネルギー効率が良いと考えられます。
このノイズを抑えるには、PWMの周期をモーターの電気的時定数の100分の1程度にすると良いと言われており、周波数で考えると100倍大きい値にするのが良さそうです。ただ、今回のモーターに関しては、1000Hz以上にすると、キーンという耳鳴り音が出始め、モータが回らなくなるような現象が見えるため、周波数の大きさには限度があるようです。
まずは、データからモーターの電気的時定数を見積もりたいと思います。1Hzの電流値の減衰が特徴的ですので、こちらのデータを使います。
まず、周期性の確認です。センサで複数の値を取得している関係上、どのくらいのタイミングでデータ点が取れているかわからないので、データから確認してみます。データの取得間隔に対する偏自己相関を計算したところ、202の位置で強い負の相関が出ており、山が立ち上がるところの周期性が見えていると考えられます。
そこで、ウィンドウ幅202として10秒間の平均電流の平均値を計算した結果、図のようになりました。電流値が立ち上がったあと、指数関数的に減少し、ある値に収束するような振る舞いに見えます。そこで、収束する先の電流値をA∞、そこからピーク値までの高さをA0、電気的時定数をτとすると、0.2秒から0.6秒の間で以下の式に従うと考えられます。
$$
A(t) = A_0 e^{-t/\tau}+A_\infty
$$
このτを求めるには、各変数を関数フィッティングして求めれば良いのですが、どの時間範囲を選ぶか、うまくフィットできてるかの確認を容易にするため、以下の様に式変形します。
$$
\log\left(-A'(t)\right) = \log \left(-\frac{dA(t)}{dt}\right) = -\frac{t}{\tau}+\log\left(\frac{A_0}{\tau} \right)
$$
微分値の対数を取ることで、求めたい周波数(1/τ)は直線の傾きとして算出することができます。データの微分には、Savitzky-Golay法を用いました。
上記の$\log(-A'(t))$のグラフを図に示します。なお、データの始まりをピーク位置に合わせるため、0.2秒左にずらしてあります。図を見ると、0から0.3秒まで直線的で、その後は弧を描いています。このことから、0から0.3秒の区間を直線でフィッティングした結果、周波数(1/τ)は約7Hzとなりました。
以上のことから、望ましい周波数はこの100倍の700Hzと設定するのが良さそうです。
プラレールを⾛らせてみよう(自動運転)
前回設定した、Arduino IDEを使ってプログラムを書き込んでいきます。
フィットネスバイクと繋げる前に、まずはプラレール単体で動作させてみましょう。折角なので、通常のプラレールではできない、自動運転(周期的な動作)をさせてみました。今回プログラムする動きは、4秒かけて加速→5秒間最高速度で走行→4秒かけて減速→3秒停止というものです。書き込むプログラムは以下のとおりです。
// ピンとチャネル設定
const int IN1 = 25;
const int IN2 = 26;
const int CHANNEL0 = 0;
const int CHANNEL1 = 1;
// PWM設定
const int PWM_BIT = 8;
const int PWM_FREQ = 700;
const int PWM_MAX = 255;
void setup() {
// ピンをアウトプットに設定
pinMode(IN1, OUTPUT);
pinMode(IN2, OUTPUT);
// チャネルのPWMを設定
ledcSetup(CHANNEL0, PWM_FREQ, PWM_BIT);
ledcSetup(CHANNEL1, PWM_FREQ, PWM_BIT);
// ピンのチャンネルを設定
ledcAttachPin(IN1, CHANNEL0);
ledcAttachPin(IN2, CHANNEL1);
}
void loop() {
// 4秒かけて加速
for(int i=1;i<=4;i++){
forward(64*i);
delay(1000);
}
// 5秒間全速走行
delay(5000);
// 4秒かけて減速
for(int i=4;i>=1;i--){
forward(64*i);
delay(1000);
}
// 3秒間停止
brake();
delay(3000);
}
// 前進
void forward(uint32_t pwm) {
if (pwm > PWM_MAX) {
pwm = PWM_MAX;
}
ledcWrite(CHANNEL0, 0);
ledcWrite(CHANNEL1, pwm);
}
// ブレーキ
void brake() {
ledcWrite(CHANNEL0, PWM_MAX);
ledcWrite(CHANNEL1, PWM_MAX);
}
上記のプログラムをArduino IDEのエディタに記載し、矢印ボタンを押してコンパイルと書き込みを実行します。無事に書き込み終わったらプラレールの中に写真のように収納しました。なお、ネジをきつく締めすぎると、車輪が回らない可能性があるので、適度に締めるのが良さそうです。
収納したら、スイッチをオンにしておきます。一度ネジを締めると、マイコンの電源のオン/オフはできないので、プラレールのオン/オフは通常通り、プラレールの駆動車のスイッチで切り替えます。では、実際に走らせてみましょう!
上手く走行時間やレールの長さを調整すれば、駅での発車・停車するなんちゃって自動運転ができたので、これだけでも顧客(娘(4歳))は大満足のようです(笑)
おわりに
次回の記事では、IoTサイクルメータを作成します。
データ分析案件では、データサイエンティストは分析対象に関する専門家と分析目標を設定し、専門家が測定したデータを使って分析することが多いのではないでしょうか。電子工作では、なにを分析すべきか、そのためにどんなデータをどうやって測定するか、全て自分で考えないといけません。
今回は、モーターの省エネ制御のために、電流量や電力量を測りましたが、実は結構面倒くさいです(笑)自分でやってみると、一つ一つのデータを取得するには知識や技術が必要なことがわかります。それらを少しでも紐解いていくと、データが生み出される背景にどんな現象が起きてるかを想像することもできるようになると思います。データ分析する上で、こういった仮説を持つことも重要なので、電子工作を通じてなにか測定することにチャレンジしてみてはいかがでしょうか。
商標
「プラレール」は株式会社タカラトミーの登録商標です。