« June 2017 | Main | August 2017 »

July 2017

07/23/2017

Arduino を使ってexercise の音楽と時間管理をしたい

Aruduinoを使ってエクササイズの音楽と時間管理をしてみたい。
先ずは、Arduinoで時間管理が出来るのか・・・試してみなければ・・・。

《リモコンで、
こんなことがしてみたい
①いわゆるラジカセの電源ON/OFF
②音楽の演奏順や演奏番号を指定
③ボリュームの増減や音量の指定

僕が現在想定しているラジカセは「東芝製 SD/USB/CDラジオ TY-CRX71(W)」で、メディアはSDカードを考えている。

この機種は、SDカードを挿入しておいて本体の電源を入れても、SDカードが認識されないので、確認不十分だが、リモコンで出来る操作はOFFのみかもしれない。
リモコンによるプログラムの手順などを確認する必要がある。
リモコンにある音量ボタンは「-/+」だけなので、Arduinoでどのようにすれば音量を変えられるか、確認する必要がある。

以上のような、確認不十分な課題があるが、とにかくArduinoで時間管理できれば、いわゆるラジカセは別のものに変更して、よりコントロールの行いやすいものにするという方法もあるだろう。

Img_20170723_171122 それで、Arduinoを使って時間管理するスケッチを作ってみることにした。

時間制御関数を調べてみると、Arduinoにはミリ秒やマイクロ秒で制御できる関数がある。この関数を使えば、開始時間からの制御が出来る。
時間関数については、Arduinoがスタートしてからの時刻のずれが問題にならないようにするため、

 millis() :Arduinoが起動してからの時間(ミリ秒)を返す

を使うことにした。また、時間をチェックするところは、ユーザー関数を作成した。

次に、エクササイズの決められた実行時間(1分間)と休憩時間(1分間)を、ほぼ正確に繰り返し(エクササイズ8回と休憩7回後、5分間の休憩を)制御するようにするため、繰り返しした際の条件判定・制御文をどうすれば良いか、随分迷った。
これに関連する制御文は、
 ① (if と) for を組み合わせた制御文
   for(初期処理;条件;変更処理){処理群}

 ② while 制御文
   while(条件){処理群}

 ③ do-while 制御文
   do{処理群}while(条件)

僕は、上記の制御文を試してみて、エクササイズの繰り返し判定には①を、時間の判定には③を用いて下記のようなスケッチを作成した。

リモコンの制御部分は、ここでは print文としてスケッチを作成してある。後で制御コードを調べた後に組み替えたい。

処理速度が速くないArduinoなので、Arduinoは2台の構成を考えている。
・ 1台は親機で、時間管理とラジカセの添付リモコンに代り、リモコンデータを送信する。
・ もう一台は、子機で、親機からの別のリモコンデータを受信して、MP3データで作成された音声を発音する。これは、以前のブログで紹介した記事「TVリモコンを使ってArduinoを喋らせる」を参考に作ることになると思う。

これらの詳細は、次回以降明らかに出来ると思う。ここでは、自分用のメモとして、下記Sketchをここに書いておく。

《時間管理のSketch案》

// Arduino を使った時間管理のための sketch
// 音量位置 Vol1:min, Vol2:med, Vol3:max
//
long i=0;
int  n=0;
int  m=0;
unsigned long atime;  // 告知時刻
unsigned long    tc;  //
unsigned long   tc2;  //
unsigned long t0 = millis();  // 時刻の初期化

// セットアップ
void setup(){
  Serial.begin(9600);
// CD  Playerへの指示
  Serial.println("Player の 電源ON");
  Serial.println("Player の 音楽を選択する");
  Serial.println("Player の Play!");
  
// CD  Playerへの指示
  Serial.println("Player の 音量 Vol1 へ");
  
  atime =15;
  
// ここから カウンター m=0 から 3回ループする
for (m=0; m<3; m++){
  Serial.println(" ");
  Serial.print("m = ");
  Serial.println(m+1);
  
// MP3 Playerへの指示:15秒前と告知する
  Serial.print("atime=");
  Serial.println(atime);
  CountTimer( atime);
  Serial.println(" ");
  Serial.println("スタート15秒前です!");

  atime = atime + 5;  // 5秒後に告知
  Serial.print("atime=");
  Serial.println(atime);
  CountTimer( atime);
  Serial.println(" ");
  Serial.println("10秒前です!");
  
// ここから カウンター n=0 から 8回ループする *****基本の運動ループ部分*****
for (n=0; n<8; n++ ){
// CD  Playerへの指示
  Serial.println("Player の 音量を Vol1 へ");
  
  Serial.print("n = ");
  Serial.println(n);
  
  atime = atime + 5;  // 5秒後に告知
  Serial.print("atime=");
  Serial.println(atime);
  CountTimer( atime);
  Serial.println(" ");
  Serial.println(" 5秒前です!");
  
  atime = atime + 2;  // 2秒後に告知
  Serial.print("atime=");
  Serial.println(atime);
  CountTimer( atime);
  tone(8, 440);
  Serial.println(" ");
  Serial.println(" 3秒前です!");
  noTone(8);
  
  atime = atime + 1;  // 1秒後に告知
  Serial.print("atime=");
  Serial.println(atime);
  CountTimer( atime);
  tone(8, 440);
  Serial.println(" ");
  Serial.println(" 2秒前です!");
  noTone(8);
  
  atime = atime + 1;  // 1秒後に告知
  Serial.print("atime=");
  Serial.println(atime);
  CountTimer( atime);
  tone(8, 440);
  Serial.println(" ");
  Serial.println(" 1秒前です!");
  noTone(8);
  
  atime = atime + 1;  // 1秒後に告知
  Serial.print("atime=");
  Serial.println(atime);
  CountTimer( atime);
  tone(8, 880);
  Serial.println(" ");
  Serial.print(n+1);
  Serial.println(" 回目の 1分間エクササイズ Go!!!!!!!!!!!!!!");
  noTone(8);
// CD  Playerへの指示
  Serial.println("Player の 音量を Vol3 へ");
  
  atime = atime + 55;  // 55秒後に告知
  CountTimer( atime);
// CD  Playerへの指示
  Serial.println(" ");
  Serial.println("Player の 音量を Vol1 へ");

  atime = atime + 2;  // 2秒後に告知
  Serial.print("atime=");
  Serial.println(atime);
  CountTimer( atime);
  tone(8, 440);
  Serial.println(" ");
  Serial.println(" 3秒前です!");
  noTone(8);

  atime = atime + 1;  // 1秒後に告知
  Serial.print("atime=");
  Serial.println(atime);
  CountTimer( atime);
  tone(8, 440);
  Serial.println(" ");
  Serial.println(" 2秒前です!");
  noTone(8);
  
  atime = atime + 1;  // 1秒後に告知
  Serial.print("atime=");
  Serial.println(atime);
  CountTimer( atime);
  tone(8, 440);
  Serial.println(" ");
  Serial.println(" 1秒前です!");
  noTone(8);
  
  atime = atime + 1;  // 1秒後に告知
  Serial.print("atime=");
  Serial.println(atime);
  CountTimer( atime);
  tone(8, 880);
  Serial.println(" ");
  Serial.print(n+1);
  Serial.println(" 回目の 1分間 Brake!!!!!!!!!!!!!!!!!!!!!!!");
  noTone(8);
// CD  Playerへの指示
  Serial.println("Player の 音楽を変更する");
  
  atime = atime + 50;  // 50秒後に告知
}
// 5分間休憩の時間をセット
  atime = atime - 50;
  atime = atime + 5*60 - 15;  // 4分45秒後に告知
  Serial.println(" ");
  Serial.print(m+1);
  Serial.println(" 回目の 5分間 Brake!!!!!!!!!!!!!!!!!!!!!!!");
// CD  Playerへの指示
  Serial.println("Player の 音量を Vol2 へ");
}
// CD  Playerへの指示
  Serial.println("Player をストップさせる");
  Serial.println("Player の電源 OFF");
}
void loop() { }
int CountTimer(unsigned long tsec)
{ // 時間をカウントチェックするためのユーザー関数
  // t0   : 正となる時刻
  // tsec : このサブプログラムで秒読みする時間

  int i=0;  // millis() を読むカウンター
  unsigned long tc;  // tc  : 現時刻
  unsigned long t2 = tsec*1000;    // t2  : t0からの tsec秒経過した時刻

  tc = millis();
  
  while( tc < t2 ){
  tc = millis();
  i=i+1;
  }
 return;
}

07/18/2017

Arduino で Sonic Radar を作る

Img_20170629_220931 Arduinoと超音波距離センサー「HC-SR04」(秋月電子から購入) を使った電子工作の中に面白いものを見つけたので、作ってみた。

これは、「Sonic Radar」というもので、デバイスから超音波を発して、跳ね返ってきた音を受信し、その時間で距離を計測するというものである。PCの助けを借りると、モニター画面上に映し出された表示結果は、レーダー画像を見ているような雰囲気がある。
参考にさせて戴いたURLは、
「ROBOTIC
TUTORIALS.COM」の中の、Ultrasonic based RADAR である。

ハードウェアの組立は、結線箇所も少なく簡単に出来る。ハードウェアの組立に関しては、多くの他のブログでも紹介記事があるので、参考にするとよい。
しかし、PCの画面に結果を表示するためには、Processingというソフトウェアの助けを借りなければならない。
僕は、Processingに関する知識が今は無いので、ネット上にあるものを拝借して出来るものをいろいろ探し回ってみた。私の組上げたシステムで表示できるもは、なかなか見つからなかった。ようやく見つけたのが、このURLである。どちらのSketch も殆どオリジナルのままである。しかし、表示部分に文字の重なりなどの問題があったので、フォントの大きさを変えたり、表示位置を変更し使わせて戴いている。
(注記:Sketch の中で init pin と言っているのは Trig Pin の事である。 まずArduinoを起動してから、次に Processing を起動してやれば良い。Processing が起動したとき、Arduino側の動きが一瞬停まるが、直ぐに同期して、PCの画面上に画像を映し出してくれる。)

《 Arduino Sketch 》

#include 
/*
code for arduino bord ultrasonic radar system.
*/            
Servo leftRightServo;         // set a variable to map the servo
int leftRightPos = 0;         // set a variable to store the servo position
const int numReadings = 10;   // set a variable for the number of readings to take
int index = 0;                // the index of the current reading
int total = 0;                // the total of all readings
int average = 0;              // the average
int echoPin = 6;              // the HC-SR04's echo pin
int initPin = 7;              // the HC-SR04's Trig pin
unsigned long pulseTime = 0;  // variable for reading the pulse
unsigned long distance = 0;   // variable for storing distance
/* setup the pins, servo and serial port */
void setup() {
  leftRightServo.attach(9);
  // make the init pin an output:
  pinMode(initPin, OUTPUT);
  // make the echo pin an input:
  pinMode(echoPin, INPUT);
  // initialize the serial port:
  Serial.begin(9600);
}  
/* begin rotating the servo and getting sensor values */
void loop() {
  for(leftRightPos = 0; leftRightPos < 180; leftRightPos++) {  // going left to right.
    leftRightServo.write(leftRightPos);
      for (index = 0; index <=numReadings;index++) {  // take x number of readings from the sensor and average them
        digitalWrite(initPin, LOW);
        delayMicroseconds(50);
        digitalWrite(initPin, HIGH);          // send signal
        delayMicroseconds(50);                // wait 50 microseconds for it to return
        digitalWrite(initPin, LOW);           // close signal
        pulseTime = pulseIn(echoPin, HIGH);   // calculate time for signal to return
        distance = pulseTime/58;              // convert to centimetres
        total = total + distance;             // update total

        delay(10);
      }
    average = total/numReadings;    // create average reading
 
    if (index >= numReadings)  {    // reset the counts when at the last item of the array
      index = 0;
      total = 0;
    }
    Serial.print("X");              // print leading X to mark the following value as degrees
    Serial.print(leftRightPos);     // current servo position
    Serial.print("V");              // preceeding character to separate values
    Serial.println(average);        // average of sensor readings
  }
  /*
  start going right to left after we got to 180 degrees
  same code as above
  */
  for(leftRightPos = 180; leftRightPos > 0; leftRightPos--) {  // going right to left
    leftRightServo.write(leftRightPos);
    for (index = 0; index <=numReadings;index++) {
      digitalWrite(initPin, LOW);
      delayMicroseconds(50);
      digitalWrite(initPin, HIGH);
      delayMicroseconds(50);
      digitalWrite(initPin, LOW);
      pulseTime = pulseIn(echoPin, HIGH);
      distance = pulseTime/58;
      total = total + distance;
      delay(10);
    }
    average = total/numReadings;
    if (index >= numReadings)  {
      index = 0;
      total = 0;
    }
    Serial.print("X");
    Serial.print(leftRightPos);
    Serial.print("V");
    Serial.println(average);
   }
}

《 Processing 用のSketch》

/*
Radar Screen Visualisation for HC-SR04
 Maps out an area of what the HC-SR04 sees from a top down view.
 Takes and displays 2 readings, one left to right and one right to left.
 Displays an average of the 2 readings
 Displays motion alert if there is a large difference between the 2 values.
 */
import processing.serial.*;   // import serial library
Serial arduinoport;           // declare a serial port
float x, y;              // variable to store x and y co-ordinates for vertices   
int radius = 350;        // set the radius of objects
int w = 300;             // set an arbitary width value
int degree = 0;          // servo position in degrees
int value = 0;           // value from sensor
int motion = 0;          // value to store which way the servo is panning
int[] newValue = new int[181];  // create an array to store each new sensor value for each servo position
int[] oldValue = new int[181];  // create an array to store the previous values.
PFont myFont;                   // setup fonts in Processing
int radarDist = 0;       // set value to configure Radar distance labels
int firstRun = 0;        // value to ignore triggering motion on the first 2 servo sweeps

/* create background and serial buffer */
void setup() {
  // setup the background size, colour and font.
  size(750, 450);
  background (0); // 0 = black
  myFont = createFont("verdana", 12);
  textFont(myFont);
  // setup the serial port and buffer
  arduinoport = new Serial(this, Serial.list()[0], 9600);
}

/* draw the screen */
void draw() {
  fill(0);                     // set the following shapes to be black
  noStroke();                  // set the following shapes to have no outline
  ellipse(radius, radius, 750, 750);  // draw a circle with a width/ height = 750 with its center position (x and y) set by the radius
  rectMode(CENTER);            // set the following rectangle to be drawn around its center
  rect(350, 402, 800, 100);    // draw rectangle (x, y, width, height)
  if (degree >= 179) {         // if at the far right then set motion = 1/ true we're about to go right to left
    motion = 1;                // this changes the animation to run right to left
  }
  if (degree <= 1) {           // if servo at 0 degrees then we're about to go left to right
    motion = 0;                // this sets the animation to run left to right
  }
  /* setup the radar sweep */
  /* 
   We use trigonmetry to create points around a circle.
   So the radius plus the cosine of the servo position converted to radians
   Since radians 0 start at 90 degrees we add 180 to make it start from the left
   Adding +1 (i) each time through the loops to move 1 degree matching the one degree of servo movement
   cos is for the x left to right value and sin calculates the y value
   since its a circle we plot our lines and vertices around the start point for everything will always be the center.
   */
  strokeWeight(7);             // set the thickness of the lines
  if (motion == 0) {           // if going left to right
    for (int i = 0; i <= 20; i++) {  // draw 20 lines with fading colour each 1 degree further round than the last
      stroke(0, (10*i), 0);    // set the stroke colour (Red, Green, Blue) base it on the the value of i
      line(radius, radius, radius + cos(radians(degree+(180+i)))*w, radius + sin(radians(degree+(180+i)))*w); // line(start x, start y, end x, end y)
    }
  } else {                     // if going right to left
    for (int i = 20; i >= 0; i--) {  // draw 20 lines with fading colour
      stroke(0, 200-(10*i), 0);      // using standard RGB values, each between 0 and 255
      line(radius, radius, radius + cos(radians(degree+(180+i)))*w, radius + sin(radians(degree+(180+i)))*w);
    }
  }
  /* Setup the shapes made from the sensor values */
  noStroke();                  // no outline
  /* first sweep */
  fill(0, 50, 250);            // set the fill colour of the shape (Red, Green, Blue)
  beginShape();                // start drawing shape
  for (int i = 0; i < 180; i++) {    // for each degree in the array
    x = radius + cos(radians((180+i)))*((oldValue[i])); // create x coordinate
    y = radius + sin(radians((180+i)))*((oldValue[i])); // create y coordinate
    vertex(x, y);              // plot vertices
  }
  endShape();                  // end shape
  /* second sweep */
  fill(0, 110, 0);
  beginShape();
  for (int i = 0; i < 180; i++) {
    x = radius + cos(radians((180+i)))*(newValue[i]);
    y = radius + sin(radians((180+i)))*(newValue[i]);
    vertex(x, y);
  }
  endShape();
  /* average */
  fill(0, 170, 0);
  beginShape();
  for (int i = 0; i < 180; i++) {
    x = radius + cos(radians((180+i)))*((newValue[i]+oldValue[i])/2); // create average
    y = radius + sin(radians((180+i)))*((newValue[i]+oldValue[i])/2);
    vertex(x, y);
  }
  endShape();
  /* if after first 2 sweeps, highlight motion with red circle*/
  if (firstRun >= 360) {
    stroke(150, 0, 0);
    strokeWeight(1);
    noFill();
    for (int i = 0; i < 180; i++) {
      if (oldValue[i] - newValue[i] > 35 || newValue[i] - oldValue[i] > 35) {
        x = radius + cos(radians((180+i)))*(newValue[i]);
        y = radius + sin(radians((180+i)))*(newValue[i]);
        ellipse(x, y, 10, 10);
      }
    }
  }
/* set the radar distance rings and out put their values, 50, 100, 150 etc.. */
  for (int i = 0; i <=6; i++) {
    noFill();
    strokeWeight(1);
    stroke(0, 255-(30*i), 0);
    ellipse(radius, radius, (100*i), (100*i)); 
    fill(200, 200, 200);
    noStroke();
    text(Integer.toString(radarDist+50), 380, (305-radarDist), 50, 50);
    radarDist+=50;
  }
  radarDist = 0;
/* draw the grid lines on the radar every 30 degrees and write their values 180, 210, 240 etc.. */
  for (int i = 0; i <= 6; i++) {
    strokeWeight(1);
    stroke(0, 55, 0);
    line(radius, radius, radius + cos(radians(180+(30*i)))*w, radius + sin(radians(180+(30*i)))*w);
    fill(200, 200, 200);
    noStroke();
    if (180+(30*i) >= 300) {
      text(Integer.toString(180+(30*i)), (radius+10) + cos(radians(180+(30*i)))*(w+10), (radius+10) + sin(radians(180+(30*i)))*(w+10), 25, 50);
    } else {
      text(Integer.toString(180+(30*i)), radius + cos(radians(180+(30*i)))*w, radius + sin(radians(180+(30*i)))*w, 60, 40);
    }
  }
/* Write information text and values. */
  noStroke();
  fill(0);
  rect(350, 402, 800, 100);
  fill(0, 100, 0);
  text("Degrees: "+Integer.toString(degree), 100, 380, 100, 50);  // use Integet.toString to convert numeric to string as text() only outputs strings
  text("Distance: "+Integer.toString(value), 100, 400, 100, 50);  // text(string, x, y, width, height)
  text("Radar Plot Graph ", 540, 380, 250, 50);
  fill(0, 50, 250);
  textSize(22);
  text("SP ROBOTICS : RADAR ", 570, 480, 650, 200);
  textSize(12);
  fill(0);
  rect(70, 60, 150, 100);
  fill(255, 255, 0); 
  text("Screen Key:", 100, 50, 150, 50);
  fill(0, 50, 250);
  rect(30, 53, 10, 10);
  text("First sweep", 115, 70, 150, 50);
  fill(0, 110, 0);
  rect(30, 73, 10, 10);
  text("Second sweep", 115, 90, 150, 50);
  fill(0, 170, 0);
  rect(30, 93, 10, 10);
  text("Average", 115, 110, 150, 50);
  noFill();
  stroke(150, 0, 0);
  strokeWeight(1);
  ellipse(29, 113, 10, 10); 
  fill(150, 0, 0);
  text("Motion", 115, 130, 150, 50);
}

/* get values from serial port */
void serialEvent (Serial arduinoport) {
  String xString = arduinoport.readStringUntil('\n');  // read the serial port until a new line
  if (xString != null) {  // if theres data in between the new lines
    xString = trim(xString); // get rid of any whitespace just in case
    String getX = xString.substring(1, xString.indexOf("V")); // get the value of the servo position
    String getV = xString.substring(xString.indexOf("V")+1, xString.length()); // get the value of the sensor reading
    degree = Integer.parseInt(getX); // set the values to variables
    value = Integer.parseInt(getV);
    oldValue[degree] = newValue[degree]; // store the values in the arrays.
    newValue[degree] = value;  
 /* sets a counter to allow for the first 2 sweeps of the servo */
    firstRun++;
    if (firstRun > 360) {
      firstRun = 360; // keep the value at 360
    }
  }
}

07/14/2017

Arduinoで(太陽)光の方向を追いかける2軸サーボ(New Algorithm)

Img_20170708_153936 Img_20170708_153958 Img_20170706_101405 Img_20170706_101420













Arduino Nanoで(太陽)光の方向を追いかける装置をブレッドボード上に作ってうまく稼働したので、小さなユニバーサル基板(60mm x 40mm)の上に部品を置いて製作してみた。
ケースも100均ショップで、適当な大きさの円筒形貯金箱を入手したので、その中に基板を入れ、ケース上面にLDRセンサー(前回作ったもの)を装着したサーボを固定した。

装置が新しくなったので、それに併せて光を追いかけるスケッチも自分のオリジナルな発想でロジックを考え、光の方向を探索する時間も短縮できるようにした。その結果、光を追いかける速度は格段に速くなり、室内などでテストした際、室内灯を消灯すると、初期の位置に戻り、点灯すると直ちに灯りの位置を向く。またセンサー部に手を翳すと、手の影を嫌ってより明るい方向を向くように動くので、その仕草がなかなか可愛い。

と書いてから、本当に明るい方向を向いているのだろうか・・・と初期の動きが気になったので、センサーを経度方向約170度分を周して、そこで明るい場所を探索の初期値にするようにスケッチを追加した。
緯度方向は、初期値を決め打ちして、探査をしていないので、そのために初期値の緯度・経度値から位置を確定するまでに少し時間がかかる場合がある。

スケッチは当初のものより少し長くなってしまった。しかし、まあまあ納得がいくようになったのでスケッチを公開することにする。
緯度方向の初期値探査が欲しい場合には、私のスケッチに倣って探せば良いし、経度方向の初期探査が不要なら、(34~55行までと関連のある部分を)外せばいい。それでも大きな問題無く稼働すると思う。

《Sketch》

// The Sunlight Tracker New Algorithm  by Savvy 2017.07.14
//
#include <Servo.h>
Servo servohori;
int svLimitMax = 175;
int svLimitMin =   5;
int servoh     = svLimitMin;   // Initial value of search angle

Servo servoverti; 
int svLimitHigh = 120;
int svLimitLow  =   5;
int servov      =  45;  // Initial value of search angle

// Assign the wireing color to LDRs
int ldrtopl = 2;  // top left LDR Green wiring
int ldrtopr = 1;  // top right LDR Yellow wiring
int ldrbotl = 3;  // bottom left LDR Blue wiring
int ldrbotr = 0;  // bottom right LDR Red wiring

int topl;
int topr;
int botl;
int botr;

int hstep;
int vstep;
int alpha;
int  beta;

 void setup () {
  servohori.attach(10);    // horizontal motor servo  (Blue wiring)
  servoverti.attach(9);    // vertical motor servo  (Green wiring)

// search initial horizontal angle between servo limit min-max angle
  int i    = 0;
  int imin = 0;
  int lx[]  = { 9999, 9999, 9999, 9999, 9999, 9999, 9999};
  int hstp  = (svLimitMax - svLimitMin) / 6;
  int lxmin = 9999;
  alpha = svLimitMin;
  beta  = servov;

  for ( i=0; i<7; i++ )
  {
   lx[i] =  wlight( alpha, beta);
   alpha = alpha + hstp;
   if( lx[i] < lxmin )
   { lxmin = lx[i];
    imin = i;
     }
  }
  delay(200);
  // this is the initial location of horizontal servo
  servohori.write( svLimitMin + hstp*imin );
}
void loop(){
  // Read the servo set angle at the moment
  servoh = servohori.read();
  servov = servoverti.read();
  
  // Read the light number of each LDR
  topl = analogRead(ldrtopl);
  topr = analogRead(ldrtopr);
  botl = analogRead(ldrbotl);
  botr = analogRead(ldrbotr);
  
  // calculate the average value
  int avgtop   = (topl + topr) / 2;   // average of top LDRs
  int avgbot   = (botl + botr) / 2;   // average of bottom LDRs
  int avgleft  = (topl + botl) / 2;   // average of left LDRs
  int avgright = (topr + botr) / 2;   // average of right LDRs

  if( (avgtop < 1012) & (avgbot < 1012) )  // the servo acts while in the light
  {
    hstep = xstep(avgright,avgleft);  // determination of the horizontal serch step angle
    servoh = servoh + hstep;
    
    if((svLimitMin < servoh) & (servoh < svLimitMax))  // acts between max - min limits
    { 
       servohori.write(servoh);
    }
    
    vstep = xstep(avgtop,avgbot);   // determination of the vertical serch step angle 
    servov = servov + vstep;
    
    if((svLimitLow < servov) & (servov < svLimitHigh))
    {
        servoverti.write(servov);
    }       
  }
  delay(200);
}
int xstep(int avg1, int avg2)
  // classify the step value from the two light value
{
  int jcase = avg1 - avg2;
  int result;
    
    if(jcase < -30 ) {
      result = 10; 
    }
    if((-30 <= jcase) & (jcase < -10)){
      result = 3;
    }
    if((-10 <= jcase) & (jcase <  -2)){
      result = 1;
    }
    if(( -2 <= jcase) & (jcase <  2)){
      result = 0;
    }
    if(( 2 <= jcase) & (jcase < 10)){
      result = -1;
    }
    if(( 10 <= jcase) & (jcase < 30)){
      result = -3;
    }
    if( 30 <= jcase ){
      result = -10;
    }
    return result;
}
int wlight(int alpha, int beta)
{
   servohori.write(alpha);
   servoverti.write(beta);
   int loc  = (analogRead(ldrtopl) + analogRead(ldrtopr) + analogRead(ldrbotl) + analogRead(ldrbotr)) / 4; 
   delay(200);
   return loc;
}

07/04/2017

Arduinoで作る太陽光の方向を向く2軸サーボ

Img_20170704_203131 Img_20170629_224615 Arduino Nano (ATmega328)を使って、(太陽)光のある方向を向く2軸サーボ装置を作ってみた。
・Arduinoは、 Nano を
・ServoMotorは、 SG92R を 2個使い
・フォトレジスターは、 手元にあったものを 4個使った。はじめは、抵抗値の基本値を選ぶ必要があるのか・・・と思ったが、ロットが揃っているものなら、それを使って良さそうだ。

Webで見つけたサイト「Arduino Solar Tracker(http://www.electronicshub.org/arduino-solar-tracker/
」などを参考に製作した。

Img_20170704_090341 Img_20170704_090529 Img_20170704_090634 フォトレジスタと抵抗の固定は、小型のユニバーサル基板の上に半田付けし、光の方向を捉え易くするために、写真のように十字型の覆いを設けた。この部分の回路図は、参考にさせてもらったブログの通りであると思う。

基板の後ろから見て、右下(赤色の配線⇒A0)、右上(黄色の配線⇒A1)、左上(緑色の配線⇒A2)、左下(青色の配線⇒A3)とした。

サーボモーターは、水平方向の制御線を水色⇒D10へ、垂直方向の制御線を白⇒D9へと繋いだ。

Solar_trackerflow_chart_20170704 最初、スケッチは参考にしたブログのものをそのまま使ったが、収束に時間がかかる事と、収束した状態でもサーボを動かしているので、左図のようにフローチャートを作成して、収束時間を短縮し、収束したらサーボモータを停めるようにスケッチを変更した。細かな所は、左図を確認すれば解るだろうと思う。


スケッチは、制御の状態を確認したかったので、オリジナルのスケッチに対してシリアルモニターに途中の状態を出力するように追記した。

出力結果を確認し、光源の位置から離れていると思われる場合には、光源に向かうサーボモータ回転角の刻み幅を大きくし、光源に近づいてくると、刻み幅を小さくするようにした。


閾値は明るさの差 eps を”5”としたが、動き方を見て調整するのが良いだろう。

稼働させてみると、光のある方向への収束時間は随分早くなったように思う。

スケッチは、下記のようである。

// The Sunlight Tracker 2017.07.04
//
#include <Servo.h>
Servo servohori;
int servoh = 90;
int servohLimitHigh = 170;
int servohLimitLow = 10;
int hstep = 1;
int eps = 5;

Servo servoverti; 
int servov = 30; 
int servovLimitHigh = 120;
int servovLimitLow = 10;
int vstep = 1;

//Assigning LDRs
int ldrtopl = 2; //top left LDR Green wiring
int ldrtopr = 1; //top right LDR Yellow wiring
int ldrbotl = 3; // bottom left LDR Blue wiring
int ldrbotr = 0; // bottom right LDR Red wiring

 void setup () {
  Serial.begin(9600);   // to check the sketch, print out to the monitor
  servohori.attach(10); // horizontal motor servo  (Blue wiring)
  servohori.write(servoh);
  servoverti.attach(9); // vertical motor servo  (Green wiring)
  servoverti.write(servov);
  delay(500);
 }

void loop(){
  servoh = servohori.read();
  servov = servoverti.read();
  Serial.print("Location of Servo   Horizontal:");
  Serial.print(servoh);
  Serial.print("deg,  Vertical:");
  Serial.print(servov);
  Serial.println("deg");
  
  //capturing analog values of each LDR
  int topl = analogRead(ldrtopl);
  int topr = analogRead(ldrtopr);
  int botl = analogRead(ldrbotl);
  int botr = analogRead(ldrbotr);
  Serial.println(" ");
  Serial.println("Captured LDR value ");
  Serial.print("TL:");
  Serial.print(topl);
  Serial.print("  TR:");
  Serial.println(topr);
  Serial.print("BL:");
  Serial.print(botl);
  Serial.print("  BR:");
  Serial.println(botr);
  
  // calculating average
  int avgtop = (topl + topr) / 2;   //average of top LDRs
  int avgbot = (botl + botr) / 2;   //average of bottom LDRs
  int avgleft = (topl + botl) / 2;  //average of left LDRs
  int avgright = (topr + botr) / 2; //average of right LDRs

  if (avgtop < avgbot) {
    Serial.print("Average Value  ");
    Serial.print("Top:");Serial.print(avgtop);
    Serial.print(" < ");
    Serial.print("Bottom:");Serial.println(avgbot);
    
    vstep = abs(avgtop-avgbot);
    if( vstep < eps ){
      Serial.println("Top is lighter than Bottom =< turn Up");
      vstep = 1 + vstep/4;
      Serial.print("servov = ");Serial.print(servov);Serial.print("+");
      Serial.println(vstep);
      servoverti.write(servov + vstep); 
      }
    else {  // to stabilize the servo-motor
      Serial.print("|Top-Bottom|=<");Serial.print(eps);
      Serial.println(" then Stop the Vertical servo");     
    }
    if (servov > servovLimitHigh) { 
      servov = servovLimitHigh;
     }
    delay(10);
  }
  else if (avgbot < avgtop) {
      Serial.print("Average Value of LDR  ");
      Serial.print("Bottom:");Serial.print(avgbot);
      Serial.print(" < ");
      Serial.print("Top:");Serial.println(avgtop);

    vstep = abs(avgtop-avgbot); 
    if( vstep > eps ){   
      Serial.println("Bottom is lighter than Top => turn Down");
      vstep = 1 + vstep/4;
      Serial.print("servov = ");Serial.print(servov);Serial.print("-");
      Serial.println(vstep);     
      servoverti.write(servov - vstep);
    }
    else {  // to stabilize the servo-motor
      Serial.print("|Top-Bottom|=<");Serial.print(eps);
      Serial.println(" then Stop the Vertical servo");       
    }
    if (servov < servovLimitLow) {
    servov = servovLimitLow;
    }
    delay(10);
  }
  else {
  }
  
  if (avgleft > avgright) {
      Serial.print("Average Value of LDR  ");
      Serial.print("Left:");Serial.print(avgleft);
      Serial.print(" > ");
      Serial.print("Right:");Serial.println(avgright);

      hstep = abs(avgleft-avgright);
    if( hstep > eps ){  
      Serial.println("Right is lighter than Left => turn Right");
      hstep = 1 + hstep/4;
      Serial.print("servoh = ");Serial.print(servoh);Serial.print("+");
      Serial.println(hstep);
      servohori.write(servoh + hstep);
    }
    else {   // to stabilize the servo-motor
      Serial.print("|Left-Right|=<");Serial.print(eps);
      Serial.println(" then Stop the Horizontal servo");         
    }
    if (servoh > servohLimitHigh) {
    servoh = servohLimitHigh;
    }
    delay(10);
  }
  else if (avgright > avgleft) {
      Serial.print("Average Value of LDR  ");
      Serial.print("Right:");Serial.print(avgright);
      Serial.print(" > ");
      Serial.print("Left:");Serial.println(avgleft);    

    hstep = abs(avgleft-avgright);
    if( hstep > eps ){   
      Serial.println("Left is lighter than Right => turn Left");
      hstep = 1 + hstep/4;
      Serial.print("servoh = ");Serial.print(servoh);Serial.print("-");
      Serial.println(hstep);     
      servohori.write(servoh - hstep);
    }
    else {  // to stabilize the servo-motor
      Serial.print("|Left-Right|=<");Serial.print(eps);
      Serial.println(" then Stop the Horizontal servo");
    }
    if (servoh < servohLimitLow) {
      servoh = servohLimitLow;
     }
    delay(10);
  }
  else  {
  }
  delay(50);
}


《備考》
ここに、
 hstep : 水平方向回転サーボモータの回転刻み角度
  vstep : 垂直方向回転サーボモータの回転刻み角度
 esp : 左右、上下のフォトレジスタから読取った値を比較した際のサーボモータの停止判定値

問題は、周囲が暗くなると、灯りを探し回るので、この動きを止める必要がありそうだ。
多分、フォトレジスタで計測した際の明るさが、ある程度大きくなったら、サーボモータの動きを止めれば良いと思う。これは、次にソーラーパネルと組み合わせる計画があるので、その際に改造するつもりだ。

« June 2017 | Main | August 2017 »

November 2017
Sun Mon Tue Wed Thu Fri Sat
      1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30    

Recent Trackbacks

無料ブログはココログ