Arduinoでステッピングモーターを制御するライブラリ「Suteppa」

このライブラリ開発記録のその1はこちらです。

 ライブラリの改良等をしていたため少し書くのが遅くなりましたが、公開できるレベルにはなったので公開したいと思います。

github.com

Suteppaとは

 Suteppaは、Arduinoでのステッピングモーターの制御を助けるためのライブラリです。注意して頂きたいのは、このライブラリ自体にステッピングモーターを回転させる機能は無いという事です。少しややこしいですが、実際にモーターを回すコードを貼ります。

「1回転→1秒→逆に1回転→1秒」を繰り返すソース

gist.github.com

解説

void step(int d)
{
    static const int hs[8] = {省略};
    static int i;
    i += d;
    if(i > 7) i = 0;
    if(i < 0) i = 7;
    byte b = hs[i];
    digitalWrite(IN1, bitRead(b, 0));
    digitalWrite(IN2, bitRead(b, 1));
    digitalWrite(IN3, bitRead(b, 2));
    digitalWrite(IN4, bitRead(b, 3));
}

 stepという関数がありますが、これは1-2相励磁でステップさせるために用意した関数です。この関数をSuteppaのinitに渡しています。Suteppaは渡されたstepを使って回転させます。このような設計にした理由は、1-2相励磁以外のいろいろな方法に対応する為です。ほかの励磁方法や、またマイクロステップをさせたい場合もあると思います。そういった場合に備えてステップを刻む関数は自分で作って渡してもらう事にしました。
 引数のint dですが、これはステップ方向を示しています。Suteppaがstepを呼ぶとき、dには-11が入ります。dによって回転方向が変わるような設計にしてください。

s.init(4096, step);

 ここでSuteppaをinitしています。一回転のステップ数と、上で説明したstep用関数を渡しています。1回転のステップ数は用途によっては必要ですが、分からない場合や、DVDのモーターのような回転せずスライドするタイプの場合等は0でも良いです。これが必要になるケースは後ほど説明しますが、基本的に0で良いと考えてください。

//100ステップを加減速に使い, 開始速度は2000us
s.beginSmooth(100, 2000);
//通常速度は700us
s.setSpeed(700);

 beginSmoothメソッドは加減速をスムーズに行うための設定用メソッドです。ここでは、加減速に最大100ステップ(合わせて200ステップ)を使い、開始速度は2000usという設定にしています。
 setSpeedで通常の速度を設定します。つまり今回の場合、100ステップかけて2000usから700usまで加速し、100ステップかけて700usから2000usまで減速します。

※usが小さい方が早く、大きい方が遅く回るので注意です。

//4096ステップ回転
s.rotate(Suteppa::RELATIVE, 4096);
delay(1000);

//0ステップへ戻る
s.rotate(Suteppa::ABSOLUTE, 0);
delay(1000);

 rotateメソッドは第1引数で回転モードを、第2引数にはステップを指定ます。ステップは負にすれば逆回転します。
 Suteppa::RELATIVEは相対ステップで、Suteppa::ABSOLUTEは絶対ステップで回転させます。よって今回の場合、現在の位置から、4096ステップ回し、その後0ステップに戻る。という事になります。  第3引数を含めた詳しいrotateの解説は下で行います。以上でデモのソースコードの解説を終わります。

rotateメソッドとtick

 rotate(int mode, long step, bool sync)

Suteppa::RELATIVE

 相対ステップです。現在の位置から相対的にステップします。

Suteppa::ABSOLUTE

 絶対ステップです。デフォルト位置からのステップへ移動します。

Suteppa::ABSOLUTE_SKIP

 同じく絶対ステップですが、最短で移動します。たとえば、一回転360ステップのモーターの場合、270ステップ地点から、0ステップへ移動するためには、+90ステップするか、-270ステップするかの2通りがあります。このモードでは、+90して最短で移動します。注意すべきはここでステップは0になります。360にはなりません。

tick

 rotateメソッドのsyncは省略できます。デフォルトではtrueです。ただ、このままだとモーターが1台しか制御できないという問題点があります。以下がその例です。s1s2の2台のモーターを制御します。

Suteppa s1;
Suteppa s2;
//初期化省略
s1.rotate(Suteppa:ABSOLUTE, 100);
s2.rotate(Suteppa:ABSOLUTE, 100);
Serial.println("ok");

 しかし、これだとs1の回転が完了した後にs2が回転します。同時には回転しません。rotateメソッドが処理を止めてしまうのです。
 この問題を解決すべく、tickで回転を行うという方法を用意しました。

s1.rotate(Suteppa:ABSOLUTE, 100, false);
s2.rotate(Suteppa:ABSOLUTE, 100, false);
while(s1.tick() || s2.tick()){}
Serial.println("ok");

 こうするとs1s2が同時に回ります。tickはモーターを回すためのメソッドで、回転中はtrueを返し、完了すればfalseを返します。回転完了後にtickを呼び続けても平気です。それ以上回転することはありません。
 ソースを見ればわかりますが、rotateメソッドはsyncがtrueだった場合最後にwhile(tick()){}をしています。tickを呼ぶ周期は可能な限り短い方が良いです。最低でも50usごとには呼んでほしいです。それ以上だとスムーズな加減速が出来なくなります。

スピードとスムーズモード

 最初のサンプルコードでも使っていますが、スムーズモードがこのライブラリの特徴です。加減速を調整することで脱調を防ぐことが出来ます。またステッピングモーターを確実に最高速度で回すことが出来ます。

setSpeed(通常速度us);
beginSmooth(ステップ数, 開始速度us);

 開始速度usから通常速度usまでステップ数分のステップでスムージングします。

スムーズモードの有効化と無効化

s.setSpeed(900);
s.beginSmooth(100, 2000);
s.rotate(Suteppa::RELATIVE, 1024);
s.endSmooth();
s.rotate(Suteppa::RELATIVE, -1024);

 スムーズモードはbeginSmoothメソッドで有効化します。endSmoothメソッドを呼ぶことでスムーズモードを解除できます。上記の場合、1024ステップをスムーズモード(2000usから900usまでを100ステップでスムーズに変化)で移動し、スムーズを無効化したのち1024ステップ戻ります。

脱調する場合

 脱調は、急な加速や減速が原因なので、脱調する場合はステップ数を増やすか、開始速度usを下げます(数値を上げます)。ただし開始速度us通常速度usの差が開きすぎるのも良くありません。その分ステップ数を増やせばよいですが、そこらへんは調整してください。基本的にステッピングモーターが始動できる速度を開始速度usに当てはめるのが良いでしょう。

デフォルトスムーズ

 頻繁にスムーズモードを有効化したり無効化する場合、いちいち値をセットするのは面倒です。そこでsetDefaultSmoothを用意しました。

s.setDefaultSmooth(ステップ数, 開始速度us);
//色々省略
s.beginSmooth();
s.rotate(省略);
s.endSmooth();
s.rotate(省略);
s.beginSmooth();
s.rotate(省略);
s.endSmooth();

 一度defautSmoothをセットすればbeginSmoothするだけでスムーズモードを有効化できます。

基準点のセット

 setHomeメソッドを用意しました。このメソッドを呼んだ位置が0ステップとしてセットされます。キャリブレーションを行う時に使えます。例えば以下のように。

s.rotate(Suteppa::RELATIVE, -9999, false);
while(s.tick()){
    if(digitalRead(SW) == 1){
        break;
    }
}
s.setHome();
s.rotate(Suteppa::ABSOLUTE, 1024);

 先頭につけたスイッチ(SW)を押すまでマイナスへ回り続けます。tickごとにdigitalRead(SW)で先頭スイッチが押されたかを確認します。押されたらwhileを抜けて回転を中止。その位置をhomeにセット。その後1024絶対ステップで回す。という流れです。キャリブレーションも簡単に実装できますね。キャリブレーションのデモは別記事で詳しく書く予定です。

おまけ

static const int RELATIVE = 0;
static const int ABSOLUTE = 1;
static const int ABSOLUTE_SKIP = 2;

こうなっているので、長くて面倒な場合は数字でも良いです。

ステッピングモーター制御ライブラリを作りたい(その5)「脱調防止できるのか実験!」

その1はこちらです。

 前回のDVDのステッピングモーターで脱調防止実験を行います。実験は現在開発中のライブラリを用いて行いますが、すいません!前回、ライブラリの説明も次回行うと言いましたが、ボリュームがたっぷりになってしまうので、ライブラリの説明自体は今回は省略させてもらいます。が一応貼っておきます。
github.com

回路

前回同様です。

ソース

 実験用ソースは一応貼りますが、ライブラリの解説がないと分かりにくいので、今回はソースの説明はしません。次回ライブラリの解説をするときにこのソースの解説も行う予定です。長いので最後に貼ります。

いざ実験

まず、このお手本をご覧ください。

2000マイクロ秒 通常モード(お手本)

YouTube


 このお手本は、ライブラリによる脱調防止機能をオフにして撮影しています。ステップとステップの時間は2000マイクロ秒です。この時点では脱調防止機能オフでも正確な位置決めが出来ています。これと同じ動きを少し早くしてやってもらいます。

1300マイクロ秒 通常モード

YouTube


 こちらは1300マイクロ秒にしたバージョンですが、途中で脱調していることが分かると思います。お手本と同じ動きをしているつもりなのですが、脱調しているためぐちゃぐちゃになっています。

ここからが本番です

 通常モードでは1300マイクロ秒で脱調してしまいました。ここからは脱調を防止したライブラリを使用したスムーズモードでの実験となります。

1300マイクロ秒 スムーズモード

YouTube


 どうでしょうか。通常モードでは脱調してしまった1300マイクロ秒を難なく突破です。スムーズモードは動画ではわかりにくいですが加減速をしています。最高速度はしっかりと1300マイクロ秒に達しています。

スムーズモードについての詳しい説明は、その2で行っています。S字加速を使用した方式ですので是非ご覧ください。

950マイクロ秒 スムーズモード

YouTube


 どこまでいけるかと思い下げていくと、950が限界のようです。ですが、スムーズモードだとここまで高速に動けることが分かります。

おまけ

 スムーズモードが動画だと分かりにくいので、加減速をより強調した設定にして動かしてみました。この動画だと加減速がはっきりわかると思います。

YouTube


次回

ライブラリの説明とソースコードの説明を行います。

ソース

gist.github.com

その6(最後)はこちらです。

ステッピングモーター制御ライブラリを作りたい(その4)「28BYJ-48とDVDのモーターをArduinoで回す。」

その1はこちらです。

 ライブラリがどうにか形になりました。ライブラリのテスト用に注文した28BYJ-48と、DVDのレーザー移動用ステッピングモーターを使います。ということで、ライブラリの説明より前にステッピングモーターをコントロールする回路の説明から入ります。今回は複雑にしないためにも制作中のライブラリは使わないで行います。
 ここでは細かい説明はしないので114.ステッピングモータを動かすこちらのページが参考になると思うので、ぜひ読んでみてください。

28BYJ-48を回す

 28BYJ-48のモジュールのIN1 IN2 IN3 IN4をそれぞれ9 10 11 12に割り当てました。このモーターは5端子のユニポーラ型なので、モジュールと言ってもただのトランジスタの集合です。ユニポーラは回路が単純なので扱いやすいですね!

ソース

gist.github.com  このソースですが、私はArduinoIDEではなくplatformIOにてC言語で開発しているため、先頭にincludeがありますが不要な場合は除いてください。
 今回は1-2相励磁で回しているので、1-2相励磁で刻むように上に配列を定義しています。回転はstep関数で行っています。stepの引数には方向を示すint dを与えます。1-1です。
 stepごとに挟んでいるdelayMicrosecondsですが、ここでは900にしています。このモーターは900位が限界っぽいです。

動画

www.youtube.com

DVD用ステッピングモーターを回す

 DVD用ステッピングモーターはバイポーラ型なので、28BYJ-48に比べて回路が面倒です。今回ステッピングモーター用のICを持っていなかったので困りました。ただ、Hブリッジが二つあれば良いので探していたら家にありました。TA7291Pです。正転反転できるタイプのモータードライバでPWMで速度も変えられます。が、今回はこれをただのHブリッジとして使います。 f:id:takumus:20160913185640j:plain f:id:takumus:20160913182917p:plain  こんな感じの回路になりました。多分TA7291Pを二つも使ってステッピングモーターを回すという、もったいないことをしたのは僕くらいだと思います!
 回路を裏から見た図です。少し灰色の線は表側を通しているという意味です。また、TA7291Pの向きですが、左上が少し欠けているのでそれが目印です。
 そこそこ適当な回路ですが動作確認は取れました。図の左側が入力、右側が出力です。左側の+1 +2 -ですが、モーターを外部電力で回す場合は+1には外部を入れ、+2にはArduinoを入れます。GNDはどちらも-へつなぎます。入力のA1 A2 B1 B2ですが、ArduinoへはA1 B1 A2 B2の順番で9 10 11 12へ繋ぎました。
 今回のような小さなモーター(大きさで判断するのは良くない)は大したことなさそうだったので、+1 +2を一緒にして両方ともArduinoに繋ぎましたが問題なく動作しました。

ソース

 28BYJ-48と同様ですがこっちのモーターは低速回転なので、delayMicromecondsが900だと完全に脱調しました。最低でも2000が安定するようです。

動画

www.youtube.com

次回

次回は制作中のライブラリを実際に使ってみます。

その5はこちらです。

ステッピングモーター制御ライブラリを作りたい(その3)「最大速度」

その1はこちらです。

 前回の記事で、S字と台形を使って滑らかな加減速を行う手法を説明しました。S字も台形も、加速に30ステップ、減速に30ステップを使っています。つまり、今の仕様のままだと最低でも合計60ステップ最低でも必要になるのです。
 例えば、前回と同じ仕様のモーター(1ステップ1.8度)だと、90度動かすのに50ステップですが、加速減速合わせて60ステップ使うのでおかしなことになります。では実際に見てみましょう。

台形型

f:id:takumus:20160911200033g:plain  90度は50ステップですので、最大速度に達する(30ステップ)事が出来ても、減速分のステップ(30ステップ)が足りないので、おかしなグラフになっています。今の仕様のままでは最低でも60ステップ分の移動がないと回転できない事になります。

S字型

f:id:takumus:20160911200034g:plain  S字も同じです。台形をベースに作っているので、同じく減速分が足りていません。

ステップが足りない場合はどうすれば良いか!

 足りないステップを無理やり足らせる方法もあります。傾斜を急にすれば良いのです。しかし、傾斜が急になればなるほど脱調の可能性は上がってしまうので、だめです。
 もう一つの方法は最大速度を下げる方法です。そもそも無理して最大速度に達しなくて良いのです。傾斜はそのままで、加減速分のステップに足りる分の、可能な限りの最大速度を計算します。今回はこの方法で行きたいと思います。

最大速度を下げる

最大速度を下げるように改良したバージョンを作りました。

台形型

f:id:takumus:20160911200035g:plain

S字型

f:id:takumus:20160911200036g:plain  アニメーションの通り、最初180度で回したときは最大速度に達して回りますが、90度で回したときは、最大速度を下げて回ります。こうすることで、加減速の傾斜を変えることなく、どのような角度に対しても緩やかに移行できます。
 どれだけ下げるべきかは、一次関数による簡単な式で導く事が出来ます。本当はもう少し細かい計算をしているのですが、後日ソースコードを公開するつもりです。  まだステッピングモーター現物を扱ったことがないのに色々と書いてしまいましたが、明日届くはずなので試したいです。これらのアニメーションはflashで作っています。アニメーションというか疑似ステッピングモーターを実際に制御するプログラムは書いているので、これをArduino(C++)へ移植してちゃんと動いてくれれば嬉しいです。

その4はこちらです。

ステッピングモーター制御ライブラリを作りたい(その2)「脱調を防ぐ方法」

その1はこちらです。

 前日、ステッピングモーターと、脱調について簡単な説明をしました。前日の説明と被りますが、

主な脱調の原因は

  1. 完全停止状態からの急発進。
  2. 高速回転中の急停止。
  3. 電気信号を送る間隔を短くしすぎる。
  4. とても重い物を動かす。

と言われています。
 cについては、電気信号を送る(以降ステップと呼びます)間隔を短くしすぎなければ良いですし、dは重い物を動かすことが出来るモーターを選べばよい話です。a bは、簡単に解決できる問題ではないので、今回から考えていきます。

急発進・急停止をしない工夫

 急発進・急停止はしないようにする方法はいくつか方法があります。その説明のために、前日のと同じく1ステップ1.8度のモーターを例に挙げます。

180度を急発進・急停止

f:id:takumus:20160911135302g:plain  これはflashでシミュレーションしたステッピングモーターなので脱調は起こりませんが、急加速、急停止をしていることが分かります。実際にこれを行うと、脱調してしまうかもしれません。

180度を徐々に発進・徐々に停止(台形型)

f:id:takumus:20160911140028g:plain  次は、徐々に加速し、徐々に減速するようにしました。これはステッピングモーター制御で良く行われる方法のひとつです。今回は、30ステップ分を加速と減速のために使っています。合計60ステップです。こうすることで、急な加速と停止は少なくなりました。しかし、台形の上辺の頂点に当たる部分で加速の大きな変化があります。これもまだ脱調の心配があるのです。それも無くしたいです。

180度を徐々に発進・徐々に停止(S字型)

f:id:takumus:20160911141255g:plain  台形での加速の変化をさらに少なくした物がこれです。今回はcos波を使って再現したのでカーブがやや急ですが、その辺は検証しながら調整したいと思います。(未だ実物を持っていないので笑)台形と同じく、30ステップ分を加速と減速に使っています。台形に比べて角が取れています。つまり急な速度変化が無くなったので脱調の可能性はグンと下がりました。

今回はここまで

 です。
 脱調が起きる原因と、それを防ぐいくつかの方法を紹介しました。このままではまだ問題があるので、次回その問題の解決を考えていきます。

その3はこちらです。

ステッピングモーター制御ライブラリを作りたい(その1)「ステッピングモーターとは」

 今までステッピングモーターを使った事がなかったので、使ってみようと思うのですが、せっかくなのでArduino用のライブラリを書きたくなりました。

サーボモーターとの違い

サーボモーターとは

ステッピングサーボはここでは扱いません。

 サーボは指定した角度へ回転することが出来ます。
 例えば、現在の軸の角度が0度でも、180度でも、軸を90度の位置へ回転しろ!と命令すれば、90度の位置まで回転します。
 本体の中には、可変抵抗と一般的なDCモーターが入っています。自分が向いている角度を可変抵抗で測りながらDCモーターを制御するため、絶対的な角度の制御が可能になっています。
 角度制御が可能なサーボの多くは、連続回転せず、180度しか回らなかったりするものが多いです。 f:id:takumus:20160910235120p:plain  可変抵抗が入っているという事はつまり。サーボの電源を切っても、自分の向いている角度を記憶しているので、電源投入時に元の角度へ戻ることが出来ます。
 また、もしも重い力がかかりすぎて、モーターが止まっても、重い力が外れた瞬間、直ちにその角度に到達します。

まとめ

  • 可変抵抗があるので角度を覚えることが出来る。
  • 回転中にモーターが止まっても、動ける状態になれば絶対に指定した角度にたどり着く。
  • 絶対的な角度指定が出来る。
  • 角度指定のできる一般的なサーボは180度しか回らない。

ステッピングモーターとは

 ステッピングモーターは電気信号を送った回数分回転します。1回の電気信号(以降1ステップと表現します)で、何度回転するかどうかは、モーターによって異なります。例えば、1ステップで1.8度回転するタイプのモーターを90度回転させるには、50ステップ送れば良いのです。ステップを送る間隔が短ければ早く。広ければ遅く回ります。 f:id:takumus:20160911130028p:plain
 また、連続回転が可能です。電気信号を送り続ければ、無限回転出来ます。
 しかしサーボと違い自分の絶対角度を知ることはできません。つまり、相対的な角度の制御しかできないのです。
 プリンターなどで用いられている物は、一番端にスイッチがあり、電源投入時にスイッチの位置までモーターを回し、触れた位置からの相対角度で制御しています。(多分ロータリーエンコーダーも合わせて使っています)
 ここでは詳しい説明を割愛しますが、雰囲気はこんな感じです。

気を付けること!

 送った回数分回るといっても、気を付けなければならない点がいくつかあります。例えば1回1.8度のモーターに、50回信号を送ったとしても、必ず90度になるとは限らないのです。
例えば、

  • ステップを高速で送ると、反応できず回転が追いつかない。
  • 重い力がかかりすぎて物理的に回転できない。
  • 高速で回っている時、急停止命令しても止まれない。

という状況で起こります。
 これらの現象を脱調と言います。脱調とは、送ったステップ分の回転を行えなかったことを言います。
 サーボの場合、うまく回転できなくても、現在の回転角度を記憶しているので、指定角度までたどり着こうと努力します。しかし、ステッピングモーターはサーボと違い自分の回転角度を認識できません。そのため、送られた信号分の回転ができたかどうか分からないのです。
 ステッピングモーターを制御するには、脱調を防止するために工夫が必要で、ロータリーエンコーダーなどと合わせて使い脱調を検知する必要があります。

まとめ

  • 送ったステップ分回転する。
  • 連続回転が可能。
  • 回転角度が取得できないため、本当にその角度にたどり着いたかが分からない。
  • 相対角度でしか指定できないため、絶対的な制御が必要な場合、スイッチなどを用いてキャリブレーションなどをする必要あり。もしくはロータリーエンコーダを合わせて使うなど。

 どうでしょう。少々難しいと思いますが、なんとなく違いが分かったでしょうか。そんなステッピングモーターを脱調しないように制御するライブラリを、明日から書いていきたいと思います。

その2はこちらです。

Raspberry PiとArduinoでアクアリウム(その4)「実際にスマホでコントロール編」

その1はこちらです。

 前回まではちょっと詳しく解説しすぎたり、内容が定まっていなかったので今回から調整します。書き直しても良いのですが、教訓として残しておきます。応援よろしくお願いします。
 実際にスマホでコントロールしてみました動画です。Arduinoは基板が上に載っていて、分かりにくいですがRaspberry Piの隣に写ってます。スマホからは専用のページでコントロールしています。 www.youtube.com

仕組み

f:id:takumus:20160908235429p:plain  動画内のiPod TouchとAndroidはどちらもブラウザで特定のページを開いていますが、このページはPiではなく、さくらのVPSで用意したウェブサーバーで立てています。ページを開くとwebsocket.io(以降ws)でさくらVPS(以降親サーバー)と繋がります。ライト点灯命令はすべてwsを使って送信されます。(動画内でもわかる通り、2台の端末でライトの点灯状態が同期しています。)そして、親サーバーはブラウザから受信した命令をPiへ流すのです。Piとは親サーバーが立てたSocketサーバーで繋がっています。PiとArduinoはSerial接続しています。
 なぜこうなったかと言うと、

  1. ポート開放をしたくなかったのと、
  2. 家は固定IPではないのと、
  3. 将来的に大人数がこの監視システムにアクセス出来たらよいと思っているので、Piはサーバーにせず極力休んでいてほしいのと、
  4. 命令ログやその他のデータの保管や処理はPiではなく、スペックの十分な親サーバーで行いたい、

など。その他理由はいくつかありますが、とにかく直接Piに外から繋ぎたくはなかったのです。
 自分にある知識を絞り出して考えた設計なので変な所ありましたら意見いただけると嬉しいです。

設定もリアルタイム

www.youtube.com  今回全ての情報はwsでリアルタイム共有されることになっているので、一応こういう仕様になっています。IoT系のもので多いのが、IからTまで非常にラグがあることです。自分的にそれは嫌だったので今回贅沢にもwsリアルタイム重視で設計しています。

ソース達

 このブログはすでに大体完成してから書いているので、ソースにはまだ紹介していない機能などが載っていて分かりにくいと思います。
 arduinoディレクトリにはarduinoのソース、piにはpiのソース。clientはスマホで表示するページのソースです。serverは、clientとpiを繋ぐ親サーバー部分です。 github.com