Servo motor

Kako servo motor SG90 zna svoj kut: impuls svakih 20 ms, tri žice, zasebno napajanje, biblioteka Servo na Arduinu i koliko mu treba da se okrene.

PWM Servo motor
Tri plava servo motora s bijelim polugama i kabelima s tri žice, a pokraj njih poluge raznih oblika i sitni vijci

Što je servo motor?

Običan motor se vrti dok ima struje. Servo motor se okrene na kut koji mu zadamo i ondje ostane. Program mu kaže okreni se na 90°, a servo se okrene i drži taj položaj, čak i kad ga nešto gura. Najčešći servo motori okreću se od 0° do 180°, pola kruga.

Servo motore nalaziš u modelima aviona i brodova, gdje pomiču kormila, u robotskim rukama i u igračkama koje mašu. U našem fliperu servo motor podiže stupić između palica da spasi kuglicu.

Servo motor SG90 sa strane: plavo kućište s dvije uške za vijke, na vrhu osovina s bijelom polugom, a sa strane tri žice, smeđa, crvena i narančasta, koje završavaju u konektoru. Uz konektor piše GND, 5V i D9

Na radionicama koristimo mali servo SG90. Težak je oko 9 grama, a ima tri žice:

  • smeđa je GND (minus),
  • crvena je napajanje od oko 5 V (plus),
  • narančasta je signal, koji servu kaže na koji se kut treba okrenuti.

Neki servo motori imaju žice drukčijih boja: crnu za GND, crvenu za plus i bijelu ili žutu za signal.

Na osovinu se nataknu poluge (engleski horn) raznih oblika.

Što je u servo motoru?

Servo motor prerezan sa strane. Brojevi: 1 motor koji stoji ispod zupčanika, sa svojim malim zupčanikom na vrhu osovine, 2 niz zupčanika gledanih sa strane, svaki s velikim i malim kotačem na istoj uspravnoj osovini, koji se redom zahvaćaju, 3 osovina koja izlazi na vrhu, 4 potenciometar ispod velikog zupčanika, na istoj osovini, 5 zelena pločica sa sklopom na dnu, iz koje izlaze tri žice
  1. Motor: mali i brz, ali slab.
  2. Zupčanici smanjuju brzinu i povećavaju silu. Motor se okrene mnogo puta dok se osovina okrene jednom.
  3. Osovina izlazi iz kućišta. Na nju stavljamo polugu.
  4. Potenciometar je spojen s osovinom i okreće se s njom, pa njegov otpor kaže na kojem je kutu osovina.
  5. Sklop uspoređuje kut koji traži program s kutom koji pokazuje potenciometar. Ako se razlikuju, okreće motor dok ne budu jednaki. Ako netko pomakne polugu, sklop to primijeti i vrati je.

To je kao kad posegneš za čašom: oči stalno javljaju koliko je ruka daleko, pa ruka uspori kad se približi i stane točno na čaši.

Ne okreći servo motor rukom

Zupčanici usporavaju motor mnogo puta, pa se motor okrene mnogo puta dok se poluga okrene jednom. Kad polugu okrećeš rukom, cijeli niz zupčanika vrtiš unatrag, a mali motor na kraju niza mora se vrtjeti mnogo brže od poluge. Za to treba velika sila, a nju nose sitni zubi plastičnih zupčanika. Zub pukne ili se odlomi, a servo motor poslije preskače, zapinje ili se više ne okreće.

Ako je servo motor uključen, još je gore: sklop primijeti da je poluga pomaknuta i svom snagom je gura natrag. Ti i motor tada se borite, a motor troši najveću struju i grije se. Polugu zato namještaj programom. Ako je ipak moraš pomaknuti rukom, na primjer da nataknuš polugu, najprije isključi napajanje, a zatim je okreći polako i bez sile.

Kako servo zna koji kut želimo?

Narančasta žica svakih 20 milisekundi dobije kratki impuls: napon skoči na 5 V i vrati se na 0 V. Kut ovisi o tome koliko impuls traje:

Tri grafa napona na signalnoj žici kroz 25 ms, svaki s dva impulsa razmaknuta 20 ms. Gore impuls traje 1 ms i poluga pokazuje 0 stupnjeva, u sredini 1,5 ms i 90 stupnjeva, dolje 2 ms i 180 stupnjeva. Crtkane crte kroz sva tri grafa pokazuju gdje završava svaki od tri impulsa

  • impuls od 1 ms znači 0°,
  • impuls od 1,5 ms znači 90°,
  • impuls od 2 ms znači 180°.

Dulji impuls znači veći kut. Točne granice malo se razlikuju od servo motora do servo motora, ali sredina je gotovo uvijek 1,5 ms.

Pozicijski i kontinuirani servo motor

Servo motori dolaze u dvije vrste:

  • Pozicijski servo motor (engleski positional) okreće se na zadani kut i ondje stane, najčešće negdje od 0° do 180°. Takav je SG90, a o njemu govori ostatak ove stranice. Impuls mu kaže gdje da stane.
  • Kontinuirani servo motor (engleski continuous rotation), na primjer FS90R, vrti se u krug, kao običan motor. Impuls mu kaže na koju stranu i koliko brzo da se vrti: oko 1,5 ms znači stani, 1 ms punom brzinom na jednu stranu, a 2 ms punom brzinom na drugu. Impulsi između daju sporije okretanje.

Kontinuirani servo motor nema potenciometra koji bi pratio osovinu, a nema ni graničnika koji zaustavlja osovinu na kraju puta. Zato ne zna na kojem je kutu i ne može stati na zadanom kutu, ali se može vrtjeti bez kraja. Izvana obje vrste izgledaju gotovo isto, pa vrstu provjeri na naljepnici ili u opisu proizvoda.

Pozicijski servo motor pomiče polugu, kormilo ili stupić u fliperu. Kontinuirani pokreće kotače malog robota, kojem tada ne treba poseban modul za upravljanje motorima.

Gdje ga koristimo?

  • Na micro:bit radionici servo motor spajamo na robota Maqueen. U MakeCodeu ga okreće blok servo write pin, iz kategorije Pins.
  • Na Arduino radionici servo motori SG90 dio su flipera: podižu stupić, a mogu pomicati i druge mehanizme.

Servo motor na Arduino radionici

Od ovog mjesta stranica je za Arduino radionicu.

Napajanje

Dok se SG90 okreće, troši nekoliko stotina mA, a kad krene ili zapne, i do 0,7 A. To je mnogo više nego što smije dati pin, a i više nego što bez problema daje USB. Kad servo krene, napon na Arduinu padne i Arduino se može sam resetirati. Zato servo motor ima svoje napajanje:

Shema: narančasta signalna žica servo motora na D9 Arduina. Crvena žica na plus zasebnog izvora od 5 V, smeđa na njegov minus. Preko izvora je elektrolitski kondenzator od 470 µF. Minus izvora spojen je s GND Arduina

  • Signal ide s pina D9, a struju servo uzima iz zasebnog izvora od 5 V, na primjer adaptera.
  • Kondenzator od 470 µF uz servo motor daje struju u trenutku kad servo krene, pa napon ne padne. U fliperu, s tri servo motora, stavljamo 1000 µF.
  • GND izvora i GND Arduina moraju biti spojeni. Servo mjeri impuls prema svom GND-u, pa bez zajedničke mase ne zna kad impuls počinje (zabluda 8).

Biblioteka Servo

Impulse ne moramo slati sami. To radi biblioteka Servo, koja dolazi s Arduino IDE-om:

// C++
#include <Servo.h>                     // biblioteka Servo, dolazi s Arduino IDE-om

Servo servo;                           // naš servo motor
const int pinServa = 9;                // narančasta žica

void setup() {
  servo.attach(pinServa);              // od sada D9 svakih 20 ms šalje impuls
}

void loop() {
  servo.write(0);                      // brzo na 0°
  delay(1000);
  servo.write(90);                     // brzo na 90°
  delay(1000);
  servo.write(180);                    // brzo na 180°
  delay(1000);
  for (int kut = 180; kut >= 0; kut--) {
    servo.write(kut);                  // polako natrag, stupanj po stupanj
    delay(15);
  }
  delay(1000);
}

Što je novo u programu?

  • Servo servo; – Stvara objekt za jedan servo motor. Za dva servo motora napravili bismo dva objekta s različitim imenima.
  • servo.attach(pinServa); – Od tog trenutka biblioteka sama, u pozadini, šalje impulse na D9, dok program radi nešto drugo.
  • servo.write(90); – Zadaje kut u stupnjevima, od 0 do 180. Biblioteka ga pretvori u trajanje impulsa: 0° je 544 µs, a 180° je 2400 µs. To je malo šire od 1 do 2 ms, da bi i servo motori sa širim granicama dosegli kraj.
  • Polagano okretanje dobivamo tako da kut mijenjamo za jedan stupanj i malo pričekamo. Cijeli put od 180° do 0° tada traje 181 · 15 ms ≈ 2,7 s.

Na Unu biblioteka Servo koristi brojač vremena Timer1, pa dok je spojen ijedan servo motor, analogWrite() ne radi na pinovima 9 i 10. Zato servo motori i palice u fliperu nisu na istom Unu. Uno može upravljati s najviše 12 servo motora.

Koji pinovi Una znaju PWM?

Arduino Uno ima PWM na šest pinova: D3, D5, D6, D9, D10 i D11. Na pločici su označeni valovitom crtom (~). Svaki par tih pinova pokreće jedan brojač vremena, pa naredba ili biblioteka koja zauzme brojač zauzme i njegova dva pina:

PinoviBrojačPWMTko ga još koristi
D5 i D6Timer0oko 980 Hzmillis() i delay(), a PWM i dalje radi
D9 i D10Timer1oko 490 Hzbiblioteka Servo
D3 i D11Timer2oko 490 Hztone() i biblioteka IRremote

Servo motoru PWM pin ne treba: biblioteka Servo šalje impulse na bilo koji digitalni pin. Signal servo motora nije isto što i PWM iz analogWrite(). analogWrite() pin pali i gasi oko 490 puta u sekundi, a servo motor očekuje jedan impuls svakih 20 ms, 50 u sekundi. Sve pinove Una opisuje stranica Raspored pinova.

Koliko se dugo okreće?

servo.write() ne čeka da se servo okrene. Program odmah ide dalje, a servo se još okreće. Graf pokazuje izračun za SG90, koji se na 4,8 V okrene za 60° u 0,1 s:

Kut poluge kroz 1,2 sekunde. Crtkano: program u 0,2 s traži 180°, a u 0,8 s 90°. Narančasto: poluga se okreće stalnom brzinom, a pred ciljem uspori. Do 180° stigne oko 0,37 s nakon naredbe, a do 90° oko 0,22 s nakon druge naredbe
Servo se okreće najvećom brzinom, a pred ciljem uspori, jer sklop smanji struju motora kad se razlika smanji. Od 0° do 180° treba mu oko 0,37 s. Zato iza servo.write() u programu treba delay() ili provjera s millis(), prije nego što računamo da je poluga stigla.

Kad servo zuji

Ako servo na kraju puta zuji i grije se, poluga ili mehanizam ne daju mu da stigne do zadanog kuta. Sklop tada stalno gura motor, a motor troši najveću struju. Kut tada smanji, na primjer na 10° do 170°, ili granice impulsa zadaj sam: servo.attach(pinServa, 1000, 2000); šalje 1000 µs za 0° i 2000 µs za 180°.

Kontinuirani servo motor u programu

Kontinuiranim servo motorom upravljamo istom bibliotekom Servo, ali servo.write() sada ne zadaje kut, nego brzinu: servo.write(90) ga zaustavi, servo.write(0) vrti ga punom brzinom na jednu stranu, a servo.write(180) na drugu. Vrijednosti između, na primjer 70 ili 110, daju sporije okretanje. Ako se servo kod 90 i dalje polako vrti, probaj 88 ili 92: točka u kojoj stoji malo se razlikuje od servo motora do servo motora.

Polagan pokret: servo motor bez trzaja

servo.write() pošalje servo motoru novi kut, a servo motor do njega jurne punom brzinom. Nagli pokret i naglo zaustavljanje udaraju zupčanike u servo motoru i mehanizam koji on pomiče: polugu, stupić ili zglob robotske ruke. Što je mehanizam teži, udarac je jači. Zato veliki pokret ne zadajemo odjednom, nego servo motor polako vodimo do cilja, malim koracima:

Planiraj prije nego što programiraš:
zapamti kut na kojem je servo motor i kut do kojeg treba stići (cilj)
ponavljaj:
ako je servo motor na cilju:
ne radi ništa
inače, ako je od zadnjeg koraka prošlo 15 ms:
pomakni kut za 1° prema cilju
pošalji novi kut servo motoru
// C++
#include <Servo.h>

Servo servo;
const int pinServa = 9;
const int POCETNI_KUT = 90;            // gdje servo stoji kad se program pokrene
const unsigned long KORAK_MS = 15;     // svakih 15 ms jedan stupanj: 60° u 0,9 s

int kutServa = POCETNI_KUT;            // kut koji je servo zadnji put dobio
int ciljServa = POCETNI_KUT;           // kut do kojeg servo polako ide
unsigned long zadnjiKorakServa = 0;    // kada se servo zadnji put pomaknuo
unsigned long zadnjaPromjena = 0;      // kada smo zadnji put promijenili cilj

void setup() {
  servo.write(POCETNI_KUT);            // prvi impuls već nosi početni kut, a ne 90°
  servo.attach(pinServa);
}

void loop() {
  // primjer: svake 4 sekunde novi cilj, naizmjence 160° i 20°
  if (millis() - zadnjaPromjena >= 4000) {
    zadnjaPromjena = millis();
    if (ciljServa == 160) {
      ciljServa = 20;
    } else {
      ciljServa = 160;
    }
  }

  pomakniPremaCilju(servo, kutServa, ciljServa, zadnjiKorakServa, KORAK_MS);
}

// Pomakne servo za jedan stupanj prema cilju, ako je od zadnjeg koraka prošlo korakMs.
// Parametri s & su reference: funkcija mijenja baš one varijable s kojima je pozvana.
void pomakniPremaCilju(Servo &s, int &kut, int cilj, unsigned long &zadnjiKorak, unsigned long korakMs) {
  if (kut == cilj) {
    return;                            // već smo na cilju
  }
  if (millis() - zadnjiKorak < korakMs) {
    return;                            // još nije vrijeme za sljedeći korak
  }
  zadnjiKorak = millis();
  if (cilj > kut) {
    kut = kut + 1;
  } else {
    kut = kut - 1;
  }
  s.write(kut);
}

Što je novo u programu?

  • kutServa, ciljServa i zadnjiKorakServa – Program pamti kut koji je servo motor zadnji put dobio, kut do kojeg treba stići i kada je napravljen zadnji korak. Ostatak programa mijenja samo ciljServa.
  • pomakniPremaCilju(servo, kutServa, ciljServa, zadnjiKorakServa, KORAK_MS); – Funkcija sve što treba dobiva kao parametre i ne dira nijednu globalnu varijablu. Zato je možeš koristiti za više servo motora. Za drugi servo motor, na primjer ruka, napraviš njegove varijable i pozoveš istu funkciju: pomakniPremaCilju(ruka, kutRuke, ciljRuke, zadnjiKorakRuke, KORAK_MS);. Oba se tada miču istodobno, svaki prema svom cilju.
  • int &kut – Znak & kaže da je parametar referenca. Funkcija ne dobiva kopiju vrijednosti, nego mijenja baš varijablu s kojom je pozvana: kad poveća kut, poveća se kutServa. Isto vrijedi za zadnjiKorak i za servo motor Servo &s. cilj i korakMs funkcija samo čita, pa ih dobiva kao obične parametre, bez &.
  • millis() - zadnjiKorak < korakMs – Korak se napravi tek kad prođe 15 ms od prošlog, kao smirivanje u lekciji o tipkalima. Program pritom ne čeka, pa loop() u međuvremenu može čitati tipke i senzore. Polagano okretanje s delay() iz programa iznad zaustavilo bi sve ostalo dok se servo motor ne okrene.
  • KORAK_MS – Određuje brzinu: s 15 ms servo motor prijeđe 60° u 0,9 s, šest puta sporije nego punom brzinom (0,1 s za 60°). S 30 ms ide upola sporije.
  • servo.write(POCETNI_KUT); prije servo.attach() – Program ne zna gdje je poluga kad se Arduino uključi. Biblioteka bi bez toga prvim impulsom poslala 90°, pa bi servo motor jurnuo onamo. Ovako prvi impuls nosi početni kut. Na početni kut servo motor i dalje skoči, ali samo ako ga je netko pomaknuo dok je bio isključen.

U pravom programu cilj ne mijenja sat, nego događaj: pritisak tipke, poruka drugog Arduina ili očitanje senzora. Za još mekši pokret korak može biti veći dok je servo motor daleko od cilja, a manji kad mu se približi.

Što smo naučili?

  • Servo motor se okrene na zadani kut i drži ga. U njemu su motor, zupčanici, potenciometar i sklop koji ih uspoređuje.
  • SG90 ima tri žice: smeđu (GND), crvenu (5 V) i narančastu (signal).
  • Kut određuje trajanje impulsa koji dolazi svakih 20 ms: oko 1 ms za 0°, 1,5 ms za 90° i 2 ms za 180°.
  • Servo motor ima svoje napajanje od 5 V, sa zajedničkim GND-om i kondenzatorom.
  • servo.write() ne čeka da se servo okrene. Na Unu biblioteka Servo isključi PWM na pinovima 9 i 10.
  • Veliki pokret zadajemo malim koracima prema cilju, s millis(), da ne udaramo zupčanike i mehanizam.
  • Pozicijski servo motor stane na zadanom kutu. Kontinuirani nema potenciometra, pa se vrti u krug, a impuls mu zadaje smjer i brzinu.

Mali kviz

1. Po čemu se servo motor razlikuje od običnog motora?
Običan motor se vrti dok ima struje. Servo motor se okrene na zadani kut i ondje ostane.
2. Kako servo zna na kojem je kutu?
S osovinom je spojen potenciometar, čiji otpor ovisi o kutu. Sklop ga uspoređuje s kutom koji traži program i okreće motor dok ne budu jednaki.
3. Koliko traje impuls za 90° i koliko često dolazi?
Oko 1,5 ms, a dolazi svakih 20 ms.
4. Kad servo krene, Arduino se resetira. Što je najvjerojatnije krivo?
Servo se napaja s Arduina ili USB-a i na trenutak povuče preveliku struju, pa napon padne. Servo treba svoje napajanje od 5 V, kondenzator uz sebe i GND spojen s GND-om Arduina.
5. Program zove servo.write(180) i odmah zatim čita senzor na poluzi. Zašto senzor još ne javlja da je poluga stigla?
servo.write() ne čeka. SG90 treba oko 0,37 s da se okrene od 0° do 180°, pa programu treba delay() ili provjera s millis().
6. Na Unu je servo motor na D9, a LED-icu na D10 želiš prigušiti s analogWrite(). Zašto ne radi?
Biblioteka Servo na Unu koristi Timer1, isti brojač vremena koji radi PWM na pinovima 9 i 10. LED-icu premjesti na pin 3, 5, 6 ili 11.
7. Po čemu se kontinuirani servo motor razlikuje od pozicijskog?
Pozicijski se okrene na zadani kut i ondje stane. Kontinuirani nema potenciometra ni graničnika, pa se vrti u krug kao običan motor, a impuls mu zadaje smjer i brzinu: oko 1,5 ms znači stani.