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 leftservo; //左ドライブ用サーボの宣言
Servo rightservo; //右ドライブ用サーボの宣言
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 -2.5

//IRリモコン関連の設定
#define MAX_TIME 150 // max ms between codes
long lastPressTime = 0;
int state = HIGH;
int receiver = 4; //D2:IR受信モジュールの入力端子を指定
IRrecv irrecv(receiver);
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;//0.08
unsigned long lastTime;
float Input, Output;
float errSum, dErr, error, lastErr;
int timeChange;
float kidt;

//超音波センサーの入出力端子設定
int trigger_front = 12;
int echo_front = 13;
int trigger_left = 10;
int echo_left = 11;
int trigger_right = 6;
int echo_right = 7;
//mode表示LEDの設定
int modeLED1 = 14;
int modeLED2 = 15;
int modeLED3 = 16;
int powerLED = 17;

char contcommand;
int modecontrol = 0; //初期モード設定
int power = 0; //初期モード設定
int pos1 = 175; //ELVサーボの初期値設定
int pos2 = 160; //SPAサーボの初期値設定

void setup() { //初期設定
  Wire.begin();
  accelgyro.initialize();
  Serial.begin(115200);
  irrecv.enableIRIn();
  elv_servo.attach(3);
  elv_servo.write(pos1);
  spa_servo.attach(5);
  spa_servo.write(pos2);
  leftservo.attach(8);
  leftservo.write(90);
  rightservo.attach(9);
  rightservo.write(90);
  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);
  pinMode(modeLED2, OUTPUT);
  pinMode(modeLED3, OUTPUT);
  pinMode(powerLED, OUTPUT);
  digitalWrite(modeLED1, LOW);
  digitalWrite(modeLED2, HIGH);
  digitalWrite(modeLED3, HIGH);
  digitalWrite(powerLED, HIGH);
}

void loop() {
  if (irrecv.decode(&results)) { // 赤外線リモコンのボタンが放された時の処理
    INVremote();
    irrecv.resume(); // Receive the next value
    if (state == HIGH) {
      state = LOW;  // Button pressed, so set state to LOW
    }
    lastPressTime = millis();
    irrecv.resume(); // Receive the next value
  }
  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(停止)
      Stop();
      modecontrol = 0;
      power = 0;
      digitalWrite (powerLED, HIGH);
      digitalWrite (modeLED1, LOW);
      digitalWrite (modeLED2, HIGH);
      digitalWrite (modeLED3, HIGH);
    }
    if (results.value == 0xF1488140) { //"テレビ用赤外線リモコンの赤ボタンに設定" mode1(自律走行開始)
      modecontrol = 0;
      power = 1;
      digitalWrite (powerLED, LOW);
      digitalWrite (modeLED1, LOW);
      digitalWrite (modeLED2, HIGH);
      digitalWrite (modeLED3, HIGH);
    }
    if (results.value == 0xF148414C) { //"テレビ用赤外線リモコンの緑ボタンに設定" mode2(手動運転開始)
      modecontrol = 1;
      //digitalWrite (powerLED, HIGH);
      digitalWrite (modeLED1, HIGH);
      digitalWrite (modeLED2, LOW);
      digitalWrite (modeLED3, HIGH);
      Stop();
    }
    if (results.value == 0xF148C144) { //"テレビ用赤外線リモコンの黄ボタンに設定" mode3(倒立自動運転開始)
      INV_start(); //起立アームの起動
      delay(2000); //安定時間の設定
      INV_end1(); //起立補助アームの格納 
      digitalWrite (modeLED1, HIGH);
      digitalWrite (modeLED2, HIGH);
      digitalWrite (modeLED3, LOW);
    }
  }

  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 > 15 && right > 10 && left > 10 ) { //前方の及び左右の設定距離内に障害物が無い場合。
      forward(); //後進
    }
    if (front < 15 && right < left) { //前方の及び左右の設定距離内に障害物が無い場合。
      back();
      turnleft(); //左旋回
    }
    if (front < 15 && right > left) { //今度は、右側が障害物に近い場合の処理。
      back();
      turnright(); //右旋回 
    }
    if (right < 10) { //左ショート旋回
      s_left();
    }
    if (left < 10) { //右ショート旋回 
      s_right();
    }
  }

  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();
        digitalWrite (modeLED1, HIGH);
        digitalWrite (modeLED2, LOW);
        digitalWrite (modeLED3, HIGH);
        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();
      ServoControl();
    }
    else { //前後の倒立角度が45度以上に傾いた場合はサーボ出力を中立(90)にする。
      leftservo.write(90);
      rightservo.write(90);
    }
    if (pos1 <= 50) { //倒立成立時の処理:ELVサーボの設定値が50以下の場合の処理。
      delay (50);
      spa_servo.write(pos2 = 160); //設定値は機体に応じて設定が必要
      elv_servo.write(pos1 = 175); //設定値は機体に応じて設定が必要
      delay (130);
      kidt = 0.12;
    }
  }
}

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

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

void INV_end2() { //アーム格納操作2
  spa_servo.write(pos2 = 143.0); //設定値は機体に応じてカット&トライの設定変更が必要
  elv_servo.write(pos1 = 46.5); //設定値は機体に応じてカット&トライの設定変更が必要
  for (pos1 = 45.0; pos1 < 175; pos1 += 1) { //設定値は機体に応じてカット&トライの設定が必要
    elv_servo.write(pos1);
    delay (15);
  }
  delay (100);
  spa_servo.write(pos2 = 160);
  leftservo.write(90);
  rightservo.write(90);
  digitalWrite (modeLED2, LOW);
  digitalWrite (modeLED3, HIGH);
  modecontrol = 0;
}

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

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

  unsigned long now = millis();
  float dt = (now - preTime) / 1000.0;
  preTime = now;
  float K = 0.8;//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 ServoControl() { // mode3 倒立運転時のサーボモーター制御
  LOutput = constrain(LOutput, -110, 100);
  ROutput = constrain(ROutput, -110, 100);
  if (LOutput > 0) { // 左サーボモーターの制御
    leftservo.write(LOutput = map(LOutput, 0, -100, 90, 110));//(LOutput, 0, 255, 90, 180)
  }
  else if (LOutput < 0) {
    leftservo.write(LOutput = map(LOutput, 0, 100, 90, 70));//(LOutput, 0, -255, 90, 0)
  }
  else {
    leftservo.write(90);
  }
  if (ROutput > 0) { // 右サーボモーターの制御
    rightservo.write(ROutput = map(ROutput, 0, -100, 90, 65));//(ROutput, 0, 255, 90, 0)
  }
  else if (ROutput < 0) {
    rightservo.write(ROutput = map(ROutput, 0, 100, 90, 115));//(ROutput, 0, -255, 90, 180)
  }
  else {
    rightservo.write(90);
  }
}

//超音波運転及び手動運転時のサーボモータ制御
void forward() { //前進
  leftservo.write(79.5);//80
  rightservo.write(100);//100
}

void Stop() { //停止
  leftservo.write(90);
  rightservo.write(90);
}

void turnright() { //右旋回(右超信地旋回)
  leftservo.write(80);//0
  rightservo.write(80);//0
  delay(300);
}

void turnleft() { //左旋回(左超信地旋回)
  leftservo.write(100);//180
  rightservo.write(100);//180
  delay(300);
}

void s_right() { //右ショート旋回
  leftservo.write(75 );//75
  rightservo.write(100);//100
  delay(100);
}

void s_left() { //左ショート旋回
  leftservo.write(80);//80
  rightservo.write(105);//105
  delay(100);
}

void back() { //後進
  leftservo.write(97);//100
  rightservo.write(80);//80
  delay(600);
}

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

元のページヘ戻る




inserted by FC2 system