ラベル Arduino の投稿を表示しています。 すべての投稿を表示
ラベル Arduino の投稿を表示しています。 すべての投稿を表示

2015年12月4日金曜日

Arduino とレゴで倒立振子(3)電源の性能

かなり前に
という記事を書きましたが,その後これに関連して学会発表をしたり特集号の記事を書いたりしてブログの更新を忘れていました.

特集号の記事は
にあります.現在は計測自動制御学会の会員でないと全文が読めませんが,一年後(2016/3)から無料で読めるようになるはずです.

また,記事の補足として実験機が動作している動画を公開しておきます.


この実験は安定化されているモバイルバッテリーを電源にしていることのメリットを示すためのもので,ギリギリ安定化できている程度の性能が悪い(=よく揺れる)制御器でも14時間程度連続して倒立しており,電源の消費によって実験の再現性が損なわれていないことを確認するものです.

制御器を完全にチューニングすれば持続時間は日単位になると思いますが,あまり意味がないわりに面倒な実験なので試していません.長時間動作させる実験では,地面の起伏とタイヤの滑りで倒立振子が予想できない方向にじわじわ動いていくために,意外と広い実験場所が必要になることと,実験を記録するためのカメラの持続時間が問題になります.もし同様の実験をされる方がいらっしゃいましたら参考にしてください.

3年間授業でこの倒立振子を使っているうちに,いくつか新しい技術が利用可能になり,改善したい点もでてきたので,そのうちまた実験機と記事をアップデートすると思います.

2012年4月30日月曜日

Arduino とレゴで倒立振子(2)モータドライバの設計

前後の記事は
です.

少しずつ書いている実験用倒立振子製作メモですが,今回はモータドライバの設計について説明します.前回述べたようにモータの電流制御を行う必要があることと電源がUSBであることを踏まえると,モータドライバには
  • モータ電圧5V,ロジック電圧5Vで動作
  • 電流容量1A以上
  • 31kHz程度で電圧をON/OFFすることが可能
といった性能が求められます.電圧・電流に関する条件はUSBバスパワーの仕様から直接生じるものですが,最後の条件は電流制御に必要とされるPWMキャリア周波数によるものです.

2012年4月22日日曜日

Arduino とレゴで倒立振子(1) 設計意図

続きの記事は
  • Arduino とレゴで倒立振子(2)モータドライバの設計
  • Arduino とレゴで倒立振子(3)電源の性能
  • です

    4コマ(6時間)で制御工学を履修していない学生さんに倒立振子を制御してもらうという大変責任重大な学生実験の担当を拝命しました.

    引き継いだ実験装置もあったのですが,20年前のPC-9801で動いており,たまにエラーが出る状況だったので,学生実験に最適化したシステムに入れ替えようと思い立ち,マイコンは Arduino,機械部分はレゴというちょっと変わった構成で倒立振子を製作しました.

    実験装置の外観


    予備機製作の際のマニュアルも必要ですし,製作に必要な情報を徐々にブログにまとめていこうかと思います.第一回はこの実験装置の設計で特徴的なところをまとめ,大雑把な設計意図について説明します.

    2009年5月26日火曜日

    Arduinoで簡易オシロスコープ

    2013/4/22 ライセンスを修正BSDライセンスとしました
    Arduinoで簡単に使える簡易オシロスコープもどきを作ってみました.ちょっとした実験にはなかなか便利なので公開しておきます.

    スクリーンショット

    全体図scr1
    マウスで周波数などの読み取りも可能
    scr2

    操作法

    • [↑][↓] 時間軸の変更
    • [space] データ取得の停止/再開
    • [s] データをcsv形式で保存
    • [マウス操作] 値の読み取り・ドラッグで周波数読み取り

    インストール

    • 下記のソースの末尾・Arduino用のプログラムの部分をArduinoに書き込んでおく.
    • 下記のソースをProcessingにコピーし,シリアルポートの設定とフォントの生成/設定を行う.
    • 実行してみる

    ソース


    // ArduinoScope v1.0.0
    // 
    // Note:
    //  1. please confirm serial port setting
    //  2. prepare font
    //  3. Arduino code is attached at the end of this code
    
    // Copyright (c) 2009-2013 I. Maruta <ichiro.maruta@gmail.com>
    // All rights reserved.
    //
    // Redistribution and use in source and binary forms, with or without
    // modification, are permitted provided that the following conditions
    // are met:
    // 1. Redistributions of source code must retain the above copyright
    //    notice, this list of conditions and the following disclaimer.
    // 2. Neither the name of the author nor the names of its contributors
    //    may be used to endorse or promote products derived from this software
    //    without specific prior written permission.
    //
    // THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
    // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    // ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
    // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
    // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
    // OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
    // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
    // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
    // OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
    // SUCH DAMAGE.
    
     
    
    import processing.serial.*;
    
    Serial ArduinoPort;  // Create object from Serial class
    
    int NumOfScopes,NumOfInput=2;
    int data_span=10000;
    Strage dfs = new Strage();
    Scope[] sp;
    
    int fontsize=16;
    PFont myFont;
    
    void setup() 
    {
      // Serial Port 
      println(Serial.list());
      String portName = Serial.list()[0]; // TODO: automatic detection?
      ArduinoPort = new Serial(this, portName, 38400);
      ArduinoPort.bufferUntil(10);
    
      // Screen
      size(800, 600);
      NumOfScopes=2;
      sp = new Scope[NumOfScopes];
      sp[0]= new Scope(0,50,10,width-100,height/2-35,512,-512,1000);
      sp[1]= new Scope(1,50,height/2+15,width-100,height/2-35,1024,0,1000);
    
      myFont = loadFont("Dotum-16.vlw");
      textFont(myFont,fontsize);
    }
    
    class Scope{
      int input_id;    // corresponding input
      int posx,posy;   // screen position of the scope
      int sizex,sizey; // pixel size of the scope
      float yu,yl;     // range of y is [yl,yu]
      int tspan;       //
      int ngx,ngy; // number of grids
      float maxposx,maxposy,minposx,minposy,maxx,minx,maxy,miny;
    
      Scope(int did,int px,int py,int sx,int sy,float syu,float syl,int ts){
        input_id=did;
        posx=px; 
        posy=py;
        sizex=sx; 
        sizey=sy;
        yu=syu; 
        yl=syl;
        tspan=ts;
        ngx=10; 
        ngy=4;
      }
    
      void grid(){
        pushStyle();
        fill(255,196);
        stroke(0,0,150);
        for(float gx=sizex; gx>=0; gx-= (float)sizex/ngx){
          line(posx+gx,posy,posx+gx,posy+sizey);
          textAlign(CENTER,TOP);
          text((int)map(gx,sizex,0,0,-tspan),posx+gx,posy+sizey+2);    
        }
        for(float gy=sizey; gy>=0; gy-= (float)sizey/ngy){
          line(posx,posy+gy,posx+sizex,posy+gy);
          textAlign(RIGHT,CENTER);
          text((int)map(gy,0,sizey,yu,yl),posx,posy+gy);    
        }
        popStyle(); 
      }
    
      int curx,cury;
    
      // draw cursor
      void cur()
      {
        // return if mouse cursor is not in this scope
        if(constrain(mouseX,posx,posx+sizex)!=mouseX 
          || constrain(mouseY,posy,posy+sizey)!=mouseY) return;
    
        pushStyle();
    
        // draw cross cursor
        stroke(255,0,0,196);
        fill(255,0,0,196);
        line(mouseX,posy,mouseX,posy+sizey);
        line(posx,mouseY,posx+sizex,mouseY);
    
        // draw measure if mouse is dragged
        if(mousePressed){
          line(curx,posy,curx,posy+sizey);
          line(posx,cury,posx+sizex,cury);
          textAlign(RIGHT,BOTTOM);
          text((int)map(curx,posx,posx+sizex,-tspan,0)+"ms, "+(int)map(cury,posy,posy+sizey,yu,yl),curx,cury);
          textAlign(LEFT,TOP); 
          text("("+nfp((int)map(mouseX-curx,0,sizex,0,tspan),1)+"ms, "+nfp((int)map(mouseY-cury,0,sizey,0,-(yu-yl)),1)+")\n"+nf(1000/map(mouseX-curx,0,sizex,0,tspan),1,2)+"Hz\n"+nf(TWO_PI*1000/map(mouseX-curx,0,sizex,0,tspan),1,2)+"rad/sec",mouseX,mouseY+2);
        }
        else{
          curx=mouseX;
          cury=mouseY;
          textAlign(RIGHT,BOTTOM);
          text((int)map(curx,posx,posx+sizex,-tspan,0)+"ms, "+(int)map(cury,posy,posy+sizey,yu,yl),curx,cury);
        }
        popStyle();
      }
    
      // draw min&max tick
      void minmax(){
        pushStyle();
        fill(255,128);
        stroke(0,0,100);
        textAlign(RIGHT,CENTER);
        line(posx,maxposy,posx+sizex,maxposy);
        text((int)maxy,posx,maxposy); 
        line(posx,minposy,posx+sizex,minposy);
        text((int)miny,posx,minposy); 
        textAlign(LEFT,CENTER);
        textAlign(CENTER,TOP);
        text("max",maxposx,maxposy); 
        textAlign(CENTER,BOTTOM);
        text("min",minposx,minposy+20); 
        popStyle();
      }
    
      // draw scope
      void Plot(){
        float sx,sy,ex,ey;
        int nof=0;
        DataFrame df_last = dfs.get(0);
    
        maxy=-1e10; // -inf
        miny=1e10;  // +inf
    
        // draw background (for transparency)
        pushStyle();
        noStroke();
        fill(0,0,64,64);
        rect(posx,posy,sizex,sizey);
        popStyle();
    
        // draw data plot
        pushStyle();
        stroke(0,255,0);
        smooth();
        strokeWeight(1);
        for(int idx=0;(dfs.get(idx).t>max(df_last.t-tspan,0)) && -idx<data_span;idx--){
          DataFrame df_new=dfs.get(idx);
          DataFrame df_old=dfs.get(idx-1);
          sx=(float) map(df_new.t, df_last.t, df_last.t - tspan, posx+sizex,posx);
          ex=(float) map(df_old.t, df_last.t, df_last.t - tspan, posx+sizex,posx);
          sy=(float) map((float)df_new.v[input_id],(float) yu,(float) yl,(float) posy,(float) posy+sizey );
          ey=(float) map((float)df_old.v[input_id],(float) yu,(float) yl,(float) posy,(float) posy+sizey );
          if(ex<posx){
            ey+=(sy-ey)*(posx-ex)/(sx-ex);
            ex=posx;
          }
          line(sx,sy,ex,ey);
          maxy=max(maxy,df_new.v[input_id]);
          if(maxy==df_new.v[input_id]){
            maxposx=sx;
            maxposy=sy;
          }
          miny=min(miny,df_new.v[input_id]);
          if(miny==df_new.v[input_id]){
            minposx=sx;
            minposy=sy;
          }
          nof++;
        }
        popStyle();
    
        //    minmax();    
    
        // draw current value of input
        pushStyle();
        textAlign(LEFT,CENTER);
        stroke(0,0,64);
        fill(0,255,0,196);
        text(df_last.v[input_id],posx+sizex,map(df_last.v[input_id], yu, yl, posy, posy+sizey ));
        popStyle();   
    
        grid();
        cur();    
      }
    }
    
    void draw() 
    { 
      background(0);
    
      for(int i=0;i<NumOfScopes;i++){
        sp[i].Plot();
      }
    }
    
    // input data buffer class
    // (now using ring buffer)
    class Strage{
      int cur;
      DataFrame[] DataFrames;
    
      Strage(){
        cur=0;
        DataFrames=new DataFrame[data_span];
        for(int idx=0;idx<data_span;idx++){
          int ret_v[] = new int[NumOfInput];
          DataFrames[idx] = new DataFrame(0,ret_v);
        }
      }
    
      void push(DataFrame d){
        cur = ((cur+1) %data_span);
        DataFrames[cur]=d;
      }
    
      DataFrame get(int idx)
      {
        int num=(cur+idx);
        for(; num<0; num+= data_span);
        return((DataFrame) DataFrames[num]);
      }
    
      void save()
      {
        String savePath = selectOutput();  // Opens file chooser
        if (savePath == null) {
          // If a file was not selected
          println("No output file was selected...");
        }else{
          PrintWriter output;
          output = createWriter(savePath); 
          DataFrame df_last = this.get(0);
          for(int idx=0;-idx<data_span;idx--){
            if(this.get(idx).t==0) break;
            output.print(this.get(idx).t-df_last.t);
            for(int k=0;k<NumOfInput;k++){
              output.print(","+this.get(idx).v[k]);
            } 
            output.println("");
          }
          output.flush();
          output.close();
        }
      }
    }
    
    class DataFrame{
      int t;
      int[] v;
      DataFrame(int st, int[] sv){
        t=st;
        v=sv.clone();
      }
    }
    
    boolean isactive=true;
    
    // buffering data from serial port
    void serialEvent(Serial myPort)
    {
      int[] vals=new int[NumOfInput];
      int timestamp;
      int[] splitdata;
      if( myPort.available() > 0) { 
        String datline=myPort.readString();
        splitdata=parseInt(datline.split(","));
        if((splitdata.length==NumOfInput+2)){
          timestamp=splitdata[0];
          for(int idx=0;idx<NumOfInput;idx++){
            vals[idx]=splitdata[idx+1];
          }
          if(isactive){
            if((timestamp-dfs.get(0).t)<0){
              dfs.cur--;
            }
            if((timestamp-dfs.get(0).t) > ((float)sp[0].tspan / sp[0].sizex/2.0) ){
              dfs.push( new DataFrame(timestamp,vals));
            }
          }
        }
      }
    }
    
    // keyboard user interface
    void keyPressed(){
      switch(key){
        // activate/deactivate scope update
      case ' ':
        isactive=!isactive;
        break;
        // save record
      case 's':
        dfs.save();
        break;
      case CODED:
        switch(keyCode){
          // Increse time span
        case UP:
          for(int i=0;i<NumOfScopes;i++){
            sp[i].tspan*=2;
          }
          break;
          // Decrease time span
        case DOWN:
          for(int i=0;i<NumOfScopes;i++){
            sp[i].tspan/=2;
          }
          break;
        }
        break;
      }
    }
    
    /* Arduino code
    void setup()
    {
      Serial.begin(38400);  
    }
    
    void loop()
    {
      Serial.print(millis());
      Serial.print(",");
      Serial.print(analogRead(0));
      Serial.print(",");
      Serial.print(analogRead(1));
      Serial.println(",");
    }
    */