Egy fényérzékelőkkel felszerelt robotnak végig kell mennie egy olyan (esetleg végtelenített) úton, amelyet fekete szigetelőszalagból ragasztottunk fehér (világos) alapra.
Csináltunk egy tankot. A tank közepében volt két a fényérzékelő, lefelé irányítva, 1 mm-re az asztaltól. A tank a lehetőségekhez viszonyítva elég masszív lett, igyekeztünk minél stabilabbat csinálni (rengeteg rúd volt benne merevítőként). Érdekességként megemlíthető, hogy míg a tank felépítménye tengelyesen szimmetrikus, és a fényérzékelők felfüggesztése is tengelyesen szimmetrikus - bár ez a tengely merőleges az előző tengelyre -, addig a tank alapja középpontosan szimmetrikus. Azaz az alapot úgy építettük meg, hogy felépítettünk egy derékszögű háromszöget, egy motorral, egy lánctalppal, majd az egész háromszöget leduplikáltuk, és összedugtuk az eredetivel.
A tank működésével különféle gondjaink adódtak: az eredetileg alkalmazott gumi "lánctalp" jól tapadt ugyan, viszont a hajtókerekek között könnyen megnyúlt, ami fordulás esetén rendkívül zavaró volt. Ezért merev, láncszemekből álló lánctalpra cseréltük, ami nem nyúlt, viszont nagyon csúszott. Ez leginkább akkor derült ki, amikor a félórás optimalizálás eredményeképpen sikerült a fényérzékelőket a pontosság érdekében 1 mm-re süllyeszteni az asztaltól, majd amikor ezek a fényérzékelők fennakadtak a szigetelőszalagból készült kanyarok gombócain és púpjain.
A fényérzékelőket százalékos módban használtuk. Alapvetően az út szélét követtük a két, egymáshoz közeli fényérzékelővel: az egyiknek mindig sötét felett, a másiknak mindig világos felett kell lennie.
A szoftverből több változatot is kipróbáltunk. Lényegi különbség a fordulási igény többféle lekezelésében volt (amikor "elfogyott" az út - a sötét érzékelő világosat kapott -, vagy amikor bekanyarodott a világos érzékelő alá). Próbáltunk ilyenkor tolatni is, visszafordulni is, többféle időzítési értékkel. Végül lemondtunk a tolatásról, mert éles, 270 fok feletti kanyarokban csak rontott a helyzeten (korrigáláskor a tank egyszerűen letolatott az útról, amikor beszorult a kanyar csúcsába).
A fordulás kivitelezésére teszteltük a következő eseteket: ellenkező irányú lánctalp-húzás azonos erővel, különböző erővel; ill. azonos irányú lánctalp húzás különböző erővel. Végül a legegyszerűbb, eredeti forráskódnál maradtunk: kanyarban egyszerűen kanyarodás 1 tizedmásodperc ideig, ellenkező irányba mozgó, azonos sebességű lánctalpakkal, majd tovább előre. Az előre mozgás folyámán busy waiting-gel figyeltük a szenzorokat.
/* tank.nqc */ void elore() { OnRev(OUT_A + OUT_C); } void jobbford() { OnFwd(OUT_C); OnRev(OUT_A); } void balford() { OnFwd(OUT_A); OnRev(OUT_C); } void init() { SetSensor(SENSOR_1, SENSOR_LIGHT); SetSensor(SENSOR_3, SENSOR_LIGHT); SetSensorMode(SENSOR_1, SENSOR_MODE_PERCENT); SetSensorMode(SENSOR_3, SENSOR_MODE_PERCENT); ClearSensor(SENSOR_1); ClearSensor(SENSOR_3); SetPower(OUT_A + OUT_C, 1); } task main() { init(); while (true) { if (SENSOR_1>50) { balford(); Wait(10); } else if (SENSOR_3<50) { jobbford(); Wait(10); } elore(); until ((SENSOR_1>50) || (SENSOR_3<50)); } }Filmek: Video23.avi, Video24.avi.
Egy plotter megvalósítását választottuk feladatnak. A rendelkezésre álló idő szűkössége miatt nem vacakoltunk a plotter interaktív vezérelhetőségének megvalósításával. A plotter három ábra megrajzolására lett felkészítve. Ezeket külön-külön kellett letölteni az RCX-be, mert egyszerre nem férnek bele, ugyanis az RCX compiler-e az alprogramhívások helyére befordítja az egész alprogramot. Ez elég kellemetlen, mert a plotter alapvetően vonalakat tud rajzolni.
A plotter felépítése a következő lett: egy-egy párhuzamos sínpár közé kényszerítve gurult egy-egy kocsin a keresztgerenda. A kocsikat külön motor hajtotta. A motorok fixen helyezkedtek el a felépítmény szélén, az áttétekkel együtt, a kocsikat lánccal rögzítettük a motorokhoz. A keresztgerendán mozgó kocsi fogaskerekekkel hajtotta magát, a keresztgerendán kiképezett sínpárba kényszerítve, fogaslécen járva. A felső kocsiba volt beleszorítva forradalmi megoldással egy golyóstollbél, amit a bemutatón a brute force eljárás segítségével letéptünk róla, hogy szigetelőszalaggal egy ujjnyi vastag hegyű filctollat rögzíthessünk a helyére.
A hardverrel rengeteg problémánk volt. Először is, az RCX azonos utasítás hatására sem volt képes ugyanazt a feszültséget rátenni két különböző kimenetére, ezért amíg erre nem jöttünk rá, a külön kimenettel vezérelt, alsó kocsihajtó motorok közül az egyik rendre lassabb volt, mint a másik, ami kedves láncgyűréshez vezetett. Ezt megszüntetendő, kettévezettük az RCX egyik kimenetét, ami használt.
Másodszor: elég későn jöttünk rá, hogy kénytelenek leszünk irdatlan áttéteket alkalmazni, ha nem akarjuk, hogy az egész miskulancia szétrepüljön az első impulzusra. Mivel szerettünk volna legalább egy 45 fokos irányszögű vonalat rajzolni azonos kimenet-feszültség mellett, ezért muszáj volt ugyanazt az áttétet megvalósítani a felső kocsi esetében, mint amelyet az alsó kocsik esetében alkalmaztunk.
Az áttéteknek azonban volt egy erős szoftver-vonatkozású vonása. Az áttét köztudomásúlag egyszerűen szorzást vagy osztást jelent, jelen esetben osztást, azaz egy lineáris leképezést. Az osztással az volt a problémánk, hogy a motorok minimális szögsebességének csökkentésén kívül a motorok szögsebességtartományát is összezsugorította.
Pl. képzeljünk el egy olyan motort, amely 0-ás fokozatban 5.000-et fordul percenként, 7-es fokozatban pedig 6.000-et (közöttük lineárisan helyezkednek el a fokozatok). Annak érdekében, hogy ezt a motort használni tudjuk, mondjuk alkalmazunk egy 1:1.000 arányú áttétet (osztást). Mi történik ekkor? Az rendben van, hogy a motor használható lesz, hiszen a minimális fordulat lejön 5-re percenként, de a maximális is csak 6 lesz, azaz fújhatjuk a 8 fokozatunkat, kaptunk egy gyakorlatilag bináris motort: vagy megy, vagy nem megy.
A problémát nyilván az okozza, hogy a motor minimális fordulatszáma túl magas a maximális-minimális különbséghez képest. A Lego motorok is ilyenek voltak, tehát az áttétek után bináris motorokhoz jutottunk.
Ez lehetetlenné tette a következő elgondolás megvalósítását. Abból indultunk ki (elég botorul), hogy a motorok szögsebességei egy az origón átmenő egyenesen helyezkednek el, azonos távolságban, azaz omega = fokozat * konstans, ahol fokozat = 0..7. Ekkor (számoljunk utána!) a teljes körből 152 különböző irányvektor mentén lehetne mozgatni a tollat. (Ha felírjuk azokat a p/q alakú racionális számokat, ahol p és q is egész, p < q, és p a [0,7], q az [1,7] intervallumban helyezkedik el, akkor 19 különböző számot kapunk. Azaz 19 iránytangens egy síknyolcadon belül.) Mivel bináris motorral rendelkeztünk, összesen a 8 főirány mentén tudtuk csak mozgatni a tollat.
A plotter szoftverének alapötlete, hogy készítsünk egy tűrhető minőségű vonalrajzolót, majd azzal közelítsük az egyes rajzokat. A vonalrajzoló alapötlete az a trivialitás volt, hogy menjünk végig annak tengelynek a mentén "pixelenként" (a megfelelő intervallumon), amelyiknek mentén nagyobb az összes elmozdulás. Arányosan, bizonyos lépésszámonként ugorjunk egyet a másik tengely mentén is.
Eredeti verziójában a vonalrajzoló minden vonalat tisztán x ill. y irányú vonalakból rajzolt meg. A rajzok minőségén javított valamelyest, amikor ezt lecseréltük arra a megoldásra, hogy amikor a "másik" tengely mentén ugrunk egyet, azt összevonjuk az "egyik" tengely mentén történő utolsó ugrással; azaz egy valóban 45 fokos vonaldarabkát húzunk (egyszerre megy mind a két motor).
Az alsó kocsikat mozgató láncoknak természetesen volt valamekkora holtjátékuk. Ezért ábránként más és más erejűre kellett kalibrálni a motorokat a vonalrajzoló rutinban, attól függően, hogy az ábrában milyen irányú vonalak domináltak, milyen irányváltások szerepeltek.
A három ábra a következő volt: egy kör, egy házikó, és egy absztraktabb ábra (ez látszik a fényképeken). A legérdekesebb a kör volt. Ennek közelítő elmozdulásvektorait előre kiszámítottuk egy pc-n, majd az egyeneseket letároltuk a forráskódban. Mivel itt azonos hosszúságú, egyenletes szögeloszlású vektorokról van szó, azonos erejűre kellett kalibrálni a motorokat a vonalrajzolóban. Jól megfigyelhető volt rajzolás közben, hogy például a kör felső, középső negyedkörének rajzolása közben a vízszintes motor folyamatosan működik, míg a függőleges motorok szakaszosan, egyre ritkábban, majd egyre gyakrabban (cos) rántanak egyet a másik tengelyen.
void draw_line(int x, int y) { int xc, yc, temp; if (x < 0) { SetPower(OUT_B, 5); SetDirection(OUT_B, OUT_REV); } else { SetPower(OUT_B, 5); SetDirection(OUT_B, OUT_FWD); } if (y < 0) { SetPower(OUT_C, 5); SetDirection(OUT_C, OUT_FWD); } else { SetPower(OUT_C, 5); SetDirection(OUT_C, OUT_REV); } if (0 == x && 0 == y) return; x = abs(x); y = abs(y); yc = 0; xc = 0; temp = 0; if (y > x) while (yc < y) { yc = yc + 1; temp = (yc * x) / y; if (temp > xc) { xc = temp; OnFor(OUT_B + OUT_C, 5); } else OnFor(OUT_C, 5); } else while (xc < x) { xc = xc + 1; temp = (xc * y) / x; if (temp > yc) { yc = temp; OnFor(OUT_B + OUT_C, 5); } else OnFor(OUT_B, 5); } } task main() { draw_line(-2,14); draw_line(-7,12); draw_line(-11,9); draw_line(-13,5); draw_line(-14,0); draw_line(-13,-5); draw_line(-11,-9); draw_line(-7,-12); draw_line(-2,-14); draw_line(2,-14); draw_line(7,-12); draw_line(11,-9); draw_line(13,-5); draw_line(14,0); draw_line(13,5); draw_line(11,9); draw_line(7,12); draw_line(2,14); }Filmek: Video37.avi, Video38.avi, Video39.avi.