工業製造
産業用モノのインターネット | 工業材料 | 機器のメンテナンスと修理 | 産業プログラミング |
home  MfgRobots >> 工業製造 >  >> Manufacturing Technology >> 製造プロセス

高精度サーボ駆動ソーラートラッカー - 最大発電効率を実現する自動追尾プラットフォーム

必要な部品と用品

フォトレジスタ(CdSセル) ×4
Adafruit Flora RGB NeoPixel LED(4個パック) ×1 同等のフルカラーLEDで代替可
タクトスイッチ 12mm ×2 同等のノーマルオープンプッシュボタンで代替可
温度センサー ×1
Arduino UNO ×1
180°サーボモーター ×2 Futaba S3003等の同等品で可
抵抗 10kΩ ×6
抵抗 220Ω ×1
コンデンサ 100µF ×2
ソーラーパネル(80mm × 80mm) ×1 Amazon等で入手可

必要な工具と材料

使用するアプリとオンラインサービス

プロジェクト概要

2つの180°サーボと4つのフォトレジスタを用いて、小型ソーラーパネルを最も光量の多い方向へ自動追尾させる実装例です。

モーターは小刻みに動き、パネルを最も明るい方向へ向けようとします。システム有効/無効を示すフルカラーLEDと、モーター有効/無効用の2つのプッシュボタンを備えています。

シリアルポートインターフェース経由で数値コマンドを送信し、モーターを指定位置へ動かすルーチンも用意。機械構造への衝撃を避けるため、即座に目標位置へジャンプするのではなく、滑らかな動きで移動するようプログラムされています。

コード

Q2コード (Arduino) - Version 2.0

// Scott Mangiacotti
// Tucson, Arizona USA
// May 2018
// Q2 Version 2.0

#include <Servo.h>

// 定数
const int GIVE_BACK_TIME = 125;  // ループ間隔(ms)

// 入力ピン
const int I_RED_BUTTON_PIN = 4;    // 赤ボタン(停止)
const int I_GREEN_BUTTON_PIN = 2;  // 緑ボタン(開始)
const int I_LDR1_PIN = A3;         // 左上
const int I_LDR2_PIN = A2;         // 右上
const int I_LDR3_PIN = A1;         // 左下
const int I_LDR4_PIN = A0;         // 右下

// 出力ピン
const int O_RED_LED_PIN = 9;       // RGB LED 赤
const int O_GREEN_LED_PIN = 11;    // RGB LED 緑
const int O_BLUE_LED_PIN = 10;     // RGB LED 青
const int O_TWIST_SERVO_PIN = 5;   // 水平回転サーボ
const int O_TILT_SERVO_PIN = 6;    // 垂直傾斜サーボ

// グローバル変数
bool gRunning = false;              // システム稼働中フラグ
bool gTrackToLightEnabled = false;  // 光追尾モードフラグ
bool gVerboseDiagMode = false;      // 診断モードフラグ

Servo gServoTwist;
int gServoTwistPositionCommand;     // 目標位置
bool gServoTwistMoveIP;             // 移動中フラグ

Servo gServoTilt;
bool gServoTiltMoveIP;
int gServoTiltPositionCommand;

int gLDR1, gLDR2, gLDR3, gLDR4;

void setup() {
  Serial.begin(9600);
  pinMode(I_RED_BUTTON_PIN, INPUT);
  pinMode(I_GREEN_BUTTON_PIN, INPUT);
  pinMode(O_RED_LED_PIN, OUTPUT);
  pinMode(O_GREEN_LED_PIN, OUTPUT);
  pinMode(O_BLUE_LED_PIN, OUTPUT);
  startup();  // 起動シーケンス実行
}

void loop() {
  // シリアルコマンド処理
  if (Serial.available() > 0) {
    int iControlCode = Serial.parseInt();
    processSerialMessage(iControlCode);
  }

  // 緑ボタン:追尾開始
  int iGreenButton = digitalRead(I_GREEN_BUTTON_PIN);
  if (iGreenButton == HIGH && gRunning == false) {
    enableTracking();
    gTrackToLightEnabled = true;
  }

  // 赤ボタン:追尾停止
  int iRedButton = digitalRead(I_RED_BUTTON_PIN);
  if (iRedButton == HIGH && gRunning == true) {
    disableTracking();
  }

  // センサー値読み取り
  readPhotoResistors();
  averageTopTwoSensors();
  averageBottomTwoSensors();

  // サーボ制御
  if (gRunning == true) {
    if (gTrackToLightEnabled == true) {
      trackToLightSensors();  // 光センサーベースの自動追尾
    } else {
      smoothMoveTwist();      // 手動コマンド時の滑らかな移動
      smoothMoveTilt();
    }
  }
  delay(GIVE_BACK_TIME);
}

// 追尾有効化
void enableTracking() {
  gRunning = true;
  gServoTwist.attach(O_TWIST_SERVO_PIN);
  gServoTilt.attach(O_TILT_SERVO_PIN);
  digitalWrite(O_GREEN_LED_PIN, HIGH);
  digitalWrite(O_RED_LED_PIN, LOW);
  Serial.println("servos enabled");
}

// 追尾無効化
void disableTracking() {
  gRunning = false;
  gTrackToLightEnabled = false;
  gServoTwist.detach();
  gServoTilt.detach();
  gServoTwistPositionCommand = gServoTwist.read();
  gServoTwistMoveIP = false;
  gServoTiltPositionCommand = gServoTilt.read();
  gServoTiltMoveIP = false;
  digitalWrite(O_RED_LED_PIN, HIGH);
  digitalWrite(O_GREEN_LED_PIN, LOW);
  Serial.println("servos disabled");
}

// 光センサー値に基づく自動追尾
void trackToLightSensors() {
  float fTop, fBottom, fLeft, fRight;
  int iTwistMoveCommand, iTiltMoveCommand;
  int iMoveAmount = 5;  // 1スキャンあたりの移動角度(GIVE_BACK_TIMEと合わせて速度決定)

  iTwistMoveCommand = gServoTwist.read();
  iTiltMoveCommand = gServoTilt.read();

  fTop = averageTopTwoSensors();
  fBottom = averageBottomTwoSensors();
  fLeft = averageLeftTwoSensors();
  fRight = averageRightTwoSensors();

  // 水平(ツイスト)方向
  if (fLeft > fRight) iTwistMoveCommand += iMoveAmount;
  else if (fRight > fLeft) iTwistMoveCommand -= iMoveAmount;

  // 垂直(チルト)方向
  if (fTop > fBottom) iTiltMoveCommand += iMoveAmount;
  else if (fBottom > fTop) iTiltMoveCommand -= iMoveAmount;

  // 範囲クランプ
  if (iTwistMoveCommand < 0) iTwistMoveCommand = 0;
  if (iTwistMoveCommand > 179) iTwistMoveCommand = 179;
  if (iTiltMoveCommand < 45) iTiltMoveCommand = 45;  // 下限制限
  if (iTiltMoveCommand > 135) iTiltMoveCommand = 135; // 上限制限

  gServoTwist.write(iTwistMoveCommand);
  gServoTilt.write(iTiltMoveCommand);

  if (gVerboseDiagMode) {
    Serial.print("LDRs: "); Serial.print(gLDR1); Serial.print(",");
    Serial.print(gLDR2); Serial.print(","); Serial.print(gLDR3); Serial.print(",");
    Serial.print(gLDR4); Serial.print(" Avgs: "); Serial.print(fTop); Serial.print(",");
    Serial.print(fBottom); Serial.print(","); Serial.print(fLeft); Serial.print(",");
    Serial.print(fRight); Serial.print(" Move: "); Serial.print(iTwistMoveCommand);
    Serial.print(","); Serial.println(iTiltMoveCommand);
  }
}

// フォトレジスタ値読み取り(0-1023)
void readPhotoResistors() {
  gLDR1 = analogRead(I_LDR1_PIN);
  gLDR2 = analogRead(I_LDR2_PIN);
  gLDR3 = analogRead(I_LDR3_PIN);
  gLDR4 = analogRead(I_LDR4_PIN);
}

// 滑らかな移動:ツイストサーボ
void smoothMoveTwist() {
  int iCurrentPos = gServoTwist.read();
  if (iCurrentPos == gServoTwistPositionCommand) {
    gServoTwistMoveIP = false; return;
  }
  gServoTwistMoveIP = true;
  int iMoveAmountPerScan = 1;  // 1スキャンあたり1度ずつ
  int iNewMoveCommand = iCurrentPos;
  if (iCurrentPos < gServoTwistPositionCommand) iNewMoveCommand += iMoveAmountPerScan;
  else iNewMoveCommand -= iMoveAmountPerScan;
  if (iNewMoveCommand < 0) iNewMoveCommand = 0;
  if (iNewMoveCommand > 179) iNewMoveCommand = 179;
  gServoTwist.write(iNewMoveCommand);
  if (gVerboseDiagMode) {
    Serial.print("Twist move: "); Serial.print(iNewMoveCommand);
    Serial.print(" target: "); Serial.println(gServoTwistPositionCommand);
  }
}

// 滑らかな移動:チルトサーボ
void smoothMoveTilt() {
  int iCurrentPos = gServoTilt.read();
  if (iCurrentPos == gServoTiltPositionCommand) {
    gServoTiltMoveIP = false; return;
  }
  gServoTiltMoveIP = true;
  int iMoveAmountPerScan = 1;
  int iNewMoveCommand = iCurrentPos;
  if (iCurrentPos < gServoTiltPositionCommand) iNewMoveCommand += iMoveAmountPerScan;
  else iNewMoveCommand -= iMoveAmountPerScan;
  if (iNewMoveCommand < 0) iNewMoveCommand = 0;
  if (iNewMoveCommand > 179) iNewMoveCommand = 179;
  gServoTilt.write(iNewMoveCommand);
  if (gVerboseDiagMode) {
    Serial.print("Tilt move: "); Serial.print(iNewMoveCommand);
    Serial.print(" target: "); Serial.println(gServoTiltPositionCommand);
  }
}

// 4センサーの平均値計算
float averageTopTwoSensors()    { return (gLDR1 + gLDR2) / 2.0; }
float averageBottomTwoSensors() { return (gLDR3 + gLDR4) / 2.0; }
float averageLeftTwoSensors()   { return (gLDR1 + gLDR3) / 2.0; }
float averageRightTwoSensors()  { return (gLDR2 + gLDR4) / 2.0; }

// シリアルコマンド処理
// 形式:上2桁がコマンドカテゴリ、下3桁が値
void processSerialMessage(int iControlCode) {
  int iControlCommand = iControlCode / 1000;
  int iControlValue = iControlCode % 1000;

  Serial.print("control code: "); Serial.println(iControlCode);
  Serial.print("command: "); Serial.println(iControlCommand);
  Serial.print("value: "); Serial.println(iControlValue);

  // 汎用コマンド (10xxx)
  if (iControlCommand == 10) {
    switch (iControlValue) {
      case 0: gVerboseDiagMode = true; digitalWrite(O_BLUE_LED_PIN, HIGH); Serial.println("diagnostics ON"); break;
      case 1: gVerboseDiagMode = false; digitalWrite(O_BLUE_LED_PIN, LOW); Serial.println("diagnostics OFF"); break;
      case 2: reportProductInfo(); break;
      case 3: digitalWrite(O_RED_LED_PIN, HIGH); Serial.println("red LED ON"); break;
      case 4: digitalWrite(O_RED_LED_PIN, LOW); Serial.println("red LED OFF"); break;
      case 5: digitalWrite(O_GREEN_LED_PIN, HIGH); Serial.println("green LED ON"); break;
      case 6: digitalWrite(O_GREEN_LED_PIN, LOW); Serial.println("green LED OFF"); break;
      case 7: digitalWrite(O_BLUE_LED_PIN, HIGH); Serial.println("blue LED ON"); break;
      case 8: digitalWrite(O_BLUE_LED_PIN, LOW); Serial.println("blue LED OFF"); break;
      case 9: Serial.print("LDR1: "); Serial.println(gLDR1); break;
      case 10: Serial.print("LDR2: "); Serial.println(gLDR2); break;
      case 11: Serial.print("LDR3: "); Serial.println(gLDR3); break;
      case 12: Serial.print("LDR4: "); Serial.println(gLDR4); break;
      case 13: enableTracking(); break;
      case 14: disableTracking(); break;
      case 19: if (gRunning && !gServoTwistMoveIP && !gServoTiltMoveIP) { gServoTwistPositionCommand=90; gServoTiltPositionCommand=90; Serial.println("servos to 90deg"); } break;
      case 21: if (gRunning) { gTrackToLightEnabled=true; Serial.println("light tracking ON"); } break;
      case 22: gTrackToLightEnabled=false; Serial.println("light tracking OFF"); break;
      default: Serial.print("invalid value: "); Serial.println(iControlValue);
    }
  }
  // ツイストサーボ直接指令 (11xxx)
  else if (iControlCommand == 11) {
    if (iControlValue >= 0 && iControlValue <= 179) {
      if (gRunning && !gServoTwistMoveIP) {
        gServoTwistPositionCommand = iControlValue;
        gServoTwistMoveIP = true;
        Serial.print("Twist cmd: "); Serial.println(gServoTwistPositionCommand);
      }
    } else { Serial.print("invalid value: "); Serial.println(iControlValue); }
  }
  // チルトサーボ直接指令 (12xxx)
  else if (iControlCommand == 12) {
    if (iControlValue >= 0 && iControlValue <= 179) {
      if (gRunning && !gServoTiltMoveIP) {
        gServoTiltPositionCommand = iControlValue;
        gServoTiltMoveIP = true;
        Serial.print("Tilt cmd: "); Serial.println(gServoTiltPositionCommand);
      } else {
        Serial.print("tilt not ready: "); Serial.print(gRunning); Serial.print(","); Serial.println(gServoTiltMoveIP);
      }
    } else { Serial.print("invalid value: "); Serial.println(iControlValue); }
  }
  Serial.println("-----");
}

// 起動シーケンス
void startup() {
  int iDelay = 500;
  reportProductInfo(); delay(iDelay);
  processSerialMessage(10003); delay(iDelay);  // 赤LED ON
  processSerialMessage(10004); processSerialMessage(10005); delay(iDelay); // 赤OFF、緑ON
  processSerialMessage(10006); processSerialMessage(10007); delay(iDelay); // 緑OFF、青ON
  processSerialMessage(10008); processSerialMessage(10009); processSerialMessage(10010); processSerialMessage(10011); processSerialMessage(10012); delay(iDelay); // LDR値表示
  enableTracking(); delay(iDelay);
  processSerialMessage(10019); delay(iDelay); // ホームポジション(90,90)
  disableTracking();
  Serial.println("startup sequence completed");
}

// 製品情報出力
void reportProductInfo() {
  Serial.println("q version 2");
  Serial.println("tucson, arizona usa");
  Serial.println("may 2018");
  Serial.print("checksum "); Serial.println("58BA-D969-2F82-08FD-2078-2777-396D-E1AA");
}

3Dモデル・筐体

組み立て詳細図(ZIP)

回路図

Fritzing回路図ファイル

製造プロセス

  1. ソーラー暖房システム:パッシブからアクティブまで、持続可能な住宅暖房ソリューション
  2. 太陽電池:初期実験から現代フォトボルタイクスまで
  3. Arduinoで実現する低コスト・リアルタイム太陽光パネル監視システム
  4. スマートブラインド:Arduinoとソーラー電源で実現する自動光制御
  5. IoT対応ソーラートラッカー:Arduinoによるスマートエネルギー管理
  6. SmartPostBox: IoT対応スマートメールボックスシステム (Sigfox + Arduino MKR Fox 1200)
  7. デュアルアクシスソーラートラッカー:自動/手動切替搭載の高効率太陽追尾システム
  8. ポータブルソーラートラッカースマホ充電器:Arduinoとサーボで太陽追尾
  9. 太陽エネルギー活用ガイド:クリーンで信頼性の高い電力へ
  10. ソーラーパネル品質管理:最適性能を引き出す必須チェックポイント
  11. ソーラーパネル製造の全工程:高品質太陽光発電へのステップバイステップガイド