“De Vliegende Hollander” - Een zelfstandig varende robot-zeilboot Willem Melching 26 januari 2012
N.B. Klik op het plaatje voor een filmpje Willem Melching Valeriusstraat 202 1075GJ Amsterdam
[email protected] Het 4e gymnasium Amsterdam Begeleider: Sven Aerts
English summary
Purpose Oceanographic research requires large, polluting and expensive ships. The goal of my research was to develop a cheaper, less polluting alternative to these research ships. I have designed and built an autonomous control system that transforms a sailing vessel into a sailing robot. Since robot ships can be smaller and do not require human operators they are much cheaper. They use the wind as their main source of energy and therefore they can be selfsupporting for a very long time. The sailing robot should be capable of following a pre-defined route, and should react to its environment, like the wind and the waves. My research was threefold. First, to investigate the minimal requirements for a fully functioning autonomous sailing robot. Second, to build this ship and write the necessary software. Third, to test whether this ship is fit for the purpose it was designed for: independent sailing. In my project I have combined theoretical and practical insights from different scientific fields such as electrical engineering, signal processing, control theory, informatics and last but not least: sailing.
Method To be autonomous, the sailing ship must at all times have information about three essential variables: wind direction, position and heading. This information is supplied by sensors and is processed in the on-board computer. This computer controls through actuators - the rudder and the sails of the ship. The software translates the information from the sensors into commands that control the vessel. 1) To control the position of the sails, the on-board computer must know the wind direction. The wind direction is determined by means of a wind vane. Every wind direction corresponds to an ideal position of the sails [1]. This position is transferred to the sail actuator. Because a sailing ship cannot sail against the wind, the information about the wind direction is also required for planning the route. 2) To plan the route, the current position
of the boat on earth is required. This information comes from a Skylab SKM53 GPS module. The on-board computer uses this information to calculate the path, a rhumb line, to the next waypoint of the current predefined route. In case the boat needs to sail too close to the wind, the route will not be a straight line but the boat will follow a zigzag pattern. Sailors call this beating. To accomplish this, I investigated several algorithms that calculate a beating pattern [2]. Then I developed my own version of such an algorithm, to make it suitable for implementation on my low-end hardware and improve the efficiency of the trajectory. 3) To keep the boat on course the onboard computer needs to know the current heading of the boat. The current heading is measured by a HMC6343 tilt compensated digital compass. The information from the compass is filtered by a 5 pole Butterworth low pass filter to reduce unwanted oscillations that originate from fluctuations in the compass reading. Then this signal is fed into a PID controller that calculates a new rudder position. All information from the sensors is combined in the on-board computer, an ARM lpc1768 development board, the mbed. Ten times per second the on-board computer processes this information and uses the predefined route to calculate a new rudder and sail position. Since there is no code available for sailing robots, I created a C++ software from scratch to run on the mbed microcontroller.
Results The result of my experiment is a ship that can sail autonomously and make decisions on its own. My tests show that under normal conditions (light to medium wind) the boat was able to complete any given route. All necessary intelligence is located on-board and therefore it can function without any contact with the shore. This makes it a true autonomous sailing robot. I succeeded in creating a proof of concept of an autonomous sailing vessel for only e350. Since the boat lacks solar cells, it is not completely self-sufficient yet. An improved version could serve as an alternative to full size research ships and could costs less than e600 (<$785) only.
Inhoudsopgave 1 Inleiding
1
2 Nut
3
3 Zeilen 3.1 De voornaamste onderdelen . . 3.1.1 De zeilen . . . . . . . . 3.1.2 Schoten . . . . . . . . . 3.1.3 Mast . . . . . . . . . . . 3.1.4 Zwaard, kiel en roer . . 3.2 De windrichtingen . . . . . . . 3.2.1 Voor de wind . . . . . . 3.2.2 Ruime wind . . . . . . . 3.2.3 Halve wind . . . . . . . 3.2.4 Aan de wind . . . . . . 3.2.5 In de wind & opkruisen
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
4 Automatisering en de kernbegrippen 4.1 Regeltechniek - de PID . . . . . . . . 4.2 PID regeling . . . . . . . . . . . . . . 4.2.1 P deel . . . . . . . . . . . . . 4.2.2 I deel . . . . . . . . . . . . . 4.2.3 D deel . . . . . . . . . . . . . 4.2.4 Combineren . . . . . . . . . . 4.3 Polar . . . . . . . . . . . . . . . . . . 4.4 Grootcirkel en loxodroom . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
4 4 4 4 5 5 6 6 6 7 7 8
van het robotzeilen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
9 10 11 12 12 13 13 15 16
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
5 Robotzeilen: hardware en software 5.1 De boordcomputer: multitasking en effici¨entie . . . . . . . . . . 5.2 PID regeling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5.3 Loxodroom . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5.4 Kompas: de koersbepaling . . . . . . . . . . . . . . . . . . . . . 5.5 Filtering . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5.6 GPS: de positiebepaling . . . . . . . . . . . . . . . . . . . . . . 5.7 Servo’s aansturen: het roer en de zeilen . . . . . . . . . . . . . 5.8 Communicatie tussen de laptop en de boordcomputer: Sailcom 5.9 PC Interface . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5.9.1 Hoofdvenster . . . . . . . . . . . . . . . . . . . . . . . . 5.9.2 Roer & zeil . . . . . . . . . . . . . . . . . . . . . . . . . 5.9.3 Kompas & vaantje . . . . . . . . . . . . . . . . . . . . . 5.9.4 PID . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5.9.5 Route . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . .
. . . . . . . . . . . . . .
. . . . . . . . . . . . . .
. . . . . . . . . . . . . .
. . . . . . . . . . . . . .
. . . . . . . . . . . . . .
. . . . . . . . . . . . . .
. . . . . . . . . . . . . .
. . . . . . . . . . . . . .
. . . . . . . . . . . . . .
17 17 17 19 20 21 22 23 23 23 24 25 26 27 28
6 Route plannen 6.1 Opkruisen . . . . . . . . . . . . . . . . 6.1.1 Wat is opkruisen? . . . . . . . 6.1.2 Algoritme van Stelzer en Pr¨oll 6.1.3 Mijn algoritme . . . . . . . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
29 29 29 30 31
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
7 Hardware: sensoren en actuatoren 7.1 Boot . . . . . . . . . . . . . . . . . . . . . . 7.1.1 Vorige pogingen . . . . . . . . . . . 7.1.2 Micromagic . . . . . . . . . . . . . . 7.2 Sensoren . . . . . . . . . . . . . . . . . . . . 7.2.1 GPS . . . . . . . . . . . . . . . . . . 7.2.2 Kompas . . . . . . . . . . . . . . . . 7.2.3 Vaantje . . . . . . . . . . . . . . . . 7.3 Actuatoren . . . . . . . . . . . . . . . . . . 7.3.1 Servo . . . . . . . . . . . . . . . . . 7.4 Elektronica . . . . . . . . . . . . . . . . . . 7.4.1 ARM microcontroller . . . . . . . . 7.4.2 Draadloze communicatie met de wal 7.5 Schema . . . . . . . . . . . . . . . . . . . . 8 Resultaten en evaluatie 8.1 Algemene indruk . . . 8.2 Hardware . . . . . . . 8.3 PID regelaar . . . . . 8.4 Route plannen . . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . . . . . . . . . . .
. . . .
. . . . . . . . . . . . .
. . . .
. . . . . . . . . . . . .
. . . .
. . . . . . . . . . . . .
. . . .
. . . . . . . . . . . . .
. . . .
. . . . . . . . . . . . .
. . . .
. . . . . . . . . . . . .
. . . .
. . . . . . . . . . . . .
. . . .
. . . . . . . . . . . . .
. . . .
. . . . . . . . . . . . .
. . . .
. . . . . . . . . . . . .
. . . .
. . . . . . . . . . . . .
. . . .
. . . . . . . . . . . . .
. . . .
. . . . . . . . . . . . .
. . . .
. . . . . . . . . . . . .
. . . .
. . . . . . . . . . . . .
. . . .
. . . . . . . . . . . . .
. . . .
. . . . . . . . . . . . .
. . . .
. . . . . . . . . . . . .
. . . .
. . . . . . . . . . . . .
. . . .
. . . . . . . . . . . . .
34 34 34 34 36 36 36 36 38 38 39 39 39 40
. . . .
41 41 41 43 43
9 Discussie
45
10 Conclusie
46
11 Appendix A: Foto’s
49
12 Appendix B: De Programmacode van de boordcomputer
54
Dankwoord Voordat ik begin wil ik een aantal mensen bedanken. Zonder hen was dit profielwerkstuk nooit geslaagd. Als eerste wil ik de school bedanken voor het profielwerkstuk-fonds. Met dit geld kon ik de dure onderdelen van mijn boot aanschaffen. Ook wil ik Pieter Adriaans bedanken, bedenker van de RoboSail, hij heeft mij ook veel adviezen gegeven over hoe ik dingen aan moest pakken. Verder wil nog aan aantal andere mensen bedanken: Estevan Veenstra als luisterend oor, Robert Rosen Jacobson, minitransat solozeiler en overbuurman, van wie ik het modelbootje gekocht heb. De MicroMagic vereniging en zijn leden hebben mij bij hun evenement gastvrij ontvangen en veel tips gegeven. Tot slot wil ik Sven Aerts, mijn begeleider, bedanken. Hij was mijn steun en toeverlaat bij het bouwen en testen van de boot, en het schrijven van het werkstuk. Hij heeft mijn werkstuk talloze keren nagekeken, heeft geholpen met het oplossen van problemen en heeft mij met eindeloos geduld dingen uitgelegd.
1
Inleiding
Iedereen kent het verhaal van de “Vliegende Hollander”, het spookschip dat zonder bemanning eeuwig over de oceanen bleef varen. Een automatisch varende zeilboot spreekt tot de verbeelding van elke kapitein. Stel je voor dat de VOC schepen vanzelf naar Indonesi¨e hadden kunnen varen en de bemanning niet maanden op gevaarlijke en onhygi¨enische schepen had hoeven doorbrengen. Maar ook tegenwoordig zouden zelfstandig zeilende boten goed van pas komen. De eerste vormen van zelfstandige besturing waren heel simpel, men zette dan gewoon het roer vast zodat er een paar uurtjes geslapen kon worden. Het echt automatisch sturen is pas iets van de afgelopen honderd jaar, omdat toen pas de machines en materialen om de benodigde mechanica te maken beschikbaar waren[3]. De eerste stuurautomaat was een mechanisch systeem dat de kracht van de wind gebruikte om het roer te bewegen. Deze stuurautomaat volgde de wind, dus als de wind draaide, dan draaide de boot mee. Pas na de snelle ontwikkelingen in de microelektronica was het mogelijk om een vaste koers te volgen. Vanaf toen waren deze regelaars elektronisch en dreven met behulp van een motor het roer aan. Deze stuurautomaten gebruikten echter geen slimme regelingen of algoritmes, dus raakten ze bij wat zwaarder weer al gauw van slag. Bovendien gingen ze ook nog uiterst kwistig met elektriciteit om, en dat is aan boord een schaars goed. Moderne stuurautomaten zijn door verdere ontwikkelingen in de automatiseringstechniek, regeltechniek en elektronica al veel beter geworden. Ze gebruiken bijvoorbeeld een GPS om zelf de koers naar een ingesteld punt uit te rekenen. Ondanks al deze vooruitgang is er nog steeds een kapitein vereist. Mijn profielwerkstuk bestaat dan ook uit het bouwen van een boot die zonder kapitein van A naar B kan zeilen, en desgewenst via C weer terug. Het idee voor dit profielwerkstuk ontstond eigenlijk toen ik een bootje bij ons in de kelder vond. Dit bootje is daar minstens dertig jaar geleden door de vorige bewoners achtergelaten. Het bootje heeft daar gestaan sinds mijn ouders en ik in dit huis wonen, maar er is nooit wat mee gedaan. Dus na dertig jaar heb ik het bootje uit de kelder gehaald en ontdaan van een dikke laag stof. Wat te voorschijn kwam was een houten rompje van een modelboot van circa 50 cm lang. Bij nadere inspectie bleek er onder het kajuitdakje een naam te staan. Hier stond dat de naam van het bouwpakket “Astrid” was. Na wat googelen bleek dat de Astrid een bouwpakket was van rond 1970 van de firma Suls uit Lunteren. Deze firma bestaat echter niet meer. De boot had de tand des tijds redelijk doorstaan, maar dat wilde natuurlijk niet zeggen dat het bootje vaarklaar was. Verre van dat zelfs, het was zo lek als een mandje, het roer zat muurvast en er ontbraken nog een mast en zeilen. Dit was het beginpunt van mijn profielwerkstuk. Mijn idee was een modelboot met robotbesturing te maken. Daarvoor heb ik zowel de hardware als de software zelf gebouwd en geprogrammeerd. De hardware bestaat uit een samenspel van sensoren, zoals een kompas en een gps, en een aantal servomotoren die de zeilen en het roer bedienen. De software bestaat uit een boordcomputer die aan boord van het schip informatie verwerkt en commando’s uitvoert. Vanaf de wal kan de boot voorzien worden van een opdracht. Om deze opdrachten te geven en informatie van de boot te ontvangen heb ik een grafische bedieningsomgeving geprogrammeerd en een communicatie protocol bedacht. Zo kon ik al mijn interesses combineren: zeilen, knutselen, programmeren, elektronica, natuurkunde en wiskunde. Robotzeilen is een ingewikkeld computerprobleem omdat zeilen een complexe beweging is waar enorm veel variabelen een rol spelen. Bij mijn literatuuronderzoek en de praktijktesten ontdekte ik al snel dat er geen behoorlijke methode bestond om een boot slim en effici¨ent tegen de wind in te laten zeilen. Laveren - zig zag tegen de wind in varen - is een van de ingewikkeldste
1
onderdelen van het zeilen. Er was een bestaand algoritme, maar ik heb een eigen algoritme geprogrammeerd wat meer geschikter was voor mijn boordcomputer. Dat maakt mijn werkstuk ook in theoretisch opzicht innovatief. Ik ben niet de eerste die zich op dit probleem gestort heeft. De eerste was Pieter Adriaans. Een pionier op het gebied van robotzeilen[4]. Hij wilde echter geen volledig autonome robot bouwen maar een ondersteuning voor solozeilers. Ik heb ook een gesprek met hem gehad, waarin ik de details van zijn boot met hem besproken heb. Daarnaast zijn er nog een aantal andere mensen bezig geweest. Bijvoorbeeld Stelzer en Pr¨oll die een systeem bedacht hebben om op te kruisen[2]. Maar ook Colin Sauz´e en Mark Neal[5] zijn bezig geweest met het maken van een dergelijke robot. Ik maak echter een compleet systeem dat alle benodigde technieken en theorie¨en combineert. Voor een goed begrip: ik heb geen boot met een afstandsbediening gemaakt, maar een volledig zelfstandig varende boot. Mijn boot is een eigentijdse versie van de Vliegende Hollander, zolang de boordcomputer en de servo’s stroom heeft kan hij over de oceanen blijven varen.
2
2
Nut
Niet alleen solo-zeilers zullen heel blij zijn met een extra elektronisch bemanningslid die hun bijna al het werk uit handen neemt als ze willen gaan slapen. Ook voor wetenschappelijk onderzoek zijn zelfstandig bestuurde zeilboten handig. Niet alleen vanwege de verminderde kosten, maar ook vanwege een nieuwe methode van gegevens verzamelen die de komende jaren waarschijnlijk steeds meer toegepast gaat worden. De belangrijkste kostenbesparing zit in het bootje en de afwezigheid van personeel die het bootje moet besturen. Een autonoom modelbootje kost in massaproductie zeer weinig en het kost geen manuren om zo’n bootje op zijn bestemming te krijgen, dit in tegenstelling tot de kosten van een complete wetenschappelijke expeditie. Zo kan er flink bespaard worden bij regulier onderzoek. Bovendien cree¨ert dit ook nieuwe mogelijkheden voor onderzoekers (zoals studenten en scholieren) met een klein budget[6]. Ook is er een verandering aan het plaatsvinden in de manier waarop data worden verzameld. De TU-Delft heeft bijvoorbeeld met hun Delfi-n3Xt project kleine goedkope satellieten gemaakt. Het doel daarvan is om een grote satelliet door een hele wolk van kleintjes te vervangen. Doordat deze kleine satellieten samenwerken kunnen ze beter data verzamelen, en als er eentje stuk gaat kan zijn taak makkelijk door anderen worden overgenomen. Als zo bijvoorbeeld een telescoop gevormd wordt, is de resolutie door de onderlinge afstand ook nog eens veel hoger dan met conventionele satellieten mogelijk zou zijn. Bovendien zijn deze kleine satellieten veel goedkoper waardoor de financi¨ele drempel om onderzoek in de ruimte te doen aanzienlijk wordt verlaagd[7][8]. Hetzelfde principe kan natuurlijk ook op bootjes toegepast worden. Stel dat je iets op verschillende plekken op aarde wil meten, dan stuur je een groot aantal bootjes, bijvoorbeeld duizend stuks, met de juiste sensoren de oceaan op. Je kan dan in korte tijd een groot aantal precieze metingen doen op veel verschillende plekken tegelijk.
3
3
Zeilen
Zeilen lijkt simpel. Iedereen die op de fiets wel eens wind mee heeft gehad, heeft gezeild. De wind duwt je voort en verder hoef je niet veel te doen. Tenminste, zo lang je wind mee hebt. Zeilen is een van de meest complexe manieren van voortbewegen. Want een zeilboot kan meer dan met wind mee voortdrijven. Al een paar duizend jaar proberen zeilers en ontwerpers zo slim mogelijk gebruik te maken van de wind. Ze kunnen ook goed overweg met wind van opzij. Recht tegen de wind in varen, dat lukt niet, maar met een kleine hoek ten opzichte van de wind lukt het om bijna tegen de wind in varen. In de loop der eeuwen is de boot van een simpel vlot met een doek om wat wind te vangen, ge¨evolueerd tot een effici¨ente racemachine. Zoals waarschijnlijk bekend houden zeilers er van om alles aan boord met speciale termen aan te duiden. Deze termen komen in de rest van mijn werkstuk ook voor, dus ik zal ze een voor een doornemen.
3.1
De voornaamste onderdelen
Een moderne boot is een complex stuk techniek. Daarom zal ik eerst de belangrijkste onderdelen van de boot bespreken en aangeven welke onderdelen in mijn robot een rol spelen. Daarna zal ik de verschillende hoeken waaronder een boot kan varen doornemen en aangeven waar bij mijn robotzeilboot de grote problemen zaten. 3.1.1
De zeilen
Een zeilboot heeft een zeil. Meestal zelfs twee (of meer) zeilen: een voorzeil en een hoofdzeil. Het voorzeil noemen we de ‘fok’, en het hoofdzeil het ‘grootzeil’. Het grootzeil zit vast aan de mast. Tussen de top van de mast en de punt van de boot loopt een draad. Zeilers noemen dit het ‘voorstag’. Het voorzeil zit aan zijn voorkant vast aan dit stag, de onderkant en de achterkant kunnen vrij bewegen. Het grootzeil zit met zijn voorkant, de lange rechte kant, aan de mast vast. De onderkant van het grootzeil zit vast aan een lange buis, deze buis heet de‘giek’. De giek zorgt dat het grootzeil min of meer op zijn plaats blijft zitten. De achterkant van het grootzeil - de langste en schuine kant - is open. 3.1.2
Schoten
Aan los wapperende zeilen heb je niet veel, die vangen geen wind. Daarom worden de zeilen aangetrokken door touwen. Maar aan boord heet dat natuurlijk geen ‘touw’. Zeilen trek je aan met een ‘schoot’. Deze schoten zitten aan de punt van de fok en onder aan de giek. Daarmee kun je de zeilen strakker of losser zetten. Afhankelijk van de wind trek je de zeilen strakker aan of laat je ze juist vieren. Zeilen zijn zo gesneden dat ze een profiel vormen wanneer je ze met de schoten aantrekt. Dat profiel is in principe vergelijkbaar met het profiel van de vleugel van een vliegtuig. Pas wanneer het zeil een mooi profiel vormt heb je ‘lift’. De boot stijgt niet op zoals een vliegtuig, maar zal wel vooruitgaan door het water. De kracht die er bij een vliegtuig voor zorgt dat hij in de lucht blijft, oefent hier een kracht uit die haaks op het zeil staat. De stand van de zeilen is dus van essentieel belang. Mijn robotzeilboot heeft ook een fok en een grootzeil. De schoten worden bediend met servomotoren en via de software worden de zeilen strakker of losser gezet. Daarbij probeert het
4
programma natuurlijk een optimale snelheid te behalen. Anders dan veel mensen denken gaat een boot die schuin vaart niet sneller. Integendeel, een boot moet bijna recht op varen. 3.1.3
Mast
Een zeilboot heeft altijd een mast. De mast zit vast, en blijft netjes overeind omdat hij op zijn plaats wordt gehouden door sterke kabels. Deze kabels heten de verstaging, je kunt daarmee de mast niet alleen vastzetten maar ook een beetje buigen of juist losser zetten. Op wedstrijdboten wordt telkens de stand van de mast veranderd. Ik heb deze factor bij mijn boot buiten beschouwing gelaten. 3.1.4
Zwaard, kiel en roer
Wanneer een fietser harde wind van opzij heeft, dan wordt de fietser opzij geduwd. De banden zorgen er echter voor dat je niet naar de weg af glijdt. De wind duwt op jouw en je fiets, terwijl de onderkant op zijn plaatst blijft. Dit moment zorgt er voor dat je schuin gaat. Je snelheid zorgt er echter voor dat je toch redelijk rechtop blijft en vooruit gaat. Een boot wordt bij zijwind ook opzij geduwd. Maar in de loop der eeuwen zijn daar wel remedies voor bedacht. Ouderwetse boten zoals platbodems hebben twee grote planken, zwaarden, aan de zijkant van de boot. Zo heeft een moderne boot midden onder zijn romp een uitstekende ‘plank’ (zie figuur 1). Deze plank noemen we zwaard of kiel. Deze plank is geplaatst in de vaarrichting, hierdoor ondervindt een boot nauwelijks weerstand als hij rechtuit gaat. Als de boot echter door de wind weggezet wordt, zorgt het oppervlakte van de plaat er voor dat de boot op zijn plek Figuur 1: Een roer en een kiel bij een modelboot blijft. Net als bij de fiets zorgt dit voor een moment waardoor de boot schuin gaat. Om de boot toch recht te houden kan er iemand op de rand van de boot gaan zitten, maar als dat niet mogelijk is (bijvoorbeeld als de krachten te groot zijn) kan er een gewicht aan het zwaard gehangen worden. Een zwaard met een gewicht er aan heet een kiel. Ook het besturen van de boot gebeurt door middel van een langwerpig en dun voorwerp onder water. Dit voorwerp zorgt er voor dat de stroming van het water onder de boot geregeld kan worden. Hierdoor kan de boot van richting veranderen. Moderne boten hebben achteraan de boot een roer. Met dit roer kun je de boot sturen, ook is het een goede manier om de boot tegen afdrijven bij zijwind te beschermen. Mijn boot heeft een kiel en een roer. Het roer kan ik met servomotoren aansturen. Dit is een essentieel onderdeel om de boot op koers te houden en natuurlijk zo effici¨ent mogelijk gebruik te maken van de wind.
5
3.2
De windrichtingen
Zowel de koers als de wind zullen in de praktijk geregeld veranderen. Dat betekent dat de wind telkens uit een andere hoek de boot bereikt. Daarom moet de zeiler - al dan niet bewust voortdurend beslissingen nemen. Steeds als de wind op de koers van de boot verandert moet hij de stand van zeilen en roer aanpassen. Daarbij moet hij ook nog voortdurend zijn snelheid blijven controleren en ook nog in de gaten houden of hij zijn einddoel zal bereiken. Zeilen is zoals gezegd - een complexe handeling. 3.2.1
Voor de wind
Als je op de fiets wind mee hebt, dan komt de wind recht van achteren. In Figuur 2 is het bootje aan de onderkant van de cirkel. Zeilers noemen dat ‘voor de wind’. Het zeil moet dan helemaal los zodat het haaks op de boot (en de wind) staat. Op die manier vang je de maximale hoeveelheid wind. Omdat de fok daarbij ‘in de schaduw’ van het grootzeil kan terecht komen, wordt de fok meestal aan de tegenovergestelde kant gezet. Ondanks het spreekwoord ‘voor de wind gaan’ is dit niet de koers waarbij de grootste snelheid bereikt wordt. Ook is het een lastige koers om veilig te varen. Als de koers (of de wind) een beetje verandert kan de wind, die natuurlijk nooit precies van achteren komt, plotseling aan de andere kant van de boot gaan waaien. Het grootzeil gaat dan vanzelf naar de andere kant, en dit gebeurt met een enorme snelheid. Dat is gevaarlijk Figuur 2: Verschillende koersen en en kan bemanningsleden verwonden en zelfs de mast zeilstanden - de wind komt van links doen breken. Deze onverwachte manoeuvre heet een klapgijp. Gelukkig is een klapgijp voor mijn schaalmodel geen probleem omdat alle onderdelen relatief licht zijn. 3.2.2
Ruime wind
Het zeilt veel prettiger als de wind niet precies recht van achteren komt, maar een beetje schuin van achter. Deze windrichting heet ‘ruime wind’. Je draait de boot dan een beetje naar de de wind toe. Door dit ‘oploeven’ zoals zeilers zeggen - komt de wind schuin van achteren. De wind valt in met ongeveer 135 graden. Bij ruime wind staat je zeil iets strakker, maar nog steeds vrij open op de wind. De snelheid zal hoger liggen dan bij voor de wind. Door de grote hoek van Figuur 3: De krachten die op het zeil inval ligt de boot vrij plat op het water. Het is daardoor een snelle en comfortabele koers. werken
6
3.2.3
Halve wind
Als je nog verder naar de wind toe stuurt oploeft - staat de wind haaks op de boot, dit heet ‘halve wind’. Bij halve wind staat je zeil in een hoek van ongeveer 45 graden op je boot. Je vraagt je natuurlijk af hoe een boot vooruit kan gaan als de wind van de zijkant komt. Vroeger kon dit ook niet. Zo moesten de schepen van de VOC wachten tot de wind gunstig was en ze naar het zuiden konden varen. Tegenwoordig is het mogelijk om wel met halve wind te varen. De wind die in het zeil komt zorgt voor een kracht die haaks op het zeil staat, schuin naar voren dus. Deze kracht is te ontbinden in twee losse krachten, een kracht naar voren en een kracht naar opzij. De kracht naar voren - dankzij het profiel in het zeil - zorgt voor snelheid. De kracht naar opzij zou de boot uit koers duwen, maar dankzij de kiel onder de boot blijft de boot min of meer op koers. De kiel zorgt voor een enorme waterweerstand als de boot naar opzij probeert te gaan. Het nadeel hieraan is wel dat de boot als het ware over de kiel heen valt en de boot scheef zal gaan. Om omslaan tegen te gaan en het schuingaan te beperken is er een gewicht onderin de kiel geplaatst. 3.2.4
Aan de wind
Als je nog verder naar de wind toestuurt, dan komt de wind schuin van voren, deze koers heet ‘aan de wind’. De hoek van inval is tussen de 40 en 25 graden. Bij aan de wind vaar je bijna tegen de wind in, en is de kracht in het zeil bijna geheel naar de zijkant gericht en nog maar voor een klein deel naar voren. De snelheid is dus relatief laag, maar toch is het Figuur 4: De krachten op het zeil bij verschilmogelijk om naar de wind toe te zeilen, iets lende koersen wat op het eerste gezicht onmogelijk lijkt. Bij aan de wind zet je je zeil zo strak mogelijk, dus bijna boven het midden van de boot.
7
3.2.5
In de wind & opkruisen
Verder oploeven kan niet, je zou dan ‘in de wind’ komen te liggen. De wind komt dan recht van voren en je komt niet meer vooruit. Punten die in de wind liggen zijn dus niet ‘bezeild’. Toch is het mogelijk om deze punten te bereiken, daar is een speciale manoeuvre voor, namelijk het opkruisen oftewel laveren. Bij opkruisen vaar je niet rechtstreeks naar het doel, maar vaar je zo scherp mogelijk aan de wind. Als je een tijdje over de ene boeg gevaren hebt, dan gooi je bij een flinke snelheid het roer een heel eind om. De punt van de boot draait naar de wind toe, maar dank zij de snelheid ga je ‘door de wind heen’ en je vangt weer wind. Maar nu van de andere kant. Je vaart dan zo scherp mogelijk over de andere boeg verder. Bij ‘door de wind gaan’ ga je dus door de wind heen om vervolgens over de andere boeg Figuur 5: De route bij het opkruisen te varen. de complete manoeuvre heet ‘overstag’ gaan. Op deze manier ga je zijwaarts heen en weer, maar kom je wel steeds dichter bij je doel. De snelheid is redelijk hoog, maar omdat je ‘zig-zaggend’ op je doel afgaat (zie figuur 5) wordt de afstand groter en ben je uiteindelijk relatief lang onderweg. Het juiste moment van overstag gaan om het traject zo effici¨ent mogelijk af te leggen is een kwestie van inschatting van de stuurman, ervaring en locale omstandigheden. Op een lang traject zul je minder vaak overstag gaan dan op een korte baan.
8
4
Automatisering en de kernbegrippen van het robotzeilen
Is zeilen dan zo ingewikkeld dat je er geavanceerde computers voor nodig hebt? Ja, want zeilen bestaat namelijk niet alleen uit het varen van een rechte lijn. Een zeilboot kan bijvoorbeeld niet tegen de wind in varen, om toch bij de bestemming aan te komen moet er een route worden gepland die wel helemaal te varen is en toch zo effici¨ent mogelijk naar het doel gaat (zie par. 6.1). Een zeilrobot heeft dus zeer uiteenlopende taken. Om de stabiliteit van een robot te bevorderen is het belangrijk om de verschillende onderdelen goed te scheiden. Elk onderdeel opereert op zichzelf en geeft zijn resultaten door aan andere onderdelen. Systemen die hoger in de hi¨erarchie staan gebruiken dan de resultaten van onderdelen lager in de hi¨erarchie[9]. Een robot bouwen die goed op zijn omgeving reageert is een zeer complexe taak. Stel dat je een boot op koers wil houden. Dat is lastig omdat er veel factoren zijn die kracht op de boot uitoefenen en waardoor hij dus van koers raakt. We kunnen deze factoren opsplitsen in overzichtelijke en onoverzichtelijk factoren. De overzichtelijke factoren zijn makkelijk te meten of te voorspellen. De voornaamste zijn: • Koers De koers is vrij precies te meten met een elektronisch kompas. Dit kan op een hoge frequentie (10 keer per seconde) en ook nog eens met een grote nauwkeurigheid. • Positie De positie is te meten met een GPS. De meetfrequentie van een GPS is al lager (1 keer per seconde), en de nauwkeurigheid is slechts drie meter[10]. Toch is dit nog ruim voldoende voor zeilers. • Windrichting De windrichting is ook belangrijk, en goed te meten met een windvaan. De benodigde sensor is alleen vrij lomp, waardoor ik hem op de kant moest zet in plaats van op het bootje. Ook zijn er een aantal onoverzichtelijke factoren, deze zijn moeilijk te meten, of zelfs onvoorspelbaar. Dat zijn: • Wind De windkracht is een belangrijk iets om te weten, want bij een sterkere wind moet er anders gestuurd worden. De windkracht is wel te meten, maar het grootste probleem zijn de vlagen. Deze zijn niet te voorspellen, en pas te meten als het eigenlijk al te laat is. • Golven De golven zijn net als de wind moeilijk te voorspellen, en toch een belangrijke factor om rekening me te houden bij het zeilen. Een schipper ziet een golf aankomen en kan hier tijdig op reageren, een robot kan pas reageren als het al te laat is. Om een robot automatisch te laten zeilen moet deze een aantal taken zelfstandig uitvoeren. Hierbij kwamen allerlei problemen naar voren. Voor elk van deze problemen moest ik een oplossing bedenken. Ik zal in deze paragraaf elke oplossing kort toelichten en bij sommige een wiskundige onderbouwing geven. Ook zal ik de algoritmes bespreken die ik ge¨ımplementeerd heb om de boot zijn taak te laten volbrengen. Het zelfstandig plannen en varen van een route vergt een ingewikkeld samenspel van een aantal elementen. Om goed te begrijpen hoe al deze elementen samenwerken en elkaar be¨ınvloeden zal ik de verschillende concepten apart uitleggen. Uitrekenen hoe een boot een route moet varen is niet van te voren uit te rekenen. Je kan niet zeggen: “Vaar 200 meter rechtdoor en ga dan rechtsaf.”. Er hoeft maar ´e´en golf of windvlaag te komen en je komt nooit meer op je bestemming aan. Om een boot op koers te houden moet de omgeving constant in de gaten gehouden worden en hier moet tijdens het varen op gereageerd worden. 9
PID-regeling Om de boot op koers te houden moet er een regeling zijn die continue het roer bijstelt zodat de boot de juiste kant op vaart. Een oplossing uit de regeltechniek is de zogenaamde proportionele, integrerende en different¨ıerende regelaar, afgekort de PID regelaar. Deze gebruikt de afwijking ten opzichte van de juiste koers om het roer in te stellen. Opkruisen Ook een belangrijke vraag is wat de ideale koers is als de te zeilen route in de wind ligt, oftewel niet bezeild is. Omdat je niet direct naar het doel kan varen moet je opkruisen. Doordat elke koers in dit patroon wel bezeild is kan de route wel gevaren worden. Maar de vraag is wat de ideale hoek ten opzichte van de wind is. Als je de hoek kleiner neemt hoef je minder afstand af te leggen, maar zeilt de boot langzamer. Als je de hoek vergroot gaat de boot sneller, maar moet je ook weer meer afstand afleggen. De oplossing hiervoor is om een grafiek te maken waarin je de snelheid ten opzichte van de wind uitzet tegen de snelheid. Uit deze grafiek kun je de beste koers aflezen[1]. Bij het opkruisen is het ook belangrijk wanneer er overstag gegaan wordt. Op papier zijn alle routes even lang, het maakt dus niet uit of je met twee slagen opkruist of met tien. In de praktijk blijkt het wel uit te maken. Een overstagmanoeuvre kost tijd en snelheid, waardoor het beter is om zo min mogelijk overstag te gaan. Maar dit heeft ook weer twee nadelen. Ten eerste wordt de benodigde ruimte voor het opkruisen groter omdat hij veel meer breedte in beslag neemt. Dit is niet handig als je in nauw vaarwater of in een vaargeul moet varen. Het tweede nadeel is wanneer na een hele tijd varen de wind plotseling zou draaien. Dan heb je een heel stuk voor niets gevaren en moet je feitelijk weer opnieuw beginnen. Het is dus zaak om de gulden middenweg te vinden. Hier heb ik in de software een mogelijkheid gemaakt om de breedte van de manoeuvre in te stellen, om zo experimenteel vast te stellen wat voor verschillende omstandigheden ideaal is. Koerslijn Er moet ook uitgerekend worden welke kant je op moet varen om in een rechte lijn naar een bepaald punt te varen. De kortste route heeft echter een nadeel, de koers is door de bolling van de de aarde niet gedurende de hele route constant. Dit is tamelijk een ingewikkeld probleem en komt in paragraaf 4.4 uitgebreid aan de orde.
4.1
Regeltechniek - de PID
Leonardo da Vinci bedacht al in de 15e eeuw al een regeling om de snelheid van wind en watermolens te regelen. Ongeveer een eeuw later, toen het mogelijk was om metaal te bewerken, ontwierp een Nederlander een temperatuurregeling die gebruik maakte van het uitzetten en inkrimpen van een metaal bij temperatuursveranderingen. De ontwikkelingen in de regeltechniek raakten in de 18e eeuw in een stroomversnelling. James Watt bouwde toen een regeling die met behulp van de zwaartekracht en de centrifugaalkracht de hoeveelheid stoom regelde die in een stoommachine gaat. Voor die tijd moest dat met de hand gebeuren[11]. Regelingen zijn echter pas gemeengoed geworden na 1960. Het Amerikaanse bedrijf Honeywell presenteerde toen een lijn van sensoren die druk en temperatuur konden meten. Deze sensoren gaven een luchtdruk af die proportioneel was met de gemeten waarde. Deze luchtdruk kon vervolgens gebruikt worden om mechanische of analoge elektronische regelaars aan te sturen. Niet veel later, in de jaren zeventig, kwamen ook digitale computergestuurde regelingen op de markt.
10
Figuur 6: Regeling op een stoommachine Alle regelaars, of ze nu mechanisch, analoog of digitaal zijn, werken volgens dezelfde basisprincipes. De regelaars hebben een input, afkomstig van een sensor. Op deze manier kunnen de regelaars de werkelijkheid of het ‘proces’ in de gaten houden. De input wordt ook wel ‘process variable’ genoemd. Ook heeft de regelaar een waarde om de input mee te vergelijken, deze heet het ‘setpoint’. De regelaar neemt vervolgens het verschil tussen de input en het setpoint, deze waarde is de afwijking of ‘error’. Vervolgens wordt deze afwijking vertaald in een output. Deze vertaalslag kan simpel zijn, maar ook gebeuren met ingewikkelde formules en algoritmes. Deze output stuurt het proces aan. De regelaar krijgt vervolgens via de input weer terugkoppeling of ‘feedback’ van zijn acties, en stelt zijn ouput weer bij als dat nodig is[12].
Figuur 7: Blokschema van een regelsysteem In de regelaar op het bootje zijn al deze elementen ook terug te vinden. De input is het kompas, die de richting meet waarin het bootje vaart. Het setpoint komt van de GPS, namelijk de koers die de boot moet varen om zijn doel te bereiken. Vervolgens wordt de error berekend en worden hier drie formules op losgelaten. De output van de regelaar wordt dan gebruikt om het roer te bedienen. Het roer zorgt er dan weer voor dat de koers van het bootje verandert. Op deze manier krijgt de regelaar via het kompas weer feedback van zijn acties.
4.2
PID regeling
Om de roerstand van het bootje te berekenen en voortdurend aan de nieuwe omstandigheden aan te passen maak ik gebruik van een proportionele, integrerende en differenti¨erende regeling, kortweg een PID regeling. De regelaar bestaat uit drie losse onderdelen die op het einde samengevoegd worden. De werking van deze drie onderdelen is het beste uit te leggen met een simpel voorbeeld. Als je ’s ochtends onder de douche stapt dan moet je de temperatuur van het water instellen. Om te beginnen draai je de kraan met koud water open om vervolgens met warm water de temperatuur te regelen. Eerst voel je temperatuur van het water, en vergelijk je deze met de gewenste temperatuur. Vervolgens schat je hoeveel je de warmwaterkraan in eerste instantie open moet draaien om de gewenste temperatuur te bereiken. Dit is het eerste deel van de regeling. Daarna warmt het water op en neemt de afwijking af. Dan komt het tweede deel van de regeling in actie, nu wordt over langere tijd bekeken hoeveel warm water er ongeveer nodig is 11
om de temperatuur zo goed mogelijk op de gewenste waarde te houden. Maar er is ook nog een derde deel nodig, dat ingrijpt in onverwachte situaties. Stel: iemand draait elders in huis de koud water kraan helemaal open, daardoor zou jouw douche plotseling veel te heet worden. Daarom moet er nu direct worden ingegrepen. Daarom kijkt het derde deel van de regeling hoe snel de temperatuur verandert. De PID-regeling bestaat altijd uit deze drie onderdelen die elk een deel van de uitgangswaarde van de regeling bepalen. Elk van de drie onderdelen berekent apart een uitgangswaarde aan de hand van de afwijking met de ingestelde koers. Daarna worden deze drie waarden met parameters vermenigvuldigd en opgeteld[13]. Voor mijn boot is de PID regelaar geschikt gemaakt voor het aanhouden van een koers en het reageren op onverwachte gebeurtenissen. 4.2.1
P deel
Het P of proportioneel deel is het deel van de uitgangswaarde dat recht evenredig is met de afwijking tot de ingestelde waarde. Dit is in formulevorm weer te geven als: P (t) = e(t)
(1)
Waarin e(t) de afwijking op tijdstip t is. Op het bootje kan de afwijking niet continue gemonitord worden, daarom wordt de afwijking op vaste tijdstippen bemonsterd. Daarom is er naast deze continue versie van het P deel ook een discrete variant nodig. Deze ziet er als volgt uit: P (ti ) = e(ti )
(2)
Het P deel is het belangrijkste deel van de regelaar. Het belangrijkste van een regeling is namelijk dat hij de afwijking met een ingestelde waarde elimineert. Daarom heeft het P deel een direct verband met de afwijking. Als er alleen een P deel toegepast zou worden zou de regelaar al redelijk werken, er treden dan alleen een aantal vervelende dingen op. Er ontstaan oscillaties en een constante afwijking wordt niet opgemerkt. 4.2.2
I deel
Het I of integrerend deel is het deel van de uitgangswaarde dat een maat is voor de cumulatieve afwijking. Dus hoe langer een afwijking aanwezig is hoe groter de waarde van het I deel. Wiskundig kan dit berekend worden door de integraal van de afwijking te nemen van het begin van de tocht tot het huidige moment. De formule van het I deel is: Z t I(t) = e(t) dt (3) 0
Ook hier is weer een discrete versie nodig om hem in het bootje te implementeren. De tijd tussen de verschillende metingen is ∆t. De discrete versie ziet er zo uit: I(ti ) =
i X
∆t · e(tk )
(4)
k=0
Dit deel is vooral bedoeld om krachten te compenseren die constant op de boot werken. Stel dat de stroom de boot continu naar rechts duwt, dan moet de boot voortdurend naar links bijsturen. In het begin - als de boot nog niet op koers ligt - zal de waarde van het I deel steeds blijven toenemen. Als de boot op koers ligt is het I deel precies groot genoeg om deze stroom
12
tegen te werken. Omdat de boot op koers ligt en de afwijking (bijna) 0 geworden is, verandert het I deel daarna niet meer. 4.2.3
D deel
Het D of differenti¨erend deel is het deel van de uitgangswaarde dat een maat is voor hoe snel de boot van koers verandert. Dit is te berekenen door de afgeleide te nemen van de afwijking, oftewel hoeveel de afwijking e de afgelopen t seconden veranderd is. In formulevorm ziet dat er zo uit: de(t) dt En de discrete versie zoals toegepast wordt in het bootje: D(t) =
D(ti ) =
(5)
e(ti ) − e(ti−1 ) ∆t
(6)
Het D deel zal vooral de effecten van golven en korte invloeden, zoals windvlagen, compenseren. Omdat dit soort invloeden vaak heftige en snelle bewegingen veroorzaken zal de uitgangswaarde van het D deel dan relatief hoog zijn. 4.2.4
Combineren
Het berekenen van de afzonderlijke delen is nog niet genoeg. Ze moeten ook in een bepaalde verhouding gecombineerd worden. Om dit te bewerkstelligen worden ze allemaal met een eigen parameter vermenigvuldigd en vervolgens opgeteld. De totale formule van een PID regelaar wordt dan:
u(ti ) = Kp ∗ P (ti ) + Ki ∗ I(ti ) + Kd ∗ D(ti ) u(ti ) = Kp · e(ti ) + Ki ·
i X
∆t · e(tk ) + Kd ·
k=0
e(ti ) − e(ti−1 ) ∆t
(7)
Dit afstellen van de PID regeling gebeurt door experimenteel de waardes van Kp , Ki en Kd te vinden waarbij de boot zich het beste gedraagt. Dit is vaak een lastig klusje omdat de factoren elkaar onderling be¨ınvloeden. Ook bij mijn robotzeiler was dit een flink karwei.
13
Figuur 8: Invloed van verschillende Kp ’s
Figuur 9: Invloed van verschillende Ki ’s
Figuur 10: Invloed van verschillende Kd ’s
14
De invloed van de parameters is goed te zien in de grafieken van figuur 8, 9 en 10. In elk plaatje wordt bijvoorbeeld de hoeveelheid P (de parameter Kp ) veranderd terwijl de andere parameters constant blijven. De blauwe lijn is de waarde die de regelaar probeert te bereiken, en de gekleurde lijnen de werkelijke toestand volgens de sensoren. Verschillen tussen de instellingen zijn: • Insteltijd De insteltijd is de tijd die nodig is om de uitgangswaarde de ingestelde waarde te laten bereiken. Zoals te zien is zorgt een lage I waarde voor een grote insteltijd. • Overshoot Overshoot is hoeveel de uitgangswaarde over de ingestelde waarde heen schiet voordat hij weer naar beneden geregeld wordt. Zoals een lage Ki waarde een lange insteltijd veroorzaakt, zorgt een hoge Ki voor een korte insteltijd, maar heeft last van veel overshoot. • Oscillatie Oscillatie is hoelang het duurt voordat de uitgangswaarde stabiel wordt. Bij veel instellingen gebeurt dit helemaal nooit en blijft het systeem instabiel.
4.3
Polar
Een boot zeilt niet op alle koersen even hard, dit komt door de vorm van de boot en de zeilen. Voor het plannen van de route is het handig om te weten onder welke hoeken de boot optimaal zeilt. Deze snelheden worden weergegeven in een poolgrafiek - ook wel polar genoemd - waarbij de hoek van de invallende wind ten opzichte van de vaarrichting wordt afgezet tegen de snelheid. Omdat deze waardes ook van de windkracht afhangen is het nodig om voor elke windkracht een eigen grafiek te maken. Als je al deze grafieken over elkaar plot en de linker helft weglaat (die is Figuur 11: Polar bijna gelijk aan de rechterhelft) krijg je een plaatje als in afbeelding 11. Uit een polargrafiek is veel informatie te halen, wij gebruiken de grafiek voornamelijk voor het bepalen van de ideale koers voor het aan de wind zeilen.
15
4.4
Grootcirkel en loxodroom
Er zijn verschillende manieren om de koers tussen twee punten op aarde uit te rekenen. Sommige complexer dan andere. De complexere formules houden rekening met de kromming van de aarde, terwijl andere dat niet doen. De kortste route tussen twee punten op een bol loopt namelijk niet recht. Deze kortste route heet een grote cirkel. Deze lijn heeft echter een belangrijk nadeel, de koers die de boot moet sturen is niet constant waardoor deze continu opnieuw moet worden berekend[14]. De lijn die g´e´en rekening houdt met de bolling van de aarde heet loxodroom of in het Engels een rhumb line1 . Bij deze lijn is de koers wel gedurende de hele route constant, maar de lengte van deze route is langer[15]. Zie afbeelding 13 voor het verschil Figuur 12: Constante richting bij een loxodroom tussen een grote cirkel route en een loxodroom. Ik heb gekozen om te varen over een loxodroom, dat is de route die een boot vaart als hij een constante kompaskoers aanhoudt. Dat is ook een rechte lijn op een kaart met een mercatorprojectie2 . Deze lijn is weliswaar niet de kortste route, maar op korte afstanden maakt dat niet heel veel uit. Om namelijk een grootcirkel (de kortste route) te varen moet het beginpunt worden onthouden en moet er steeds voor de huidige plek op de route een nieuwe koers worden uitgerekend. De formule om de loxodroom uit te rekenen is als volgt: ∆ϕ = ln(
tan( lat2 2 + π4 ) tan( lat2 1 + π4 )
)
q = cos(lat1 ) ∆lat = lat2 − lat1 ∆lat = lon2 − lon1 d=
p
∆lat2 + q 2 · ∆lon2 · 6371 km
(8)
θ = atan2(∆lon, ∆ϕ)
(9)
In deze formule is (lat1 ,lon1 ) het beginpunt en (lat2 ,lon2 ) het doel.
Figuur 13: Verschil tussen een grote cirkel en een loxodroom (rhumb line) 1
Loxodroom komt van het griekse λoχoσ (schuin) en δρoµoσ (lopen) De mercatorprojectie is een manier om de wereldbol op een plat vlak af te beelden. Het is bedacht door de Vlaamse Cartograaf Gerardus Mercator in 1569. De kaart komt tot stand door de wereldbol op een cilinder te projecteren. Gebieden die verder van de evenaar liggen worden dus groter afgebeeld. Omdat een route met een constante kompaskoers een rechte lijn is op een kaart met een mercatorprojectie wordt deze projectie in de scheepvaart veel gebruikt. 2
16
5
Robotzeilen: hardware en software
Voor het robotzeilen heb je hardware en software nodig. In de boot zit een boordcomputer die zijn eigen programma draait. Op de kant staat een laptop met daarin software om de boot in de gaten te houden. Deze laptop is g´e´en afstandsbediening, de boot vaart immers zelfstandig. Wel kunnen vanuit de laptop de parameters worden aangepast en een nieuwe route op worden gegeven. Verder is de boot voorzien van een aantal sensoren, zoals de GPS en het kompas. Ook kan de boot met servomotoren sturen en de zeilen aantrekken dan wel vieren. Op de kant staat ook een sensor: een windvaan. De communicatie tussen de boot en de laptop op de kant gaat via een zender- en ontvangersysteem. Aanvankelijk werkte mijn software via een opdrachtregel met ingewikkelde commando’s. Dat heb ik inmiddels vervangen door een grafische bedieningsinterface.
5.1
De boordcomputer: multitasking en effici¨ entie
Op de boot draait de software op een boordcomputer, deze software is in C++ geschreven om een zo effici¨ent mogelijk programma te krijgen. C++ staat relatief dicht op de machinetaal die ook daadwerkelijk uitgevoerd wordt. Dat kost meer tijd om te schrijven, maar daarmee heb je meer invloed op wat er precies onder de motorkap gebeurt. De C++ code wordt door een compiler omgezet in machinetaal, geschikt voor de boordcomputer. Het is belangrijk om te bedenken dat de boordcomputer geen besturingssysteem heeft, zoals een PC. Het voordeel daarvan is dat al je geheugen en rekenkracht beschikbaar zijn voor je programma waardoor je heel snel en effici¨ent de hardware kan aanspreken. Het nadeel is echter dat je niet meerdere programma’s tegelijkertijd kan draaien zoals op een PC. Eigenlijk kan een gewone PC ook niet meerdere dingen tegelijkertijd doen. Windows geeft elk programma steeds ´e´en voor ´e´en tijd. Dat gaat heel snel waardoor het lijkt alsof alles tegelijkertijd werkt. In mijn software doe ik dat eigenlijk ook. Het programma bestaat uit een eeuwige lus waarin steeds elk onderdeel een beetje tijd krijgt toebedeeld. Sommige stukjes programma kunnen echter voorrang krijgen als het belangrijk is dat er heel snel iets gedaan moet worden. Als er bijvoorbeeld een instructie van de wal ontvangen wordt, stopt het programma tijdelijk waar hij mee bezig was om de boodschap te verwerken.
5.2
PID regeling
De PID regeling heb ik eigenlijk ge¨ımplementeerd zoals formule 7. Ik doe de losse stukjes ´e´en voor ´e´en om ze dan op te tellen. De code ziet er dan als volgt uit: void Pid::update(void){ float p,i,d; Setpoint = sp->get(); Input = pv->get();
//variabeles declaren die later gebruikt gaan worden //SP ophalen //Koers ophalen
state["sp"] = Setpoint; //sp opslaan op een plek waar andere processen // dat ook kunnen zien if (state["roer_auto"]){ //als het roer autmoatisch bestuurd moet worden float error = verschil(Input,Setpoint); //afwijking uitrekenen 17
float schaal = state["schaal"]; state["error"] = error;
//Deelfactor voor de paramters uitlezen //afwijking opslaan
ITerm += ((state["ki"] / schaal ) * SampleTime * error); //I term uitrekenen //en optellen bij vorige I if(ITerm > 25) ITerm = 25; else if(ITerm < -25) ITerm = -25;
//Zorgen dat de I niet te groot wordt
float dInput = verschil(lastInput,Input);
//Afgeleide berekenen
p = (state["kp"] / schaal ) * (error + state["roll"] * 0.01 * state["pid_roll"]); i = ITerm; //I uitrekenen d = - ( (state["kd"] / schaal ) / SampleTime) * dInput; //D uitrekenen Output = p + i + d; //Optellen if(Output > outMax) //Zorgen dat de output niet te groot wordt Output = outMax; else if(Output < outMin) Output = outMin;
lastInput = Input;
state["p"] = p; state["i"] = i; state["d"] = d;
//Huidig setpoint opslaan voor later
//P,I,D waardes opslaan
output->set( (int)Output + 50); //Roer instellen } } De blauwe tekst achter “//” is toelichting, en heeft verder geen invloed op de code. In de code zitten een aantal beveiligingen en extra dingen ingebouwd die in de formule niet zitten. In totaal heb ik een viertal toevoegingen gemaakt, die de PID geschikt maken voor een boot[16]. De eerste toevoeging is de formule om de het kleinste verschil tussen twee koersen te berekenen. Normaal kun je het verschil tussen de huidige waarde en de ingestelde waarde berekenen door ze van elkaar af te trekken. Maar met koersen werkt dat niet. Het is namelijk mogelijk om door de 0 heen naar de 360 te gaan. Het verschil tussen 359 graden en 1 graad is slechts twee graden en niet 358 (359 − 1). De tweede toevoeging is een beveiliging dat de I term niet te groot wordt. Als de te varen koers plotseling verandert, bijvoorbeeld als er overstag gegaan moet worden, dan wordt de afwijking ineens ook veel groter. Deze afwijking wordt steeds maar opgeteld bij de I term, 18
daardoor zou deze term veel groter worden dan nodig is. In de tijd die de boot nodig heeft om de nieuwe koers te gaan varen is de I term al zo groot geworden dat het roer alleen nog maar maximale uitslag blijft gegeven, ook als de te varen koers reeds bereikt is. De beveiliging zorgt er voor dat de I term niet groter kan worden dan een bepaalde waarde. De derde toevoeging zit in de manier waarop de afgeleide berekend wordt. Je kan de afgeleide van de afwijking nemen, maar dan zou deze reageren op veranderingen van de koers en veranderingen in het setpoint. Het roer krijgt dan een zwiep als de ingestelde waarde verandert, en dat is niet de bedoeling. Daarom wordt de afgeleide van de koers genomen. De vierde en laatste toevoeging is een beveiliging die ervoor zorgt dat het roer nooit de opdracht kan krijgen om meer dan een vooraf ingesteld maximum te sturen, anders zou de servomotor het roer of de boot zelf kapot kunnen maken.
5.3
Loxodroom
Ook de loxodroom (Engels: rhumb line) is volgens formule 8 en 9 geprogrammeerd. Omdat de ingebouwde goniometrische functies van de boordcomputer in radialen rekenen, en de rest van het systeem in graden werkt, moeten de hoeken eerst omgerekend worden van graden naar radialen, en op het einde weer terug naar graden. double afstand(pos *cur, pos *tar){ double lat_cur_r = cur->lat * deg2rad; double lon_cur_r = cur->lon * deg2rad; double lat_tar_r = tar->lat * deg2rad; double lon_tar_r = tar->lon * deg2rad; double df = log(tan(lat_tar_r/2.0 + PI/4.0)/tan(lat_cur_r/2.0 + PI/4.0));
double dla = lat_tar_r - lat_cur_r; double dlo = lon_tar_r - lon_cur_r; double q; if (df == 0) q = cos(lat_cur_r); else q = dla / df;
double r = 6371.0;
double d = sqrt(pow(dla,2) + pow(q,2) * pow(dlo,2)) * r; return d;
} double koers(pos *cur, pos *tar){ double lat_cur_r = cur->lat * deg2rad; 19
double lon_cur_r = cur->lon * deg2rad; double lat_tar_r = tar->lat * deg2rad; double lon_tar_r = tar->lon * deg2rad; double df = log(tan(lat_tar_r/2.0 + PI/4.0)/tan(lat_cur_r/2.0 + PI/4.0));
double dla = lat_tar_r - lat_cur_r; double dlo = lon_tar_r - lon_cur_r;
double k = atan2(dlo,df) * rad2deg; if (k < 0) k += 360; return k;
}
5.4
Kompas: de koersbepaling
Het elektronische kompas is met een I2 C (Inter-Integrated circuit) bus aan de boordcomputer verbonden. Een uitgebreide beschrijving is voor onze doeleinden niet nodig. Wat belangrijk is om te weten is dat het een serieel protocol is waarbij elk van de “slaves” (het kompas in dit geval) een eigen adres heeft. Om een van de “slaves” uit te lezen, stuurt de “host” (de boordcomputer) een adres. Om het kompas aan te spreken is dit 50 (of 0x32 hexadecimaal). Hierdoor begint het kompas te luisteren. Daarna stuurt de host een opdracht, in dit geval 80 (0x50) wat “sample heading” betekent. Het kompasje gaat dan aan het werk om de koers te meten. Hier moet even op gewacht worden. Daarna kunnen er 6 waardes uitgelezen worden, deze waardes worden tot slot omgerekend in een werkbaar formaat[17]. void Kompas::update(void) { //wat variabeles om als zend en ontvang buffer te fungeren char compass_out[1]; char compass_in[6]; //als het kompas niet bezig met calibreren kan er gelezen worden if (cal == 0) { //zet 0x50(="sample heading") in zend buffer compass_out[0] = 0x50; //stuur deze buffer over de bus compass.write(ADDR,compass_out,1); //geef het kompas even de tijd om de opdracht uit te voeren wait_ms(1); //Daarna kunnen er 6 8bits waardes uitgelezen worden compass.read(ADDR,compass_in,6); 20
//Deze 6 8 bits waardes moeten omgerekend worden naar 3 16 bits waardes int compass_heading = (int)compass_in[0] << 8 | (int)compass_in[1]; int compass_pitch = (int)compass_in[2] << 8 | (int)compass_in[3]; int compass_roll = (int)compass_in[4] << 8 | (int)compass_in[5]; int n = compass_heading / 10;
int verschil if (verschil verschil if (verschil verschil
= n - prev; > 180) -= 360; < -180) += 360;
if (abs(verschil) > 60) { if (verschil > 0) prev += 20; else prev -= 20; } else { koers = n; }
} else koers =
0;
}
5.5
Filtering
Het signaal wat uit het kompas kwam bleek echter niet helemaal zuiver. Er zat nog een kleine hoeveelheid ruis op. Deze ruis zorgde voor kleine maar snelle veranderingen in het signaal. Het probleem hiervan was dat het differenti¨erende deel van de PID regelaar heel heftig op deze veranderingen reageerde. Daarom moest deze ruis worden ge¨elimineerd. Eerst heb ik geprobeerd een lopend gemiddelde te nemen over een aantal metingen. Dit werkte nog niet naar behoren. Ten eerste kwam er nog steeds te veel ruis door dit filter heen. Ten tweede zorgde het ook nog eens voor onacceptabele vertraging, hierdoor duurde het veel te lang voordat een verandering in de koers zichtbaar werd. Daarom ben ik op zoek gegaan naar een ander filter. Het gewone signaal heeft een vrij lage frequentie, de boot verandert nooit meer dan een paar keer per seconde van koers. De ongewenste fluctuaties van het kompas hebben een hogere frequentie. Daarom moet dit filter de lage frequenties doorlaten maar de hogere tegenhouden. Een geschikt filter voor dit doeleinde is een Butterworth laag doorlaat filter. Dit filter bestond oorspronkelijk uit weerstanden en condensatoren om hem in een analoge schakeling toe te passen. Later is ook de wiskundige basis van dit filter gebruikt om formules te vinden die door de computer gebruikt kunnen worden. Het te filteren signaal wordt dan in deze formules ingevoerd en dat levert dan een gefilterde versie van het signaal op.[18]
21
In deze formules zitten een aantal parameters die het gedrag van het filter bepalen. Deze parameters zijn afhankelijk van een aantal eigenschappen van het filter. De eerste eigenschap is de ‘cutoff’ frequentie, dit is de frequentie waarbij het filter signalen begint te verzwakken. De tweede eigenschap is hoevel ‘polen’ het filter heeft, dit bepaald hoe het filter zich gedraagt rond de cutoff frequentie. Als een filter veel polen heeft worden signalen die vlak boven de cutoff frequentie zitten nauwelijks meer doorgelaten, terwijl dit verloop vloeiender gaat voor een filet met minder Figuur 14: Het effect van het aantal polen op polen. In figuur 14 is het gedrag van het fil- het gedrag rond de cutoff frequentie ter te zien als het aantal polen verandert wordt. Op de horizontale as staat de frequentie en op de verticale as de verzwakking. Om de juiste parameters te berekenen heb ik gebruik gemaakt van een website[19], maar programma’s als matlab kunnen dit ook. Ik heb een filter gebruikt met 5 polen, een sample frequentie van 10Hz en een cutoff frequentie van 1Hz. Het filter is op de volgende manier ge¨ımplementeerd:
GAIN = 7.796778047e+02; xv[0] xv[5] yv[0] yv[5]
= xv[1]; xv[1] = xv[2]; xv[2] = xv[3]; xv[3] = xv[4]; xv[4] = xv[5]; = meting / GAIN; = yv[1]; yv[1] = yv[2]; yv[2] = yv[3]; yv[3] = yv[4]; yv[4] = yv[5]; = (xv[0] + xv[5]) + 5 * (xv[1] + xv[4]) + 10 * (xv[2] + xv[3]) + ( 0.1254306222 * yv[0]) + ( -0.8811300754 * yv[1]) + ( 2.5452528683 * yv[2]) + ( -3.8060181193 * yv[3]) + ( 2.9754221097 * yv[4]); koers = yv[5];
5.6
GPS: de positiebepaling
De GPS geeft de boot informatie over de positie, maar een GPS stuurt veel meer informatie dan ik nodig heb. Mijn programma haalt uit de stroom van data de benodigde informatie. Een voorbeeld van wat de GPS elke seconde stuurt: $GPGGA,075851.891,5611.5305,N,00302.0369,W,0,00,4.8,44.0,M,52.0,M,,0000*77 $GPGSA,A,1,,,,,,,,,,,,,4.8,4.8,0.7*37 $GPGSV,3,1,12,20,82,116,,01,79,246,,32,54,077,,17,48,254,*70 $GPGSV,3,2,12,23,46,168,,24,40,128,,04,25,295,,11,24,143,*73 $GPGSV,3,3,12,31,22,065,,13,15,190,,12,11,343,,25,00,019,*7D $GPRMC,075851.891,V,5611.5305,N,00302.0369,W,002.2,252.9,160411,,,N*68 $GPVTG,252.9,T,,M,002.2,N,004.1,K,N*0B Hierin staat alle informatie over de positie, de actuele tijd, de snelheid en nog veel meer. Zo vaart de boot uit dit voorbeeld om 7:58:51 UTC op 56 graden noorderbreedte en 3 graden westerlengte. Gelukkig volgt de GPS een protocol dat makkelijk is uit te lezen: het NMEA protocol. Elke NMEA-string begint met een $ en daarna volgen een aantal kenletters. De 22
verschillende gegevens zijn gescheiden door een komma. De voor mijn programma belangrijkste string is de GPGGA string, hierin staat namelijk de informatie over de actuele positie[20].
5.7
Servo’s aansturen: het roer en de zeilen
Een servo moet aangestuurd worden met pulsjes (later meer hierover). Deze pulsjes moeten gemaakt worden in de boordcomputer maar het zou voor de programmeur erg lastig zijn als het programma ook echt elke 20ms moet stoppen waar hij mee bezig is om een puls te maken. Daarom is de mbed voorzien van een pulsgenerator. Als deze eenmaal ingesteld is blijft hij zelfstandig pulsjes genereren[21].
5.8
Communicatie tussen de laptop en de boordcomputer: Sailcom
Met de laptop op de wal kan ik de boot en de boordcomputer in de gaten houden. Ook is het mogelijk om de boot een nieuwe koers en informatie over de windrichting te geven. De laptop is g´e´en afstandsbesturing maar een monitor voor de verrichtingen van de boot. Om de communicatie mogelijk te maken heb ik zelf een protocol bedacht: SailCom. Elk commando begint met een ‘$’ om het begin aan te geven, en de opdracht wordt afgesloten met een‘#’. Er zijn twee soorten opdrachten: leesopdrachten en schrijfopdrachten. Elke leesopdracht begint met get, gevolgd door de naam van de waarde die gelezen moet worden. Als antwoord stuurt de boot dan de waarden. Elke schrijfopdracht begint met set, gevolgd door de naam van de te schrijven waarde en de te schrijven waarde. Een commando ziet er dus zo uit: “$get koers#” of “$set ki 12.4#”.
5.9
PC Interface
De software werd op een gegeven moment zo complex dat bediening met een opdrachtregel te lastig werd. Daarom heb ik een grafische bedieningsinterface gemaakt. Deze interface is muisgestuurd en kan geschikt gemaakt worden voor een touchscreen. De informatie van en naar de boot wordt volledig gevisualiseerd met virtuele instrumenten en grafieken. Ook zit er een kaart in het programma om de route makkelijk te kunnen bekijken en aan te passen. Data en grafieken worden automatisch in een logboek opgeslagen. Het programma dat op de laptop draait is geschreven in Python, een minder effici¨ente taal dan C(++) maar veel geschikter voor het maken van grafische interfaces[22]. Hiermee heb ik een grafische schil gemaakt om met de boot te communiceren. Het eerste deel van het programma zorgt voor de communicatie met de boot en zorgt er voor dat er steeds actuele data voorhanden is en dat deze ook wordt opgeslagen voor latere referentie. Het tweede deel zorgt dat deze data duidelijk op het scherm weergegeven wordt en geeft de gebruiker de mogelijkheid de boot te bedienen zonder dat hij allemaal ingewikkelde commando’s uit zijn hoofd hoeft te leren. Het derde deel communiceert met de windvaan en zorgt ervoor dat de boot weet waar de wind vandaan komt. Ik zal de software bespreken aan de hand van screenshots van het programma.
23
5.9.1
Hoofdvenster
Figuur 15: Het hoofdvenster In het hoofdvenster kun je snel de belangrijkste dingen aflezen en instellen. Koers, windrichting, roerstand en zeilstand worden met virtuele instrumenten weergegeven. Met behulp van de schuifjes kun je de belangrijkste dingen instellen, waaronder het roer, het zeil en de parameters van de PID regelaar.
24
5.9.2
Roer & zeil
Figuur 16: Roer & Zeil In het tweede venster kun je alles zien over het roer en het zeil. Je kan de actuele stand aflezen en ze instellen. Ook kan je instellen of het roer en het zeil automatisch of handmatig bediend moeten worden. Ook zijn er grafiekjes van de roer- en zeilstand over de afgelopen 100 seconden.
25
5.9.3
Kompas & vaantje
Figuur 17: Kompas & Vaantje In het derde venster kan je de koers en de windrichting aflezen met een instrument en een grafiekje. Ook zit er een knopje om de kalibratie van het kompas in en uit te schakelen.
26
5.9.4
PID
Figuur 18: PID In het vierde venster kun je alles van de PID regelaar aflezen en instellen. Bijna alle belangrijke dingen zijn in een grafiekje of een tekstvakje af te lezen. Ook is het mogelijk om de parameters (Kp , Ki en Kd ) in te stellen. Daarnaast kan het setpoint met de hand worden ingesteld. Het is ook mogelijk om in te stellen in welke mate de helling van de boot mee genomen wordt in het regelproces.
27
5.9.5
Route
Figuur 19: Route In het laatste venster is het mogelijk om de route die het bootje vaart aan te passen. Links kun je de actuele gegevens van de route aflezen: de positie, de afstand tot het volgende punt en of er opgekruist wordt of niet. Ook kun je daar kiezen tussen gewoon rechtuit varen langs een vaste koers of dat er een van te voren ingestelde route gevaren moet worden waarbij eventueel ook wordt opgekruist. Met de schuifjes kun je instellen hoe hoog de boot aan de wind gaat zeilen en hoe breed de rakken mogen zijn bij het opkruisen (zie ook paragraaf 6.1). Rechts daarvan is een lijst met de punten waaruit de route bestaat. Deze punten zijn te verwijderen en te verplaatsen met de knopjes rechts van de lijst. Het toevoegen van nieuwe punten kan op drie manieren. De eerste manier is door direct de co¨ordinaten in te voeren. De tweede manier is door een koers en een afstand in te geven. Er wordt dan een waypoint toegevoegd met die afstand en koers ten opzichte van de huidige koers. Tenslotte is het mogelijk om de huidige positie als waypoint toe te voegen. Helemaal rechts wordt een kaartje getoond van de lijst met waypoints de huidige positie (met een rood puntje). Tussen deze punten wordt de route aangegeven die gevaren gaat worden. Hierbij wordt ook rekening gehouden met opkruisen.
28
6
Route plannen
6.1 6.1.1
Opkruisen Wat is opkruisen?
Hoewel een zeilboot niet tegen de wind in kan varen, is het toch mogelijk om naar een bovenwinds gelegen punt te varen. Dit doe je door op te kruisen (zie ook paragraaf 6.1). Bij opkruisen vaar je niet direct naar je doel, maar vaar je in een zig-zag koers naar je doel. Omdat je dit wilt bekorten, probeer je wel tamelijk recht in de wind te varen. Zeilers noemen dit hoog of scherp aan de wind. Deze hoek is afhankelijk van de constructie van de boot en de zeilen die gevoerd worden. Na een tijd gevaren te hebben ga je door de wind en ga je over de andere boeg weer aan de wind varen. In theorie kun je elk bovenwinds doel bereiken met slechts een keer over stag te gaan. Je hakt de route als het ware in stukjes, elk stukje noemen we een rak. Deze aanpak heeft nadelen maar ook voordelen. Wanneer je vaak overstag gaat wordt de totale afstand niet korter, maar je verliest snelheid en dus tijd bij elke overstag manoeuvre. Dus waarschijnlijk duurt het langer om je doel te bereiken. Slechts een keer overstag gaan is in theorie wellicht sneller, maar heeft in de praktijk een groot nadeel. Wanneer namelijk de wind draait terwijl je onderweg bent dan zul je alsnog vaker overstag moeten gaan. Bovendien heeft de route met maar twee slagen een veel breder vaarwater nodig.In de praktijk zul je dus een afstand verdelen in meerdere rakken. Mijn robotzeilboot kan niet alleen de complete overstag manoeuvre maken, maar bepaalt ook zelfstandig wanneer en hoe vaak de boot overstag moet gaan om zo snel mogelijk het doel te bereiken. Dankzij de software en de instrumenten weet de boot ook wanneer het doel is bereikt. Deze vaardigheid komt zelfs op de dure, professionele stuurautomaten van zeiljachten niet voor.
29
6.1.2
Algoritme van Stelzer en Pr¨ oll
Stelzer en Pr¨oll stellen een algoritme voor om op te kruisen[2]. Zij maken hierbij gebruik van de vmg (velocity made good), oftewel de component van de bootsnelheid richting het doel. Hun algoritme berekent op elk moment de vmg op de huidige koers en de vmg als ze v vmg op dat moment over de andere boeg zouden varen, op het moment dat die een factor n hoger is dan de huidige vmg besluit het algoritme overstag te gaan. Het resultaat van dit algoritme is te zien in afbeelding 21. Zoals je ziet zorgt dit er voor dat als de boot dichter bij het doel komt het aantal slagen zou toenemen. Om dit op te lossen hebben Stelzer en Pr¨oll een correctie toegepast op de factor n. Zij hebben Figuur 20: vmg die afhankelijk gemaakt van de afstand tot het doel: n = 1 + Pdc met parameter P c. Dit maakt het resultaat al een stuk beter maar het lost het probleem nog steeds niet op.
Figuur 21: Algoritme van Stelzer en Pr¨oll
30
6.1.3
Mijn algoritme
Omdat het algoritme van Stelzer en Pr¨oll voor alle mogelijke koersen een vmg moet uitrekenen is het vrij rekenintensief. Het moet immers 360 berekeningen uitvoeren en hier de koers met de hoogste vmg uitkiezen. Dit is een verspilling van energie en een zeer ineffici¨ent gebruik van de boordcomputer. Daarom heb ik zelf een ander algoritme geschreven. In plaats van het volledige pooldiagram van de boot door te rekenen, heeft mijn algoritme genoeg aan de scherpste koers die de boot kan varen. Daarna besluit het algoritme of er opgekruist moet worden of niet. Als er niet opgekruist hoeft te worden, vaart hij rechtstreeks naar zijn doel, anders gaan er een aantal dingen gebeuren. Bij opkruisen doorloopt het algoritme een viertal stappen. Als eerste wordt de huidige positie opgeslagen als begin van het opkruisen en als begin van het eerste rak. Deze positie wordt later nog gebruikt. Daarna moet de keuze gemaakt worden over welke boeg er wordt opgekruist. Hier wordt de kant gekozen die op dat moment het dichtste bij de richting van de boot ligt; die is immers het snelste te bereiken. Daarna moet de lengte van het te varen stuk uitgerekend worden. De lengte van het stuk is van verschillende dingen afhankelijk: de totale afstand van het traject en de hoek met de wind. Als er recht tegen de wind in opgekruist moet worden zijn de twee stukken even lang, maar als de wind schuin inkomt moet het ene stuk langer zijn dan het andere om zo effici¨ent mogelijk naar het doel te gaan.
α
δ
d
l
De twee lengtes worden als volgt berekend: l=
d sin(α ± δ)
(10)
Hier is d de afstand die elke slag richting het doel moet worden afgelegd (zie Afbeelding 22), α de hoek tussen de richting naar het doel en de wind en δ de scherpste hoek die gezeild kan worden. De ± is afhankelijk van Figuur 22: Mijn algoritme voor het over welke boeg er gevaren wordt. opkruisen
31
Dit is de C++ code van het algoritme: double Goto::get(void){ if (holdcourse) return course; double k = koers(&huidig->get(),&doel->get()); double d = afstand(&huidig->get(),&doel->get());
//koers naar doel berekenen //afstand nar doel berekenen
double ware_wind = vaantje->get();
//ware wind uit vaantje halen
if (ware_wind > 360) ware_wind -= 360; if (fabs(verschil(k,ware_wind)) < minhoek + hys){ if (!opkruisen){ begin_opkruisen = huidig->get(); begin_rak = huidig->get(); koers_begin = k; eerste = 1;
//kijken of er moet worden opgekruist //kijken of er niet al opgekruist wordt //huidige positie opslaan als begin van het // opkruisen //en als begin van het huidige rak //de koers van de layline opslaan //installen dat het eerste rak is
} else{ reset = 0; } opkruisen = 1; } if (fabs(verschil(k,ware_wind)) > minhoek + hys){ opkruisen = 0; eerste = 0; } if (!opkruisen){ return k; } else { double boeg1 = ware_wind + (double)minhoek; double boeg2 = ware_wind - (double)minhoek; if (boeg1 > 360) boeg1 -= 360; if (boeg2 < 0) boeg2 += 360;
//kijken of het al bezeild is
//als er niet opgekruist hoeft te worden //direct naar het doel varen //anders: //twee koersen uitrekenen
//lengte van de rakken uitrekenen double double double double
lengte = afstand(&begin_opkruisen, &doel->get()) / 5.0; a = fabs(verschil(ware_wind,koers_begin)); lengte1 = lengte / sin(((double)minhoek+a) * deg2rad); lengte2 = lengte / sin(((double)minhoek-a) * deg2rad);
if (eerste){ //eerste rak 2x zo kort lengte1 /= 2.0; lengte2 /= 2.0; //bij eerste rak kiezen welke koers het dichste bij de huidige licht
32
if (fabs(verschil(boeg1,kompas->get())) < fabs(verschil(boeg2,kompas->get()))) boeg = 1; else boeg = 2; } if (boeg == 1){ if (afstand(&begin_rak,&huidig->get()) > lengte1){ //als het einde van // het rak bereikt is eerste = 0; boeg = 2; //ga overstag begin_rak = huidig->get(); } return boeg1; } if (boeg == 2){ if (afstand(&begin_rak,&huidig->get()) > lengte2){ eerste = 0; boeg = 1; begin_rak = huidig->get(); } return boeg2; } } }
33
7
Hardware: sensoren en actuatoren
In dit hoofdstuk zal ik van elk onderdeel op de boot de werking en het nut uitleggen. Al deze onderdelen zorgen er namelijk samen voor dat de boot niet alleen kan varen, maar ook de juiste beslissingen kan maken om zijn doel te bereiken. Ik heb de onderdelen in vier groepen onderverdeeld: 1. De boot Alles wat te maken heeft met de boot en het zeilen zelf 2. De sensoren Alles wat zorgt dat de boordcomputer genoeg informatie heeft om beslissingen te nemen 3. De actuatoren Alles om te zorgen dat de beslissingen van de boordcomputer ook uitgevoerd worden 4. Elektronica De boordcomputer en de verbinding met de wal
7.1 7.1.1
Boot Vorige pogingen
Zoals ik al in de inleiding schreef is het idee voor dit profielwerkstuk ontstaan toen ik een modelbootje in de kelder vond. Er moest echter nog veel gebeuren om het bootje vaarklaar te maken, van robotzeilen was toen nog geen sprake. Bij deze eerste boot - De Astrid - heb ik de romp en het dek waterdicht gemaakt door ze te voorzien van een aantal lagen epoxy. Ook heb ik waterdichte luikjes gemaakt om nog bij de elektronica te kunnen. Deze luikjes heb ik voorzien van tochtstrip om de aansluiting waterdicht te maken. Het was lastig om een kabeldoorvoer te maken om de verschillende compartimenten te verbinden. Voor de mast heb ik in de modelbouwwinkel een aluminium profieltje gekocht en dit heb ik afgezaagd tot een lengte van ´e´en meter. De verstaging bestand uit staalkabel met een diameter van 0,5mm en werd met miniatuurspannertjes op spanning gebracht. De zeilen heb ik met behulp van het computerprogramma Sailcut CAD ontworpen. In dit programma geef je de eigenschappen van je zeil in zoals bolling, lengte van de mast en lengte van de giek. Daarna berekent het programma de maten van de losse banen van het zeil. Deze banen print je uit en trek je over op spinnakerdoek. Daarna heb ik de losse banen uitgeknipt en aan elkaar genaaid. Dit heb ik voor het grootzeil en de fok zo gedaan. Met dit bootje heb ik de eerste testen van de hardware gedaan en toen is de liefde voor zelfstandig varende modelbootjes ontstaan. Figuur 23 is een foto van de Astrid. Bij het testen bleek toch dat de motoren niet sterk genoeg waren en de boot niet waterdicht genoeg was voor de dure elektronica die ik er in wilde plaatsen. Helaas bleek het bootje dus uiteindelijk minder geschikt dan ik gedacht had. Ik heb toen niet opgegeven en een nieuw bootje gezocht om mijn profielwerkstuk af te kunnen maken. Daarom heb ik een commercieel, op afstand bestuurbaar modelzeilbootje gekocht en deze aangepast: de micromagic. 7.1.2
Micromagic
Ik gebruik nu een Micromagic, een modelzeilboot waar over de hele wereld wedstrijden mee gevaren worden. De boot is gemaakt voor radiografische besturing, dus is hij goed waterdicht en is er voldoende ruimte voor de inbouw van servomotoren, batterijen en elektronica. Ik heb de oorspronkelijk elektronica eruit gehaald om plaats te maken voor de boordcomputer en de 34
Figuur 23: Foto van de eerste boot: De Astrid bijbehorende onderdelen. Het is een modern en licht ontwerp. Het bootje is daarom zeer snel en wendbaar. Dat maakt het makkelijker om de regelingen af te stellen.
Figuur 24: Foto van de huidige boot: MicroMagic - De Vliegende Hollander
35
7.2 7.2.1
Sensoren GPS
Om de plek van het bootje op aarde te bepalen (en dus ook de positie ten opzichte van het doel) heb ik gebruik gemaakt van een GPS module. Deze GPSmodule stuurt ´e´en keer per seconde de positie van het bootje over een seri¨ele uitgang die verbonden is met de hoofdcomputer.
7.2.2
Kompas
Figuur 25: GPS module
Het kompas is het belangrijkste instrument op een zeilboot. En voor deze autonome zeilboot worden aan dit kompas een aantal hoge eisen gesteld: ten eerste mag er geen grote afwijking ontstaan als het kompas gekanteld werd (iets wat veel kompassen wel hebben), en ten tweede moest het een flink aantal keer per seconde de koers kunnen meten. Het elektronisch kompas dat ik gevonden heb is een Honeywell HMC6343[17]. Dit kompas is, in tegenstelling tot veel andere elektronische kompassen, niet gevoelig voor kantelen. Dit komt omdat het kompas het magnetisch veld van de aarde niet in ´e´en richting meet maar door het in drie loodrechte vectoFiguur 26: Elektronisch ren in de X, Y en Z richting te meten. Samen met de helling kan het kompas uit deze gegevens exact de koers uitrekenen, zelfs als kompas de sensor gekanteld is. Bovendien kan het kompas dit tot 10x per seconde. Ook bezit het kompas de mogelijkheid om zichzelf te kalibreren waardoor hij niet meer be¨ınvloed wordt door metalen objecten (zoals de servomotoren) in de omgeving. 7.2.3
Vaantje
Om een boot te laten zeilen moet je natuurlijk wel weten waar de wind vandaan komt. Daarom moest hier wat op gevonden worden. Er zijn veel verschillende manieren om de wind te meten, het kan bijvoorbeeld door een sensor te maken die rondom de temperatuur meet (de kant waar de wind vandaan komt is ietsje kouder), maar dat is niet zo’n precieze methode. Ook kan er gebruik gemaakt worden van ultrasoon-pulsen, maar dit zijn hele dure systemen. De enige oplossing die nog over bleef was een klassiek windvaantje. Het windvaantje heeft Figuur 27: Windvaantje een vaan, deze vaan vangt wind en draait naar de wind toe. Onder de vaan zit een systeem dat de richting bepaalt. Het vaantje was oorspronkelijk een onderdeel van een stuurautomaat voor een grote boot. Daarom was er helaas geen documentatie die vertelde hoe de elektronica van de sensor in elkaar 36
zat. Er zat dus niets anders op dan de sensor uit elkaar te halen en zelf een kijkje in het binnenwerk te nemen. De elektronica om de windhoek te meten bestond uit een ledje en vier lichtgevoelige weerstanden (LDR’s) die samen twee spanningsdelers vormden. Hierboven zat een spiegelend plaatje dat in elke positie een andere mate van reflectie had. Door deze mate van reflectie kwam er afhankelijk van de hoek een andere hoeveelheid licht op de weerstanden. Nadat ik 5 volt op de spanningsdelers en het ledje gezet had kwamen er twee spanningen uit het vaantje. Met een oscilloscoop kon ik zien dat er twee sinusvormige signalen uit het vaantje kwamen wanneer je er aan draaide. Deze spanningen verschillen 90 graden in fase.
Figuur 28: Gemeten spanningen Helaas was het vaantje te groot en te zwaar om op het bootje te plaatsen, daarom staat het vaantje op een oud fotostatief op de kant en zorgt het programma op de computer ervoor dat de boot weet waar de wind vandaan komt. Als er een tweede versie van dit bootje komt maak ik de boot iets groter waardoor het vaantje w´el op de boot kan en het bootje niet meer afhankelijk is van de computer op de kant om te weten waar de wind vandaan komt.
37
7.3 7.3.1
Actuatoren Servo
Om de bewegingen van het roer en het zeil te regelen, gebruik ik elektromotoren. Echter geen gewone elektromotoren maar servo’s. Servo’s zijn elektromotoren met een ingebouwde regeling. Je zegt tegen een servo in welke positie hij moet staan en vervolgens gaat de servo naar die plaats toe bewegen. Als hij de positie bereikt heeft stopt hij met draaien. Ook als bijvoorbeeld de wind aan het zeil trekt zorgt de servo er voor dat hij toch op zijn plek blijft door een tegenkracht uit te oefenen. Dit is heel handig omdat je er daarna van uit kan gaan dat je roer en zeil in de ingestelde stand blijven staan. De servo is aan te sturen met pulsjes met een welbepaalde lengte. De lengte van deze pulsjes geeft dan positie aan.
Figuur 29: Golfvorm servo Zoals in figuur 29 te zien is, heeft het signaal een periode van 20ms oftewel een frequentie van 50Hz. Aan het begin van elke periode wordt er een puls gestuurd. De lengte van deze puls bepaalt de stand van de servo; als deze 1ms is draait de servo helemaal naar links, als deze 2ms is dan draait de servo helemaal naar rechts. Het is erg belangrijk dat deze pulsen ook echt elke 20ms herhaald worden, anders staat de as van de servo wel in de juiste positie, maar staat deze niet meer vast omdat de servo geen kracht meer levert.
Figuur 30: Servomotor
38
7.4 7.4.1
Elektronica ARM microcontroller
De boordcomputer van het robot-bootje wordt aangedreven door een ARM microcontroller. Het is het kleine broertje van het type processor dat in moderne smartphones zit. Ik maak gebruik van een Mbed, een ontwikkelbordje voor de LPC1768 ARM microcontroller. Op dit ontwikkelbordje zit de processor zelf, een stukje elektronica dat nieuwe programma’s in de chip kan laden, een geheugen om dingen op te slaan en spanningsregelaars om te zorgen dat de (te hoge) spanning van de batterijen omgezet worden naar een spanning waar de andere elektronica iets mee kan. De processor loopt op 100MHz en kan met 32 bits getallen werken. Ook heeft hij speciale instructies voor goniometrische functies waardoor er zeer snel met co¨ordinaten gewerkt kan worden. Bovendien was het nodig dat hij minstens twee seri¨ele poorten moest hebben om met de GPS en met de zender te kunnen communiceren en een uitgang moest hebben om automatisch pulsjes te genereren die de servo’s aansturen. Ten slotte heeft hij voldoende in- en uitgangen voor eventuele uitbreidingen.
Figuur 31: Microcontroller met aansluitmogelijjkheden
7.4.2
Draadloze communicatie met de wal
Figuur 32: Zigbee module
Voor de communicatie tussen de wal en het schip heb ik gebruik gemaakt van Xbee Pro zenders. Dit zijn industri¨ele modules voor draadloze communicatie. Ze gaan volgens de specificaties tot 1,5 km. In deze Xbee modules zit speciale software, die door de ingebouwde foutcorrectie en andere algoritmes ervoor zorgt dat met grote waarschijnlijk alle data die verzonden wordt, ook zo ontvangen wordt. Ik gebruik deze modules om de boot te vertellen als deze een andere route moet gaan varen. Ook kan ik voor testdoeleinden waardes opvragen, bijvoorbeeld de actuele koers. Deze waardes kan ik dan op de computer tonen. Ik gebruik deze modules en de computer dus niet als afstandbediening. Als de route ingesteld is, is de zender niet meer nodig.
39
Figuur 33: Het aansluitschema 20 19 18 17 16 15 14 13 12 11
HMC6343
x
XBee
4x AA
AD0/DIO0 AD1/DIO1 AD2/DIO2 AD3/DIO3 RTS/AD6/DIO6 ASC/AD5/DIO5 VREF ON/SLEEP CTS/DIO7 AD4/DIO4
y
GND
GND
100u
C1
GND
1 2 3 4 5 6 7 8 9 10
3.3V
3.3V
GND
VCC DOUT DIN DO8 RESET PWM0/RSSI PWM1 RESERVED DTR/DI8 GND
+
z
VCC GND
SDA SCL
VCC
VCC GND
TXD RXD
SDA SCL
GND VIN VB NR 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
VOUT VU IFIF+ RDRD+ TDTD+ DD+ 30 29 28 27 26 25 24 23 22 21
MBED
RXD
GND
100u
C2
3.3V
+
GND
VCC
Kompas
+ -
40 GND
VCC
VCC GND
SIG
VCC GND
SIG
Roer
Zeil
GND
3.3V VCC GND
TXD RXD
GPS
7.5 Schema
Om al deze hardware samen te laten werken moest ik deze wel op de juiste manier aansluiten. Ik heb toen aan de hand van de datasheets van de verschillende onderdelen een aansluitschema gemaakt, zie figuur 33. Vervolgens heb ik alles volgens dit schema aan elkaar gesoldeerd.
8
Resultaten en evaluatie
Nadat ik al deze losse onderdelen samengevoegd had kon ik met mijn bootje gaan varen. Bij elke proefvaart heb ik nieuwe idee¨en opgedaan en deze heb ik vervolgens direct verwerkt in de soft- en hardware. Als afsluiting ben ik naar een landelijke dag van de MicroMagic-club gegaan. Ik heb natuurlijk niet aan de wedstrijd meegedaan, maar ik heb de boot uitgebreid kunnen testen. Ook heb ik veel tips gekregen over de zeilvoering, dat maakte de boot een stuk beter controleerbaar. Ook heb ik daar de tip gekregen om met kleinere zeiltjes te varen. De boot werkte deze dag volledig naar behoren en ik heb alle data die uit het bootje kwam opgeslagen. Tijdens deze en eerdere vaartochten heb ik foto’s en filmpjes gemaakt. In dit hoofdstuk zal ik verslag doen van deze vaartochten en mijn bevindingen ondersteunen met kaartjes en grafieken.
8.1
Algemene indruk
Mijn algemene indruk van mijn robotzeiler was zeer goed. De robot volgde vrij precies de route en kwam altijd bij zijn doel uit. De ene keer deed hij er wat langer over dan de andere keer. De software bleek in de praktijk goed te bedienen te zijn om het bootje snel te vertellen wat hij moest doen, en het bootje luisterde hier dan ook prima naar. Ook zorgde de regelaar er voor dat de boot dicht in de buurt zat van de koers die hij moest varen. De boot had nog wel last van weersinvloeden als golven en wind. Maar een echte kapitein heeft natuurlijk ook moeite met het op koers houden van zijn boot als het weer minder goed is. Vaak reageerde de boot niet alleen te laat, maar ook nog tamelijk heftig, of juist niet heftig genoeg omdat de boot de aard van de verstoring niet kende. Als een mens een kleine afwijking merkt kan hij door de dingen om hem heen waar te nemen voorspellen of de afwijking gewoon een kleine afwijking was of dat er nog meer gaat komen. Denk hier bijvoorbeeld aan een grote golf die op de boot afkomt. Een echte kapitein ziet de golf aankomen en op het moment dat hij merkt dat de golf bij de boot is stuurt hij extra heftig. Door de verkeerde reactie lag de boot vaak uit koers waardoor het even duurde voordat de regelaar de koers weer hersteld had. Ook had de boot moeite met het sturen in hele lichte wind en in hele zware wind. De boot stuurde dan teveel of juist te weinig, ik moest daarom voor elk windtype de regelaar anders instellen. Dit probleem kwam vooral naar voren bij het overstag gaan. Hier werd bij een lage snelheid te weinig gestuurd. Doordat de boot te langzaam door de wind gaat heeft hij vrij lang de wind recht van voren. De snelheid neemt enorm af en de manoeuvre duurt daardoor veel te lang, soms wel vijftien tot twintig seconden. En als de boot dan eenmaal overstag was schoot hij vaak voorbij de ingestelde koers van 35 graden ten opzichte van de wind. De boot moest dan na de overstagmanoeuvre ook nog herstellen. Wel kwam de boot altijd weer op koers en bereikte altijd zijn doel.
8.2
Hardware
De grootste vijand van elektronica is het water. Ik was dan ook bang dat er water bij de elektronica zou komen. Toch had ik hier nog niet genoeg rekening mee gehouden want bij de eerste proefvaarten had ik nog last van water in de boot, met gelukkig nauwelijks ernstige gevolgen. Alleen de communicatie met de laptop was uitgevallen. Gelukkig was de boot op dat moment geen route aan het varen, en doordat ik aan lagerwal zat dreef de boot uiteindelijk vanzelf terug. Ik was toen gewaarschuwd en heb daarna alle elektronica ingesmeerd met een speciaal soort coating om ze tegen water te beschermen.
41
Het vaantje kon door zijn gewicht en grootte helaas niet op het bootje geplaatst worden. Ik had toen besloten om hem op een statief op de kant te zetten en dan op de laptop aan te sluiten. De laptop zou dan de windrichting doorgeven aan het bootje. Het was alleen heel onhandig dat het vaantje met een kabel aan de laptop vast zat. Als je aan het experimenteren bent wil je soms de laptop even meenemen om dichter bij het water te kunnen staan. Ook viel me op dat de wind meestal relatief constant bleek te zijn. Ik heb er toen voor gekozen om de windrichting soms met de hand in te stellen en daarna zonder vaantje en dus zonder kabel te werken. De servo’s bleken inderdaad zo goed te werken als ik gedacht had. Dit was niet heel verassend omdat servo’s in bijna elke vorm van modelbouw gebruikt worden, en dus goed beproefd zijn. Ze waren makkelijk aan te sturen en luisterden prima naar wat de boordcomputer ze vertelde. Ook waren ze snel en krachtig genoeg om bij harde wind snel de zeilen aan te kunnen passen en vlot te sturen. Over de zender en ontvanger die de communicatie verzorgden tussen het bootje en de laptop was ik iets minder tevreden. Het gespecificeerde bereik van 1,5km werd lang niet altijd gehaald. Toen ik tussen allemaal andere mensen in stond die ook hun afstandsbediening gebruikten was het al na 75 meter afgelopen. Ik denk dat (naast de storing van andere zenders) het grootste probleem is dat de antenne op de boot zo dicht bij het water zit. Vlak boven het wateroppervlakte kan de Xbee last hebben van reflecties van de elektromagnetische straling op de golven. Hierdoor kan het bereik heel erg verkort wordt. Ook hier zou een langere of hogere antenne welkom zijn. De boordcomputer deed zijn werk uitstekend. Hij was snel genoeg om de route te plannen, de sensoren in te lezen, bij te houden waar hij was, de roer en zeilstanden te berekenen en de servo’s aan te sturen. Dat hij ook nog constant door de laptop op de wal lastig gevallen werd bleek hem niet te hinderen bij zijn werk. De laptop vraagt namelijk regelmatig aan het bootje waar hij is en wat hij aan het doen is. Deze commando’s werden dus tegelijkertijd ge¨ınterpeteerd en het juiste antwoord werd terug gezonden. De sensoren werkten ook goed. Het kompas was zijn hoge prijs zeker waard. Terwijl het bootje door de elementen alle kanten op werd geschud bleef het kompas keurig de juiste koers doorgeven. Met de GPS was het nog wel even experimenteren voordat deze goed werkte. Doordat de antenne zo dicht op het water zat bleek de plaatsing heel belangrijk. Een herpositioneren van enkele centimeters maakte al een verschil tussen geen ontvangst en perfecte ontvangst. Na veel experimenteren bleek hij het best te werken als hij tegen de onderkant van het dekseltje geplakt zat. Nadat hij daar geplaatst was, heb ik verder geen problemen meer gehad. Bij een volgende versie van mijn robot zou ik de antenne hoger willen plaatsen. Wellicht een losse antenne aan dek of in de mast. De energievoorziening van de boot bleek ook prima te werken. De vier AA batterijen konden de boot voor circa vijf uur van stroom voorzien. Als de boot zijn servo’s niet hoefde te bewegen nam dit zelfs toe tot tien uur. Daarom zou de boot zichzelf met zonnecellen van stroom kunnen voorzien. Deze zouden dan lithium-ion accu’s op kunnen laden om ’s nachts door te varen. Ook zou de software meer bewust gemaakt kunnen worden van het stroomverbruik wat hij veroorzaakt. Als de accu’s bijvoorbeeld bijna leeg zijn zou hij kunnen besluiten om minder vaak zijn roer aan te passen.
42
8.3
PID regelaar
De PID regelaar werkte redelijk tot goed. De boot bleef in de buurt van de te varen koers, maar was wel gevoelig voor externe factoren. Het eerste probleem was dat er een kleine ruis in het signaal van het kompas zat. De differenti¨erende deel regeerde heel heftig op deze kleine doch snelle veranderingen. Ik heb toen een filter toegepast wat de ruis er uit filterde, het signaal werd zo veel strakker en de regelaar reageerde daarna veel beter. Het grootste probleem kwam naar voren bij serieuze proefvaarten. Er bleek door de constante druk van de wind een constante afwijking te ontstaan. Het integrerende deel van de regelaar moet deze afwijking oplossen. Het bleek echter dat de afwijking wel verdween als ik het integrerende zwaarder liet wegen. Er ontstond daardoor echter een nog groter probleem: de boot ging niet meer overstag. Doordat het integrerende deel na een stuk varen een bepaalde waarde had aangenomen bleef de boot goed op koers. Bij het overstag gaan moet dit deel als het ware omkeren. Het moet de andere kant op compenseren omdat de wind na de overstagmanoeuvre van de andere kant komt. Dit omkeren duurde echter zo lang dat de boot rondjes begon te varen. Daardoor moest ik het integrerende deel uitschakelen.
Figuur 34: Constante afwijking in de koers In figuur 34 is een stukje van een proefvaart te zien. In het stuk van 10 tot 100 seconden is deze aan het opkruisen. De boot heeft in het stuk van 30 tot 50 seconden moeite om overstag te gaan. Verder is de constante afwijking goed te zien. De rode lijn is de koers zoals hij had moeten zijn en de blauwe lijn de werkelijk gevaren koers. In de hele grafiek is er een afwijking van soms wel enkele tientallen graden. Aan het feit dat deze afwijking vrij constant blijft, is te zien dat het probleem in het integrerende deel zit. Verder zien we in de grafiek dat in het stuk van 25 tot 50 seconden de boot moeite heeft met overstag gaan. De te varen koers gaat daar in een keer 70 graden omhoog terwijl het vrij lang duurt voordat de boot echt begin te draaien.
8.4
Route plannen
Het plannen van de route ging goed. Ten eerste kwam de boot altijd waar hij moest komen. Dat hij hier soms wat langer over deed dan nodig was lag niet aan de route, maar aan de manier waarop deze uitgevoerd werd. Door de slechte overstagmanoeuvres wordt bijvoorbeeld heel veel tijd verloren. In figuur 35 is een kaart afgebeeld van een gevaren route. Ik had het bootje de opdracht gegeven om tussen twee punten heen en weer te varen. De wind kwam hier van rechts, dus om van punt A naar punt B te varen moest er worden opgekruist; terug kon er voor de wind gevaren worden. Laten we in het bespreken bij punt A beginnen en naar onder vertrekken.
43
De boot is bij punt A en gaat naar punt B. Hij merkt dat er opgekruist moet worden en er wordt een route gepland. Het bootje vaart dan schuin naar onder, zo scherp aan de wind als hij maar kan. Als de berekende afstand afgelegd is krijgt de regelaar van de software de opdracht om overstag te gaan. Op dat moment gaan er een aantal dingen mis. Op het kaartje is niet te zien dat het lang duurt, maar er is wel een ander probleem zichtbaar. Na de overstagmanoeuvre schiet de boot te ver door. Daarom lijkt het bootje een stukje terug te varen. Het duurt dan even voordat de juiste koers hersteld is. De boot gaat dan schuin naar boven. Als hij boven is gekomen gaat hij weer overstag en gaat er weer hetzelfde mis. En na nog een keer overstag heeft hij punt B bereikt. Het bootje draait dan en gaat voor de wind naar punt A terug. Het voordewindse rak wordt effici¨ent afgelegd.
Figuur 35: Een kaartje van een gevaren route
44
9
Discussie
Uit de resultaten blijkt dat het doel van het werkstuk gehaald is: de boot vaart zelfstandig van A naar B en zelfs via C weer terug. Dit doet hij nog niet op de beste manier, maar hij komt er wel. Alle problemen die er nu nog zijn kunnen verbeterd worden. Daarvoor is wel een grotere boot nodig is. De boot is in zijn huidige staat nog niet echt autonoom: de boot heeft nog geen windvaan aan boord. Dit probleem is op te lossen door een grotere boot te nemen en daar een windvaan op te plaatsen. Een ander probleem is de energievoorziening. De boordcomputer en de overige hardware lopen nu op batterijen, maar die kunnen zichzelf niet opladen. En door deze beperking kan de boot maximaal vijf uur achtereen varen, daarna zijn de batterijen leeg. Om de boot qua stroom zelfvoorzienend te maken moeten er twee dingen gebeuren. Ten eerste moet de boot zelf energie opwekken met zonnecellen. Deze energie moet hij opslaan in batterijen zodat er genoeg stroom opgeslagen is om de nacht door te komen. Aan de andere kant moet ik de hardware meer bewust maken van de energie die hij gebruikt. Als er bijvoorbeeld door bewolking minder energie beschikbaar is moet de boot minder vaak aanpassingen aan het roer en zeil doen. Het zuinig omgaan met energie moet naast snelheid een factor zijn waar de boot rekening mee moet houden. De boot kan nu ook nog geen objecten en andere schepen ontwijken. Hiervoor zou er een radar of een sonar op de boot geplaatst moeten worden. De resultaten hiervan zouden er dan samen met een aangepast routeplan systeem voor moeten zorgen dat de boot niet tegen andere schepen of boeien opvaart. De communicatie is nu nog niet geschikt om de boot op praktische manier in te zetten. Het is dan nodig om op afstand meetresultaten te ontvangen en nieuwe routes in te stellen, maar dat gaat niet als je na een paar honderd meter geen contact meer hebt. Daarom moet er een iridium satelliettelefoon op komen. Via deze dataverbinding kan de boot dan bijvoorbeeld ´e´en keer per dag zijn meetresultaten doorsturen en een nieuwe route binnenhalen. De problemen met het overstag gaan zou ik in een volgende versie graag willen oplossen. Mijn plan is om de actuele snelheid mee te nemen in de regeling. De bootsnelheid is namelijk van invloed op het gedrag van de boot bij roeruitslag. Bij overstag gaan is de snelheid laag en moet er meer roeruitslag worden gegeven dan bij kleine correcties op een hoge snelheid. Ook de filters die gebruikt worden moeten nog verbeterd worden. Deze filters zorgen nu voor een vertraging in de koersinformatie. Dit komt niet door een gebrek aan rekenkracht maar doordat het even duurt voordat een verandering in koers aan de uitgang van het filter zichtbaar wordt. Een oplossing hiervoor zou een Kalman filter zijn. Deze filters halen niet de ongewenste delen uit vorige metingen, maar proberen juist de volgende meting te voorspellen. Deze voorspelling wordt daarna constant verbeterd door naar de vorige metingen en voorspellingen te kijken. Bij zwaar weer gaat de boot nu heel schuin waardoor deze nauwelijks meer te controleren is. Het zou mooi zijn als de boot zijn zeilen aan kon passen aan de windkracht. De boot zou bijvoorbeeld bij harde wind de zeilen een stukje kunnen inrollen. Hierdoor wordt het oppervlakte kleiner en gaat de boot minder schuin. Zo kan de boot bij zwaar weer toch doorvaren. Ook zou de boot bij licht weer extra zeilen kunnen voeren, zoals bijvoorbeeld een spinnaker. Om de stabiliteit te bevorderen zou ik als volgende boot graag een multihull bouwen. Deze gaan sneller en gaan veel minder scheef. Dit maakt metingen van bijvoorbeeld de waterdiepte veel eenvoudiger analyseerbaar en betrouwbaarder. Om al deze wijzigingen door te voeren is er een nieuwe en grotere boot nodig. Het lijkt me erg leuk en leerzaam om deze boot nog te bouwen, ook al is het niet meer voor mijn profielwerkstuk.
45
10
Conclusie
Het doel van mijn werkstuk was het bouwen van een boot die zelfstandig van A naar B kan zeilen, desgewenst via C en langs D weer terug. De kapitein en zijn bemanning worden volledig vervangen door een computer die een route plant en zelfstandig de nodige acties onderneemt om de boot zijn doel te laten bereiken. Ik heb eerst uitgezocht uit welke onderdelen een dergelijk systeem moest bestaan. Daarna heb ik alles over deze onderdelen uitgezocht en ze samengevoegd. Na veel testen en aanpassen ben ik zo tot een resultaat gekomen. Ik heb in dit proces heel veel uit moeten zoeken en ik heb hier heel veel van geleerd. Ik heb me verdiept in regeltechniek. Ik heb leren knutselen aan modelbootjes. En ik heb veel bijgeleerd over C++, door boeken te lezen en vooral veel te programmeren. Doordat de data realtime verwerkt moest worden ga je veel beter op je programmeerstijl letten: elke instructie die je te veel gebruikt zorgt dat je programma langzamer regeert op de sensoren. Ook heb ik leren opmaken in LATEX, een programma wat zeer veel gebruikt wordt om wetenschappelijke artikelen en verslagen mee te schrijven. Maar het belangrijkste wat ik van dit werkstuk geleerd heb is dat het mogelijk is om met veel doorzettingsvermogen, interesse en gezond verstand iets te maken wat tien jaar geleden nog science fiction was. Iets wat nu ook in academische kringen onderzocht wordt. Ik hoop daarom dan ook dat dit werkstuk andere mensen aan zal moedigen om ook een dergelijk project te beginnen en hun resultaten te publiceren. Tot slot verwacht ik dat dit soort bootjes echt gebruikt gaan worden bij onderzoek. Ze zijn relatief goedkoop en makkelijk te vervangen. Omdat ze volledig autonoom metingen kunnen doen, zijn maar weinig mensen nodig om de data te verzamelen. Ook kunnen grote groepen bootjes samenwerken om een groot gebied te bestuderen. Dit kan mogelijkheden bieden voor kleine groepen onderzoekers met een klein budget die toch graag data willen verzamelen. Terwijl ik met mijn werkstuk bezig was heeft mijn bootje al veel aandacht gekregen. Ik ben op dit moment aan het overleggen met Waternet om mijn bootje in te zetten bij metingen van de waterdiepte. De bedoeling is dat mijn bootje regelmatig een kaart maakt van de diepte van een meer. Mijn bootje is natuurlijk de ideale kandidaat om zoiets langere tijd in de gaten te houden. Ik hoop dat Waternet mij die kans geeft mijn boot verder te ontwikkelen en hem te gebruiken bij hun metingen. Maar zelf ga ik er in ieder geval mee door.
46
Referenties [1] Ross Garrett (1987). The Symmetry of Sailing. Adlard Coles Nautical, 2nd edition. [2] Tobias Pr¨ oll Roland Stelzer (2007). Autonomous sailboat navigation for short course racing. Elsevier Journal of Robotics, 56 (7):604–614. URL http://www.cci.dmu.ac.uk/ administrator/components/com_jresearch/assets/publications/1266686445.pdf. [3] Peter Christian Frthmann (1998). Self-Steering Under Sail. Mcgraw-Hill. URL http: //www.windpilot.com/n/pdf/bookeng.pdf. [4] Pieter Adriaans (2003). Robot op Zee. Boom. [5] Mark Neal Colin Sauz´e (2008). Design considerations for sailing robots performing long term autonomous oceanography. URL http://cadair.aber.ac.uk/dspace/bitstream/ handle/2160/643/robotdesign.pdf. [6] Mark Neal (2006). A hardware proof of concept of a sailing robot for ocean observation. IEEE Journal of Oceanic Engineering, 31 (2):462–469. URL http://citeseerx.ist.psu. edu/viewdoc/download?doi=10.1.1.103.1486&rep=rep1&type=pdf. [7] Wikipedia: Cubesat. URL http://en.wikipedia.org/wiki/CubeSat, bezocht op 22-12012. [8] Leonard David (September 2004). Cubesats: Tiny spacecraft, huge payoffs. URL http:// www.space.com/308-cubesats-tiny-spacecraft-huge-payoffs.html, bezocht op 22-12012. [9] Rodney A. Brooks (1988). A robust layered control system for a mobile robot. IEEE Journal of Robotics and Automation, 4 (6):14–23. URL http://mit.dspace.org/bitstream/ handle/1721.1/6432/AIM-864.pdf?sequence=2. [10] Skm53 datasheet. URL http://www.skylab.com.cn/datasheet/SkyNav_SKM53_DS.pdf. [11] Morton Dan Morris H. Michael Newman (1994). Direct digital control of building systems. John Wiley & Sons, Inc. [12] Wikipedia: Closed-loop transfer function. URL http://en.wikipedia.org/wiki/ Closed-loop_transfer_function, bezocht op 22-1-2012. [13] Wikiepdia: Pid controller. URL http://en.wikipedia.org/wiki/PID_controller, bezocht op 22-1-2012. [14] Wikipedia: Great circle. URL http://en.wikipedia.org/wiki/Great_circle, bezocht op 22-1-2012. [15] Wikipedia: Rhumb line. URL http://en.wikipedia.org/wiki/Rhumb_line, bezocht op 22-1-2012. [16] Brett Beauregard (4 2011). Improving the beginners pid. URL http://brettbeauregard. com/blog/2011/04/improving-the-beginners-pid-introduction/. [17] Hmc6343 datasheet. URL http://www51.honeywell.com/aero/common/documents/ myaerospacecatalog-documents/Missiles-Munitions/HMC6343.pdf.
47
[18] Wikipedia: Butterworth filter. filter.
URL http://en.wikipedia.org/wiki/Butterworth_
[19] Tony Fisher (Octobre 1999). Interactive digital filter design. URL http://www-users.cs. york.ac.uk/~fisher/mkfilter/trad.html. [20] Dale de Priest. Nmea data. URL http://www.gpsinformation.org/dale/nmea.htm, bezocht op 22-1-2012. [21] Lpc1768 datasheet. URL http://www.nxp.com/documents/data_sheet/LPC1769_68_67_ 66_65_64_63.pdf. [22] Mark Lutz (1999). Learning Python. O’Reilly, 4th edition.
48
11
Appendix A: Foto’s
49
50
51
52
53
12
Appendix B: De Programmacode van de boordcomputer
Ik heb in mijn werkstuk slechts een klein deel van de programmacode behandeld die gebruikt wordt door het bootje. Deze losse stukjes zijn nog veel te weinig om een bootje te laten varen. Om toch een volledig overzicht te geven heb ik alle programmacode in deze bijlage gezet.
Main main.cpp #include "mbed.h" #include "split.h" #include <string> #include
#include #include #include #include #include #include #include #include #include #include
"pws.h" "roer.h" "zeil.h" "kompas.h" "gps_wrapper.h" "route.h" "vaantje.h" "pid.h" "state.h" "GPS.h"
std::map<std::string, float> state; std::map<std::string, float> updateable; DigitalOut led(LED1); //invoer GPS g(NC, p27); Gps gps(&g); Kompas kompas; Vaantje vaantje; //uitvoer Roer roer; Zeil zeil; //verwerking Route route(&gps); Goto got(&gps,&route,&kompas,&vaantje); Pid pid(&got,&kompas,&roer); //zender Serial zender(p13,p14); string buffer; Ticker blinker; Ticker updater;
void blink(void){
54
led = !led; }
void newData(void){ char c; while (zender.readable()) { c = zender.getc(); if (state["echo"]) zender.putc(c);
if (c == ’$’) { buffer.clear(); } else if (c == ’#’){ if (state["echo"]) zender.printf("\n\r"); std::vector<std::string> commands = split(buffer); if (commands[0] == "get" && commands.size() >= 2) zender.printf("%f \n\r", state[commands[1]]); else if (commands[0] == "set" && commands.size() >= 3){ if (commands[2] == "True") updateable[commands[1]] = 1; else if (commands[2] == "False") updateable[commands[1]] = 0; else{ float n; sscanf(commands[2].c_str(),"%f",&n); updateable[commands[1]] = n; } } else if (commands[0] == "route_set"){ route.update(commands); } else if (commands[0] == "route_get"){ zender.printf("%s\n\r", route.lees().c_str() ); } else if (commands[0] == "gps_gga"){ zender.printf("%s\n\r", gps.getgga().c_str() ); } } else buffer += c;
} }
55
void update(void){ std::map<std::string, float>::iterator iter; for (iter = updateable.begin(); iter != updateable.end(); iter++) { if (state["echo"]) zender.printf("%s Updated: %f\n\r", iter->first.c_str(),iter->second); state[iter->first] = iter->second; } updateable.clear(); }
int main(void) { //setup zender zender.baud(115200); zender.attach(&newData); //attach tickers blinker.attach(&blink, 0.5); updater.attach(&update, 0.1); //echo off state["echo"] = 0;
while (1) { ; } }
56
Invoer gps wrapper.h #ifndef GPS_WRAPPER_H #define GPS_WRAPPER_H #include #include #include #include #include #include #include
"mbed.h" "pws.h" "math.h" "GPS.h" "state.h" <string> <map>
class Gps{ pos positie; GPS * gps; Ticker updater; public: char gga[128]; Gps(GPS *); void update(); void set(pos *); pos get(void); void simuleer(double,double); string getgga(void); };
#endif
gps wrapper.cpp #include "gps_wrapper.h" Gps::Gps(GPS * g){ gps = g; gps->baud(9600); gps->format(8, GPS::None, 1); gps->setGga(gga); updater.attach(this,&Gps::update,1.0); } void Gps::update(void){ state["lat"] = gps->latitude(); state["lon"] = gps->longitude(); } void Gps::set(pos* new_pos){ positie.lat = new_pos->lat; positie.lon = new_pos->lon; }
57
pos Gps::get(void){ positie.lat = state["lat"];//gps->latitude(); positie.lon = state["lon"];//gps->longitude(); /*//update state state["lat"] = positie.lat; state["lon"] = positie.lon;*/ return positie; } string Gps::getgga(void){ string s = gga; return s; } void Gps::simuleer(double koers, double afstand){ koers *= deg2rad; afstand /= 6371.0; double lat1 = positie.lat * deg2rad; double lon1 = positie.lon * deg2rad; double dlat = afstand * cos(koers); double lat2 = dlat + lat1; double df = log(tan(lat2/2.0 + PI/4.0)/tan(lat1/2.0 + PI/4.0)); double q; if (df == 0) q = cos(lat1); else q = dlat / df; double dlon = afstand * sin(koers) / q; double lon2 = fmod((lon1 + dlon + PI),(2.0 * PI)) - PI; positie.lat = lat2 * rad2deg; positie.lon = lon2 * rad2deg; }
kompas.h #ifndef KOMPAS_H #define KOMPAS_H #include "mbed.h" #include <math.h> #include "state.h" #include <string> #include <map>
#define ADDR 0x32 #define NZEROS 5
58
#define NPOLES 5 class Kompas{ I2C compass; int cal; int koers,prev; Ticker refresh; int opslag[10]; int filter_num;
// sda, scl
float xv[NZEROS+1], yv[NPOLES+1]; float GAIN; public: Kompas(); int get(void); void update(void); void startcal(void); void stopcal(void); void set_num(int n){filter_num = n;} int get_num(void){return filter_num;} };
#endif
kompas.cpp #include "kompas.h" Kompas::Kompas():compass(p9, p10) { koers = 0; prev = 0; cal = 0; state["filter_num"] = 1; wait(0.5); char compass_out[3]; compass_out[0] = 0xF1; compass_out[1] = 0x05; compass_out[1] = 0x02; compass.write(ADDR,compass_out,3); wait_ms(1); refresh.attach(this,&Kompas::update,0.1);
} void Kompas::update(void) { //wat variabeles om als zend en ontvang buffer te fungeren char compass_out[1]; char compass_in[6];
59
//als het kompas niet bezig met calibreren kan er gelezen worden if (cal == 0) { //zet 0x50(="sample heading") in zend buffer compass_out[0] = 0x50; //stuur deze buffer over de bus compass.write(ADDR,compass_out,1); //geef het kompas even de tijd om de opdracht uit te voeren wait_ms(1); //Daarna kunnen er 6 8bits waardes uitgelezen worden compass.read(ADDR,compass_in,6);
//Deze 6 8 bits waardes moeten omgerekend worden naar 3 16 bits waardes int compass_heading = (int)compass_in[0] << 8 | (int)compass_in[1]; state["pitch"] = ((int)compass_in[2] << 8 | (int)compass_in[3])/10.0; state["roll"] = (int)((int)compass_in[4] << 8 | (int)compass_in[5]) /10.0; if (state["roll"] > 180) state["roll"] -= 6553.6; if (state["pitch"] > 180) state["pitch"] -= 6553.6; int n = compass_heading / 10;
int verschil if (verschil verschil if (verschil verschil
= n - prev; > 180) -= 360; < -180) += 360;
if (abs(verschil) > 60) { //grote uitschieters filteren if (verschil > 0) prev += 20; else prev -= 20; } else { //butterworth if (state["filter_num"] == 1){ GAIN = 7.796778047e+02; xv[0] xv[3] xv[5] yv[0] yv[3] yv[5]
= = = = = =
xv[1]; xv[1] = xv[2]; xv[2] = xv[3]; xv[4]; xv[4] = xv[5]; n / GAIN; yv[1]; yv[1] = yv[2]; yv[2] = yv[3]; yv[4]; yv[4] = yv[5]; (xv[0] + xv[5]) + 5 * (xv[1] + xv[4]) + 10 * (xv[2] + xv[3]) + ( 0.1254306222 * yv[0]) + ( -0.8811300754 * yv[1]) + ( 2.5452528683 * yv[2]) + ( -3.8060181193 * yv[3]) + ( 2.9754221097 * yv[4]); koers = yv[5];
60
}else if (state["filter_num"] == 2){ GAIN = 4.557963942e+01; xv[0] xv[3] xv[5] yv[0] yv[3] yv[5]
xv[1]; xv[1] = xv[2]; xv[2] = xv[3]; xv[4]; xv[4] = xv[5]; n / GAIN; yv[1]; yv[1] = yv[2]; yv[2] = yv[3]; yv[4]; yv[4] = yv[5]; (xv[0] + xv[5]) + 5 * (xv[1] + xv[4]) + 10 * (xv[2] + xv[3]) + ( 0.0112635125 * yv[0]) + ( -0.1111638406 * yv[1]) + ( 0.3863565587 * yv[2]) + ( -0.9738493318 * yv[3]) + ( 0.9853252393 * yv[4]); koers = yv[5]; }else{ koers = n; } if (koers koers = if (koers koers =
= = = = = =
> 360) 360; < 0) 0;
}
} else koers =
0;
//update state state["koers"] = koers; } int Kompas::get(void) { return state["koers"]; }
void Kompas::startcal(void) { char compass_out[1]; if (cal == 0) { cal = 1; compass_out[0] = 0x71; compass.write(ADDR,compass_out,1); } } void Kompas::stopcal(void) { char compass_out[1]; if (cal == 1) { compass_out[0] = 0x7E; compass.write(ADDR,compass_out,1); cal = 0;
61
} }
vaantje.h #ifndef VAANTJE_H #define VAANTJE_H #include "state.h"
class Vaantje{ public: Vaantje(); int get(void); void set(int); }; #endif
vaantje.cpp #include "vaantje.h"
Vaantje::Vaantje(){ state["wind"] = 180; } int Vaantje::get(void){ return state["wind"]; } void Vaantje::set(int h){ state["wind"] = h; }
62
Uitvoer roer.h #ifndef ROER_H #define ROER_H #include "mbed.h" #include "state.h" //#include <string> //#include <map>
class Roer{ PwmOut led; PwmOut rudder; Ticker updater;
public: Roer(); void set(int); void write(void); }; #endif
roer.cpp #include "roer.h" Roer::Roer():led(LED2),rudder(p21) { state["roer"] = 50; updater.attach(this,&Roer::write,0.1); } void Roer::set(int a) { state["roer"] = a; this->write(); } void Roer::write(void){ if (state["roer"] state["roer"] if (state["roer"] state["roer"]
> = < =
76) 76; 5) 5;
led = state["roer"] / 100.0; rudder.pulsewidth(0.001 + state["roer"] / 100000.0); }
63
zeil.h #ifndef ZEIL_H #define ZEIL_H #include "vaantje.h" #include "mbed.h" #include "state.h" #include "pws.h" class Zeil{ PwmOut led; PwmOut sail; Ticker updater; public: Zeil(); void set(int); void write(void); }; #endif
zeil.cpp #include "zeil.h" Zeil::Zeil() : led(LED3),sail(p22) { state["zeil"] = 50; state["koers_sp"] = 50; updater.attach(this,&Zeil::write,0.1); } void Zeil::set(int a) { state["zeil"] = a; this->write(); }
void Zeil::write(void){ if (state["zeil_auto"]){ int s; if (state["opkruisen"]){ s = 35; } else{ double hoek = fabs(verschil((state["sp"] * (100 - state["koers_sp"]) + state["koers_sp"] * state["koers"]) / 100.0, state["wind"])); if (hoek <= 40) s = 35; else if (hoek > 40 && hoek <= 100) s = (hoek - 40) * (55.0/60.0) + 35;
64
else s = 90; } state["zeil"] = s; } if (state["zeil"] state["zeil"] if (state["zeil"] state["zeil"]
> = < =
90) 90; 35) 35;
led = state["zeil"] / 100.0; sail.pulsewidth(0.001 + state["zeil"] / 100000.0); }
65
Status state.h #ifndef STATE_HPP #define STATE_HPP #include <map> #include extern std::map<std::string, float> state;
#endif
66
Verwerking goto.h #ifndef GOTO_H #define GOTO_H #include #include #include #include #include #include #include
"pws.h" "gps_wrapper.h" "route.h" "vaantje.h" "kompas.h" "rhumb.h" <math.h>
#include "state.h" #include <string> #include <map> class Goto{ Gps * huidig; Route * doel; Vaantje * vaantje; Kompas * kompas; int int int int int
opkruisen; eerste; reset; rak; hys;
pos begin_opkruisen; pos begin_rak; double koers_begin; int boeg; int holdcourse; int course; int minhoek; public: Goto(Gps *, Route *, Kompas *, Vaantje *); double get(void); void setminhoek(int m){minhoek = m;} void sethys(int h){hys = h;} int getminhoek(void){return minhoek;} };
#endif
67
goto.cpp #include "goto.h" Goto::Goto(Gps * g, Route * r, Kompas * k, Vaantje * v){ huidig = g; doel = r; kompas = k; vaantje = v; opkruisen = 0; eerste = 1; reset = 0; rak = 0; state["holdcourse"] = 1; state["course"] = 0; state["minhoek"] = 35; state["breedte"] = 20;
hys = 5; }
double Goto::get(void){ if (state["holdcourse"]) return state["course"]; double k = koers(&huidig->get(),&doel->get()); double d = afstand(&huidig->get(),&doel->get());
//koers naar doel berekenen //afstand nar doel berekenen
state["cts"] = k; state["afstand"] = d;
double ware_wind = vaantje->get();
//ware wind uit vaantje halen
if (ware_wind > 360) ware_wind -= 360;
if (fabs(verschil(k,ware_wind)) < state["minhoek"] + hys){ //kijken of er moet worden opgekruis if (!opkruisen){ //kijken of er niet al opgekruist wordt begin_opkruisen = huidig->get(); //huidige positie opslaan als begin van het // opkruisen begin_rak = huidig->get(); //en als begin van het huidige rak koers_begin = k; //de koers van de layline opslaan eerste = 1; //installen dat het eerste rak is } else{ reset = 0; } opkruisen = 1; }
68
if (fabs(verschil(k,ware_wind)) > state["minhoek"] + hys){ opkruisen = 0; eerste = 0; }
//kijken of het al bezeild is
state["opkruisen"] = opkruisen; if (!opkruisen){ //als er niet opgekruist hoeft te worden return k; //direct naar het doel varen } else //anders: { double boeg1 = ware_wind + (double)state["minhoek"]; //twee koersen uitrekenen double boeg2 = ware_wind - (double)state["minhoek"]; if (boeg1 > 360) boeg1 -= 360; if (boeg2 < 0) boeg2 += 360;
double lengte = afstand(&begin_opkruisen, &doel->get()) * state["breedte"]/100.0; // uitrekenen double a = fabs(verschil(ware_wind,koers_begin)); double lengte1 = lengte / sin(((double)state["minhoek"]+a) * deg2rad); double lengte2 = lengte / sin(((double)state["minhoek"]-a) * deg2rad); if (eerste){ //eerste rak 2x zo kort lengte1 /= 2.0; lengte2 /= 2.0; //bij eerste rak kiezen welke koers het dichste bij de huidige licht if (fabs(verschil(boeg1,kompas->get())) < fabs(verschil(boeg2,kompas->get()))) boeg = 1; else boeg = 2; } if (boeg == 1){ if (afstand(&begin_rak,&huidig->get()) > lengte1){ //als het einde van //het rak bereikt is eerste = 0; boeg = 2; //ga overstag begin_rak = huidig->get(); } return boeg1; } if (boeg == 2){ if (afstand(&begin_rak,&huidig->get()) > lengte2){ eerste = 0; boeg = 1; begin_rak = huidig->get(); } return boeg2; } } }
69
//lengte v
pid.h #ifndef PID_H #define PID_H #include #include #include #include
"pws.h" "kompas.h" "goto.h" "roer.h"
#include "state.h" #include <string> #include <map>
class Pid{ /*working variables*/ float Input, Output, Setpoint; float ITerm, lastInput; float SampleTime; //0.1 sec float outMin, outMax; Goto * sp; Kompas * pv; Roer * output; Ticker updater; public: Pid (Goto *, Kompas *, Roer *);
void update(void); }; #endif
pid.cpp #include "pid.h" Pid::Pid(Goto * p, Kompas * k, Roer * r){ sp = p; pv = k; output = r; SampleTime = 0.1; //0.1 sec outMax = 50; outMin = -50; state["kp"] = 0; state["ki"] = 0; state["kd"] = 0; state["schaal"] = 200; state["pid_roll"] = 100; updater.attach(this,&Pid::update,0.1);
70
} void Pid::update(void){ float p,i,d; Setpoint = sp->get(); Input = pv->get();
//variabeles declaren die later gebruikt gaan worden //SP ophalen //Koers ophalen
state["sp"] = Setpoint; //sp opslaan op een plek waar andere processen dat ook kunnen zien if (state["roer_auto"]){ //als het roer autmoatisch bestuurd moet worden float error = verschil(Input,Setpoint); //afwijking uitrekenen float schaal = state["schaal"]; //Deelfactor voor de paramters uitlezen state["error"] = error; //afwijking opslaan ITerm += ((state["ki"] / schaal ) * SampleTime * error); //I term uitrekenen en //optellen bij vorige I if(ITerm > 25) ITerm = 25; else if(ITerm < -25) ITerm = -25;
//Zorgen dat de I niet te groot wordt
float dInput = verschil(lastInput,Input);
//Afgeleide berekenen
/*Compute PID Output*/ p = (state["kp"] / schaal ) * (error + state["roll"] * 0.01 * state["pid_roll"]); i = ITerm; //I uitrekenen d = - ( (state["kd"] / schaal ) / SampleTime) * dInput; //D uitrekenen Output = p + i + d; //Optellen if(Output > outMax) //Zorgen dat de uitput niet te groot wordt Output = outMax; else if(Output < outMin) Output = outMin;
lastInput = Input;
state["p"] = p; state["i"] = i; state["d"] = d;
//Huidig setpoint opslaan voor later
//P,I,D waardes opslaan
output->set( (int)Output + 50); //Roer instellen } }
rhumb.h #ifndef RHUMB_H #define RHUMB_H //#include "route.h"
71
//P uitre
#include "pws.h" double afstand(pos *, pos *); double koers(pos * , pos *);
#endif
rhumb.cpp #include "rhumb.h" #include <math.h>
double afstand(pos *cur, pos *tar){ double lat_cur_r = cur->lat * deg2rad; double lon_cur_r = cur->lon * deg2rad; double lat_tar_r = tar->lat * deg2rad; double lon_tar_r = tar->lon * deg2rad; double df = log(tan(lat_tar_r/2.0 + PI/4.0)/tan(lat_cur_r/2.0 + PI/4.0));
double dla = lat_tar_r - lat_cur_r; double dlo = lon_tar_r - lon_cur_r; double q; if (df == 0) q = cos(lat_cur_r); else q = dla / df;
double r = 6371.0;
double d = sqrt(pow(dla,2) + pow(q,2) * pow(dlo,2)) * r; return d;
} double koers(pos *cur, double lat_cur_r = double lon_cur_r = double lat_tar_r = double lon_tar_r =
pos *tar){ cur->lat * cur->lon * tar->lat * tar->lon *
deg2rad; deg2rad; deg2rad; deg2rad;
double df = log(tan(lat_tar_r/2.0 + PI/4.0)/tan(lat_cur_r/2.0 + PI/4.0));
double dla = lat_tar_r - lat_cur_r; double dlo = lon_tar_r - lon_cur_r;
72
double k = atan2(dlo,df) * rad2deg; if (k < 0) k += 360; return k;
}
route.h #ifndef ROUTE_H #define ROUTE_H #include #include #include #include
<string> <sstream>
#include #include #include #include #include
"pws.h" "split.h" "rhumb.h" "gps_wrapper.h" "state.h"
#include <map> using namespace std;
class Route{ vector<pos> waypoints; Gps * gps;
public: Route(Gps *); void update(std::vector<std::string>); string read(void); pos get(void); string lees(void); };
#endif
route.cpp #include "route.h" Route::Route(Gps * g){ state["loop"] = 0;
73
state["active"] = 0; state["near"] = 0.003; gps = g; pos tmp; tmp.lat = 1; tmp.lon = 2; waypoints.push_back(tmp); }
void Route::update(std::vector<std::string> positions){ vector<pos> nieuw; string line; pos tmp; int n;
for (n=1;n!=positions.size();n+=2){ tmp.lat = atof(positions[n].c_str()); tmp.lon = atof(positions[n+1].c_str()); nieuw.push_back(tmp); } waypoints = nieuw;
} pos Route::get (void){ if (state["active"] > waypoints.size()-1) state["active"] = waypoints.size() -1;
if (afstand(&gps->get(),&waypoints[state["active"]]) < state["near"]){ state["active"]++; if (state["active"] == waypoints.size() ){ if (state["loop"]) state["active"] = 0; else state["active"] = waypoints.size() -1; } } return waypoints[state["active"]];
} string Route::lees(void){ stringstream ss (stringstream::in | stringstream::out); string r;
74
int n; for (n=0;n!=waypoints.size();n++){ ss << waypoints[n].lat << " " << waypoints[n].lon << " "; } r = ss.str(); return r; }
75
Other pws.h #ifndef PWS_H #define PWS_H
#define PI 3.14159265 #define deg2rad PI / 180.0 #define rad2deg 180.0 / PI struct pos{ double lat; double lon; }; double verschil(double,double); #endif
pws.cpp #include "pws.h" double verschil(double van, double naar){ double error = naar - van; if (error error if (error error
> 180) -= 360; < -180) += 360;
return error; }
split.h #ifndef SPLIT_H #define SPLIT_H #include <string> #include #include <sstream>
std::vector<std::string> split(std::string); #endif
split.cpp #include "split.h"
std::vector<std::string> split(std::string str){ std::string buf;
76
std::stringstream ss(str); std::vector<std::string> tokens; while (ss >> buf) tokens.push_back(buf); return tokens; }
77