This shows you the differences between two versions of the page.
pm:prj2024:fstancu:ioan.birjovanu [2024/05/24 07:34] ioan.birjovanu |
pm:prj2024:fstancu:ioan.birjovanu [2024/05/27 11:50] (current) ioan.birjovanu |
||
---|---|---|---|
Line 9: | Line 9: | ||
{{ pm:prj2024:fstancu:robot.jpg?770 }} | {{ pm:prj2024:fstancu:robot.jpg?770 }} | ||
- | Robotul a fost printat 3d folosind o imprimantă Creality CR-10S Pro V2, iar design-ul a fost creat în Autodesk Fusion 360. | + | 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ă ===== | ===== Descriere generală ===== | ||
Line 29: | Line 29: | ||
* **Servomotorul:** Un motor cu rotație fixă, unghiul de rotație al acestuia este controlat de microcontroller folosind un semnal PWM. | * **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. | * **Controllerul brushless:** Acesta controlează viteza de rotație a motorului brushless, primește valoarea PWM de la microcontroller. | ||
+ | |||
+ | <note tip> | ||
+ | Radiocomanda transmite receiverului 6 canale, dintre care noi folosim 4: | ||
+ | * Canalul 1: Joystick-ul drept, axa Ox - folosit pentru a controla virajul robotului. | ||
+ | * Canalul 2: Joystick-ul drept, axa Oy - folosit pentru a controla direcția de deplasare a robotului. | ||
+ | * Canalul 3: Joystick-ul stâng, axa Oy - folosit pentru a controla viteza motorului brushless. | ||
+ | * Canalul 5: Switch-ul A - folosit pentru a activa flamethrowerul. | ||
+ | </note> | ||
+ | |||
+ | {{ pm:prj2024:fstancu:flysky_schematic.jpg?770 }} | ||
+ | |||
+ | Astfel, acționând canalul 5 al teleccomenzii, robotul se va comporta în următorul fel: | ||
+ | |||
+ | {{ pm:prj2024:fstancu:robot_flame.jpg?770 }} | ||
===== Hardware Design ===== | ===== Hardware Design ===== | ||
Line 52: | Line 66: | ||
* Voltmetru OKY4092 | * Voltmetru OKY4092 | ||
- | ===== Software Design ===== | + | <note tip> |
+ | 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ă. | ||
+ | </note> | ||
+ | {{ pm:prj2024:fstancu:robot_3d_model.jpg?770 }} | ||
<note tip> | <note tip> | ||
- | Descrierea codului aplicaţiei (firmware): | + | 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. |
- | * 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 | + | |
</note> | </note> | ||
- | ===== Rezultate Obţinute ===== | + | {{ pm:prj2024:fstancu:screw_switch.jpg?770 }} |
<note tip> | <note tip> | ||
- | Care au fost rezultatele obţinute în urma realizării proiectului vostru. | + | Î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. |
</note> | </note> | ||
- | ===== Concluzii ===== | + | {{ pm:prj2024:fstancu:robot_3d_model_under2.jpg?770 }} |
- | ===== Download ===== | + | <note tip> |
+ | După asamblarea șasiului printat 3D și montarea componentelor electrice în acesta, robotul va arăta astfel: | ||
+ | </note> | ||
- | <note warning> | + | {{ pm:prj2024:fstancu:robot_internals.jpg?770 }} |
- | 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**. | + | ===== Software Design ===== |
+ | |||
+ | <note tip> | ||
+ | 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: | ||
</note> | </note> | ||
- | ===== Jurnal ===== | + | <code> |
+ | #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; | ||
+ | } | ||
+ | } | ||
+ | </code> | ||
<note tip> | <note tip> | ||
- | Puteți avea și o secțiune de jurnal în care să poată urmări asistentul de proiect progresul proiectului. | + | 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: | ||
</note> | </note> | ||
- | ===== Bibliografie/Resurse ===== | + | <code> |
+ | #include <avr/io.h> | ||
+ | #include <avr/interrupt.h> | ||
+ | #include <util/delay.h> | ||
- | <note> | + | #define CLOCK_SPEED 16000000UL |
- | Listă cu documente, datasheet-uri, resurse Internet folosite, eventual grupate pe **Resurse Software** şi **Resurse Hardware**. | + | #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; | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | Î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. | ||
+ | |||
+ | ===== Concluzii ===== | ||
+ | Acest proiect m-a ajutat să învăț despre programarea microprocesoarelor Atmel in AVR C, fără a folosi abstraction layer-ul oferit de Arduino IDE. De asemenea mi-a consolidat aptitudinile de CAD și de design de circuite. | ||
+ | |||
+ | Abilitatea de a citi semnalele dintr-un receiver hobby-grade cu ajutorul unui microprocesor deschide o plajă largă de potențiale proiecte viitoare. | ||
+ | |||
+ | ===== Download ===== | ||
+ | <note tip> | ||
+ | Link Repository Git: [[https://github.com/IonutBirjovanu/AVR-BattleBot|BattleBot]] | ||
</note> | </note> | ||
+ | |||
+ | <note tip> | ||
+ | Arhiva care conține următoarele fisiere: | ||
+ | * 3D - un fisier cu toate piesele 3D folosite în proiectarea robotului. | ||
+ | * Battlebot - implementarea proiectului în Arduino | ||
+ | * media - conținut media cu robotul | ||
+ | * Schematic photos - figurile schematicelor robotului | ||
+ | * src - implementarea proiectului în AVR C | ||
+ | </note> | ||
+ | |||
+ | {{pm:prj2024:fstancu:proiect_pm_birjovanu_ioan.zip|arhiva}} | ||
+ | |||
+ | ===== Bibliografie/Resurse ===== | ||
+ | |||
+ | **Resurse Software** | ||
+ | * [[https://ocw.cs.pub.ro/courses/pm/lab/lab0-2023|Laboratorul 0: GPIO]] | ||
+ | * [[https://ocw.cs.pub.ro/courses/pm/lab/lab2-2023|Laboratorul 2: Întreruperi, Timere]] | ||
+ | * [[https://ocw.cs.pub.ro/courses/pm/lab/lab3-2023-2024|Laboratorul 3: Timere, PWM]] | ||
+ | |||
+ | **Resurse Hardware** | ||
+ | * [[https://howtomechatronics.com/|HowToMechatronics]] | ||
+ | * [[https://electronoobs.com/|ElectroNoobs]] | ||
<html><a class="media mediafile mf_pdf" href="?do=export_pdf">Export to PDF</a></html> | <html><a class="media mediafile mf_pdf" href="?do=export_pdf">Export to PDF</a></html> | ||