Cozmo entdeckt seine Welt

Im letzten Beitrag habe ich den Roboter Cozmo vorgestellt und seine grundlegenden Funktionen erklärt. Heute möchte ich ein erstes kleines Programmierbeispiel mit dem mitgelieferten Python-SDK zeigen.

In diesem Programm-Prototyp findet der Roboter selbstständig seinen Weg über den Schreibtisch. Er agiert dabei als einfaches autonomes Fahrzeug. Die eingebaute Bilderkennungs-Engine des Roboters wird verwendet, um sich zu orientieren und sich zu positionieren. Die Erkundung der umgebenden Welt erfolgt dabei in Echtzeit. Die Objekterkennung wird anhand vordefinierter Marker unterstützt. Zur Demonstration der Funktionsweise habe ich für Cozmo eine kleine Welt aus Bausteinen ausgelegt.

Definition von Objekten

Ein wichtiger Bestandteil von Cozmos Wahrnehmung ist seine Frontkamera. Der Videostream wird fortlaufend ausgewertet und auf bekannte Objekte untersucht. So werden unter anderem die Powercubes, Gesichter, Haustiere und die Ladestation automatisch erkannt. Darüber hinaus können Cozmo auch eigene, benutzerdefinierte Objekte bekannt gemacht werden. Im vorliegenden Beispiel sollen dies einfache Holzbausteine sein. Diese sind zur Unterscheidung für den Roboter mit speziellen Markern beklebt, welche im weiteren Programmablauf ausgewertet werden können.

Benutzerdefinierte Objekte können im CozmoSDK mit eindeutigen Abmessungen definiert werden. Das kann zum Beispiel so aussehen:

async def defineObjects(robot: cozmo.robot.Robot):
        wall_obj0 = await robot.world.define_custom_wall(CustomObjectTypes.CustomType01, CustomObjectMarkers.Circles2, 73, 22, 30, 20, False)

Damit kann Cozmo nun alle Objekte Typs „Baustein mit Kreissymbol“ erkennen, und diese im Raum anhand der bekannten Maße einordnen. Das heißt, wenn Cozmo ein solches Objekt sieht, kann er berechnen, welche Lage der Baustein im Raum relativ zu ihm selbst hat. Die Entfernungs- und Lageberechnungen sind erstaunlich genau. Hinweis: Beim Aufkleben der Marker muss man sehr präzise vorgehen, da schräg angebrachte Marker dazu führen, dass das Objekt unter Umständen mit falschen Z-Koordinaten oder verdreht im 3D-Modell platziert wird.

Objekte ansteuern

Der Roboter erforscht seine Umgebung und kann die erkannten Objekte anhand der dreidimensionalen Repräsentation punktgenau ansteuern. Bekannte Hindernisse können umfahren werden.

Im Beispielprogramm werden die benutzerdefinierten Objekte genutzt, um einen Weg durch das Bausteinlabyrinth zu finden. Ein Kreisymbol zeigt den Weg nach rechts und ein Diamantsymbol den Weg nach links. Die anderen Symbole sind ohne Funktion und markieren lediglich unpassierbare Wände.

Zum Ansteuern einer Position in Cozmos 3D-Welt muss nicht unbedingt bis auf die Ebene der Motorsteuerung, respektive die Ansteuerung der beiden Ketten hinabgestiegen werden. Es gibt bereits eine vorgefertigte Funktion, welche ihn an eine spezielle Position im Raum fahren lässt. Die einzige Schwierigkeit besteht noch darin, mittels Vektorrechnung die Zielposition zwischen dem angepeilten Marker und der aktuellen Position zu berechnen. Ohne diese Einschränkung würde Cozmo sonst stets bis zur Mitte des erkannten Markerbausteins fahren und diesen dabei verschieben.

Asynchroner Aufruf der Funktion go_to_pose zum Ansteuern einer Position im dreidimensionalen Raum:

try:
    print("object %s" % str(CustomObject.object_type))
    await robot.go_to_pose(pose=position).wait_for_completed()
    print("Done.")
except:
    print("Didn't go to the object.")

Video autonomes Fahren

Das Video zeigt den Ablauf des Programms im Detail. Im Fenster links oben wird die dreidimensionale Repräsentation von Cozmos Welt mit dem im SDK enthaltenen OpenGL-Viewer dargestellt. Im Fenster daneben sieht man den Livestream der Frontkamera. Die erkannten Objekte werden fortlaufend als Annotation eingeblendet. Unten rechts sieht man wie Cozmo seinen Weg durch den Parcours findet.

Wie fließend Bilderkennung und Auswertung erfolgen, kann man im Video gut verfolgen. Beachtenswert ist, mit welcher Präzision die einzelnen Abbiegemarker angesteuert werden. Interessant ist es außerdem, zu sehen, dass selbst im Hintergrund befindliche Objekte visualisiert werden. So werden im Verlauf der Fahrt zum Beispiel auch die Ladestation und Powercubes erkannt.

Die Herausforderung bei der Programmierung des Roboters liegt im Besonderen in der Nebenläufigkeit der einzelnen Prozesse. Die Bilderkennung und das Manövrieren des Roboters laufen parallel und asynchron ab. Asynchrone Programmierung folgt einem von der sequentiellen bzw. ereignisorientierten Programmiermethode abweichenden Ansatz, bei dem die einzelnen Befehle nicht nacheinander, sondern nebeneinander abgearbeitet werden. Die Rückgabewerte von Funktionen oder angeforderten Aktionen stehen nicht unmittelbar nach der Ausführung zur Verfügung, sondern erst nach unbestimmter Zeitspanne. Das asynchrone Programmierparadigma wird von Python mit der Bibliothek asyncio unterstützt.