Peltorobotin reititys siksak -täytöllä



Samankaltaiset tiedostot
v 8 v 9 v 5 C v 3 v 4

A ja B pelaavat sarjan pelejä. Sarjan voittaja on se, joka ensin voittaa n peliä.

Vektorien pistetulo on aina reaaliluku. Esimerkiksi vektorien v = (3, 2, 0) ja w = (1, 2, 3) pistetulo on

1. (a) Seuraava algoritmi tutkii, onko jokin luku taulukossa monta kertaa:

Matematiikan tukikurssi

Ympyrä 1/6 Sisältö ESITIEDOT: käyrä, kulma, piste, suora

13 Lyhimmät painotetut polut

MAB3 - Harjoitustehtävien ratkaisut:

Johdatus graafiteoriaan

1 Kertaus. Lineaarinen optimointitehtävä on muotoa:

Matematiikan tukikurssi

3 Suorat ja tasot. 3.1 Suora. Tässä luvussa käsitellään avaruuksien R 2 ja R 3 suoria ja tasoja vektoreiden näkökulmasta.

Algoritmit 1. Luento 9 Ti Timo Männikkö

MAB3 - Harjoitustehtävien ratkaisut:

z 1+i (a) f (z) = 3z 4 5z 3 + 2z (b) f (z) = z 4z + 1 f (z) = 12z 3 15z 2 + 2

, on säännöllinen 2-ulotteinen pinta. Määrää T x0 pisteessä x 0 = (0, 1, 1).

T Syksy 2004 Logiikka tietotekniikassa: perusteet Laskuharjoitus 7 (opetusmoniste, kappaleet )

isomeerejä yhteensä yhdeksän kappaletta.

Johdatus tekoälyn taustalla olevaan matematiikkaan

Tarkennamme geneeristä painamiskorotusalgoritmia

Lineaarikombinaatio, lineaarinen riippuvuus/riippumattomuus

58131 Tietorakenteet ja algoritmit (kevät 2014) Uusinta- ja erilliskoe, , vastauksia

Königsbergin sillat. Königsberg 1700-luvulla. Leonhard Euler ( )

3.3 Paraabeli toisen asteen polynomifunktion kuvaajana. Toisen asteen epäyhtälö

Algoritmi on periaatteellisella tasolla seuraava:

58131 Tietorakenteet ja algoritmit (syksy 2015) Toinen välikoe, malliratkaisut

58131 Tietorakenteet (kevät 2009) Harjoitus 11, ratkaisuja (Topi Musto)

Harjoitus 3 ( )

Algoritmit 2. Luento 13 Ti Timo Männikkö

Harjoitus 3 ( )

Yhtälöryhmä matriisimuodossa. MS-A0004/A0006 Matriisilaskenta. Tarkastellaan esimerkkinä lineaarista yhtälöparia. 2x1 x 2 = 1 x 1 + x 2 = 5.

Matematiikan taito 9, RATKAISUT. , jolloin. . Vast. ]0,2] arvot.

Graafit ja verkot. Joukko solmuja ja joukko järjestämättömiä solmupareja. eli haaroja. Joukko solmuja ja joukko järjestettyjä solmupareja eli kaaria

Diplomi-insinööri- ja arkkitehtikoulutuksen yhteisvalinta 2017 Insinöörivalinnan matematiikan koe , Ratkaisut (Sarja A)

Datatähti 2019 loppu

Polkuintegraali yleistyy helposti paloitain C 1 -poluille. Määritelmä Olkoot γ : [a, b] R m paloittain C 1 -polku välin [a, b] jaon

Derivaatan sovellukset (ääriarvotehtävät ym.)

811120P Diskreetit rakenteet

811120P Diskreetit rakenteet

Olkoon seuraavaksi G 2 sellainen tasan n solmua sisältävä suunnattu verkko,

BM20A5800 Funktiot, lineaarialgebra ja vektorit Harjoitus 4, Syksy 2016

Algoritmit 1. Luento 8 Ke Timo Männikkö

7 Vapaus. 7.1 Vapauden määritelmä

Avaruuden kolme sellaista pistettä, jotka eivät sijaitse samalla suoralla, määräävät

MS-A0202 Differentiaali- ja integraalilaskenta 2 (SCI) Luento 1: Parametrisoidut käyrät ja kaarenpituus

Kuva 1: Funktion f tasa-arvokäyriä. Ratkaisu. Suurin kasvunopeus on gradientin suuntaan. 6x 0,2

Solmu 3/2001 Solmu 3/2001. Kevään 2001 ylioppilaskirjoitusten pitkän matematiikan kokeessa oli seuraava tehtävä:

10. Painotetut graafit

1. Olkoot vektorit a, b ja c seuraavasti määritelty: a) Määritä vektori. sekä laske sen pituus.

Demo 1: Simplex-menetelmä

4.1 Kaksi pistettä määrää suoran

Valitaan alkio x 1 A B ja merkitään A 1 = A { x 1 }. Perinnöllisyyden nojalla A 1 I.

9. Vektorit. 9.1 Skalaarit ja vektorit. 9.2 Vektorit tasossa

2 Pistejoukko koordinaatistossa

Injektio (1/3) Funktio f on injektio, joss. f (x 1 ) = f (x 2 ) x 1 = x 2 x 1, x 2 D(f )

Cantorin joukon suoristuvuus tasossa

Maksimit ja minimit 1/5 Sisältö ESITIEDOT: reaalifunktiot, derivaatta

Kertaus. x x x. K1. a) b) x 5 x 6 = x 5 6 = x 1 = 1 x, x 0. K2. a) a a a a, a > 0

= 5! 2 2!3! = = 10. Edelleen tästä joukosta voidaan valita kolme särmää yhteensä = 10! 3 3!7! = = 120

Kuva Suomen päätieverkko 1 Moottoritiet on merkitty karttaan vihreällä, muut valtatiet punaisella ja kantatiet keltaisella värillä.

Tietorakenteet ja algoritmit - syksy

2. Seuraavassa kuvassa on verkon solmujen topologinen järjestys: x t v q z u s y w r. Kuva 1: Tehtävän 2 solmut järjestettynä topologisesti.

Yhtälön oikealla puolella on säteen neliö, joten r. = 5 eli r = ± 5. Koska säde on positiivinen, niin r = 5.

Tekijä Pitkä matematiikka b) Kuvasta nähdään, että b = i 4 j. c) Käytetään a- ja b-kohtien tuloksia ja muokataan lauseketta.

Kannan vektorit siis virittävät aliavaruuden, ja lisäksi kanta on vapaa. Lauseesta 7.6 saadaan seuraava hyvin käyttökelpoinen tulos:

Mb8 Koe Kuopion Lyseon lukio (KK) sivu 1/2

Johdatus verkkoteoriaan 4. luento

Matematiikan tukikurssi

Täydellisyysaksiooman kertaus

jakokulmassa x 4 x 8 x 3x

Ota tämä paperi mukaan, merkkaa siihen omat vastauksesi ja tarkista oikeat vastaukset klo 11:30 jälkeen osoitteesta

IDL - proseduurit. ATK tähtitieteessä. IDL - proseduurit

Kertaus. Integraalifunktio ja integrointi. 2( x 1) 1 2x. 3( x 1) 1 (3x 1) KERTAUSTEHTÄVIÄ. K1. a)

Vanhoja koetehtäviä. Analyyttinen geometria 2016

Kahden suoran leikkauspiste ja välinen kulma (suoraparvia)

ATK tähtitieteessä. Osa 3 - IDL proseduurit ja rakenteet. 18. syyskuuta 2014

Juuri 7 Tehtävien ratkaisut Kustannusosakeyhtiö Otava päivitetty c) sin 50 = sin ( ) = sin 130 = 0,77

Luku 8. Aluekyselyt. 8.1 Summataulukko

Matematiikan tukikurssi, kurssikerta 3

Yhtälöryhmä matriisimuodossa. MS-A0007 Matriisilaskenta. Tarkastellaan esimerkkinä lineaarista yhtälöparia. 2x1 x 2 = 1 x 1 + x 2 = 5.

Algoritmit 2. Luento 11 Ti Timo Männikkö

Diskreetin matematiikan perusteet Laskuharjoitus 2 / vko 9

Vapaus. Määritelmä. jos c 1 v 1 + c 2 v c k v k = 0 joillakin c 1,..., c k R, niin c 1 = 0, c 2 = 0,..., c k = 0.

Injektio. Funktiota sanotaan injektioksi, mikäli lähtöjoukon eri alkiot kuvautuvat maalijoukon eri alkioille. Esim.

MS-A0003/A0005 Matriisilaskenta Malliratkaisut 5 / vko 48

Lineaarinen yhtälöryhmä

Algoritmit 1. Demot Timo Männikkö

Tampereen yliopisto Tietokonegrafiikka 2013 Tietojenkäsittelytiede Harjoitus

Juuri 6 Tehtävien ratkaisut Kustannusosakeyhtiö Otava päivitetty Vastaus: Määrittelyehto on x 1 ja nollakohta x = 1.

V. V. Vazirani: Approximation Algorithms, luvut 3-4 Matti Kääriäinen

Tietorakenteet ja algoritmit Johdanto Lauri Malmi / Ari Korhonen

Ristitulolle saadaan toinen muistisääntö determinantin avulla. Vektoreiden v ja w ristitulo saadaan laskemalla determinantti

Matemaatiikan tukikurssi

Tehtävä 8 : 1. Tehtävä 8 : 2

Matematiikan tukikurssi: kurssikerta 10

Matematiikan tukikurssi

Transkriptio:

Peltorobotin reititys siksak -täytöllä Ville Räisänen(ville.raisanen@tut.fi) 27.6.2007-19.4.2008 Sisältö 1 Johdanto 3 1.1 Määritelmiä ja notaatio..................... 3 1.2 Peltorobotin reititysongelma................... 4 1.3 Yhteys jyrsinkoneongelmaan................... 6 1.4 Ongelman vaikeus........................ 7 1.5 MATLAB-implementaatio.................... 7 1.5.1 Implementaation tämänhetkinen tilanne........ 9 1.5.2 Työaluetta kuvaavat tietorakenteet........... 9 2 Verkon luonti 11 2.1 Inklinaatio, koordinaatisto ja siksak-janat........... 11 2.2 Siksak-graafi............................ 12 2.3 Algoritmi solmupisteiden löytämiseksi............. 12 2.4 Solmupisteiden yhdistäminen.................. 13 2.5 Algoritmi solmupisteiden yhdistämiseksi............ 15 2.6 Algoritmi FindNextNode..................... 15 2.7 MATLAB-implementaatio.................... 17 3 Kulun suunnittelu verkossa 20 3.1 Monotonisen reitin muodostus.................. 20 3.2 Kulun muodostus pinoamalla monotonisia reittejä....... 22 3.3 Kauppamatkustajan ongelma ja paluuliikkeiden minimointi. 23 3.4 Matkan minimointi ja kiinalaisen postinkuljettajan ongelma. 24 3.5 MATLAB-implementaatio.................... 24 4 Verkon kulun palauttaminen aidoksi reitiksi 28 4.1 Lyhimmät reitit.......................... 29 4.2 Näkyvyysgraafi.......................... 29 4.3 Dijkstra n algoritmi........................ 30 4.4 Pseudoesteet........................... 30 4.5 MATLAB-implementaatio.................... 32 4.5.1 Näkyvyysgraafi...................... 32 1

4.5.2 Lyhimmät reitit kahden pisteen välillä......... 34 4.5.3 Reitin palautus...................... 35 5 Esikäsittely 37 5.1 Inklinaatio............................. 37 5.1.1 Käännösten minimointi................. 37 5.2 Reunapolygonien sisennys.................... 37 5.2.1 Johdanto......................... 38 5.2.2 Esisisennys........................ 38 5.2.3 Murtoviivagraafin muodostus.............. 39 5.2.4 Silmukoiden etsintä.................... 39 5.2.5 Kierrosluku........................ 41 5.2.6 Polygonien laajentaminen................ 41 5.3 Alueen jako osiin......................... 41 6 MATLAB-esimerkkiajoja ja ideoita jatkokehittelyä varten 43 6.1 Yksinkertaisien alueiden luominen............... 43 6.2 Yksinkertaisen reitin luominen................. 44 6.2.1 Pseudoesteet....................... 45 6.2.2 Reunakulun lisääminen................. 46 6.2.3 Reitin arvostelu...................... 48 6.3 Alueen jako osiin ja parhaan kokonaisreitin etsintä...... 50 6.4 Sarkatäyttö............................ 51 6.5 Spiraalitäyttö........................... 51 2

1 Johdanto Peltorobotin reititysongelma muistuttaa jyrsinkoneongelmaa, jossa pyritään etsimään jollakin tavoin optimaalinen kulku jyrsinkoneen terälle. Tässä tekstissä pyrimme selvittämään reititysongelmien yhteyksiä, eroja ja kuvaamaan implementoimamme siksak -algoritmin teoreettista taustaa. 1.1 Määritelmiä ja notaatio Määritelmä 1 Polygonaalinen ketju(murtoviiva) on yksinkertainen 1 janoista e 1,..., e n 1 koostuva käyrä, missä janat määrääytyvät pisteistä v 1,...,v n : e i = v i v i+1. Pisteitä kutsutaan ketjun kärjiksi ja kärkien v 1,...,v n määräämää ketjua merkitään PC(v 1,...,v n ). 6 00000000000000 11111111111111 00000000000000000 11111111111111111 01 00000000000000 00000000000000 11111111111111 00000000000000000 11111111111111111 11111111111111 7 01 5 00000000000000000 11111111111111111 01 00000000000000 11111111111111 01 01 00000000000000000 11111111111111111 1100000 6 01 01 01 00000000000000 00000000000000 00000000000000 11111111111111 01 01 00000000000000000 11111111111111111 5 111111111111110 1 01 00000000000000 00000000000000 00000000000000 11111111111111 11111111111111 111111111111110 1 01 01 00 11 00000000000000000 00000000000000000 11111111111111111 11111111111111111 00000000000000000 11111111111111111 7 4 00000000000000000 11111111111111111 00000000000000 00000000000000 11111111111111 11111111111111 11111111111111 3 00000000000000000 11111111111111111 4 01 00000000000000000 11111111111111111 01 00000000000000000 11111111111111111 3 1 00000000000000 11111111111111 01 00000000000000000 11111111111111111 1 2 2 00000000000000000 11111111111111111 01 00000000000000000 11111111111111111 8 Kuva 1: Positiivisesti suunnistettu polygoni POLY (v 1,...,v 8 ) ja negatiivisesti suunnistettu polygoni POLY (v 1,...,v 7 ). Positiivisesti suunnistetulla polygonilla yleensä viitataan polygonin sisäpuolelleen rajaamaan alueeseen. Negatiivinen suunnistus puolestaan vittaa ulkopuoliseen alueeseen. Määritelmä 2 Polygoni 2 POLY (v 1,...,v n ) on suljettu polygonaalinen ketju PC(v 1,...,v n,v 1 ). Kärkiä v 1,...,v n kutsutaan polygonin kärjiksi ja vastaavia reunoja e 1,..., e n polygonin reunoiksi. Kärkien v 1,...,v n määrittämää polygonia merkitään POLY (v 1,...,v n ). Kärkiä voidaan tarvittaessa indeksoida modulo-aritmetiikalla siten, että v 0 = v n,v n+1 = v 1,jne... Polygoni on positiivisesti suunnistettu, jos janat määrävät kärjet on indeksoitu vastapäivään. Jos polygoni ei ole positiivisesti suunnistettu, se on 1 Yksinkertaisella tässä tarkoitetaan sitä, että ainoat suljettujen janojen leikkaukset tapahtuvat vierekkäisten janojen reunapisteissä. 2 Kyseessä on itseasiassa yksinkertainen polygoni. Yleisemmässä polygonin määrittelyssä reunojen sallitaan leikkaavan keskenään mielivaltaisesti. 3

negatiivisesti suunnistettu. Positiivisesti suunnistettu polygoni samaistetaan ketjun sisälleen rajoittamaan äärelliseen joukkoon ja negatiivisesti suunnistettu polygoni ketjun ulkopuoliseen rajoittamattomaan joukkoon. 3 Jos kirjoitamme polygoneille P 1 P 2, viittaamme polygonien määräämiin tason joukkoihin. Polygonin(tai yleisemmin murtoviivan) kärjen sisäkulman ollessa pienempi kuin π, kärki on konveksi. Muulloin kärki on reflex 4. Kärkeen v r liittyvää sisäkulmaa merkitsemme IA(v r ). Polygoni on konveksi, jos jokaista sen pisteparia yhdistävä jana on polygonin osajoukko. Määritelmä 3 Murtoviiva C on monotoninen suoran L suhteen, jos jokaisen L :n suhteen ortogonaalisen suoran L leikkaus C:n kanssa on enintään yksittäinen piste tai jana. Edelleen polygoni P on monotoninen suoran L suhteen, jos sen reuna P voidaan jakaa kahteen L:n suhteen monotoniseen polygonaaliseen ketjuun. Polygonin kärki v i on monotoninen suoran L suhteen, jos polygonaalinen ketju PC(v i 1, v i, v i+1 ) on vastaavasti monotoninen. Merkitsemme vektoreita lihavoidulla ja vastaavia skalaareja lihavoimattomalla. Vektoria vastaavaa yksikkövektoria merkitsemme hatulla. ts. [ ] x v = vˆv = missä ˆv = v y v = 1 [ ] x. v y Määritelmä 4 Alue P on jono (C 0, C 1,..., C n ) polygoneja, missä C 0 on positiivisesti suunnistettu ja C 1,..., C n C 0 negatiivisesti suunnistettuja. C 0 määrää alueen ulkoreunan ja C 1,..., C n esteet. 1.2 Peltorobotin reititysongelma Yleistettyä peltorobottiongelmaa voimme karakterisoida ainakin seuraavasti: Annettu: 1. Alue P. 2. Työkoneen leveys d ja muoto. 3. Sallitut kulkualueet D 1,..., D m P:n ulkopuolella (pos. suunnistettuja polygoneja). 4. Mahdolliset määrätyt aloitus ja lopetuspisteet. 3 Tämä on käsittääkseni vain tässä dokumentissa käytetty erikoinen määrittely. 4 Suomenkielinen terminologia ei ilmeisesti ole kovin vakiintunutta, joten käytän suoraan englanninkielistä termiä. 4

C 0 C 1 Kuva 2: Positiivisesti ja negatiivisesti suunnistetut polygonit määräävät alueen. 5. Käännöksien, ajamatta jätetyn alueen, sekä ulkopuolella ajetun matkan, kustannussuhde kuljettuun matkaan P:ssa. Ratkaistava: 1. Janoista koostuva reitti (e 1,...e N ) (murtoviiva), joka minimoi kustannukset. Yksinkertaistaaksemme tarkastelua, oletamme ainakin: 1. Alue on tason joukko. Alueella siis ei ole mäkiä tai kuoppia, joiden kaarevuus pitäisi ottaa huomioon. 2. Ajettu alue on Minkowski-summa 5 reitistä ja d/2-säteisestä ympyrästä. Tämä ei arvatenkaan vastaa todellisuutta kovin hyvin ja yksinkertaistuksen vaikutus tulee selvittää tarkemmin. Joudummeko esimerkiksi pienentämään uranleveyttä? 3. Jottemme kulje kielletyllä alueella, oletamme edellisen nojalla, että Polygoneja on sisennetty d/2 janojen normaalin suuntaan. Positiivisesti suunnistetut polygonit tällöin kutistuvat ja negatiivisesti suunnistetut laajenevat. (Kuva 4) 4. Työkoneen käännökset tapahtuvat tietyssä pisteessä työalueen sisässä 6 ja ovat täten äärettömän jyrkkiä. Aidot traktorit joutuvat kaartamaan käännöspisteen ympäristössä. Edelleen kaarre saattaa vastata puoliympyrää, jonka säde on suurempi kuin d/2. (Kuva 3) 5 Minkowski summa joukoille A ja B määritellään A B = {x = a + b : a A, b B} 6 Ilmeisesti käännökset tehdään usein työalueen ulkopuolella tai sisennyksessä muodostettavien kiertourien päältä, joten oletus on ilmeisesti väärä? Edelleen koko lopputeksti perustuu tälle oletukselle. 5

5. Kulku alueen ulkopuolella ei ole sallittua. ts. ulkopuolella ajetun matkan kustannussuhde on ääretön. 6. Työkoneen leveys pysyy vakiona. Kuva 3: Verkkomallin olettama käännös sekä kaksi realistisempaa käännöstä. Molemmissa jälkimmäisistä kaarteen ulkopuolelta sekä viimeisimmässä kaarteen sisäpuolelta saattaa jäädä työaluetta ajamatta. 9 8 7 6 5 6. 4 5. 3 4. 2 3. 1 2. 0 1. 1 0. 4-1. 2 0 2 4 6 8 Kuva 4: Oletamme, että peltoa kuvaavaa polygonia on sisennetty siten, ettemme aja kielletyllä alueella. Ehkäistäksemme edellisten oletusten aiheuttamia virheitä saatamme joutua pienentämään työkoneen uranleveyttä d. 1.3 Yhteys jyrsinkoneongelmaan Peltorobotin reititysongelma muistuttaa paljon jyrsinkoneiden terän radan suunnittelua(milling problem). Kyseistä aihetta on jo tutkittu ainakin jonkin verran ja noudatamme tässä tekstissä pääosin samankaltaista lähestymistapaa kuin lähteissä [9] ja [3]. 6

Toisin kuin jyrsinkoneissa, peltoroboteilla myös jyrkkien käännösten minimoiminen on oleellista. Jyrsinprosessissa kahden pisteen välillä voidaan siirtyä suorinta reittiä nostaessamme terän ylös, mutta peltoroboteille vastaava ei aina ole mahdollista. Pellon keskellä voi olla esimerkiksi järvi, jonka ylittäminen ei ole toivottavaa. Joudumme siis useissa tilanteissa hakemaan lyhimmän reitin pellolla kahden eri pisteen välillä. 1.4 Ongelman vaikeus Vaikka luopuisimme edelleen käännöksien kustannuksista, ongelma olisi vieläkin NP-vaikea [3]. Täydellisen ratkaisun etsiminen lienee siis turhaa yksinkertaisimpien tapausten ulkopuolella. Luonnollisempi lähestymistapa on turvautua tiettyihin yksinkertaisempiin strategioihin. Jyrsinkoneiden täyttöstrategiat voidaan karkeasti jakaa kahteen tyyppiin. Spiraalitäytössä polygonin reunoja sisennetään toistuvasti luoden joukon sisäkkäisiä uria, joita kuljetaan spiraalimaisesti. Siksaktäytössä puolestaan luomme joukon määrätyn suuntaisia suoria, joiden leikkaukset muodostavat alueen kanssa verkkomaisen rakenteen siksak-janoja, joita kuljemme edestakaisin kuvan osoittamalla tavalla. Kuva 5: Siksak-täytössä kuljemme aina ennaltavalitun suuntaisilla siksakjanoilla sekä niiden välillä. Spiraali-täytössä kuljemme sisäänpäin alueen reunojen suuntaisesti. Tässä tekstissä emme pyri etsimään todistetusti parasta(tai edes hyvää) reittiä, vaan luomme suuren määrän kelvollisia reittejä ja valitsemme näistä parhaan edellämainittujen kustannusten mukaisesti. Emme myöskään pyri reaaliaikaiseen suunnitteluun, joten voimme turvallisesti olettaa, että käytössämme on kohtuullisesti laskenta-aikaa. 1.5 MATLAB-implementaatio MATLAB-implementaatio on jaettu alihakemistoihin hyvin pitkälti vastaten tämän dokumentin kappaleita seuraavasti: 7

\common Yleiset tiedostot \cost Reittien arvostelemiseen käytettävät rutiinit \gui Graafinen käyttöliittymä \mesh Verkon luomiseen liittyvät koodit \path Reitin luominen verkossa \preprocess Työalueen esikäsittely \splitmerge Työalueen jako osiin \return Verkossa luodun reitin palauttaminen aidoksi reitiksi \shortestpath Näkyvyysgraafi ja lyhimmät reitit \windingnum Työalueen sisennykseen liittyvät rutiinit Siispä ennen dokumentissa käsiteltävien koodien suorittamista tulee hakemistopolkujen alustamiseksi kirjoittaa addpath common cost gui mesh path preprocess return shortestpath... splitmerge windingnum Kunkin kappaleen lopussa pyrin käsittelemään vastaavan osan MATLABtoteutusta ja sen käyttöä. Koodi on pyritty jakamaan mahdollisimman riippumattomiin ja erillisiin osiin. Olen pyrkinyt kirjoittamaan lyhyet ohjeet jokaiselle komennolle. Ohjeet saadaan esiin kirjoittamalla help komento. Yksittäisen reitin suunnitelu verkossa voidaan karkeasti jakaa seuraaviin osiin: 1. Työaluetta sisennetään työkoneen leveyteen nähden verrannollinen matka siten, että työkoneen kulkiessa alueen reunaa pitkin se ei törmää ulkoreunaan tai esteisiin. Tämä suoritetaan windingnum-hakemiston rutiineilla. 2. Sisennetylle työalueelle luodaan inklinaatiovektorin määräämä verkko(mesh). Jos verkkoa halutaan poikkeuttaa jokin matka, työaluetta siirretään ennen verkon luomista. 3. Verkossa suunnitellaan reitti antamalla jokin aloitussolmu.(path) 4. Verkon reitti palautetaan aidoksi kulkureitiksi etsimällä tarvittavat lyhimmät reitit solmujen välillä, hyödyntämällä pseudoesteitä, jne... (return ja shortestpath). Jos verkkoa on poikkeutettu, palautettava kulkureitti siirretään poikkeutukseen nähden vastakkaisesti osumaan oikean työalueen kohdalle. Jos työalue halutaan jakaa osiin(splitmerge), muodostetaan jokaiseen osaan reitti erikseen. Edelleen, jos haemme hyvää reittiä arvostelemalla suuremman joukon eri reittejä, käytämme cost-hakemiston funktioita. 8

1.5.1 Implementaation tämänhetkinen tilanne Tällä hetkellä implementaatio on hyvä nähdä pikemminkin kokoelmana erilaisia työkaluja kuin kiinteänä hyvin viimeisteltynä menetelmänä tai ohjelmistona reittien muodstamiseen. Tämä dokumentaatio selvittää kuitenkin kaikki oleelliset askeleet näiden työkalujen käytössä siksak-reittien muodostamiseen ja prosessi voidaan automatisoida hyvin pitkälle. Toisaalta tässä on jälleen oleellista painottaa, että kirjoittajan käsitys peltoreittien muodostuksesta käytännössä voi olla hyvin puutteellinen! Koodi on kirjoitettu kokonaan MATLAB:lla ja sitä on noin 110 kilotavua ja 4000 riviä. 7 Ilmeisesti MATLAB-Compilerilla m-koodi voidaan kääntää C/C++-ohjelmistoissa hyödynnettäväksi kirjastoksi. Toisaalta tällä hetkellä edes MATLAB-koodin eri osat eivät ole toteutettu nopeuden kannalta lähellekään ideaalisesti eivätkä ne myöskään ole virheettömiä. Alemman tason ohjelmistokielillä kuten C:llä tai C++:lla voidaan hyödyntää hienostuneempia tietorakenteita ja konekieleksi käännettävä koodi on yleensä muutenkin paljon nopeampaa. Toisaalta MATLAB tarjoaa erinomaiset työkalut eri tulosten visualisointiin. Koodista puuttuu tällä hetkellä(29.3.2008) kunnolliset työkalut muodostettujen reittien arvostelemiseen. Etenkin käännösten arvostelu on osoittautunut erittäin hankalaksi. 1.5.2 Työaluetta kuvaavat tietorakenteet Polygoni MATLAB:ssa esitetään nx2 matriisina, missä matriisin rivi k vastaa pisteparia [x k, y k ]. Polygoni on suunnistettu rivien määräämien pisteiden mukaan. Ts. esimerkiksi koodinpätkä P = [0 0; 2 0; 2 2;1 4;0 2]; polyc_draw(p, 0.2, 0); piirtää polygonin kuvassa 6a. Jos matriisin P rivien järjestys käännetään, polygonin suunnistus ja täten nuolien suunta kuvassa muuttuu. Rivien kääntö onnistuu komennolla invert kirjoittamalla P=invert(P). Polygonin suunnistuksen selvittäminen onnistuu komennolla polygon_orientation. Funktio palauttaa 1 tai 1 riippuen onko suunnistus positiivinen tai negatiivinen. Edelleen polygonin etumerkillisen(suunnistuksen määräämän) pintaalan voi laskea komennolla polygon_area sekä sisäkulmat komennolla sisakulmat. Komento unit muodostaa mielivaltaista vektoria vastaavan yksikkövektorin. Työalue esitetään Cell Arraynä polygoneja, missä ensimmäinen polygoneista määrää alueen ulkoreunan ja jälkimmäiset esteitä. Luonnollisesti ulkoreunan polygonin tulee olla positiivisesti suunnistettu ja esteiden polygonien negatiivisesti suunnistettuja. Koodinpätkä 7 Tästä voi päätellä, että suurin osa kirjoittajan ajasta on mennyt algoritmien implementointiin eikä uusien ideoiden keksimiseen. 9

P = [0 0; 2 0; 2 2;1 4;0 2]; H1 = [0.5 0.5; 0.5 1; 1.5 1;1.5 0.5]; H2 = [0.25 2;0.75 2.5;0.75 2]; H3 = invert([2-0.25 2;2-0.75 2.5;2-0.75 2]); C = {P, H1, H2, H3}; määrää alueen kuvassa 6b. 4 3.5 3 2.5 2 4 3.5 3 2.5 2 1.5 1 0.5 0 0.5 0.5 0 0.5 1 1.5 2 2.5 1.5 1 0.5 0 0.5 0.5 0 0.5 1 1.5 2 2.5 Kuva 6: Polygoni P = [0 0; 2 0; 2 2; 1 4; 0 2] ja alue C = {P, H1, H2, H3} Alueita voidaan piirtää draw_contour(c)-komennolla tai piirtämällä polygonit yksittäin polyc_draw:lla, kuten kuvassa on tehty. 10

2 Verkon luonti Määritelmä 5 Inklinaatiovektori p = ˆpd, missä ˆp = (cos φ p, sin φ p ) on inklinaatio ja φ p inklinaatiokulma. Haluamme täyttää alueen P janoilla(urilla), jotka ovat inklinaation suuntaisia ja joiden etäisyys on d. Työalueen ajaminen tarkoittaa näiden janojen sekä jokaisen polygonin reunojen kulkua. Rajoitumme tässä ja seuraavassa kappaleessa näiden janojen ja niihin liittyvien reittien käsitteelyyn. 2.1 Inklinaatio, koordinaatisto ja siksak-janat Määritelmä 6 Vektorin r sisänormaali on normaalivektori, joka osoittaa r:n suhteen vasemmalle ja, jonka pituus on r. Vektorin r sisänormaalia merkitsemme IN(r). Ts. vetorin r sisänormaalivektori [ ] 0 1 n = IN(r) = r 1 0 Esimerkki 1 Konveksille polygonille POLY (v 1,...,v n ) POLY (v 1,...,v n ) = n {p R 2 : IN(r k+1 r k ) T (p r k ) 0} k=1 Siirrymme uuteen koordinaatistoon, jonka määräävät r ja sen sisänormaali n. ts. koordinaatistomuunnoksella [ ] n = 1 [ ] [ ] [ ] ] T x x ˆ 1 [n ] nt [ˆn ˆp = d p d y y pˆ. (2.1) T p Välttääksemme sekaannusta viittaamme jatkossa eri koordinaatistoihin käyttämällä merkkejä n ja p tai x ja y. Määritelmä 7 Siksak-suora ZL(i), i Z tarkoittaa joukkoa [ ] ZL(i) = {(n, p) R 2 : n = i} = {(x, y) R 2 : ˆn T x = id + }, (2.2) y missä d on uranleveys ja [0, d) siksak-janojen poikkeutus. Poikkeutus lisätään ennen verkon luontia ja vähennetään lopullisen reitinluonnin jälkeen. Sen erillinen käsittely tässä on täten tarpeetonta. Suorien leikkaukset P:n kanssa koostuvat usein useammasta erillisestä janasta, joita kutsumme siksak-janoiksi. Suoraan ZL(i) liittyvien P:n siksak-janojen joukkoa merkitään ZS(P, i). Kyseessä on siis joukko janoja, joten l = ZL(i) P, i Z. (2.3) l ZS(P,i) 11

2.2 Siksak-graafi Siksak-janojen päätepisteitä sanotaan siksak-solmuiksi sekä lokaaleja maksimija minimipisteitä n-koordinaatin suhteen ääriarvo-solmuiksi. Ilmeisesti ääriarvosolmut ovat aina kahden reunajanan leikkauspisteessä, joten niiden Ääriarvosolmujen tulkitaan aina olevan reunajanojensa alkupisteissä vastaavan reunan suunnistuksen mukaisesti. Siksak-solmut yhdistetään janoja pitkin vaakasuorilla kaarilla. Edelleen kaikki polygonin reunalla vierekkäiset solmut yhdistetään reunoja pitkin. Näin saamme verkkoa kuvaavan siksak-graafin G(P,p, ). 6 4 6 (R5L8) 8 5 2 0 7 3 n 2 (R4L1) 2 1 (R2L7) 4 (R3L2) p 4 2 0 2 4 6 8 10 Kuva 7: Siksak-solmut 3, 5, 7, 8, minimisolmut 1 ja 4 sekä maksimisolmut 2 ja 6. p = (d, 0),n = (0, d), d = 3. 2.3 Algoritmi solmupisteiden löytämiseksi Siksak-solmut esiintyvät polygonien C 0,...C n ja siksak-suorien leikkauspisteissä, joten ilmeinen keino leikkauspisteiden etsimiseksi on hakea suorien ja polygonien leikkauspisteet. 8 Toisaalta, koska suorat ovat kaikki samansuuntaisia, leikkauksien raaka laskenta ei ole välttämätöntä. Leikkauspisteet saadaan yksinkertaisesti laskemalla janojen päätepisteiden projektiot suuntavektorin ˆn virittämälle suo- 8 Held[9] viittaa ilmeisesti jonkinlaiseen Sweep Line -algoritmiin, joka suoriutuu työstä lineaarisessa ajassa suhteessa janojen määrään. 12

ralle. Janojen kanssa leikkaavat siksak-suorat saamme tästä suoraan jakamalla d:llä ja pyöristämällä sisäänpäin. Tarkat p-koordinaatit määritetään kulmakertoimien avulla. Haemme siis np-koordinaatistossa jokaista polygonin reunajanaa vastaavat n-koordinaatin maksimi- ja minimipisteet, jotka pyöristetään sisäänpäin suhteessa niiden määrittämään väleihin. Lemma 1 Olkoon r 1, r 2 R 2 janan e päätepisteet. Nyt e leikkaa siksaksuoria uuden koordinaatiston pisteissä (i, p(i)), missä Todistus: p(i) = p 2 p 1 n 2 n 1 (i n 1 ) + p 1, Siirrymme uuteen koordinaatistoon (n, p): i = min k=1,2 n k,.., max k=1,2 n k. (2.4) p k = 1 d r k T ˆp ja n k = 1 d r k T ˆn. k {1, 2} Siispä jana leikkaa siksak-viivoja ZL(i), missä i = min k=1,2 n k,.., max k=1,2 n k. Edelleen n-koordinaatteja vastaavat p-koordinaatit saamme p = kn + p(0), missä k = p 2 p 1 n 2 n 1 on kulmakerroin sekä p(0) = p 1 kn 1. Jos n 1 = n 2, tai leikkauksia on yksi, oletamme, ettei suora yksikään suora leikkaa janan kanssa. Ääriarvopisteet löydetään käymällä läpi jokaisen polygonin kärjet. Mikäli kärjen viereiset kärjet(janat) ovat molemmat joko tarkasteltavan kärjen alatai yläpuolella koordinaatin n-suhteen, on kyseessä ääriarvopiste. 2.4 Solmupisteiden yhdistäminen Jotta voisimme hyödyntää yksinkertaisia kulkureitin suunnitteluun käytettäviä strategioita, luomme verkon solmupisteisiin tietynlaisen struktuurin ympärillä sijaitsevista solmuista. Määräämme siksak-graafin kaaret linkittämällä tiettyjen sääntöjen mukaan ylös, alas, oikealle tai vasemmalle, jolloin reitin muodostava algoritmi voi helposti päättää kulkusuunnan lokaalin ympäristönsä perusteella. Jokaiseen solmupisteeseen liitetään seuraava tietorakenne: 9 struct Solmu{ Up, Down, Left, Right : Osoittimet viereisiin solmuihin. Point : Solmun koordinaatit. Type : Solmun tyyppi. Visited : Tieto siitä onko solmun kautta jo kuljettu. } 9 Tietorakenne on peräisin Heldin kirjasta [9]. Tosin kirjassa ei ole tarkemmin määritelty mitä suunnilla tarkoitetaan, joten tässä esiintyvä käsittely varmaankin poikkeaa siitä jonkin verran. 13

Siksak-janojen päätepisteet ovat tyyppiä 1, maksimit tyyppiä 2 ja minimit tyyppiä 3. 15 7. 14 13 Left Up 21 Left Right 20 12 6. 11 22 Down Up 10 5. 9 1 Left Down 2 8 4. 7 2 0 2 4 6 3. Tyypin 1 solmuista linkitetään reunalla aina ylös ja alas riippuen suunnasta, johon reunalla on lähdettävä kulkemaan saavuttaaksemme ensimmäisenä kyseessä olevan solmun. Koska tyypin 1 solmut ovat aina siksakjanojen reunapisteitä, linkitetään niistä myös oikealle tai vasemmalle sen mukaan kummassa suunnassa kyseessä oleva siksak-jana on. Ääriarvopisteiden määrittelyn nojalla on ilmeistä, ettei niistä voida linkittää muihin solmuihin yksikäsitteisesti ylös tai alas. Siispä reunaa pitkin yhteydessä olevat viereiset solmut linkitetään sen mukaan oikealle tai vasemmalle kumpi niihin johtavista ääriarvopisteeseen nähden viereisistä janoista virittää oikealla ja vasemmalla puolella olevat puolisuorat. Tarkasteltaessamme graafia G(P,p, ), tyypin 1 solmut ovat yhdistetty vaakasuorilla kaarilla, jos ne ovat saman siksak-janan päätepisteitä. Edelleen kaksi solmua on yhdistetty pystysuoralla kaarella, mikäli ne ovat vierekkäisiä solmuja reunalla. ts. on mahdollista kulkea niiden välillä reunaa pitkin kulkematta minkään toisen solmun kautta. Graafin pysty- ja vaakasuorat kaaret eivät suoraan vastaa linkityksiä! Toinen, ehkä parempi, tapa [3] linkittää solmut olisi viitata oikealle ja vasemmalle ainoastaan siksak-janojen päätepisteistä. Edelleen reunalla viitattaisiin positiiviseen ja negatiiviseen kiertosuuntaan. 10 10 Ikävä kyllä kirjoittaja havaitsi tämän seikan liian myöhään. 14

Lemma 2 Olkoon v 1 G tyypin 1 solmu sekä v 2 G ääriarvosolmu. Tällöin deg(v 1 ) = 3 ja deg(v 2 ) = 2. 2.5 Algoritmi solmupisteiden yhdistämiseksi 1. Käy ne verkon vaakasuorat suorat läpi, joilla on tyypin 1 solmuja Järjestä solmut suoralla p-koordinaatin mukaan pienimmästä suurimpaan. Yhdistä solmut pareittain linkittämällä solmuista asianmukaisesti vasemmalle tai oikealle. (1 2, 3 4, jne..). 2. Käy verkon kaikkien polygonien kaikki solmuja sisältävät janat läpi. Olkoon janan solmut n 1,..., n N järjestettynä n-koordinaatin mukaisesti pienimmästä suurimpaan. Yhdistä solmut n 2,..., n N 1 keskenään linkittämällä asianmukaisesti ylos ja alas. Yhdistä tyypin 1 reunasolmut sisäänpäin. (n 1 ylös, n N alas). Ääriarvosolmut yhdistetään sisäänpäin oikealle tai vasemmalle riippuen solmuun nähden viereisestä janasta. Koska ääriarvosolmut ovat aina janan alussa, kyseessä on edellinen jana. Yhdistä edelleen vastaavasti reunasolmut ulospäin. Koska viereisillä janoilla ei välttämättä ole solmuja, on etsittävä ensimmäinen vastaantuleva solmu. (katso FindNextNode) 2.6 Algoritmi FindNextNode Lähtiessämme tietystä solmusta etenemään polygonin reunalla, on usein tarpeellista etsiä seuraava vastaantuleva solmu. Edelleen on selvitettävä, onko näin löydetty solmu verkossa linkitettävä ylös, alas, vasemmalle tai oikealle. Tehtävä ei ole aivan triviaali, joten käsittelen sitä seuraavassa tarkemmin. Määritelmä 8 Jana on positiivisesti suunnistettu, jos sen loppupiste on koordinaatin n suhteen ylempänä kuin alkupiste. Muulloin se on negatiivisesti suunnistettu. Voimme liittää janan suunnistukseen reaaliluvun yksinkertaisesti laskemalla n-koordinaattien erotuksen. SegmentOrientation(seg, nextseg) 1 SO (nextseg seg) T ˆn 15

prevseg dir > 0 thisseg node nextseg nextseg dir > 0 thisseg node prevseg Kuva 8: Kaksi esimerkkitapausta siirtyessämme janojen välillä. Vasemmanpuoleisessa kuvassa nsu ˆ T p > psu ˆ T p ja dir > 0, joten etenemme oikealle. Jos etenemissuunta olisi vastakkainen (dir < 0), etenisimme vasemmalle. FindNextNode(node, dir) 1 nodes Etsi solmun kanssa samalla janalla olevat solmut ja järjestä ne n-koordinaatin mukaan pienimmästä suurimpaan. 2 SO SegmentOrientation(node.Segment) 3 isdown dir SO < 0 4 ulospain (node = first(nodes) isdown) (node = last(nodes) isdown) 5 if ulospain 6 then if isdown 7 then newn ode Prev(nodes, node) 8 else newn ode Next(nodes, node) 9 if node.type 1 10 then isright FindNextNode(node, dir) 11 isright isright 12 else ulospain 13 segment Etsi etenemissuunnassa ensimmäinen jana, jolla on solmuja. Järjestä solmut pienimmästä suurimpaan. 14 if SegmentOrientation(segment) dir > 0 15 then newn ode = First(segment) 16 else newn ode = Last(segment) 17 if node.type 1 18 then thisseg,prevseg,nextseg Etsi node:n janan sekä siitä nähden edellisen ja seuraavan janan alkupisteet. 19 psu, ˆ nsu ˆ suuntavektorit thisseg prevseg, thisseg nextseg. 20 isright (dir ( nsu ˆ psu) ˆ T p) > 0 Tyypin 1 solmuille isdown ilmaisee linkitetäänkö uusi solmu alkuperäisen ylä- vai alapuolelle. Tyypin 1 solmut oletetaan janan sisäpisteiksi, joten uusi solmu on janan alapuolella, jos ja vain jos dir SO < 0. Rivillä 4 selvitetään 16

olemmeko siirtymässä alkuperäisen solmun janalta pois. Jos emme ole, niin uusi solmumme on jonossa nodes joko seuraavana tai edellisenä. Jos node on tällöin ääriarvosolmu, on vastakkaiseen suuntaan edetessä seuraava solmu jollakin muulla janalla. Siispä uusi solmu on alkuperäiseen nähden oikealla jos ja vain, jos vastakkaiseen suuntaan edetessä löydetty solmu ei ole. Jos olemme siirtymässä toiselle janalle, uusi solmu ei välttämättä ole ensimmäisellä vastaantulevalla janalla. Siispä meidän täytyy etsiä reunaa pitkin etenemissuunnassa, kunnes löydämme ensimmäisen vastaantulevan janan, joka ei ole tyhjä. Tämän jälkeen etenemissuunnan ja janan suunnistuksen mukaan valitsemme kyseessä olevan janan reunimmaisen alkion asianmukaisesta reunasta(14-16). Jos olemme lähteneet ääriarvosolmusta, tulee vielä selvittää linkitetäänkö uusi solmu siitä oikealle vai vasemmalle. Koska ääriarvosolmu on aina janansa alussa, laskemme siitä suuntavektorit nsu ˆ ja psu ˆ seuraavan ja edellisen janan alkupisteisiin. Rivillä 20 tutkimme virittääkö kulkusuuntaamme liittyvä jana vastakkaiseen janaan nähden oikeanpuoleisen puolisuoran. 2.7 MATLAB-implementaatio Inklinaatiovektori esitetään 2x1-matriisina [x, y], missä vektorin normi määrää siksak-janojen etäisyyden ja suunta suunnan. Verkkoa ja sen linkityksiä kuvaava rakenne on hieman monimutkaisempi. Verkon yksittäinen solmu on struct-rakenne, joka sisältää tiedot solmun paikasta(koordinaatit, polygoni, jana), tyypistä ja siihen yhteydessä olevista muista solmuista. Solmuja kuvaavat struct-rakenteet varastoidaan cell arrayhin, joka koodissa on yleensä nimetty Mn tai MeshNodes. Solmut numeroidaan niiden indeksin mukaan kyseisessä cell arrayssä. Valitaan jokin inklinaatio ja luodaan verkko edellisessä kappaleessa määritetylle alueelle: p = 0.1*[1 3]; Mn = create_mesh_nodes(p, C); Mn = mesh_connect_nodes(p, Mn, C); mesh_draw(p, Mn, C, 1); Funktio create_mesh_nodes luo solmut vastaten tiettyä aluetta ja inklinaatiovektoria ja mesh_connect_nodes linkittää näin saadun rakenteen solmut keskenään solmujen ympäristön mukaan. Tarkempi toiminta on pyritty selittämään teoriaosuudessa edellä. 17

4 3.5 3 2.5 2 14 13 12 11 33 (R32L35) 10 27 32 25 16 (R14L15) 2629 (R25L29) 2830 (R27L28) 35 34 31 36 (R31L34) 8 1.5 1 15 20 (R19L23) 18 17 7 0.5 23 22 21 24 (R17L21) 0 6 5 4 3 2 1 9 (R7L1) 2. 0.5 1. 0. -1. -2. -3. 2 1 0 1-4. -5. 2 3 4-6. Kuva 9: Inklinaatiovektorin määräämä verkko alueelle C. Tummat janat vastaa siksak-janoja ja ympyrät siksak-solmuja. Ylös tai alaspäin kohdistetut kolmiot vastaavat lokaaleja maksimi- tai minimisolmuja. Kaikki solmupisteet on kuvassa numeroitu indeksinsä mukaan Mn:ssä. Nyt kuvasta havaitsemme, että solmu 33 on maksimisolmu, joka on kolmannella esteistä(neljäs polygoni). Solmu on selvästi linkitetty oikealle solmuun 32 ja vasemmalle solmuun 35(näkyy huonosti). Voimmekin MAT- LAB:ssa kokeilla olemmeko oikeassa seuraavasti: >> Mn{33} ans = Right: 32 Up: 0 Left: 35 Down: 0 Point: [-1.2500 8.7500] Segment: 2 Type: 2 Visited: 0 Polygon: 4 Koska kyseessä on ääriarvosolmu, sitä ei ole likitetty ylös tai alas. Kenttä 18

Point määrää solmun koordinaatit inklinaatiovektorin määräämässä koordinaatistossa. Polygon kertoo polygonin indeksin aluetta kuvaavassa cellarrayssä C ja Segment janan(jolla solmu) rivin polygonia esittävässä matriisissa. Type=1,2 tai 3 riippuen siitä onko kyseessä siksak-, maksimi- vai minimisolmu. Edelleen kenttä Visited liittyy reitinmuodostukseen, joten sitä ei tässä tarvita. Komennolla find_nodes voimme etsiä solmuja niiden kenttien mukaan. Esimerkiksi voimme hakea kaikki maksimisolmut järjestettynä n-koordinaatin mukaan seuraavasti >> find_nodes(mn, struct( Type, 2), 1) ans = 33 20 26 16 19

3 Kulun suunnittelu verkossa Koska siksak-janojen päätepisteet ovat aina astetta 3, kulkua, joka sisältäisi kaikki kaaret täsmälleen kerran(eulerian trail) ei ole olemassa. Perusideana onkin muodosti suuri määrä kelvollisia reittejä, jotka kattavat koko työalueen ja valita näistä paras jonkin arvosteluperusteen mukaan. Määritelmä 9 Umpikuja on konveksia ja Jakaja reflex:iä kärkeä vastaava ääriarvosolmu. Määritelmien merkitys on selvä varsinkin, kun d lähenee nollaa. Suorittaessamme inklinaation normaalin suuntaista(tai vastakkaista) siksak-täyttöä, jakajasolmuissa alue jakaantuu erikseen käsiteltäviin monotonisiin osiin. Saapuessamme umpikujapisteeseen, joudumme puolestaan palaamaan jollekin alueelle, jota ei vielä ole käsitelty. Jos d ei ole tarpeeksi pieni suhteessa reunajanojen pituuteen, monet edellisen määritelmän mukaiset umpikujat ja jakajat menettävät merkityksensä. Määritelmä 10 Olkoon v jakaja tai umpikuja ja v p sekä v n v:stä nähden ensimmäiset vastaantulevat tyypin 1 solmut positiivisessa ja negatiivisessa kiertosuunnassa. Olkoon edelleen v joko minimipiste ja v p sekä v n sen yläpuolella tai v maksimipiste ja v p,v n v:n alapuolella. Tällöin v on aito, jos se on v p ja v n välisten minimipisteiden minimi tai maksimipisteiden maksimi. Määritelmä 11 Graafin G = (V, E) kulku(walk) on äärellinen jono W = (v 0,e 1,v 1,e 2,...,v n 1,e n,v n ) graafin solmuja ja kaaria siten, että kaaren e i päätesolmuina ovat aina v i 1 ja v i. Edelleen kulku on reitti(path), jos mikään kaari ei esiinny siinä kahta kertaa. Edellisten määritelmien nojalla on oleellista huomioida ero puhuessamme reitistä graafissa ja reitistä työalueella. Työalueen reitti vastaa aina graafin kulkua, muttei välttämättä graafin reittiä. 3.1 Monotonisen reitin muodostus Määritelmä 12 Graafin reitti on monotoninen, jos se ei kulje joko alas tai ylös. ts. reitti kulkee n-koordinaatin suhteen vain ylös tai alas. Ideana on siis muodostaa reitti, joka kattaa jotain ennaltamääräämätöntä monotonista polygonia vastaavan osan työalueesta. Lähdemme muodostamaan siksak-täyttöä mielivaltaisesta solmusta joko ylös tai alas kulkien vuorotellen 20

siksak-janoja oikealle ja vasemmalle kunnes saavumme joko umpikujaan tai siksak-janalle, jossa on jo käyty. Seuraavissa pseudokoodeissa, AddToPath viittaa implementaatiokohtaiseen funktioon, joka lisää palautettavaan reittiin argumenttina olevan solmun ja merkitsee sen kuljetuksi. Algoritmin perusidea on seuraava: 1. Hakeudumme alussa ensimmäiseen vastaantulevaan tyypin 1 solmuun liikkumalla toistuvasti oikealle. Näin päädymme lopulta johonkin tyypin 1(mikäli olemassa) solmuun, koska jokainen ääriarvosolmu on linkitettu sekä oikealle ja vasemmalle. Jos tyypin 1 solmuja ei ole olemassa ajaudumme loputtomaan silmukkaan. Tämä on otettava huomioon implementoinnissa. 2. While luupin alussa oletetaan, että olemme tyypin 1 solmussa. 3. Solmu on aina jonkin siksak-janan päässä, joten siirrymme janan toiseen päähään oikealle tai vasemmalle sen mukaan kumpaan suuntaan on olemassa linkki. 4. Nyt olemme janan toisessa päässä ja siirrymme reunalla ylös tai alas argumentin dir mukaan. Jos uudessa solmussa on käyty, lopetamme. Jos taas kyseessä on tyypin 1 solmu siirrymme while-luupin alkuun. 5. Jos emme ole lopettaneet tai palanneet while-luupin alkuun, olemme ääriarvosolmussa. (a) Etsimme positiivisessa ja negatiivisessa kiertosuunnassa ensimmäiset vastaantulevat tyypin 1 solmut. Jos molemmissa solmuissa on käyty, lopetamme. Toisaalta, jos vain positiivisesta kiertosuunnasta löytyneessä solmussa on käyty, siirrymme jälkimmäiseen solmuun. Muulloin siirrymme ensimmäiseen. (b) Palaamme while-luupin alkuun. 21

CreatePath(startN ode, dir) 1 curnode startnode 2 while curnode.type 1 3 do AddToPath(curN ode) 4 curn ode curn ode.right 5 dead_end false 6 while dead_end 7 do AddToPath(curN ode) 8 if curn ode.right 9 then curn ode curn ode.right 10 else curnode curnode.left 11 AddToPath(curN ode) 12 if dir = 1 13 then curnode curnode.up 14 else curn ode curn ode.down 15 if curn ode.v isited 16 then dead_end true 17 else AddToPath(curN ode) 18 if curnode.type 1 19 then posnode, negnode curnode 20 poslist, neglist 21 while negnode.type 1 22 do negn ode FindNextNode(negN ode, 1) 23 if negnode.type 1 24 then INSERT(negList, negn ode) 25 while posnode.type 1 26 do posn ode FindNextNode(posN ode, 1) 27 if negnode.type 1 28 then INSERT(posList, posn ode) 29 if negn ode.v isited 30 then AddToPath(negList) 31 curnode negnode 32 elseif posn ode.v isited 33 then AddToPath(posList) 34 curnode posnode 35 else dead_end true 3.2 Kulun muodostus pinoamalla monotonisia reittejä Edellä muodostettu reitti kattaa vain osan työalueesta. Siispä reittejä on tarpeen muodostaa useita jonkin periaatteen mukaisesti. Eräs yksinkertainen ja kohtuullisen hyvin toimiva idea on pinota edellisellä algoritmilla muodostetun reitin ympäristöstä solmuja, joissa ei ole käyty. Edellisen reitin muodostamisen jälkeen käymme sen läpi tutkimalla jokaisen 22

tyypin 1 solmun ympäristöä positiivisessa ja negatiivisessa kiertosuunnassa. Ensimmäiset näin löydetyt ääriarvosolmut, joissa ei ole käyty, lisätään pinoon. Seuraavaksi pinosta valitaan päällimmäisin solmu, jossa ei ole käyty. Solmu viittaa aina tiettyyn siksak-janaan, joten CreatePath ajetaan uudelleen siitä pinon päällimmäistä solmua vastaavan siksak-janan päätysolmusta, joka on edellisen reitin päätöspistettä lähempänä. Samaa prosessia toistetaan kunnes pino on tyhjä. Pinottuja solmuja vastaavien reittien suunnat valitaan kiertosuunnan ja solmun janan orientaation perusteella. Reitti kulkee ylös tai alas solmuun johtaneen kierron mukaisesti. toolpathstack(startn ode, dir) 1 Push(stack, startn ode, dir) 2 while stack 3 do startn ode, dir Pop(stack) 4 while startnode.v isited stack 5 do startn ode, dir Pop(stack) 6 path CreatePath(startN ode, dir) 7 AddToToolpath(path) 8 for ind_path 1 to length(path) 9 do curn ode path[ind_path] 10 if curnode.type 1 11 then continue 12 posnode, negnode curnode 13 while posnode.type 1 posnode = curnode 14 do posn ode FindNextNode(posN ode, 1) 15 while negnode.type 1 negnode = curnode 16 do negn ode FindNextNode(negN ode, 1) 17 if posn ode.v isited 18 then SO SegmentOrientation(posN ode.p olygon, posn ode.segment) 19 Push(stack, posn ode, SO) 20 if negn ode.v isited 21 then SO SegmentOrientation(negN ode.p olygon, negn ode.segment 22 Push(stack, negn ode, SO) 3.3 Kauppamatkustajan ongelma ja paluuliikkeiden minimointi Lähteessä [3] Arkin, Held ja Smith antavat seuraavat tulokset: Lause 1 Paluuliikkeiden määrä verkossa, jota vastaavalla alueella ei ole esteitä, voidaan minimoida polynomiajassa suhteessa polygonin kärkien määrään. Esteillä, jotka leikkaavat vain yksittäistä siksak-janaa ei verkon kannalta ole merkitystä. Edelleen algoritmin suoritusaika on eksponentiaalinen paluuliikkeiden määrän suhteen. 23

Lause 2 Paluuliikkeiden määrän minimointi verkossa, jota vastaavalla alueella on esteitä, on NP-vaikea ongelma. Paluuliikkeiden minimoinnin ongelma voidaan palauttaa kauppamatkustajan ongelmaksi seuraavasti: 1. Jokaisen siksak-janan keskelle asetetaan uusi solmu ja janaa vastaava kaari korvataan kahdella kaarella vanhojen reunasolmujen sekä uuden solmun välillä. 2. Näille kaarille sekä kaikille reunakaarille asetetaan paino 0. 3. Kaikki siksak-janojen päätepisteitä vastaavat solmut linkitetään keskenään pareittain kaarilla, joiden painoksi asetetaan 1. Paluuliikkeiden minimointi ei todennäköisesti ole keskeisin pyrkimys tehtävässämme, joten edellämainittujen [3]:n algoritmien tarkempi käsittely tai implementointi ei ole tarpeen. 3.4 Matkan minimointi ja kiinalaisen postinkuljettajan ongelma Kiinalaisen postinkuljettajan ongelmassa etsimme lyhintä kulkua, joka kattaa graafin kaikki kaaret vähintään kerran. Ongelmasta tekee mielenkiintoisen se, että sen ratkaisuun on olemassa polynomiaikaisia algoritmeja. Toisaalta ratkaisemalla ongelman emme välttämättä minimoi muuta kuin kokonaismatkan, jolla koko työalue saadaan kuljettua. Kyseisillä algoritmeilla voi olla käyttöä siis vain silloin, kun kokonaismatka on kustannuksissa määräävänä. Edelleen ongelman ennakko-oletuksena on paluuliikkeiden jättäminen pois ja ongelman optimaalinen ratkaisu yleisessä tapauksessa lienee laskennallisesti liian raskas monimutkaisemmille alueille. Ainakin [8] antama algoritmi edellyttää mm, että laskisimme lyhimmän reitin pituuden jokaisen paritonta astetta olevan solmuparin välillä. Nopein [6] antama algoritmi tähän on suoritusajaltaan O(V 2 lgv + V E). 3.5 MATLAB-implementaatio Mielivaltainen reitti verkossa on helppo kuvata vektorina(nx1-matriisi) solmuja, joiden kautta reitti kulkee. Edelleen myöhemmän käsittelyn kannalta on syytä aina selvittää onko kyseessä liike siksak-janoilla, reunalla vai paluuliike. Funktio create_path, joka on tarkemmin käsitelty edellä luo monotonista polygonin osaa vastaavan reitin lähtien tietystä ääriarvosolmusta ja lopettaen ensimmäiseen vastaantulevaan aitoon umpikujaan. Funktio palautta nx1-matriisin niiden solmujen indeksejä, joissa on jo käyty. Voimme havainnollistaa tätä seuraavasti: 24

>> path = create_path(p, Mn, C, 1, 9); >> path ans = Columns 1 through 13 9 7 1 2 8 10 31 32 11 12 19 20 23 Columns 14 through 23 5 6 28 29 15 16 14 25 26 26 >> path_draw(p, Mn, [path zeros(length(path), 1)], 0) 4 3.5 3 2.5 2 14 13 12 11 33 (R32L35) 10 27 32 25 16 (R14L15) 2629 (R25L29) 2830 (R27L28) 35 34 31 36 (R31L34) 8 1.5 1 15 20 (R19L23) 18 17 7 0.5 23 22 21 24 (R17L21) 0 6 5 4 3 2 1 9 (R7L1) 2. 0.5 1. 0. -1. -2. -3. 2 1 0 1-4. -5. 2 3 4-6. Kuva 10: Monotoninen reitti solmusta 9 solmuun 26. Huomaa, että solmu 14 on likitetty oikealle solmusta 16 ja 16 ylös solmusta 14. Edellinen kuva voi vaikuttaa hieman sekavalta, joten voimme visualisoida reittiä myös seuraavasti: subplot(1,2,1) draw_contour(c, g ); path_draw(p, Mn, [path zeros(length(path), 1)], 0); axis equal 25

subplot(1,2,2) mesh_draw(p, Mn, C, 0); path_draw(p, Mn, [path zeros(length(path), 1)], 0); 4 4 3.5 3.5 3 3 2.5 2.5 2 2 1.5 1.5 1 1 0.5 0.5 0 0 0.5 1 1.5 2 2. 0 0 0.5 1 1.5 2 Kuva 11: Kahdella eri tavalla visualisoitu reitti verkossa. Tietysti edellä esitetty reitti ei kata koko työaluetta, joten joudumme pinoamaan reittejä komennolla toolpath_stack. path = toolpath_stack2(p, Mn, C, 9); >> path ans = Columns 1 through 13 9 7 1 2 8 10 31 32 11 12 19 20 23 0 0 0 0 0 0 0 0 0 0 0 0 0 Columns 14 through 26 5 6 28 29 15 16 14 25 26 26 27 13 22 0 0 0 0 0 0 0 0 0 1 0 1 0 26

Columns 27 through 36 4 3 21 24 17 34 36 36 35 18 0 0 0 0 0 0 0 1 0 1 path_draw(p, Mn, path); Tästä ja seuraavasta kuvasta havaitsemme helposti, että paluuliikkeet ovat väleillä 26 27, 13 22, 36 35. Solmussa 18 havaitsemme, että kaikki siksak-janat on kuljettu. 4 3.5 3 2.5 2 4 3.5 3 2.5 2 14 13 12 11 33 (R32L35) 10 27 32 25 16 (R14L15) 2629 (R25L29) 2830 (R27L28) 35 34 3136 (R31L34) 8 1.5 1.5 1 1 15 20 (R19L23) 19 18 17 7 0.5 0.5 23 22 21 24 (R17L21) 0 0 0.5 1 1.5 2 0 2. 1. 6 5 4 3 2 1 9 (R7L1) 0 0.5 1 1.5 2 2.5 0. -1. Kuva 12: Työkoneen reitti verkossa, joka kattaa kaikki siksak-janat. Katkoviiva merkitsee paluuliikkeitä. 27

4 Verkon kulun palauttaminen aidoksi reitiksi Kulku verkossa tapahtuu tiettyjen solmujen välillä, joten se ei suoraan vastaa työkoneen käytännössä kulkemaa reittiä. Verkon kulku on siis jollain tavoin palautettava aidoksi reitiksi. Jaamme tarkastelun liikkeiden mukaan kolmeen eri tyyppiin 1. Liike siksak-janoja pitkin. 2. Liike reunoja pitkin janojen välillä. 3. Paluuliike umpikujista. Siksak-janoilla lyhin reitti on aina triviaalisti janoja pitkin. Paluuliikeillä pyrimme hakemaan lyhimmät reitit solmujen välillä, mutta toisaalta lyhimpien reittien etsiminen on hyödyllistä myös reunaliikkeillä. Yksinkertaisin hyväksyttävä ratkaisu olisi kulkea reunaa pitkin, mutta tämä ei aina ole optimaalista. Kuva 13: Vasemmanpuoleisessa kuvassa lyhin reitti ylemmältä solmulta alemmalle on suora kun taas oikeanpuoleisessa joudumme kiertämään reunaa pitkin. Tämän kappaleen perusongelmana on siis löytää lyhin reitti kahden pisteen välillä. Tämä ei kuitenkaan implementaatiossamme ole onnistunut kaikilta osin nopeuden tai luotettavuuden kannalta täydellisesti, joten etsimme lyhimmät reitit vain paluuliikkeillä ja muulloin kierrämme estettä tai alueen ulkoreunaa tiettyyn kiertosuuntaan. Jos käytössämme olisi ideaalinen algoritmi lyhimpiä reittejä varten, koko kappaleen perusidea olisi yksinkertainen: Korvaamme verkon kulun jokaisen solmuparin välillä lyhimmällä reitillä alueen sisässä. Ideaalisella algoritmilla tarkoitamme tarpeeksi tarpeeksi nopeaa algoritmia, joka assosioi jokaiseen sille annettuun pistepariin alueella murtoviivan, joka kuvaa pisteiden välillä lyhintä reittiä alueen sisässä. 28

4.1 Lyhimmät reitit Yleisesti reittejä kahden mielivaltaisen pisteen välillä on ääretön määrä, mutta ainakin polygoniesteillä tilannetta saadaan yksinkertaistettua havainnolla: Lemma 3 Alueella P kahden pisteen välinen lyhin reitti koostuu murtoviivasta, jonka sisemmät kärjet ovat estepolygonien kärkiä. Tarkempi todistus löytyy ainakin lähteistä [10] ja [4], mutta perusideana lienee huomioida, että lyhimmän reitin pitää myös olla lokaalisti lyhin. Jos käänteisesti oletamme jonkin kaarevan osareitin olevan lyhin, reittiä saadaan lyhennettyä suoristamalla osareitti reunajanojen mukaisesti. Lyhin reitti kulkee siis yksinomaan esteiden kärkien kautta. Tämäntyyppisiä reittejä on äärellinen määrä ja löydämme niistä lyhimmän tarkastelemalla vastaavaa graafiteoreettista ongelmaa. 1 0.9 0.8 18 41 17 42 43 44 0.7 40 21 15 25 24 23 22 0.6 12 14 39 26 13 0.5 11 27 0.4 19 38 8 10 9 28 0.3 37 7 29 30 0.2 36 31 1 35 6 34 33 32 0.1 2 3 5 4 0 0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 16 20 Kuva 14: Lyhin reitti näkyvyysgraafissa solmujen 26 ja 38 välillä. 4.2 Näkyvyysgraafi Ideana on siis muodostaa graafi, jonka solmuina ovat polygonien kärjet sekä reitin alku- ja loppupisteet. Kaksi solmua on yhdistetty kaarella, jos niiden välillä on mahdollista liikkua suoraan törmäämättä yhteenkään esteseen. ts. 29

solmut näkevät toisensa. Tämäntyyppistä graafia kutsutaan näkyvyysgraafiksi. Koska tutkimme kulkureittejä, asetamme kaarien painoksi niiden euklidisen pituuden. Näin saamme painotetun graafin, josta voimme etsiä alueen lyhintä kulkureittiä yksinkertaisesti etsimällä lyhintä kulkua graafissa kahden solmun välillä. Luotettavien näkyvyysgraafialgoritmien toteuttaminen tietokoneella ei ole ollenkaan triviaali tehtävä ja valmiita implementaatioita ei löydä helpolla. Yleensä kirjoissa annetuissa algoritmeissa vielä oletetaan, että lukijan käytössä on tarkka aritmetiikka. Enemmän tietoja eri algoritmeista lyhimmän reitin etsimiseksi ja mm. O(n logn) algoritmi löytyy lähteestä [1]. Tehtävään on siis olemassa paljon parempia algoritmeja, mutta niiden toteuttaminen lienee vaikeampaa. Implementoimamme algoritmi pohjautuu osittain Berg:in antamaan algoritmiin [4], mutta on osoittautunut kohtuullisen luotettavaksi vaikkakin hitaaksi. Toisaalta työalueen näkyvyysgraafi täytyy laskea vain kerran eikä se muutu inklinaation muuttuessa. Ongelmaksi muodostuu kuitenkin se, että meidän tulee löytää lyhimpiä reittejä myös solmujen, jotka eivät vastaa näkyvyysgraafin kärkiä, välillä. Näkyvyysgraafialgoritmin suoritusaika on O(n 3 ), joten näkyvyysgraafin yleinen määritys kaikkien solmujen suhteen ei ole järkevää. Koska lyhintä reittiä kuvaavien janojen sisemmät pisteet ovat aina polygonien kärjillä, tämä ei ole tarpeellistakaan. Etsiessämme lyhintä reittiä polygonien kärkipisteiden ulkopuolisten pisteiden välillä, meidän tarvitsee erikseen hakea reitin aloitus- ja lopetuspisteistä näkyvät polygonin kärjet, jotka voidaan väliaikaisesti lisätä näkyvyysgraafiin. 4.3 Dijkstra n algoritmi Dijkstra n algoritmilla löydämme painotetussa graafissa lyhimmät reitit määrätystä alkusolmusta kaikkiin muihin graafin solmuihin, joihin on olemassa kulku. Se sopii siis siis tarkoitukseeme hyvin. Lisäksi algoritmin implementaatioita löytyy helposti ja se on hyvin nopea, joten sen tarkempi käsittely lienee tässä turhaa. 4.4 Pseudoesteet Kaikkia esteitä ei ole aina järkevää käsitellä osana verkkoa vaan ne kannattaa kiertää. Kierrettäviä esteitä ei käsitellä osana alkuperäistä verkkoa, johon reitti on edellisen kappaleen keinoin muodostettu. Siispä kutsumme niitä pseudoesteiksi. Pseudoesteet auttavat monissa tilanteissa etenkin vähentämään turhia paluuliikkeitä, koska verkon reitinmuodostusalgoritmit eivät näe niitä. 30

Luomme toisen verkon, johon pseudoesteet on lisätty siten, ettei ne muuta alkuperäisten solmujen indeksejä. Koska pseudoesteille kulku uudessa verkossa käy aina siksak-janojen kautta, on siksak-janojen kulku ainoa uudella tavalla huomioitava osuus. Tietysti lyhimmät reitit paluuliikkeillä tosin lasketaan myös alueelle, joka sisältää uudet esteet. Verkon reitti on muodostettu alkuperäisessä verkossa, joten on käytävä läpi jokainen siksak-janan kulku. Koska janojen päätyjen indeksit eivät ole muuttuneet siirtyessämme tarkastelemaan uutta verkkoa, tarvitsee meidän vain hakea reitti uudessa verkossa samojen solmujen välillä. Idea on seuraava: 1. Kuljemme janaa pitkin kunnes saavumme esteelle tai janan loppuun. 2. Jos olemme esteellä tutkimme positiivisessa ja negatiivisessa kiertosuunnassa kumpi reitti on lyhyempi janalle esteen toisella puolella ja kuljemme sitä pitkin. 3. Jos olemme janan lopussa, lopetamme. 4. Siirrymme kohtaan 1. Edellisessä algoritmissa on usein hyödyllistä muodostaa kierrettävistä esteistä nk. konveksi verho(convex hull). Lyhin kiertoreitti kahden konveksin verhon reunapisteen välillä on ilmeisesti toinen kiertosuunnista. Kuva 15: Yhtenäisellä viivalla kuvattu este ja katkonaisella viivalla kuvattu vastaava konveksi verho. Kuvasta on jokseenkin selvää, että konveksin verhon kiertäminen on paljon kannattavempaa etenkin jos esteen koko on pieni. 31

4.5 MATLAB-implementaatio Näkyvyysgraafiin liittyvä koodi ja Dijkstran algoritmin implementaatio löytyy hakemistosta shortestpath. Näkyvyysgraafin implementaatio on todennäköisesti epävakain ja sekavin osa koko projektin koodista. 4.5.1 Näkyvyysgraafi Kuva 16: Esimerkki näkyvyysgraafista alueelle C. visiblevertices_polar määrittää tietystä alueen polygonin kärjestä tai pisteestä näkyvät alueen kärjet. Kärkeen nähden viereisien kärkien näkyvyyden selvittäminen ei ole aina luotettavaa, joten ne on syytä hylätä ja lisätään funktiossa visibilitygraph_polar manuaalisesti. Funktiota visiblevertices_polar voidaan käyttää seuraavasti (katso. help visiblevertices_polar): >> subplot(1, 2, 1), draw_contour(c, b ); >> visiblevertices_polar(c, 2, 2, [], 1) ans = 2 2 1 4 4 4 3 3 1 1 1 3 3 3 1 2 3 1 5 1 32

>> subplot(1, 2, 2), draw_contour(c, b ); >> visiblevertices_polar(c, 1, 2, [1 1.5 0 1.99*pi], 1) ans = 1 1 4 4 4 1 3 3 3 1 2 2 2 3 3 1 2 4 2 3 1 5 2 3 jolloin päädymme seuraavaan kuvaajaan 4 4 3.5 3.5 3 3 2.5 2.5 2 2 1.5 1.5 1 1 0.5 0.5 0 0 0.5 1 1.5 2 0 0 0.5 1 1.5 2 Kuva 17: Esimerkkikuvaaja yksittäisistä kärjistä ja pisteistä näkyvistä alueen polygonien kärjistä. Koko näkyvyysgraafin saamme laskettua komennolla visibilitygraph_polar seuraavasti(ks. aikaisempi kuva). >> [Vout, E, V] = visibilitygraph_polar(c, 1); >> [Vout, V] ans = 1.0000 0 0 1.0000 1.0000 2.0000 2.0000 0 1.0000 2.0000 3.0000 2.0000 2.0000 1.0000 3.0000 33

4.0000 1.0000 4.0000 1.0000 4.0000 5.0000 0 2.0000 1.0000 5.0000 6.0000 0.5000 0.5000 2.0000 1.0000 7.0000 0.5000 1.0000 2.0000 2.0000 8.0000 1.5000 1.0000 2.0000 3.0000 9.0000 1.5000 0.5000 2.0000 4.0000 10.0000 0.2500 2.0000 3.0000 1.0000 11.0000 0.7500 2.5000 3.0000 2.0000 12.0000 0.7500 2.0000 3.0000 3.0000 13.0000 1.2500 2.0000 4.0000 1.0000 14.0000 1.2500 2.5000 4.0000 2.0000 15.0000 1.7500 2.0000 4.0000 3.0000 Vout(ensimmäiset kolme saraketta) sisältää listan näkyvyysgraafin solmuista ja niitä vastaavista koordinaateista. V(Jälkimmäiset kaksi saraketta) kertovat solmuja vastaavat alkuperäisen alueen polygonit ja niiden kärjet. Koska matriisissa E on 59 riviä, näytetään siitä vain osa EDU>> E(1:5, :) ans = 1 1 5 2 1 2 3 1 9 4 1 6 5 1 7 E on lista näkyvyysgraafin solmujen välisistä kaarista. Ensimmäinen sarake sisältää kaarien numeroinnin. Toinen ja kolmas sarake määräävät jokaisen kaaren alku- ja loppusolmut. Koska kyseessä ei ole suunnattu graafi, solmujen järjestyksellä ei itsessään ole väliä. Toisaalta funktio asettaa toiseen sarakkeeseen aina sen solmun, jolla on pienempi indeksi. Tämä helpottaa taulukon käsittelyä jälkeenpäin. 4.5.2 Lyhimmät reitit kahden pisteen välillä Funktio shortestpath etsii lyhimmän reitin kahden alueen pisteen välillä. Funktion suora käyttö on jokseenkin vaivalloista, mutta voimme etsiä lyhimmän reitin kahden solmun välillä alueellamme esimerkiksi seuraavasti kuvan 2.7 tilanteessa: >> startp = Mn{9}; >> endp = Mn{25}; >> n = inner_normal(p)*norm(p, 2); >> startp.point = startp.point*[n;p]; 34

4 14 3.5 13 12 3 11 2.5 2 33 (R32L35) 10 32 27 25 (R14L15) (R25L29) (R27L28) 16 2629 30 3436 31 28 35 8 (R31L34) 1.5 1 15 20 (R19L23) 18 17 7 0.5 23 22 21 24 (R17L21) 0 6 5 4 3 2 1 9 (R7L1) 2. 0.5 0 0.5 1 1.5 2 2.5 1. Kuva 18: Lyhin reitti solmujen 9 ja 25 välillä. >> endp.point = endp.point*[n;p]; >> [Vout, E, V] = visibilitygraph_polar(c, 0); >> shortestpath(startp, endp, C, Vout, E, V) ans = 2.0000 0 1.5000 1.0000 0.2500 2.0000 0.3750 2.1250 >> clf >> mesh_draw(p, Mn, C, 1); >> plot(ans(:, 1), ans(:, 2), k, LineWidth, 5) Näin saamme kuvan 18. 4.5.3 Reitin palautus Yleensä näkyvyysgraafiin liittyvien funktioiden suora käyttö ei ole tarpeellista. Verkon kulku voidaan palauttaa aidoksi reitiksi funktiolla path_topoints. Jälleen tuttuun tapaan funktion argumenttien ja palautteen muoto ilmenee kirjoittamalla help path_topoints, joten annamme vain esimerkin sen käytöstä. 35

4 3.5 3 2.5 2 1.5 1 0.5 0 0 0.5 1 1.5 2 Kuva 19: Verkon kulku palautettu aidoksi reitiksi. Tämän kuvaajan ei ole tarkoitus olla miellyttävä vaan havainnollistaa algoritmin toimintaa. Jatkamme kappaleessa kolme luodulla verkon kululla path seuraavasti: pp = path_topoints(p, Mn, C, {}, path); draw_contour(c, g: ) axis equal ppplot(p, pp, 0.4, 0.1) Tämä koodi tuottaa kuvan 19. Komentoa ppplot käytetään reittien animoituun piirtämiseen. 36

5 Esikäsittely 5.1 Inklinaatio 5.1.1 Käännösten minimointi Peltorobottiongelmassa yleensä paljon hyödyllisempää on pyrkiä minimoimaan käännöksien kuin paluuliikkeiden määrää. On helppo havaita, että jyrkkien käännösten määrä korreloi hyvin voimakkaasti siksak-janojen määrän kanssa. Itseasiassa lähes kaikki jyrkät käännökset tapahtuvat yleensä juuri siksakjanojen päissä. On siis syytä selvittää kuinka määritämme inklinaation siten, että siksak-janoja on minimaalinen määrä? Kohtuullinen tulos saadan helposti yksinkertaisella brute force -tekniikalla. Käymme kaikki inklinaatiot välillä [0, π) läpi tietyllä tiheydellä ja laskemme jokaiseen inklinaatioon liittyvien siksak-janojen määrän. Koska jokaiseen janaan liittyy täsmälleen 2 päätepistettä, janojen määrä saadaan suoraan yhtälön (2.4) kautta summaamalla työalueen kaikkien reunajanojen yli ja jakamalla kahdella. 5.2 Reunapolygonien sisennys Välttääksemme törmäyksiä esteiden tai reunan kanssa, on niitä kuvaavia polygoneja sisennettävä tai laajennettava asianmukaisesti. Toisinsanoen tarvitsemme nk. polygon offset-algoritmin. Perusideana on, että algoritmille syötetään mielivaltainen polygoni P ja sisennysmatka δ, jonka jälkeen algoritmi muodostaa joukon P δ uusia polygoneja, jonka polygonien rajoittamat pisteet ovat vähintään matkan δ päässä alkuperäisen polygonin reunasta.p δ on joukko, koska sisennys voi jakaa yksittäisen polygonin useampaan osaan. Työkonetta edustava δ-säteinen pallo voi liikkua P δ :ssa koskematta alkuperäisen polygonin P reunaan. Täyttöongelmassamme on ilmeistä, että sisennetyn polygonin on oltava kuitenkin mahdollisimman suuri, jotta osa työalueesta ei jäisi kulkematta. Haemme siis mahdollisimman suuria pistevieraita polygoneja P δ P siten, että P δ :n jokainen piste on vähintään matkan δ päässä alkuperäisen polygonin reunasta P. Edelläoleva järkeily pätee vain polygonin(tässä tapauksessa ulkoreunan) sisentämiseen. Polygoneja on esteiden tapauksessa puolestaan laajennettava. Tällöin haemme mahdollisimman pientä polygonia P δ siten, että P P δ ja P δ :n reunan jokainen piste on vähintään matkan δ päässä P:n reunan jokaisesta pisteestä. Vaikka ongelma vaikuttaa päällepäin hyvin yksinkertaiselta, yleisen polygoneja sisentävän algoritmin implementointi on erittäin vaikeata. Eräs lähestymistapa on nk. straight skeleton-muunnos (ks. [7] ja [2])., mutta perusperiaatteeltaan ja implemennoinniltaan yksinkertaisemmaksi osoittautui seuraavaksi käsiteltävä algoritmi, joka pohjautuu perusidealtaan lähteeseen [5]. 37

P δ P δ P Kuva 20: Alkuperäinen polygoni P ja vastaava sisennetty polygoni P δ sekä laajennettu polygoni P δ. 5.2.1 Johdanto Emme ole yksikäsitteisesti määritelleet sisennettyä tai laajennettua polygonia. Kyseiset polygonit voidaan muodostaa useilla tavoin. Emme myöskään hae laajinta(ideaalista) sisennystä, koska tällöin sisennys ei välttämättä enään ole polygoni. 11 Määritellään joukko S(P, δ) niiden suunnattujen suorien joukoksi, jotka saadaan siirtämällä jokaista P:n janaa sisänormaalinsa suuntaan matka δ ja valitsemalla siirretyn janan virittämä suora. Oletetaan, että sisennetty polygoni P δ on näiden suorien joukon osajoukko(kuva 21). Kuvasta voimme päätellä, että P δ saadaan valitsemalla näistä suorista juuri oikeat janat ja yhdistämällä ne polygoneiksi. Edelleen janojen alku- ja loppupisteet vaikuttavat olevan suorien leikkauspisteitä. 5.2.2 Esisisennys P δ saadaan selvästi jotenkin kulkemalla näitä suoria pitkin leikkauspisteestä leikkauspisteeseen. Toisaalta kuvasta ilmenee, että suorat muodostavat sotkuisen verkon, jossa on hyvin suuri määrä näin kierrettäviä silmukoita. Oleellinen kysymys onkin seuraava: Voimmeko valita näistä suorista sellaiset osat, jotka eivät esiinny sisennetyn polygonin sisässä? 11 Jos haemme täsmälleen δ etäisyydellä reflex-kärjestä olevia pisteitä, lopputulos on kaareva. 38

Kuva 21: Oletamme sisennetyn polygonin olevan siirrettyjen suorien osajoukko. Tämä onnistuu muodostamalla polygonista P suljettu murtoviiva P PRE δ seuraavasti: 1. Korvataan jokainen konveksi kärki v kärjillä v p = v + δn p ja v n = v + δn n, missä n p ja n n on kärjestä v nähden edellisen ja seuraavan reunan yksikköulkonormaalit. 2. Korvataan jokainen reflex kärki v kärkeen yhteydessä olevia reunoja vastaavien siirrettyjen suorien leikkauspisteellä. Näin päädymme kuvaan 22. Ensimmäisestä kuvasta nähdään, että sisennetty polygoni muodostaa nyt kokonaisen silmukan, joten periaatteessa polygonin määrittäminen tässä tapauksessa pitäisi olla kohtuullisen helppoa. Toisaalta jälkimmäisessä kuvassa, jossa δ on paljon suurempi, silmukoita on useita. Molemmissa kuvista on kuitenkin oleellista huomioida, että P δ muodostuu tietyistä kuvan erillisistä silmukoista, joiden sisäpuolella ei ole muita janoja. Haluamme siis selvittää leikkauksien määräämät silmukat ja tämän jälkeen mitkä silmukoista kuvaavat sisennystä P δ. 5.2.3 Murtoviivagraafin muodostus Muodostamme graafin, jonka solmut ovat Pδ PRE :n kärkiä tai janojen leikkauspisteitä. Edelleen näiden solmujen välille asetetaan graafiin kaaret vastaten Pδ PRE :n janoja. Näin päädymme kuvaan 23. 5.2.4 Silmukoiden etsintä Haluamme etsiä hakea kuvan 23 kaltaisesta graafista kaikki silmukat. Kaikki edelliset vaiheet algoritmista ovat hyvin yksinkertaisia ja nopeita, mutta sil- 39

(a) P0.04 PRE (b) P0.1 PRE Kuva 22: Kaksi P:n esisisennystä. 2 3 16 17 18 30 20 196 129 14 28 15 5 4 9 1121 23 24 10 25 7 12 26 2722 13 8 Kuva 23: P PRE 0.1 :tä vastaava graafi. mukoiden etsintään emme ole löytäneet nopeata ja täysin luotettavaa keinoa. Siispä menettelemme yksinkertaisesti seuraavasti. Muodostetaan jokaisesta graafin kaaresta kulku lähtemällä kumpaankin suuntaan edeten solmupisteiden välillä kääntymällä aina niin vasemmalle kuin mahdollista. Jos tietyn solmun kautta on kuljettu kahdesti, reitti hylätään. Lisättäessä silmukoita muistiin tutkitaan onko sama silmukka lisätty jo aiemmin. Tämä menettely ei toimi aina, mutta emme ole onnistuneet tuottamaan yhtään tapausta, jossa algoritmi pettäisi käsiteltäessä sisennettyä polygonia vastaavaa murtoviivaa. 40

5.2.5 Kierrosluku Nyt meillä on joukko silmukoita ja haluamme selvittää mitkä niistä vastaavat P δ :n polygoneja. Kun tarkastelemme kuvaa 22, havaitsemme että kelvollisia silmukoita karakterisoi se, että ne ovat positiivisesti suunnistettuja. ts. reunajanat kiertävät vastapäivään. Yleisemmin ilmeisesti 12 pätee, että kelvollisten silmukoiden sisäpisteiden nk. kierrosluku Pδ PRE :n suhteen on 1. ts. 1 dθ = 1 2π Pδ PRE 2π i θ i = 1 2π arccos i ( ) (vi r) (v i+1 r) sgn θ i = 1, v i+1 r v i r (5.1) missä θ i on etumerkillinen kulma kahden kärjen välillä kierrosluvun laskentapisteestä r nähtynä. θ 2 θ 4 θ 3 θ4 θ 1 2π 2π θ 1 θ 2 θ 3 θ 1 + θ 2 + θ 3 + θ 4 = 2π θ 1 + θ 2 + θ 3 + θ 4 = 0 Kuva 24: Kolme esimerkkiä kierrosluvun laskemisesta. Selvästi kierrosluku positiivisesti suunnistetun polygonin sisäpisteille on 1. Murtoviivan suunnistuksen kääntäminen vaihtaa etumerkkiä. Edelleen kierroluku suljetun murtoviivan rajoittamien silmukoiden ulkopuolisille pisteille on aina 0. Koska nyt tunnemme graafin kaikki silmukat saamme polygonin P sisennyksen P δ hakemalla jokaiselle silmukalle sisäpisteen r ja laskemalla r:n kierrosluku koko murtoviivan Pδ PRE suhteen. 5.2.6 Polygonien laajentaminen 5.3 Alueen jako osiin Yleisen alueen mielivaltainen jako osiin ei aina ole aivan triviaali tehtävä, joten joudumme jälleen rajoittumaan erikoistapauksiin. Olemme implementoineet algoritmit, joilla alue voidaan jakaa kahteen osaan joko janalla, joka 12 Edellisessä järkeilyssä ei ole formaalisti todistettu mitään, joten en ole kovin varma pätevätkö tulokset yleisesti. 41

lävistää ainoastaan ulkoreunan kahdessa paikassa tai sitten kahdella janalla, jotka molemmat lävistävät ulkoreunan ja saman esteen. (a) Alkuperäinen alue (b) Jaettu alue Kuva 25: Alueen jako ainoastaan ulkoreunoja leikkaavalla janalla. (a) Alkuperäinen alue (b) Jaettu alue Kuva 26: Alueen jako ulkoreunoja leikkaavalla ja yksittäistä estettä leikkaavalla janalla. Perusideana algoritmissa on yksinkertaisesti lähteä kiertämään ulkoreunan ja janojen leikkauspisteistä positiiviseen kiertosuuntaan ja muodostaa näin kaksi positiivisesti suunnistettua polygonia. Jos leikkaamme janalla estettä, kyseinen este katsotaan tuhotuksi. Kaikille muille esteelle selvitämme kumman juuri muodostetuista polygoneista sisässä ne ovat. Tämä onnistuu helposti esimerkiksi valitsemalla mielivaltainen esteen kärkipiste ja laskemalla kierroluku polygonin suhteen. Positiivisesti suunnistetun polygonin sisässä kierrosluku on aina 1. Edellinen menettely jakaa alueen aina täsmälleen kahteen osaan, joiden kesken ei-tuhotut esteet jaetaan. Mikään ei tietenkään estä jakamasta aluetta useampiin osiin käyttämällä algoritmia toistuvasti. 42

6 MATLAB-esimerkkiajoja ja ideoita jatkokehittelyä varten Seuraavassa annan muutaman käytännön esimerkin MATLAB-koodin käytöstä ja esitän ideoita mahdollista jatkokehittelyä varten. 6.1 Yksinkertaisien alueiden luominen Haluamme luoda yksinkertaisen alueen eri algoritmien kokeilemista varten. Tähän on lukijalle työkalu polydraw, jolla voi piirtää polygoneja ja kopioida ne leikepöydän kautta MATLAB:in komentoriville. Kuva 27: Clear figure pyhkii kuvaajan ja New aloittaa uuden polygonin piirtämisen. Ennen New-napin käyttöä kannattaa jokainen polygoni siirtää verticeslaatikosta MATLAB:iin matriisiksi. ts. Alue voidaan luoda esimerkiksi seuraavasti: 1. Käynnistetään työkalu komennolla polydraw. 2. Piirretään alueen ulkoreuna positiivisesti suunnistettuna murtoviivavana. Ts. jätämme piirtämättä viivan polygonin viimeisen ja ensimmäisen kärjen väliltä. 3. Valitaan vertices-laatikon sisältö ja kopioidaan se leikepöydälle painamalla CTRL+Insert. 43