Logistieke Robots/les 4: verschil tussen versies
(23 tussenliggende versies door dezelfde gebruiker niet weergegeven) | |||
Regel 9: | Regel 9: | ||
== Sturen van de bestelling door centrale systeem == | == Sturen van de bestelling door centrale systeem == | ||
Een robot moet een bestelling doorkrijgen van een computer die het overzicht heeft over alle bestellingen. Deze controller moeten we aan een centraal object in Blender toegevoegen. We kiezen hiervoor de vloer (Ground object). | Een robot moet een bestelling doorkrijgen van een computer die het overzicht heeft over alle bestellingen. Deze controller moeten we aan een centraal object in Blender toegevoegen. We kiezen hiervoor -vrij willekeurig- de vloer (Ground object). | ||
Net als in de vorige les ontwikkelen we de controller via de logic bricks en via Python scripting. | Net als in de vorige les ontwikkelen we de controller via de logic bricks en via Python scripting. | ||
Regel 26: | Regel 26: | ||
=== Stap 3: Programmeren script “central_controller.py” === | === Stap 3: Programmeren script “central_controller.py” === | ||
In deze stap programmeren we de centrale controller die de bestellingen verstuurt aan de robot. Hier zullen we voor het eerst | In deze stap programmeren we de centrale controller die de bestellingen verstuurt aan de robot. Hier zullen we voor het eerst ingewikkelder Python kennis gebruiken. Het script bouwen we op de volgende manier op: | ||
* Importeren van de nodige packages, ophalen van de controller en het object en declareren van de sensor. Dit hebben we ook gezien in de vorige les. | * Importeren van de nodige packages, ophalen van de controller en het object en declareren van de sensor. Dit hebben we ook gezien in de vorige les. | ||
Regel 62: | Regel 62: | ||
“box_articles” is een lokale variabele in het script - en geen game-property. Dit is een array van strings: deze declareren we op de manier van Python. | “box_articles” is een lokale variabele in het script - en geen game-property. Dit is een array van strings: deze declareren we op de manier van Python. | ||
Shelf_1 tot en met Shelf_8 corresponderen met de kasten die te zien zijn in het pakhuis en iedere kast heeft één soort artikel. Shelf_1 | Shelf_1 tot en met Shelf_8 corresponderen met de kasten die te zien zijn in het pakhuis en iedere kast heeft één soort artikel. Shelf_1 is dus een kast vol met artikelen nummer 1. | ||
* We willen dat het systeem pas begint wanneer we op ENTER drukken. Wanneer deze sensor, “sens”, een positieve puls krijgt, wil je Robot_11 een bestelling (order) meegeven. Dit kan op de volgende manier: | * We willen dat het systeem pas begint wanneer we op ENTER drukken. Wanneer deze sensor, “sens”, een positieve puls krijgt, wil je Robot_11 een bestelling (order) meegeven. Dit kan op de volgende manier: | ||
Regel 85: | Regel 85: | ||
== Inpakken van de bestelling == | == Inpakken van de bestelling == | ||
De centrale controller kan nu een order sturen naar de Robot_11. Robot_11 moet wel in staat zijn de message op te kunnen vangen en vervolgens aan de slag te gaan. Dit doen we weer aan de hand van de zelfde stappen als in de vorige paragraaf. | |||
=== Stap 1: Python script aanmaken voor Robot_11 === | |||
Creëer een python script door op het ‘+’ teken te drukken in de header van de tekst editor. Geef het script de naam “robot.py”. | |||
=== Stap 2: Logic bricks voor Robot_11=== | |||
* Voeg een message “Start” sensor toe | |||
* Voeg een collission “Shelf” sensor toe met de Tap functie aan | |||
* Voeg de python controller toe | |||
* Voeg een steering “TrackTo” actuator toe, facing de –X as. | |||
* Voeg een Integer Game Property “article_number” toe en vink je blauwe “i” aan. We hebben gezien dat je deze ook aan kunt maken via Python scripting maar alleen op deze manier kun je ervoor zorgen dat de variabele in de 3D window te zien is. | |||
[[Bestand:Les4-add-game-property.png|350px|Add game property article_number]] | |||
=== Stap 3: Programmeren script “robot.py” === | |||
* Importeer de nodige packages, haal de controller op en definieer de sensoren/actuatoren. | |||
<syntaxhighlight lang="Python"> | |||
# import necessary packages | |||
import bge | |||
import GameLogic | |||
import re | |||
# get the current controller | |||
cont = bge.logic.getCurrentController() | |||
robot = cont.owner | |||
# get sensors and actuators | |||
start_sens = cont.sensors["Start"] | |||
shelf_sens = cont.sensors["Shelf"] | |||
track_act = cont.actuators["TrackTo"] | |||
</syntaxhighlight> | |||
* Bij een positieve puls door het ontvangen van de “Start” message vang je de bestelling als volgt op: | |||
<syntaxhighlight lang="Python"> | |||
if start_sens.positive: | |||
print("Start new order:") | |||
get_order() | |||
</syntaxhighlight> | |||
get_order kan je op de volgende manier definiëren: | |||
<syntaxhighlight lang="Python"> | |||
def get_order(): | |||
# get articles from central controller | |||
robot["articles"] = start_sens.bodies[0][1:] | |||
print(robot["articles"]) | |||
# set game properties | |||
robot["article_number"] = 1 | |||
robot["target_desk"] = False | |||
robot["robot_id"] = start_sens.bodies[1] | |||
</syntaxhighlight> | |||
Te zien is dat je de messages van de central controller via de functie “bodies” op kan halen. De variabele <code>article_number</code> wordt op de waarde 1 gezet omdat de robot klaar is om het eerste artikel op te halen. <code>target_desk</code> wordt aangemaakt en betekent of de robot op dit moment wel/niet naar de tafel aan het lopen is. “robot_id” wordt hier aangemaakt zodat de robot zelf weet welke robot hij is. Dit zorgt ervoor dat we dit script kunnen hergebruiken voor alle robots. | |||
* Net als in Les 3 hebben we ook een <code>main()</code> functie die op dezelfde manier aangeroepen wordt. Deze kun je overal neerzetten, bijvoorbeeld zo: | |||
<syntaxhighlight lang="Python"> | |||
if start_sens.positive: | |||
print("Start new order:") | |||
get_order() | |||
main() | |||
</syntaxhighlight> | |||
* de <code>main()</code> functie lijkt erg op die van Les 3, maar dan iets ingewikkelder omdat je steeds een nieuw artikel mee geeft: | |||
<syntaxhighlight lang="Python"> | |||
def main(): | |||
if shelf_sens.positive: | |||
if robot["target_desk"] == True: | |||
robot["article_number"] += 1 | |||
if robot["article_number"] - 1 == len(re.findall(r'[^\s ]+', robot["articles"])): | |||
cont.deactivate(track_act) | |||
print("done robot 11") | |||
GameLogic.sendMessage("Done", robot["robot_id"] , "Ground", "") | |||
robot["target_desk"]= not robot["target_desk"] | |||
cont.activate(track_act) | |||
if robot["target_desk"] == False: | |||
shelf_sens.propName = "Shelf" | |||
if robot["article_number"] <= len(re.findall(r'[^\s ]+', robot["articles"])): | |||
track_act.target = re.findall(r'[^\s ]+', robot["articles"])[robot["article_number"]-1] | |||
else: | |||
shelf_sens.propName = "Desk" | |||
track_act.target = "Desk_" + str(robot["robot_id"]) | |||
</syntaxhighlight> | |||
De steering actuator moet geactiveerd worden via de robot.py controller. Dat kan door de ingebouwde functie “activate”. De if/else combinatie hebben we al gezien maar het target verandert nu wel steeds. Dit zijn de artikelen opgeslagen in robot[“articles”]. Met behulp van een python functie (uit de package “re” die we ingeladen hebben) kunnen we de juiste artikelen eruit halen. | |||
Wanneer de robot weer een aanraking heeft met de tafel en het aantal opgehaalde artikelen is gelijk aan het totaal aantal op te halen artikelen, is de robot klaar. Hiervoor gebruiken we de python functie len() die de lengte van de array teruggeeft. Wanneer het aantal op te halen artikelen bereikt is, in dit geval 3, kan de actuator gedeactiveerd worden. | |||
== Nieuwe bestelling == | == Nieuwe bestelling == |
Huidige versie van 31 mrt 2015 om 20:45
Les 4
In de vorige les heb je de basis geleerd om een controller te ontwikkelen met behulp van Python. We hebben ook de omgeving geïntroduceerd waar we de rest van het project mee blijven werken: een magazijn. De robots krijgen bestellingen binnen die uit verschillende artikelen bestaan. Ze moeten deze artikelen gaan verzamelen om de bestelling compleet te maken. Dit kan natuurlijk op meerdere manieren: een robot kan de artikelen 1 voor 1 ophalen, allemaal in 1 keer of misschien kan deze robot maar een maximaal aantal artikelen tillen, of een maximaal gewicht. Als de bestelling compleet is, krijgt een robot een nieuwe bestelling.
In deze les leren we hoe we een controller kunnen maken voor 1 robot die de artikelen 1 voor 1 ophaalt. Open de file “warehouse_one_order.blend”. Wat je in deze les ontwikkelt, gebruik je in de volgende lessen als basis om op door werken.
Sturen van de bestelling door centrale systeem
Een robot moet een bestelling doorkrijgen van een computer die het overzicht heeft over alle bestellingen. Deze controller moeten we aan een centraal object in Blender toegevoegen. We kiezen hiervoor -vrij willekeurig- de vloer (Ground object).
Net als in de vorige les ontwikkelen we de controller via de logic bricks en via Python scripting.
Stap 1: Python script aanmaken voor Ground object
- Selecteer het Ground object;
- Maak een nieuw Python script aan door op het ‘+ New’ teken te drukken in de header van de tekst editor. Geef het script de naam “central_controller.py”;
- Voeg een Python controller aan met dit script.
Stap 2: Logic bricks voor Ground object
- Voeg een keyboard “Start” sensor toe met de ENTER key. Druk op Tap. Dit betekent dat de sensor eenmalig een positieve puls geeft aan de controller in plaats van zolang de sensor waar is.
- Verbind deze sensor met de Python-controller van de vorige stap.
Stap 3: Programmeren script “central_controller.py”
In deze stap programmeren we de centrale controller die de bestellingen verstuurt aan de robot. Hier zullen we voor het eerst ingewikkelder Python kennis gebruiken. Het script bouwen we op de volgende manier op:
- Importeren van de nodige packages, ophalen van de controller en het object en declareren van de sensor. Dit hebben we ook gezien in de vorige les.
import bge
import GameLogic
import random
cont = bge.logic.getCurrentController() # controller of this script
central = cont.owner # game object owning this controller
# get sensors
sens = cont.sensors["Start"]
- Definiëren van variabelen. We hebben al gezien dat je variabelen kunt definiëren door “Game Properties” aan te maken voor een game-object zoals een robot. De vloer is ook een dergelijk game-object waar we een controller en variabelen aan kunnen koppelen. Deze variabelen kun je aanmaken en een waarde geven via het script met behulp van de volgende code:
game_object[“naam_variabele”] = waarde
Belangrijke variabelen in het pakhuis zijn hoeveel boxen er gevuld moeten worden, hoe groot de bestellingen zijn en welke artikelen in deze box kunnen zitten. Dit coderen we dus als volgt:
# define game properties
central["boxes_tot"] = 5
central["box_size"] = 3
box_articles = ["Shelf_1", "Shelf_2", "Shelf_3", "Shelf_4", "Shelf_5", "Shelf_6", "Shelf_7", "Shelf_8"]
“boxes_tot” en “box_size” zijn game properties verbonden aan het Ground object.
“box_articles” is een lokale variabele in het script - en geen game-property. Dit is een array van strings: deze declareren we op de manier van Python. Shelf_1 tot en met Shelf_8 corresponderen met de kasten die te zien zijn in het pakhuis en iedere kast heeft één soort artikel. Shelf_1 is dus een kast vol met artikelen nummer 1.
- We willen dat het systeem pas begint wanneer we op ENTER drukken. Wanneer deze sensor, “sens”, een positieve puls krijgt, wil je Robot_11 een bestelling (order) meegeven. Dit kan op de volgende manier:
if sens.positive:
robot = "11"
Send_order(robot)
waarin Send_order
als volgt gedefinieerd is:
def Send_order(robot):
order = ""
for j in range(central["box_size"]):
order += " " + random.choice(box_articles)
GameLogic.sendMessage("Start", order , "Robot_"+robot , "Ground")
GameLogic.sendMessage("Start", robot , "Robot_"+robot , "Ground")
Zoals je kunt zien krijgt de functie Send_order een getal mee van de robot waar de bestelling naar toe moet. Dit doen we omdat we straks meer robots aan gaan sturen met deze functie. Vervolgens maken we een order aan. Dit moet in de vorm van een String variabelen omdat we deze anders niet mee kunnen sturen naar de robot. We plakken steeds een willekeurig artikel (tussen 1 en 8) aan de order. Wanneer we de lengte van de order bereikt hebben, in ons geval 3, sturen we de order naar de robot en ook de variabele “robot”. Dit laatste doen we zodat we de robot.py controller kunnen hergebruiken voor alle robots. We hoeven dan niet een aparte python controller te maken voor iedere robot. De sendMessage functie is een functie die Blender al voor ons heeft gemaakt. Aan deze functie moet je de titel geven van de message (let op: de robot moet dus een message “Start” sensor hebben), wat er in de message staat, naar wie de message toe moet en wie deze message stuurt.
Inpakken van de bestelling
De centrale controller kan nu een order sturen naar de Robot_11. Robot_11 moet wel in staat zijn de message op te kunnen vangen en vervolgens aan de slag te gaan. Dit doen we weer aan de hand van de zelfde stappen als in de vorige paragraaf.
Stap 1: Python script aanmaken voor Robot_11
Creëer een python script door op het ‘+’ teken te drukken in de header van de tekst editor. Geef het script de naam “robot.py”.
Stap 2: Logic bricks voor Robot_11
- Voeg een message “Start” sensor toe
- Voeg een collission “Shelf” sensor toe met de Tap functie aan
- Voeg de python controller toe
- Voeg een steering “TrackTo” actuator toe, facing de –X as.
- Voeg een Integer Game Property “article_number” toe en vink je blauwe “i” aan. We hebben gezien dat je deze ook aan kunt maken via Python scripting maar alleen op deze manier kun je ervoor zorgen dat de variabele in de 3D window te zien is.
Stap 3: Programmeren script “robot.py”
- Importeer de nodige packages, haal de controller op en definieer de sensoren/actuatoren.
# import necessary packages
import bge
import GameLogic
import re
# get the current controller
cont = bge.logic.getCurrentController()
robot = cont.owner
# get sensors and actuators
start_sens = cont.sensors["Start"]
shelf_sens = cont.sensors["Shelf"]
track_act = cont.actuators["TrackTo"]
- Bij een positieve puls door het ontvangen van de “Start” message vang je de bestelling als volgt op:
if start_sens.positive:
print("Start new order:")
get_order()
get_order kan je op de volgende manier definiëren:
def get_order():
# get articles from central controller
robot["articles"] = start_sens.bodies[0][1:]
print(robot["articles"])
# set game properties
robot["article_number"] = 1
robot["target_desk"] = False
robot["robot_id"] = start_sens.bodies[1]
Te zien is dat je de messages van de central controller via de functie “bodies” op kan halen. De variabele article_number
wordt op de waarde 1 gezet omdat de robot klaar is om het eerste artikel op te halen. target_desk
wordt aangemaakt en betekent of de robot op dit moment wel/niet naar de tafel aan het lopen is. “robot_id” wordt hier aangemaakt zodat de robot zelf weet welke robot hij is. Dit zorgt ervoor dat we dit script kunnen hergebruiken voor alle robots.
- Net als in Les 3 hebben we ook een
main()
functie die op dezelfde manier aangeroepen wordt. Deze kun je overal neerzetten, bijvoorbeeld zo:
if start_sens.positive:
print("Start new order:")
get_order()
main()
- de
main()
functie lijkt erg op die van Les 3, maar dan iets ingewikkelder omdat je steeds een nieuw artikel mee geeft:
def main():
if shelf_sens.positive:
if robot["target_desk"] == True:
robot["article_number"] += 1
if robot["article_number"] - 1 == len(re.findall(r'[^\s ]+', robot["articles"])):
cont.deactivate(track_act)
print("done robot 11")
GameLogic.sendMessage("Done", robot["robot_id"] , "Ground", "")
robot["target_desk"]= not robot["target_desk"]
cont.activate(track_act)
if robot["target_desk"] == False:
shelf_sens.propName = "Shelf"
if robot["article_number"] <= len(re.findall(r'[^\s ]+', robot["articles"])):
track_act.target = re.findall(r'[^\s ]+', robot["articles"])[robot["article_number"]-1]
else:
shelf_sens.propName = "Desk"
track_act.target = "Desk_" + str(robot["robot_id"])
De steering actuator moet geactiveerd worden via de robot.py controller. Dat kan door de ingebouwde functie “activate”. De if/else combinatie hebben we al gezien maar het target verandert nu wel steeds. Dit zijn de artikelen opgeslagen in robot[“articles”]. Met behulp van een python functie (uit de package “re” die we ingeladen hebben) kunnen we de juiste artikelen eruit halen. Wanneer de robot weer een aanraking heeft met de tafel en het aantal opgehaalde artikelen is gelijk aan het totaal aantal op te halen artikelen, is de robot klaar. Hiervoor gebruiken we de python functie len() die de lengte van de array teruggeeft. Wanneer het aantal op te halen artikelen bereikt is, in dit geval 3, kan de actuator gedeactiveerd worden.
Nieuwe bestelling
Als er meerdere bestellingen ingepakt moeten worden, kan de robot aan de centrale controller doorgeven dat hij klaar is voor een nieuwe bestelling. Deze nieuwe bestelling is er omdat we in de central_controller aangegeven hebben dat we 5 boxen willen vullen.
Logic bricks uitbreiding voor Ground object
- voeg een message “Done” sensor toe
- voeg een integer game property toe met de naam “boxes_done”, zodat we deze in de 3D view kunnen zien
Script uitbreiding voor Ground object
- De zojuist aangemaakt sensor moet in het script opgehaald worden:
- Wanneer de message een positieve puls geeft moet de centrale controller hierop reageren door nog een order te sturen. Dit kan op de volgende manier:
Script uitbereiding voor Robot object In de controller van de robot hoeft maar 1 regel toegevoegd te worden. Denk zelf over deze regel na. Als dit niet lukt zoek dan het verschil in onderstaande code:
Kijk of je programma werkt door naar de spelmodus te gaan. De output in de Terminal moet er als volgt uit zien (waarbij de inhoud van de orders kan verschillen):
Plusopdracht
- Visualiseer dat artikelen vastgehouden worden door de robot en op de tafel geplaats worden. Wanneer alle artikelen compleet zijn, mogen ze verdwijnen in de box.
- De robot haalt de artikelen nu 1 voor 1 op. Implementeer het scenario dat de robot alle artikelen in 1 keer op kan halen. De robot loopt dus eerst langs meerdere kasten voordat