… enn um kóða

Áður hefur verið fjallað um kóðann sem les bókstafi úr Hershey stafrófinu og þýðir færslu milli tveggja punkta í kartesísku hnitakerfi yfir á form sem lýsir færslu í kerfinu. Nú er komið að því að fjalla stuttlega um hvernig þær upplýsingar eru túlkaðar til að framkvæma sjálfa færsluna. Sú túlkun fer fram í kóða sem er keyrður á arduino örtölvunni. (Neðst í færslunni eru hlekkir sem vísa beint á allan kóða sem notaður var í verkefninu)

Virkni kóðans er í stórum dráttum tvískipt:

1) Í aðal-lykkju forritsins er tekið á móti skipunum frá notanda og gengið úr skugga um að þær skipanir séu á réttu sniði

2) Innri skeiðklukka keyrir reglulega interrupt lykkju sem stýrir því hvernig mótorar eru púlsaðir.

Fyrst þarf að hlaða inn söfnum af kóða frá þriðja aðila. Þau eru: math.h, servo.h og mstimer2.h.

#include <MsTimer2.h>  // timer library, fyrir interrupt adgerdina
#include <math.h>      // math library, fyrir reikningsadgerdir
#include <Servo.h>     // servo library, fyrir servo motor sem styrir armi
Servo penna_servo;     // skapa tilvik af Servo klasanum: penna_servo

Einu nauðsynlegu pinnarnir eru eftirfarandi:
*einn pinni fyrir servó mótorinn sem færir penna af og á töflu
*pinnar sem stýra skrefi og stefnu fyrir hvorn stepper-mótor (samtals fjórir pinnar)
Útgangar örtölvunnar eru skilgreindir hér:

int STEPV = 38;                   // pinni 38 = skref vinstri motor
int DIRV = 36;                    // pinni 36 = stefna vinstri motor
int STEPH = 42;                   // pinni 42 = skref haegri motor
int DIRH = 40;                    // pinni 40 = stefna haegri motor
int SERVO_PIN = 9;                // pinni 9 = servo motor sem styrir penna

Skilgreindar eru (global) breytur:

int dir1, rm1, dir2, rm2, pen;    //geyma serial skilabod fra tolvu
int err;    //villumelding fyrir serial samskipti (1 ef villa, 0 annars)
int pennastada=0;    //"pennastada" man i hvada stodu penni er (0 er uppi)
int fast_motor;    //motor sem skal snuast fleiri skref (0: vinstri, 1: haegri)
volatile int stada=0;    //telur fjolda skrefa sem fast_motor hefur tekid
int skref=0;    //fjoldi skrefa motors sem skal taka fleiri skref en hinn
int stutt=0;    //fjoldi skrefa motors sem skal taka faerri skref en hinn
long stuttskref=0;
long fyrra_stuttskref=0;
int idle=1;    //gefur til kynna hvort taeki se ad framkvaema skipun eda ekki

Breyturnar „stuttskref“ og „fyrra_stuttskref“ eru skilgreindar fyrir interrupt aðgerðina (sjá síðar).

Þá eru gerðar eftirfarandi frumstillingar:
*allir pinnar skilgreindir sem úttak
*allir pinnar settir LOW í upphafi
*interrupt skeiðklukka er stillt, þannig að fallið timerfcn() mun keyrast með tveggja millisekúndna millibili eftir að hún er ræst.

void setup() {
  Serial.begin(115200);           // Serial samskipti um USB, baud rate
                                  // baud rate: 115200 bitar a sekundu.
  pinMode(STEPV,OUTPUT);          // Utgangar fyrir styringu stepper motora.
  pinMode(DIRV,OUTPUT);           // ...
  pinMode(STEPH,OUTPUT);          // ...
  pinMode(DIRH,OUTPUT);           // ...
  digitalWrite(STEPV,LOW);        // Allir utgangar eru LOW i upphafi.
  digitalWrite(DIRV,LOW);         // ...
  digitalWrite(STEPH,LOW);        // ...
  digitalWrite(DIRH, LOW);        // ...

  penna_servo.attach(SERVO_PIN);  // skilgreini pinna fyrir servo-motor
  penna_servo.write(90);          // penni er uppi i upphafi

  // stilla skeidklukku
  MsTimer2::set(2, timerfcn);     // MsTimer2::set(lota i millisekundum, interrupt adgerd)
}

Lítum þá á aðal-lykkju forritsins sem les sífellt af serial-porti til að taka á móti nýjum skipunum. Í upphafi hverrar umferðar er sett „err=0;“ sem þýðir „í upphafi aflesturs er engin villa“. Þá er gerð tilraun til að lesa af serial-porti streng sem hefur formið “fnnnnfnnnnu” (útskýrt í síðasta pósti).

Föllin formerki(), tolustaftur() og uppnidur() sjá til þess að réttar upplýsingar séu skráðar í hvert svið (að formerki komi þar sem búist er við formerki og svo framvegis), eða annars mun vera gefin villumelding (err=1;). Þegar aflestri er lokið er athugað hvort einhver  villumelding hafi orðið.

Ef err=1, þýðir það að ekki hefur tekst að lesa streng  á réttu formi og í því tilfelli er sent til baka spurningarmerki til að gefa notandanum tækifæri á að senda skipunina aftur og forritið fer aftur á byrjunarreit lykkjunnar. Það skal tekið fram, að ef serial helst tómur verða gefin villuskilaboð, og röð spurningamerkja send til notanda.

Ef err=0 við lok lesturs, merkir það að tekist hefur að taka á móti skipun sem er á skiljanlegu formi. Í því tilfelli eru skilaboðin send inn í fallið motors.

Ef serial buffer er að fyllast, er sendur til baka bókstafurinn K, til að láta notanda vita að örtölvan þurfi tíma til að vinna úr því sem þegar hefur verið sent.

void loop() {
  err=0;                          // adur en lestur hefst er engin villumelding !

  // serial lestur hefst    
  dir1=formerki();	          // 1=rettsaelis, -1=rangsaelis, 0=error
  rm1=tolustafur()*1000;          // thusundir skrefa a vinstri motor
  rm1+=tolustafur()*100;          // hundrud skrefa a vinstri motor
  rm1+=tolustafur()*10;           // tugir skrefa a vinstri motor
  rm1+=tolustafur()*1;            // einstok skref a vinstri motor

  dir2=formerki();                // 1=rettsaelis, -1=rangsaelis, 0=error
  rm2=tolustafur()*1000;          // thusundir skrefa a haegri motor
  rm2+=tolustafur()*100;          // hundrud skrefa a haegri motor
  rm2+=tolustafur()*10;           // tugir skrefa a haegri motor
  rm2+=tolustafur()*1;            // einstok skref a haegri motor

  pen=uppnidur();                 // 1=penni upp, 0=penni nidur / error

  if (idle&&err) {	          // ef plotter er idle og error ...
    Serial.println("?");          // ... senda "?" a serial.
  } else {		  	  // Annars hreyfa motora
    idle=0;                       // Plotterinn er ekki lengur laus
    motors(rm1,rm2,dir1,dir2,pen); // Skilgreina hreyfinguna
  }

  if (Serial.available()>=64) {  // ef serial buffer er ad fyllast
    Serial.println("K");         // ... senda 'K' a serial
  }

  delay(10);                     // leyfa arduino ad 'anda'
}

Hér eru föllin formerki(), tolustafur() og uppnidur() sem ganga úr skugga um að strengurinn sem er lesinn inn sé á réttu formi.

// skipun les formerki (+/-)
int formerki(void) {
  char c;
  if (err) {
    return 0;                    // ef komin villa: skila 0
  }
  c=Serial.read();
  if (c=='+') {                  // ef lesinn er "+": skila 1
    return 1;
  }
  if (c=='-') {                  // ef lesinn er "-": skila -1
    return -1;
  }
  err=1;                         // annars er komin villa
  return 0;                      // => skila 0
}

// skipun les tolustaf (0 til 9)
int tolustafur(void) {
  int c;
  if (err) {              
    return 0;                    // ef komin villa: skila 0
  }
  c=Serial.read()-48;
  if ((c<0) || (c>10)) {         // ef lesid er eitthad annad en tolustafur
    err=1;                       // er komin villa
    return 0;                    // => skila 0
  }
  return c;                      // annars skila tolustafnum (0 til 9)
}

// skipun les 'U' eda 'N'
int uppnidur() {
 char c;
 if (err) {                  
   return 0;                     // ef komin villa: skila 0
 } 
 c=Serial.read();
 if (c=='u' || c=='U') {         // ef lesid er "u" eda "U"
   return 1;                     // skila 1
 }
 if (c=='n' || c=='N') {         // ef lesid er "n" eda "N"
   return 0;                     // skila 0
 }
 err=1;                          // annars er komin villa
 return 0;                       // => skila 0
}

Hér á undan var minnst á fallið motors. Fallið stillir snúningsátt hvors mótors og sér til þess að penninn sé í réttri stöðu. Fallið merkir sérstaklega hvor mótorinn eigi að fara fleiri skref (fast_motor=0 ef vinstri motor fer fleiri skref, annars 1), og skráir hærri fjölda skrefa í breytuna skref, en lægri skrefafjölda í breytuna stutt. Að lokum er sjálfvirka interrupt lykkjan, sem stýrir púlsun mótoranna, ræst

void step_motors(int trm1,int trm2,int tdir1,int tdir2, int penni) {
  // Her eru breytur stilltar fyrir interrupt adgerdina. Interrupt adgerdin
  // mun sja til thess a motorar eru hreyfdir a medan adal hluti forritsins
  // getur tekid a moti serial skilabodum fra tolvu.
  // Fyrst tharf ad stilla penna (penni upp eda penni nidur)
  
  // Ef penni skal vera uppi OG penni var adur nidri
  if ((penni==1)&&(penni!=pennastada)) {
    for (int T = 0; T < 25; T++) {          // lyfta penna rolega upp
       penna_servo.write(40+T);        
       delay(50);
       }
       pennastada=penni;                    // uppfaera stodu pennans
  }
  
  // Ef penni skal vera nidri OG penni var adur uppi
  if ((penni==0)&&(penni!=pennastada)) {
     for (int T = 0; T < 25; T++) {         // setja penna rolega nidur
        penna_servo.write(65-T);
        delay(20+T*2);
     }
     pennastada=penni;                      // uppfaera stodu pennans
  }
  
  // Skilgreina fjolda skrefa og snuningsatt a hvorum motor fyrir sig
  rm1=trm1; rm2=trm2; dir1=tdir1; dir2=tdir2;
  
  stada=0;                     // i upphafi hefur ekkert skref verid tekid
  if (trm1>trm2) {             // ef skref fyrir vinstri motor eru fleiri
    skref=trm1;                // er "skref" skilgreint sem sa fjoldi
    stutt=trm2;                // og "stutt" sem fjoldi skrefa a haegri motor
    fast_motor=0;              // => vinstri motor snyst hradar
  } else {                     // annars... alveg ofugt
    skref=trm2;
    stutt=trm1;
    fast_motor=1;              // => haegri motor snyst hradar
  }
  
  if (dir1==1) {               // stilla snuningsatt vinstri motors
    digitalWrite(DIRV,LOW);
  } else {
    digitalWrite(DIRV,HIGH);
  }
  
  if (dir2==1) {               // stilla snuningsatt haegri motors
    digitalWrite(DIRH,LOW);
  } else {
    digitalWrite(DIRH,HIGH);
  }
  
  MsTimer2::start();           // raesa interrupt skeidklukku (hefja hreyfingu)
}

Ef vitað er hvor mótorinn eigi að fara fleiri skref, er hægt að láta þann mótor snúast á hámarkshraða, en láta hinn mótorinn sem snýst færri skref, snúast hlutfallslega hægar. Eftir að sjálfvirka interrupt lykkjan hefur verið gangsett, mun mótorinn sem snýst lengra því vera púlsaður í hverri umferð, en í hverri umferð er einnig athugað hvort að kominn sé tími til að púlsa hægari mótorinn. Það er gert með inerpóleringu (línulegri brúun), sem er innbyggt fall í arduino tungumálinu (map). Það er notað þannig:

 stuttskref=map(stada,1,skref,1,stutt);
// eða almennt: y=map(x,x0,x1,y0,y1);

Þá lítur interrupt callback fallið „timerfcn“ þannig út:

void timerfcn() {
  // mappa stodugildi hradari motorsins yfir a haegari motor
  // til ad vita hvort kominn se timi til ad taka skref a motornum
  // sem snyst haegar
  stuttskref=map(stada,1,skref,1,stutt);
  long t=stuttskref-fyrra_stuttskref;      // mismunur fra tvhi sidast (ath. mun aldrei verda >1)
  if (t>=1) {
     fyrra_stuttskref=stuttskref;
     skref_motor(!fast_motor);
  }
  skref_motor(fast_motor);
  
  stada++;
  
  if (stada==skref) {            // ef hreyfingu er lokid...
    fyrra_stuttskref=0;          // 
    idle=1;                      // nu er plotterinn laus
    MsTimer2::stop();            // stodva interrupt skeidklukku
  }
}

Eini tilgangur fallsins skref_motor er að púlsa viðkomandi mótor.

void skref_motor(char m) {
  int i;
  if (m==0) {
    digitalWrite(STEPV, LOW);    // Thessi breyting fra LOW i HIGH myndar pulsinn sem
    digitalWrite(STEPV, HIGH);   // segir motorstyringingunni ad taka skref
    } else {
    digitalWrite(STEPH, LOW);    // Thessi breyting fra LOW i HIGH myndar pulsinn sem
    digitalWrite(STEPH, HIGH);   // segir motorstyringingunni ad taka skref
  } 
}

Þá er umfjöllun um kóðann lokið, svo höfum þetta nóg í bili.

Hér er hægt að sækja í arduino kóðann (*.pde og *.pdf).
Hér er hægt að sækja MATLAB kóðann (*.m)

Categories: Uncategorized

Kóðinn

Til þess að koma letri á töfluna þurfti að leggjast í mikla kóðavinnu. Fyrst var búið til fall í matlab sem tók inn tvo punkta p1 og p2 og síðan upphafsstaðsetningu pennans. Kallað var í fallið á þennan hátt hér, [faerslan,vx,vy]=faersla(p1,p2,vx,vy). Breytan ‘faerslan’ voru þær upplýsingar sem sendar voru til arduino borðsins og voru á forminu -3000+1000n þar sem formerkin tákna snúningsátt mótoranna og tölurnar fjölda skrefa sem átti að taka á hvorum mótor. Hver hringur á mótorunum var 2000 skref. Í inntaksstikunum p1 og p2 voru einnig upplýsingar um hvort penninn ætti að vera uppi eða niðri í hreyingunni á milli punktanna og gefur síðasti bókstafurinn í ‘faerslan’ það til kynna n fyrir niðri og u fyrir uppi. Þá var færslu fallið komið.

Nú þurfti að fá letur sem hægt væri að breyta í einfaldar punktafærslur. Fundum við library á netinu sem er með mörg þúsund tákn sem búið er að breyta á svona punktaform. Settið er hægt að finna hér: http://paulbourke.net/dataformats/hershey/. Hver stafur er táknaður með streng og tökum við dæmi um stafinn H til þess að útskýra hvernig þetta gengur fyrir sig. Strengurinn fyrir H lítur þá svona út.

8 9MWOMOV RUMUV ROQUQ

Þar sem fyrsti tölustafurinn táknar númer stafs í skjalinu. Næsti tölustafur fjölda hnútpunkta í skjalinu. Síðan koma pör af bókstöfum ‘MW’ táknar því einn punkt og eru hnitin reiknuð út frá miðju stafrófsins eða ‘R’. ‘MW’ er þá upphafspunktur stafsins og er byrjað að teikna þaðan, síðan koma næstu punktar koll af kolli. Þegar runa byrjar á bókstafnum ‘R’ segir það okkur að lyfta þarf pennanum áður en hann er færður í næsta hnit. ‘MW’ er þá t.d. hnitið ‘M’-‘R’=-5 og ‘W’-‘R’=5 og út fæst [-5,5]. Búið var til forrit sem tók inn þessa stafi og breytti hverjum og einum þeirra í runu af punktum sem síðan voru settir inn í teikniðfallið ‘faerslan’. Var þá öllum stöfunum breytt yfir á form sem stepperarnir gátu skilið.

Síðan var að lokum búið til fall sem gat lesið inn streng t.d. MAGNUS og breytt hverjum staf í punktaform sem síðan kallaði í færslufallið og breytti yfir í færslu fyrri mótoranna. Dæmi um kóðann sem skrifar MAGNUS fylgir hér.

function [hlidrun]=texti(textinn,vx,vy)
hlidrun=[];
for i=1:length(textinn)

	if textinn(i)==' '
		[stafur]=stafir(27)
	else
		a=double(textinn(i))
		%Köllum í fallið sem býr til punktanna fyrir stafina
		stafur=stafir(a-96)
		%Skoðum hvernig hver stafur lítur út
		plot(stafur(:,1),stafur(:,2))
		%Til þess að sjá stafina í plottinu er beðið í tvær sekúndur
		pause(2)
		[n,m]=size(stafur)
		for k=1:n-1
			%Færsla fyrir hvernig punkt reiknuð út fyrir hvern staf
			[faerslan,vx,vy]=faersla(stafur(k,:),stafur(k+1,:),vx,vy)
			%Sett inn í fylki
			hlidrun=[hlidrun;faerslan];
		end
	end
end

Var þá textinn magnús skrifaður á eftirfarandi hátt með því að kalla í eftirfarandi:

[hlidrun]=texti('magnus',vx,vy)

Þar sem vx og vy eru þau hnit sem við byrjum í.

Útkoman úr þessu er þá:

hlidrun =

+0615-0950n
-0615+0950u
+0960-1086n
-0245+0778u
+0245-0778n
-0245+0778u
+0593-0928n
-0621+0568u
+0442-0195u
+0265-0784n
-0265+0784u
+0956-1090n
-0727+0547u
+0349-0157n
-0243+0183u
+0440-0208u
-0244+0188n
-0177+0083n
-0108-0024n
+0050-0170n
+0209-0313n
+0226-0246n
+0241-0179n
+0171-0077n
+0103+0022n
-0205+0308n
-0261+0123u
+0261-0123n
-0348+0163u
+0436-0205u
+0623-0923n
-0623+0923u
+1138-1178n
-0608+0907u
+0608-0907n
-0535+0538u
+0437-0227u
+0409-0604n
+0225-0247n
+0242-0192n
+0086-0047n
+0105+0004n
-0048+0149n
-0398+0590n
-0020-0296u
+0439-0246u
-0243+0199n
-0176+0098n
-0107-0004n
+0069-0100n
+0156-0147n
+0485-0391n
+0155-0148n
+0138-0199n
-0103-0004n
-0171+0093n
-0241+0192n
+0048+0163u
+0436-0246u

Það eina sem er þá eftir er þá að skrifa þessu gögn inn á serial portið og er það gert með fprintf(S,hlidrun). Og fáum við þá fylgjandi mynd.

Myndband af því hvernig þetta er gert fylgir síðan hér að neðan.

Categories: Uncategorized

Upphaf verkefnisins

Í byrjun voru tvær hugmyndir í gangi þær voru.

1. Búa til xy plotter

 

2. Búa til tæki sem skrifar á töflu

Eftir dálitla umhugsun var ákveðið að gera tækið sem skrifar á töflu þar sem það er meira krefjandi verkefni og er meiri nýsköpun fólgin í því en xy plotternum.

Mynd af frumgerð töfluteiknarans má sjá á meðfylgjandi mynd.

 

Eins og sjá má samanstendur lausnin af tveimur mótórum, keðju og tveimur tannhjólum. Það var því ljóst að byrja þurfti á því að redda því. Farið var í GÁP og þar fengum við að gramsa í körfu fyrir ónýt strekkjaratannhjól. Í fyrstu ætluðu þeir síðan að safna ónýtum keðjum og láta okkur fá þegar við kæmum næst. En að lokum gáfu þeir okkur sex splunkunýjar reiðhjólakeðjur og þökkum við þeim kærlega fyrir það.

Farið var í mótóraleiðangur upp í Marel þar sem ljóst var að nauðsynlegt var að fá öfluga steppermótóra til þess að drífa þetta keðjuverk sem var ansi þungt. Þeir hjá Marel voru virkilega þægilegir og fengum við tvo high torque mótóra sem gefnir eru upp fyrir 2,5A á hvern fasa.

Við áttum tvær mótórstýringar þannig allt var tilbúið fyrir fyrstu prufun. Fljótlega kom í ljós að mótórstýringin gat ekki höndlað þann mikla straum sem þurfti til að fá allt út úr mótornum. Þær voru einungis gefnar upp fyrir 750 mA. Þá var farið í fálkann og spurðum við lengi út í lausnir á þessu vandamáli. Að lokum komumst við að því að þeir hjá fálkanum gáfu marga svona mótóra til Keilis. Okkur datt þá í hug að þeir gætu átt einhverjar  mótórstýringar. Við komum okkur þá í samband við Rúnar Unnþórsson sem er forstöðumaður þar. Hann sagðist hafa tvær stýringar og ætlaði að athuga hvort við gætum ekki fengið þær. Það var í lagi og við fengum tvær 7A stýringar til þess að drífa mótórana.

Stýringarnar má sjá hér:

http://www.geckodrive.com/g203v-p-34.html

Mótórana má sjá hér:

http://jvl.dk/default.asp?Action=Details&Item=344 og datasheet http://jvl.dk/default.asp?Action=Details&Item=344

Hér að neðan má síðan sjá nokkrar myndir af fyrstu uppsettningunni.

 

Hér sést uppsetning.

Hér sést síðan betur tengingin við mótórinn.

Hér sést rafbúnaðurinn og gömlu mótórstýringarnar.

 

Staða verkefnisins:

Búið að er að setja upp mekanismann og fá hann til að virka.

Núna fer af stað forritun til þess að hægt sé að skrifa/teikna eitthvað af viti með tækjunum.

Það á síðan eftir að búa til gripinn sem mun halda á pennanum. Nauðsynlegt er að hanna hann svo að hann sé virkilega stöðugur til þess að penninn geti sinnt sínu verkefni af kostgæfni.

 

En höfum þetta nóg í bili.

Categories: Uncategorized