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

2012年12月14日

Raspberry PiでSPG20-1332を回す

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク

Raspberry Piで秋月電子で売っている安いステッピングモーター(コパル製 SPG20-1332)を回す。
以前、Arduinoで二相励磁駆動させたものをRaspberry Piでやってみました。



回路は以下の様な感じで。


Raspberry PiのI/Oは3.3V系なので、トランジスタアレイ(TD62004APG)を入れてレベルシフトしています。

#include <stdio.h>

int main()
{
    int i;
    
    system("echo \"7\" > /sys/class/gpio/export");
    system("echo \"8\" > /sys/class/gpio/export");
    system("echo \"9\" > /sys/class/gpio/export");
    system("echo \"10\" > /sys/class/gpio/export");
    system("echo \"out\" > /sys/class/gpio/gpio7/direction");
    system("echo \"out\" > /sys/class/gpio/gpio8/direction");
    system("echo \"out\" > /sys/class/gpio/gpio9/direction");
    system("echo \"out\" > /sys/class/gpio/gpio10/direction");
    
    for (i=0; i<360; i++) {
            system("echo \"1\" > /sys/class/gpio/gpio7/value");
            system("echo \"1\" > /sys/class/gpio/gpio8/value");
            system("echo \"0\" > /sys/class/gpio/gpio7/value");
            system("echo \"1\" > /sys/class/gpio/gpio8/value");
            system("echo \"1\" > /sys/class/gpio/gpio9/value");
            system("echo \"0\" > /sys/class/gpio/gpio8/value");
            system("echo \"1\" > /sys/class/gpio/gpio9/value");
            system("echo \"1\" > /sys/class/gpio/gpio10/value");
            system("echo \"0\" > /sys/class/gpio/gpio9/value");
            system("echo \"1\" > /sys/class/gpio/gpio10/value");
            system("echo \"1\" > /sys/class/gpio/gpio7/value");
            system("echo \"0\" > /sys/class/gpio/gpio10/value");
    }
    
    system("echo \"7\" > /sys/class/gpio/unexport");
    system("echo \"8\" > /sys/class/gpio/unexport");
    system("echo \"9\" > /sys/class/gpio/unexport");
    system("echo \"10\" > /sys/class/gpio/unexport");
}
480ステップで1回転なので、120回ループしてあげれば1回転、上記では360回ループなので3回転します。

とりあえず動かすだけなので、system関数でシェルを呼び出す感じのへたれコード。
本格的に使うならレジスタを直で叩く方が良いと思います。(速度的に雲泥の差)

追記:PythonでRPi.GPIOを利用して上記をそっくりそのまま写すならこんな感じかな?(未検証)
import RPi.GPIO as GPIO

GPIO.setmode(GPIO.BCM)
GPIO.setup(7,GPIO.OUT)
GPIO.setup(8,GPIO.OUT)
GPIO.setup(9,GPIO.OUT)
GPIO.setup(10,GPIO.OUT)
    
for i in range(0,360):
      GPIO.output(7,GPIO.HIGH)
      GPIO.output(8,GPIO.HIGH)
      GPIO.output(7,GPIO.LOW)
      GPIO.output(8,GPIO.HIGH)
      GPIO.output(9,GPIO.HIGH)
      GPIO.output(8,GPIO.LOW)
      GPIO.output(9,GPIO.HIGH)
      GPIO.output(10,GPIO.HIGH)
      GPIO.output(9,GPIO.LOW)
      GPIO.output(10,GPIO.HIGH)
      GPIO.output(7,GPIO.HIGH)
      GPIO.output(10,GPIO.LOW)

GPIO.cleanup()
ループ部分は以前の記事に近い形で書き直せるはず。(放置)

2012年11月7日

球面プロッタを応募してみた

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク

7月頃に作った卓球玉に絵を描く装置(球面プロッタ)を電子工作コンテスト2012に応募してみたり。
http://elecontest.com/entrylist/043

以前の完成記事からの変更点は、回路をブレッドボードからバニラシールドに移した点、ボタンひとつでデフォルト描画パターンを単独描画(PCレス)できるようにした点の2つ。

ソースコードはこちら
pingPongBot(KumamotoNCT).ino (9299Byte)
※「熊本高専」と描画する座標データを内包

紹介動画はこちら



ちなみに動画はiMovieで作成。
普段はAfterEffectsばかり使ってるのでiMovieには慣れてなかったり...

2012年7月13日

球面プロッタ完成 (SPG20-332 + MP4401 その3)

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク

Arduinoでステッピングモータ SPG20-332を回すやつの続きの続き。
Arduino側の制御プログラムが完成し、残りはベクターデータから(相対)座標を取り出してArduinoにコマンドを送るプログラムの制作のみ。

とりあえず、簡単な図形ということで、星型を描かせてみました。



始点と終点がずれているのは、ボールの回転軸が微妙にずれているため。
工作精度 or 強度の問題なので、ボールをしっかりと軸の中心に固定できるように固定具を作り直す必要がありそうです。

プログラムはこんな感じ。
鬼のようなネストなど存在しない。(エレガントに書き直す気力が無いので放置)
#include <Servo.h>

#define SERVO_PIN 3 //サーボモータの接続ピン
#define PEN_UP_ANGLE 115 //ペン上げ角度
#define PEN_DOWN_ANGLE 100 //ペン下げ角度
#define MOTOR_DELAY 7 //励磁間隔

const byte motorPen[]={
  0x09,0x0C,0x06,0x03}; //ペン軸 励磁パターン
const byte motorBall[]={
  0x30,0x60,0xC0,0x90}; //ボール軸 励磁パターン
Servo servoPen; //ペン持ち上げ用サーボを定義
char incoming[100]; //シリアルからのコマンド受け取り用
int x,y; //移動先座標と差分
int magDrivePen,magDriveBall; //現在の励磁状態を記憶

void setup(){
  DDRB |=B00001111; //ポートB出力設定
  DDRD |=B11110000; //ポートD出力設定
  servoPen.attach(SERVO_PIN); //サーボ出力ピン設定
  servoPen.write(PEN_UP_ANGLE); //ペン持ち上げ
  Serial.begin(9600); //シリアル通信9600bps
}

void getCommand(char *buf) //シリアル通信から座標を取得
{
  int i = 0;
  char c;
  while(1){
    if(Serial.available()){
      c = Serial.read();
      buf[i] = c;
      if (c == '\n') break;
      i++;
    }
  }
  buf[i] = '\0';
}

void movePenForward() //ペン前進
{
  magDrivePen++;
  if(magDrivePen>3)magDrivePen=0;
  PORTB &=0xf0;
  PORTB |= motorPen[magDrivePen];
  delay(MOTOR_DELAY);
}

void movePenBackward() //ペン後進
{
  magDrivePen--;
  if(magDrivePen<0)magDrivePen=3;
  PORTB &=0xf0;
  PORTB |= motorPen[magDrivePen];
  delay(MOTOR_DELAY); 
}

void moveBallForward() //ボール前進
{
  magDriveBall++;
  if(magDriveBall>3)magDriveBall=0;
  PORTD &=0x0f;
  PORTD |= motorBall[magDriveBall];
  delay(MOTOR_DELAY);
}

void moveBallBackward() //ボール後進
{
  magDriveBall--;
  if(magDriveBall<0)magDriveBall=3;
  PORTD &=0x0f;
  PORTD |= motorBall[magDriveBall];
  delay(MOTOR_DELAY); 
}

void writing(int dx, int dy) //座標移動(Bresenhamの線描アルゴリズム)
{
  int e = 0; //誤差蓄積用
  if(abs(dx) > abs(dy)){ //傾きが1未満の場合
    if(dx>=0){ //x軸方向が正の場合
      for (int sx = 0; sx < dx; sx++) {
        movePenForward();
        if(dy>=0){ //y軸方向が正の場合
          e += dy;
          if (e > dx){
            e -= dx;
            moveBallForward();
          }
        }
        else{ //y軸方向が負の場合
          e += abs(dy);
          if (e > dx){
            e -= dx;
            moveBallBackward();
          }
        }
      }
    }
    else{ //x軸方向が負の場合
      for (int sx = 0; sx < abs(dx); sx++) {
        movePenBackward();
        if(dy>=0){ //y軸方向が正の場合
          e += dy;
          if (e > abs(dx)){
            e -= abs(dx);
            moveBallForward();
          }
        }
        else{ //y軸方向が負の場合
          e += abs(dy);
          if (e > abs(dx)){
            e -= abs(dx);
            moveBallBackward();
          }
        }
      }
    }
  }
  else{ //傾きが1以上の場合
    if(dy>=0){ //y軸方向が正の場合
      for (int sx,sy = 0; sy < dy; sy++) {
        moveBallForward();
        if(dx>=0){ //x軸方向が正の場合
          e += dx;
          if (e > dy){
            e -= dy;
            movePenForward();
            sx++;
          }
        }
        else{ //x軸方向が負の場合
          e += abs(dx);
          if (e > dy){
            e -= dy;
            movePenBackward();
            sx++;
          }
        }
      }
    }
    else{ //y軸方向が負の場合
      for (int sx,sy = 0; sy < abs(dy); sy++) {
        moveBallBackward();
        if(dx>=0){ //x軸方向が正の場合
          e += dx;
          if (e > abs(dy)){
            e -= abs(dy);
            movePenForward();
            sx++;
          }
        }
        else{ //x軸方向が負の場合
          e += abs(dx);
          if (e > abs(dy)){
            e -= abs(dy);
            movePenBackward();
            sx++;
          }
        }
      }
    }
  }
}

void loop(){
  getCommand(incoming); //シリアルから読み込み

  char *cmdX, *cmdY, *cmdPen;
  int penUpDown;

  if(incoming){ //座標系、ペン状態別にコマンドを分離
    cmdX = strtok(incoming,",");
    cmdY = strtok(NULL,",");
    cmdPen = strtok(NULL,",");
    penUpDown = atoi(cmdPen);
  }

  if(penUpDown == 0)servoPen.write(PEN_UP_ANGLE); //ペン上げ
  if(penUpDown == 1)servoPen.write(PEN_DOWN_ANGLE); //ペン下げ
  delay(200); //サーボの動作待ち

  x=atoi(cmdX); //charからintへ変換
  y=atoi(cmdY);

  writing(x,y); //指定座標へ移動
  
  Serial.print("X:");
  Serial.print(cmdX); //ペン軸の移動量表示
  Serial.print("/Y:");
  Serial.print(cmdY); //ボール軸の移動量表示
  Serial.print("/Pen:");
  Serial.println(cmdPen); //ペンの上げ下げ状態表示
}
ポイントはブレゼンハムの線分アルゴリズム。
傾きが1以外(1より大きい or 1より小さい)の時も真っ直ぐに線を引くために使いました。

でもって、Processing側はこんな感じの雑なコード。(あくまでも動作確認用なので)
実際は、Arduinoから返ってくる座標とペン状態を受け取ってから次のステップに移るようにする必要がある。
import processing.serial.*;
Serial port;
 
void setup()
{
  size(100,100);
  port = new Serial(this, "/dev/tty.usbmodem1421", 9600);
  port.clear();
}
 
void draw() {
  for(int i=0;i<=4;i++){
  delay(1500);
  port.write("-15,43,1\n");
  delay(1500);
  port.write("38,-30,1\n");
  delay(1500);
  port.write("-45,0,1\n");
  delay(1500);
  port.write("38,30,1\n");
  delay(1500);
  port.write("-15,-43,1\n");
  delay(1500);
  port.write("-1,80,0\n");
  }
  exit();
}

余談
写真の左上にチラッと見えてるパソコンは、最近買ったMacBook Pro Retinaディスプレイモデルのフルカスタムマシン。
こいつのUSBポートから電源を取ると、Arduino UNOにACアダプタを刺さなくても普通に動作しました。

2012年6月28日

SPG20-332 + MP4401 その2

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク

Arduinoでステッピングモータ SPG20-332を回すやつの続き。
葉山先生の華麗なるアルミ板とPOM板加工によって上の写真(左)のような状態に。
卓球のピンポン球に絵を描くということでピン球botと命名され、あとを任されました

ボールを回転させる軸とペンの向きを変える軸の2軸なので、少し接続を変更。


pin 4〜7に接続したステッピングモータをボールの軸、pin 8〜11に接続したステッピングモータをペンの軸とする。


ATmega168/328のI/OポートはB、C、Dの3つで、port Bがpin 8〜13、port Cがpin A0〜A5、port Dがpin 0〜7となっている。
ローレベルかつ高速に制御したい時は、これらのポートのコントロールレジスタを直接いじってあげれば良い。→Reference/PortManipulation
(というのも、前回の記事で載せた方式で制御しようとすると励磁する速度の問題で細かい振動が発生するっぽいので)
プログラムはこんな感じで。
#include <Servo.h>
const byte motorPen[]={0x03,0x06,0x0C,0x09}; //PortBの励磁パターン
const byte motorBall[]={0x90,0xC0,0x60,0x30}; //PortDの励磁パターン
Servo servoPen;

void setup(){
  DDRB |=B00001111; //PortB 8,9,10,11
  DDRD |=B11110000; //PortD 4,5,6,7
  servoPen.attach(3);
}

void loop(){
 for(int n=0;n<20;n++){
   servoPen.write(100);
   for(int i=0;i<4;i++){
     PORTB &=0xf0;
     PORTB |= motorPen[i];
     PORTD &=0x0f;
     PORTD |= motorBall[i];
     delay(8);
   }
 }
 for(int n=0;n<20;n++){
   servoPen.write(115);
   for(int i=3;i>=0;i--){
     PORTB &=0xf0;
     PORTB |= motorPen[i];
     PORTD &=0x0f;
     PORTD |= motorBall[i];
     delay(8);
   }
 }
}

DDRB |= B00001111でポートBの下位4ビット(pin 11,10,9,8)を出力に設定。
DDRD |= B11110000でポートDの上位4ビット(pin 7,6,5,4)を出力に設定。
PORTBとPORTDを初期化し、あらかじめ定義しておいたモータの励磁パターンを投げてあげるという仕組み。
サーボはペンの持ち上げに使用。

これで、以下の動画のように動作する。


行き(右から左)はペン先をボール表面に降ろし、帰り(左から右)はペン先を持ち上げています。

次は起動時のゼロ点補正機構と、座標指定で描いてくれるようなプログラムを作っていきます。

2012年6月24日

SPG20-332 + MP4401

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク

Arduinoでステッピングモーター SPG20-332(≒SPG20-1332)を回す。
モータードライバーとしてMP4401を使う。
Fritzingの練習も兼ねて作ってみた接続図。
(同梱されてたステッピングモーターのSVGを弄ってSPG20-S332用にピン配置を変更しています)

Arduino     MP4401  
 pin 10 ----- pin 01
 pin 11 ----- pin 12
 pin 12 ----- pin 05
 pin 13 ----- pin 08

MP4401   SPG20-332
 pin 02 ----- pin 1
 pin 09 ----- pin 6
 pin 11 ----- pin 2
 pin 04 ----- pin 5

MP4401のpin3とpin10をVinに、pin6とpin7をGNDに接続、SPG20-332のpin3とpin4をVinに接続。
今回は2相励磁で回すのでArduinoには7.5VのACアダプタを接続しました。
(7〜9Vくらいの電源なら良いんじゃないかと)

プログラムはこんな感じでいいはず。
int pin[4] = {10,11,12,13};

void forward(int rot){
  for(int i=0;i<rot*120;i++){
    for(int i=0;i<4;i++){
       int prev = i-1;
       int next = i+1;
       if(prev < 0) prev = 3;
       if(next > 3) next = 0;
       digitalWrite(pin[prev], LOW);
       digitalWrite(pin[i], HIGH);
       digitalWrite(pin[next], HIGH);
       delay(2);
     }
   }
}
void reverse(int rot){
  for(int i=0;i<rot*120;i++){
     for(int i=3;i>=0;i--){
       int prev = i+1;
       int next = i-1;
       if(prev > 3) prev = 0;
       if(next < 0) next = 3;
       digitalWrite(pin[prev], LOW);
       digitalWrite(pin[i], HIGH);
       digitalWrite(pin[next], HIGH);
       delay(2);
     }
   }
}

void setup(){
  for(int i=0;i<4;i++){
    pinMode(pin[i], OUTPUT);
  }
}

void loop(){
  forward(1); // 1回正転
  reverse(1); // 1回逆転
  forward(3); // 3回正転
  reverse(3); // 3回逆転
  forward(5); // 5回正転
  reverse(5); // 5回逆転
}

2相励磁なのでトルクは十分。
ステッピングモーターを2個使ってプロッタとか作ってみたいなぁと思っているところです。

追記:ポートレジスタで制御する方法を書きました → SPG20-332 + MP4401 その2