This is an old revision of the document!


BattleBot

Introducere

Proiectul este reprezentat de un robot controlat wireless cu o radiocomanda FlySky FS-I6. Funcționalitățile robotului sunt: deplasarea plană(directie de tip tanc), aruncătorul de flăcări(acționat de un switch de pe telecomandă) și controlul vitezei spinnerului cilindric.

Inspirat de seria BattleBots, am dorit să proiectez un battlebot de la 0. Astfel, am ajuns la design-ul curent: Un șasiu simetric(pentru a funcționa și dacă este răsturnat), cu două roți motorizate pentru deplasare și un cilindru asimetric ca armă principală.

robot.jpg

Robotul a fost printat 3D folosind o imprimantă Creality CR-10S Pro V2, iar design-ul a fost creat în Autodesk Fusion 360.

Descriere generală

Schema bloc include șapte componente principale: radiocomanda, receiverul, microcontrollerul Arduino Uno, driverul de motoare pentru direcție, modulul cu releu care pornește transformatorul step-up, servomotorul și controllerul pentru motorul brushless.

Schema de mai jos descrie modul prin care componentele interacționează între ele.

diagrama_robot.jpg

Descriere componente:

  • Radiocomanda: Un sistem radio RC folosit pentru a controla diverse electronice. Transmite inputul la un receiver.
  • Receiverul: Primește datele de la radiocomandă și le codifică într-ul semnal PWM, care poate fi citit de microcontroller.
  • Microcontrollerul: Citește semnalul PWM din receiver, și îl folosește pentru a controla restul componentelor electronice.
  • Driverul de motoare: Primește semnale de la microcontroller și controlează motoarele robotului pentru a aigura deplasarea.
  • Releul: Primește un semnal de la microcontroller pentru a conecta la o sursă de curent un transformator stepUp.
  • Servomotorul: Un motor cu rotație fixă, unghiul de rotație al acestuia este controlat de microcontroller folosind un semnal PWM.
  • Controllerul brushless: Acesta controlează viteza de rotație a motorului brushless, primește valoarea PWM de la microcontroller.

Hardware Design

În imaginea de mai jos se poate observa schema electrică a proiectului.

Listă componente:

  • Radiocomanda FlySky Fs-I6 2.4GHz
  • Receiver radio FlySky Fs-IA6 2.4GHz
  • Placă de dezvoltare Arduino Uno R3 ATmega328P
  • Driver motoare DC L298N
  • Motoare DC cu reductor JGB37
  • Baterie LiPo Gens Ace Soaring 1300mAh 3S
  • Speed Controller 30A BLDC
  • Motor brushless A2212/6T 2200KV
  • Servomotor SG90 9G
  • Modul releu comandat 5V
  • Modul transformator step-up 20kV
  • Voltmetru OKY4092

Modelul 3D al robotului a fost conceput în jurul componentelor hardware. Am început cu o platformă cu găuri de montare pentru un Arduino Uno, pe care am extins-o pentru a adăuga bateria și motoarele pentru deplasarea robotului și pentru armă.

robot_3d_model.jpg

Pentru a porni robotul din exterior fără a-l dezasambla pentru a accesa bateria, am proiectat un întrerupător cu șurub: acesta asigură conexiunea circuitului de alimentare al robotului cu borna pozitivă a bateriei atunci când se înfiletează șurubul.

screw_switch.jpg

Întrerupătorul este accesibil din exterior și este plasat pe panoul central inferior al robotului. Săgeata de pe panoul din imaginea de mai jos semnifică direcția de rotire(spre dreapta) a șurubului pentru a porni robotul. Pentru a opri robotul, întrerupătorul va fi rotit spre stânga.

robot_3d_model_under2.jpg

După asamblarea șasiului printat 3D și montarea componentelor electrice în acesta, robotul va arăta astfel:

robot_internals.jpg

Software Design

După finalizarea etapei de proiectare de hardware, am programat inițial microcontrollerul folosind environmentul Arduino IDE pentru a testa funcționalitățile acestuia și a mă asigura că acesta funcționează corect. Codul inițial în Arduino pentru robot a fost:

#include <Servo.h>

#define RCPinFWD 2
#define RCPinSide 3
#define RCPinSwitch 4

#define Motor1Pin1 9
#define Motor1Pin2 8
#define Motor2Pin1 11
#define Motor2Pin2 10
#define RelayPin 13
#define ServoPin 12

volatile long StartTimeFWD = 0;
volatile long CurrentTimeFWD = 0;
volatile long PulsesFWD = 0;
int PulseWidthFWD = 0;

volatile long StartTimeSide = 0;
volatile long CurrentTimeSide = 0;
volatile long PulsesSide = 0;
int PulseWidthSide = 0;

int SwitchValue;

Servo servo;

void setup() {
  Serial.begin(9600);
  pinMode(RCPinFWD, INPUT_PULLUP);
  pinMode(RCPinSide, INPUT_PULLUP);
  pinMode(RCPinSwitch, INPUT);

  attachInterrupt(digitalPinToInterrupt(RCPinFWD),PulseTimerFWD,CHANGE);
  attachInterrupt(digitalPinToInterrupt(RCPinSide),PulseTimerSide,CHANGE);

  pinMode(Motor1Pin1, OUTPUT);
  pinMode(Motor1Pin2, OUTPUT);
  pinMode(Motor2Pin1, OUTPUT);
  pinMode(Motor2Pin2, OUTPUT);

  servo.attach(ServoPin);
  pinMode(RelayPin, OUTPUT);
}

void loop() {
  if (PulsesFWD < 2000){
    PulseWidthFWD = PulsesFWD;
  } 
  if (PulsesSide < 2000){
    PulseWidthSide = PulsesSide;
  } 
  Serial.print(PulseWidthFWD);
  Serial.print("    ");
  Serial.println(PulseWidthSide);

  if (PulseWidthFWD > 1000 && PulseWidthSide > 1000) {
    if (PulseWidthFWD > 1550) {
      digitalWrite(Motor1Pin1, HIGH);
      digitalWrite(Motor1Pin2, LOW);
    }
    else if (PulseWidthFWD < 1450) {
      digitalWrite(Motor1Pin1, LOW);
      digitalWrite(Motor1Pin2, HIGH);
    }
    else {
      digitalWrite(Motor1Pin1, LOW);
      digitalWrite(Motor1Pin2, LOW);
    }

    if (PulseWidthSide > 1550) {
      digitalWrite(Motor2Pin1, HIGH);
      digitalWrite(Motor2Pin2, LOW);
    }
    else if (PulseWidthSide < 1450) {
      digitalWrite(Motor2Pin1, LOW);
      digitalWrite(Motor2Pin2, HIGH);
    }
    else {
      digitalWrite(Motor2Pin1, LOW);
      digitalWrite(Motor2Pin2, LOW);
    }
  }
  else {
    digitalWrite(Motor2Pin1, LOW);
    digitalWrite(Motor2Pin1, LOW);
    digitalWrite(Motor1Pin1, LOW);
    digitalWrite(Motor1Pin1, LOW);
  }

  SwitchValue = pulseIn(RCPinSwitch, HIGH);
  Serial.print("___");
  Serial.print(SwitchValue);
  Serial.print("___");

  servo.write(SwitchValue);

  if (SwitchValue > 1000)
    digitalWrite(RelayPin, HIGH);
  else
    digitalWrite(RelayPin, LOW);
}

void PulseTimerFWD(){
  CurrentTimeFWD = micros();
  if (CurrentTimeFWD > StartTimeFWD){
    PulsesFWD = CurrentTimeFWD - StartTimeFWD;
    StartTimeFWD = CurrentTimeFWD;
  }
}
void PulseTimerSide(){
  CurrentTimeSide = micros();
  if (CurrentTimeSide > StartTimeSide){
    PulsesSide = CurrentTimeSide - StartTimeSide;
    StartTimeSide = CurrentTimeSide;
  }
}

Codul de Arduino a rămas referința de bază pentru design-ul codului in AVR C pentru microcontrollerul AtMega328P. Acesta este codul pentru microcontroller scris în AVR C:

#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>

#define CLOCK_SPEED 16000000UL
#define BAUD 9600
#define MYUBRR CLOCK_SPEED/16/BAUD-1

#define RCPinFWD PD2
#define RCPinSide PD3
#define RCPinSwitch PD4

#define Motor1Pin1 PB1
#define Motor1Pin2 PB0
#define Motor2Pin1 PB3
#define Motor2Pin2 PB2
#define RelayPin PB5
#define ServoPin PB4

volatile long StartTimeFWD = 0;
volatile long CurrentTimeFWD = 0;
volatile long PulsesFWD = 0;
int PulseWidthFWD = 0;

volatile long StartTimeSide = 0;
volatile long CurrentTimeSide = 0;
volatile long PulsesSide = 0;
int PulseWidthSide = 0;

int SwitchValue;

void setup();
void loop();
void PulseTimerFWD();
void PulseTimerSide();
void init_timer1();
void init_timer0();
long pulseIn(uint8_t pin, uint8_t state);
long micros();

volatile unsigned long timer0_overflow_count = 0;

int main(void) {
    setup();
    while (1) {
        loop();
    }
}

void setup() {
    // Set input and output pins
    DDRD &= ~((1 << RCPinFWD) | (1 << RCPinSide) | (1 << RCPinSwitch)); // Set as input
    PORTD |= (1 << RCPinFWD) | (1 << RCPinSide); // Enable pull-up resistors

    DDRB |= (1 << Motor1Pin1) | (1 << Motor1Pin2) | (1 << Motor2Pin1) | (1 << Motor2Pin2) | (1 << RelayPin) | (1 << ServoPin);

    // Set up external interrupts for FWD and Side
    EICRA |= (1 << ISC00) | (1 << ISC10); // Any logical change on INT0 and INT1 generates an interrupt
    EIMSK |= (1 << INT0) | (1 << INT1); // Enable INT0 and INT1

    init_timer1();
    init_timer0();

    sei(); // Enable global interrupts
}

void loop() {
    if (PulsesFWD < 2000) {
        PulseWidthFWD = PulsesFWD;
    }
    if (PulsesSide < 2000) {
        PulseWidthSide = PulsesSide;
    }

    if (PulseWidthFWD > 1000 && PulseWidthSide > 1000) {
        if (PulseWidthFWD > 1550) {
            PORTB |= (1 << Motor1Pin1);
            PORTB &= ~(1 << Motor1Pin2);
        } else if (PulseWidthFWD < 1450) {
            PORTB &= ~(1 << Motor1Pin1);
            PORTB |= (1 << Motor1Pin2);
        } else {
            PORTB &= ~((1 << Motor1Pin1) | (1 << Motor1Pin2));
        }

        if (PulseWidthSide > 1550) {
            PORTB |= (1 << Motor2Pin1);
            PORTB &= ~(1 << Motor2Pin2);
        } else if (PulseWidthSide < 1450) {
            PORTB &= ~(1 << Motor2Pin1);
            PORTB |= (1 << Motor2Pin2);
        } else {
            PORTB &= ~((1 << Motor2Pin1) | (1 << Motor2Pin2));
        }
    } else {
        PORTB &= ~((1 << Motor1Pin1) | (1 << Motor1Pin2) | (1 << Motor2Pin1) | (1 << Motor2Pin2));
    }

    SwitchValue = pulseIn(RCPinSwitch, HIGH);

    OCR1A = SwitchValue; // Set PWM for servo

    if (SwitchValue > 1000) {
        PORTB |= (1 << RelayPin);
    } else {
        PORTB &= ~(1 << RelayPin);
    }
}

ISR(INT0_vect) {
    PulseTimerFWD();
}

ISR(INT1_vect) {
    PulseTimerSide();
}

void PulseTimerFWD() {
    CurrentTimeFWD = micros();
    if (CurrentTimeFWD > StartTimeFWD) {
        PulsesFWD = CurrentTimeFWD - StartTimeFWD;
        StartTimeFWD = CurrentTimeFWD;
    }
}

void PulseTimerSide() {
    CurrentTimeSide = micros();
    if (CurrentTimeSide > StartTimeSide) {
        PulsesSide = CurrentTimeSide - StartTimeSide;
        StartTimeSide = CurrentTimeSide;
    }
}

void init_timer1() {
    // Timer1 setup for servo control
    TCCR1A = (1 << WGM11) | (1 << COM1A1);
    TCCR1B = (1 << WGM13) | (1 << WGM12) | (1 << CS11);
    ICR1 = 39999; // 50Hz frequency
}

void init_timer0() {
    // Timer0 setup for micros
    TCCR0A = 0;
    TCCR0B = (1 << CS01) | (1 << CS00); // Prescaler 64
    TIMSK0 = (1 << TOIE0); // Enable overflow interrupt
}

ISR(TIMER0_OVF_vect) {
    timer0_overflow_count++;
}

long micros() {
    unsigned long m;
    uint8_t oldSREG = SREG;

    cli();
    m = timer0_overflow_count;
    uint8_t t = TCNT0;

    if ((TIFR0 & (1 << TOV0)) && (t < 255)) {
        m++;
    }

    SREG = oldSREG;
    return ((m << 8) + t) * (64 / (F_CPU / 1000000L));
}

long pulseIn(uint8_t pin, uint8_t state) {
    uint8_t bit = digitalPinToBitMask(pin);
    uint8_t port = digitalPinToPort(pin);
    uint8_t stateMask = (state ? bit : 0);
    unsigned long width = 0;

    // Wait for any previous pulse to end
    while ((*portInputRegister(port) & bit) == stateMask);

    // Wait for the pulse to start
    while ((*portInputRegister(port) & bit) != stateMask);

    // Wait for the pulse to stop
    while ((*portInputRegister(port) & bit) == stateMask) {
        width++;
        if (width >= 1000000) {
            return 0;
        }
    }

    return width;
}

În codul de AVR, folosim hardware interrupts pe pinii de input pentru a identifica semnalele PWM de la receiverul radio, iar cu timerul 0 le măsurăm valoarea. Vom folosi apoi acele valori pentru a activa driverul de motoare DC pentru direcție, împreună cu servomotorul si controllerul de viteză pentru motorul brushless. Pentru a trimite un semnal PWM corespunzător valorii primite de la receiver la speed controller și servomotor, vom folosi timerul 1.

Descrierea codului aplicaţiei (firmware):

  • mediu de dezvoltare (if any) (e.g. AVR Studio, CodeVisionAVR)
  • librării şi surse 3rd-party (e.g. Procyon AVRlib)
  • algoritmi şi structuri pe care plănuiţi să le implementaţi
  • (etapa 3) surse şi funcţii implementate

Rezultate Obţinute

Care au fost rezultatele obţinute în urma realizării proiectului vostru.

Concluzii

Download

O arhivă (sau mai multe dacă este cazul) cu fişierele obţinute în urma realizării proiectului: surse, scheme, etc. Un fişier README, un ChangeLog, un script de compilare şi copiere automată pe uC crează întotdeauna o impresie bună ;-).

Fişierele se încarcă pe wiki folosind facilitatea Add Images or other files. Namespace-ul în care se încarcă fişierele este de tipul :pm:prj20??:c? sau :pm:prj20??:c?:nume_student (dacă este cazul). Exemplu: Dumitru Alin, 331CC → :pm:prj2009:cc:dumitru_alin.

Jurnal

Puteți avea și o secțiune de jurnal în care să poată urmări asistentul de proiect progresul proiectului.

Bibliografie/Resurse

Listă cu documente, datasheet-uri, resurse Internet folosite, eventual grupate pe Resurse Software şi Resurse Hardware.

Export to PDF

pm/prj2024/fstancu/ioan.birjovanu.1716532190.txt.gz · Last modified: 2024/05/24 09:29 by ioan.birjovanu
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0