Cercetari teoretice si aplicative privind realizarea unui quadcopter Coordonator stiintific : S.L. Dr. Ing. Besnea Daniel Absolvent Lupu Stefan -… [306184]

Universitatea POLITEHNICA din Bucuresti

Facultatea de Inginerie Mecanica si Mecatronica

Departamentul Mecanica de Precizie si Mecatronica

LUCRARE DE LICENTA

Cercetari teoretice si aplicative privind realizarea unui quadcopter

Coordonator stiintific : S.L. Dr. Ing. Besnea Daniel

Absolvent: [anonimizat]

2018-2019

Cap. 1 – [anonimizat] s-a dezvoltat considerabil pe plan mondial si i [anonimizat] a informatiilor din mediul extern.

Tema acestei lucrari se bazeaza pe o aplicatie de tip UAV ( unmanned aircraft vehicle ), [anonimizat] a sistemelor de supraveghere video de la sol .

[anonimizat], elementele constructive ale acesteia; se va realiza o tehnologie de executie cu calcule si se va proiecta aplicatia 3D pentru a se putea realiza simularea corespunzatoare inainte de introducerea in functiune a aplicatiei . In etapa finala o sa se construiasca aplicatia pe baza componentelor achizitionate si se va face o [anonimizat].

Scopul final al lucrarii este acela de a mentine platforma stabila in mediul aerian urmand sa poata efectua operatiile necesare proiectarii ei: detectarea, urmarirea, recuperarea si culegerea de informatii din mediul extern.

Cap. 2 – Stadiul actual al cunoasterii in domeniul sistemelor aeriene fara pilot uman

2.1 Generalitati

Drona este un dispozitiv de tip UAV (Unmanned Aerial Vehicle) capabil sa zboare fara sa fie pilotat de o persoana, care a fost folosit initial doar in scopuri militare pentru recunoasterea unor obiective si bombardarea unor tinte strategice.

La fel cum s-a [anonimizat] a [anonimizat], au ajuns sa fie produse si in scopuri comerciale. [anonimizat] a [anonimizat] o noua “jucarie” [anonimizat].

[anonimizat], [anonimizat] o gama diversificata de persoane.

Dronele civile sunt controlate wireless prin intermediul unei telecomenzi sau a [anonimizat], [anonimizat].

UAV-[anonimizat]:

Fotografie si inregistrari video;

Geomapping;

Agricultura;

Networking;

Monitorizarea schimbarilor climatice;

Constructii etc.

2.2 Evolutia sistemelor aeriene fara pilot uman

Pentru a [anonimizat], trebuie sa aruncam o privire asupra istoricului . [anonimizat] a [anonimizat] a fi si astazi. [anonimizat].

Inventia zborului fara pilot uman a fost utilizata incepand cu anul 1849 cand baloanele incarcate cu bombe au fost folosite de catre armata austriaca in timpul unui atac asupra Venetiei. Totusi, daca e sa ne rezumam la o abordare mai conventionala, prima lupta a unui aparat de zbor fara pilot uman nu dateaza mai devreme de perioada anilor 1890, cand Otto Lilienthal, un pionier al aviatiei germane a experimentat cu planoare fara echipaj uman pentru a testa modelele de zbor usoare, cu aripi fixe. Acesta dorea ca aparatele sale sa fie functionale pentru a se putea introduce in mediul aerian cu scopul evitarii accidentarii bravilor piloti. Rezultatul a fost unul pozitiv.

Pierpont Langley a experimentat aparate de zbor cu motor cu aburi. Interesant este faptul ca experimentele sale foloseau un sistem de catapultare. Aparatul lui Langley denumit “ Aerodrom ” a reusit sa zboare ceva mai putin de o mila de-a lungul raului Potomac in cadrul unui proiect mai larg privind un avion cu echipaj uman. Desi proiectul a fost ulterior abandonat, zborul de testare a ramas intiparit in istoria aviatiei, intrucat a fost primul zbor de succes pe distanta lunga folosindu-se un aparat de zbor autopropulsat. Evenimentul se intampla cu mai multi ani inaintea faimosului zbor al fratilor Wright.

Fig. 2.2.a: Aerodromul lui P.Langley care a intreprins cu success primul zbor din lume pe distanta lunga al unui aparat de zbor fara pilot actionat de un motor. A fost lansat prin catapultare in 1896 si a zburat aproape o mila peste raul Potomac,Virginia,SUA. A fost actionat de un motor cu aburi cu un singur cilindru care punea in miscare 2 elici propulsoare prin transmisie cu roti dintate.

Zborul istoric al fratilor Wright a aratat omenirii ca zborul controlat putea fi realizat utilizand aripi din panza intinsa pe o rama pentru a coordona ruliul (miscare alternativa de inclinare a unei nave sau a unei aeronave in jurul axei sale longitudinale ) aparatului de zbor, si aceasta realizare a avut un puternic impact in dezvoltarea tehnica din industria aviatica. Succesul fratilor Wright a constituit catalizatorul care a modelat stiinta aviatica in ceea ce este ea astazi.

Nu mult timp dupa aceea, Lawrence Sperry cunoscut ca si “ bunicul pilotului automat ” a folosit giroscopul pe care compania familiei sale il inventase pentru a construi primii piloti automati care sa ii ajute pe piloti sa isi controleze avioanele, reducand in mare masura munca pilotului uman.

Mai tarziu se remarca profesorul Archibald Low care a dezvoltat sistemele de ghidare prin radio pentru a controla avionele de la distanta, culminand cu controlul de la distanta al zborului Ruston Proctor AT ( tinta aeriana ) in 1917. Trebuie de asemenea notat ca Nikola Tesla demonstrase deja capacitatea de a controla de la distanta o ambarcatiune cu multi ani mai inainte, in 1898.

In anul 1922 este momentul in care primul aparat de zbor de tip quadcopter a zburat cu succes. Cunoscut sub denumirea de “ Elicopterul De Bothezat “, a folosit rotoare dispuse intr-o structura de forma X. S-au efectuat aproximativ 100 de zboruri si s-a atins o altitudine maxima de 5m. Totusi, proiectul nu a continuat, deoarece necesita complexitati mecanice.

Fig. 2.2.b: Elicopterul proiectat de Gheorghe De Bothezat, coborand pe campul McCook dupa ce a ramas in aer timp de 2 minute si 45 secunde

La scurt timp s-au adus imbunatatiri minore si s-au fabricat 12.000 de asemenea aparate. Chiar si in prezent, aceste constructii raman o utilizare militara majora fiind dezvoltate si mult mai autonome.

Exemple de drone precum TDR-1 sau faimosul B-17 au fost utilizate in Al Doilea Razboi Mondial pentru a putea livra bombe pe teritoriul inamic, aparatul de zbor fiind manevrat de la sol.

Aceste constructii se remarca si in anul 1960 cand Compania Ryan Aeronautics din California producea dronele-tinta aeriene, si care se fabrica si in prezent. Acestea au avut ca si scop supravegherea, recunoasterea si evaluarea distrugerilor rezultate in urma unei batalii.

In cele din urma se ajunge si in anul 2003 cand acestea incep sa fie construite si utilizate de civili in scopuri personale, urmand sa se aduca imbunatatiri pe partea hardware si a design-ului.

Putin mai tarziu, in anul 2009 a inceput proiectul de pilot automat ArduPilot al carui scop a fost sa faca drone mai accesibile maselor. Initial s-au bazat pe platforma electronica Arduino si au utilizat baterii termoelectrice pentru a mentine aparatul de zbor stabil, urmand ca mai apoi ArduPilot sa utilizeze senzori automati, cum ar fi accelerometer, giroscoape si intr-un final sa fie primii pe piata mondiala care sa controleze si sa distribuie toate vehiculele fara echipaj uman, fie ca sunt aparate de zbor cu aripi fixe, multirotori, barci sau vehicule de teren.

2.3 Echipamente existente

Dronele sunt categorisite fie dupa configuratie, fie dupa forma. Configuratia poate fi impartita in 2 grupe: aripi fixe, configuratie care este similara cu cea a aeroplanelor simple, unde ridicarea este produsa de miscarea aripilor inainte prin aer si autogir, configuratie in care aripile ( rotoarele ) se invart pentru a produce ridicarea, precum in cazul unui elicopter.

2.3.1 Aripi fixe

Aparatul de zbor cu aripi fixe foloseste o sursa de propulsie ( de regula un motor electric sau o elice ) pentru a genera o forta ascensionala pe aripi care sa permita realizarea zborului.

Cand un astfel de aparat zboara, unghiul sau ( altitudinea de zbor ) se poate schimba de-alungul a 3 axe:

axa de inclinare laterala ( tagaj Y-Y ), ceea ce presupune ca aparatul se misca in sus sau in jos;

axa verticala sau de viraj ( giratie Z-Z ), care se refera la schimbarea directiei aparatului de zbor ;

axa longitudinala ( ruliu X-X ), care implica balansarea aparatului de zbor la stanga si la dreapta.

Fig. 2.3.1.a: cele 3 axe ale unui aparat de zbor

Pentru a controla un aparat de zbor cu aripi fixe, aripile si sectiunea cozii dispun de niste arii mici ca niste flaps-uri, numite suprafete de control.

Exista 3 suprafete de control:

eleronii, care controleaza ruliul aparatului de zbor;

carma sau directia care controleaza deviatia de la drum;

profundorii care controleaza inclinarea longitudinala.

Fig. 2.3.1.b: cele 3 suprafete de control ale unui aparat de zbor

Indiferent de aranjamentul aripilor unui aparat de zbor, drone sau aripi fixe, un alt criteriu de clasificare a proiectului este locul de amplasare al motorului. Cel mai adesea, motoarele sunt localizate in partea stanga a aparatului de zbor, si astfel sunt clasificate dupa configuratie ca “ propulsori ” – motorul impinge aparatul de zbor inainte. Configuratiile motoarelor propulsoare sunt:

Configuratiile motoarelor care trag ( sau “ tractori ” ) au motorul montat cu elicea in fata de unde trage dupa sine aparatul de zbor inainte. Aceasta este o configuratie mai conventionala care poate fi mai eficienta decat cea cu propulsori, deoarece curentul de aer care loveste elicea nu este perturbat de corpul aparatului de zbor. Cu toate acestea, pentru ca elicea este de obicei montata in fata oricarei camere care se afla la bord, se poate crea o situatie neplacuta din cauza efectului “ rolling shutter ” ( distorsiune cauzata de metoda de fotografiere “ obturator oscilant ” ), care face ca imaginea sa apara in fotografie ca un model liniar ciudat. Cu toate acestea, pentru camerele orientate in jos ( ca cele de la UAV-urile folosite pentru cartografiere ), aceasta nu este o problema.

2.3.2 Aripi zburatoare

Aripile zburatoare ( avion tip “ numai aripa ” ) sunt platforme de drone destul de populare, deoarece ele au putine componente si sunt mai usor de transportat. Sunt de asemenea mai eficiente si pot inainta prin curentii de aer mai usor decat alte platforme de drone.

O aripa zburatoare are 2 suprafete de control, asa ca are nevoie de doar 2 servomecanisme, in comparatie cu aparatele de zbor mai conventionale care au nevoie de 4. Suprafetele de control de pe aripile zburatoare se numesc elevoini, deoarece combina atat functiile unui elevator ( inclina longitudinal aeroplanul in sus si in jos ) cat si pe cele ale unui eleron ( misca aparatul de zbor la stanga si la dreapta ).

Aparatul de control al zborului trebuie sa stie sa actioneze atat cat trebuie pentru ca fiecare suprafata de control sa se miste corespunzator pentru a indeplini actiunea dorita.

Aripile zburatoare sunt excelente pentru drone, deoarece ramane foarte mult spatiu pentru a monta tot echipamentul iar motoarele si elicile sunt amplasate in spate in afara campului de luat vederi al camerei.

Principalul lor defect este totusi faptul ca centrul de greutate este mai sensibil decat la alte configuratii si aparate de zbor.

Fig. 2.3.2: aparat de zbor cu aripi zburatoare ( Sursa imagine: Compania A.F.T Design S.R.L )

2.3.3 Aparate de zbor cu rotor

Termenul aparate de zbor cu rotor se utilizeaza pentru a defini aparatul de zbor prevazut cu un rotor care se invarte in aer si care genereaza suficienta forta de ascensiune pentru ca aparatul sa poata zbura.

Acestea se clasifica in doua categorii principale :

Elicoptere conventionale

Multicoptere

Multicopterele se clasifica in functie de numarul de motoare pe care le utilizeaza aparatul – de exemplu, in cadrul acestui proiect, quadcopterul care are 4 motoare.

Elicoptere conventionale

Acestea sunt capabile sa asigure controlul prin schimbarea inclinatiei longitudinale si a unghiului paletelor de rotor. La baza este un mecanism complex numit disc pendular. Majoritatea elicopterelor utilizeaza un singur disc pendular mare care creeaza un efort de torsiune in directia opusa rotatiei. De aceea rotorul de pe coada aparatului este necesar pentru a echilibra devierea de la directia de deplasare si a mentine elicopterul pe traiectoria corecta. Acest lucru este in consecinta considerat ca fiind un punct slab care face proiectul usor ineficient, deoarece o parte din energie este folosita pur si simplu pentru a mentine elicopterul pe aceeasi directie in loc sa contribuie la generarea ascensiunii.

Principalul dezavantaj al elicopterelor conventionale este acela ca sunt mai periculoase din cauza lamelelor rotoarelor mai largi si mai grele. Mai mult decat atat, complexitatea lor inseamna ca sunt de obicei mai fragile intr-un accident decat platformele dronelor obisnuite precum quadcopterele. Cu toate aceste au avantaje multiple cum ar fi gradul mare de stabilitate in conditiile de vant, comparativ cu multicopterele .

Fig. 2.3.3.a : elicopter conventional,aparat de zbor cu rotor

Multicoptere

Clasificare:

Tricopter

Quadcopter forma +

Quadcopter forma X

Hexacopter +

Hexacopter X

Y6 copter

X8 copter

In mod tipic, denumirea conventionala pentru copterele multirotor urmareste sistemul latin de numerotare astfel “ tri ” denota trei motoare, “ quad ” denota patru , “ hexa ” denota sase si asa mai departe urmand de sufixul “ –copter ”.

Tricopter

Tricopterul utilizeaza 3 motoare dispuse intr-o configuratie triunghiulara, cu unul in spate si doua in fata. Bratele unui elicopter sunt in mod tipic separate la 120°, ceea ce este un avantaj atunci cand folosim o camera montata la bord, deoarece acest unghi larg de separare inseamna ca bratele si elicele vor sta departe de campul de luat vederi al camerei.

Un alt beneficiu consta in faptul ca, deoarece tricopterele utilizeaza trei motoare sunt mai ieftin de construit, desi pentru a controla in totalitate elicopterul, motorul din spate trebuie sa se balanseze lateral pentru a roti aparatul la stanga si la dreapta ( viraj ). Acest lucru inseamna, de asemenea ca tricopterele pot schimba directia mult mai repede decat alte configuratii, cum ar fi quadcopterele.

Fig. 2.3.3.b: tricopter

Totusi, constructia tricopterelor se dovedeste a fi mai complexa, din cauza mecanismului de deviere a directiei de deplasare care trebuie sa balanseze motorul din spate, ceea ce are cel putin avantajul ca garanteaza un zbor lin, lucru benefic pentru situatiile in care utilizam drone pentru filmari.

Quadcopter

Quadcopterele, care sunt cel mai obisnuit tip de drone mulltirotor, au patru motoare aranjate simetric in configuratie “ + ” sau “ X ”. Intr-un aranjament +, partea din fata a quadcopterului este aliniata direct cu un motor. Intr-un aranjament X , partea din fata a quadcopterului este directionat intre cele doua motoare frontale. Aceasta ultima configuratie este mai utila si mai obisnuita, deoarece presupune ca bratele si elicele obtureaza mai putin campul vizual al unei camere de luat vederi de la bord care este orientata spre a lua vederi din fata.

Din cele patru motoare ale unui quadcopter, doua se invartesc in sensul acelor de ceas si celelalte doua se invartesc in sens opus acelor de ceas. Aceasta va compensa efortul de torsiune creat de motoare si va mentine quadcopterul cu fata catre directia corecta ( indeplineste astfel aceeasi functie ca rotorul de pe coada in cazul elicopterului traditional ).

Pentru a controla quadcopterul, vitezele motoarelor sunt variante – de exemplu, pentru a balansa quadcopterul inainte, cele doua motoare frontale incetinesc, in timpul in care cele doua motoare din spate accelereaza.

Principalul avantaj este acela ca sunt aparate usor de construit si de controlat.

Fig. 2.3.3.c: quadcopter

Hexacopter

In afara de faptul evident ca avand sase motoare pot ridica mai mult echipament , un beneficiu aditional este acela ca, datorita faptului ca motoarele sunt spatiate la o distanta mai mica in jurul centrului, daca un motor cedeaza, hexacopterul poate, de regula, sa ramana stabil, utilizand restul motoarelor si poate astfel sa aterizeze in siguranta. Daca un motor cedeaza la un tricopter sau quadcopter aterizarea se face in conditii eronate, deoarece fiecare motor este critic in mentinerea stabilitatii dronei. Din aceasta cauza, numeroase drone utilizate pentru fotografii aeriene profesionale au o configuratie fie de hexacopter, fie de octocopter, avand in vedere ca patru motoare ofera mai multa siguranta si pot transporta o greutate mai mare.

Fig. 2.3.3.d: hexacopter

Y6 copterele

Configuratia Y6 este un mix de tricopter si hexacopter. Are sase motoare pe trei brate, aranjate exact ca la tricopter, cu doua brate in fata spatiate intre ele la 120° si un singur brat in spate. Totusi, desi Y6 copterele au sase motoare in total, ele sunt considerate ca fiind hexacoptere. Fiecare brat are doua motoare montate pe el, unul cu fata in sus si celalalt cu fata in jos. Acest aranjament este cunoscut sub denumirea de aranjament coaxial de motoare. In mod tipic, fiecare motor se va roti intr-o directie opusa.

Ca si avantaje, acestea se ansambleaza si se realizeaza mai usor comparativ cu hexacopterele obisnuite. De asemenea, bratele au o greutate mai redusa iar motoarele prezinta o mare redundanta datorita modului in care au fost montate, fiecare pereche de motor actionand pe aceeasi axa de presiune. Daca se intampla ca un motor sa cedeze, configuratia Y6 abia daca va inregistra vreo diferenta, alta decat o usoara scadere de o sesame a fortei de presiune.

Singurul mic dezavantaj al cadrelor Y6 este ca exista o pierdere marginala de eficienta ( doar in jur de 5% ) din cauza motoarelor din partea de jos, care functioneaza in aerul turbulent ejectat in jos de catre motoarele de sus. Cu toate acestea, aceasta reducere de eficienta este compensata de catre greutatea mai mica a ramei ( doar trei brate in comparativ cu sase ), astfel ca acest efect se considera neglijabil.

Fig. 2.3.3.d: Y6 copter

Octocopter

Acesta este un aparat cu 8 motoare cu acelasi spatiu egal intre ele. Acestea sunt dintre cele mai mari multicoptere, un aparat de zbor tipic avand un diametru de 1m. Ca si in cazul hexacopterelor, cresterea numarului de motoare inseamna ca octocopterele pot purta sarcini utile mai grele si prezinta extra redundanta de motor. In timp ce un hexacopter poate de obicei doar sa supravietuiasca in urma cedarii unui singur motor ( sau probabil doua daca sunt asezate in parti opuse unul fata de celalalt ), un octocopter poate sa suporte mai multe cedari de motor fara sa se prabuseasca – in functie de sarcina utila si de care motor cedeaza.

Octocopterele sunt folosite de obicei, ca drone profesionale de filmat datorita capacitatii lor de a purta sarcina utila si datorita redundantei unghiului de deriva al motorului, deoarece unele camere profesionale pentru filmari costa mai mult decat majoritatea masinilor si sunt foarte grele.

X8 copter

Configuratia X8 este de fapt, o rama de quadcopter cu opt motoare, o combinatie de quadcopter si octocopter. Ramele X8 au patru brate cu doua motoare fiecare, unul fiind orientat in sus, iar celalalt in jos. Copterele X8 au aceleasi avantaje ca octocopterele, principala lor caracteristica fiind capacitatea de a ridica sarcini utile grele. Exact ca si configuratia Y6, X8 are de asemenea avantajul de a avea doua motoare plasate de-a lungul aceleasi axe de presiune, asa ca este chiar mai stabil in cazul in care un motor cedeaza in timpul zborului ( presupunand ca celelalte sapte produc suficienta forta de propulsie pentru a mentine dronele in aer ).

Fig.2.3.3.e: X8 copter

X8 copter – Unicopter

Acestea nu reprezinta o clasificare oficiala, dar exista foarte multe cazuri de coptere neobisnuite care au un singur motor. Acestea folosesc o singura elice care se roteste pentru a produce forta de propulsie si sunt controlate prin mici flapsuri aflate sub elicea care manipuleaza curentul de aer pentru a permite dronei sa se roteasca sau sa mearga inainte, etc. Aceste tipuri pot fi considerate asemanatoare cu elicopterele conventionale, deoarece folosesc un rotor principal pentru a genera ascensiunea.

Avantajul principal consta in aceea ca e mai simplu din punct de vedere mecanic, deoarece lamelele principalului rotor sunt fixe si micile flapsuri de sub lamela sunt folosite pentru a controla drona. Mai mult, deoarece paleta este de asemenea ingradita de un tub, elicele sunt putin mai eficiente in comparatie cu lamelele rotorului de aceeasi marime dintr-un elicopter ( deoarece tubul reduce pierderile de forta de propulsie la marginile lamelelor din motive aerodinamice ).

Un alt aspect important al tubului in termeni de siguranta, este ca impiedica lamelele, care se rotesc repede si care sunt astfel periculoase de a rani pe cineva; de asemenea, protejeaza impotriva prafului si mizeriei care pot patrunde inauntru. Cu toate acestea, tubul prezinta un mare inconvenient, si anume instabilitatea la conditiile de vant. Din cauza ariei lor frontale mari, dronele unicopter sunt foarte susceptibile de a fi impinse dintr-o parte in alta de rafale de vant, ceea ce inseamna ca nu pot fi folosite cand vantul e foarte puternic. De asemenea, deoarece motoarele/lamelele se afla in mijlocul dronei, acest lucru limiteaza spatial disponibil in care echipamentul poate fi montat.

X8 copter – Bicopter

Bicopterele au doua motoare si, desi nu sunt foarte folosite din cauza prolemelor de stabilitate, dar cu toate acestea, au un design placut ( asemanator cu aparatul de zbor Pandora Warrior din filmul avatar ).

Fig. 2.3.3.f: bicopter ( Sursa imagine Pandora Warrior : www.fpvguy.com )

Bicopterele sunt mai folosite pentru constructiile tip hybrid ( tiltrotor ) care se va transforma intr-un aparat de zbor cu aripi fixe si directive de deplasare in fata ( precum Boeing V22 Osprey care poate decola si ateriza ca un elicopter ), dar care pentru a zbura pe distante mai lungi, isi va bascula rotoarele inainte si se va transforma intr-un aparat de zbor cu aripi fixe.

Fig. 2.3.3.g: bicopter tip hybrid ( Sursa imagine Boeing V22 Osprey: wikipedia )

Aceste modele de drone sunt construite in special pentru misiuni militare.

X8 copter – Mai mult de opt motoare

Pe piata industriei exista si X8 copter cu 18 motoare ! Acestea sunt construite pentru a putea fi folosite la ridicarea unor greutati mari sau pur si simplu pentru proiecte distractive. Oricate motoare ar avea o astfel de drona, se va tine seama ca trebuie sa fie un numar par de motoare pentru o stabilitate cat mai buna.

2.4 Probleme privind stabilitatea sistemelor aeriene fara pilot uman

In cadrul temei abordate si conform simularilor efectuate, s-a depistat o prima problema privind stabilitatea quadcopter-ului in aer, si anume: calibrarea celor patru esc-uri ( reductoare de turatie ) din software-ul CLEANFLIGHT-Configurator bazat pe Arduino. O calibrare incorecta a condus la oprirea brusca a unuia dintre motoare in timpul zborului, efectul fiind acela de prabusire a sistemului aerian fara pilot uman.

O alta problema care are acelasi efect final ca si cel anterior, o reprezinta calibrarea incorecta a celor patru motoare fara perii. Rezultatul fiind oprirea brusca a motorului dupa cinci secunde de zbor sau functionarea intrerupta a acestuia.

Urmatoarea problema se refera la modul de pilotare care este destul de dificil, si anume: comanda de stanga efectuata de catre radio comanda este de fapt comanda de dreapta, iar dreapta este stanga. Indiferent de constructie, aceasta ordine este valabila pentru toate aparatele de zbor fara pilot uman, iar o manevrare neconforma duce la o stabilitate scazuta a aparatului in aer avand ca rezultat final, prabusirea sau lovirea de anumite obiecte contondente.

Datorita puterii unui singur motor, elicea poate atinge 8310 rot/min ceea ce inseamna un nivel mediu de pericol pentru persoanele din jur. Propulsoarele nu au o carcasa exterioara si tocmai de aceea este necesara montarea lor cu o foarte mare rigurozitate, altfel urmarile pot fii asemanatoare ca cele din fig. 2.4.a, sau mult mai grave.

Daca turatia este combinata si cu o anumita distanta, efectul poate fii unul destul de sever !

Fig. 2.4.a: rana cauzata de calibrarea incorecte a elicei

De asemenea, toate acestea nu pot fii posibile daca alimentarea nu se efectueaza. Prin urmare, trebuie sa se asigure alimentarea acumulatorului inainte de pornirea si punerea in functiune a aparatului de zbor. Nu trebuie neglijata aceasta etapa si in niciun caz nu este permisa incarcarea incompleta a acumulatorului, deoarece pot aparea probleme in timpul zborului si anume: oprirea brusca a alimentarii catre motoare mai devreme decat era prevazut, distribuirea energiei inegala catre cele patru motoare, aterizare neprevazuta, erori la bordul placutei de control, etc.

O alta problema substantiala este reprezentata de conectarea controller-ului de zbor la software-ul CLEANFLIGHT de configurare si calibrare. Pasii sunt destul de simpli:

Se conecteaza controllerul de zbor la calculator prin usb si micro-usb;

Apoi se instaleaza driver-ul controllerului de zbor;

Se instaleaza extensia CLEANFLIGHT din magazinul web chrome;

Dupa care urmeaza partea in care trebuie sa incarcam Firmware-ul si sa ii dam Flash ( exceptie de la regula pentru prima configurare, care nu necesita reset manual ).

Acesti pasi sunt descrisi in amanunt la capitolul 6 din acest proiect !

Iar aici intervine eroarea, deoarece controllerului de zbor trebuie sa ii dam un reset manual inainte de optiunea “flash” din programul de configurare “cleanflight” , si anume, trebuie sa utilizam o penseta pentru a face un scurt-circuit intre pinii de reset de pe controller, iar daca la final nu obtinem un rezultat pozitiv ( doar led-ul albastru ramane aprins ), trebuie sa repetam operatiunea de 2,3,4 … n ori pana reusim .

Fig. 2.4.b: pinii de reset ai placutei principale

O ultima problema este reprezentata de montarea si/sau configurarea incorecta a GPS-ului, deoarece fara acesta, quadcopterul nu isi poate calibra coordonatele pozitionarii in aer, iar rezultatul final este o manevrare mai dificila decat cea dorita.

Cap. 3 – Prezentarea solutiei proprii si modul de comanda

Principiul de functionare

Quadcopterul este alcatuit din doua tije perpendiculare la capatul carora se afla motoarele. In zona centrala este situata placa de baza impreuna cu restul componentelor electronice si bateria. Elicele, patru la numar sunt pozitionate fiecare deasupra celor patru motoare, iar rotirea acestora genereaza forta de impingere. In acelasi timp, aerul opune rezistenta acestei miscari de rotire, generand o forta in directia opusa directiei de rotatie a elicei. Motoarele aflate pe axe diferite se rotesc in directii opuse pentru a contracara acest efect. Urmarea este ca, daca vitezele de rotatie ale celor patru elice sunt egale, fortele aparute in urma frecarii cu aerul se anuleaza reciproc.
Configuratia quadcopterului din acest proiect este de forma literei X .

3.2 Alegerea cadrului

Cel mai important lucru in construirea unui aparat de zbor fara pilot uman este rama multimotor, unde ulterior pe aceasta vom atasa restul de componente. Aceasta rama avand in componenta ei bratele atasate, care reprezinta de fapt cadrul ce se poate confectiona din materiale plastice, poliamida, nylon sau diferite materiale usoare, dar totusi rezistente pentru a sustine greutatea componentelor ce urmeaza a fi atasate. Cadrul se poate realiza ori pe masini cu comanda numerica ( CNC ) utilizand operatia de frezare ori pe imprimante 3D.

Cadrul ales si folosit de mine este alcatuit din rama centrala din fibra de sticla si patru brate din nylon-poliamida. Operatia de confectionare a intreg cadrului a fost de frezare si finisare pe CNC cu 5 axe.

Fig. 3.2: cadrul Q450

3.3 Alegerea motoarelor brushless

Scopul motoarelor electrice este de a converti energia electrica in energie mecanica sub forma miscarii de rotatie. Aceste sisteme electromecanice sunt caracterizate de anumiti parametrii care ajuta la prezicerea performantelor motorului. Viteza in absenta sarcinii descrie viteza motorului cand nu este cuplul aplicat, iar viteza de mers in gol descrie momentul de cuplu maxim al motorului cand viteza acestuia este de 0 RPM ( rotatii pe minut ). Alti parametrii de interes sunt cuplul, curentul si tensiunea de alimentare a motorului. Modul de functionare al acestor motoare este simplu, cuplul fiind creat de forta de magnetism aparuta intre magnetii stationari si electromagnetii alimentati astfel incat, polaritatea dintre cele doua componente sa fie mereu inversa. Motoarele brushed, desi au avantajul unui pret mai mic, au electromagnetii plasati pe rotor si magnetii ficsi pe stator, de aceea sunt predispuse supraincalzirii, dar si uzuri din cauza comutatoarelor. La motoarele brushless, electromagnetii sunt plasati pe stator si magnetii stationari pe rotor. Polaritatea electromagnetilor este schimbata de data aceasta de un circuit de control care imbunatateste precizia motorului.

Fig.3.3.a: motor cu perii vs fara perii

Rpm= Kv * volt Factorul Kv este cel mai folosit pentru a descrie capacitatile unui motor. Dupa cum se observa, la aceeasi tensiune este proportional cu numarul de rotatii pe minut (RPM). Tipul de motor trebuie adaptat tipului de quadcopter dorit. O drona de mare performanta, acrobatica, va avea un motor de viteza mare. Pe de alta parte, un quadcopter folosit in zboruri lungi, trebuie sa consume cat mai putina energie, de aceea motoarele cu Rpm mic sunt de dorit in aceasta situatie.

Motorul cu perii are in rotor o bobina care este alimentata la o sursa de tensiune continua prin intermediul periilor de carbon. Statorul motorului contine un magnet permanent.

Motorul fara perii se mai numeste in industria de biciclete electrice si motor de curent continuu Brushless Trifazat.

Acest motor are bobinele din cupru plasate pe stator, iar rotorul sau contine o serie de magneti permanenti. Motorul este alimentat cu trei forme de unda diferite care genereaza in stator un camp magnetic.

Avantajele si dezavantajele motorului cu perii

Motorul cu perii disipa energie prin frecare si prin incalzire destul de mult;

Tot din cauza scanteilor de la perii se produce un factor de distorsiune a sistemului de alimentare prin reinjectarea de impulsuri parazite in sens invers ( de aceea mai cad controller-ele cu perii );

Pe deoparte este mai costisitor, pentru ca necesita intretinere ( schimbare perii ), pe de alta parte poate fi mai convenabil, pentru ca se poate prelungi viata lui prin aceasta intretinere;

Este mai avantajos de controlat, folosind doar doua fire, reglajul turatiei facandu-se in tensiune;

Viteza este limitata de forta de frecare ( cu cat avem viteza mai mare cu atat forta de frecare a periilor este mai mare ).

Avantajele si dezavantajele motorului fara perii

Lipsa de mentenanta constituie un avantaj;

Nu exista forta de frecare, ceea ce duce la obtinerea de cuplu si viteza mai mare, cat si o mai buna racire;

Un dezavantaj il constituie faptul ca, controlul lui se face intr-un mod mai complex, deci e nevoie de un controller neaparat;

Transmiterea semnalelor in bobine se face cu ajutorul senzorilor Hall care pot da erori daca se intrerup ( problema rezolvata cu ajutorul controllerelor universale );

Dimensiuni mai reduse si mai compacte, dar fara sa afecteze cuplul.

Datorita acestor avantaje si dezavantaje am ales patru motoare fara perii de la firma Turnigy de 1100KV ( rpm ), putere maxima 910W, curent maxim 50A, rezistenta interna 0.023Ohm, curent fara sarcina 3.1A, greutate 159g, diametru arbore 5mm.

Fig.3.3.b: turnigy motor D2836/8

Electronic speed control prescurtat si ESC ( reductoarele de turatie )

Un Electronic Speed Control ( ESC ) este un circuit ce indeplineste doua functii principale. Prima este de a actiona asemenea unui regulator de tensiune, reducand tensiunea furnizata de baterie pana la valoarea necesara functionarii motoarelor si microcontrollerelor. A doua functie este de a converti semnalul de la Hoverfly Open intr-un semnal de control pentru motorul brushless.

Spre deosebire de speed controllerele bazate pe rezistori, ESC-urile nu functioneaza aplicand motoarelor fractiuni din tensiunea de alimentare proportionale cu viteza pe care o dorim, ci aplicand intreaga tensiune, dar oprind-o si pornind-o extrem de rapid. In cazul celor aparute primele, diferenta de tensiune nefolosita pentru miscarea motorului ar fi fost irosita, fiind disipata sub forma de caldura. Variind raportul dintre timpii de „ON” ( tensiune maxima ) si „ OFF ” ( tensiune de 0V ), ESC-ul variaza de fapt tensiunea medie citita de motor. Cu alte cuvinte, la orice moment de timp, tensiunea de control este ori complet „ON” ori complet „OFF”. Aceasta face ca acest tip de control sa fie, cel putin teoretic, 100% eficient. In realitate totusi, ESC-urile sunt dispozitive imperfecte, eroarea acestora datorandu-se in principal timpului de comutare al tranzistoarelor folosite in circuit si rezistentelor interne. BEC ( Battery Eliminator Ciruit ) este circuitul ce reguleaza tensiunea furnizata de baterie. Este extrem de folositor in aplicatii de quadcopter, deoarece bateriile suplimentare ar creste greutatea dronei.

Fig.3.4.a: schema de principiu ESC

Am ales urmatoarele ESC-uri avand urmatoarele caracteristici principale si m-am asigurat sa fie compatibile cu motoarele alese la pasul anterior :

– Model: 20A XSC Micro Opto multi-rotor ESC
– Input Voltage: 3~4S (11.1~14.8V)
– Continuous Current: 20 amps
– Burst Current: 22 amps
– Cut Off Voltage: 9.9V (3S)/13.2V (4S)
– Frequency: 50Khz-5Mhz (CPU), signal 50hz

Fig.3.4.b: ESC dys – Motor Limit: 11000kv/rpm

ESC-urile nu functioneaza aplicand motoarelor fractiuni din tensiunea de alimentare proportionale cu viteza pe care o dorim, ci aplicand intreaga tensiune, dar oprind-o si pornind-o extrem de rapid. In cazul celor aparute primele, diferenta de tensiune nefolosita pentru miscarea motorului ar fi fost irosita, fiind disipata sub forma de caldura. Variind raportul dintre timpii de „ON” ( tensiune maxima ) si „OFF” ( tensiune de 0V ), ESC-ul variaza de fapt tensiunea medie citita de motor. Cu alte cuvinte, la orice moment de timp, tensiunea de control este ori complet „ON” ori complet „OFF”. Aceasta face ca acest tip de control sa fie, cel putin teoretic, 100% eficient. In realitate totusi, ESC-urile sunt dispozitive imperfecte, eroarea acestora datorandu-se in principal timpului de comutare al tranzistoarelor folosite in circuit si rezistentelor interne. BEC (Battery Eliminator Ciruit ) este circuitul ce reguleaza tensiunea furnizata de baterie. Este extrem de folositor in aplicatii de quadcopter, deoarece bateriile suplimentare ar creste greutatea dronei.

Alegerea controlerului de zbor

Am ales controlerul de zbor ”Flip32 All In One Pro v1.03” deoarece el contine un microcontroller puternic STM103 pentru procesare rapida, sursa de tensiune de 5V si 3A ce poate alimenta si ESC-urile de la motoare, ce suporta alimentare de pana la 6S, senzor de curent de maxim 90A ce poate monitoriza descarcarea acumulatorului, MinimOSD pentru a suprapune peste imaginea transmisa live, informatii despre datele de zbor, dar si un barometru MS5611 ce poate masura presiunea atmosferica si astfel sa mentina o altitudine constanta, cu o eroare cat mai mica.

Controller-ul de zbor este capabil sa se conecteze si la un GPS extern, astfel ca permite un zbor foarte stabil.

Fig.3.5: Flip32 All In One Pro v1.03

Caracteristici tehnice:

Microcontroller STM103 cu firmware Cleanflight

Numar maxim canale de iesire PWM: 8 – poate controla un octocopter

Barometru MS5611 ce ajuta la mentinerea altitudinii

Conexiune USB pentru configurare controller si OSD

Contine sursa de tensiune in comutatie de 5V, 3A (alimentare 2S – 6S)

Cerinte hardware compatibile cu MinimOSD/MWOSD/CC3D OSD

Senzor de curent (maxim 90A) si de tensiune (2S – 6S)

Chip pentru OSD cu consum mai redus de energie fata de variantele traditionale utilizate

Suporta SPPM si receptie seriala

Contina si o mica zona pentru distributia energiei

Masa: 17 grame .

Acest controler de zbor a fost realizat conform unui cod sursa scris in software-ul Arduino 1.8.8 si s-a anexat in detaliu la finalul acestui proiect la sectiunea “ ANEXA.Cod Sursa “ si de asemenea regasim la Cap.6 detaliile cu privire la configurarea setarilor acestei placute !

Alegerea bateriei LiPo – Turnigy 2200 mah

Bateriile LiPo ( prescurtare de la Litiu-Polimer ) sunt un tip de baterii reincarcabile ce au permis echipamentelor RC ( radio controlled ) sa prospere recent. Se poate considera ca aceste baterii sunt motivul pentru care zborul aparatelor electrice este o optiune viabila fata de modelele alimentate prin combustibili.

Motivele pentru care aceste baterii sunt atat de folosite sunt :

Greutatea mica si faptul pot avea orice forma sau marime;

Posibilitatea de a stoca multa energie intr-un pachet de mici dimensiuni;

Rata mare de descarcare pentru motoare electrice cu consum mare Bateriile LiPo nu folosesc o solutie electrolitica lichida ci un polimer electrolitic separator intre anodul si catodul bateriei, ce permite schimbul ionilor de litiu.

Am ales urmatorul tip de baterie:

Fig.3.6: acumulator turnigy 2.2

Caracteristici principale:

– Capacitate: 2200mAh
– Configuratie: 3S1P / 11.1v / 3Cell

Alegerea Gps-ului

• uBlox Neo-7M module
• 56-channel 
• GPS L1 C/A, GLONASS L1 FDMA
• QZSS L1 C/A
• Galileo E1B/C
• SBAS: WAAS, EGNOS, MSAS
• 10Hz update rate
• 25x25x2 Ceramic patch antenna
• Rechargeable 3V Backup battery
• Low noise 3.3V regulator
• I2C EEPROM storage
• Power and fix LED’s
• Pedestal Mount/Case
• Pixhawk/PX4 compatible
• LNA MAX2659ELT+
• Pre-configured 38,400 Baud and prams

Fig.3.7: gps neo-7M

Alegerea propulsoarelor

Aceste propulsoare sunt proiectate special pentru a se potrivi cu motoarele DJI.

Diametrul elicei X :10.00 inch

Fig.3.8: propulsoare

Alegerea Radiocomenzii RC si a receptorului

Telecomanda entry-level cu 6 canale la o frecventa de 2.4GHz ce poate functiona si cu telemetrie. Telecomanda ideala pentru controlul multicopterelor. Din cele 6 canale, in mod normal 4 dintre ele vor fi folosite pentru comenzile referitoare la orientarea spatiala ( controlul inaltimii, controlul rotatiei fata de axa centrala si controlul pentru directionarea in planul dronei – inainte/inapoi, stanga/dreapta), iar restul de doua pot fi folosite pentru controlul modului de zbor (de exemplu, zbor simplu, asistat de GPS, Return-to-home, etc).\

Caracteristici tehnice:

Numar canale: 10 canale prin PPM sau 6 canale prin PWM;

Frecventa de transmisie: 2.408 ~ 2.475 GHz;

Latime de banda: 500 kHz;

Numar canale RF: 135;

Putere de transmisie: pana la 20dBm;

Sistem 2.4GHz AFHDS 2A;

Modulatie GFSK;

Rezolutie citire joystick-uri: 0 ~ 4095 (pe 12 biti);

Alarma pentru tensiune prea mica de alimentare (< 4.2V);

Port DSC MicroUSB sau PPM;

Tensiune de alimentare: 4.2V ~ 6V;

Lungime antena: 2 x 26 mm;

Certificate: CE0678, FCC ID: N4ZI6S00, RCM;

Functioneaza cu 4 baterii/acumulatori AA ce NU sunt incluse.

Fig.3.9.a: RadioComanda Turnigy tgy-i6S

Turnigy TGY-iA6C este un stil nou in receptoare. Se bazeaza pe fiabilitatea receptorului TGY-iA6B, dar este construit special pentru utilizarea cu configuratii PPM, i.BUS si S.BUS. Acesta este un plus excelent pentru transmitatoarele Turnigy TGY-i6, Turnigy Evolution sau Turnigy AFDHS 9x.

Antenele duale ofera iA6C o receptie fara precedent. Legaturile incorporate va permit sa utilizati senzorii optionali de telemetrie.

Un receptor excelent pentru telefoanele cu rotor multi-rotor, in timp ce se reduce la cablare si cu monitorizare integrata a bateriei.

Caracteristici:

Functie de semnal AFHDS 2A fiabila si fara interferente 2.4GHz 
PPM, i.BUS, S.BUS 
Design ultra-usor in greutate 
Antene duale pentru cea mai fiabila functionare fara interferente 
Porturi de conectare pentru utilizarea senzorilor telemetri optionali 
Monitorizare baterie

Canal: 8 
Frecventa: 2.4GHz ISM Gama de Frecventa
latime de banda: 500KHz
Band Cantitate: 135
Putere RF: <20dBm
Sistem: AFHDS2A
Date de iesire: PPM, i.BUS, S.BUS
Antena Lungime: 26mm
Putere: Actualizare wireless 4,0V ~ 6,5V: Da
Date canal: Pozitia centrului: 1500 ± 30us; Interval: 1000 ~ 2000 ± 30us
Intarziere canal: <15ms
Greutate neta: 7.9g
Dimensiuni: 41x25x10mm
Gama: > 300 metri
Certificare: CE0678, FCC ID: N4ZIA6C00, RCM

Fig.3.9.b: receptor tgy-IA6C

Cap. 4 – Memoriu Tehnic

4.1 Modelarea si Simularea motorului de curent continuu fara perii ( Brushless DC Motor )

Schema in impedanta pentru motorul de curent continuu:

Fig.4.1.a: schema in impedanta

Unde:

ZR=R

ZL=Ls

M=j*s*ω

Avem urmatoarele valori:

u= 0→12V

R=13,3 Ω

L=310μH

km=ke=11.4*10-3

J=0.67 g*cm2

MR=0.1197*10-3 N*m

C~0

Se cer: ω=?, i=?

Ecuatii:

u=zR*i+zL*i+ke*ω

Mm=km*i

Mm=M1+M2+MR

ω=M1*zj

ω=M2*zc

Schema de simulare in software-ul MatLab

Fig.4.1.b: schema de simulare in MatLab

Rezultatele obtinute sub forma grafica

Fig.4.1.c: Graficul lui omega

Fig.4.1.c: Graficul curentului

4.2 Dimensionarea lagarul cu rulment radial din interiorul statorului

In general lagarele cu rulmenti servesc la sustinerea arborilor, osiilor sau a altor organe de masini cu miscare de rotatie si sunt capabile sa preia fortele care actioneaza asupra acestora. Lagarele cu rulmenti sunt lagare cu frecare de rostogolire fiind realizate cu ajutorul rulmentilor. Rulmentii sunt ansambluri independente !

Avantajele lagarelor cu rulmenti constau in:

pierderi prin frecare reduse ( randament ridicat );

gabarit axial redus;

consum de lubrifiant mic;

intretinere usoara;

standardizarea pe scara internationala, prin care se asigura interschimbabilitatea acestora.

Dezavantajele lagarelor cu rulmenti constau in:

dimensiuni mari in directie radiala;

durata de functionare redusa in cazul vitezelor mari;

comportare nesatisfacatoare in cazul socurilor si vibratiilor;

necesitatea unei precizii de executie ridicate si a unor conditii severe de montaj.

Calcule in desfasurare !!!

4.3 Directia de rotatie al celor patru rotoare

Fig.4.1.d: Directia rotoarelor

Cand toate cele patru roatoare ating aceeasi viteza, momentul generat este zero ( Mom=0 ) iar quadcopter-ul isi mentine altitudinea. Cand viteza s-a stabilizat rotoarele ofera o forta de echilibru impotriva fortei gravitationale iar quadcopter-ul isi mentine altitudinea.

4.4 Ecuatii de miscare

4.4.1 Directia matriceala, matricea cosinusurilor

Rotatia vectorului in jurul axei “ x ” poate fi descrisa ca matrice:

In jurul axei “ y “ :

In jurul axei “ z ” :

Iar rotatia consecutiva in jurul axelor x,y,z poate fi scrisa astfel:

A = Rx Ry Rz =

Matricea A descrie coordonatele de transformare raportate la sol.

Primele ecuatii stabilite, descriu schimbarea pozitiei in functie de altitudinea quadcopter-ului :

-1 ·

4.4.2 Tranformarea vitezelor unghiulare

Transformarea vitezei unghilare raportate la cadrul fixat pe sol: ( conform cartii “ Flight Dynamics Principles – Michael V. Cook ” )

Unde:

Al doilea set de ecuatii de stare descriu schimbarea de altitudine in functie de rotatia corpului:

-1 ·

4.4.4 Acceleratia liniara

Acceleratia liniara a cadrului fixat la sol este data de a doua lege a lui Newton:

F = m ·

unde m este masa quadcopter-ului si este vectorul de viteza al cadrului. Iar vectorul o sa se deriveze total.

= m + m ·

Rescriem produsul incrucisat:

= m ·

Daca se neglijeaza fortele aerodinamice atunci intervin forte externe care actioneaza asupra aparatului de zbor, si anume: propulsoarele “ T “ si forta de greutate “ W “.

Forta de greutate este proiectata in functie de altitudinea quadcopter-ului iar propulsia actioneaza intotdeauna pe directia axei Z.

= m ·

Forta de greutate actioneaza intotdeauna pe directia axei z a cadrului aflat pe sol. Facand conversia cu matricea cosinusurilor, rezulta:

A · – = m ·

Rescriind organizat, rezulta:

= rv-qw-g sinθ

= pw-ru+g cosθsinɸ

= qu-pv+g cosɸcosθ –

4.4.5 Acceleratia unghiulara

Aplicarea unui cuplu extern va schimba acceleratia unghiulara a quadcopter-ului:

M=

Dar vectorul unghiular isi schimba directia si se aplica derivate totala a vectorului H:

M=+ω · H,

si H = I· ω,

unde, ω reprezinta schimbarea altitudinii, si I reprezinta momentul de inertie al quadcopter-ului.

Cand quadcopter-ul este simetric fata de planul xz si yz, iar axele de rotatie coincid cu axele principale, atunci momentul de inertie este:

Atunci,

M= I· + ω·I·ω

Dupa expansiune,

Mx= Ix+qr( Iz-Iy )

My= · Iy+pr( Ix-Iz )

Mz= · Iz +pq( Iy-Ix )

Si din cauza simetriei xz si yz,

Ix Iy

Iar ecuatiile pot fii scrise simplificat:

Mx= Ix+qr( Iz-Iy )

My= · Iy+pr( Ix-Iz )

Mz= · Iz

4.5 Momentele giroscopice ale propulsoarelor

Cap.5 – Proiectarea sistemului aerian de tip quadcopter in SolidWorks2016

Componentele Cadrului:

Cele patru suporturi ( picioare )

Fig.5.a: suport/picior cadru

Fig.5.a’: suport/picior cadru

Cele doua placute pe care vin montate restul componentelor

Fig.5.b: placa inferioara PCB

Fig.5.b’: placa superioara PCB

Surubul de asamblare cu cap de strangere imbus 2mm

Fig.5.c: surub cu cap imbus

Ansamblul final al cadrului

Fig.5.d: ansamblu final cadru

Bateria Turnigy

Fig.5.e: acumulator turnigy 2200mAh

Propulsoarele ( patru la numar )

Fig.5.f: propulsoare

Componentele motorului:

Stator + rulment radial

Fig.5.g: stator + rulment radial

Rotor

Fig.5.h: rotor

Surub

Fig.5.i: surub

Capac de sprijin

Fig.5.j: capac de sprijin

Axa fixa

Fig.5.k: axa fixa

Saiba

Fig.5.l: saiba

Capac stranger elice

Fig.5.m: capac strangere elice

Ansamblul final Motor – Elice

Fig.5.n: ansamblu motor-elice

Vedere din sectiune

Fig.5.n’: vederea din sectiune a ansamblului motor-elice

Componentele Gps-ului:

Capac spate

Fig.5.o: capac spate gps

Capac fata

Fig.5.o’: capac fata gps

Partea electronica, gps-ul propriu zis

Fig.5.p: partea electronica a gps-ului

Ansamblu Gps

Fig.5.p’: ansamblu gps

Fig.5.q: ansamblu gps

Suport gps

Fig.5.r: suport gps

Fig.5.s: statorul suportului gps

( 4x ) ESC-urile

Fig.5.t: ESC dys

Receptorul

Fig.5.u: receptor turnigy IA6C

Controlerul de zbor

Fig.5.v: Flip32 All In One Pro v1.03

Fig.5.v’: controllerul Flip32 All In One Pro v1.03 si suportul format din patru picioare

Ansamblul Final al Quadcopter-ului

Fig.5.w: ansamblu final quadcopter

Fig.5.w’: ansamblu final quadcopter

Cap. 6 – Configurarea, calibrarea si initializarea setarilor folosind extensia CLEANFLIGHT. Prezentarea programului utilizand Arduino pentru orientarea sistemului aerian

Controllerul de zbor “FLIP32 AIO Flight Controller Acro&Pro v1.03” este placa principala de control care da semnal componentelor si pe care o configuram astfel:

Primul pas este de a ne asigura ca am realizat conectivitatea componentelor pe Flight Controller asa cum ne-a fost indicat in manualul placii;

Fig.6.a: schema de principiu a conectivitatii pe placa de control

Al doilea pas constituie conectarea prin USB si Micro-USB la un calculator;

Fig.6.b: conectarea la calculator

Urmatoarea etapa este de a instala driver-ul placutei ( operatiune reusita si pe care o verificam intrand in My Computer – Management – Device Manager );

Fig.6.c: verificarea instalarii driver-ului in My Computer-Manager

Selectam din cipul de comanda de pe Flight Controller optiunea “ B ” ( FC Mode, CP2102 connected to FC );

Fig.6.d: cipul de comanda al placii principale de control

Dupa care instalam extensia CLEANFLIGHT din Magazinul Web Chrome ( sau o descarcam in calculator de pe site-ul oficial );

Inainte de a ne conecta, incarcam un Fireware si ii dam Flash ( operatiune care o sa aiba reusita la prima incercare, iar urmatoarele incercari vor necesita reset la placa de control cu ajutorul unei pensete si incercam sa facem un scurt-circuit intre pinii de reset asa cum se observa in Fig. 6.g );

Fig.6.e: conectarea placutei la extensia CLEANFLIGHT

Fig.6.f: comanda flash firmware finalizata

Fig.6.g: resetarea placutei utilizand o penseta

Daca conectarea s-a realizat cu succes, o sa apara meniul de configurari ca in Fig.6.h !

Fig.6.h: ecranul de comanda si de setare al configuratiilor

Ultimul pas consta in calibrarea esc-urilor, configurarea receptorului, configurarea radio comenzii, gps-ului si a motoarelor conform parametrilor din manualul placutei de control si a testarii in prealabil.

Controllerul de zbor “ FLIP32 AIO Flight Controller Acro&Pro v1.03” are la baza tehnologia ARDUINO prin care s-a scris codul sursa al firmware-ului ! In fig.6.i se poate observa interfata acestuia si meniul codurilor sursa scrise pe etape. Codul sursa final a fost atasat la sectiunea “ ANEXA. Cod Sursa “ de la finalul acestui proiect !

Fig.6.i: lista codurilor scrise in Arduino

Cap. 7 – Concluzii

In urma finalizarii etapelor teoretice si practice ale actualei lucrari prezentate, au fost indeplinite cu succes obiectivele propuse la inceputul proiectului, acelea de a se realiza un quadcopter, bazat pe calcule bine efectuate, care sa poata fii manevrabil de la sol prin intermediul unei radiocomenzi.

Existenta acestor aparate de zbor fara pilot uman ( denumite UAV ) difera de la o contructie la alta, de la un design la altul, asa cum s-a putut remarca mai sus, dar toate au un numitor comun si anume: siguranta sistemului de zbor construit. Orice functionare anormala a oricarei componente din ansamblul aparatului, poate conduce la urmari severe si tocmai de aceea este necesara verificarea starii de functionare a fiecarei piese.

Prezentul proiect s-a bazat pe consultarea multiplelor surse de informatie din documente de specialitate: carti, studii de caz, articole online,etc., avand ca scop informarea personala pentru a se putea realiza prezenta lucrare.

In vederea efectuarii probei practice, s-au ales componente eligibile pentru tipul aplicatiei prezentate tinandu-se cont si de raportul cost-performanta.

Alegerea controller-ului de zbor a insemnat un punct de mare importanta, deoarece acesta este placuta principala de control care preia semnalele si le distribuie mai departe pentru ca aplicatia sa poata functiona in conditii optime. Acesta avand bazele codului sursa scris in Arduino, a facut ca intreaga operatiune sa se bazeze pe un control sigur si fara erori de configurare.

Pe de alta parte, un alt punct destul de important a constat in alegerea reductoarelor de turatie care indeplinesc rolul de reglare si control al stabilitatii dinamice in zbor a aparatului construit. Acestea reduc si puterea motorului pana cand se recapata controlul dorit din radiocomanda.

Pentru efectuarea unui bun control de la sol prin intermediul radiocomenzii, s-au efectuat reglementari de software la baza placii principale de control care sa calibreze puterea motoarelor si a reductoarelor de turatie.

Testarea functionarii sistemului de zbor fara pilot uman s-a realizat intr-un spatiu deschis urmarindu-se miscarile de perturbatie si receptarea semnalelor pe o distanta de douazeci de metri.

Consideratii Perspective

Toate aplicatiile de tip UAV trebuie sa asigure un grad ridicat de siguranta atat pentru pilotul care controleaza aparatul de la sol, cat si pentru cetatenii din jur, tocmai de aceea s-au impus anumite prevederi legale de catre CAA ( Civil Aviation Authority ) pe care le regasim structurate sub forma de articole. Art.137 si Art.138 sunt generale si se refera la faptul ca o persoana nu trebuie sa actioneze in mod iresponsabil avand ca scop final punerea in pericol a aparatului de zbor si a persoanelor din jurul acestuia. Restul articolelor, cum ar fi Art.166 si Art.167 sunt reglementari impuse in exclusivitate pentru aparatele de zbor mici fara pilot uman, categorie in care se incadreaza si aparatul realizat in urma acestui proiect.

Pentru a se reusi o pilotare de la sol prin intermediul radiocomenzii cat mai usoara si sigura s-au creat aplicatii de simulare speciale, unele dintre ele fiind gratuite. Majoritatea acestor simulatoare includ un cablu USB care se conecteaza la un calculator si care face conexiunea cu radiocomanda.

Totusi, cel mai important aspect care trebuie mentionat este controlul de siguranta dinaintea zborului. Cu alte cuvinte, este necesara o verificare detaliata a quadcopter-ului inaintea fiecarei puneri in functiune.

Trebuie sa se verifice urmatoarele aspecte:

daca acumulatorul este incarcat;

mediul ambiant si cat de aglomerat este acesta;

echipamentul daca raspunde comenzilor dorite;

functionarea in conditii normale a motoarelor;

semnalul gps al aparatului;

calibrarea ESC-urilor;

directia/sensul propulsoarelor montate;

De asemenea, intotdeauna este necesar sa avem la noi o trusa de prim ajutor si pentru orice informatii putem apela la persoane in domeniu de pe www.dronetrest.com !

Dezvoltari Viitoare

Cu toate ca in decursul timpului s-au construit si perfectionat aceste aparate de zbor fara pilot uman, se pot gasi, totusi, noi imbunatatiri si dezvoltari. Pentru tema acestei lucrari abordate, propun urmatoarele etape spre o mai buna perfectionare:

Creearea unui controller de zbor mai stabil din punct de vedere software care sa nu necesite resetarea si update-ul firmware-ului odata la o anumita perioada;

Inlocuirea gps-ului cu o tehnologie mai stabila pentru un control de zbor mai usor;

Inlocuirea acumulatorului pentru o durata de zbor mai lunga;

Montarea unei camere video pentru supraveghere;

Realizarea unui cablaj mai solid pe o placa PCB externa si izolata.

Cap. 8 – B I B L I O G R A F I E

Multumiri Dl. S.l.dr.ing. Besnea Daniel pentru coordonare !

Multumiri Companiei Autonomous Flight Technologies S.R.L (A.F.T) pentru indrumare si permisiunea de realizare a imaginilor din cadrul companiei

S-a utilizat softul SolidWorks 2016 pentru realizarea desenului de executie

https://en.wikipedia.org/wiki/Langley_Aerodrome – Accesat la data de 07.10.2018

https://ro.wikipedia.org/wiki/George_de_Bothezat – Accesat la data de 07.10.2018

https://en.wikipedia.org/wiki/Flight_control_surfaces – Accesat la data de 12.10.2018

Book: Build Your Own Drone Manual-Alex Elliott-Editura Haynes – Accesata la data de 27.10.2018, 28.10.2018, 30.10.2018, 01.05.2019.

Book: Flight Dynamics Principles – Michael V. Cook – Accesata la data de 04.05.2019

www.fpvguy.com – Accesat la data de 20.11.2018

https://en.wikipedia.org/wiki/Bell_Boeing_V-22_Osprey – Accesat la data de 20.11.2018

https://blog.bimax.ro – Accesat la data de 28.11.2018

https://hobbyking.com – Accesat pentru comandarea pieselor la data de 02.01.2019

https://grabcad.com/tristan.newman-1/models – Accesat la data de 10.02.2019

https://nathan.vertile.com/blog/2015/11/09/rctimer-oze32-integrated-flight-controller-review-and-setup/#38e538cb2 – Accesat la 20.04.2019

ANEXA. Cod Sursa

#define MEMCHECK // to enable memory checking.

#if 1

__asm volatile ("nop");

#endif

#ifdef MEMCHECK

extern uint8_t _end; //end of program variables

extern uint8_t __stack; //start of stack (highest RAM address)

void PaintStack(void) __attribute__ ((naked)) __attribute__ ((section (".init1"))); //Make sure this is executed at the first time

void PaintStack(void)

{

//using asm since compiller could not be trusted here

__asm volatile (" ldi r30,lo8(_end)\n"

" ldi r31,hi8(_end)\n"

" ldi r24,lo8(0xa5)\n" /* Paint color = 0xa5 */

" ldi r25,hi8(__stack)\n"

" rjmp .cmp\n"

".loop:\n"

" st Z+,r24\n"

".cmp:\n"

" cpi r30,lo8(__stack)\n"

" cpc r31,r25\n"

" brlo .loop\n"

" breq .loop"::);

}

uint16_t UntouchedStack(void)

{

const uint8_t *ptr = &_end;

uint16_t count = 0;

while (*ptr == 0xa5 && ptr <= &__stack)

{

ptr++; count++;

}

return count;

}

#endif

// Frequently used expressions

#define PGMSTR(p) (char *)pgm_read_word(p)

//––––––––––––––––––––––––

#define MWVERS "MW-OSD – R1.9.1.1"

//#define MWVERS "MW-OSD – R1.9"

#define MWOSDVERSION 1911 // 1660=1.6.6.0 for GUI

#define EEPROMVER 17 // for eeprom layout verification

#include <avr/pgmspace.h>

#undef PROGMEM

#define PROGMEM __attribute__(( section(".progmem.data") )) // Workaround for http://gcc.gnu.org/bugzilla/show_bug.cgi?id=34734

#include <EEPROM.h>

#include <util/atomic.h> // For ATOMIC_BLOCK

#include "Config.h"

#include "Def.h"

#include "symbols.h"

#include "GlobalVariables.h"

#include "math.h"

#if defined LOADFONT_LARGE

#include "fontL.h"

#elif defined LOADFONT_DEFAULT

#include "fontD.h"

#elif defined LOADFONT_BOLD

#include "fontB.h"

#endif

#ifdef I2C_UB_SUPPORT

#include "WireUB.h"

#endif

#ifdef I2C_SUPPORT

#include <Wire.h>

#endif

#if defined USEMS5837

#include "MS5837.h"

MS5837 MS5837sensor;

#endif //USEMS5837

#if defined PROTOCOL_SKYTRACK

#include "SKYTRACK.h"

#endif

//––––––––––––––––––––––––

void setup()

{

#ifdef HARDRESET

MCUSR &= (0xFF & (0 << WDRF));

WDTCSR |= (1 << WDCE) | (1 << WDE) | (0 << WDIE);

WDTCSR = (0 << WDE) | (0 << WDIE);

#endif

Serial.begin(BAUDRATE);

#ifndef PROTOCOL_MAVLINK //use double speed asynch mode (multiwii compatible)

uint8_t h = ((F_CPU / 4 / (BAUDRATE) – 1) / 2) >> 8;

uint8_t l = ((F_CPU / 4 / (BAUDRATE) – 1) / 2);

UCSR0A |= (1 << U2X0); UBRR0H = h; UBRR0L = l;

#endif

#ifdef I2C_UB_SUPPORT

// I2C initialization

WireUB.begin(I2C_UB_ADDR, -1);

TWBR = 2; // Probably has no effect.

// Compute rx queue timeout in microseconds

// = 3 character time at current bps (10bits/char)

WireUB.setWriteTimo((1000000UL) / (I2C_UB_BREQUIV / 10) * 3);

#endif

MAX7456SETHARDWAREPORTS

ATMEGASETHARDWAREPORTS

LEDINIT

#if defined EEPROM_CLEAR

EEPROM_clear();

#endif

checkEEPROM();

readEEPROM();

#ifndef STARTUPDELAY

#define STARTUPDELAY 1000

#endif

#ifndef INTRO_DELAY

#define INTRO_DELAY 6

#endif

delay(STARTUPDELAY);

#ifdef VTX_RTC6705

vtx_init();

#ifdef IMPULSERC_HELIX

vtx_flash_led(5);

# endif

#endif

MAX7456Setup();

#if defined GPSOSD

timer.GPS_initdelay = INTRO_DELAY;

#else

#endif

#if defined FORECSENSORACC

MwSensorPresent |= ACCELEROMETER;

#endif

#if defined FORCESENSORS

MwSensorPresent |= GPSSENSOR;

MwSensorPresent |= BAROMETER;

MwSensorPresent |= MAGNETOMETER;

MwSensorPresent |= ACCELEROMETER;

#endif

setMspRequests();

#ifdef ALWAYSARMED

armed = 1;

#endif //ALWAYSARMED

#ifdef KKAUDIOVARIO

Wire.begin();

setupSensor();

pressure = getPressure();

lowpassFast = lowpassSlow = pressure;

#endif //KKAUDIOVARIO

#if defined USEMS5837

Wire.begin();

MS5837sensor.init();

MS5837sensor.setFluidDensity(FLUID_DENSITY); // kg/m^3

#endif // USE MS_5837

GPS_time=946684801; // Set to Y2K as default.

datetime.unixtime = GPS_time;

Serial.flush();

for (uint8_t i = 0; i < (1+16); i++) {

MwRcData[i]=1500;

}

}

//––––––––––––––––––––––––

#if defined LOADFONT_DEFAULT || defined LOADFONT_LARGE || defined LOADFONT_BOLD

void loop()

{

switch (fontStatus) {

case 0:

MAX7456_WriteString_P(messageF0, 32);

MAX7456_DrawScreen();

delay(3000);

displayFont();

MAX7456_WriteString_P(messageF1, 32);

MAX7456_DrawScreen();

fontStatus++;

delay(3000);

break;

case 1:

updateFont();

MAX7456Setup();

MAX7456_WriteString_P(messageF2, 32);

displayFont();

MAX7456_DrawScreen();

fontStatus++;

break;

}

LEDOFF

}

#elif defined DISPLAYFONTS

void loop()

{

MAX7456Setup();

displayFont();

MAX7456_DrawScreen();

}

#else

// ampAlarming returns true if the total consumed mAh is greater than

// the configured alarm value (which is stored as 100s of amps)

bool ampAlarming() {

int used = pMeterSum > 0 ? pMeterSum : (amperagesum / 360);

return used > (Settings[S_AMPER_HOUR_ALARM] * 100);

}

//––––––––––––––––––––––––

void loop()

{

#if defined TX_GUI_CONTROL //PITCH,YAW,THROTTLE,ROLL order controlled by GUI for GPSOSD and MAVLINK

switch (Settings[S_TX_TYPE]) {

case 1: //RPTY

tx_roll = 1;

tx_pitch = 2;

tx_yaw = 4;

tx_throttle = 3;

break;

case 2: //TRPY

tx_roll = 2;

tx_pitch = 3;

tx_yaw = 4;

tx_throttle = 1;

break;

default: //RPYT – default xxxflight FC

tx_roll = 1;

tx_pitch = 2;

tx_yaw = 3;

tx_throttle = 4;

break;

}

#endif // TX_GUI_CONTROL //PITCH,YAW,THROTTLE,ROLL order controlled by GUI

alarms.active = 0;

timer.loopcount++;

if (flags.reset) {

resetFunc();

}

#if defined (KISS)

if (Kvar.mode == 1)

screenlayout = 1;

else

screenlayout = 0;

#elif defined (OSD_SWITCH)

if (MwSensorActive & mode.osd_switch)

screenlayout = 1;

else

screenlayout = 0;

#elif defined (OSD_SWITCH_RC)

rcswitch_ch = Settings[S_RCWSWITCH_CH];

screenlayout = 0;

if (Settings[S_RCWSWITCH] == 1) {

if (MwRcData[rcswitch_ch] > TX_CHAN_HIGH) {

screenlayout = 2;

}

else if (MwRcData[rcswitch_ch] > TX_CHAN_MID) {

screenlayout = 1;

}

}

else {

if (MwSensorActive & mode.osd_switch)

screenlayout = 1;

}

#else

screenlayout = 0;

#endif

if (screenlayout != oldscreenlayout) {

readEEPROM();

}

oldscreenlayout = screenlayout;

// Blink Basic Sanity Test Led at 0.5hz

if (timer.Blink2hz)

LEDON

else

LEDOFF

//––––– Start Timed Service Routines –––––––––––––

unsigned long currentMillis = millis();

#ifdef IMPULSERC_HELIX

vtx_process_state(currentMillis, vtxBand, vtxChannel);

#endif //IMPULSERC_HELIX

#ifdef KKAUDIOVARIO

if (millis() > timer.audiolooptimer) {

timer.audiolooptimer += 20;

AudioVarioUpdate();

}

#endif //KKAUDIOVARIO

#ifdef MSP_SPEED_HIGH

if ((currentMillis – previous_millis_sync) >= sync_speed_cycle) // (Executed > NTSC/PAL hz 33ms)

{

previous_millis_sync = previous_millis_sync + sync_speed_cycle;

#ifdef CANVAS_SUPPORT

if (!fontMode && !canvasMode)

#else

if (!fontMode)

#endif

{

#ifdef PROTOCOL_MSP

if (timer.GUI_active == 0) {

mspWriteRequest(MSP_ATTITUDE, 0);

}

#endif

}

}

#endif //MSP_SPEED_HIGH

if ((currentMillis – previous_millis_low) >= lo_speed_cycle) // 10 Hz (Executed every 100ms)

{

previous_millis_low = previous_millis_low + lo_speed_cycle;

timer.tenthSec++;

timer.halfSec++;

timer.Blink10hz = !timer.Blink10hz;

#ifdef USEMS5837

MS5837sensor.read();

#endif //USEMS5837

if (GPS_fix && armed) {

if (Settings[S_UNITSYSTEM])

tripSum += GPS_speed * 0.0032808; // 100/(100*1000)*3.2808=0.0016404 cm/sec –> ft/50msec

else

tripSum += GPS_speed * 0.0010; // 100/(100*1000)=0.0005 cm/sec –> mt/50msec (trip var is float)

trip = (uint32_t) tripSum;

}

#if defined (GPSOSD)

handleRawRC();

#endif

#ifndef KISS

amperagesum += amperage;

#else

if (!Settings[S_MWAMPERAGE])

amperagesum += amperage;

#endif // KISS

#ifndef GPSOSD

#ifdef MSP_SPEED_MED

#ifdef CANVAS_SUPPORT

if (!fontMode && !canvasMode)

#else

if (!fontMode)

#endif

{

#ifdef PROTOCOL_MSP

if (timer.GUI_active == 0) {

mspWriteRequest(MSP_ATTITUDE, 0);

}

#endif // KISS

}

#endif //MSP_SPEED_MED

#endif //GPSOSD

} // End of slow Timed Service Routine (100ms loop)

if ((currentMillis – previous_millis_high) >= hi_speed_cycle) // 33 Hz or 100hz in MSP high mode.

{

previous_millis_high = previous_millis_high + hi_speed_cycle;

uint16_t MSPcmdsend = 0;

if (queuedMSPRequests == 0)

queuedMSPRequests = modeMSPRequests;

uint32_t req = queuedMSPRequests & -queuedMSPRequests;

queuedMSPRequests &= ~req;

switch (req) {

case REQ_MSP_STATUS:

MSPcmdsend = MSP_STATUS;

break;

#ifdef INTRO_FC

case REQ_MSP_FC_VERSION:

MSPcmdsend = MSP_FC_VERSION;

break;

#endif

case REQ_MSP_RC:

MSPcmdsend = MSP_RC;

break;

case REQ_MSP_RAW_GPS:

MSPcmdsend = MSP_RAW_GPS;

break;

case REQ_MSP_COMP_GPS:

MSPcmdsend = MSP_COMP_GPS;

break;

case REQ_MSP_ATTITUDE:

MSPcmdsend = MSP_ATTITUDE;

break;

case REQ_MSP_ALTITUDE:

MSPcmdsend = MSP_ALTITUDE;

break;

case REQ_MSP_ANALOG:

MSPcmdsend = MSP_ANALOG;

break;

case REQ_MSP_MISC:

MSPcmdsend = MSP_MISC;

break;

case REQ_MSP_RC_TUNING:

MSPcmdsend = MSP_RC_TUNING;

break;

case REQ_MSP_PID_CONTROLLER:

MSPcmdsend = MSP_PID_CONTROLLER;

break;

case REQ_MSP_PID:

MSPcmdsend = MSP_PID;

break;

#ifdef MENU_SERVO

case REQ_MSP_SERVO_CONF:

MSPcmdsend = MSP_SERVO_CONF;

break;

#endif

#ifdef USE_MSP_PIDNAMES

case REQ_MSP_PIDNAMES:

MSPcmdsend = MSP_PIDNAMES;

break;

#endif

#ifdef CORRECTLOOPTIME

case REQ_MSP_LOOP_TIME:

MSPcmdsend = MSP_LOOP_TIME;

break;

#endif

case REQ_MSP_BOX:

#ifdef BOXNAMES

MSPcmdsend = MSP_BOXNAMES;

#else

MSPcmdsend = MSP_BOXIDS;

#endif

break;

case REQ_MSP_FONT:

MSPcmdsend = MSP_OSD;

break;

#if defined DEBUGMW

case REQ_MSP_DEBUG:

MSPcmdsend = MSP_DEBUG;

break;

#endif

#if defined SPORT

case REQ_MSP_CELLS:

MSPcmdsend = MSP_CELLS;

break;

#endif

#ifdef MULTIWII_V24

case REQ_MSP_NAV_STATUS:

if (MwSensorActive & mode.gpsmission)

MSPcmdsend = MSP_NAV_STATUS;

break;

#endif

#ifdef CORRECT_MSP_BF1

case REQ_MSP_CONFIG:

MSPcmdsend = MSP_CONFIG;

break;

#endif

#ifdef MENU_FIXEDWING

case REQ_MSP_FW_CONFIG:

MSPcmdsend = MSP_FW_CONFIG;

break;

#endif

#ifdef HAS_ALARMS

case REQ_MSP_ALARMS:

MSPcmdsend = MSP_ALARMS;

break;

#endif

#ifdef MSP_RTC_SUPPORT

case REQ_MSP_RTC:

MSPcmdsend = MSP_RTC;

break;

#endif

case REQ_MSP_VOLTAGE_METER_CONFIG:

MSPcmdsend = MSP_VOLTAGE_METER_CONFIG;

break;

#ifdef MSPV2

case REQ_MSP2_INAV_AIR_SPEED:

MSPcmdsend = MSP2_INAV_AIR_SPEED;

break;

#endif

}

if (!fontMode) {

#ifdef KISS

Serial.write(0x20);

#elif defined SKYTRACK

DrawSkytrack();

#elif defined PROTOCOL_MSP

#ifdef CANVAS_SUPPORT

if (!canvasMode)

#endif

{

if (MSPcmdsend != 0) {

#ifdef MSPV2

if (MSPcmdsend > 254) {

mspV2WriteRequest(MSPcmdsend, 0);

}

else

#endif

{

mspWriteRequest(MSPcmdsend & 0xff, 0);

}

}

}

#endif // KISS

MAX7456_DrawScreen();

}

ProcessSensors(); // using analogue sensors

if ( allSec < INTRO_DELAY ) {

displayIntro();

timer.lastCallSign = onTime – CALLSIGNINTERVAL;

}

else

{

if (armed) {

previousarmedstatus = 1;

timer.disarmed = OSDSUMMARY;

if (configMode == 1)

configExit();

}

#ifndef HIDESUMMARY

if (previousarmedstatus && !armed) {

configPage = 0;

ROW = 10;

COL = 1;

configMode = 1;

setMspRequests();

}

#else

if (previousarmedstatus && !armed) {

previousarmedstatus = 0;

configMode = 0;

}

#endif //HIDESUMMARY

if (configMode)

{

displayConfigScreen();

}

#ifdef CANVAS_SUPPORT

else if (canvasMode)

{

// In canvas mode, we don't actively write the screen; just listen to MSP stream.

if (lastCanvas + CANVAS_TIMO < currentMillis) {

MAX7456_ClearScreen();

canvasMode = false;

}

}

#endif

else

{

setMspRequests();

#if defined USE_AIRSPEED_SENSOR

useairspeed();

#endif //USE_AIRSPEED_SENSOR

displayHorizon(MwAngle[0], MwAngle[1]);

#if defined FORCECROSSHAIR

displayForcedCrosshair();

#endif //FORCECROSSHAIR

displayVoltage();

displayVidVoltage();

if ((rssi > Settings[S_RSSI_ALARM]) || (timer.Blink2hz))

displayRSSI();

if (((amperage / 10) < Settings[S_AMPERAGE_ALARM]) || (timer.Blink2hz))

displayAmperage();

if (((!ampAlarming()) || timer.Blink2hz))

displaypMeterSum();

#ifdef DISPLAYEFFICIENCYTIME

displayRemainingTime();

#endif

displayFlightTime();

#if defined (DISPLAYWATTS)

displayWatt();

#endif //DISPLAYWATTS

#if defined (DISPLAYEFFICIENCY)

displayEfficiency();

#endif //DISPLAYEFFICIENCY

#if defined (DISPLAYAVGEFFICIENCY)

displayAverageEfficiency();

#endif //DISPLAYAVGEFFICIENCY

#ifdef SHOW_TEMPERATURE

displayTemperature();

#endif

#ifdef VIRTUAL_NOSE

displayVirtualNose();

#endif

displayArmed();

displayCurrentThrottle();

#ifdef FREETEXTLLIGHTS

if (MwSensorActive & mode.llights) displayCallsign(getPosition(callSignPosition));

#elif FREETEXTGIMBAL

if (MwSensorActive & mode.camstab) displayCallsign(getPosition(callSignPosition));

#else

if (fieldIsVisible(callSignPosition)) {

#ifdef PILOTICON

if (Settings[S_CALLSIGN_ALWAYS] == 3) {

displayIcon(getPosition(callSignPosition));

}

else if (Settings[S_CALLSIGN_ALWAYS] == 2) {

if ( (onTime > (timer.lastCallSign + CALLSIGNINTERVAL))) { // Displays 4 sec every 60 secs

if ( onTime > (timer.lastCallSign + CALLSIGNINTERVAL + CALLSIGNDURATION))

timer.lastCallSign = onTime;

displayCallsign(getPosition(callSignPosition));

}

}

else if (Settings[S_CALLSIGN_ALWAYS] == 1) {

displayCallsign(getPosition(callSignPosition));

}

#else

if (Settings[S_CALLSIGN_ALWAYS] == 2) {

if ( (onTime > (timer.lastCallSign + CALLSIGNINTERVAL))) { // Displays 4 sec every 60 secs

if ( onTime > (timer.lastCallSign + CALLSIGNINTERVAL + CALLSIGNDURATION))

timer.lastCallSign = onTime;

displayCallsign(getPosition(callSignPosition));

}

}

else if (Settings[S_CALLSIGN_ALWAYS] == 1) {

displayCallsign(getPosition(callSignPosition));

}

#endif

}

#endif

displayHeadingGraph();

displayHeading();

#if defined SUBMERSIBLE

#if defined USEMS5837

MwAltitude = (float)100 * MS5837sensor.depth();

#endif //USEMS5837

if (millis() > timer.fwAltitudeTimer) { // To make vario from Submersible altitude

timer.fwAltitudeTimer += 1000;

MwVario = MwAltitude – previousfwaltitude;

previousfwaltitude = MwAltitude;

}

#endif // SUBMERSIBLE

displayAltitude(((int32_t)GPS_altitude*10),MwGPSAltPosition,SYM_GPS_ALT);

displayAltitude(MwAltitude/10,MwAltitudePosition,SYM_ALT);

displayClimbRate();

displayVario();

displayNumberOfSat();

displayDirectionToHome();

displayDistanceToHome();

displayDistanceTotal();

displayDistanceMax();

displayAngleToHome();

displayGPSdop();

// displayfwglidescope(); //note hook for this is in display horizon function

if (!armed)

GPS_speed = 0;

display_speed(GPS_speed, GPS_speedPosition, SYM_SPEED_GPS);

display_speed(AIR_speed, AIR_speedPosition, SYM_SPEED_AIR);

displayWindSpeed(); // also windspeed if available

displayItem(MAX_speedPosition, speedMAX, SYM_MAX, speedUnitAdd[Settings[S_UNITSYSTEM]], 0 );

displayGPSPosition();

#ifdef GPSTIME

if (fieldIsVisible(GPS_timePosition))

displayDateTime();

#endif

#ifdef MAPMODE

mapmode();

#endif

displayMode();

#ifdef I2CERROR

displayI2CError();

#endif

#ifdef SPORT

if (MwSensorPresent)

displayCells();

#endif

#ifdef DEBUG

displayDebug();

#endif

#ifdef HAS_ALARMS

displayAlarms();

#endif

#ifdef MAV_STATUS

displayMAVstatustext();

#endif

}

}

} // End of fast Timed Service Routine (50ms loop)

if (timer.halfSec >= 5) {

timer.halfSec = 0;

timer.Blink2hz = ! timer.Blink2hz;

#if 0

// XXX What is this for? On-Arm power setting?

// XXX May be "Power up at minimum power, then goto stored power on-arming."for stick based blind operation or something similar

// XXX Leave commented out until intension is known.

#ifdef VTX_RTC6705

vtx_set_power(armed ? vtxPower : 0);

#endif // VTX_RTC6705

#endif

}

if (millis() > timer.seconds + 1000) // this execute 1 time a second

{

#if defined (GPSTIME) && !defined (UBLOX)

datetime.unixtime++;

updateDateTime(datetime.unixtime);

#endif //GPSTIME

if (timer.armedstatus > 0)

timer.armedstatus–;

timer.seconds += 1000;

timer.tenthSec = 0;

#ifdef MAV_STATUS

if (timer.MAVstatustext > 0)

timer.MAVstatustext–;

#endif

#ifdef DEBUGDPOSLOOP

framerate = timer.loopcount;

timer.loopcount = 0;

#endif

#ifdef DEBUGDPOSPACKET

packetrate = timer.packetcount;

timer.packetcount = 0;

#endif

#ifdef DEBUGDPOSRX

serialrxrate = timer.serialrxrate;

timer.serialrxrate = 0;

#endif

onTime++;

#if defined(AUTOCAM) || defined(MAXSTALLDETECT)

if (!fontMode)

MAX7456CheckStatus();

#endif

#ifdef ALARM_GPS

if (timer.GPS_active == 0) {

GPS_numSat = 0;

}

else {

timer.GPS_active–;

}

#endif // ALARM_GPS

if (timer.disarmed > 0) {

timer.disarmed–;

}

if (timer.MSP_active > 0) {

timer.MSP_active–;

}

if (timer.GUI_active > 0) {

timer.GUI_active–;

#if defined GPSOSD

timer.GPS_initdelay = 2;

#endif

}

#if defined(GPSOSD) && !defined(NAZA)

if (timer.GPS_initdelay == 1) {

GPS_SerialInit();

}

if (timer.GPS_initdelay > 0) {

timer.GPS_initdelay–;

}

#endif

if (!armed) {

#ifndef MAPMODENORTH

armedangle = MwHeading;

#endif

}

else {

flyTime++;

flyingTime++;

configMode = 0;

setMspRequests();

}

allSec++;

/*

if((timer.accCalibrationTimer==1)&&(configMode)) {

mspWriteRequest(MSP_ACC_CALIBRATION,0);

timer.accCalibrationTimer=0;

}

*/

#ifdef PROTOCOL_MSP

if ((timer.magCalibrationTimer == 1) && (configMode)) {

mspWriteRequest(MSP_MAG_CALIBRATION, 0);

timer.magCalibrationTimer = 0;

}

if (timer.magCalibrationTimer > 0) timer.magCalibrationTimer–;

#endif

if (timer.rssiTimer > 0) timer.rssiTimer–;

}

// setMspRequests();

serialMSPreceive(1);

#ifdef FIXEDLOOP

delay(1);

#endif

} // End of main loop

#endif //main loop

//––––––––––––––––––––––––

//FONT management

uint8_t safeMode() {

return 1; // XXX

}

// Font upload queue implementation.

// Implement a window for curr + the previous 6 requests.

// First-chance to retransmit at curr-3 (retransmitQueue & 0x10)

// First-chance retransmit marked as used at retransmitQueue |= 0x01

// 2 to N-chance retransmit marked at curr-6 (retransmitQueue & 0x02)

void initFontMode() {

if (armed || configMode || fontMode || !safeMode())

return;

// queue first char for transmition.

retransmitQueue = 0x80;

fontMode = 1;

// setMspRequests();

}

void fontCharacterReceived(uint8_t cindex) {

if (!fontMode)

return;

uint8_t bit = (0x80 >> (nextCharToRequest – cindex));

// Just received a char..

if (retransmitQueue & bit) {

// this char war requested and now received for the first time

retransmitQueue &= ~bit; // mark as already received

write_NVM(cindex); // Write to MVRam

}

}

int16_t getNextCharToRequest() {

if (nextCharToRequest != lastCharToRequest) { // Not at last char

if (retransmitQueue & 0x02) { // Missed char at curr-6. Need retransmit!

return nextCharToRequest – 6;

}

if ((retransmitQueue & 0x11) == 0x10) { // Missed char at curr-3. First chance retransmit

retransmitQueue |= 0x01; // Mark first chance as used

return nextCharToRequest – 3;

}

retransmitQueue = (retransmitQueue >> 1) | 0x80; // Add next to queue

return nextCharToRequest++; // Notice post-increment!

}

uint8_t temp1 = retransmitQueue & ~0x01;

uint8_t temp2 = nextCharToRequest – 6;

if (temp1 == 0) {

fontMode = 0; // Exit font mode

// setMspRequests();

return -1;

}

// Already at last char… check for missed characters.

while (!(temp1 & 0x03)) {

temp1 >>= 1;

temp2++;

}

return temp2;

}

//––––––––––––––––––––––––

// MISC

void resetFunc(void)

{

#ifdef I2C_UB_SUPPORT

WireUB.end();

#endif

#if defined HARDRESET

MCUSR &= ~(1 << WDRF);

WDTCSR |= (1 << WDCE) | (1 << WDE) | (1 << WDIE);

WDTCSR = (1 << WDIE) | (0 << WDE) | (0 << WDP3) | (0 << WDP2) | (0 << WDP1) | (1 << WDP0);

while (1);

#elif defined BOOTRESET

asm volatile (" jmp 0x3800");

#else

asm volatile (" jmp 0");

#endif

}

void setMspRequests() {

if (fontMode) {

modeMSPRequests = REQ_MSP_FONT;

}

else if (configMode) {

modeMSPRequests =

REQ_MSP_STATUS |

REQ_MSP_RAW_GPS |

REQ_MSP_ATTITUDE |

REQ_MSP_RAW_IMU |

REQ_MSP_ALTITUDE |

REQ_MSP_RC_TUNING |

REQ_MSP_PID_CONTROLLER |

REQ_MSP_ANALOG |

#ifdef USE_MSP_PIDNAMES

REQ_MSP_PIDNAMES |

#endif

REQ_MSP_PID |

#ifdef CORRECTLOOPTIME

REQ_MSP_LOOP_TIME |

#endif

#ifdef CORRECT_MSP_BF1

REQ_MSP_CONFIG |

#endif

#ifdef DEBUGMW

REQ_MSP_DEBUG |

#endif

#ifdef SPORT

REQ_MSP_CELLS |

#endif

#ifdef HAS_ALARMS

REQ_MSP_ALARMS |

#endif

#ifdef MENU_SERVO

REQ_MSP_SERVO_CONF |

#endif

#ifdef MENU_FIXEDWING

REQ_MSP_FW_CONFIG |

#endif

#ifdef USE_FC_VOLTS_CONFIG

#if defined(CLEANFLIGHT) || defined(BETAFLIGHT)

REQ_MSP_VOLTAGE_METER_CONFIG |

#else

REQ_MSP_MISC |

#endif

#endif

REQ_MSP_RC;

}

else {

modeMSPRequests =

REQ_MSP_STATUS |

#ifdef DEBUGMW

REQ_MSP_DEBUG |

#endif

#ifdef SPORT

REQ_MSP_CELLS |

#endif

#ifdef MSP_USE_GPS

REQ_MSP_RAW_GPS |

REQ_MSP_COMP_GPS |

#endif //MSP_USE_GPS

#ifdef MSP_USE_BARO

REQ_MSP_ALTITUDE |

#endif //MSP_USE_BARO

#ifdef HAS_ALARMS

REQ_MSP_ALARMS |

#endif

#ifdef MSP_SPEED_LOW

REQ_MSP_ATTITUDE |

#endif

#ifdef MSP_USE_ANALOG

REQ_MSP_ANALOG |

#endif //MSP_USE_ANALOG

#ifdef MSPV2

REQ_MSP2_INAV_AIR_SPEED |

#endif

REQ_MSP_RC;

if (!armed) {

modeMSPRequests |=

REQ_MSP_BOX |

#if defined INTRO_FC && defined PROTOCOL_MSP

REQ_MSP_FC_VERSION |

#endif // INTRO_FC && defined PROTOCOL_MSP

#ifdef USE_FC_VOLTS_CONFIG

#if defined(CLEANFLIGHT) || defined(BETAFLIGHT)

REQ_MSP_VOLTAGE_METER_CONFIG |

#else

REQ_MSP_MISC |

#endif // defined(CLEANFLIGHT) || defined(BETAFLIGHT)

#endif // USE_FC_VOLTS_CONFIG

#ifdef MSP_RTC_SUPPORT

REQ_MSP_RTC |

#endif // MSP_RTC_SUPPORT

0;

}

#if defined MULTIWII_V24

if (MwSensorActive & mode.gpsmission)

modeMSPRequests |= REQ_MSP_NAV_STATUS;

#endif

}

if (timer.GUI_active != 0) {

modeMSPRequests = 0;

queuedMSPRequests = 0;

}

queuedMSPRequests &= modeMSPRequests; // so we do not send requests that are not needed.

}

void writeEEPROM(void) // OSD will only change 8 bit values. GUI changes directly

{

for (uint8_t en = 0; en < EEPROM_SETTINGS; en++) {

EEPROM.write(en, Settings[en]);

}

for (uint8_t en = 0; en < EEPROM16_SETTINGS; en++) {

uint16_t pos = EEPROM_SETTINGS + (en * 2);

uint16_t data = Settings16[en];

write16EEPROM(pos, data);

}

EEPROM.write(0, EEPROMVER);

}

void readEEPROM(void)

{

for (uint8_t en = 0; en < EEPROM_SETTINGS; en++) {

Settings[en] = EEPROM.read(en);

}

// config dependant – set up interrupts

#if defined INTC3

if (Settings[S_MWRSSI] == 1) {

DDRC &= ~(1 << DDC3); // PORTC |= (1 << PORTC3);

//DDRC &=B11110111;

#ifndef INTD5

Settings[S_PWM_PPM] = 0;

#endif

}

#endif

#if defined INTD5

DDRD &= ~(1 << DDD5); // PORTD |= (1 << PORTD5);

#endif

cli();

#if defined INTC3

if (Settings[S_MWRSSI] == 1) {

if ((PCMSK1 & (1 << PCINT11)) == 0) {

PCICR |= (1 << PCIE1);

PCMSK1 |= (1 << PCINT11);

}

}

#endif

#if defined INTD5

if ((PCMSK2 & (1 << PCINT21)) == 0) {

PCICR |= (1 << PCIE2);

PCMSK2 |= (1 << PCINT21);

}

#endif

sei();

// config dependant – voltage reference

#ifdef IMPULSERC_HELIX

//Ignore setting because this is critical to making sure we can detect the

//VTX power jumper being installed. If we aren't using 5V ref there is

//the chance we will power up on wrong frequency.

Settings[S_VREFERENCE] = 1;

#endif //IMPULSERC_HELIX

if (Settings[S_VREFERENCE])

analogReference(DEFAULT);

else

analogReference(INTERNAL);

for (uint8_t en = 0; en < EEPROM16_SETTINGS; en++) {

uint16_t pos = (en * 2) + EEPROM_SETTINGS;

Settings16[en] = EEPROM.read(pos);

uint16_t xx = EEPROM.read(pos + 1);

Settings16[en] = Settings16[en] + (xx << 8);

}

// Read screen layouts

uint16_t EEPROMscreenoffset = EEPROM_SETTINGS + (EEPROM16_SETTINGS * 2) + (screenlayout * POSITIONS_SETTINGS * 2);

for (uint8_t en = 0; en < POSITIONS_SETTINGS; en++) {

uint16_t pos = (en * 2) + EEPROMscreenoffset;

screenPosition[en] = EEPROM.read(pos);

uint16_t xx = (uint16_t)EEPROM.read(pos + 1) << 8;

screenPosition[en] = screenPosition[en] + xx;

if (flags.signaltype == 1) {

uint16_t x = screenPosition[en] & 0x1FF;

if (x > LINE06) screenPosition[en] = screenPosition[en] + LINE;

if (x > LINE09) screenPosition[en] = screenPosition[en] + LINE;

}

}

}

void checkEEPROM(void)

{

uint8_t EEPROM_Loaded = EEPROM.read(0);

if (EEPROM_Loaded != EEPROMVER) {

for (uint8_t en = 0; en < EEPROM_SETTINGS; en++) {

EEPROM.write(en, pgm_read_byte(&EEPROM_DEFAULT[en]));

}

for (uint8_t en = 0; en < EEPROM16_SETTINGS; en++) {

uint16_t pos = EEPROM_SETTINGS + (en * 2);

uint16_t data = pgm_read_word(&EEPROM16_DEFAULT[en]);

write16EEPROM(pos, data);

}

uint16_t base = EEPROM_SETTINGS + (EEPROM16_SETTINGS * 2);

for (uint8_t ilayout = 0; ilayout < 3; ilayout++) {

for (uint8_t en = 0; en < POSITIONS_SETTINGS * 2; en++) {

uint16_t pos = base + (en * 2);

uint16_t data = pgm_read_word(&SCREENLAYOUT_DEFAULT[en]);

write16EEPROM(pos, data);

}

base += POSITIONS_SETTINGS * 2;

}

}

}

void write16EEPROM(uint16_t pos, uint16_t data)

{

EEPROM.write(pos , data & 0xff);

EEPROM.write(pos + 1, data >> 8 );

}

void gpsdistancefix(void) {

int8_t speedband;

static int8_t oldspeedband;

static int8_t speedcorrection = 0;

if (GPS_distanceToHome < 10000) speedband = 0;

else if (GPS_distanceToHome > 50000) speedband = 2;

else {

speedband = 1;

oldspeedband = speedband;

}

if (speedband == oldspeedband) {

if (oldspeedband == 0) speedcorrection–;

if (oldspeedband == 2) speedcorrection++;

oldspeedband = speedband;

}

GPS_distanceToHome = (speedcorrection * 65535) + GPS_distanceToHome;

}

void ProcessSensors(void) {

/*

special note about filter: last row of array = averaged reading

*/

//––––– ADC sensor / PWM RSSI / FC data read into filter array

static uint8_t sensorindex;

uint16_t sensortemp;

for (uint8_t sensor = 0; sensor < SENSORTOTAL; sensor++) {

sensortemp = analogRead(sensorpinarray[sensor]);

//– override with FC voltage data if enabled

if (sensor == 0) {

if (Settings[S_MAINVOLTAGE_VBAT]) {

sensortemp = MwVBat;

}

}

#ifdef MAV_VBAT2

// assume vbat2 on FC if vbat1 is

if (sensor == 1) {

if (Settings[S_MAINVOLTAGE_VBAT]) {

sensortemp = MwVBat2;

}

}

#endif

//– override with PWM, FC RC CH or FC RSSI data if enabled

if (sensor == 4) {

if (Settings[S_MWRSSI] == 3) { // RSSI from a TX channel

sensortemp = MwRcData[Settings[S_RSSI_CH]] >> 1;

}

else if (Settings[S_MWRSSI] == 2) { // RSSI from Flight controller

sensortemp = MwRssi;

}

else if (Settings[S_MWRSSI] == 1) { // RSSI from direct OSD – PWM

sensortemp = pwmRSSI >> 1;

if (sensortemp == 0) { // timed out – use previous

sensortemp = sensorfilter[sensor][sensorindex];

}

}

else { // RSSI from direct OSD – Analog

}

}

//– Apply filtering

#if defined FILTER_HYSTERYSIS // Hysteris incremental averaged change

static uint16_t shfilter[SENSORTOTAL];

int16_t diff = (sensortemp << FILTER_HYSTERYSIS) – shfilter[sensor];

if (abs(diff) > (FHBANDWIDTH << FILTER_HYSTERYSIS)) {

shfilter[sensor] = sensortemp << FILTER_HYSTERYSIS;

}

else if (diff > 0) {

shfilter[sensor]++;

}

else if (diff < 0) {

shfilter[sensor]–;

}

sensorfilter[sensor][SENSORFILTERSIZE] = (shfilter[sensor] >> FILTER_HYSTERYSIS << 3);

#elif defined FILTER_AVG // Use averaged change

sensorfilter[sensor][SENSORFILTERSIZE] = sensorfilter[sensor][SENSORFILTERSIZE] – sensorfilter[sensor][sensorindex];

sensorfilter[sensor][sensorindex] = (sensorfilter[sensor][sensorindex] + sensortemp) >> 1;

sensorfilter[sensor][SENSORFILTERSIZE] = sensorfilter[sensor][SENSORFILTERSIZE] + sensorfilter[sensor][sensorindex];

#else // No filtering

sensorfilter[sensor][SENSORFILTERSIZE] = sensortemp << 3;

#endif

}

//––––– Voltage

if (!Settings[S_MAINVOLTAGE_VBAT]) { // not MWII

uint16_t voltageRaw = sensorfilter[0][SENSORFILTERSIZE];

if (!Settings[S_VREFERENCE]) {

voltage = float(voltageRaw) * Settings[S_DIVIDERRATIO] * (DIVIDER1v1);

}

else {

voltage = float(voltageRaw) * Settings[S_DIVIDERRATIO] * (DIVIDER5v);

}

}

else {

voltage = sensorfilter[0][SENSORFILTERSIZE] >> 3;

}

vidvoltageWarning = Settings[S_VIDVOLTAGEMIN];

uint16_t vidvoltageRaw = sensorfilter[1][SENSORFILTERSIZE];

if (!Settings[S_VREFERENCE]) {

vidvoltage = float(vidvoltageRaw) * Settings[S_VIDDIVIDERRATIO] * (DIVIDER1v1);

}

else {

vidvoltage = float(vidvoltageRaw) * Settings[S_VIDDIVIDERRATIO] * (DIVIDER5v);

}

//––––– Temperature

#ifdef SHOW_TEMPERATURE

#if defined USEMS5837

temperature = (float)(10 * MS5837sensor.temperature());

#elif defined PROTOCOL_MAVLINK && !defined USE_TEMPERATURE_SENSOR

#else

temperature = (sensorfilter[3][SENSORFILTERSIZE] >> 3);

temperature = map (temperature, Settings16[S16_AUX_ZERO_CAL], Settings16[S16_AUX_CAL], 0 , 100);

#endif

#endif

//––––– Current

if (Settings[S_MWAMPERAGE] == 2) { // Virtual

uint32_t Vthrottle = map(MwRcData[THROTTLESTICK], LowT, HighT, 0, 100);

Vthrottle = constrain(Vthrottle, 0, 100);

amperage = (Vthrottle + (Vthrottle * Vthrottle * 0.02)) * Settings16[S16_AMPDIVIDERRATIO] * 0.01;

if (armed)

amperage += Settings16[S16_AMPZERO];

else

amperage = Settings16[S16_AMPZERO];

}

else if (Settings[S_MWAMPERAGE] == 1) { // from FC

if (MWAmperage < 0)

amperage = (MWAmperage – AMPERAGE_DIV / 2) / AMPERAGE_DIV;

else

amperage = (MWAmperage + AMPERAGE_DIV / 2) / AMPERAGE_DIV;

}

else { // Analog

amperage = sensorfilter[2][SENSORFILTERSIZE] >> 3;

amperage = map(amperage, Settings16[S16_AMPZERO], AMPCALHIGH, AMPCALLOW, Settings16[S16_AMPDIVIDERRATIO]);

if (amperage < 0) amperage = 0;

}

//––––– RSSI

rssi = sensorfilter[4][SENSORFILTERSIZE] >> 3; // filter and remain 16 bit

if (configMode) {

if ((timer.rssiTimer == 15)) {

Settings16[S16_RSSIMAX] = rssi; // tx on

}

if ((timer.rssiTimer == 1)) {

Settings16[S16_RSSIMIN] = rssi; // tx off

timer.rssiTimer = 0;

}

}

rssi = map(rssi, Settings16[S16_RSSIMIN], Settings16[S16_RSSIMAX], 0, 100);

rssi = constrain(rssi, 0, 100);

//––––– For filter support

sensorindex++;

if (sensorindex >= SENSORFILTERSIZE)

sensorindex = 0;

}

#if defined INTD5

ISR(PCINT1_vect) { // Default Arduino A3 Atmega C3

#define PWMPIN1 DDC3

static uint16_t s_LastRising = 0;

uint16_t l_PulseDuration;

uint16_t l_CurrentTime = micros();

uint8_t l_pinstatus = PINC;

sei();

l_PulseDuration = l_CurrentTime – s_LastRising;

if ((l_pinstatus & (1 << PWMPIN1))) { // transitioned to high

s_LastRising = l_CurrentTime;

if ((l_PulseDuration) > 3000) { //assume this is PPM gap so exit

return;

}

}

else{ // // transitioned to low

if ((900 < l_PulseDuration) && (l_PulseDuration < 2200)) {

#if defined DEBUG

pwmval1 = l_PulseDuration;

#endif

pwmRSSI = l_PulseDuration;

}

}

}

#else // INTD5

ISR(PCINT1_vect) { // Default Arduino A3 Atmega C3

#define PWMPIN1 DDC3

static uint8_t s_RCchan = 1;

static uint16_t s_LastRising = 0;

static uint16_t s_LastFalling = 0;

uint16_t l_PulseDuration;

uint16_t l_CurrentTime = micros();

uint8_t l_pinstatus = PINC;

sei();

l_PulseDuration = l_CurrentTime – s_LastRising;

if ((l_pinstatus & (1 << PWMPIN1))) { // transitioned to high

s_LastRising = l_CurrentTime;

if ((l_PulseDuration) > 3000) { //assume this is PPM gap so exit

s_RCchan = 1;

return;

}

if (Settings[S_PWM_PPM]) {//ppm

if (s_RCchan <= TX_CHANNELS) { // avoid array overflow if > standard ch PPM

MwRcData[s_RCchan] = l_PulseDuration; // Val updated

}

if (s_RCchan == 4){

#if defined DEBUG

pwmval1 = l_PulseDuration;

#endif

#if defined TX_GUI_CONTROL

reverseChannels();

#endif // TX_GUI_CONTROL

}

s_RCchan++;

}

}

else{ // // transitioned to low

s_LastFalling = l_CurrentTime;

if (!Settings[S_PWM_PPM]) {//pwm

if ((900 < l_PulseDuration) && (l_PulseDuration < 2250)) {

#if defined DEBUG

pwmval1 = l_PulseDuration;

#endif

#ifdef NAZA

Naza.mode = 0;

if (l_PulseDuration > NAZA_PMW_HIGH) {

Naza.mode = NAZA_MODE_HIGH;

}

else if (l_PulseDuration > NAZA_PMW_MED) {

Naza.mode = NAZA_MODE_MED;

}

else if (l_PulseDuration > NAZA_PWM_LOW) {

Naza.mode = NAZA_MODE_LOW;

}

#endif

#ifdef PWM_OSD_SWITCH

MwRcData[rcswitch_ch] = l_PulseDuration;

#elif defined PWM_THROTTLE

MwRcData[THROTTLESTICK] = l_PulseDuration;

#else

pwmRSSI = l_PulseDuration;

#endif

}

}

}

}

#endif // INTD5

#if defined INTD5

ISR(PCINT2_vect) { // // Secondary Arduino D5 Atmega D5

#define PWMPIN2 DDD5

static uint8_t s_RCchan = 1;

static uint16_t s_LastRising = 0;

static uint16_t s_LastFalling = 0;

uint16_t l_PulseDuration;

uint16_t l_CurrentTime = micros();

uint8_t l_pinstatus = PIND;

sei();

l_PulseDuration = l_CurrentTime – s_LastRising;

if ((l_pinstatus & (1 << PWMPIN2))) { // transitioned to high

s_LastRising = l_CurrentTime;

if ((l_PulseDuration) > 3000) { //assume this is PPM gap so exit

s_RCchan = 1;

return;

}

if (Settings[S_PWM_PPM]) {//ppm

if (s_RCchan <= TX_CHANNELS) { // avoid array overflow if > standard ch PPM

MwRcData[s_RCchan] = l_PulseDuration; // Val updated

}

if (s_RCchan == 4){

#if defined DEBUG

pwmval2 = l_PulseDuration;

#endif

#if defined TX_GUI_CONTROL

reverseChannels();

#endif // TX_GUI_CONTROL

}

s_RCchan++;

}

}

else{ // // transitioned to low

s_LastFalling = l_CurrentTime;

if (!Settings[S_PWM_PPM]) {//pwm

if ((950 < l_PulseDuration) && (l_PulseDuration < 2150)) {

pwmval2 = l_PulseDuration;

#ifdef NAZA

Naza.mode = 0;

if (l_PulseDuration > NAZA_PMW_HIGH) {

Naza.mode = NAZA_MODE_HIGH;

}

else if (l_PulseDuration > NAZA_PMW_MED) {

Naza.mode = NAZA_MODE_MED;

}

else if (l_PulseDuration > NAZA_PWM_LOW) {

Naza.mode = NAZA_MODE_LOW;

}

#endif

#ifdef PWM_OSD_SWITCH

MwRcData[rcswitch_ch] = l_PulseDuration;

#else

MwRcData[THROTTLESTICK] = l_PulseDuration;

#endif

}

}

}

}

#endif // INTD5

void EEPROM_clear() {

for (int i = 0; i < 512; i++)

EEPROM.write(i, 0);

}

int16_t filter16( int16_t filtered, int16_t raw, const byte k) {

filtered = filtered + (raw – filtered) / k; // note extreme values may overrun. 32 it if required.

return filtered;

}

int32_t filter32( int32_t filtered, int32_t raw, const byte k) {

filtered = filtered + (raw – filtered) / k; // note extreme values may overrun. 32 it if required.

return filtered;

}

int32_t filter32F( float filtered, float raw, const byte k) {

filtered = filtered + (raw – filtered) / k; // note extreme values may overrun. 32 it if required.

return (int32_t) filtered;

}

#if defined USE_AIRSPEED_SENSOR

void useairspeed() {

#define AIRDENSITY 1.225 // Density of air kg/m3

int16_t pressuresensor = (int16_t)(sensorfilter[3][SENSORFILTERSIZE] >> 3) – Settings16[S16_AUX_ZERO_CAL];

if (pressuresensor < 0) {

pressuresensor = 0;

}

pressuresensor = ((int32_t)pressuresensor * Settings16[S16_AUX_CAL]) / 500;

constrain(pressuresensor, -410, 410);

int16_t Pa = map(pressuresensor, -410, 410, -2000, 2000); // Pressure – actual pascals

AIR_speed = (int32_t)100 * sqrt((2 * Pa) / AIRDENSITY); // Speed required in cm/s

}

#endif //USE_AIRSPEED_SENSOR

void reverseChannels(void) { //ifdef (TX_REVERSE)

for (uint8_t i = 1; i <= 4; i++) {

if (Settings[S_TX_CH_REVERSE] & (1 << i))

MwRcData[i] = 3000 – MwRcData[i];

}}

Similar Posts