Робот на радио-управлении

Тема в разделе "Глядите, что я сделал", создана пользователем vladimir_s84, 27 июл 2015.

  1. vladimir_s84

    vladimir_s84 Нерд

    Спасибо!
     
  2. vladimir_s84

    vladimir_s84 Нерд

    Всем привет после длительного перерыва. Прошу прощения за то, что долго не выкладывал материал. У меня родился сын и это радостное событие временно поставило на паузу мои поделки). Так вот, возвращаясь к нашим баранам...

    Мне удалось наладить протокол обмена данными. Также я сделал пульт дистанционного управления с дисплеем, на котором отображается информация с параметрами машинки, а также информация параметров самого пульта. Машинка получилась универсальной. В ней встроен не один модуль для связи, что позволяет мне в дальнейшем написать ПО под iOS, под Android, а также написать собственное ПО для ПК. Для меня этот мини-проект - источник дополнительных знаний и опытов. Также планируется добавить (он уже приобретен) модуль голосового управления.

    Чуть позже подробно опишу и покажу все, что я сделал на данный момент.
    car.jpg
     
    alp69 нравится это.
  3. vvr

    vvr Инженерище

    сын это хорошо!
    поздравляю!
     
  4. vladimir_s84

    vladimir_s84 Нерд

    Спасибо!
     
  5. vladimir_s84

    vladimir_s84 Нерд

    С момента последнего поста, помимо рождения сына, у меня произошло в жизни еще одно серьезное изменение, я мигрировал в другую страну. Времени это и сил отняло очень много, поэтому я пропал на некоторое время.

    Не смотря на это, я все же перевез с собой мою поделку). Вот выкладываю ниже видео того, что получилось. Если кому-то интересно детально поговорить на счет поделки или кода (его я выложу ниже) - пишите тут или мне на почту v_shcherbukhin@icloud.com
     
  6. vladimir_s84

    vladimir_s84 Нерд

    И так, подведем черту.

    Робот "SHCH-ROBOT V.1"

    1. Собираем самого робота.

    Детали:
    - Arduino Uno V3;
    - Motor Shield (2 канала, 2 А);
    - Power Shield;
    - Multiservo Shield (расширенная китайская версия);
    - 4 микромотора с передаточными шестернями, формирующими редуктор и D-образным шпинделем;
    - Bluetooth модуль (адаптированный для работы с iPhone);
    - Bluetooth модуль для работы с Android и ПК;
    - 1 радио-модуль APC220;
    - Wi-fi камера от квадрокоптера;
    - 2 сервопривода Tower Pro MG90;
    - Отсек на 6 аккумуляторов по 1.2В;
    - Кнопка включения/выключения робота;
    - Колодка с 8-мью переключателями (вкл/выкл) для дополнительных опций;
    - Звуковой зумер;

    Этапы сборки:
    2015-07-30 17-50-19.JPG 2015-07-30 17-15-52.JPG 2015-07-30 17-16-01.JPG 2015-07-30 18-17-38.JPG 2015-07-30 18-19-10.JPG 2015-08-03 15-55-47.JPG 2015-08-03 15-55-55.JPG 2015-08-03 16-27-57.JPG 2015-08-03 17-32-40.JPG 2015-08-03 17-32-47.JPG
     
    Последнее редактирование: 16 май 2016
  7. vladimir_s84

    vladimir_s84 Нерд

  8. vladimir_s84

    vladimir_s84 Нерд

    2. Собираем пульт ДУ

    Детали:
    - Arduino Nano V3;
    - 1 радиомодуль APC220;
    - 4 кнопки управления;
    - джойстик;
    - LCD дисплей (два цвета);
    - кнопка влючения/выключения пульта;
    - кнопка вкл/выкл для подключения к ПК;
    - корпус от джойстика SonyPlayStation;
    - самодельный держатель для телефона;
    - аккумулятор крона на 9В.
    - разъем для подключения зарядного устройства;

    Зарядка пульта происходит при подключения к нему самодельного зарядного устройства, переделанного из зярядного для кроны.

    Этапы сборки:
    2015-10-22 01-05-58.JPG 2015-10-22 01-06-05.JPG 2015-10-22 01-06-12.JPG 2015-10-22 04-25-10.JPG 2015-10-22 04-25-00.JPG 2015-10-22 04-25-21.JPG 2015-10-22 04-25-37.JPG 2015-10-22 05-01-28.JPG
     
  9. vladimir_s84

    vladimir_s84 Нерд

    КОД РОБОТА

    Код (C++):
    #include <EasyTransfer.h>
    #include <Wire.h>
    #include <Servo.h>

    #define SPEED_1      5
    #define DIR_1        4

    #define SPEED_2      6
    #define DIR_2        7

    Servo servoX;
    Servo servoY;

    const int SERVO_X_PIN = 10;
    const int SERVO_Y_PIN = 11;
    const int MAX_POS = 1023;   // VRx и VRy выдают значения от 0 до 1023
    const int MAX_ANGLE = 180;
    int s_yVal;
    int s_xVal;
    int last_change_x;
    int data_change_x;
    int last_change_y;
    int data_change_y;
    int buzerPin = 12;

    EasyTransfer ET;
    int light = 9;

    struct SEND_DATA_STRUCTURE {
      byte control_signal;
      byte command_car;
      int yVal;
      int xVal;
      boolean isClicked;
      int data_battery;
    };

    SEND_DATA_STRUCTURE mydata;

    const int TIMEOUT_TIME_MS = 1500;
    unsigned long lastPilotSymbolTime;

    long previousMillis = 0;
    long interval = 8;

    int analoginput = A1;

    float vout = 0.0;
    float vin = 0.0;
    int value = 0;
    float R1 = 996000.0;
    float R2 = 99400.0;

    float empty = 5.8;
    float main_low = 6.15;
    float main_middle = 6.5;
    float main_high = 6.85;
    float main_full = 7.2;

    enum States
    {
    WAITING,
    CHECK,
    RUNNING,
    BATTERY,
    SENDING,
    TIMEOUT
    };

    States state;
    States onWait();
    States onCheck();
    States onRun();
    States onBattery();
    States onSend();
    States onTimeout();

    void setup()
    {
      for (int i = 4; i < 8; i++)
      {
        pinMode(i, OUTPUT);
      }
     
      pinMode(light, OUTPUT);
      pinMode(buzerPin, OUTPUT);
      digitalWrite(buzerPin, HIGH);

      Serial.begin(57600);
      ET.begin(details(mydata), &Serial);
      lastPilotSymbolTime = millis();
      servoX.attach(SERVO_X_PIN);
      servoY.attach(SERVO_Y_PIN);
      servoX.write(95);
      servoY.write(50);
      last_change_x = 0;
      last_change_y = 0;
      mydata.xVal = 511;
      mydata.yVal = 511;
    }

    void loop()
    {
      switch (state)
      {
        case WAITING:
          state = onWait();
          break;
        case CHECK:
          state = onCheck();
          break;
        case RUNNING:
          state = onRun();
          break;
        case BATTERY:
          state = onBattery();
          break;
        case SENDING:
          state = onSend();
          break;
        case TIMEOUT:
          state = onTimeout();
          break;
      }
      xy_servo();
    }

    States onWait()
    {
      if(ET.receiveData())
      {
        return CHECK;
      }
      if (lastPilotSymbolTime && (millis() - lastPilotSymbolTime > TIMEOUT_TIME_MS))
      {
        return TIMEOUT;
      }
      return WAITING;
    }

    States onCheck()
    {
    if(mydata.control_signal == 32)
      {
        digitalWrite(9, HIGH);
        lastPilotSymbolTime = millis();
        return RUNNING;
      }
      return WAITING;
    }

    States onRun()
    {  
    switch (mydata.command_car)
      {
        case 34:
          analogWrite(SPEED_1, 230);
          digitalWrite(DIR_1, LOW);
          analogWrite(SPEED_2, 230);
          digitalWrite(DIR_2, LOW);
          break;
        case 35:
          analogWrite(SPEED_1, 230);
          digitalWrite(DIR_1, HIGH);
          analogWrite(SPEED_2, 230);
          digitalWrite(DIR_2, HIGH);
          break;
        case 36:
          analogWrite(SPEED_1, 230);
          digitalWrite(DIR_1, HIGH);
          analogWrite(SPEED_2, 230);
          digitalWrite(DIR_2, LOW);
          break;
        case 37:
          analogWrite(SPEED_1, 230);
          digitalWrite(DIR_1, LOW);
          analogWrite(SPEED_2, 230);
          digitalWrite(DIR_2, HIGH);
          break;
        case 38:
          analogWrite(SPEED_1, 0);
          analogWrite(SPEED_2, 0);
          break;
      }
      return BATTERY;
    }

    States onBattery()
    {
       value = analogRead(analoginput);
       vout = (value*4.9) / 1024.0;
       vin = vout/(R2/(R1+R2));
       vin*=10;
       vin = floor(vin+0.5);
       vin/=10;
       if (vin < 0.99){
          vin = 0.0;
        }
       else if (vin > 7.2){
          vin = 7.2;
        };

       if (vin <= empty)
       {
        mydata.data_battery = 0;
       }

       else if (vin > empty & vin <= main_low)
       {
        mydata.data_battery = 25;
       }

       else if (vin > main_low & vin <= main_middle)
       {
        mydata.data_battery = 50;
       }

       else if (vin > main_middle & vin <= main_high)
       {
        mydata.data_battery = 75;
       }

       else if (vin > main_high & vin <= main_full)
       {
        mydata.data_battery = 100;
       }
       else if (vin > main_full)
       {
        mydata.data_battery = 100;
       }
    return SENDING;
    }

    States onSend()
    {
         mydata.control_signal = 0x21; //33
         mydata.data_battery;
         ET.sendData();
         return WAITING;
    }

    States onTimeout()
    {
      digitalWrite(9, LOW);
      analogWrite(SPEED_1, 0);
      analogWrite(SPEED_2, 0);
      digitalWrite(buzerPin, HIGH);

      if (Serial.available())
      {
        lastPilotSymbolTime = millis();
        return WAITING;
      }
      return TIMEOUT;
    }
    void xy_servo()
    {
      unsigned long currentMillis = millis();

      if(currentMillis - previousMillis > interval){
       if(mydata.xVal < 100)
       {
        s_xVal = servoX.read();
        if (s_xVal == 170)
        {
          servoX.write(170);
        }
        else
        {
          data_change_x = s_xVal + last_change_x;
          servoX.write(data_change_x);
          data_change_x = 0;
          last_change_x = 0;
        }
        last_change_x++;
       }
       if(mydata.xVal > 900)
       {
        s_xVal = servoX.read();
        if (s_xVal == 20)
        {
          servoX.write(20);
        }
        else
        {
          data_change_x = s_xVal - last_change_x;
          servoX.write(data_change_x);
          data_change_x = 0;
          last_change_x = 0;
        }
        last_change_x++;
       }
       if(mydata.xVal > 100 && mydata.xVal < 900)
       {
        s_xVal = servoX.read();
        servoX.write(s_xVal);
        last_change_x = 0;
       }
       //////////////////////////////////////////
       if(mydata.yVal > 900)
       {
        s_yVal = servoY.read();
        if (s_yVal == 70)
        {
          servoY.write(70);
        }
        else
        {
          data_change_y = s_yVal + last_change_y;
          servoY.write(data_change_y);
          data_change_y = 0;
          last_change_y = 0;
        }
        last_change_y++;
       }
       if(mydata.yVal < 100)
       {
        s_yVal = servoY.read();
        if (s_yVal == 20)
        {
          servoY.write(20);
        }
        else
        {
          data_change_y = s_yVal - last_change_y;
          servoY.write(data_change_y);
          data_change_y = 0;
          last_change_y = 0;
        }
        last_change_y++;
       }
       if(mydata.yVal > 100 && mydata.yVal < 900)
       {
        s_yVal = servoY.read();
        servoY.write(s_yVal);
        last_change_y = 0;
       }
       if (mydata.isClicked){
        digitalWrite(buzerPin, LOW);
        servoX.write(95);
        servoY.write(50);
        last_change_x = 0;
        last_change_y = 0;
       }
       else{
        digitalWrite(buzerPin, HIGH);
       }
        previousMillis = currentMillis;
      }
    }
     
  10. vladimir_s84

    vladimir_s84 Нерд

    КОД ПУЛЬТА

    Код (C++):
    #include <EasyTransfer.h>
    #include <OLED_I2C.h>

    #define BUTTON_LEFT 4
    #define BUTTON_RIGHT 5
    #define BUTTON_UP 6
    #define BUTTON_DOWN 7

    const int POS_Y_PIN = A2;
    const int POS_X_PIN = A3;
    const int BUTTON_SERVO = 8;

    OLED  myOLED(SDA, SCL);
    EasyTransfer ET;

    struct SEND_DATA_STRUCTURE{
      byte control_signal;  
      byte command_car ;
      int yVal;
      int xVal;
      boolean isClicked;
      int data_battery;
    };

    SEND_DATA_STRUCTURE mydata;

    const int TIMEOUT_TIME_MS = 1500;
    unsigned long lastPilotSymbolTime;
    unsigned long previousMillis = 0;
    long timeout = 1500;

    extern uint8_t SmallFont[];
    extern uint8_t MediumNumbers[];
    extern uint8_t batteryh0_16[];
    extern uint8_t batteryh1_16[];
    extern uint8_t batteryh2_16[];
    extern uint8_t batteryh3_16[];
    extern uint8_t batteryh4_16[];
    extern uint8_t wireless3_16[];
    extern uint8_t wireless0_16[];
    extern uint8_t arrow_down_16[];
    extern uint8_t arrow_right_16[];
    extern uint8_t arrow_left_16[];
    extern uint8_t arrow_up_16[];
    extern uint8_t batteryhc_16[];
    extern uint8_t clearBitmap[];

    int analoginput = A1;

    int x = 0;
    int y = 0;

    uint8_t* img_batt;
    uint8_t* img_batt2;
    char* val_batt;
    char* val_batt2;

    float vout = 0.0;
    float vin = 0.0;
    int value = 0;
    float R1 = 996000.0;
    float R2 = 99400.0;

    float empty = 6.0;
    float main_low = 6.75;
    float main_middle = 7.5;
    float main_high = 8.25;
    float main_full = 9.0;

    enum States
    {
    SENDING,
    WAITING,
    CHECK,
    BATTERY_MAIN,
    BATTERY_CAR,
    CHARGE,
    RUNNING,
    SERVO,
    TIMEOUT
    };

    States state;
    States onSend();
    States onWait();
    States onCheck();
    States onBattery_main();
    States onBattery_car();
    States onCharge();
    States onRun();
    States onServo();
    States onTimeout();

    void setup()
    {
      Serial.begin(57600);
      ET.begin(details(mydata), &Serial);

      myOLED.begin();
      myOLED.setFont(SmallFont);
      myOLED.clrScr();
      myOLED.update();
       
      pinMode(BUTTON_LEFT, INPUT);
      pinMode(BUTTON_RIGHT, INPUT);
      pinMode(BUTTON_UP, INPUT);
      pinMode(BUTTON_DOWN, INPUT);

      mydata.xVal = 511;
      mydata.yVal = 511;
      lastPilotSymbolTime = millis();
    }

    void loop()
    {
      switch (state)
      {
        case SENDING:
          state = onSend();
          break;
        case WAITING:
          state = onWait();
          break;
        case CHECK:
          state = onCheck();
          break;
        case BATTERY_MAIN:
          state = onBattery_main();
          break;
        case BATTERY_CAR:
          state = onBattery_car();
          break;
        case CHARGE:
          state = onCharge();
          break;
         case RUNNING:
          state = onRun();
          break;
         case SERVO:
          state = onServo();
          break;
        case TIMEOUT:
          state = onTimeout();
          break;
      }
    }

    States onSend()
    {
         mydata.control_signal = 0x20; //32
         ET.sendData();
         return WAITING;
    }

    States onWait()
    {
      if(ET.receiveData()){
        return CHECK;
        }
      if (lastPilotSymbolTime && (millis() - lastPilotSymbolTime > TIMEOUT_TIME_MS))
      {
        myOLED.clrScr();
        myOLED.print("OFF", x+20, y+8);
        myOLED.drawBitmap(0, 0, wireless0_16, 16, 16);
        myOLED.update();
        return TIMEOUT;
      }
      return WAITING;
    }

    States onCheck()
    {
         if(mydata.control_signal == 33){
          mydata.control_signal = 0x20; //32
          myOLED.print("ON ", x+20, y+8);
          myOLED.drawBitmap(0, 0, wireless3_16, 16, 16);
          myOLED.update();
          lastPilotSymbolTime = millis();
          return BATTERY_MAIN;
        }
      return SENDING;
    }

    States onBattery_main()
    {
       value = analogRead(analoginput);
       vout = (value*4.9) / 1024.0;
       vin = vout/(R2/(R1+R2));
       vin*=10;
       vin = floor(vin+0.5);
       vin/=10;
       if (vin < 0.99){
          vin = 0.0;
        }
       else if (vin > 9.0){
          vin = 9.0;
        };
       
       if (vin <= empty)
       {
        img_batt = batteryh0_16;
        val_batt = "  0";
       }
       
       else if (vin > empty & vin <= main_low)
       {
        img_batt = batteryh1_16;
        val_batt = " 25";
       }
     
       else if (vin > main_low & vin <= main_middle)
       {
        img_batt = batteryh2_16;
        val_batt = " 50";
       }
     
       else if (vin > main_middle & vin <= main_high)
       {
        img_batt = batteryh3_16;
        val_batt = " 75";
       }
     
       else if (vin > main_high & vin <= main_full)
       {
        img_batt = batteryh4_16;
        val_batt = "100";
       }
     
       else if (vin > main_full)
       {
        img_batt = batteryh4_16;
        val_batt = "100";
       }
       
        myOLED.drawBitmap(110, 4, img_batt, 16, 16);
        myOLED.print(val_batt, x+80, y+8);
        myOLED.print("%", x+100, y+8);
        myOLED.update();
       
       
      return BATTERY_CAR;
    }

    States onBattery_car()
    {
        if(mydata.data_battery == 0){
        img_batt2 = batteryh0_16;
        val_batt2 = "  0";
        }
        else if(mydata.data_battery == 25){
        img_batt2 = batteryh1_16;
        val_batt2 = " 25";
        }
        else if(mydata.data_battery == 50){
        img_batt2 = batteryh2_16;
        val_batt2 = " 50";
        }
        else if(mydata.data_battery == 75){
        img_batt2 = batteryh3_16;
        val_batt2 = " 75";
        }
        else if(mydata.data_battery == 100){
        img_batt2 = batteryh4_16;
        val_batt2 = "100";
        }
        myOLED.drawBitmap(110, 50, img_batt2, 16, 16);
        myOLED.print(val_batt2, x+80, y+54);
        myOLED.print("%", x+100, y+54);
        myOLED.update();
       
      return CHARGE;
    }

    States onCharge()
    {
      if(mydata.data_battery == 0 || vin <= empty)
      {
      myOLED.clrCircle(63,33,10);
      myOLED.drawBitmap(54, 25, clearBitmap, 16, 16);
      myOLED.drawBitmap(54, 25, batteryhc_16, 16, 16);
      myOLED.print("recharge", 0, 54);
      myOLED.update();
      return SENDING;
      }
      if(mydata.data_battery > 0 & vin > empty)
      {
        return RUNNING;
      }
      return CHARGE;
    }

    States onRun()
    {
       if (digitalRead(BUTTON_LEFT) == HIGH)
       {
        mydata.command_car = 0x24; //36
        myOLED.clrCircle(63,33,10);
        myOLED.print("Left     ", x, y+54);
        myOLED.drawBitmap(54, 25, arrow_left_16, 16, 16);
        myOLED.update();
       }
       else if (digitalRead(BUTTON_RIGHT) == HIGH)
       {
        mydata.command_car = 0x25; //37
        myOLED.clrCircle(63,33,10);
        myOLED.print("Right    ", x, y+54);
        myOLED.drawBitmap(54, 25, arrow_right_16, 16, 16);
        myOLED.update();
       }
       else if (digitalRead(BUTTON_UP) == HIGH)
       {
        mydata.command_car = 0x22; //34
        myOLED.clrCircle(63,33,10);
        myOLED.print("Up       ", x, y+54);
        myOLED.drawBitmap(54, 25, arrow_up_16, 16, 16);
        myOLED.update();
       }
       else if (digitalRead(BUTTON_DOWN) == HIGH)
       {
        mydata.command_car = 0x23; //35
        myOLED.clrCircle(63,33,10);
        myOLED.print("Down     ", x, y+54);
        myOLED.drawBitmap(54, 25, arrow_down_16, 16, 16);
        myOLED.update();
       }
       else
       {
        mydata.command_car = 0x26; //38

        myOLED.drawBitmap(54, 25, clearBitmap, 16, 16);
        myOLED.print("STOP     ", x, y+54);
        myOLED.drawCircle(63,33,10);
        myOLED.print("P", x+60, y+30);
        myOLED.update();
       }
     
      return SERVO;
    }

    States onServo()
    {
    mydata.isClicked = digitalRead(BUTTON_SERVO);
    mydata.isClicked = mydata.isClicked ^ 1;

    mydata.yVal = analogRead(POS_Y_PIN);
    mydata.xVal = analogRead(POS_X_PIN);
      lastPilotSymbolTime = millis();
    return SENDING;
    }

    States onTimeout()
    {
      unsigned long currentMillis = millis();
      if(currentMillis - previousMillis >= timeout){
      mydata.control_signal = 0x20; //32
      ET.sendData();

      value = analogRead(analoginput);
       vout = (value*4.9) / 1024.0;
       vin = vout/(R2/(R1+R2));
       vin*=10;
       vin = floor(vin+0.5);
       vin/=10;
       if (vin < 0.99){
          vin = 0.0;
        }
       else if (vin > 9.0){
          vin = 9.0;
        };
       
       if (vin <= empty)
       {
        img_batt = batteryh0_16;
        val_batt = "  0";
       }
       
       else if (vin > empty & vin <= main_low)
       {
        img_batt = batteryh1_16;
        val_batt = " 25";
       }
     
       else if (vin > main_low & vin <= main_middle)
       {
        img_batt = batteryh2_16;
        val_batt = " 50";
       }
     
       else if (vin > main_middle & vin <= main_high)
       {
        img_batt = batteryh3_16;
        val_batt = " 75";
       }
     
       else if (vin > main_high & vin <= main_full)
       {
        img_batt = batteryh4_16;
        val_batt = "100";
       }
     
       else if (vin > main_full)
       {
        img_batt = batteryh4_16;
        val_batt = "100";
       }
       
        myOLED.drawBitmap(110, 4, img_batt, 16, 16);
        myOLED.print(val_batt, x+80, y+8);
        myOLED.print("%", x+100, y+8);
        myOLED.print("CAR Disconnect", x+23, y+35);
        myOLED.update();
     
      previousMillis = currentMillis;
      }

      if (Serial.available())
      {
        lastPilotSymbolTime = millis();
        myOLED.print("              ", x+23, y+35);
        myOLED.update();
        return SENDING;
      }
      return TIMEOUT;
    }
     
  11. vladimir_s84

    vladimir_s84 Нерд

  12. vladimir_s84

    vladimir_s84 Нерд

    Вот и все. Сейчас занят умным домом, потом обязательно выложу в следующей статье. Постараюсь выложить очень подробно свою работу с подробными комментариями.
     
  13. vladimir_s84

    vladimir_s84 Нерд

    Приветствуются комментарии к коду. Это мой первый опыт. Уверен, можно что-то оптимизировать.
     
  14. vladimir_s84

    vladimir_s84 Нерд

    Ну и сам тест-драйв)