sketchの説明。
(この項、2015年6月6日追記)

コピーしてArduino IDEに貼り付けて下さい。
スケッチは素人なので完全ではありません。間違いやアドバイスがあればお知らせ下さい。
/*このスケッチを丸ごとコピーするだけでは再現できません。
特に倒立は、フレームの形状や使用部品及び部品配置によってバランスが異なり、
安定させるには機体に合わせた設定値の変更が必要です。
------------------------------------------------------------------------------------*/

#include "Servo.h"//サーボ用ライブラリー
#include "IRremote.h"//赤外線リモコン用ライブラリー
//ArduinoとMPU6050とのI2C通信用に以下3つのライブラリー追加する。
#include "Wire.h"
#include "I2Cdev.h"
#include "MPU6050.h"

Servo elv_servo;//エレベーターアーム用サーボの宣言
Servo spa_servo;//サポートアーム用サーボの宣言
//MPU6050の設定値
MPU6050 accelgyro;
int16_t ax, ay, az;
int16_t gx, gy, gz;
#define Gry_offset 0
#define Gyr_Gain 0.00763358
#define pi 3.14159
/* 重要“Angle_offset”は倒立時の前後バランスの設定(この設定が狂ってると上手く倒立出来ません)
センサーの取り付けは、振動などで動かないように固定します。*/
#define Angle_offset 86.5

/*ギアモーターによっては左右の回転数が異なる場合があります。真直ぐに前進するように
RM_offsetとLM_offsetの値を変えて回転差を補正する必要があります。*/
#define RM_offset 10
#define LM_offset 19

//IRリモコン関連の設定
#define MAX_TIME 150
long lastPressTime = 0;
int state = HIGH;
int recv_pin = 2;//D2:IR受信モジュールの入力端子を指定
IRrecv irrecv(recv_pin);
decode_results results;

//PID制御関連
float kp, ki, kd;
float r_angle, f_angle, omega;
float Turn_Speed = 0, Turn_Speed_K = 0;
float Run_Speed = 0, Run_Speed_K = 0;
float LOutput, ROutput;
unsigned long preTime = 0;
float SampleTime = 0.08;
unsigned long lastTime;
float Input, Output;
float errSum, dErr, error, lastErr;
float kidt;
int timeChange;

//超音波センサーの入出力端子設定
int trigger_right = 3;//D3
int echo_right = 4;//D4
int trigger_front = 7;//D7
int echo_front = 8;//D8
int trigger_left = 9;//D9
int echo_left = 10;//D10
int modeLED1 = 13;//D13:入出力端子不足でLEDが1個しか使えない。
int RMA = 16;//A2:右モータ-ドライバーA端子へ
int RMB = 17;//A3:右モータ-ドライバーB端子へ
int REN = 6;//右モータ-ドライバーイネーブル端子へ
int LMA = 14;//A0:左モータ-ドライバーA端子へ
int LMB = 15;//A1:左モータ-ドライバーB端子へ
int LEN = 5;//左モータ-ドライバーイネーブル端子へ
char contcommand;
int modecontrol = 0;//初期モード設定
int power = 0;//初期モード設定
int pos1 = 2;//ELVサーボの初期値設定
int pos2 = 90;//SPAサーボの初期値設定

void setup() { //初期設定
  Wire.begin();
  accelgyro.initialize();
  Serial.begin(115200);
  elv_servo.attach(11); //D11
  elv_servo.write(pos1);
  spa_servo.attach(12); //D12
  spa_servo.write(pos2);
  irrecv.enableIRIn();
  pinMode(RMA, OUTPUT);
  pinMode(RMB, OUTPUT);
  pinMode(LMA, OUTPUT);
  pinMode(LMB, OUTPUT);
  pinMode(REN, OUTPUT);
  pinMode(LEN, OUTPUT);
  pinMode(trigger_front, OUTPUT);
  pinMode(echo_front, INPUT);
  pinMode(trigger_left, OUTPUT);
  pinMode(echo_left, INPUT);
  pinMode(trigger_right, OUTPUT);
  pinMode(echo_right, INPUT);
  pinMode(modeLED1, OUTPUT);
  digitalWrite(modeLED1, HIGH);
}

void loop() {
  if (irrecv.decode(&results)) { // 赤外線リモコンのボタンが放された時の処理
    INV_remote();
    irrecv.resume();
    if (state == HIGH) {
      state = LOW;
    }
    lastPressTime = millis();
    irrecv.resume(); 
  }
  if (state == LOW && millis() - lastPressTime > MAX_TIME) {
    state = HIGH;
    Run_Speed_K = 0;//リモコンボタンが放された時0に戻す
    Turn_Speed = 0;//リモコンボタンが放された時0に戻す

/*IRリモコンの送信機について
使用するIRリモコンはボタンが押されてる間、同じコードが連続出力するタイプで有ること。
その理由は、倒立運転時に前進/後進ボタンが押されると出力されたコード数を加減算して
前後進の速度制御を行うからです。
使用するIRリモコンにより、出力コードが異なるので先にコード確認用ソフトで使用ボタンの
コードを調べて書き換えます。以下のコードはシャープのテレビ用リモコンの例です。*/

    if (results.value == 0xF1480148) { //"テレビ用赤外線リモコンの青ボタン" mode0(停止)
      digitalWrite( modeLED1, HIGH);
      Stop();
      modecontrol = 0;
      power = 0;
    }
    if (results.value == 0xF1488140) { //"テレビ用赤外線リモコンの赤ボタンに設定" mode1(自律走行開始)
      digitalWrite( modeLED1, HIGH); //倒立運転インジケータLED OFF 
      modecontrol = 0;
      power = 1;
    }
    if (results.value == 0xF148414C) { //"テレビ用赤外線リモコンの緑ボタンに設定" mode2(手動運転開始)
      digitalWrite( modeLED1, HIGH); //倒立運転インジケータLED OFF 
      modecontrol = 1;
      Stop();
    }
    if (results.value == 0xF148C144) { //"テレビ用赤外線リモコンの黄ボタンに設定" mode3(倒立自動運転開始)
      digitalWrite( modeLED1, LOW ); //倒立運転インジケータLED ON
      INV_start(); //起立アームの起動
      delay(2000); //安定時間の設定
      INV_end1(); //起立補助アームの格納 
    }
  }

  if ( power == 1) { // 超音波のセンサー受信距離による自律運転
// mode1 各超音波センサーの制御
    long duration_front, duration_left, duration_right, right, left, front;
    digitalWrite(trigger_front, LOW);
    delayMicroseconds(2);
    digitalWrite(trigger_front, HIGH);
    delayMicroseconds(5);
    digitalWrite(trigger_front, LOW);
    duration_front = pulseIn(echo_front, HIGH);
    front = duration_front / 29 / 2;
    Serial.print("  front="); Serial.println(front);

    digitalWrite(trigger_left, LOW);
    delayMicroseconds(2);
    digitalWrite(trigger_left, HIGH);
    delayMicroseconds(5);
    digitalWrite(trigger_left, LOW);
    duration_left = pulseIn(echo_left, HIGH);
    left = duration_left / 29 / 2;
    Serial.print("  left="); Serial.println(left);

    digitalWrite(trigger_right, LOW);
    delayMicroseconds(2);
    digitalWrite(trigger_right, HIGH);
    delayMicroseconds(5);
    digitalWrite(trigger_right, LOW);
    duration_right = pulseIn(echo_right, HIGH);
    right = duration_right / 29 / 2;
    Serial.print("  right="); Serial.println(right);
    
//以下の障害物回避制御は簡単な方法ですが、工夫によってはもっと複雑に制御が可能です。
    if (front > 20 && right > 15 && left > 15 ) { //前方の及び左右の設定距離内に障害物が無い場合。
      forward();//前進
    }
    if (front < 20 && right < left) { //前方設定距離内に障害物があり、かつ左右を比較し、右側が障害物に近い場合の処理。
      back(); //後進
      delay(600); //backの距離は、この値を変えて調整を行います。
      turnleft(); //右旋回     
      delay(60); //右旋回時間の調整
      Stop; //停止
    }
    if (front < 20 && right > left) { //今度は、右側が障害物に近い場合の処理。
      back(); //後進
      delay(600); //backの距離は、この値を変えて調整を行います。
      turnright(); //右旋回 
      delay(60); //旋回時間の調整
      Stop; //停止
    }
    if (right < 10) { //右側が設定値に達した場合の処理。
      turnleft(); //左旋回
      delay(60); //旋回時間の調整
      Stop; //停止
    }
    if (left < 10) { //左側が設定値に達した場合の処理。
      turnright(); //右旋回
      delay(60); //右旋回旋回時間の調整
      Stop; //停止
    }
    else {
      Stop; //停止
    }
  }

  if (modecontrol == 1) { // mode2 手動運転時の赤外線リモコン操作
    switch (results.value) {
      case 0xF148EA81: //テレビ用赤外線リモコンの十字キーの“↑キー”に設定
        forward();
        break;
      case 0xF148EB80: //テレビ用赤外線リモコンの十字キーの“←キー”に設定
        s_left();
        break;
      case 0xF1484A8B: //テレビ用赤外線リモコンの十字キー中心の“決定ボタン”に設定
        Stop();
        break;
      case 0xF1481B8F: //テレビ用赤外線リモコンの十字キーの“→キー”に設定
        s_right();
        break;
      case 0xF1480481: //テレビ用赤外線リモコンの十字キーの“↓キー”に設定
        back();
        break;
      case 0xF148AF80: //テレビ用赤外線リモコンの“終了ボタン”に設定
        modecontrol = 0;
        Stop();
        break;
      default:
        ;
    }
    delay(500);
  }

  if (modecontrol == 2 ) { // mode3 倒立制御
    accelgyro.getMotion6(&ax, &ay, &az, &gx, &gy, &gz);
    r_angle = (atan2(ay, az) * 180 / pi + Angle_offset); 
    omega = Gyr_Gain * (gx + Gry_offset);
    if (abs(r_angle) < 45) { //前後の倒立角度が45度以内の場合の処理。
      myPID();
      MotorControl();
    }
    else { //前後の倒立角度が45度以上に傾いた場合は左右モーターの出力を0にする。
      analogWrite(REN, 0);
      analogWrite(LEN, 0);
    }
    if (pos1 >= 30) { //倒立成立時の処理:ELVサーボの設定値が30以上の場合の処理。
      delay (100);
      spa_servo.write(pos2 = 90); //設定値は機体に応じて設定が必要
      elv_servo.write(pos1 = 2); //設定値は機体に応じて設定が必要
      delay (50);
      kidt = 0.1; //ki値を戻す
    }
  }
}

void INV_start() { //起立アーム操作
  spa_servo.write(pos2 = 115.6); //設定値は機体に応じてカット&トライの設定が必要
  for (pos1 = 2; pos1 < 111.2; pos1 += 1) { //設定値は機体に応じてカット&トライの設定が必要
    elv_servo.write(pos1);
    delay(10);
  }
}

void INV_end1() { //アーム格納操作1
  spa_servo.write(pos2 = 114.0); //設定値は機体に応じてカット&トライの設定変更が必要
  elv_servo.write(pos1 = 110); //設定値は機体に応じてカット&トライの設定変更が必要
  kidt  = 0.02; //倒立起動時のみki値を下げます。
  modecontrol = 2;
}

void INV_end2() { //アーム格納操作2
  spa_servo.write(pos2 = 113); //設定値は機体に応じてカット&トライの設定変更が必要
  elv_servo.write(pos1 = 110); //設定値は機体に応じてカット&トライの設変更定が必要
  delay (100);
  for (pos1 = 110; pos1 > 2; pos1 -= 1) { //設定値は機体に応じてカット&トライの設定が必要
    elv_servo.write(pos1);
    delay (20);
  }
  delay (100);
  spa_servo.write(pos2 = 90);
  digitalWrite( modeLED1, LOW );
  modecontrol = 0;
  Stop();
  power = 0;
}

void myPID() { //倒立PID制御
  kp = 20; //要調整 設定値は機体に応じてカット&トライの設定変更が必要
  ki = kidt;
  kd = 800; //要調整 設定値は機体に応じてカット&トライの設定変更が必要

  unsigned long now = millis();
  float dt = (now - preTime) / 500.0;
  preTime = now;
  float K = 0.8;
  float A = K / (K + dt);
  f_angle = A * (f_angle + omega * dt) + (1 - A) * r_angle; 

  timeChange = (now - lastTime);
  if (timeChange >= SampleTime) {
    Input = f_angle;
    error = Input;
    errSum += error * timeChange;
    dErr = (error - lastErr) / timeChange;
    Output = kp * error + ki * errSum + kd * dErr;
    LOutput = Output + Run_Speed + Turn_Speed;
    ROutput = Output + Run_Speed - Turn_Speed;
    lastErr = error;
    lastTime = now;
  }
}

void INV_remote() { //mode3 倒立運転時のリモコン操作
  switch (results.value) {
    case 0XF148EA81: // forward:前進
      Serial.print("FORWARD"); Serial.println(Run_Speed);
      Run_Speed_K = -15;
      Run_Speed = Run_Speed + Run_Speed_K; //十字キーの“↑キー”を押してる間、-15を加算し続けます。
      break;
    case 0XF1480481: // back:後進
      Serial.print("BACK"); Serial.println(Run_Speed);
      Run_Speed_K = 15;
      Run_Speed = Run_Speed + Run_Speed_K; //十字キーの“↓キー”を押してる間、15を加算し続けます。
      break;
    case 0XF148EB80: // left:左旋回
      Turn_Speed = 20;
      break;
    case 0XF1481B8F: // right:右旋回
      Turn_Speed = -20;
      break;
    default:
      Run_Speed_K = 0;
      Turn_Speed = 0;
  }
}

void MotorControl() { //倒立運転時のモーター制御
  if (LOutput > 0) {
    digitalWrite(RMA, LOW);
    digitalWrite(RMB, HIGH);
  }
  else if (LOutput < 0) {
    digitalWrite(RMA, HIGH);
    digitalWrite(RMB, LOW);
  }
  else {
    digitalWrite(RMA, LOW);
    digitalWrite(RMB, LOW);
  }
  if (ROutput > 0) {
    digitalWrite(LMA, LOW);
    digitalWrite(LMB, HIGH);
  }
  else if (ROutput < 0) {
    digitalWrite(LMA, HIGH);
    digitalWrite(LMB, LOW);
  }
  else {
    digitalWrite(LMA, LOW);
    digitalWrite(LMB, LOW);
  }
  analogWrite(REN, min(255, abs(LOutput)));
  analogWrite(LEN, min(255, abs(ROutput)));
}

//超音波運転及び手動運転時のサーボモータ制御
void forward() { //前進
  digitalWrite(RMA, LOW);
  digitalWrite(RMB, HIGH);
  digitalWrite(LMA, LOW);
  digitalWrite(LMB, HIGH);
  analogWrite(REN, 60 + RM_offset);
  analogWrite(LEN, 60 + LM_offset);
  delay(500);
}

void Stop() { //停止
  digitalWrite(RMA, HIGH);
  digitalWrite(RMB, HIGH);
  digitalWrite(LMA, HIGH);
  digitalWrite(LMB, HIGH);
  analogWrite(REN, 255);
  analogWrite(LEN, 255);
}

void turnright() { //右旋回(右超信地旋回)
  digitalWrite(RMA, HIGH);
  digitalWrite(RMB, LOW);
  digitalWrite(LMA, LOW);
  digitalWrite(LMB, HIGH);
  analogWrite(REN, 60 + RM_offset);
  analogWrite(LEN, 60 + LM_offset);
}

void turnleft() { //左旋回(左超信地旋回)
  digitalWrite(RMA, LOW);
  digitalWrite(RMB, HIGH);
  digitalWrite(LMA, HIGH);
  digitalWrite(LMB, LOW);
  analogWrite(REN, 60 + RM_offset);
  analogWrite(LEN, 60 + LM_offset);
}

void s_right() { //右ショート旋回
  digitalWrite(RMA, LOW);
  digitalWrite(RMB, HIGH);
  digitalWrite(LMA, LOW);
  digitalWrite(LMB, HIGH);
  analogWrite(REN, 40 + RM_offset);
  analogWrite(LEN, 65 + LM_offset);
}

void s_left() { //左ショート旋回
  digitalWrite(RMA, LOW);
  digitalWrite(RMB, HIGH);
  digitalWrite(LMA, LOW);
  digitalWrite(LMB, HIGH);
  analogWrite(REN, 65 + LM_offset);
  analogWrite(LEN, 45 + RM_offset);
}

void back() { //後進
  digitalWrite(RMA, HIGH);
  digitalWrite(RMB, LOW);
  digitalWrite(LMA, HIGH);
  digitalWrite(LMB, LOW);
/*ギアモーターによっては正転時と反転時に回転数が異なる場合があります。真直ぐに後進するように
REN&LENのPWM値を変えて回転差を補正する必要があります。*/
  analogWrite(REN, 64.5 + RM_offset);
  analogWrite(LEN, 60 + LM_offset);
}

参考にさせて頂いた公開ページ
倒立ロボット:www.instructables.com/id/InstaBots-Upright-Rover/
3sonar自律走行ロボット:http://letsmakerobots.com/node/37746

元のページヘ戻る




inserted by FC2 system