かみやんの技術者ブログ

主にプログラムの話です

ロータリエンコーダを作る!

さてモータドライバ回路によって、モータが回るようになったら次は、ロボットの位置制御ですね。
モータドライバだけだと、

「今から、左右両方のモータを80%のパワーで1秒間、正転!」とか
「今から、左のモータは止めて、右のモータを80%のパワーで1秒間、正転!」

というような命令になってしまいます。位置制御になると

「ここから、前に時速4kmで3m直進せよ!」
「ここで左へ、R1mで90度曲がれ!」

というような命令にできます。
できることならm単位ではなくcm単位とかmm単位だとよい。

という訳で移動量を知る必要があるわけです。移動量を知る方法は下記のようなタイプがあるかな。

  1. 反射型光センサ:車輪の回転数、回転角度を白黒等で色分けされたパターンを光センサで読み取る
  2. 透過型光センサ:車輪の回転数、回転角度をスリットのある円盤の前後から発光ダイオードと受光器で挟み読み取る
  3. 接触型センサ:車輪の回転数、回転角度を白黒に塗り分けされたパターンの代わりに、導電体と絶縁体のパターンを作り接触した導線でON・OFFを読み取る
  4. 回転計:車輪とは別に移動量を計測するためだけの回転盤を床に接するようにして光センサ等で読み取る
  5. CCD型:光学式マウスのようにロボットの下にカメラをつけて床の模様のズレを計測する
  6. ロボットビジョン:前方や天井、全方位カメラなどで空間の景色から自分の位置を求める

という感じでしょうか。

1)が一番簡単。ただし外の光を取り込んでしまうとカウントを誤るかも。
2)はちょっとスペースがいるし、センサもちょっと1)より高いかも。
3)は、こんな方法なら安価か?と思って考えたが、光センサが十分安かったので実装する意味がなさそう。
4)は、1〜3)は駆動輪の下が滑りやすく、または、急発進などで、スリップしていた場合、駆動輪でカウントすると実際には進んでいないのにカウントしてしまう。スリップという外乱に弱い。という問題を克服できる。
5)もスリップに強くなるが、カメラ分だけ高くなり、ソフトウェアも重くなる
6)は、ソフトウェアが重いうえに精度がでなさそう。

という感じでしょうか。
1)が安そう。ということでフォトセンサを探したところ、
SG105が安かった。
SG105は、千石で110円。安い。
データシートをみると、最適距離が0.8mmとある。近すぎて使いこなせるかな〜と心配ではあるが逆に0.8mmと近いということは、細かい白黒パターンまで読み取れるともいえるわけで、細かいパターンまで読めるということは、何センチ前に進んだかという精度が上がるということで良しとしよう。

回路図は上記データシートの一番下のページにあるのでそれでOK。
その回路の発光ダイオード側の抵抗値の設計:

 定格が50mAなので、その半分で使うとすると25mA。25mAで5Vなので
 R=5V/0.025A=200Ω
 ということで200Ωとした。定格いっぱいに使いたい人は100Ωでどうぞ。

データシートのRLという抵抗だがこちらは感度になるので、半固定抵抗10kΩとした。
半固定抵抗で0Ωに誤ってしてしまうと短絡になるので、直列に470Ωをつけた。
ちなみにデータシートのグラフにあるように、このRLという抵抗は500Ωを超えると、抵抗が大きいほどレスポンスが悪くなるようだ(ただ感度はよくなる)。

抵抗2つを決めたので、早速ブレッドボード上に回路を組んでみた。
紙にマジックで黒い線を書いて電圧が変わるか調べた。
わりといい感じ。黒0.2、白2.4Vぐらい。ただしセンサと紙の距離が離れると白のときの電圧がすごく小さくなるのでできるだけ近くで距離を保つことが重要そうだ。

次に、ロータリエンコーダのパターンだが、最初CADで書こうと思ったが、Alibre Design Xpressでは、三次元上では、回転フィーチャでたくさん配置できるのだが、二次元図面ではなぜか回転配置機能がない。という訳でJavaでパターンを書いてPNGに出力するコードを速攻で書いた。

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.File;

import javax.imageio.ImageIO;

/* ファイル概要:
 * Created on 2008/04/18 by kamiya
 */

/**
 * クラス概要:ロボットの車輪用ロータリエンコーダ
 * PNG画像として図を出力する
 * @author Eiji Kamiya ibis inc. 2008/4/18
 */
public class RotaryEncoder {
    static final int D1=1200;//外円直径(単位:ピクセル)
    static final int D2=1000;//内円直径(単位:ピクセル)
    static final int DIV=288;//分割数(白と黒の合計数)
    static final int DIV2=20;//ポリゴン化のときの円弧の分割数
    static final int R=50;//中心の十字の半径(単位:ピクセル)
    /**
     * @param args
     */
    public static void main(String[] args) {
        BufferedImage image;
        try {
            image = new BufferedImage(D1,D1,BufferedImage.TYPE_INT_BGR);
            Graphics2D g=image.createGraphics();
            g.setColor(Color.WHITE);
            g.fillRect(0, 0, D1, D1);
            g.setColor(Color.BLACK);
            g.drawArc(0, 0, D1-1, D1-1, 0, 360);//外円
            g.drawArc((D1-D2)/2, (D1-D2)/2, D2-1, D2-1, 0, 360);//内円
            g.drawLine(D1/2, D1/2-R, D1/2, D1/2+R);//中心十字縦
            g.drawLine(D1/2-R,D1/2,D1/2+R,D1/2);//中心十字横
            int[] px=new int[DIV2*2];
            int[] py=new int[DIV2*2];
            double unit=(360.0/(double)DIV);
            for(int j=0;j<DIV;j+=2){
                double ang1=360.0*(double)j/(double)DIV;
                int k=0;
                for(int i=0;i<DIV2;i++){//外円弧
                    double ang2=ang1+unit*(double)i/(double)DIV2;
                    px[k]=(int)(D1/2+D1/2*Math.cos(2.0*Math.PI*ang2/360.0));
                    py[k]=(int)(D1/2+D1/2*Math.sin(2.0*Math.PI*ang2/360.0));
                    k++;
                }
                for(int i=DIV2-1;i>=0;i--){//内円弧
                    double ang2=ang1+unit*(double)i/(double)DIV2;
                    px[k]=(int)(D1/2+D2/2*Math.cos(2.0*Math.PI*ang2/360.0));
                    py[k]=(int)(D1/2+D2/2*Math.sin(2.0*Math.PI*ang2/360.0));
                    k++;
                }
                g.fillPolygon(px, py, DIV2*2);
            }
            ImageIO.write(image, "png", new File("RotaryEncoder.png"));
            System.out.println("Success");
        } catch(Exception e){
            e.printStackTrace();
        }
    }

}

コマンドライン引数をパースする処理はないので適当に定数を変更して実行でいろいろなサイズで出力してください。出力先もカレントディレクトリのみです。
で出力された画像をPowerPointに貼って、PowerPointの画像サイズ機能で10cmに設定。

OA用紙に印刷。印刷したパターンをハサミで切って、厚紙に貼る。
厚紙を切って、中心をケガキ針で穴をあける。
消しゴムの8mm x 8mm x 5mmぐらいに切って真中をケガキ針で穴をあけてモータに円盤を止めるストッパとする。

100円で買ったマブチ130に円盤と消しゴムストッパを圧入する。

パターンは、白と黒の合計数で、36分割、72分割、144分割、288分割を作りました。
どれぐらいの分割数までなら光センサ(フォトインタラプタ SG105)が、反応できるのか、どれぐらいの分割数までならマイコンが追い付くのか、これで実験しようと思います。

ちなみにフォトインタラプタの出力をオシロ Stingrayで見ようと思ったらうまくいかず。。なんか正弦波みたいなのがでる。おかしいな〜と思って電池にプローブを当てても正弦波(+−2.5Vぐらいの)。手でプローブを触っても正弦波。なんかオシロの使い方わかっていないのか。昨日のPWM信号を測ったらちゃんとでる。うーむ。