Toteutusdokumentti FuBot Tuomo Tilli Tommi Sankola Robottiohjelmoinnin harjoitustyö Helsingin yliopisto, Tietojenkäsittelytieteen laitos 2.6.2009
Sisällysluettelo 1. Johdanto...3 2. Käyttöohje...3 3. Ohjelman toiminta ja rakenne...3 4. Testauksen kuvaus...4 5. Oppimistavoitteet...4 Ohjelmoinnin perusteet...4 Ohjelmoinnin jatkokurssi...6 Muut kurssit...8 6. Jälkipuhe...8 LIITE 1...9 Rakennusohjeet...9
1. Johdanto Harjoitustyönä rakensimme Lego Mindstorms NXT -robotin, jonka tarkoituksena on etsiä pöydältä punainen pallo ja kuljettaa se maalialueelle. Robotti käyttää valosensoria pöydän reunojen, pallon ja maalialueen tunnistamiseen. Robotti osaa pysähtyä pöydän reunalle ja laskea minkä suuntainen pöydän reuna on. Näin ollen, robotti osaa kääntyä oikeaan suuntaan pöydän reunalle tullessaan. Robotti tietää myös oman sijaintinsa xy-koordinaatistossa, kun origo on robotin lähtöpaikka. Jos robotti löytää maalialueen ennen pallon löytymistä, tallentaa robotti maalialueen paikan, jotta se voi pallon löydettyään viedä pallon suoraan maalialueelle. Robotin runko on rakennettu Castor Bot -mallia soveltamalla. Robottiohjelma on ohjelmoitu Javaohjelmointikielellä. 2. Käyttöohje Jotta ohjelman voi kääntää, täytyy koneessa olla asennettuna Java SE development KIT, jonka saa Javan sivuilta osoitteesta http://java.sun.com/javase/downloads/index.jsp. Lisäksi täytyy koneessa olla asennettuna LeJOS, jonka saa osoitteesta http://lejos.sourceforge.net/nxj-downloads.php. Ohjelman käyttäjällä täytyy tietysti olla myös Lego Mindstorms NXT -robotti, jolla itse ohjelman suoritus tapahtuu. Katso robotin rakennusohjeet liitteestä 1. Kun Java SE development KIT ja LeJOS on asennettu, täytyy NXT:n firmware päivittää. Tämä tapahtuu kytkemällä NXT USB -kaapelilla koneeseen ja suorittamalla komento nxjflash. Itse ohjelman kääntäminen tapahtuu komennolla nxjc (esim. nxjc Fubot.java). Käännetyn ohjelman lataaminen NXT:lle tapahtuu komennolla nxj (esim. nxj Fubot). NXT:n täytyy olla päällä tässä vaiheessa. Nyt ohjelma on ladattu NXT:lle ja se voidaan suorittaa etsimällä ohjelma NXT:n valikosta. Ohjelman suoritus voidaan pysäyttää painamalla ESCAPE -nappia. Ohjelman käyttäminen on todella helppoa. Ohjelman käyttämistä varten tarvitaan nelikulmainen pöytä, punainen pallo ja maalialueeksi valkoinen paperi. Pöydällä ei saa olla muita esineitä. Jotta ohjelma toimisi parhaiten, olisi valaistuksen hyvä olla melko tasainen pöydän päällä. Kun ohjelma käynnistyy, robotti mittaa ympäristön valoisuuden valosensorin avulla. Tämän arvon avulla robotti tunnistaa pöydän reunan, maalialueen ja pallon. Ohjelman käynnistyttyä, käyttäjän ei tarvitse tehdä mitään. Ohjelman suoritus päättyy, kun robotti on löytänyt pallon ja vienyt sen maalialueelle. Robotti ei tallenna paikkoja, joissa se on käynyt, joten on mahdollista, että robotti tutkii aina samaa kohtaa pöydästä eikä se löydä palloa tai maalialuetta ikinä. Tämä on kuitenkin epätodennäköistä. Pöydän koolla ei ole muuten väliä, mutta kohteiden löytämisessä voi kestää kauemmin isolla pöydällä kuin pienellä pöydällä. Pöydän on hyvä olla nelikulmio, jossa kulmat ovat 90 astetta. 3. Ohjelman toiminta ja rakenne FuBotilla on erilaisia käytösmalleja, joita se noudattaa. Näistä käytösmalleista yksi on aina kulloinkin voimassa, ja niiden käynnistyminen edellyttää esiehtojen täyttymistä. Lisäksi käytösmalleilla on oma hierarkiansa, joka ratkaisee tilanteet, joissa useamman käytösmallin esiehdot toteutuvat. Näitä käytösmalleja on FuBotilla neljä: Seek, Locate, LocateEdge ja LocateGoal. Robotin sijaintia ja suuntaa pidetään yllä lejosin Pilot-luokan navigaatiometodien avulla. Lisäksi ympäristöä kartoitetaan siten, että pöydän reunat tallennetaan taulukkoon.
Robottiohjelma on suunniteltu nelikulmaisia pöytiä varten, mutta siihen on periaatteessa helppo lisätä tuki monimutkaisemmille pöydille. Tällöin pitää toteuttaa pöydän kulmien tunnistaminen. Robotin tilaa pidetään yllä Fubot-luokan staattisissa kentissä. 3.1 Käytösmallit Seek on oletuskäytösmalli. Tällöin robotti tekee yksinkertaista etsintäliikettä, eli se tekee käännöksiä oikealle ja vasemmalle siten, että ulompaa rengasta käännetään eteenpäin ja sisempää rengasta taaksepäin. Käännöksen parametreissä on optimoitavaa, mutta alustavissa testeissä tällä metodilla todettiin saatavan katettua riittävän laaja alue. Jos sekä maalialue että pallo on löytynyt, ajetaan suoraan maalialueen xy-koordinaatteihin. Jos maalialue ei tältä kohtaa löydy, jatketaan tavanomaista etsimistä. Locate käynnistyy silloin, kun robotti havaitsee valosensorillaan punaisen pallon. Tällöin robotti ajaa eteenpäin pallon luo ja sulkee kyntensä lukiten pallon säilöönsä. LocateGoal käynnistyy silloin, kun maalialue löydetään. Jos robotilla on tällöin pallo mukanaan, se avaa kyntensä ja lopettaa ohjelmansa. Muussa tapauksessa maalialueen xy-koordinaatit merkataan muistiin, ja jatketaan pallon hakemista. LocateEdge käynnistyy silloin, kun robotti kohtaa pöydän reunan. Kun näin käy, tarkistetaan, onko löydetty reunan pistekoordinaatti riittävän lähellä jonkin jo löydetyn reunan suoraa. Jos se sopii, piste lisätään kyseisen reunan pistejoukkoon. Jos piste ei sovi millekään suoralle, ja kaikkia neljää reunaa ei ole löydetty, käännytään niin kauan, kunnes kohdataan pöydän reuna uudestaan, ja otetaan tämä piste ylös. Näiden kahden pisteen avulla lasketaan reunan suoran yhtälö. Nyt tutkitaan suoran avulla uudestaan, josko reunan voisi tunnistaa joksikin jo löydetyksi reunaksi. Jos suora on riittävän yhdenmukainen jonkin muun reunan suoran kanssa, sen laskemiseen käytetyt pisteet lisätään reunan suoran pistejoukkoon. Muussa tapauksessa, jos kaikkia neljää reunaa ei ole vielä löydetty, tehdään tästä suorasta uusi reuna. Jos kaikki reunat ovat löytyneet, eikä tämä suora sovi yhteenkään näistä, ei tehdä mitään. Lopuksi käännytään reunasta poispäin. Jos reuna saatiin tunnistettua, käännytään reunan suuntaisesti. Muuten yksinkertaisesti käännytään 80 astetta pöydän keskelle päin. 4. Testauksen kuvaus Ohjelman testaus tapahtui ohjelmoinnin lomassa tulostamalla NXT:n näyttöön erilaisia tietoja kuten robotin x- ja y-koordinaatit, robotin kulma x-akseliin nähden, valosensorin antama arvo, robotin viimeksi kääntymä kulma, liikuttu matka, viimeksi liikuttu matka, robotin löytämien pöydän reunojen kulmakertoimet ja vakiot. Näiden arvojen avulla pystyttiin näkemään liikkuuko robotti pöydällä oikein. Näyttöön tulostettujen arvojen perusteella pystyttiin päättelemään, että robotin paikassa tulee kokoajan pientä virhettä. Virhe johtuu siitä, että moottorit eivät pyöri aina juuri haluttua määrää, vaan pyörimisessä tulee jonkin verran heittoa. Tätä heittoa ei pystytty korjaamaan. Toinen kohta missä tuli vielä isompia virheitä oli pöydän reunojen laskeminen. Pöydän reunojen yhtälöt lasketaan kahden (tai useamman) pisteen avulla. Nämä pisteet määritetään valosensorin avulla, mutta valosensori ei ole niin tarkka, että jokainen piste olisi juuri pöydän reunalla, vaan toinen piste saattoi olla enemmän pöydällä kuin toinen. Tästä johtuen pöydän reunojen yhtälöissä
saattoi olla suuriakin virheitä. Näin ollen emme oikein voineet käyttää näitä tietoja hyväksi niin hyvin kuin olisimme halunneet. 5. Oppimistavoitteet Harjoitustyössä ohjelmoinnin peruskursseilta opituista taidoista ja java-osaamisesta oli hyötyä, vaikkei ohjelmassa olekaan monimutkaisia, rekursiivisia algoritmeja tai tietorakenteita. Lisäksi ohjelmoitaessa tuli hyödynnettyä vähän rinnakkaisohjelmointi- ja ohjelmointitekniikka(java)kursseilta tuttuja, säikeisiin ja mutexiin liittyviä tekniikoita. Ohjelmoinnin perusteet Seuraavia Ohjelmoinnin perusteet -kurssin oppimistavoitteiden mukaisia taitoja tarvittiin harjoitustyössä. Pääteema Algoritmit ja ohjausrakenteet Lähestyy oppmistavoitteita Saavuttaa oppimistavoitteet Syventää oppmistavoitteita Muutujat ja tyypit Tuntee ja osaa selittää ohjelmointikielen, kääntämisen ja tulkitsemisen idean. Osaa selittää sijoitusoperaationm erkityksen ja algoritmin suorituksen etenemisen ajassa. Osaa simuloida yksinkertaisia algoritmeja. Oivaltaa muuttujan tyypin ja arvon idean. Osaa laatia yksinkertaisia algoritmeja. Osaa selittää käsitteen "algoritmin tila". Tajuaa miten loogiset lausekkeet ovat väittämiä algoritmin tilasta. Osaa laatia logiikaltaan ja ulkoasul-taan tyylikkäitä ohjelmia Osaa käyttää muuttujia ja kirjoittaa lausekkeita, joiden tyyppi on int, double, boolean ja String. Tuntee alkeistyypin ja viittaustyypin eron. Tuntee sijoitusyhteensopivuuden merkityksen ohjelmoinnissa. Tajuaa muodollisten parametrien ja paikallisten muuttujien Tuntee hieman tyypityksenhi storiaa ja osaa arvioida erilaisten ratkaisujen seurauksia.
Aliohjelmat Oivaltaa algoritmin nimeämisen ja kutsumisen periaatteen. Luokat oliot ja kapselointi Hahmottaa luokkamäärittelyn olion piirustuksina, joista voidaan luoda erillisiä olioita. käyttäytymisen Osaa käyttää luokkia muuttujien tyyppeinä. Osaa indeksoiden viitata dynaamisesti taulukkomuuttujan komponentteihin. Osaa määritellä ja kutsua aliohjelmia, Javan metodeita. Osaa selittää ja käyttää muodollisia ja todellisia parametreja. Tuntee millä tavoin metodi arvoparametrivälityksestä huolimatta voi muuttaa parametrin arvoa, mikäli parametrin tyyppi (luokka) sen sallii. Tuntee metodien kuormittamisen tekniikan ja osaa myös käytännössä laatia kuormitettuja metodeita ja konstruktoreita. Tietää, että Javan arvoparametri välitys on vain yksi vaihtoehto parametrivälit ykselle: on olemassa esimerkiksi kieliä, joissa käytetään viiteparametr eja. Osaa määritellä yksityisiä ilmentymämuuttujia ja ohjelmoida aksessoreita. Tuntee käsitteen "olion tila". Tietää millainen elinkaari olioilla on ja miten se eroaa metodin paikallisten muuttujien elinkaaresta. Osaa välittää olioita parametreina. Tuntee automaattisen roskienkeruun merkityksen. Ymmärtää mitä seurauksia automaattisell a roskienkeruulla on Javakielen sovellettavuuteen ja mihin Java tästä syystä on hyvä, mihin kerta kaikkiaan sopimaton.
Ohjelmoinnin jatkokurssi Pääteema Luokkamäärittelyn tekniikat Lähestyy oppmistavoitteita Tietää että "static" liittyy luokkaan, "eistatic" ilmentymään. Saavuttaa oppimistavoitteet Periytyminen Alkaa tajuta, miten aliluokka perii yliluokan ominaisuudet, kentät ja metodit. Hahmottaa Objectluokasta alkavan luokkien puumaisen periytymishierar kian. Virhetilanteiden käsittely Tietää, että virheet pitää jotenkin hoidella. Hahmottaa poikkeusten käsittelyn vaikutuksen ohjelman Osaa käyttää ohjelmoinnissa luokka- ja ilmentymämuuttujia sekä luokka- ja ilmentymämetodeita. Tuntee näkyvyyden säätelyn mahdollisuudet ja ongelmat: yksityinen kalusto, pakkaustason kalusto, näkyminen aliluokkaan, julkinen kalusto. Ymmärtää yliluokkaaliluokkasuhteen ja osaa myös ohjelmoida aliluokkia. Tuntee ja osaa ohjelmoinnissa ottaa huomioon sen, että konstruktorit eivät periydy ja sen mitä tästä seuraa. Osaa käyttää ohjelmoinnissa ilmauksia this, super, this() ja super(). Tietää millaisia lisäyksiä periytyminen tuo näkyvyyssääntöihin. Tuntee erilaisia tapoja käsitellä virhetilanteita. Tuntee tarkistettujen ja tarkistamattomien poikkeusten periaatteen ja osaa Syventää oppmistavoitteita Osaa jäsennellä ohjelman arkkiteh-tuuria luokka- ja oliotason tekniikoilla ja erilaisilla näkyvyyk-sillä.
suoritukseen. Ohjelmointitekniikka Oivaltaa tyyppimuunnoks en tarpeen. Osaa luonnehtia, mitä rekursio on. laatia ohjelmia, joissa poikkeuksia käsitellään Exceptiontasolla Tuntee alkeistyypit ja niiden väliset sijoitusyhteensopivuussäännöt sekä myös eksplisiittisen tyyppimuunnoksen laveammasta alkeistyypistä suppeampaan. Osaa selittää yksinkertaisen rekursiivisen metodin toiminnan. Tuntee pakkausten periaatteen. Osaa lähdemateriaalia käyttäen laatia ohjelmia, jotka lukevat ja kirjoittavat tekstitiedostoja. Tuntee erilaisia ohjelman toimintaperiaatteita: tietoja kyselevä ohjelma, komentotulkki, suodatin, tapahtumaohjattu ohjelma. Osaa myös ohjelmoida kolmella ensin mainitulla tyylillä. Osaa laatia rekursiivisia metodeita. Ymmärtää miksi rekursiivinen metodi Fibonaccin lukujen laskentaan on aikavaativuudeltaan eksponentiaalinen, binäärihaku logaritminen ja aavistelee ymmärtävänsä, miksi pikajärjestäminen on keskimääräisessä tapauksessa O(n*log n). Osaa ohjelmoida omia geneerisiä luokkia. Muut kurssit Matematiikkaa tarvittiin jonkin verran. Trigonometrisia funktioita käytettiin pöydän reunojen laskemisessa sekä robotin sijainnin laskemisessa. 6. Jälkipuhe Alunperin olimme tekemässä sellaista robottia, joka olisi etsinyt pallon ultraäänisensorin ja valosensorin avulla. Huomasimme kuitenkin, että ultraäänisensori ei oikein havaitse palloa. Havaitsimme myös valosensorissa puuteita. Kohteen täytyy olla hyvin lähellä valosensoria, jotta
sensori havaitisisi tarpeeksi ison eron kohteen värissä. Sensorien heikko taso johti siihen, että meidän täytyi miettiä robotin rakenne ja toiminta uudestaan. Myös robotin moottorit toimivat välillä täysin satunnaisesti, emmekä kyenneet selvittämään sen syytä. Myös lejosin bugit aiheuttivat päänvaivaa. Ohjelma saattoi kaatua muistin loppumiseen yksinkertaisessa neliön laskemisessa, mikä kierrettiin kertolaskun aukikirjoittamisella. Virheiden löytäminen oli myös vaikeampaa, sillä lejosin poikkeusten selvittäminen ja kaatumisista palautuminen on työlästä. Robottiohjelmointi tarjoaa perinteisiin harjoitustyöihin verrattuna erilaisia haasteita siinä, että ohjelmointiympäristön tarjoamat työkalut ovat keskeneräisiä ja jokseenkin vajaita. Lisähaastetta tulee siitä, että robotin moottorit ja sensorit ovat epätäydellisiä. Tämä vastannee joissakin tilanteissa paremmin ohjelmoijan tosiasiallisia haasteita kuin perinteinen harjoitustyö. Aloittelevalle ohjelmoijalle robottiohjelmointia emme kuitenkaan varauksetta suosittele, sillä ohjelmointiongelmien sijaan paljon aikaa menee toissijaisten ongelmien kanssa painimiseen, mikä voi ehkä vaikeuttaa hyvätyylisen ohjelmointitavan oppimista.
LIITE 1 Rakennusohjeet Robotti on rakennettu vain paketissa olevia osia käyttämällä.castor Botin rakennusohjeet ovat sivulla http://www.nxtprograms.com/castor_bot/steps.html. Tässä robotti sivusta. Kuvasta näkee miten keskusyksikkö on kiinnitetty moottoreihin.
Kuva robotin pohjasta. Kuvassa näkyy kaksi tukipalkkia robotin pohjassa.
Kuva peräosasta.
Toinen kuva perästä. Pallo pysyy paikallaan robotin painon ansiosta. Valkoinen palkki ylhäällä on sinänsä turha, mutta siihen voi esimerkiksi sitoa johtoja.
Kuvassa näkyy miten leuat on toteutettu. Ylhäällä oleva iso ratas on muuten turha, mutta siitä voi kädellä pyörittää moottoria.
Kuva robotin etuosasta alhaaltapäin. V:n muotoisen osan on tarkoitus pitää palloa paikallaan.