RobotLib Modules

Uit RobotMC.be
Ga naar: navigatie, zoeken
Categorie: RobotLib

RobotLib Demo's

Algemeen

Robotlib biedt een pak functionaliteit en vele handige features. Een deel hiervan is beschreven op deze wiki. Maar vaak is het handiger om gewoon te laten zien hoe je iets doet of bepaalde functionaliteit gebruikt. Hiervoor zijn de 'Demo' file. Deze vindt je terug op github.

Uitproberen

Uitproberen van een demo file is eenvoudig als je een werkend sample1 project hebt. Stel dat de DemoCommandLine.cpp wilt proberen, download het bestand, zet het in je projectdirectory en voeg het toe aan de project list in makefile.do:

createlist(project) {     
  "($PRJ_path)main.cpp"
  "($PRJ_path)Prj_Hal_motors.cpp"    
  "($RL_path)Stubs_Globals.cpp"
  "($PRJ_path)DemoCommandLine.cpp"
}

Compileren, uploaden en klaar.

Geef het commando 'demo' in het terminal programma om de specifieke code aan te roepen. En kijk ook zeker naar de broncode van de demo, want die is ruim van commentaar voorzien. Of beter nog hernoem het bestand (om verwarring te voorkomen) en wijzig deze naar eigen wens en inzicht.

Opbouw & werking

Een demo file bevat alle code die je in je projectfiles een plaats moet geven om de functionaliteit te gebruiken. Vaste onderdelen (voor zover relevant):

  • DECLARATIONS - prototypes van relevante classes en functies. Neem deze op in project.h om ze in je hele project toegankelijk te maken.
  • INSTANCES - classes en variabelen die we gaan gebruiken.
  • DefaultDemoSetup() - de functie waarin alles wordt ingesteld en geactiveerd.
  • CliCmd_DefaultDemo() - deze functie laat zien hoe je gebruik maakt van de geboden functionaliteit.

Mogelijk bevat de demo nog andere code. Dit zal steeds code zijn die - direct of indirect - wordt gebruikt bij de setup of voor het commando 'demo'.

De code in DefaultDemoSetup() wordt door het sample project aangeroepen. Neem de code over in je sample project. Als je de hele functie wilt hergebruiken, geef deze functie dan een andere naam. Ditzelfde geldt voor de functie CliCmd_DefaultDemo(). Er mogen immers geen twee dezelfde functies in een project voorkomen en deze namen zijn gereserveerd voor de demo's. En... door deze standaard naam kun je in principe maar 1 demo tegelijk activeren!

Meerdere demo's tegelijk

Demo's zijn bedoeld om snel te laten zien hoe je iets doet. Normaal activeer je 1 demo, kijkt hoe het werkt en neemt de relevante delen over in files van je project. Daarna heb je de demo niet meer nodig en kun je desgewenst een andere demo activeren.

Het is mogelijk om meerdere demo's tegelijk te activeren. Als eerste moet je dan zorgen dat de namen van de functies uniek zijn. Dit doe je door het volgende statement in project.h te plaatsen:

#define UNIQUE_DEMO_NAME 

In iedere demo wordt bovenaan de naam opgegeven, bijvoorbeeld

#define DEMO_NAME DemoTiming

Als je UNIQUE_DEMO_NAME hebt gedefinieerd, wordt de DEMO_NAME gebruikt in plaats van DefaultDemo. De namen van de functies worden in dit geval DemoTimingSetup() en CliCmd_DemoTiming(). Je kunt nu dus meerdere demo's opnemen in de bestandenlijst in makefile.do.

Demo's

DemoSharpSensor

Deze demo laat zien hoe je de sharp class kunt gebruiken om altijd de afstand in mm beschikbaar te hebben. Door gebruik van de ADC werkt demo deze (vooralsnog) alleen de STM32F4. De sharp class werkt wel op andere boards, zoals de STM32VL.

Bekijk DemoSharpSensor.cpp

DemoPCA9685

Deze demo laat zien hoe je de PCA9685, een PWM controller met I2C interface, gebruikt om Servo's aan te sturen. Tevens een demo van de Servo class.

Bekijk DemoPCA9685.cpp

DemoBH1750

De BH1750 is een lichtsensor met I2C interface. Deze demo laat zien hoe je 1 of 2 van deze sensoren uitleest.

Bekijk DemoBH1750.cpp

DemoSRF08

De SRF08 is een ultrasoonsensor met I2C interface. Deze demo laat zien hoe zo'n sensor uitleest, inclusief de LDR (lichtsensor).

Bekijk DemoSRF08.cpp

DemoPCF8574

De PCF8574 is i2c IO chip met 8 digitale in/uitgangen. Deze demo laat zien je de pinnen als ingang leest en als uitgang kunt aansturen.

Bekijk DemoPCF8574.cpp

DemoPCF8591

De PCF8591 is 4-kanaals 8-bit Analoog-Digitaal convertor plus een 1-kanaals 8-bit Digitaal-Analoog convertor. Deze demo laat zien je de analoge ingangen uitleest en hoe je de analoge uitgang aanstuurt.

Bekijk DemoPCF8591.cpp

DemoLineFollower

De LineFollower laat je robot een lijn volgen. Het is geen probleem als de lijn kort onderbroken is en als je de lijn kwijt bent, wordt deze weer gezocht. De LineFollower maakt gebruikt van een bestaande lijnsensor. De demo laat zien hoe je de LineFollower configureert en aanroept als standaard beweging (UmLineFollower).

Bekijk DemoLineFollower.cpp

DemoArIo

ArIo is een class waarmee je de IO lijnen van een Arduino kunt gebruiken. Het ondersteunt 12 digitale pinnen (input en output), 8 bit PWM op 6 van de digitale pinnen. De analoge pinnen kunnen gelezen worden met 10 bits resolutie. De Arduino wordt aangesloten via I2C. De firmware voor de Arduino (getest met een ATMEGA328) vind je onder 'Projects\Arduino\Ario.

Bekijk DemoArIo.cpp

DemoPointVector

Een punt (X, Y) en een vector (distance, length) zijn twee respresentaties van hetzelfde. Een vector werkt handig met sensoren en navigatie omdat deze relatief is. Punten zijn handig als navigeert in het assenstelsel en noodzakelijk om vectoren bij elkaar op te tellen. Technisch gezien zijn TPoint en TVector classes met (o.a.) operators. Hierdoor kun je rekenen met punten en vectoren alsof het gewone variabelen zijn. De demo laat zien hoe.

Bekijk DemoPointVector.cpp

DemoTaskFunction

Een TaskFunction wordt met een vaste interval aangeroepen, bijvoorbeeld om een sensor uit te lezen of iets te bewaken. Deze demo laat zien hoe je zo'n functie maakt.

Bekijk DemoTaskFunction.cpp

DemoTiming

Deze demo laat verschillende timing mogelijkheden van Robolib zien.

Bekijk DemoTiming.cpp

DemoStm32vldAdc

Deze demo laat zien hoe je de Analoog-Digitaal convertor (ADC) gebruikt op het STM32 Value Line discovery board. Het bevat ook een handig overzicht van de analoge pinnen van deze processor.

Bekijk DemoStm32vldAdc.cpp

DemoDebugPrint

Deze demo laat zien hoe je verschillende optionele print functies kunt gebruiken om te loggen. Handig om te (laten) zien wat er onder water gebeurt.

Bekijk DemoDebugPrint.cpp

DemoCommandLine

Deze demo laat zien hoe je zelf commando's kunt toevoegen aan de command line interface (het console).

Bekijk DemoCommandLine.cpp

DemoPfKeyFunction

Deze demo laat zien hoe je een functie maakt om acties te koppelen aan functietoetsen op de afstandsbediening.

Bekijk DemoPFKeyFunction.cpp

DemoLipoBattery

Laat zien hoe je de nieuwe LipoBattery class gebruikt om je batterij te bewaken.

Bekijk DemoLipoBattery.cpp

DemoMission

Geen uitgebreide demo, maar de belangrijkste code snippers, handig bij elkaar in 1 bestand. Zo gaat een missie porgrammeren nog sneller!

Bekijk DemoMissionSample.cpp

DemoMoverListMode

Deze demo laat zien hoe je een lijst van Universal Movements (bewegingen) maakt, die vervolgens aaneengesloten worden uitgevoerd.

Bekijk DemoMoverListMode.cpp

DemoMoverCallBack

Deze demo laat zien hoe je een UniversalMover uitbreidt met je eigen code, bijvoorbeeld om een wand te volgen of een obstakel te vermijden. Met name nuttig met de Mover in list mode. Patrick vindt de listmode ideaal voor 'rapid prototyping'. Op zijn verzoek is de call-back functie toegevoegd, om eenvoudig geavanceerd gedrag mogelijk te maken. De ervaringen gaan we ongetwijfeld teruglezen op PlankTrick

Bekijk DemoMoverCallBack.cpp

DemoIsr1msWedge

(todo)

Overige demo's

Bekijk DemoAx12Servo.cpp

Bekijk DemoIcp.cpp

Bekijk DemoJoyStickControl.cpp

Bekijk DemoLidarLite.cpp

Bekijk DemoLineSensorQTR8A_MAX127.cpp

Bekijk DemoMPU6050.cpp

Bekijk DemoPfKeyRepository.cpp

Bekijk DemoPid.cpp

Bekijk DemoPresentation.cpp

Bekijk DemoRegistry.cpp

Bekijk DemoRemoteControl.cpp

Bekijk DemoStm32f4EepromSim.cpp

Bekijk DemoVL53L0x.cpp

Bekijk DemoVL6180x.cpp

Bekijk DemoZK1.cpp

RobotLib Infrastructuur

Timing

delay()

De functie delay() is een blokkerende delay routine, gebaseerd op de HAL microseconde functie. De functie is bedoeld voor gebruik tijdens opstarten / initialisatie. Na het opstarten (in de hoofdlus) wordt afgeraden om blokkerende wachttijden te gebruiken.

delay(5);

millis()

De functie millis() geeft de tijd in miliseconden terug. Onderstaande code laat zien hoe je eens per miliseconde een actie kunt uitvoeren.

static int PrevMsCounter;
int Now = millis();
if (PrevMsCounter != Now) {
   PrevMsCounter = Now;
   // ------------------------	
   // eens per miliseconde   
   // ------------------------	
}

De functie micros() geeft de tijd in microseconden terug voor hoge-resolutie timing.

MsInterval

De class TMsInterval wordt gebruikt om een taak met vaste interval uit te voeren.

Om de class te kunnen gebruiken, moeten we eerst een 'instance' van deze class maken en deze een naam geven. Bij het aanmaken geven we de gewenst interval (in milliseconden) op als paramater:

TMsInterval  MainSecInterval(1000);

Bovenstaande regel maakt de instance 'MainSecInterval' aan, van het type TMsInterval, zoals 'int test;' een variabele (instance) maakt van het type int. Bij het aanmaken van MainSecInterval geven 1000 milliseconden mee als interval-tijd. Bovenstaande regel zetten we voor (buiten) de functie zetten die de instance gaat gebruiken.

Om te bepalen of de interval is verstreken, wordt de methode (functie) 'Due()' van deze class aangeroepen:

if (MainSecInterval.Due()) {
   // doe iets
}

Due() geeft 'true' terug als de tijd verstreken is en 'false' als het nog niet zo ver is. Als een interval is verstreken, start automatisch een volgende interval.

StopWatch

Met de StopWatch functies kan de tijd worden gemeten, bijvoorbeeld van de duur van de functie. De functie StopWatchReset() slaat de huidige tijd op en de functie StopWatchGet() geeft de tijd sinds de laatste StopWatchReset() terug, in microseconden. De functie StopWatchGet() kan meerdere keren achter elkaar worden aangeroepen en zal dan een steeds langere tijd teruggeven.

Let op: de StopWatch functies kunnen niet gebruikt worden voor meerdere, gelijktijdige tijdsmetingen omdat slechts één 'ResetTijd' wordt opgeslagen.

Let op: de maximale tijd die de StopWatch functie kan meten wordt bepaald door de HAL microseconde functie. Afhankelijk van de processor is dit een 16-bit of 32-bit teller. Bij een 16-bit teller is de maximale meettijd 65 miliseconden, bij een 32-bit teller ruim 70 minuten.

StopWatchReset();    // leg starttijd vast
// doe iets
j = StopWatchGet(); // huidige tijd - starttijd  
printf("loop kost %d microseconden (performance indicator).\n", j);

printf

Printf is de krachtige print-functie van C.

De ARM compiler maakt gebruik van newlib voor de standaard C functies. Omdat de printf() functie van newlib bijzonder veel geheugen gebruikt, maakt RobotLib gebruik van een alternatieve functie voor printf(). Deze functie is een stuk compacter is en biedt alle printf() functionaliteit, inclusief het printen van floats.

int i = 6 * 9;
printf("The meaning of life is 42 or %d\n", i);

Op het internet zijn veel tutorials te vinden over het gebruik van printf en format strings, bijvoobeeld hier

De functie HalCoreInit() zorgt ervoor dat de output van printf() naar de console uart wordt verstuurd.

Met behulp van de functie fprintf() kan naar andere 'devices' worden geprint, bijvoorbeeld een lcd scherm:

fprintf(TextDisplayPutChar, "RobotLib ready.");

De eerste parameter is een pointer naar de functies waar de karakters naar toe gestuurd moeten worden. Deze functie heeft char als parameter en return type 'void'

void TextDisplayPutChar(char c);

Let op:

  • Printf is niet 're-entrant'. Het gebruik van printf in een interrupt routine (naast gebruik in het hoofdprogramma) zal hierdoor niet altijd correct werken. Veel printen vanuit een interrupt routine kan ook onverwachte effecten hebben op de timing en wordt afgeraden. Enkele karakters printen met STDOUT_PUTCHAR('^') is een beter alternatief, als tenminste CUART_QUEUED is gedefineerd in Hal_*_config.h (zie HAL voor meer info).
  • Robotlib heeft geen eigen sprintf() (en vergelijkbare) functies. Bij gebruik van sprintf() zal extra code uit newlib worden meegelinkt (gebruikt ca 14k extra flash geheugen).
  • De alternatieve printf functie heet eigenlijk fformat(). Een marco definieert dat printf() wordt vertaald naar fformat()

newlib

De ARM compiler gebruikt de standaard newlib library. Deze library bevat alle standaard C en C++ functies. De Hal bevat (in syscalls.c) enkele functies die newlib nodig heeft om te functioneren.

CLI (Command Line Interface)

De RobotLib Command Line Interface (CLI) handelt opdrachten af die via de seriele poort (console) worden gegeven. De details vindt je hier.

Overigen

Debug klasses

RobotLib heeft ingebouwde logging. Deze logging is verdeeld in niveau's, te weten:

  • ERROR
  • WARNING
  • INFO
  • DEBUG

Per module kan worden ingesteld hoeveel logging gewenst is. Het laagste niveau (0) geeft alleen ERRORs, terwijl niveau 1 ERRORs en WARNINGs geeft. Niveau 2 voegt hier INFO aan toe en niveau drie geeft, met DEBUG erbij, alle beschikbare informatie per module.

Hieronder een overzicht van de robotlib modules met hun nummer:

#define NR_RLIB_DEBUG_CLASSES    80       // number of classes used by robotlib
#define RLM_USER               (100)      // first user class

// Robotlib Debug module nummers
#define RLM_RC5                  (1)      // RC decoder
#define RLM_RC                   (2)      // RC received code handling
#define RLM_CLI                  (3)      // command line interface
#define RLM_EENPARIG_VERTRAGEN   (4)
#define RLM_BATTERY              (5)

#define RLM_XY_VECTOR            (6)
#define RLM_POSITION             (7)      // read & process encoder info
#define RLM_PARAM_STORE          (9)
#define RLM_PID                  (10)
#define RLM_PID_TUNE             (11)
#define RLM_REGISTRY             (12)
#define RLM_CMD_REPOS            (13)

#define RLM_MOVER                (14)
#define RLM_UM_MOTORCONTROL      (15)
#define RLM_UM_MOTORCONTROL2     (16)
#define RLM_UM_CALL_HIERARCHY    (17)
#define RLM_UM_PWM               (18)
#define RLM_UM_SPEED_ROTATION    (19)
#define RLM_UM_SPEED_LR          (20)
#define RLM_UM_SPEED_HEADING     (21)
#define RLM_UM_DISTANCE_HEADING  (22)
#define RLM_UM_ROTATE            (23)
#define RLM_UM_ARC               (24)
#define RLM_UM_XY                (25)
#define RLM_UM_XY2               (26)     // end values
#define RLM_UM_XYPHI             (27)
#define RLM_UM_ACCURACY          (28)
#define RLM_MOTOR_SIM            (29)
#define RLM_UM_ARC2POSE          (30)
#define RLM_UM_LINE_FOLLOWER     (31)     // UmLineFollower + TLineFollower class
#define RLM_UM_FLOOR_REF_POINT   (32)     // UmFloorReferencePoint + TFloorReferencePoint class
#define RLM_UM_WAYPOINT          (33)     // UmWaypoint + TWaypoint class
#define RLM_SPEEDCURVE           (34)
#define RLM_PCA9685              (35)
#define RLM_SERVO                (36)
#define RLM_MISSION              (37)
#define RLM_SHARP                (38)
#define RLM_TASKS                (39)
#define RLM_KDU                  (40)
#define RLM_LCD                  (41)
#define RLM_RPLIDAR_COMMS        (42)
#define RLM_RPLIDAR_USE          (43)
#define RLM_ICP                  (44)
#define RLM_POINT_LIST           (45)
#define RLM_ARIO                 (46)
#define RLM_LINESENSOR           (47)
#define RLM_ESP                  (48)
#define RLM_AX12                 (49)
#define RLM_VL53L0X              (50)
#define RLM_VL6180X              (51)
#define RLM_MSGBROKER            (52)
#define RLM_GRID                 (53)
#define RLM_PRESENTATION         (54)
#define RLM_ROS                  (55)

#define RLM_NVMEMORY             (61)
#define RLM_MFS                  (62)
#define RLM_BLOCK_DEVICE         (63)
#define RLM_BASIC_SETUP          (64)
#define RLM_BASIC_RUN            (65)

(Let op: mogelijk is deze lijst niet bijgewerkt. Zie rlib_cpp.h in je distributie voor de meest actuele nummering.)

Modulenummers onder 100 zijn gereserveerd voor RobotLib. Nummers vanaf 100 kun je in je programma gebruiken.

Je kunt het niveau van een module verhogen via de commandline. De meeste modules staan standaard op 0, zodat alleen ERRORs getoond worden. Om te zien waar de module MOVER (op hoofdlijnen) mee bezig is, kan het niveau van deze module (nummer 14) op INFO (2) worden gezet met:

   dlevel 14 2 

Let op:

  • Log niet teveel. De bandbreedte van de seriele poort is beperkt (115k2 baud, ofwel zo'n 11kbyte/seconde) en bij veel logging zal dit de uitvoering van RobotLib vertragen, met allerlei foutmeldingen en onvoorspelbaar gedrag als gevolg.
  • Gebruik de broncode om de betekenis van specifieke debug print-statements te bepalen.
  • De demo DebugPrint laat zien hoe je zelf logging kunt gebruiken in je code.

RobotLib Integratie

Motor-aansturing

Motor-calibratie

RobotLib Positie

RobotLib Bewegen

Universal Mover

De 'Mover' stuurt de beweging van je robot en is daarom een belangrijke module. Je kunt de Mover opdracht geven een beweging uit te voeren en wachten tot deze meldt dat dit is afgerond. Maar indien nodig kun je de beweging ook bijsturen of afbreken.

Algemene eigenschappen Um bewegingen

  • Een beweging wordt gestart met een enkele aanroep van de Set-functie.
  • De beweging zorgt voor een geleidelijke opbouw/afbouw van de snelheid.
  • Bij een aantal bewegingen kan de 'eindsnelheid' opgegeven worden. De snelheid wordt afgebouwd tot deze snelheid als de robot het eindpunt nadert.
  • Als de opgegeven snelheid negatief is, rijdt de robot achteruit.
  • De beweging gaat door totdat een nieuwe Set-functie wordt aangeroepen of totdat het eindpunt is bereikt.
  • In 'Direct' mode voert de Mover de gevraagde beweging direct uit en meldt als deze is afgerond. Een nieuwe opdracht aan de Mover breekt de lopende beweging af en start direct met de nieuwe.
  • In 'Direct' mode kan de beweging bijgesteld worden met de Update functie.
  • In 'List' mode geef je een aantal bewegingen door aan de Mover. De Mover meldt als alle bewegingen zijn afgerond. In list mode kun je bijvoorbeeld in 1 x opdracht geven om de 8 bewegingen van het UMBMark 'vierkantje' uit te voeren.

Opmerkingen m.b.t. List mode:

  • List mode ondersteunt alleen bewegingen met een eindpunt.
  • Bijsturen van bewegingen (Update-functie) in List mode is mogelijk. Controleer hiervoor eerst of de Mover met de betreffende stap bezig is.
  • Afbreken van de lijst tijdens het uitvoeren is geen probleem. Afbreken van een specifieke beweging in de lijst en doorgaan met de volgende uit de lijst wordt niet ondersteund.

Voorbeeld

Neem bijvoorbeeld de beweging 'SpeedHeading', die wordt gebruikt om de robot met een bepaalde snelheid in een bepaalde richting te laten rijden. De functies zijn ondergebracht in de robotlib file 'UmSpeedHeading.cpp' en de belangrijkste functies daarin zijn:

  • UmSpeedHeading() - stelt de gewenste parameters in voor deze beweging en laad deze in de Mover.
  • UmSpeedHeadingUpdate() - wijzigt de parameters van de beweging terwijl deze actief is. Dit is niet voor alle bewegingen mogelijk en het wijzigen van parameters kan in sommige gevallen abrupte bewegingen van de robot veroorzaken.
  • UmSpeedHeadingTakt() - de interne functie die door Mover wordt aangeroepen om de beweging uit te voeren.

Een deel van de bewegingen, zoals UmYX, heeft een duidelijk eindpunt en dit wordt door Mover bijgehouden. Of een beweging is gereed is, kan worden opgevraagd met Mover.IsDone().

Een typische aanroep van een beweging (met eindpunt) in een statemachine ziet er als volgt uit:

case 6 :    // Rij naar 0, 0
      if (NewState) {
         // voor het eerst in deze state
         Mover.DirectMode();     // Set direct mode (UmSet commando's werken direct)
         UmXY(0, 0, 500, 0);  // X, Y, Speed, EndSpeed - alles in mm(/sec)
      }
      if (Mover.IsDone()) {      // Als de beweging klaar is
         State++;                // naar volgende state
      }
   break;

Ondersteunde bewegingen

De bewegingen zelf zijn geen onderdeel van de Mover, maar zijn sets van functies ondergebracht in separate bestanden. Hieronder een overzicht van de beschikbare bewegingen. Omdat de bewegingen geen onderdeel zijn van de Mover, kunnen nieuwe bewegingen (of alternatieven voor de beschikbare bewegingen) worden toegevoegd zonder aanpassing van de Mover.

UmStop

  • Stop de robot.
  • Eindpunt: ja (te gebruiken in direct en list mode)

UmPwm

  • Stuurt de Pwm van de beide motoren.
  • Eindpunt: nee (te gebruiken in direct mode en in list mode bruikbaar i.c.m. call-back functie)
  • Opmerking: werkt direct, geen geleidelijke opbouw van snelheid.

UmSpeedLR

  • Stuurt de snelheid van de beide moteren.
  • Eindpunt: nee (in list mode bruikbaar i.c.m. call-back functie)

UmSpeedHeading

  • Stuurt de snelheid en richting van de robot.
  • Eindpunt: nee (te gebruiken in direct mode en in list mode bruikbaar i.c.m. call-back functie)

UmSpeedHeading

  • Stuurt de snelheid en richting van de robot.
  • Eindpunt: nee (te gebruiken in direct mode en in list mode bruikbaar i.c.m. call-back functie)
  • Opmerking: Als bij aanvang de snelheid laag is, roteert de robot eerst naar de gevraagde richting. UmSpeedHeading is de basis voor veel andere bewegingen (bijvoorbeeld UmXY) en die bewegingen ondersteunen daardoor ook deze rotatie bij aanvang.

UmDistanceHeading

  • Rij de robot een bepaalde afstand in de opgegeven richting.
  • Eindpunt: ja (te gebruiken in direct en list mode)
  • UmDistanceHeadingRel is een variant die relatief werkt ten opzichte van de startheading.

UmRotate

  • Draait de robot in de gevraagde richting (zonder rijden)
  • Eindpunt: ja (te gebruiken in direct en list mode)
  • UmRotateRel is een variant die relatief werkt ten opzichte van de startheading.

UmArc

  • Rij een boog (met gegeven straal) tot de gevraagde richting
  • Eindpunt: ja (te gebruiken in direct en list mode)
  • UmArcRel is een variant die relatief werkt ten opzichte van de startheading.

UmXY

  • Rij naar het opgegeven punt
  • Eindpunt: ja (te gebruiken in direct en list mode)

UmXYPhi

  • Rij naar het opgegeven punt; zorg ervoor dat robot eindigt in de gevraagde rijrichting.
  • Eindpunt: ja (te gebruiken in direct en list mode)
  • Opmerking: de robot heeft soms extra ruimte nodig om in de gevraagde rijrichting te eindigen. Dit wordt bepaald door een generiek algorithme, wat met een tweetal parameters beinvloed kan worden.

UmXYPhi2

Rlib umxyphi2.jpg
  • Rij naar het opgegeven punt (x, y); zorg ervoor dat robot eindigt in de gevraagde rijrichting (degrees).
  • Eindpunt: ja (te gebruiken in direct en list mode)
  • Opmerking: de robot rijdt een voorspelbaar traject naar het doel: in een rechte lijn naar een circel (met straal 'Radius'), vervolgens langs de circel naar een hulppunt (op 'Distance' voor het doel, in de gevraagde rijrichting) en dan in de gevraagde rijrichting naar het doel. Let op dat je niet binnen de circel start, want werkt het niet. Als 'fallback' rijdt de robot het hulppunt, draait naar doel en rijdt het laatste stuk.

UmRobotPosition

  • Geen beweging van de robot, maar aanpassing van de positie.
  • Eindpunt: ja (te gebruiken in direct en list mode)
  • Opmerking: Um-implementatie van Position.CorrectPose(). Een virtuele teleportatie die handig voor testen in list mode, met simulator of met de robot als de wielen van de vloer zijn. In andere situaties is het beter om de Postion.CorrectPose() methode direct aan te roepen.

Samenhang

Bovenstaand overzicht bevat een aantal eenvoudige bewegingen zoals SpeedHeading, waarbij de robot in een bepaalde richting rijdt. Maar ook complexe beweging zoals UmXYPhi2, die de robot naar een bepaald punt laat rijden en dan via een boog naar het gewenste eindpunt. Om deze bewegingen te maken, maakt UmXYPhi van andere bewegingen. Onderstaande figuur geeft een overzicht van de samenhang tussen de bewegingen. RL UniversalMovements.jpg

HAL Core modules

Seriele poort

Timing

HAL Motor-aansturing

SharpSensor module

Sharp levert diverse sensoren om afstand te meten door middel van driehoeksmeting. De Robotlib module ondersteunt de analoge varianten, zoals de GP2Y0A21YK0F, met een bereik van 10 tot 80 cm.

Hardware en HAL

Sluit de Sharp sensor aan op een analoge ingang. De uitgangsspaning van een Sharp sensor kan iets hoger worden dan de analoge ingang kan verwerken. Met een spanningsdeler van 220E/1k breng je de spanning terug naar een veilig niveau (zie dit voorbeeld).

Let op dat de Sluit de servo aan op de robot. De voedingsspanning van de servo bij voorkeur gescheiden houden van de voeding van je controller en je sensoren, want zeker de zwaardere servo's gebruiken veel stroom als ze belast worden en kunnen daardoor de rest van de electronica verstoren.

Setup

Demo SharpSensor laat zien hoe je SharpSensors toevoegt aan je programma.

Deze demo geeft drie werkende sensoren en kunnen we deze uitlezen vanaf de commandline:

Planck> sharp list
SharpList
 Sharp 0 SharpL (434 mm)
 Sharp 1 SharpM (752 mm)
 Sharp 2 SharpR (289 mm)
Done.
Planck>
Planck> sharp print 0
SharpL Distance: 233, Raw: 963, A: 319215, B: -98, TC: 3
Planck> 
  • Het commando 'sharp list' geeft een overzicht van de beschikbare Sharp sensoren. De sensoren zijn genummerd en deze nummers kun je gebruiken voor de andere sharp-commando's via de commandline.
  • Het commando 'sharp print 0' geeft de details van een specifieke Sharp sensor. In dit geval voor Sharp 0 (wat, zoals we gezien hebben met sharp list, SharpL is). Buiten de afstand (distance), wordt ook de waarde gegeven die de ADC teruggeeft (raw) en de calibratie-waarden A en B (waarover later meer). TC is de ingestelde tijdsconstante.

De SharpSensor class bevat een laagdoorlaat-filter om ruis te onderdrukken en TC is de tijdconstantie van dit filter. De sensor geeft eens in de 40ms een nieuwe uitgangswaarde en de waarde 3 in ons voorbeeld geeft aan dat 2^3 = 8 samples nodig zijn om 63.2% van deze waarde over te nemen. Als we de class iedere miliseconde aanroepen, is dit 8 miliseconden. In 3x deze tijd (24ms) is 95% van het verschil verwerkt, goed in verhouding tot de 40ms conversietijd.

Calibratie

De uitgangsspanning van de Sharp sensoren varieert met de afstand, maar de relatie tussen spanning en afstand is verre van linear. Omdat afstanden in mm meer betekenis voor ons hebben dan de uitgangsspanning in mV en omdat regellussen beter werken met een linear signaal, rekent de SharpSensor class de ingangsppanning om naar mm. Deze conversie moet vooraf worden gecalibrareerd En dit gaat als volgt:

1. Kies twee afstanden waarop je de sensor wilt calibreren. Deze afstanden moeten niet te dicht bij elkaar liggen, maar ook niet op de randen van het meetbereik van de sensor. En bij voorkeur ook nog in de buurt van het bereik waar je de sensor voornamelijk wilt gebruiken. Een compromis dus. Voor een 10-80cm sensor kies je bijvoorbeeld 150 en 300 of 400mm.

2. Plaats een goed (infrarood) reflecterend object recht voor de sensor, op de eerste afstand.

3. Roep de calibratie-routine voor deze sensor aan met de afstand (100mm):

Planck> sharp cal 0 100
Calibrate SharpL: data opgeslagen, berekening (nog) niet mogelijk.
Planck>

De routine meldt dat het calibratie-punt is opgeslagen. Voor calibratie zijn twee (verschillende) punten nodig, dus de calibratie kan nog niet worden berekend.

4. Plaats nu het object op de tweede afstand (250mm) en roep de calibratieroutine aan:

Planck> sharp cal 0 250
Calibratie-data: SharpL : A = 107787.554688, B = -32.906982 (Let op: calibratie tijdelijk ingesteld. Maak calibratie desgewenst permanent in sourcecode.)
Planck>

De calibratie zijn berekend op resp. 107788 en -33. De waarden zijn (tijdelijk) ingesteld.

5. Je kunt de calibratie controleren door het object op verschillende afstanden te plaatsen en de sensor uit te lezen:

Planck> sharp list
SharpList
 Sharp 0 SharpL (247 mm)
 Sharp 1 SharpM (147 mm)
 Sharp 2 SharpR (273 mm)
Done.
Planck> sharp list  
SharpList
 Sharp 0 SharpL (103 mm)
 Sharp 1 SharpM (142 mm)
 Sharp 2 SharpR (273 mm)
Done.
Planck> 

Mocht de afwijking te groot zijn, herhaal dan de procedure. Tevreden? Dan gaan we naar de laatste stap.

6. Voeg de calibratie toe in de sourcecode.

//--------------------------------------------------------------------------
// Sharp setup
SharpL.CalibrationA = 107788;  
SharpL.CalibrationB = -33;  

De calibratie-routine geeft de instellingen door aan de SharpSensor, maar bij reset van de robot zijn deze weer weg. Door de calibratie op te nemen in de sourcecode, maak je de calibratie permanent.

Commando's

Dit zijn de commando's die beschikbaar zijn voor de sharp sensoren:

sharp list                - list all sharps (with distance info)
sharp cal   <nr> <mm>     - collect calibration info for sharp <nr>
sharp tc    <nr> <tc>     - set time-constant for sharp <nr>
sharp ab    <nr> <a><b>   - set calibration value A en B for sharp <nr>
sharp print <nr>          - print all info of sharp <nr>

De meeste hebben we al gezien. 'sharptc' wordt gebruikt om de tijdconstante aan te passen. Met 'sharpab' kun je de parameters A en B direct invoeren.

Servo module

Als je dit leest, weet je vast wat servo's zijn ;). Kleine modules met een uitgaande as die je in verschillende standen kunt zetten. Servo's hebben een 3-pin connector voor ground, 5-6V voeding en een stuur-signaal. Dit stuursignaal, een puls tussen 500 en 2500 microseconden die iedere 20 miliseconden herhaald wordt, bepaalt de stand van de servo.

Hardware en HAL

Sluit de servo aan op de robot. De voedingsspanning van de servo bij voorkeur gescheiden houden van de voeding van je controller en je sensoren, want zeker de zwaardere servo's gebruiken veel stroom als ze belast worden en kunnen daardoor de rest van de electronica verstoren.

De functie HalServoSet() van de STM32F4 HAL kan op 4 pinnen (PE9, PE11, PE13 en PE14) een servosignaal maken. De HAL van de andere processoren heeft (nog) geen HalServoSet() functie. Maar... niet getreurd. Robotlib heeft ook functies om de PCA9685 aan te sturen, een chip die via i2c wordt aangestuurd en 16 servo (pwm) outputs heeft. Adafruit biedt een module met de PCA9685 en 16 servo-connectoren aan voor $15 en in het verre oosten zijn dit soort modules voor de helft van dit bedrag verkrijgbaar.

Setup

We gaan de hierboven genoemde functies niet direct gebruiken, maar via TServo, de servo class van robotlib. De eerste stap hierbij is de declaratie van de servo's die we gaan gebruiken. Hieronder een voorbeeld van drie servo's van een grijper. We delcareren deze, bijvoorbeeld bovenin in main.cpp, als volgt:

TServo ServoGripL("GripL", PCA9685_ServoSet, 2);
TServo ServoGripR("GripR", PCA9685_ServoSet, 1);
TServo ServoGripLift("GripLift", PCA9685_ServoSet, 0);

Iedere regel begint TServo, de naam van de servo-class. Vervolgens geven we, net als bij een gewone variabele, een naam aan de class instance. Daarachter volgen 3 parameters:

  • De eerste parameter is de naam van de servo, die voor logging gebruikt wordt. Het is verstandig deze (nagenoeg) gelijk te houden aan de naam van de class instance.
  • De tweede parameter is de naam van de functie, waarmee de servo wordt aangestuurd. In dit geval gebruiken we de ServoSet() functie van de PCA9685, maar op een STM32F4 kan dit ook de functie HalServoSet zijn.
  • De derde parameter geeft aan welke uitgang gebruikt wordt. De waarde is afhankelijk van de gebruikte ServoSet functie. Voor PCA9685_ServoSet() zijn waarden van 0 tot en met 15 geldig.

Nu we de servo's hebben, hoeven we alleen nog maar signaal-pinnen te initiaiseren. Dit doen we in de functie main, ergens voor de hoofdlus, met de volgende aanroep:

PCA9685_ServoInit();

De STM32F4 HAL heeft een vergelijkbare functie, HalServoInit().

We kunnen de servo's nu gebruiken in main.cpp. Om ze ook bekend te stellen in de andere files van ons project, zetten we het volgende in project.h:

extern TServo   ServoGripL;
extern TServo   ServoGripR;
extern TServo   ServoGripLift;

Met bovenstaande statements geven we aan dat ergens anders (extern) de instances ServoGripL, ServoGripR en ServoGripLift van het type TServo zijn aangemaakt. Als je van compacte code houdt, kun je ook in 1x alle drie de instances bekend stellen:

extern TServo   ServoGripL, ServoGripR, ServoGripLift;

Tot slot melden we de servo's aan, zodat ze met vaste interval (MAIN_TAKT) worden aangeroepen:

MainTasks.Add(FP_FNAME(ServoGripL));
MainTasks.Add(FP_FNAME(ServoGripR));
MainTasks.Add(FP_FNAME(ServoGripLift));

We hebben nu drie werkende servo's en kunnen we deze bedienen vanaf de commandline:

Planck> servolist
ServoList
Servo 0 GripL
Servo 1 GripR
Servo 2 GripLift
Done.

Planck> servoraw 0 1600
Tservo::Raw GripL Value: 1600, setpoint: 1600, output: 1600, Enable: 0
GripL Channel: 2, a: 10.000000, b: 500.000000, Min: 500, Max: 2500, Step: 2500, Setpoint: 1600, Enable: 0

Planck> servoenable 0 1
GripL Channel: 2, a: 10.000000, b: 500.000000, Min: 500, Max: 2500, Step: 2500, Setpoint: 1600, Enable: 1
Planck>
  • Het commando 'servolist' geeft een overzicht van de beschikbare servo's. De servo's zijn genummerd en deze nummers kun je gebruiken voor de andere servo-commando's via de commandline.
  • Het commando 'servoraw' zet de ruwe waarde (tijd in microseconden) voor een bepaalde servo. In dit geval voor servo 0 (wat, zoals we gezien hebben met servolist, de servo GripL is).
  • Met het commando 'servoenable' kunnen we servo's in- en uitschakelen. Ook hier is de eerste parameter het servo-nummer. Als de tweede parameter 1 is, wordt de servo ingeschakeld terwijl 0 de servo uitschakelt.

In bovenstaande logging zien we channelnummer 2 terug, dit is de waarde die we hebben opgegeven bij de declaratie. Ook wordt ook aangegeven wat de minimale en maximale waarde van deze servo is. Robotlib blokeert waarden die buiten dit bereik liggen, om te voorkomen dat de servo tegen het mechanisch eindpunt wordt gestuurd, met hoog stroomgebruik en mogelijk schade als gevolg. Uiteraard kunnen we deze begrensing ook strenger zetten als dat ons uitkomt.

De grenswaarden van de servo kunnen we opzoeken door de waarden van de servo met het commando servoraw langzaam naar de uitersten te brengen (resp. 500 en 2500) totdat de servo niet meer verder draait.

Planck> servomax 0 3000
GripL Channel: 2, a: 10.000000, b: 500.000000, Min: 500, Max: 3000, Step: 2500, Setpoint: 1500, Enable: 0
Planck> servoraw 0 2200
GripL Channel: 2, a: 10.000000, b: 500.000000, Min: 500, Max: 3000, Step: 2500, Setpoint: 2200, Enable: 0
Planck> servorenable 0 1
GripL Channel: 2, a: 10.000000, b: 500.000000, Min: 500, Max: 3000, Step: 2500, Setpoint: 2200, Enable: 1
Planck> servoraw 0 2300
GripL Channel: 2, a: 10.000000, b: 500.000000, Min: 500, Max: 3000, Step: 2500, Setpoint: 2300, Enable: 1
Planck> servoraw 0 2400
GripL Channel: 2, a: 10.000000, b: 500.000000, Min: 500, Max: 3000, Step: 2500, Setpoint: 2400, Enable: 1
Planck> servoraw 0 2500
GripL Channel: 2, a: 10.000000, b: 500.000000, Min: 500, Max: 3000, Step: 2500, Setpoint: 2500, Enable: 1
Planck> servoraw 0 2600
GripL Channel: 2, a: 10.000000, b: 500.000000, Min: 500, Max: 3000, Step: 2500, Setpoint: 2600, Enable: 1
Planck> servoraw 0 2700
GripL Channel: 2, a: 10.000000, b: 500.000000, Min: 500, Max: 3000, Step: 2500, Setpoint: 2700, Enable: 1
Planck> servoraw 0 2600
GripL Channel: 2, a: 10.000000, b: 500.000000, Min: 500, Max: 3000, Step: 2500, Setpoint: 2600, Enable: 1
Planck> 

In bovenstaand voorbeeld wordt eerst het maximum van servo 0 omhoog gezet met 'servomax 0 3000', wordt een redelijke startwaarde gekozen (servoraw 2200) en de servo ingeschakeld (servoenable 0 1).

Vervolgens wordt stapje voor stapje de grens van de servo opgezocht. Voor deze servo blijkt die te liggen tussen 2600 en 2700. Dit blijkt ook zo te zijn voor servo GripR en de minimale waarde ligt tussen 500 en 600. We stellen deze waarde voortaan vast in bij de setup van de servo in de main functie met:

PCA9685_ServoInit();
ServoGripL.Min = 600;
ServoGripL.Max = 2600;
ServoGripL.Enable(true);
ServoGripR.Min = 600;
ServoGripR.Max = 2600;
ServoGripR.Enable(false);

Met deze instellingen is de servo beschermd tegen verkeerde aansturing. We kunnen hier meteen de servo inschakelen (zie ServoGripL.Enable(true) of uitschakelen (ServoGripR.Enable(false)).

De bescherming kunnen we controleren door de 'raw' waarde buiten het bereik op te geven. Niet de opgegeven waarde, maar het minimum of maximum zal dan als setpoint worden gebruikt:

Planck> servoraw 0 0
GripL Channel: 2, a: 10.000000, b: 500.000000, Min: 600, Max: 2600, Step: 2500, Setpoint: 600, Enable: 1

Grote stappen, snel thuis

Zodra je een nieuwe waarde opgeeft voor een servo, zal deze met de maximale snelheid naar de gevraagde positie toedraaien. Hoe snel dit is, hangt af van het type servo en de voedingsspanning. Een standaard servo draait 60 graden in 0.2 seconden. Vaak is dit ook de bedoeling, maar soms is dit te snel.

In deze gevallen kun je gebruik maken van de 'Step'. Dit property, net als Min, Max en Raw een getal in microseconden, geeft de maximale stapgrootte van de servo en beperkt de snelheid waarmee het servo signaal verandert. Stel bijvoorbeeld de volgende situatie voor:

  • De minimale puls voor een bepaalde servo is 600 us.
  • De maximale puls voor deze servo is 2400 us en de servo is dan 180 graden gedraaid ten opzichte van het minimum.
  • Hieruit volgt (voor deze servo!) dat 1800 us verschil een draai van 180 graden geeft, ofwel 10 us per graad.
  • Als de main takt van je robot is ingesteld op 20ms heb je iedere seconde 1000 / 20 = 50 stapjes

Zet je nu Step op 15, dan draait de servo 15 / 10 = 1.5 graden per takt ofwel 1.5 * 50 = 75 graden per seconde. De servo draait nu een stuk langzamer, wat vooral van belang is als je grotere of zwaardere dingen beweegt met je servo.

Graden

Hierboven is beschreven hoe we de servo class kunnen gebruiken in 'raw' mode (microseconden). De servo class kan daarbij de sturing binnen veilige grenzen houden en desgewenst de draaisnelheid beperken. Maar er is meer!

In de werkelijke wereld werken we met de servo-stand in graden en niet in microseconden. Steeds omrekenen van graden naar microseconden is niet handig. En het wordt nog vervelender als we onze servo gaan vervangen, want dit betekent in negen van de tien gevallen dat de waarden in microseconden ook veranderen. We kunnen dit voorkomen door de servo class te vertellen wat het aantal microseconden per graad is voor onze servo, en wat we 0 graden noemen. Een stappenplannetje:

  • Geef de servo een bepaald (raw) stuursignaal, bijvoorbeeld 1000 microseconden.
  • Bepaal de stand van de servo.
  • Pas het stuursignaal van de servo aan tot deze precies 90 graden is gedraaid.
  • Bereken het verschil in stuursignaal, doe dit * 10 en deel het door de hoek (90).
  • Dit noemen we getal A.
  • Pas het stuursignaal van de servo aan tot deze op '0 graden' staat. Wat dit is mag je zelf bepalen en kan bijvoorbeeld 'dicht' zijn voor een grijper, horizontaal voor een arm of rechtuit voor een sensor.
  • Dit getal noemen we B.
Planck> servoenable 0 1
GripL Channel: 2, A: 100, B: 500, Min: 600, Max: 2600, Step: 0, Setpoint: 1500, Enable: 1

Planck> servoraw 0 1000
GripL Channel: 2, A: 100, B: 500, Min: 600, Max: 2600, Step: 0, Setpoint: 1000, Enable: 1

Planck> servoraw 0 990
GripL Channel: 2, A: 100, B: 500, Min: 600, Max: 2600, Step: 0, Setpoint: 990, Enable: 1

Planck> servoraw 0 980
GripL Channel: 2, A: 100, B: 500, Min: 600, Max: 2600, Step: 0, Setpoint: 980, Enable: 1

Planck> servoraw 0 970
GripL Channel: 2, A: 100, B: 500, Min: 600, Max: 2600, Step: 0, Setpoint: 970, Enable: 1

Planck> servoraw 0 980
GripL Channel: 2, A: 100, B: 500, Min: 600, Max: 2600, Step: 0, Setpoint: 980, Enable: 1
Planck> servoraw 0 1900
GripL Channel: 2, A: 100, B: 500, Min: 600, Max: 2600, Step: 0, Setpoint: 1900, Enable: 1

Planck> servoraw 0 1910
GripL Channel: 2, A: 100, B: 500, Min: 600, Max: 2600, Step: 0, Setpoint: 1910, Enable: 1

Planck> servoraw 0 1920
GripL Channel: 2, A: 100, B: 500, Min: 600, Max: 2600, Step: 0, Setpoint: 1920, Enable: 1

Planck> servoraw 0 1910
GripL Channel: 2, A: 100, B: 500, Min: 600, Max: 2600, Step: 0, Setpoint: 1910, Enable: 1
Planck> servoab 0 103 980

GripL Channel: 2, A: 103, B: 980, Min: 600, Max: 2600, Step: 0, Setpoint: 2600, Enable: 1
Planck> servohoek 0 0

GripL Channel: 2, A: 103, B: 980, Min: 600, Max: 2600, Step: 0, Setpoint: 980, Enable: 1
Planck> servohoek 0 90

GripL Channel: 2, A: 103, B: 980, Min: 600, Max: 2600, Step: 0, Setpoint: 1907, Enable: 1
Planck> 

In bovenstaand voorbeeld wordt na enig zoeken bepaald dat het eerste punt op 980 zit en het tweede punt (90 graden verder) op 1910. De waarde van A wordt (1910 - 980) * 10 / 90 = 103. Het eerste punt is het (zelfgekozen) nulpunt, dus B wordt ingesteld op 980.

Nadat de getallen A en B zijn ingesteld met het commando 'servoab A B', wordt de werking gecontroleren met het commando 'servohoek HOEK'. De draairichting kun je desgewenst omdraaien met een min-teken voor getal A.

Commando's

Dit zijn de commando's die beschikbaar zijn voor servo's:

servolist                - List registered servo's
servohoek  <nr> <value>  - Set hoek of Servo <nr> to <valu>
servoraw   <nr> <value>  - Set raw of Servo <nr> to <valu>
servoenable <nr> <1/0>   - Enable/Disable Servo <nr>
servomin   <nr> <min>    - Set <min> of Servo <nr>
servomax   <nr> <max>    - Set <max> of Servo <nr>
servostep  <nr> <step>   - Set <step> of Servo <nr>
servoab    <nr> <A> <B>  - Set A & B of Servo <nr>
servoprint <nr>          - Print Servo <nr> details

Code

We hebben de servo geconfigureerd en gebruikt via de command-line. Dat gaat handig en snel, maar uiteindelijk willen we de servo waarschijnlijk vanuit ons programma aansturen. Gelukkig zijn de namen, properties en parameters van de class nagenoeg gelijk aan de gebruikte commando's. Wel gebruiken deze namen hoofdletters en omdat je met de naam al aangeeft welke servo je bedoelt, hoef je geen servo-nummer op te geven. Hieronder een voorbeeld.

ServoGripL.Min  = 600;
ServoGripL.Max  = 2600;      
ServoGripL.A    = 103;
ServoGripL.B    = 980;    
ServoGripL.Step = 20;    
ServoGripL.Hoek(0);
ServoGripL.Enable(true);

Eigen Takt-taak

Robotlib heeft lijstjes met taken, die het periodiek uitvoert. Voor intelligente sensoren en besturing is dit vaak iedere 20 of 50ms (MAIN_TAKT_INTERVAL) terwijl voor sensoren met een analoge ingang (zoals Sharp sensoren) iedere miliseconde worden aangeroepen. Als je zelf routines gaat maken, bijvoorbeeld om een sensor uit te lezen, kun deze ook aanmelden zodat ze periodiek worden aangeroepen. Dit kan op twee manieren: we kunnen een functie aanmelden als taak en we kunnen een class aanmelden.

Functie

Het eerste wat we doen, is een functie maken. Als voorbeeld een functie om een aantal ADC ingangen te lezen (op een STM32VL) en deze in een array te plaatsen:

void LijnSensorTakt()
{  
   // lees 9 ingangen van lijnsensor
   LineSensorValue[0] = HalAdcRead(ADC_Channel_1);
   LineSensorValue[1] = HalAdcRead(ADC_Channel_2);
   LineSensorValue[2] = HalAdcRead(ADC_Channel_3);
   LineSensorValue[3] = HalAdcRead(ADC_Channel_4);
   LineSensorValue[4] = HalAdcRead(ADC_Channel_5);
   LineSensorValue[5] = HalAdcRead(ADC_Channel_6);
   LineSensorValue[6] = HalAdcRead(ADC_Channel_7);
   LineSensorValue[7] = HalAdcRead(ADC_Channel_8);
   LineSensorValue[8] = HalAdcRead(ADC_Channel_9);
}

Voor deze code is het nodig dat we de array LineSensorValue declareren. Dit doen we bovenin de file en daar zetten we meteen het prototype van de functie neer:

int LineSensorValue[10];
void LijnSensorTakt();

Voor het aanmelden is het type functie is belangrijk: geen parameters en geen (void) return. Zoals onze functie. De functie melden we als volgt aan:

MsTasks.Add(FP_FNAME(LijnSensorTakt));

Na aanmelden zal de functie automatisch aangeroepen worden.

Class

Hierboven hebben we gezien hoe we een functie kunnen toevoegen aan een takenlijst. Maar het is ook mogelijk om een class te maken en die toe te voegen aan een takenlijst. Als je zelf een class hebt gemaakt, zijn dit de stappen om deze als taak aan te melden:

1. Laat je class erven van TTaktObject

class TMySensor: public TTaktObject

2. Maak een methode 'void TMySensor::Takt()' met daarin de code die periodiek uitgevoerd moet worden.

3. Instantieer de class:

TMySensor MijnSensor;

4. Meld de instantie aan:

  MsTasks.Add(FP_FNAME(MijnSensor));

Taakbeheer

Robotlib geeft je de gereedschappen om (bijvoorbeeld) met state machines) en 'cooperative multitasking' systeem te bouwen. Dit betekent dat robotlib ervoor zorgt dat iedere taak aangeroepen wordt als dat nodig is. Daar staat tegenover dat van iedere taak wordt verwacht dat het niet te lang bezig is, dat het samenwerkt met de rest.

Soms is het nodig om te controleren of dit inderdaad goed gaat. Hieronder staat een aantal hulpmiddelen die robotlib biedt.

Tasks menu

Robotlib kent standaard 3 taaklijsten: miliseconden, main en idle. In deze lijsten kunnen taken worden geplaatst. Via het 'task' menu kan meer informatie over de taken worden opgevraagd.

SimBot> ? task
Tasks info
  task list               - List all tasks (with cpu load).
  task interval      <ms> - Set stat interval.
  task dump               - Dump all tasks details.
SimBot> task list

Het commando 'task list' print beknopte gegevens van alle taken die geregistreerd zijn:

SimBot> task list
TaktList MiliSec_tasks (996/sec)
   0 BuzzerTakt                      0.1 %
TaktList Main_tasks (20/sec)
   5 Cli_Takt                        0.0 %
  10 Position                        0.0 %
  30 Mission                         0.0 %
  60 Mover                           0.0 %
  90 Presentation                    0.0 %
  99 IntervalGuardTakt               0.0 %
TaktList Idle_tasks (assume 5000/sec)
   0 RcDispatchTakt                  0.7 %
   0 Kdu                             2.7 %
   0 Rc5DebugTakt                    0.3 %
   0 Cli_Takt                        0.8 %
   0 TextLcdQueueTakt                0.9 %
  99 IdleLoop                        0.5 %
Cpu load: 5.5 %
SimBot> task dump

De taken zijn gegroepeerd per taaklijst en per taaklijst wordt (tussen haakjes) vermeld hoe vaak deze werd uitgevoerd in de afgelopen seconde. Indien een taak vaker dan 5000 keer per seconde wordt uitgevoerd - iets wat bij Idle_taken zeker mogelijk is - wordt 5000 als maximum aangenomen.

Per taak worden de volgende velden weergegeven:

  • Order - een waarde die is opgegeven bij het aanmelden van de taak en de plaats van de taak in de lijst bepaalt (zie ook 'task dump' hieronder).
  • Taaknaam - normaal de naam van de functie of class die uitgevoerd wordt.
  • CPU belasting over de laatste seconde

Soms is het nodig om meer details van taken te krijgen. Gebruik hiervoor het commando 'task dump'.

SimBot> task dump
TriggerDump flag set.
SimBot> Tasklist dump start.
TaktList MiliSec_tasks [avrg: 1004 vari: 7 n: 996]
   0 BuzzerTakt                  @53050650:2    [avrg: 1 vari: 0 n: 996]
TaktList Main_tasks [avrg: 50042 vari: 8256 n: 20]
   5 Cli_Takt                    @53027156:2    [avrg: 1 vari: 0 n: 20]
  10 Position                    @53027162:15   [avrg: 15 vari: 0 n: 20]
  30 Mission                     @53027183:1    [avrg: 1 vari: 0 n: 20]
  60 Mover                       @53027188:6    [avrg: 6 vari: 0 n: 20]
  90 Presentation                @53027200:1    [avrg: 1 vari: 0 n: 20]
  99 IntervalGuardTakt           @53027205:2    [avrg: 1 vari: 0 n: 20]
TaktList Idle_tasks [avrg: 42 vari: 6 n: 23286]
   0 RcDispatchTakt              @53051426:2    [avrg: 1 vari: 0 n: 23286]
   0 Kdu                         @53051432:6    [avrg: 5 vari: 0 n: 23286]
   0 Rc5DebugTakt                @53051401:1    [avrg: 0 vari: 0 n: 23286]
   0 Cli_Takt                    @53051407:1    [avrg: 1 vari: 0 n: 23286]
   0 TextLcdQueueTakt            @53051413:2    [avrg: 1 vari: 0 n: 23286]
  99 IdleLoop                    @53051419:1    [avrg: 0 vari: 0 n: 23286]
Tasklist dump end.

Per taak wordt de naam gegeven met daarvoor het 'order' nummer, de volgorde. Dit nummer bepaalt de volgorde waarin de taken van een lijst worden uitgevoerd. Dit is met name van belang voor taken in de 'main' lijst. Over het algemeen is de volgorde die je daar wilt hebben:

  • READ - lezen van sensoren, zodat je een actueel beeld hebt van de omgeving.
  • PROCESS - het uitvoeren van de missies, die op hoog niveau bepalen wat er moet gebeuren.
  • WRITE - het uitvoeren van de Mover, die de motoren aanstuurt op basis van de input van missies.

Taken met een lager nummer worden eerder uitgevoerd. Als twee taken hetzelfde nummer hebben, worden ze uitgevoerd in de volgorde dat ze zijn aangemeld. Het 'order' nummer is optioneel bij de aanmelding van de taak. Laat je het weg, dan is het nummer 0.

Naast het order nummer en de naam wordt een aantal kentallen gegeven:

@53050650:2

Dit geeft aan wanneer de de taak voor het laatst is gestart (53050650) en hoe lang de uitvoering heeft geduurd (2, beide waarden in microseconden).

[avrg: 1 vari: 0 n: 996]

Tussen vierkante haken worden statistische gegevens van de taak getoond:

  • avrg - de gemiddelde tijd die de uitvoering van de taak heeft geduurd (1). Een hoge gemiddelde tijd betekent dat de taak veel CPU tijd verbruikt. Dit is te verwachten bij taken die via i2c communiceren.
  • vari - de variantie - het kwadraat van de standaard deviatie van de tijd (1). Een hoge vari geeft aan dat de taak af en toe veel tijd vraagt, typisch voor taken die maar af en toe wat te doen hebben, zoals TextLcdQueueTC (die de karakters via i2c naar het lcd stuurt).
  • n - het aantal maal dat de taak is uitgevoerd (996).

Magic string

Ook de command line interpreter (CLI) is 1 van de taken en het kan dus voorkomen dat een taak zich niet goed gedraagt, waardoor de CLI het commando 'tasks' niet kan uitvoeren. In dit geval kun je de magic string '!@#$%' gebruiken. Als je deze ingeeft rapporteert iedere taaklijst of er een taak actief is, en zo ja, welke.

Magic String!
No task running at list MiliSec_taken
No task running at list Main_taken
Running task CliTC at list Idle_taken
Magic String done.

Normaal is 1 taak actief, soms geen. Als steeds dezelfde taak actief is, is de kans groot dat deze de andere taken blokkeert. In bovenstaand voorbeeld is de taak CliTC actief, een taak uit de lijst Idle_taken.

Opmerkingen:

  • De magic string zal alleen werken als de console uart gebufferd is (op basis van interrupts). Dit wordt ingesteld in prj_hal_conf.h met '#define CUART_QUEUED'
  • De magic string wordt herkend en afgehandeld in de interrupt routine en is daarmee een paardenmiddel, dat andere processen kan verstoren.

Configuratie

Een belangrijk deel van de configuratie van RobotLib gebeurt compile-time. Via menu's kun je diverse aanpassingen doen in bijvoorbeeld PID controllers, de repository en registry. Maar na een reset zijn deze instelling weer weg. Naast deze twee uitersten heeft robotlib nog een optie: gegevens opslaan in een eeprom met het MicroFilesysteem.

Sommige modules, zoals de repository, maken direct gebruik van het MicroFileSystem. Modules die een vaste configuratie hebben maken meestal gebruik van de Non Volatile module.

MicroFileSystem

Met het MicroFileSysteem kunnen in een eeprom (24lc256) configuratiegegevens opgeslagen worden.

RedBot> ? file
MicroFileSystem
  file dump                   - File system dump
  file format       <fcode>   - File system format
  file defrag        <commit> - Defrag file system (test/commit)
  file delete        <name>   - Delete file <name>
  file list                   - list files
  file truncate     <block>   - Truncate (repair) file system if damaged (0 = auto repair)
  file testfile      <name> <open_size> <data_size> - create file for test
  file type          <name>   - type (print) file <name>
  file backup        <name>   - backup file <name>
  file backupall              - backup all files

Voordat het filesysteem gebruikt kan worden, moet het geformatteerd worden:

RedBot> file format 1
FormatCode: 144

De parameter 1 dient als beveiling, zodat je niet per ongeluk alle files weggooit. Een alternatief voor 'are you sure?'. In bovenstaand voorbeeld is de format niet uitgevoerd, maar wordt de code 144 gegeven. Je kunt nu met 'format 144' de eeprom formatteren.

RedBot> file list
Mfs list
file: 'PID-MotorL' (c) - 12 bytes
file: 'PID-MotorR' (c) - 12 bytes
file: 'PID-SpeedHeading' (c) - 12 bytes
file: 'ReposDefault' (r) - 49 bytes
Done.

Het commando 'file list' geeft een overzicht van alle files. Toegang tot de file loopt via de naam. Daarnaast heeft iedere file een type (de letter tussen haakjes). Net als de file extensie op Windows geeft dit geeft een extra beveiliging tegen fouten. In tegenstelling tot de file extensie is het geen onderdeel van de naam. Je kunt dus geen twee files aanmaken met verschillende file type maar dezelfde naam.

Met 'file delete' kun je files verwijderen. Als je veel files aanmaakt en weer verwijdert, kan het filesysteem gefragmenteerd raken waardoor er geen ruimte overblijft om grotere files op te slaan. Fragmentatie kun je verwijderen met 'file defrag'.

Defrag kan - net als alle andere bewerkingen op het filesysteem - zoveel tijd vragen dat de timing van de robot verstoord wordt. Hiervan komt een melding en die kun je dus negeren. Het is verstandig om geen bewerkingen op het filesysteem te doen terwijl de robot een missie uitvoert.

Het commando 'file backup' stelt een bestand veilig door het naar het console te sturen.

RedBot> file backup PID-SpeedHeading
ÀFILE PID-SpeedHeading 99 12 6860 0000 99 bb 16 3c 7f 6a bc 3c 0a d7 23 3bÀ

Alle informatie van de file staat op 1 regel en door dit te loggen kun je het bewaren. Je kunt een bestand herstellen (aanmaken, overschrijven) door die ene regel (inclusief de 'À' aan het begin en einde) naar de robot te sturen.

Het commando 'file backupall' stuurt de inhoud van alle files naar het consolem weer een regel per bestand. Zeker bij grotere bestanden kan dit vrij veel tijd kosten.

Non Volatile

Modules kunnen de 'non volatile' (nv) module gebruiken om configuratie op te slaan. De non-volatile module heeft de volgende eigenschappen:

  • Geschikt om configuraties met vast format op te slaan en in te laden.
  • Configuratie wordt opgeslagen onder de naam van de module. Het is niet mogelijk om meerdere configuraties op te slaan voor dezelfde module.
  • Bij opstarten van RobotLib wordt de configuratie van iedere module automatisch geladen, voor zover deze beschikbaar zijn.
  • Indien voor een module geen configuratiebestand beschikbaar is, worden de defaults van de module gebruikt.
  • Vanuit de nv module kan de configuratie van alle andere modules worden opgeslagen en (opnieuw) ingeladen.
RedBot> ? nv
NonVolatile store
  nv save         nr - save cfg
  nv load         nr - load cfg
  nv list            - list clients
RedBot> nv list

Het menu van de nv module is eenvoudig. De output van 'nv list' geeft het volgnummer en de naam van alle modules:

NvList
 Nv 0 LineSensorQTR8A
 Nv 1 PID-MotorL
 Nv 2 PID-MotorR
 Nv 3 PID-SpeedHeading
 Nv 4 PID-Linefollower
Done.

Het volgnummer wordt gebruikt als parameter bij save en load. Let op: de volgorde van modules kan veranderen als RobotLib opnieuw wordt gecompileerd.

In onderstaand voorbeeld wordt de configuratie van de PID-controller PID-SpeedHeading opnieuw geladen.

RedBot> nv load 3
[61:I] NvStore.load PID-SpeedHeading
[61:I] NvCfg.ValidData - load done.
RedBot>