Schachspiel mittels kollaborierendem UR-Roboter unter ROS: Unterschied zwischen den Versionen
(196 dazwischenliegende Versionen von 2 Benutzern werden nicht angezeigt) | |||
Zeile 13: | Zeile 13: | ||
<!-- Dies hier wird nicht angezeigt --> | <!-- Dies hier wird nicht angezeigt --> | ||
<center><b>Danksagung</b><br> | |||
<b>Vorab wollen wir uns herzlich bei Digital Games Technology (DGT) bedanken. Diese haben uns bei unserem Projekt unterstützt und auch das Schachbrett samt der Figuren kostenlos zur Verfügung gestellt.</b></center><br><br><br> | |||
== Einleitung == | == Einleitung == | ||
Als Teil des Bachelorstudiengangs Mechatronik an der HSHL, wird im siebten Semester das Fach Produktiontechnik VII unterrichtet. Semesterbegleitend wird dieses Fach als Praktikum durchgeführt. In diesem Praktikum sollen die Studierenden ein mechatronisches Projekt in kleinen Gruppen selbstständig bearbeiten. Das hier beschriebene Projekt handelt vom Schachspiel eines Roboterarms (UR3) unter ROS. | Als Teil des Bachelorstudiengangs Mechatronik an der HSHL, wird im siebten Semester das Fach Produktiontechnik VII unterrichtet. Semesterbegleitend wird dieses Fach als Praktikum durchgeführt. In diesem Praktikum sollen die Studierenden ein mechatronisches Projekt in kleinen Gruppen selbstständig bearbeiten. Das hier beschriebene Projekt handelt vom Schachspiel eines Roboterarms (UR3) unter ROS. | ||
===Aufgabenstellung=== | ===Aufgabenstellung=== | ||
Aufgabe ist es, dem von der HSHL bereitgestellten Roboterarm UR3 das Schachspielen beizubringen. Dazu soll die Software "Robot Operation System" (ROS) verwendet werden. Das System soll letzten Endes dazu in der Lage sein gegen einen menschlichen Gegenspieler anzutreten. Dazu benötigt das System eine Möglichkeit zur Erkennung des Spielfelds und eine Positionserkennung der einzelnen Spielfiguren. Zusätzlich muss das System dazu in der Lage sein, das Spielfeld auswerten und korrekte Spielzüge ausführen zu können. | Aufgabe ist es, dem von der HSHL bereitgestellten Roboterarm UR3 das Schachspielen beizubringen. Dazu soll die Software "Robot Operation System" (ROS) verwendet werden. Das System soll letzten Endes dazu in der Lage sein gegen einen menschlichen Gegenspieler anzutreten. Dazu benötigt das System eine Möglichkeit zur Erkennung des Spielfelds und eine Positionserkennung der einzelnen Spielfiguren. Zusätzlich muss das System dazu in der Lage sein, das Spielfeld auswerten und korrekte Spielzüge ausführen zu können. | ||
===Robot Operating System (ROS)=== | |||
Dies ist ein Entwicklungssystem speziell für roboterbasierte Anwendungen. Hierbei wird zwischen Knoten (Nodes) kommuniziert. Jeder Knoten hat eine eigene Aufgabe, in diesem Projekt z.B. das Ansteuern des Roboters. Diese Kommunikation findet über "Topics", "Services" oder "Actions" statt. Genauere Tutorials zur Installation und den funktonsweisen der Kommunikation befinden sich hier: [http://docs.ros.org/en/foxy/Tutorials.html '''ROS Tutorials''']<br> | |||
In diesem Projekt wird ROS Noetic verwendet, da hierfür bereits die notwendigen Treiber für den Roboter existieren. | |||
==Vorgehensweise nach V-Modell== | ==Vorgehensweise nach V-Modell== | ||
Zur strukturierten Bearbeitung des Projektes wird nach dem V-Modell vorgegangen. Durch dieses vorgehen kann sichergestellt werden, dass alle notwendigen Schritte die zur erfolgreichen Bearbeitung eines Projektes benötigt werden auch durchgeführt werden. <br> | [[Datei:V-Modell2020.png|rechts|Abbildung 1: V-Modell2020.png|400px |mini]] | ||
Die Verwaltung der verschiedenen erstellten Dokumente wird über das Programm [https://tortoisesvn.net/index.de.html TortoiseSVN]<ref>[https://tortoisesvn.net/index.de.html ''TortoiseSVN Client'']</ref> gewährleistet. Nachfolgend werden die Dokumente unter den jeweiligen Punkten des V-Modells als Link zum Download bereitgestellt. | Zur strukturierten Bearbeitung des Projektes wird nach dem V-Modell vorgegangen (vgl. Abbildung 1). Durch dieses vorgehen kann sichergestellt werden, dass alle notwendigen Schritte die zur erfolgreichen Bearbeitung eines Projektes benötigt werden auch durchgeführt werden. <br> | ||
Die Verwaltung der verschiedenen erstellten Dokumente wird über das Programm [https://tortoisesvn.net/index.de.html TortoiseSVN]<ref>[https://tortoisesvn.net/index.de.html ''TortoiseSVN Client'']</ref> gewährleistet. Nachfolgend werden die Dokumente unter den jeweiligen Punkten des V-Modells als Link zum Download bereitgestellt. | |||
Die an das Projekt gestellten Anforderungen werden im ersten Schritt des V-Modelles in der Anforderungsliste definiert. Dabei wird die Anforderungsliste in verschiedene Kategorien unterteilt: | |||
===Anforderungsdefinition=== | |||
Die an das Projekt gestellten Anforderungen werden im ersten Schritt, der [https://migsvn.hshl.de:65489/usvn/svn/UniversalRobots/Mitarbeiter/2021_Listner_Reuter/01_Anforderungsliste/ '''Anforderungsdefinition''']<ref>[https://migsvn.hshl.de:65489/usvn/svn/UniversalRobots/Mitarbeiter/2021_Listner_Reuter/01_Anforderungsliste/ ''Anforderungsdefinition'']</ref>, des V-Modelles in der Anforderungsliste definiert. Dabei wird die Anforderungsliste in verschiedene Kategorien unterteilt: | |||
*Anforderungen an das System | *Anforderungen an das System | ||
*Sicherheit | *Sicherheit | ||
Zeile 33: | Zeile 39: | ||
*Dokumentation | *Dokumentation | ||
[https://migsvn.hshl.de:65489/usvn/svn/UniversalRobots/Mitarbeiter/2021_Listner_Reuter/02_Funktionaler_Systementwurf/ '''funktionaler Systementwurf''']<ref>[https://migsvn.hshl.de:65489/usvn/svn/UniversalRobots/Mitarbeiter/2021_Listner_Reuter/02_Funktionaler_Systementwurf/ ''funktionaler Systementwurf'']</ref> | ===Funktionaler Systementwurf=== | ||
[[Datei:NodeStruktur.JPG|rechts|Abbildung 2: Nodestruktur.JPG|400px |mini]] | |||
Der [https://migsvn.hshl.de:65489/usvn/svn/UniversalRobots/Mitarbeiter/2021_Listner_Reuter/02_Funktionaler_Systementwurf/ '''funktionaler Systementwurf''']<ref>[https://migsvn.hshl.de:65489/usvn/svn/UniversalRobots/Mitarbeiter/2021_Listner_Reuter/02_Funktionaler_Systementwurf/ ''funktionaler Systementwurf'']</ref>, zeigt in einer schematischen Darstellung die Struktur und Verknüpfung der einzelnen in ROS zu programmierenden Nodes (vgl. Abbildung 2). Der Aufbau der vier geplanten Nodes lässt sich wie folgt beschreiben: | |||
*Node 1: Sendet die Befehle <code>0x08 (DGT_CMD_CLOCK_BUTTON)</code> zum anfordern der Schachuhrdaten & <code>0x42 (DGT_REQ_BOARD)</code> zum anfordern der Spielfeldinformationen an das Smart Board | |||
*Node 1: Sendet die erhaltenen Spielfeldinformationen über ein Topic an Node 2. | |||
*Node 2: Wertet über die Einbindung der Stockfish-Schachengine die Spielfeldinformationen aus und ermittelt den Zug den der Roboter ausführen muss. | |||
*Node 2: Sendet den ermittelten Spielzug (Figur, von Feld XY, nach Feld YZ) an Node 3. | |||
*Node 3: Ermittelt die Koordinaten die dem Roboter zur Bewegungsausführung bereitgestellt werden müssen. | |||
*Node 3: Sendet die Befehle über ein weiteres Topic an Node 4. | |||
*Node 4: Verantwortlich für die auszuführenden UR_Befehle | |||
Zusätzlich findet sich unter dem o.g. Link ein Programmablaufplan (PAP) der Auskunft über den generellen Ablauf und Rahmenbedingungen die bei der Zugdurchführung zu beachten sind. | |||
===Technischer Systementwurf=== | |||
[[Datei:Technischer Systementwurf Schachspiel.jpg|rechts|Abbildung 2: Nodestruktur.JPG|300px |mini]] | |||
Im [https://migsvn.hshl.de:65489/usvn/svn/UniversalRobots/Mitarbeiter/2021_Listner_Reuter/03_Technischer_Systementwurf/ '''technischen Systementwurf''']<ref>[https://migsvn.hshl.de:65489/usvn/svn/UniversalRobots/Mitarbeiter/2021_Listner_Reuter/03_Technischer_Systementwurf/ ''technischer Systementwurf'']</ref> werden die verwendeten Komponenten und deren Schnittstellen übersichtlich dargestellt. Gleichzeitig wird hier gezeigt, wie die Komponenten untereinander verbunden sind. Der Datenaustausch zwischen dem DGT Smart Board und dem Ubuntu-PC erfolgt über einen USB-C Anschluss. Dabei sendet das Board die Daten in Form von Hexadezimalzahlen. Die Verbindung zwischen Smart Board und Schachuhr erfolgt über einen AUX-Anschluss. Die Uhr sendet, wie auch das Smart Board, die Daten in Hexadezimalzahlen. Die Verbindung zwischen dem Ubuntu-PC und dem UR3-Roboter, erfolgt über einen Ethernet Anschluss. | |||
<br><br><br><br><br><br><br><br><br><br> | |||
===Komponentenspezifikation=== | |||
In der [https://migsvn.hshl.de:65489/usvn/svn/UniversalRobots/Mitarbeiter/2021_Listner_Reuter/04_Komponentenspezifikation/ '''Komponentenspezifikation''']<ref>[https://migsvn.hshl.de:65489/usvn/svn/UniversalRobots/Mitarbeiter/2021_Listner_Reuter/04_Komponentenspezifikation/ ''Komponentenspezifikation'']</ref> wird definiert, welche Komponenten konkret für die Erfüllung der Aufgaben verwendet werden sollen. Dabei wurden die Komponenten in Hardware und Software unterteilt. | |||
====Hardware==== | |||
{| class="mw-datatable" | |||
! style="font-weight: bold;" | Komponenten | |||
! style="font-weight: bold;" | Beschreibung | |||
! style="font-weight: bold;" | Preis | |||
! style="font-weight: bold;" | Bild | |||
|- | |||
|UR3 | |||
|Hauptakteur des Projektes ist der UR3 Roboterarm der Firma Universal Robots (UR). Der UR3 ist der kleinste der von UR angebotenen kollaborierenden Roboter. Dabei hat der UR3 ein Gewicht von 11 kg mit einer maximalen Traglast von 3 kg. Die Reichweite des Roboters beträgt bei voll ausgestrecktem Arm 500 mm. Alle Gelenke des UR3 lassen sich um +/- 360° drehen. Der Roboter benötigt eine Betriebspanung von 24 V im Schaltschrank und 12 V an der Werkzeugschnittstelle. Die Kommunikation des Roboters zu PC findet über einen Ethernet-Anschluss statt. Weitere Informationen sind dem Datenblatt<ref name = 'Datenblatt UR3'> UR3Roboter: ''UniversalRobots- UR3Roboter''. 2022. Online im Internet: https://www.i-botics.de/wp-content/uploads/2016/08/ur3_de.pdf; Abruf: 08.01.2022</ref> zu entnehmen. | |||
|Kostenlos, da bereits vorhanden | |||
|[[Datei:UR3Roboter.jpg|126px|mini|zentriert|UR3Roboter]] | |||
|- | |||
|DGT Smart Board und Schachfiguren | |||
|Das freundlicherweise vom Hersteller Digital Game Technology (DGT) bereitgestellte "DGT Smart Board" wird üblicherweise zur Übertragung von Turnierspielen ins Internet verwendet. Das bedeutet, dass dieses Smart Board die Möglichkeit zum Auslesen der einzelnen Figuren auf den bestimmten Feldern bietet. Die Datenübertragung wird durch ein USB-Kabel sichergestellt. Gleichzeitig kann eine von DGT hergestellte Schachuhr an das Schachbrett angeschlossen werden. Weitere Informationen sind unter der Quelle <ref name = 'DGT Smart Board' > DGT: ''DGT - DGT Smart Board''. 2022. Online im Internet: https://digitalgametechnology.com/products/home-use-e-boards/smart-board-with-indices; Abruf: 08.01.2022</ref> zu finden. | |||
|0 €, Gesponsort vom Hersteller Digital Game Technology (DGT). Bei Chessware: 260€ | |||
|[[Datei:DGT Smartboard.png|126px|mini|zentriert|DGT Smartboard]] | |||
|- | |||
| DGT 3000 Schachuhr | |||
| Die Schachuhr DGT 3000 kann über ein AUX Kabel mit dem DGT Smart Board verbunden werden. Diese Verbindung ermöglicht eine Zeit- und Spielererkennung beim auslesen des Boards. Weitere Informationen zur Schachuhr sind unter der Quelle <ref name = 'DGT 3000' > DGT: ''DGT - DGT3000''. 2022. Online im Internet: https://digitalgametechnology.com/products/chess-clocks/dgt3000-fide-approved; Abruf: 08.01.2022</ref> zu finden. | |||
|65 € | |||
|[[Datei:DGT 3000 Schachuhr.png|126px|mini|zentriert|DGT3000 Schachuhr]] | |||
|- | |||
| Greifbacken | |||
| Um alle Spielfiguren mit einer ausreichenden Genauigkeit greifen und positionieren zu können wird ein neues Greifer-Design benötigt. Der Greifer verfügt über zwei winkelige Auflageflächen, um eine Zentrierung der Figuren zwischen den Greifbacken zu ermöglichen. Dadurch können Figuren selbst dann aufgenommen werden, wenn diese nicht perfekt mittig auf dem Spielfeld positioniert werden. Zusätzlich wird die Länge des Greifers erhöht, um so auch die größten Figuren ohne Kollisionsgefahr greifen zu können. Das 3D-Model ist unter folgendem Link zu finden: https://wiki.hshl.de/wiki/index.php/Datei:Greifbacken.step | |||
| kostenlos, da auf privatem 3D-Drucker erstellt | |||
|[[Datei:Greifbacken Schachspiel.jpg|126px|mini|zentriert|DGT3000 Schachuhr]] | |||
|} | |||
====Software==== | |||
Als Programmierumgebung wird das Robot Operating System (ROS) verwendet. ROS ist eine Softwareanwendung, die bei der Programmierung und Inbetriebnahme von mehrachsigen Greifrobotersystemen zur Anwendung kommt. Dabei besteht ROS aus mehreren Softwarebibliotheken. ROS bietet verschiedenste Werkzeuge. Von Treibern für diverse Roboter bis hin zur graphischen Simulation von Bewegungsabläufen durch RViz. Durch die höhere Verfügbarkeit von Bibliotheken unter dem Linux Betriebssystem (Ubuntu 16.04 bis 20.04) ist eine Nutzung von ROS unter Linux zu empfehlen. | |||
*<b>ROS</b> | |||
**<b>Versionen:</b> Durch die stetige Weiterentwicklung von ROS, finden sich diverse Versionen im Internet. Die aktuellen und noch durch einen Support unterstütze Versionen sind unter: [https://docs.ros.org/ '''www.docs.ros.org''']<ref>[https://docs.ros.org/ ''www.docs.ros.org'']</ref> zu finden. Bei der Auswahl der ROS-Version ist unbedingt auf die Kompatibilität von ROS-Version und benötigter Ubuntu Version zu achten. Je nach Version werden verschiedene Bibliotheken angeboten. Daher sollte nach Möglichkeit im Vorhinein recherchiert werden, welche ROS-Version für die geplante Anwendung benötigt wird. | |||
**<b>Installation:</b> Nach Auswahl der gewünschten ROS-Version wird die Anwendung unter Ubuntu durch Terminalbefehle installiert. Zum Öffnen des Terminals ALT + STRG + T drücken. Hier die nötigen Befehle zur Installation nach und nach in das Terminalfenster kopieren und ausführen. Ebenfalls sollten alle Kommentare zwischen den Befehlen gelesen und befolgt werden. Je nachdem welche Version installiert werden soll ergeben sich ebenfalls andere Installationswege. | |||
**<b>Funktionsweise:</b> Unter [https://docs.ros.org/en/foxy/Tutorials.html '''www.docs.ros.org/en/foxy/Tutorials.html''']<ref>[https://docs.ros.org/en/foxy/Tutorials.html ''www.docs.ros.org/en/foxy/Tutorials.html'']</ref> finden sich diverse Tutorials zur Funktionsweise von ROS2 Foxy. Die allgemeine Funktionsstruktur bleibt sowohl bei älteren als auch neueren ROS-Versionen grundsätzlich dieselbe. Nachfolgend werden die wichtigsten Grundlagen von ROS kurz erklärt | |||
***<b>Nodes:</b> <ref name = 'Understanding ROS 2 nodes'> ROS: ''ROS 2 nodes''. 2022. Online im Internet: https://docs.ros.org/en/foxy/Tutorials/Understanding-ROS2-Nodes.html#nodes-in-ros-2; Abruf: 08.01.2022</ref> Die sogenannten Nodes sind in ROS für die Abgrenzung von verschiedenen Modulen verantwortlich. Dabei haben in ROS die verschiedenen Module eine eigene Aufgabe (z.B. eine Node liest die Umdrehungsfrequenz eines Motors und eine Node ermittelt die Geschwindigkeit). Um einen Datenaustausch zu ermöglichen ist jede Node dazu in der Lage Informationen zu versenden und zu empfangen. Dazu werden allerdings weitere Funktionen innerhalb der Node benötigt: Die sogenannten Topics, Services und Actions. | |||
***<b>Topics:</b> <ref name = 'Understanding ROS 2 topics'> ROS: ''ROS 2 topics''. 2022. Online im Internet: https://docs.ros.org/en/foxy/Tutorials/Topics/Understanding-ROS2-Topics.html; Abruf: 08.01.2022</ref> Durch den modularen Aufbau von komplexen Systemen unter ROS muss eine Kommunikation unter den Nodes hergestellt werden. Dazu werden die ROS-Topics verwendet. Dabei sendet eine Node, durch den sogenannten Publisher an den Subscriber einer anderen Node, eine Nachricht. Das Topic erfüllt hier den Zweck des Weiterleitens der Informationen an die dafür vorgesehene Node. Ein Topic kann dabei nicht auswerten, ob alle an das Topic angeschlossene Nodes die Information erhalten müssen, oder nur einzelne Nodes. Das bedeutet das jede Information mit allen Subscribern, die an das Topic angeschlossen ist, geteilt wird. | |||
***<b>Services:</b> <ref name = 'Understanding ROS 2 services'> ROS: ''ROS 2 services''. 2022. Online im Internet: https://docs.ros.org/en/foxy/Tutorials/Services/Understanding-ROS2-Services.html; Abruf: 08.01.2022</ref> Die sogenannten sevices sind eine weitere Art der Kommunikation von Nodes in ROS. Anders als beim Datenaustausch durch ein Topic, wird hier eine Information erst gesendet, wenn der Service Server eine Anfrage erhalten hat. Dazu wird eine Anfrage innerhalb des Service Clients einer Node an den Service gesendet. Dieser Service unterscheidet dann zunächst, ob es sich bei der Information um eine Anfrage oder eine Antwort handelt. Sofern der Service die Information als Anfrage einstuft, wird diese an den Service Server weitergeleitet. Dabei ist wichtig zu erwähnen, dass es beim Datenaustausch über den ROS-Service immer nur einen Service Server, aber mehrere Service Clients geben kann. Erhält der Service eine Anfrage sendet dieser eine Antwort zurück an den Service. Dieser leitet die Antwort dann an den Service Client zurück. | |||
***<b>Actions:</b> <ref name = 'Understanding ROS 2 actions'> ROS: ''ROS 2 actions''. 2022. Online im Internet: https://docs.ros.org/en/foxy/Tutorials/Understanding-ROS2-Actions.html; Abruf: 08.01.2022</ref> Eine weitere Art der Kommunikation zwischen zwei Nodes ist die ROS-Action. Actions bestehen aus drei Teilen, dem Ziel, einem Feedback und einem Ergebnis. Gleichzeitig vereinen die Actions die Eigenschaften der Services und Topics miteinander. Hier sendet ein Action Client ein Ziel an den Action Server. Über den Feedback Publisher wird nach Annahme des Ziels ein durchgehender Strom aus Informationen an den Feedback Subscriber übermittelt. Dadurch kann der Action Client auswerten, wie viel Abweichung noch zum Zielzustand besteht. Sobald der Zielzustand erreicht ist, wird durch den Action Server eine Antwort an den Client gesendet um beispielsweise eine Bewegung eines Roboters zu beenden. | |||
*<b>RViz</b> | |||
**<b>Getting started with MoveIt and RViz:</b> RViz ist ein Simulationstool das zur Bewegungsplanung und -steuerung verwendet werden kann. Dabei werden die Bewegungsdaten über die Bibliothek "MoveIt" an den Roboter übermittelt. Ein Tutorial für RViz und MoveIt findet sich unter: https://ros-planning.github.io/moveit_tutorials/ <ref name = 'Getting started with MoveIT and RViz'> RViz and MoveIt: ''Getting started with MoveIT and RViz''. 2022. Online im Internet: https://ros-planning.github.io/moveit_tutorials/; Abruf: 08.01.2022</ref> | |||
**<b>Laden des UR3-Modells in RViz Schritt für Schritt:</b> die Nachfolgenden Befehle müssen jeweils in einem einzelnen Terminal in der aufgeführten Reihenfolge eingegeben werden um RViz mitsamt dem UR3-Modell zu starten. | |||
<div style="width:1200px; height:300px; overflow:auto; border: 2px solid #088"> | |||
<pre> | |||
//Lädt den korrekten Workspace und lädt die UR-Description (URDF & SRDF)// | |||
cd catkin_ws | |||
source devel/setup.bash | |||
roslaunch ur_description ur3_upload.launch | |||
//Lädt den korrekten Workspace und lädt die "planning scene" (Umgebungsinformation)// | |||
cd catkin_ws | |||
source devel/setup.bash | |||
roslaunch ur3_moveit_config ur3_moveit_planning_execution.launch | |||
//Lädt den korrekten Workspace und öffnet RViz zur Steuerung// | |||
cd catkin_ws | |||
source devel/setup.bash | |||
roslaunch ur3_moveit_config moveit_rviz.launch config:=true | |||
</pre> | |||
</div> | |||
<br clear = all> | |||
[https://migsvn.hshl.de:65489/usvn/svn/UniversalRobots/Mitarbeiter/2021_Listner_Reuter/ | ===Programmierung/Entwicklung=== | ||
In Schritt [https://migsvn.hshl.de:65489/usvn/svn/UniversalRobots/Mitarbeiter/2021_Listner_Reuter/05_Entwicklung/ '''Entwicklung''']<ref>[https://migsvn.hshl.de:65489/usvn/svn/UniversalRobots/Mitarbeiter/2021_Listner_Reuter/05_Entwicklung/ ''Entwicklung'']</ref> wird die notwendige Software programmiert. Hierbei sind drei unterschiedliche C++-Skripte entwickelt worden. | |||
# Programm zum einmaligen Auslesen des Boards und Ausgabe im Terminal. | |||
# Programm zum kontinuierlichen auslesen und ausgeben der Schachuhr. | |||
# Kombination aus Programm eins und zwei, also kontinuierliches auslesen der Uhr und bei korrekter Spielererkennung Ausgabe des Spielfeldes im Terminal.<br> | |||
Zunächst einmal muss aber die Kommunikation zwischen dem Board und dem Computer hergestellt werden. Dies ist unter Ubuntu nicht einfach als Anfänger. Es müssen bestimmte Flaggen gesetzt oder deaktiviert werden. Zudem muss ermittelt werden, welcher Port der USB-Stecker belegt. In unserem Fall war dies immer <code>/dev/ttyACM0</code>, jedoch ist dies nicht immer zwingend der Fall. Um den Port herauszufinden hilft das Programm [https://www.der-hammer.info/pages/terminal.html HTerm]<ref>[https://www.der-hammer.info/pages/terminal.html ''HTerm Download'']</ref>. Eine genauere Beschreibung zum Auslesen von seriellen Schnittstellen befindet sich [https://blog.mbedded.ninja/programming/operating-systems/linux/linux-serial-ports-using-c-cpp/ hier]<ref>[https://blog.mbedded.ninja/programming/operating-systems/linux/linux-serial-ports-using-c-cpp/ ''mbedded.ninja'']</ref>. Diese schrittweise Befolgung der Anleitung hat uns zu dem folgendem Unterprogramm geführt, welches als Setup vorher ausgeführt werden muss.<br><br> | |||
<b>Setup Funktion</b><br> | |||
Beschreibung: Dient der Vorbereitung des Informationsaustausch via USB.<br> | |||
<div style="width:1200px; height:300px; overflow:auto; border: 2px solid #088"> | |||
<pre> | |||
void setup() | |||
{ | |||
serial_port = open("/dev/ttyACM0", O_RDWR); //"/dev/ttyACM0" ist der angeschlossene Port muss ggf. angepasst werden! | |||
// Check for errors | |||
if (serial_port < 0) | |||
{ | |||
printf("Error %i from open: %s\n", errno, strerror(errno)); | |||
} | |||
// Create new termios struct, we call it 'tty' for convention | |||
// No need for "= {0}" at the end as we'll immediately write the existing | |||
// config to this struct | |||
struct termios tty; | |||
// Read in existing settings, and handle any error | |||
// NOTE: This is important! POSIX states that the struct passed to tcsetattr() | |||
// must have been initialized with a call to tcgetattr() overwise behaviour | |||
// is undefined | |||
if (tcgetattr(serial_port, &tty) != 0) | |||
{ | |||
printf("Error %i from tcgetattr: %s\n", errno, strerror(errno)); | |||
} | |||
tty.c_cflag &= ~PARENB; // Clear parity bit, disabling parity (most common) | |||
tty.c_cflag |= PARENB; // Set parity bit, enabling parity | |||
tty.c_cflag &= ~CSTOPB; // Clear stop field, only one stop bit used in communication (most common) | |||
tty.c_cflag |= CSTOPB; // Set stop field, two stop bits used in communication | |||
tty.c_cflag &= ~CSIZE; // Clear all the size bits, then use one of the statements below | |||
tty.c_cflag |= CS8; // 8 bits per byte (most common) | |||
tty.c_cflag &= ~CRTSCTS; // Disable RTS/CTS hardware flow control (most common) | |||
tty.c_cflag |= CREAD | CLOCAL; // Turn on READ & ignore ctrl lines (CLOCAL = 1) | |||
tty.c_lflag &= ~ICANON; | |||
tty.c_lflag &= ~ECHO; // Disable echo | |||
tty.c_lflag &= ~ECHOE; // Disable erasure | |||
tty.c_lflag &= ~ECHONL; // Disable new-line echo | |||
tty.c_lflag &= ~ISIG; // Disable interpretation of INTR, QUIT and SUSP | |||
tty.c_iflag &= ~(IXON | IXOFF | IXANY); // Turn off s/w flow ctrl | |||
tty.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL); // Disable any special handling of received bytes | |||
tty.c_oflag &= ~OPOST; // Prevent special interpretation of output bytes (e.g. newline chars) | |||
tty.c_oflag &= ~ONLCR; // Prevent conversion of newline to carriage return/line feed | |||
// tty.c_oflag &= ~OXTABS; // Prevent conversion of tabs to spaces (NOT PRESENT IN LINUX) | |||
// tty.c_oflag &= ~ONOEOT; // Prevent removal of C-d chars (0x004) in output (NOT PRESENT IN LINUX) | |||
tty.c_cc[VTIME] = 1; // Wait for up to 1s (10 deciseconds), returning as soon as any data is received. | |||
tty.c_cc[VMIN] = 0; | |||
// Set in/out baud rate to be 9600 | |||
cfsetispeed(&tty, B9600); | |||
cfsetospeed(&tty, B9600); | |||
// Save tty settings, also checking for error | |||
if (tcsetattr(serial_port, TCSANOW, &tty) != 0) | |||
{ | |||
printf("Error %i from tcsetattr: %s\n", errno, strerror(errno)); | |||
} | |||
} | |||
</pre> | |||
</div> | |||
<br clear = all> | |||
Zusätzlich muss in einem Terminal der Befehl <code>$ sudo adduser $USER dialout</code> ausgeführt werden. Hierdurch wird der Fehler errno13 unterbunden, welcher die unzureichende Berechtigung mitteilt (Ausgabe im Terminal: permission denied). Nach dem Ausführen des Befehls muss der PC einmal neugestartet werden, damit die Gruppenänderung in Kraft tritt. Nun hat man die Berechtigung, um auf den USB-Port zugreiffen zu können.<br> | |||
Als nächstes folgen die drei verschiedenen Programme.<br><br> | |||
<b>Board auslesen</b><br> | |||
Beschreibung: Liest das Board einmal aus und gibt die Daten in das Terminal aus. Programm beendet sich nach der einmaligen Ausgabe. Die Ausgabe ist ein 8x8 Feld mit zweistelligen Hex-Werten, welches das Board darstellt.<br> | |||
<div style="width:1200px; height:300px; overflow:auto; border: 2px solid #088"> | |||
<pre> | |||
#include <stdio.h> | |||
#include <string.h> | |||
#include <iostream> | |||
#include <fcntl.h> // Contains file controls like O_RDWR | |||
#include <errno.h> // Error integer and strerror() function | |||
#include <termios.h> // Contains POSIX terminal control definitions | |||
#include <unistd.h> | |||
using namespace std; | |||
int serial_port; | |||
void setup(); | |||
int main() | |||
{ | |||
setup(); //öffnet den Port | |||
char read_buf [100]; //erzeugt ein Array in welches die Daten geschrieben werden | |||
unsigned char msg[1] = {'\x42'}; //die zu übermittelnde Nachricht "DGT_REQ_BOARD" in hex siehe DGT-Manuel | |||
int i=0; //Zähler für for-Schleife | |||
write(serial_port, msg, sizeof(msg)); //Schreibt die Nachricht auf den Port | |||
int n = read(serial_port, &read_buf, sizeof(read_buf)); //liest erhaltene information aus | |||
// n is the number of bytes read. n may be 0 if no bytes were received, and can also be negative to signal an error. | |||
for(i = 3; i<n; i++) //Ausgabe der Information ins Terminal | |||
{ | |||
int HexOut = read_buf[i]; //char zu int konvertierung | |||
if(HexOut<16) //für konstante zweistellige Hexzahlen; vorherige Ausgabe Zahl<16 mit 0 | |||
{ | |||
cout << "0"; | |||
} | |||
cout << hex << HexOut << " "; //Ausgabe der int-zahl als Hex | |||
if((i-2)%8 == 0) //dient der formatierung; erzeugt 8x8 Feld im Terminal | |||
{ | |||
cout << endl; | |||
} | |||
} | |||
close(serial_port); | |||
} | |||
void setup() | |||
{ | |||
serial_port = open("/dev/ttyACM0", O_RDWR); //"/dev/ttyACM0" ist der angeschlossene Port muss ggf. angepasst werden! | |||
// Check for errors | |||
if (serial_port < 0) | |||
{ | |||
printf("Error %i from open: %s\n", errno, strerror(errno)); | |||
} | |||
// Create new termios struct, we call it 'tty' for convention | |||
// No need for "= {0}" at the end as we'll immediately write the existing | |||
// config to this struct | |||
struct termios tty; | |||
// Read in existing settings, and handle any error | |||
// NOTE: This is important! POSIX states that the struct passed to tcsetattr() | |||
// must have been initialized with a call to tcgetattr() overwise behaviour | |||
// is undefined | |||
if (tcgetattr(serial_port, &tty) != 0) | |||
{ | |||
printf("Error %i from tcgetattr: %s\n", errno, strerror(errno)); | |||
} | |||
tty.c_cflag &= ~PARENB; // Clear parity bit, disabling parity (most common) | |||
tty.c_cflag |= PARENB; // Set parity bit, enabling parity | |||
tty.c_cflag &= ~CSTOPB; // Clear stop field, only one stop bit used in communication (most common) | |||
tty.c_cflag |= CSTOPB; // Set stop field, two stop bits used in communication | |||
tty.c_cflag &= ~CSIZE; // Clear all the size bits, then use one of the statements below | |||
tty.c_cflag |= CS8; // 8 bits per byte (most common) | |||
tty.c_cflag &= ~CRTSCTS; // Disable RTS/CTS hardware flow control (most common) | |||
tty.c_cflag |= CREAD | CLOCAL; // Turn on READ & ignore ctrl lines (CLOCAL = 1) | |||
tty.c_lflag &= ~ICANON; | |||
tty.c_lflag &= ~ECHO; // Disable echo | |||
tty.c_lflag &= ~ECHOE; // Disable erasure | |||
tty.c_lflag &= ~ECHONL; // Disable new-line echo | |||
tty.c_lflag &= ~ISIG; // Disable interpretation of INTR, QUIT and SUSP | |||
tty.c_iflag &= ~(IXON | IXOFF | IXANY); // Turn off s/w flow ctrl | |||
tty.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL); // Disable any special handling of received bytes | |||
tty.c_oflag &= ~OPOST; // Prevent special interpretation of output bytes (e.g. newline chars) | |||
tty.c_oflag &= ~ONLCR; // Prevent conversion of newline to carriage return/line feed | |||
// tty.c_oflag &= ~OXTABS; // Prevent conversion of tabs to spaces (NOT PRESENT IN LINUX) | |||
// tty.c_oflag &= ~ONOEOT; // Prevent removal of C-d chars (0x004) in output (NOT PRESENT IN LINUX) | |||
tty.c_cc[VTIME] = 1; // Wait for up to 1s (10 deciseconds), returning as soon as any data is received. | |||
tty.c_cc[VMIN] = 0; | |||
// Set in/out baud rate to be 9600 | |||
cfsetispeed(&tty, B9600); | |||
cfsetospeed(&tty, B9600); | |||
// Save tty settings, also checking for error | |||
if (tcsetattr(serial_port, TCSANOW, &tty) != 0) | |||
{ | |||
printf("Error %i from tcsetattr: %s\n", errno, strerror(errno)); | |||
} | |||
} | |||
</pre> | |||
</div> | |||
<br clear = all> | |||
<b>Uhr auslesen</b><br> | |||
Beschreibung: Liest die Uhr kontinuierlich aus und gibt die Daten in das Terminal aus. Programm beendet sich nicht selbstständig. Hierfür <code>STRG + C</code> ins Terminal eingeben. Es werden hierbei insgesamt 10 Bytes eingelesen, aber nur Bytes 4-10 Ausgegeben. Die ersten drei Bytes sind Header Bytes und können ignoriert werden.<br> Die ausgegebenen 7 Bytessind folgende: <br> | |||
*Bytes 4-6: Zeit Spieler Weiß (Stunde|Minute|Sekunde)<br> | |||
*Bytes 7-9: Zeit Spieler Schwarz (Stunde|Minute|Sekunde)<br> | |||
*Byte 10: Spieler aktivität (z.B. Welcher Spieler dran ist)<br> | |||
Zusätzlich werden in diesem Modus auch Feldaktualisierungen gemeldet. Hierbei werden bei Bewegung einer Figur Zusätzlich ins Terminal je zwei Bytes geschrieben. Byte eins gibt das Feld an, welches sich verändert hat. Und Byte zwei gibt an welche Figur sich auf dem Feld nun befindet (für die Aufschlüsselung Feld und Figur siehe DGT-Manuel). Da beim Bewegen einer Figur für gewöhnlich zwei Felder aktualisiert werden (Start Feld und Ziel Feld) werden dementsprechend auch zwei mal zwei Bytes ausgegeben. ACHTUNG! bei Figuren die sich mehr als ein Feld weit bewegen können, kann es passieren, dass auch mehr als zwei aktualisierte Felder angezeigt werden (z.B. wenn ein Turm vier Felder über das Board ohne anheben nach vorne geschoben wird). | |||
<div style="width:1200px; height:300px; overflow:auto; border: 2px solid #088"> | |||
<pre> | |||
#include <stdio.h> | |||
#include <string.h> | |||
#include <iostream> | |||
#include <fcntl.h> // Contains file controls like O_RDWR | |||
#include <errno.h> // Error integer and strerror() function | |||
#include <termios.h> // Contains POSIX terminal control definitions | |||
#include <unistd.h> | |||
using namespace std; | |||
int serial_port; | |||
void setup(); | |||
int main() | |||
{ | |||
setup(); //öffnet den Port | |||
char read_buf[100]; //erzeugt ein Array in welches die Daten geschrieben werden | |||
unsigned char msg[] = { '\x08' }; //die zu übermittelnde Nachricht "DGT_CMD_CLOCK_BUTTON" in hex siehe DGT-Manuel | |||
//Da die Uhr nicht immer auf Hexzahlen anspricht ggf.mit ASCII übermitteln | |||
int i = 0; //Zähler für for-Schleife | |||
int HexOut = 0; //Variable zum konvertieren char zu int | |||
int n = 0; //Variable zum ermitteln, wie viel Bytes empfangen wurden | |||
write(serial_port, msg, sizeof(msg)); //Schreiben der Nachricht an USB-Port | |||
//Nach erhalt der Nachricht sendet die Uhr kontinuierlich, | |||
//sobald sich etwas in der Anzeige verändert | |||
while (true) | |||
{ | |||
n = read(serial_port, &read_buf, sizeof(read_buf)); //liest erhaltene Information aus | |||
if (n>0) //gibt nur Daten ins Terminal aus, wenn welche empfangen wurden | |||
{ | |||
for (i = 3; i<n; i++) | |||
{ | |||
HexOut = read_buf[i]; //char zu int konvertierung | |||
if (HexOut<16) //für konstante zweistellige Hexzahlen; vorherige Ausgabe Zahl<16 mit 0 | |||
{ | |||
cout << "0"; | |||
} | |||
cout << hex << HexOut << " "; //Ausgabe der int-zahl als Hex | |||
} | |||
cout << endl; //Zeilenumbruch; WICHTIG!!! ohne Zeilenumbruch gibt das Terminal | |||
//die Informationen erst nach einem overflow aus! | |||
} | |||
usleep(100); //Systempause | |||
} | |||
close(serial_port); | |||
} | |||
void setup() | |||
{ | |||
serial_port = open("/dev/ttyACM0", O_RDWR); | |||
// Check for errors | |||
if (serial_port < 0) | |||
{ | |||
printf("Error %i from open: %s\n", errno, strerror(errno)); | |||
} | |||
// Create new termios struct, we call it 'tty' for convention | |||
// No need for "= {0}" at the end as we'll immediately write the existing | |||
// config to this struct | |||
struct termios tty; | |||
// Read in existing settings, and handle any error | |||
// NOTE: This is important! POSIX states that the struct passed to tcsetattr() | |||
// must have been initialized with a call to tcgetattr() overwise behaviour | |||
// is undefined | |||
if (tcgetattr(serial_port, &tty) != 0) | |||
{ | |||
printf("Error %i from tcgetattr: %s\n", errno, strerror(errno)); | |||
} | |||
tty.c_cflag &= ~PARENB; // Clear parity bit, disabling parity (most common) | |||
tty.c_cflag |= PARENB; // Set parity bit, enabling parity | |||
tty.c_cflag &= ~CSTOPB; // Clear stop field, only one stop bit used in communication (most common) | |||
tty.c_cflag |= CSTOPB; // Set stop field, two stop bits used in communication | |||
tty.c_cflag &= ~CSIZE; // Clear all the size bits, then use one of the statements below | |||
tty.c_cflag |= CS8; // 8 bits per byte (most common) | |||
tty.c_cflag &= ~CRTSCTS; // Disable RTS/CTS hardware flow control (most common) | |||
tty.c_cflag |= CREAD | CLOCAL; // Turn on READ & ignore ctrl lines (CLOCAL = 1) | |||
tty.c_lflag &= ~ICANON; | |||
tty.c_lflag &= ~ECHO; // Disable echo | |||
tty.c_lflag &= ~ECHOE; // Disable erasure | |||
tty.c_lflag &= ~ECHONL; // Disable new-line echo | |||
tty.c_lflag &= ~ISIG; // Disable interpretation of INTR, QUIT and SUSP | |||
tty.c_iflag &= ~(IXON | IXOFF | IXANY); // Turn off s/w flow ctrl | |||
tty.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL); // Disable any special handling of received bytes | |||
tty.c_oflag &= ~OPOST; // Prevent special interpretation of output bytes (e.g. newline chars) | |||
tty.c_oflag &= ~ONLCR; // Prevent conversion of newline to carriage return/line feed | |||
// tty.c_oflag &= ~OXTABS; // Prevent conversion of tabs to spaces (NOT PRESENT IN LINUX) | |||
// tty.c_oflag &= ~ONOEOT; // Prevent removal of C-d chars (0x004) in output (NOT PRESENT IN LINUX) | |||
tty.c_cc[VTIME] = 1; // Wait for up to 1s (10 deciseconds), returning as soon as any data is received. | |||
tty.c_cc[VMIN] = 0; | |||
// Set in/out baud rate to be 9600 | |||
cfsetispeed(&tty, B9600); | |||
cfsetospeed(&tty, B9600); | |||
// Save tty settings, also checking for error | |||
if (tcsetattr(serial_port, TCSANOW, &tty) != 0) | |||
{ | |||
printf("Error %i from tcsetattr: %s\n", errno, strerror(errno)); | |||
} | |||
} | |||
= | </pre> | ||
</div> | |||
<br clear = all> | |||
<b>Kombination aus Uhr auslesen und Board auslesen</b><br> | |||
Beschreibung: Liest kontinuierlich die Schachuhr aus. Sobald das Programm in Byte 10 (wie im vorherigen Programm beschrieben) 0x13 (Spieler Schwarz) empfängt, wird das Board einmal ausgelesen und ins Terminal ausgegeben. Desweiteren läuft ein Zähler mit, welcher die Periode mit ausgibt (hilft auch beim Debuggen). Daraufhin wird eine Flagge gesetzt, welche dafür sorgt, dass das Board nicht erneut ausgeben wird, solange Spieler Schwarz noch am Zug ist. Sobald das Programm in Byte 10 0x09 (Spieler Weiß) empfängt, wird die gesetzte Flagge zurückgesetzt. | |||
<div style="width:1200px; height:300px; overflow:auto; border: 2px solid #088"> | <div style="width:1200px; height:300px; overflow:auto; border: 2px solid #088"> | ||
<pre> | <pre> | ||
#include <stdio.h> | |||
#include <string.h> | |||
#include <iostream> | |||
#include <fcntl.h> // Contains file controls like O_RDWR | |||
#include <errno.h> // Error integer and strerror() function | |||
#include <termios.h> // Contains POSIX terminal control definitions | |||
#include <unistd.h> | |||
using namespace std; | |||
int serial_port; | |||
void setup(); | |||
/************************************************************************************/ | |||
int main() | |||
{ | |||
char read_buf[100]; //Array anlegen in denen die erhaltenen Daten eingelesen werden | |||
unsigned char msg1[] = { '\x08' }; //Der Befehl "DGT_CMD_CLOCK_BUTTON" in Hex | |||
unsigned char msg2[] = { '\x42' }; //Der Befehl "DGT_REQ_BOARD" in Hex | |||
unsigned char msg3[] = { '\x03' }; //Der Befehl "DGT_CMD_CLOCK_END" in Hex | |||
int ControlFlag = 1; //Kontroll Flagge als Indikator, dass das Board bereits ausgegeben wurde; am Anfang 1, um anfängliche falsch Übertragungen zu verhindern! | |||
int i = 0; //Zähler für "for" Schleifen | |||
int HexOut = 0; //Variable dient zum konvertieren "char" zu "int" | |||
int n = 0; //Variable dient zum Kontrollieren, dass informationen vom Board erhalten wurden | |||
int p = 0; //Zähler der Perioden für Spieler 2 | |||
setup(); //ermöglicht den Zugriff auf die Schnittstelle zum Board; vollständige Funktion unten! | |||
while (true) //dient als Dauerschleife zum empfangen der Daten; Abbruchbedingung bei Spielende muss noch hinzugefügt werden! | |||
{ | |||
/*********************************************************/ | |||
//Do-Schleife dient zum stetigen abfragen des Spielers | |||
do { | |||
write(serial_port, msg1, sizeof(msg1)); | |||
usleep(100); | |||
n = read(serial_port, &read_buf, sizeof(read_buf)); | |||
HexOut = read_buf[9]; | |||
if (HexOut == '\x09') //Wenn Spieler1 am Zug ist wird die Kontrollflagge zurückgesetzt | |||
{ | |||
ControlFlag = 0; //Reset der Kontrollflagge | |||
} | |||
} while (HexOut != '\x13'); //Abbruchbedingung ist der Spielerwechsel (wenn Spieler2 am Zug ist wird die Schleife verlassen) | |||
/*********************************************************/ | |||
if (ControlFlag == 0) //Wenn Spieler2 am Zug ist UND noch NICHT das Board ausgelesen wurde, wird das Board ausgelesen | |||
{ | |||
ControlFlag = 1; //Setze Kontrollflagge auf 1 um die erneute Ausgabe des Boards im selben Zug zu unterbinden! | |||
write(serial_port, msg2, sizeof(msg2)); //Übertragung des Befehls 0x42 an Board | |||
usleep(100); //Systempause um eventuelle verzögerungen des Boards und entsprechenden Auslesefehler zu vermeiden | |||
n = read(serial_port, &read_buf, sizeof(read_buf)); //Auslesen der Informationen des Boards; n entspricht der Länge des übermittelten "char" Arrays | |||
for (i = 3; i<n; i++) //Daten werden mittels for-Schleife auf Terminal ausgegeben | |||
{ | |||
HexOut = read_buf[i]; //Konvertiere char zu int | |||
if (HexOut<16) //Um durchgängig zwei Ziffern auszugeben wird bei hex-zahlen kleiner 16 noch eine 0 zuvor ausgegeben | |||
{ | |||
cout << "0"; | |||
} | |||
cout << hex << HexOut << " "; //Ausgabe in Hex | |||
if ((i - 2) % 8 == 0) //erzeugt in dem Terminal ein 8x8 Feld | |||
{ | |||
cout << endl; | |||
} | |||
} | |||
p = p + 1; //Zählt die Periode | |||
cout << endl << "Periode: " << dec << p; //gibt die Periode in Terminal aus | |||
cout << endl << "-------------------------------------------------" << endl; //dient der übersichtlichkeit | |||
} | |||
usleep(100); | |||
} | |||
close(serial_port); | |||
} | |||
/****************************************************************************************/ | |||
// Nichts unter dieser Linie verändern!!! | |||
/**************************************************************************************/ | |||
void setup() | |||
{ | |||
serial_port = open("/dev/ttyACM0", O_RDWR); | |||
// Check for errors | |||
if (serial_port < 0) { | |||
printf("Error %i from open: %s\n", errno, strerror(errno)); | |||
} | |||
// Create new termios struct, we call it 'tty' for convention | |||
// No need for "= {0}" at the end as we'll immediately write the existing | |||
// config to this struct | |||
struct termios tty; | |||
// Read in existing settings, and handle any error | |||
// NOTE: This is important! POSIX states that the struct passed to tcsetattr() | |||
// must have been initialized with a call to tcgetattr() overwise behaviour | |||
// is undefined | |||
if (tcgetattr(serial_port, &tty) != 0) { | |||
printf("Error %i from tcgetattr: %s\n", errno, strerror(errno)); | |||
} | |||
tty.c_cflag &= ~PARENB; // Clear parity bit, disabling parity (most common) | |||
tty.c_cflag |= PARENB; // Set parity bit, enabling parity | |||
tty.c_cflag &= ~CSTOPB; // Clear stop field, only one stop bit used in communication (most common) | |||
tty.c_cflag |= CSTOPB; // Set stop field, two stop bits used in communication | |||
tty.c_cflag &= ~CSIZE; // Clear all the size bits, then use one of the statements below | |||
tty.c_cflag |= CS8; // 8 bits per byte (most common) | |||
tty.c_cflag &= ~CRTSCTS; // Disable RTS/CTS hardware flow control (most common) | |||
tty.c_cflag |= CREAD | CLOCAL; // Turn on READ & ignore ctrl lines (CLOCAL = 1) | |||
tty.c_lflag &= ~ICANON; | |||
tty.c_lflag &= ~ECHO; // Disable echo | |||
tty.c_lflag &= ~ECHOE; // Disable erasure | |||
tty.c_lflag &= ~ECHONL; // Disable new-line echo | |||
tty.c_lflag &= ~ISIG; // Disable interpretation of INTR, QUIT and SUSP | |||
tty.c_iflag &= ~(IXON | IXOFF | IXANY); // Turn off s/w flow ctrl | |||
tty.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL); // Disable any special handling of received bytes | |||
tty.c_oflag &= ~OPOST; // Prevent special interpretation of output bytes (e.g. newline chars) | |||
tty.c_oflag &= ~ONLCR; // Prevent conversion of newline to carriage return/line feed | |||
// tty.c_oflag &= ~OXTABS; // Prevent conversion of tabs to spaces (NOT PRESENT IN LINUX) | |||
// tty.c_oflag &= ~ONOEOT; // Prevent removal of C-d chars (0x004) in output (NOT PRESENT IN LINUX) | |||
tty.c_cc[VTIME] = 1; // Wait for up to 1s (10 deciseconds), returning as soon as any data is received. | |||
tty.c_cc[VMIN] = 0; | |||
// Set in/out baud rate to be 9600 | |||
cfsetispeed(&tty, B9600); | |||
cfsetospeed(&tty, B9600); | |||
// Save tty settings, also checking for error | |||
if (tcsetattr(serial_port, TCSANOW, &tty) != 0) { | |||
printf("Error %i from tcsetattr: %s\n", errno, strerror(errno)); | |||
} | |||
} | |||
Zeile 68: | Zeile 649: | ||
</div> | </div> | ||
<br clear = all> | <br clear = all> | ||
Dies sind Lauffähige C++-Programme, siehe nachfolgend in dem Kapitel Komponententest entsprechende Beispiele. Jedoch sind dies noch keine Nodes welche in ROS eingebunden werden können. Diese Skripte müssen dementsprechend noch zu Nodes konvertiert werden! Außerdem sind diese C++-Skripte Vorlagen zur ersten von vier geplanten Nodes. | |||
===Komponententest=== | ===Komponententest=== | ||
Nach dem Erhalt der Komponenten müssen diese zunächst auf deren Funktionsfähigkeit überprüft werden. Die Funktionsfähigkeit wurde durch selbstgeschriebene C++-Skripte verifiziert. Die unten stehende Tabelle zeigt die Ergbenisse. | |||
{| class="mw-datatable" | |||
! style="font-weight: bold;" | Komponenten | |||
! style="font-weight: bold;" | Beschreibung | |||
! style="font-weight: bold;" | Bild | |||
|- | |||
|DGT Smart Board | |||
|Zu sehen im rechten Bild ist die Ausgabe des Programms SampleProgramm.cpp im Terminal. Hier wird eine 8x8-Matrix, welche das Schachbrett abbildet, dargestellt. Oben links beginnend mit dem Feld A8 und unten rechts abschließend mit dem Feld H1. Hierbei wurde die Startaufstellung ausgelesen. 0x01-0x06 sind Figuren des weißen Spielers und 0x07-0x0c die des schwarzen Spielers. Zusätzlich ist ein Zähler zu sehen, der ausgibt in welcher Periode die Ausgabe statt findet. | |||
|[[Datei:Ergebnis Komponententest Board auslesen.jpg|126px|mini|zentriert|Ergebnis Komponententest Smart Board]] | |||
|- | |||
| DGT 3000 Schachuhr | |||
| Im rechten Bild ist die Ausgabe des Programms UhrAuslesen.cpp im Terminal zu sehen. Ausgegeben werden sieben Bytes, die als hexadezimale Zahlen dargestellt werden. Die ersten drei Zahlen zeigen die verbleibende Zeit des schwarzen Spielers an die darauffolgenden drei Bytes die verbleibende Spielzeit des weißen Spielers. Die letzte Zahl gibt an welcher Spieler aktuell am Zug ist. OBACHT! Die hexadezimale Zahle muss NICHT umgerechnet werden! Die Schachuhr zählt intern in Hex aber die dargestellten Zahlen sind wie Dezimalzahlen zu lesen.<br> | |||
Beispiel Zeile 1 im Bild: 0x00 0x59 0x19 -> 00h 59min 19sec <br> | |||
Die letzte Zahl in den Zeilen ist 0x13 welches Spieler schwarz darstellt. Für ihn läuft im Bild die Zeit runter. Die 0x12 in der letzten Zeile zeigt, dass im Zug von schwarz die Uhr pausiert wurde. Bei weiß wäre dies 0x08, da weiß als 0x09 ausgegeben wird. | |||
|[[Datei:Ergebnis Komponententest Schachuhr auslesen.jpg|126px|mini|zentriert|Ergebnis Komponententest Schachuhr DGT3000]] | |||
|} | |||
===Integrationstest=== | ===Integrationstest=== | ||
noch nicht erfolgt | |||
===Systemtest=== | ===Systemtest=== | ||
noch nicht erfolgt | |||
===Abnahmetest=== | ===Abnahmetest=== | ||
noch nicht erfolgt | |||
===Literatur=== | |||
weitere nützliche [https://migsvn.hshl.de:65489/usvn/svn/UniversalRobots/Mitarbeiter/2021_Listner_Reuter/10_Literatur/ '''sonstige Literatur''']<ref>[https://migsvn.hshl.de:65489/usvn/svn/UniversalRobots/Mitarbeiter/2021_Listner_Reuter/10_Literatur/ ''sonstige Literatur'']</ref> ist unter diesem Punkt zu finden. | |||
== Zusammenfassung == | == Zusammenfassung == | ||
[[Datei:Fortschirtt im V-Modell Stand 22.01.2022.JPG|rechts|Abbildung 1: Fortschritt des Projektes visualisiert im V-Modell Stand 22.01.2022|400px |mini]] | |||
Das Projekt "Schachspiel mittels kollaborierendem UR-Roboter unter ROS" ist im 7. Semester des Praktikums Produktionstechnik VII erfolgt. Hierbei konnten die im Studium erlernten Fähigkeiten angewendet werden. Mit Hilfe des V-Modells ist das neue Projekt entworfen und entwickelt worden. Das strukturierte Vorgehen des V-Modells hilft bei der Planung und Entwicklung des Projektes, jedoch können auch durch die Anwendung des Modells nicht sämtliche Probleme vorausgeahnt werden. Im unserem Projekt beispielsweise die Einbindung des Robotermodells in RViz. Allerdings kann durch das kleinschrittige und strukturierte Vorgehen der Umfang des Projektes im Vorhinein gut abgegrenzt werden. Abschließend kann gesagt werden, dass das Praktikum das eigenständige Arbeiten schult und einen gut auf den realen Berufsalltag vorbereitet.<br> | |||
Zusammenfassend lässt sich sagen, dass das Projekt leider nicht zu einem fertigen Abschluss gebracht werden konnte. Allerding konnten einige Schritte des V-Modells abgearbeitet werden um möglichen zukünftigen Studierenden den Abschluss des Projektes zu erleichtern. So konnten die ersten vier Schritte des V-Modells (Anforderungsdefinition, funktionaler Systementwurf, technischer Systementwurf und Komponentenspezifikation) erfolgreich bearbeitet werden. Das Projekt befindet sich nun zwischen Schritt fünf und sechs. Teile der Programmierung konnten bereits abgeschlossen werden und können als funktionsfähige C++-Skripte zur Verfügung gestellt werden. Mit Hilfe der fertigen Skripte konnten ebenfalls die beschafften Komponenten bereits auf deren Funktionstüchtigkeit überprüft werden. Wie im Ausblick beschrieben hält das Projekt noch viele spannende Aufgaben für zukünftige Jahrgänge bereit.<br><br><br><br> | |||
== Ausblick == | == Ausblick == | ||
Da nicht alle Tätigkeiten die zur Umsetzung des Projektes benötigt waren umgesetzt werden konnten, werden nachfolgend einige noch offene Aufgaben aufgeführt und kurz erläutert. | |||
*vorhandenes C++-Sktipt in ROS-Node umwandeln: Da ROS mit einzelnen Nodes arbeitet und diese mit der C++ Syntax programmiert werden können, muss das vorliegende und funktionstüchtige C++-Skript zum auslesen der Board- und Uhreninformationen in eine ROS-Node transferiert werden. | |||
*Verbindung zwischen UR3 und RViz herstellen: Für die Bewegungsplanung über ROS/RViz muss die Verbindung zwischen UR3 und Computer über die Ethernet Schnittstelle hergestellt werden. | |||
*Node 2 - 4 erstellen: Um das System zu vervollständigen muss die Schachengine "Stockfish" durch eine Node angesprochen und alle notwendigen Informationen der Schachengine zur Verfügung gestellt werden. Nachfolgend muss das mapping der Koordinaten durch eine Node und die Robotersteuerung durch eine Node programmiert werden. | |||
*Weiterverfolgung des V-Modells: Nach Vervollständigung der Programmierung kann dann mit dem Intrgrations-, Systems- und Abnahmetest fortgefahren werden | |||
== Probleme und Lösungen == | == Probleme und Lösungen == | ||
*<b>ROS2 Kompatibilitätsprobleme:</b>Unter [https://github.com/UniversalRobots/Universal_Robots_ROS2_Driver'''Universal Robots ROS2 Treiber''']<ref>Universal Robots ROS2 Treiber ''https://github.com/UniversalRobots/Universal_Robots_ROS2_Driver''], Abruf: 22.01.2022</ref> finden sich diverse Treiber für die unterschiedlichen Universal Robots Greifarme zum Download. Leider war es uns nicht möglich die Treiber unter ROS2 Foxy zu installieren. Während des Schrittes "Colcon_build" kam es immer wieder an unterschiedlichsten Stellen zu einem "Fatal_Error". Der Grund könnte das noch frühe Entwicklungsstadium der ROS2 Foxy Treiber sein. Daher haben wir uns dazu entschieden die ROS Version zu wechseln. Verwendet wird nun ROS Noetic. Diese Treiber werden unter [https://github.com/UniversalRobots/Universal_Robots_ROS_Driver '''Universal Robots ROS Treiber''']<ref>Universal Robots ROS Treiber ''https://github.com/UniversalRobots/Universal_Robots_ROS_Driver''], Abruf: 22.01.2022</ref> zur Verfügung gestellt. Die Installation dieser Treiber funktionierte reibungslos. | |||
*<b>UR3-Greiferdesign:</b> Der bereits für den UR3 vorhandene Greifer bietet nicht die Möglichkeit die Schachfiguren aufnehmen zu können. Durch die Höhe der Figuren würde es zu Kollisionen mit zwischen Roboter und Figur kommen, sofern die Figuren am Sockel gegriffen werden sollen. Ein Greifen am Sockel der Figuren ist zu empfehlen, da die Figuren alle über einen Runden Sockel mit lediglich unterschiedlichen Durchmessern verfügen. Durch eine Dreipunktaufnahme können die Figuren so gegriffen und zentriert werden, um ein präzises Greifen und Platzieren jederzeit ermöglichen zu können. Die Entwickelten Greifer sind unter dem Punkt [https://migsvn.hshl.de:65489/usvn/svn/UniversalRobots/Mitarbeiter/2021_Listner_Reuter/05_Entwicklung/ '''Entwicklung''']<ref>[https://migsvn.hshl.de:65489/usvn/svn/UniversalRobots/Mitarbeiter/2021_Listner_Reuter/05_Entwicklung/ ''Entwicklung'']</ref> zu finden. | |||
*<b>Einbindung von Robotermodell in RViz:</b> Das Einbinden des UR3-Modells in RViz stellte eine große Herausforderung dar. Die Lösung lag in der korrekten Ausführung aller Befehle in der richtigen Reihenfolge wie unter "Laden des UR3-Modells in RViz Schritt für Schritt:" beschrieben. Durch das schrittweise Befolgen und Eingeben der Befehle in jeweils eigenständige Terminals kann das UR3-Modell erfolgreich in RViz geladen werden. | |||
*<b>passende Hardware</b>: Eine große Frage, die sich bereits früh im Projektverlauf gestellt hatte, war die Frage nach der Art und Weise der Erkennung des Spielfelds und der Figuren. Nach eine Recherche haben wir uns an den Smart Board Hersteller DGT gewandt und nach Informationen zu den Schachbrettern gefragt. Kontakt hierfür war Herr Thijs van Zeijts des Kundensupports. Das Niederländische Unternehmen DGT war so freundlich und hat uns ein Smart Board mitsamt Spielfiguren kostenlos zur Verfügung gestellt. Außerdem wurde eine Beschreibung der Kommunikationsprotokolls mit Befehlen für das Smart Board zur Verfügung gestellt. Dieses ist unter dem Punkt "Literatur" zu finden.<br> | |||
== Ungelöste Probleme == | |||
{| class="mw-datatable" | |||
! style="font-weight: bold;" | Problem | |||
! style="font-weight: bold;" | Beschreibung | |||
! style="font-weight: bold;" | Lösungsansatz | |||
! style="font-weight: bold;" | Ergebnis | |||
|- | |||
|rowspan="2"|Verbindung zwischen UR3 und Ubuntu-PC / ROS | |||
|rowspan="2"|Verbindungsaufbau zwischen Ubuntu-PC / ROS und dem UR3-Roboter ist nicht möglich | |||
|Anpassung der IP-Adressen von Roboter und Ubuntu-PC. Die beiden Geräte befanden sich zu Beginn nicht im selben Netzwerk, wodurch selbst ein Anpingen über das Terminal und den Befehl <code>ping [IP-Adresse]</code> nicht möglich war | |||
|Anpingen des UR3-Roboters nun erfolgreich über das Terminal möglich, jedoch weiterhin keine Verbindung in RViz möglich | |||
|- | |||
|Durch den Befehl <code>roslaunch ur_robot_driver <robot_type>_bringup.launch robot_ip:=IP-Adress</code> soll der Treiber des UR3-Roboters gestartet werden. Gleichzeitig soll ROS durch diesen Befehl die Verbindung zum UR3 herstellen | |||
|Kein Verbindungsaufbau möglich. Befehl wird nach kurzer Laufzeit aufgrund von unbekannten Fehler abgebrochen | |||
|- | |||
|Senden der Befehle an Schachuhr inkonsistent | |||
|Die Programme sind lauffähig und geben in der Regel die Informationen wie gewollt im Terminal aus. In seltenen Fällen erfolgt die Ausgabe dauerhaft, und nicht wie gewollt einmal pro Periode | |||
|Die Ursache des Problems ist unbekannt. Als Lösungsansatz kann daher nur der Neustart des Systems genutzt werden | |||
|Durch einen wiederholten Neustart des Systems kann der Fehler behoben werden | |||
|} | |||
== Literaturverzeichnis == | == Literaturverzeichnis == |
Aktuelle Version vom 22. Februar 2022, 10:34 Uhr
Autoren: Manfred Listner, Benjamin Reuter
Betreuer: Prof. Dr. Mirek Göbel & Marc Ebmeyer
Wintersemester: 2021/2022
Fachsemester: 7
zurück zum Hauptartikel: Praktikum Produktionstechnik
Vorab wollen wir uns herzlich bei Digital Games Technology (DGT) bedanken. Diese haben uns bei unserem Projekt unterstützt und auch das Schachbrett samt der Figuren kostenlos zur Verfügung gestellt.
Einleitung
Als Teil des Bachelorstudiengangs Mechatronik an der HSHL, wird im siebten Semester das Fach Produktiontechnik VII unterrichtet. Semesterbegleitend wird dieses Fach als Praktikum durchgeführt. In diesem Praktikum sollen die Studierenden ein mechatronisches Projekt in kleinen Gruppen selbstständig bearbeiten. Das hier beschriebene Projekt handelt vom Schachspiel eines Roboterarms (UR3) unter ROS.
Aufgabenstellung
Aufgabe ist es, dem von der HSHL bereitgestellten Roboterarm UR3 das Schachspielen beizubringen. Dazu soll die Software "Robot Operation System" (ROS) verwendet werden. Das System soll letzten Endes dazu in der Lage sein gegen einen menschlichen Gegenspieler anzutreten. Dazu benötigt das System eine Möglichkeit zur Erkennung des Spielfelds und eine Positionserkennung der einzelnen Spielfiguren. Zusätzlich muss das System dazu in der Lage sein, das Spielfeld auswerten und korrekte Spielzüge ausführen zu können.
Robot Operating System (ROS)
Dies ist ein Entwicklungssystem speziell für roboterbasierte Anwendungen. Hierbei wird zwischen Knoten (Nodes) kommuniziert. Jeder Knoten hat eine eigene Aufgabe, in diesem Projekt z.B. das Ansteuern des Roboters. Diese Kommunikation findet über "Topics", "Services" oder "Actions" statt. Genauere Tutorials zur Installation und den funktonsweisen der Kommunikation befinden sich hier: ROS Tutorials
In diesem Projekt wird ROS Noetic verwendet, da hierfür bereits die notwendigen Treiber für den Roboter existieren.
Vorgehensweise nach V-Modell
Zur strukturierten Bearbeitung des Projektes wird nach dem V-Modell vorgegangen (vgl. Abbildung 1). Durch dieses vorgehen kann sichergestellt werden, dass alle notwendigen Schritte die zur erfolgreichen Bearbeitung eines Projektes benötigt werden auch durchgeführt werden.
Die Verwaltung der verschiedenen erstellten Dokumente wird über das Programm TortoiseSVN[1] gewährleistet. Nachfolgend werden die Dokumente unter den jeweiligen Punkten des V-Modells als Link zum Download bereitgestellt.
Anforderungsdefinition
Die an das Projekt gestellten Anforderungen werden im ersten Schritt, der Anforderungsdefinition[2], des V-Modelles in der Anforderungsliste definiert. Dabei wird die Anforderungsliste in verschiedene Kategorien unterteilt:
- Anforderungen an das System
- Sicherheit
- Schnittstellen
- Software / Werkzeuge
- Dokumentation
Funktionaler Systementwurf
Der funktionaler Systementwurf[3], zeigt in einer schematischen Darstellung die Struktur und Verknüpfung der einzelnen in ROS zu programmierenden Nodes (vgl. Abbildung 2). Der Aufbau der vier geplanten Nodes lässt sich wie folgt beschreiben:
- Node 1: Sendet die Befehle
0x08 (DGT_CMD_CLOCK_BUTTON)
zum anfordern der Schachuhrdaten &0x42 (DGT_REQ_BOARD)
zum anfordern der Spielfeldinformationen an das Smart Board - Node 1: Sendet die erhaltenen Spielfeldinformationen über ein Topic an Node 2.
- Node 2: Wertet über die Einbindung der Stockfish-Schachengine die Spielfeldinformationen aus und ermittelt den Zug den der Roboter ausführen muss.
- Node 2: Sendet den ermittelten Spielzug (Figur, von Feld XY, nach Feld YZ) an Node 3.
- Node 3: Ermittelt die Koordinaten die dem Roboter zur Bewegungsausführung bereitgestellt werden müssen.
- Node 3: Sendet die Befehle über ein weiteres Topic an Node 4.
- Node 4: Verantwortlich für die auszuführenden UR_Befehle
Zusätzlich findet sich unter dem o.g. Link ein Programmablaufplan (PAP) der Auskunft über den generellen Ablauf und Rahmenbedingungen die bei der Zugdurchführung zu beachten sind.
Technischer Systementwurf
Im technischen Systementwurf[4] werden die verwendeten Komponenten und deren Schnittstellen übersichtlich dargestellt. Gleichzeitig wird hier gezeigt, wie die Komponenten untereinander verbunden sind. Der Datenaustausch zwischen dem DGT Smart Board und dem Ubuntu-PC erfolgt über einen USB-C Anschluss. Dabei sendet das Board die Daten in Form von Hexadezimalzahlen. Die Verbindung zwischen Smart Board und Schachuhr erfolgt über einen AUX-Anschluss. Die Uhr sendet, wie auch das Smart Board, die Daten in Hexadezimalzahlen. Die Verbindung zwischen dem Ubuntu-PC und dem UR3-Roboter, erfolgt über einen Ethernet Anschluss.
Komponentenspezifikation
In der Komponentenspezifikation[5] wird definiert, welche Komponenten konkret für die Erfüllung der Aufgaben verwendet werden sollen. Dabei wurden die Komponenten in Hardware und Software unterteilt.
Hardware
Komponenten | Beschreibung | Preis | Bild |
---|---|---|---|
UR3 | Hauptakteur des Projektes ist der UR3 Roboterarm der Firma Universal Robots (UR). Der UR3 ist der kleinste der von UR angebotenen kollaborierenden Roboter. Dabei hat der UR3 ein Gewicht von 11 kg mit einer maximalen Traglast von 3 kg. Die Reichweite des Roboters beträgt bei voll ausgestrecktem Arm 500 mm. Alle Gelenke des UR3 lassen sich um +/- 360° drehen. Der Roboter benötigt eine Betriebspanung von 24 V im Schaltschrank und 12 V an der Werkzeugschnittstelle. Die Kommunikation des Roboters zu PC findet über einen Ethernet-Anschluss statt. Weitere Informationen sind dem Datenblatt[6] zu entnehmen. | Kostenlos, da bereits vorhanden | |
DGT Smart Board und Schachfiguren | Das freundlicherweise vom Hersteller Digital Game Technology (DGT) bereitgestellte "DGT Smart Board" wird üblicherweise zur Übertragung von Turnierspielen ins Internet verwendet. Das bedeutet, dass dieses Smart Board die Möglichkeit zum Auslesen der einzelnen Figuren auf den bestimmten Feldern bietet. Die Datenübertragung wird durch ein USB-Kabel sichergestellt. Gleichzeitig kann eine von DGT hergestellte Schachuhr an das Schachbrett angeschlossen werden. Weitere Informationen sind unter der Quelle [7] zu finden. | 0 €, Gesponsort vom Hersteller Digital Game Technology (DGT). Bei Chessware: 260€ | |
DGT 3000 Schachuhr | Die Schachuhr DGT 3000 kann über ein AUX Kabel mit dem DGT Smart Board verbunden werden. Diese Verbindung ermöglicht eine Zeit- und Spielererkennung beim auslesen des Boards. Weitere Informationen zur Schachuhr sind unter der Quelle [8] zu finden. | 65 € | |
Greifbacken | Um alle Spielfiguren mit einer ausreichenden Genauigkeit greifen und positionieren zu können wird ein neues Greifer-Design benötigt. Der Greifer verfügt über zwei winkelige Auflageflächen, um eine Zentrierung der Figuren zwischen den Greifbacken zu ermöglichen. Dadurch können Figuren selbst dann aufgenommen werden, wenn diese nicht perfekt mittig auf dem Spielfeld positioniert werden. Zusätzlich wird die Länge des Greifers erhöht, um so auch die größten Figuren ohne Kollisionsgefahr greifen zu können. Das 3D-Model ist unter folgendem Link zu finden: https://wiki.hshl.de/wiki/index.php/Datei:Greifbacken.step | kostenlos, da auf privatem 3D-Drucker erstellt |
Software
Als Programmierumgebung wird das Robot Operating System (ROS) verwendet. ROS ist eine Softwareanwendung, die bei der Programmierung und Inbetriebnahme von mehrachsigen Greifrobotersystemen zur Anwendung kommt. Dabei besteht ROS aus mehreren Softwarebibliotheken. ROS bietet verschiedenste Werkzeuge. Von Treibern für diverse Roboter bis hin zur graphischen Simulation von Bewegungsabläufen durch RViz. Durch die höhere Verfügbarkeit von Bibliotheken unter dem Linux Betriebssystem (Ubuntu 16.04 bis 20.04) ist eine Nutzung von ROS unter Linux zu empfehlen.
- ROS
- Versionen: Durch die stetige Weiterentwicklung von ROS, finden sich diverse Versionen im Internet. Die aktuellen und noch durch einen Support unterstütze Versionen sind unter: www.docs.ros.org[9] zu finden. Bei der Auswahl der ROS-Version ist unbedingt auf die Kompatibilität von ROS-Version und benötigter Ubuntu Version zu achten. Je nach Version werden verschiedene Bibliotheken angeboten. Daher sollte nach Möglichkeit im Vorhinein recherchiert werden, welche ROS-Version für die geplante Anwendung benötigt wird.
- Installation: Nach Auswahl der gewünschten ROS-Version wird die Anwendung unter Ubuntu durch Terminalbefehle installiert. Zum Öffnen des Terminals ALT + STRG + T drücken. Hier die nötigen Befehle zur Installation nach und nach in das Terminalfenster kopieren und ausführen. Ebenfalls sollten alle Kommentare zwischen den Befehlen gelesen und befolgt werden. Je nachdem welche Version installiert werden soll ergeben sich ebenfalls andere Installationswege.
- Funktionsweise: Unter www.docs.ros.org/en/foxy/Tutorials.html[10] finden sich diverse Tutorials zur Funktionsweise von ROS2 Foxy. Die allgemeine Funktionsstruktur bleibt sowohl bei älteren als auch neueren ROS-Versionen grundsätzlich dieselbe. Nachfolgend werden die wichtigsten Grundlagen von ROS kurz erklärt
- Nodes: [11] Die sogenannten Nodes sind in ROS für die Abgrenzung von verschiedenen Modulen verantwortlich. Dabei haben in ROS die verschiedenen Module eine eigene Aufgabe (z.B. eine Node liest die Umdrehungsfrequenz eines Motors und eine Node ermittelt die Geschwindigkeit). Um einen Datenaustausch zu ermöglichen ist jede Node dazu in der Lage Informationen zu versenden und zu empfangen. Dazu werden allerdings weitere Funktionen innerhalb der Node benötigt: Die sogenannten Topics, Services und Actions.
- Topics: [12] Durch den modularen Aufbau von komplexen Systemen unter ROS muss eine Kommunikation unter den Nodes hergestellt werden. Dazu werden die ROS-Topics verwendet. Dabei sendet eine Node, durch den sogenannten Publisher an den Subscriber einer anderen Node, eine Nachricht. Das Topic erfüllt hier den Zweck des Weiterleitens der Informationen an die dafür vorgesehene Node. Ein Topic kann dabei nicht auswerten, ob alle an das Topic angeschlossene Nodes die Information erhalten müssen, oder nur einzelne Nodes. Das bedeutet das jede Information mit allen Subscribern, die an das Topic angeschlossen ist, geteilt wird.
- Services: [13] Die sogenannten sevices sind eine weitere Art der Kommunikation von Nodes in ROS. Anders als beim Datenaustausch durch ein Topic, wird hier eine Information erst gesendet, wenn der Service Server eine Anfrage erhalten hat. Dazu wird eine Anfrage innerhalb des Service Clients einer Node an den Service gesendet. Dieser Service unterscheidet dann zunächst, ob es sich bei der Information um eine Anfrage oder eine Antwort handelt. Sofern der Service die Information als Anfrage einstuft, wird diese an den Service Server weitergeleitet. Dabei ist wichtig zu erwähnen, dass es beim Datenaustausch über den ROS-Service immer nur einen Service Server, aber mehrere Service Clients geben kann. Erhält der Service eine Anfrage sendet dieser eine Antwort zurück an den Service. Dieser leitet die Antwort dann an den Service Client zurück.
- Actions: [14] Eine weitere Art der Kommunikation zwischen zwei Nodes ist die ROS-Action. Actions bestehen aus drei Teilen, dem Ziel, einem Feedback und einem Ergebnis. Gleichzeitig vereinen die Actions die Eigenschaften der Services und Topics miteinander. Hier sendet ein Action Client ein Ziel an den Action Server. Über den Feedback Publisher wird nach Annahme des Ziels ein durchgehender Strom aus Informationen an den Feedback Subscriber übermittelt. Dadurch kann der Action Client auswerten, wie viel Abweichung noch zum Zielzustand besteht. Sobald der Zielzustand erreicht ist, wird durch den Action Server eine Antwort an den Client gesendet um beispielsweise eine Bewegung eines Roboters zu beenden.
- RViz
- Getting started with MoveIt and RViz: RViz ist ein Simulationstool das zur Bewegungsplanung und -steuerung verwendet werden kann. Dabei werden die Bewegungsdaten über die Bibliothek "MoveIt" an den Roboter übermittelt. Ein Tutorial für RViz und MoveIt findet sich unter: https://ros-planning.github.io/moveit_tutorials/ [15]
- Laden des UR3-Modells in RViz Schritt für Schritt: die Nachfolgenden Befehle müssen jeweils in einem einzelnen Terminal in der aufgeführten Reihenfolge eingegeben werden um RViz mitsamt dem UR3-Modell zu starten.
//Lädt den korrekten Workspace und lädt die UR-Description (URDF & SRDF)// cd catkin_ws source devel/setup.bash roslaunch ur_description ur3_upload.launch //Lädt den korrekten Workspace und lädt die "planning scene" (Umgebungsinformation)// cd catkin_ws source devel/setup.bash roslaunch ur3_moveit_config ur3_moveit_planning_execution.launch //Lädt den korrekten Workspace und öffnet RViz zur Steuerung// cd catkin_ws source devel/setup.bash roslaunch ur3_moveit_config moveit_rviz.launch config:=true
Programmierung/Entwicklung
In Schritt Entwicklung[16] wird die notwendige Software programmiert. Hierbei sind drei unterschiedliche C++-Skripte entwickelt worden.
- Programm zum einmaligen Auslesen des Boards und Ausgabe im Terminal.
- Programm zum kontinuierlichen auslesen und ausgeben der Schachuhr.
- Kombination aus Programm eins und zwei, also kontinuierliches auslesen der Uhr und bei korrekter Spielererkennung Ausgabe des Spielfeldes im Terminal.
Zunächst einmal muss aber die Kommunikation zwischen dem Board und dem Computer hergestellt werden. Dies ist unter Ubuntu nicht einfach als Anfänger. Es müssen bestimmte Flaggen gesetzt oder deaktiviert werden. Zudem muss ermittelt werden, welcher Port der USB-Stecker belegt. In unserem Fall war dies immer /dev/ttyACM0
, jedoch ist dies nicht immer zwingend der Fall. Um den Port herauszufinden hilft das Programm HTerm[17]. Eine genauere Beschreibung zum Auslesen von seriellen Schnittstellen befindet sich hier[18]. Diese schrittweise Befolgung der Anleitung hat uns zu dem folgendem Unterprogramm geführt, welches als Setup vorher ausgeführt werden muss.
Setup Funktion
Beschreibung: Dient der Vorbereitung des Informationsaustausch via USB.
void setup() { serial_port = open("/dev/ttyACM0", O_RDWR); //"/dev/ttyACM0" ist der angeschlossene Port muss ggf. angepasst werden! // Check for errors if (serial_port < 0) { printf("Error %i from open: %s\n", errno, strerror(errno)); } // Create new termios struct, we call it 'tty' for convention // No need for "= {0}" at the end as we'll immediately write the existing // config to this struct struct termios tty; // Read in existing settings, and handle any error // NOTE: This is important! POSIX states that the struct passed to tcsetattr() // must have been initialized with a call to tcgetattr() overwise behaviour // is undefined if (tcgetattr(serial_port, &tty) != 0) { printf("Error %i from tcgetattr: %s\n", errno, strerror(errno)); } tty.c_cflag &= ~PARENB; // Clear parity bit, disabling parity (most common) tty.c_cflag |= PARENB; // Set parity bit, enabling parity tty.c_cflag &= ~CSTOPB; // Clear stop field, only one stop bit used in communication (most common) tty.c_cflag |= CSTOPB; // Set stop field, two stop bits used in communication tty.c_cflag &= ~CSIZE; // Clear all the size bits, then use one of the statements below tty.c_cflag |= CS8; // 8 bits per byte (most common) tty.c_cflag &= ~CRTSCTS; // Disable RTS/CTS hardware flow control (most common) tty.c_cflag |= CREAD | CLOCAL; // Turn on READ & ignore ctrl lines (CLOCAL = 1) tty.c_lflag &= ~ICANON; tty.c_lflag &= ~ECHO; // Disable echo tty.c_lflag &= ~ECHOE; // Disable erasure tty.c_lflag &= ~ECHONL; // Disable new-line echo tty.c_lflag &= ~ISIG; // Disable interpretation of INTR, QUIT and SUSP tty.c_iflag &= ~(IXON | IXOFF | IXANY); // Turn off s/w flow ctrl tty.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL); // Disable any special handling of received bytes tty.c_oflag &= ~OPOST; // Prevent special interpretation of output bytes (e.g. newline chars) tty.c_oflag &= ~ONLCR; // Prevent conversion of newline to carriage return/line feed // tty.c_oflag &= ~OXTABS; // Prevent conversion of tabs to spaces (NOT PRESENT IN LINUX) // tty.c_oflag &= ~ONOEOT; // Prevent removal of C-d chars (0x004) in output (NOT PRESENT IN LINUX) tty.c_cc[VTIME] = 1; // Wait for up to 1s (10 deciseconds), returning as soon as any data is received. tty.c_cc[VMIN] = 0; // Set in/out baud rate to be 9600 cfsetispeed(&tty, B9600); cfsetospeed(&tty, B9600); // Save tty settings, also checking for error if (tcsetattr(serial_port, TCSANOW, &tty) != 0) { printf("Error %i from tcsetattr: %s\n", errno, strerror(errno)); } }
Zusätzlich muss in einem Terminal der Befehl $ sudo adduser $USER dialout
ausgeführt werden. Hierdurch wird der Fehler errno13 unterbunden, welcher die unzureichende Berechtigung mitteilt (Ausgabe im Terminal: permission denied). Nach dem Ausführen des Befehls muss der PC einmal neugestartet werden, damit die Gruppenänderung in Kraft tritt. Nun hat man die Berechtigung, um auf den USB-Port zugreiffen zu können.
Als nächstes folgen die drei verschiedenen Programme.
Board auslesen
Beschreibung: Liest das Board einmal aus und gibt die Daten in das Terminal aus. Programm beendet sich nach der einmaligen Ausgabe. Die Ausgabe ist ein 8x8 Feld mit zweistelligen Hex-Werten, welches das Board darstellt.
#include <stdio.h> #include <string.h> #include <iostream> #include <fcntl.h> // Contains file controls like O_RDWR #include <errno.h> // Error integer and strerror() function #include <termios.h> // Contains POSIX terminal control definitions #include <unistd.h> using namespace std; int serial_port; void setup(); int main() { setup(); //öffnet den Port char read_buf [100]; //erzeugt ein Array in welches die Daten geschrieben werden unsigned char msg[1] = {'\x42'}; //die zu übermittelnde Nachricht "DGT_REQ_BOARD" in hex siehe DGT-Manuel int i=0; //Zähler für for-Schleife write(serial_port, msg, sizeof(msg)); //Schreibt die Nachricht auf den Port int n = read(serial_port, &read_buf, sizeof(read_buf)); //liest erhaltene information aus // n is the number of bytes read. n may be 0 if no bytes were received, and can also be negative to signal an error. for(i = 3; i<n; i++) //Ausgabe der Information ins Terminal { int HexOut = read_buf[i]; //char zu int konvertierung if(HexOut<16) //für konstante zweistellige Hexzahlen; vorherige Ausgabe Zahl<16 mit 0 { cout << "0"; } cout << hex << HexOut << " "; //Ausgabe der int-zahl als Hex if((i-2)%8 == 0) //dient der formatierung; erzeugt 8x8 Feld im Terminal { cout << endl; } } close(serial_port); } void setup() { serial_port = open("/dev/ttyACM0", O_RDWR); //"/dev/ttyACM0" ist der angeschlossene Port muss ggf. angepasst werden! // Check for errors if (serial_port < 0) { printf("Error %i from open: %s\n", errno, strerror(errno)); } // Create new termios struct, we call it 'tty' for convention // No need for "= {0}" at the end as we'll immediately write the existing // config to this struct struct termios tty; // Read in existing settings, and handle any error // NOTE: This is important! POSIX states that the struct passed to tcsetattr() // must have been initialized with a call to tcgetattr() overwise behaviour // is undefined if (tcgetattr(serial_port, &tty) != 0) { printf("Error %i from tcgetattr: %s\n", errno, strerror(errno)); } tty.c_cflag &= ~PARENB; // Clear parity bit, disabling parity (most common) tty.c_cflag |= PARENB; // Set parity bit, enabling parity tty.c_cflag &= ~CSTOPB; // Clear stop field, only one stop bit used in communication (most common) tty.c_cflag |= CSTOPB; // Set stop field, two stop bits used in communication tty.c_cflag &= ~CSIZE; // Clear all the size bits, then use one of the statements below tty.c_cflag |= CS8; // 8 bits per byte (most common) tty.c_cflag &= ~CRTSCTS; // Disable RTS/CTS hardware flow control (most common) tty.c_cflag |= CREAD | CLOCAL; // Turn on READ & ignore ctrl lines (CLOCAL = 1) tty.c_lflag &= ~ICANON; tty.c_lflag &= ~ECHO; // Disable echo tty.c_lflag &= ~ECHOE; // Disable erasure tty.c_lflag &= ~ECHONL; // Disable new-line echo tty.c_lflag &= ~ISIG; // Disable interpretation of INTR, QUIT and SUSP tty.c_iflag &= ~(IXON | IXOFF | IXANY); // Turn off s/w flow ctrl tty.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL); // Disable any special handling of received bytes tty.c_oflag &= ~OPOST; // Prevent special interpretation of output bytes (e.g. newline chars) tty.c_oflag &= ~ONLCR; // Prevent conversion of newline to carriage return/line feed // tty.c_oflag &= ~OXTABS; // Prevent conversion of tabs to spaces (NOT PRESENT IN LINUX) // tty.c_oflag &= ~ONOEOT; // Prevent removal of C-d chars (0x004) in output (NOT PRESENT IN LINUX) tty.c_cc[VTIME] = 1; // Wait for up to 1s (10 deciseconds), returning as soon as any data is received. tty.c_cc[VMIN] = 0; // Set in/out baud rate to be 9600 cfsetispeed(&tty, B9600); cfsetospeed(&tty, B9600); // Save tty settings, also checking for error if (tcsetattr(serial_port, TCSANOW, &tty) != 0) { printf("Error %i from tcsetattr: %s\n", errno, strerror(errno)); } }
Uhr auslesen
Beschreibung: Liest die Uhr kontinuierlich aus und gibt die Daten in das Terminal aus. Programm beendet sich nicht selbstständig. Hierfür STRG + C
ins Terminal eingeben. Es werden hierbei insgesamt 10 Bytes eingelesen, aber nur Bytes 4-10 Ausgegeben. Die ersten drei Bytes sind Header Bytes und können ignoriert werden.
Die ausgegebenen 7 Bytessind folgende:
- Bytes 4-6: Zeit Spieler Weiß (Stunde|Minute|Sekunde)
- Bytes 7-9: Zeit Spieler Schwarz (Stunde|Minute|Sekunde)
- Byte 10: Spieler aktivität (z.B. Welcher Spieler dran ist)
Zusätzlich werden in diesem Modus auch Feldaktualisierungen gemeldet. Hierbei werden bei Bewegung einer Figur Zusätzlich ins Terminal je zwei Bytes geschrieben. Byte eins gibt das Feld an, welches sich verändert hat. Und Byte zwei gibt an welche Figur sich auf dem Feld nun befindet (für die Aufschlüsselung Feld und Figur siehe DGT-Manuel). Da beim Bewegen einer Figur für gewöhnlich zwei Felder aktualisiert werden (Start Feld und Ziel Feld) werden dementsprechend auch zwei mal zwei Bytes ausgegeben. ACHTUNG! bei Figuren die sich mehr als ein Feld weit bewegen können, kann es passieren, dass auch mehr als zwei aktualisierte Felder angezeigt werden (z.B. wenn ein Turm vier Felder über das Board ohne anheben nach vorne geschoben wird).
#include <stdio.h> #include <string.h> #include <iostream> #include <fcntl.h> // Contains file controls like O_RDWR #include <errno.h> // Error integer and strerror() function #include <termios.h> // Contains POSIX terminal control definitions #include <unistd.h> using namespace std; int serial_port; void setup(); int main() { setup(); //öffnet den Port char read_buf[100]; //erzeugt ein Array in welches die Daten geschrieben werden unsigned char msg[] = { '\x08' }; //die zu übermittelnde Nachricht "DGT_CMD_CLOCK_BUTTON" in hex siehe DGT-Manuel //Da die Uhr nicht immer auf Hexzahlen anspricht ggf.mit ASCII übermitteln int i = 0; //Zähler für for-Schleife int HexOut = 0; //Variable zum konvertieren char zu int int n = 0; //Variable zum ermitteln, wie viel Bytes empfangen wurden write(serial_port, msg, sizeof(msg)); //Schreiben der Nachricht an USB-Port //Nach erhalt der Nachricht sendet die Uhr kontinuierlich, //sobald sich etwas in der Anzeige verändert while (true) { n = read(serial_port, &read_buf, sizeof(read_buf)); //liest erhaltene Information aus if (n>0) //gibt nur Daten ins Terminal aus, wenn welche empfangen wurden { for (i = 3; i<n; i++) { HexOut = read_buf[i]; //char zu int konvertierung if (HexOut<16) //für konstante zweistellige Hexzahlen; vorherige Ausgabe Zahl<16 mit 0 { cout << "0"; } cout << hex << HexOut << " "; //Ausgabe der int-zahl als Hex } cout << endl; //Zeilenumbruch; WICHTIG!!! ohne Zeilenumbruch gibt das Terminal //die Informationen erst nach einem overflow aus! } usleep(100); //Systempause } close(serial_port); } void setup() { serial_port = open("/dev/ttyACM0", O_RDWR); // Check for errors if (serial_port < 0) { printf("Error %i from open: %s\n", errno, strerror(errno)); } // Create new termios struct, we call it 'tty' for convention // No need for "= {0}" at the end as we'll immediately write the existing // config to this struct struct termios tty; // Read in existing settings, and handle any error // NOTE: This is important! POSIX states that the struct passed to tcsetattr() // must have been initialized with a call to tcgetattr() overwise behaviour // is undefined if (tcgetattr(serial_port, &tty) != 0) { printf("Error %i from tcgetattr: %s\n", errno, strerror(errno)); } tty.c_cflag &= ~PARENB; // Clear parity bit, disabling parity (most common) tty.c_cflag |= PARENB; // Set parity bit, enabling parity tty.c_cflag &= ~CSTOPB; // Clear stop field, only one stop bit used in communication (most common) tty.c_cflag |= CSTOPB; // Set stop field, two stop bits used in communication tty.c_cflag &= ~CSIZE; // Clear all the size bits, then use one of the statements below tty.c_cflag |= CS8; // 8 bits per byte (most common) tty.c_cflag &= ~CRTSCTS; // Disable RTS/CTS hardware flow control (most common) tty.c_cflag |= CREAD | CLOCAL; // Turn on READ & ignore ctrl lines (CLOCAL = 1) tty.c_lflag &= ~ICANON; tty.c_lflag &= ~ECHO; // Disable echo tty.c_lflag &= ~ECHOE; // Disable erasure tty.c_lflag &= ~ECHONL; // Disable new-line echo tty.c_lflag &= ~ISIG; // Disable interpretation of INTR, QUIT and SUSP tty.c_iflag &= ~(IXON | IXOFF | IXANY); // Turn off s/w flow ctrl tty.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL); // Disable any special handling of received bytes tty.c_oflag &= ~OPOST; // Prevent special interpretation of output bytes (e.g. newline chars) tty.c_oflag &= ~ONLCR; // Prevent conversion of newline to carriage return/line feed // tty.c_oflag &= ~OXTABS; // Prevent conversion of tabs to spaces (NOT PRESENT IN LINUX) // tty.c_oflag &= ~ONOEOT; // Prevent removal of C-d chars (0x004) in output (NOT PRESENT IN LINUX) tty.c_cc[VTIME] = 1; // Wait for up to 1s (10 deciseconds), returning as soon as any data is received. tty.c_cc[VMIN] = 0; // Set in/out baud rate to be 9600 cfsetispeed(&tty, B9600); cfsetospeed(&tty, B9600); // Save tty settings, also checking for error if (tcsetattr(serial_port, TCSANOW, &tty) != 0) { printf("Error %i from tcsetattr: %s\n", errno, strerror(errno)); } }
Kombination aus Uhr auslesen und Board auslesen
Beschreibung: Liest kontinuierlich die Schachuhr aus. Sobald das Programm in Byte 10 (wie im vorherigen Programm beschrieben) 0x13 (Spieler Schwarz) empfängt, wird das Board einmal ausgelesen und ins Terminal ausgegeben. Desweiteren läuft ein Zähler mit, welcher die Periode mit ausgibt (hilft auch beim Debuggen). Daraufhin wird eine Flagge gesetzt, welche dafür sorgt, dass das Board nicht erneut ausgeben wird, solange Spieler Schwarz noch am Zug ist. Sobald das Programm in Byte 10 0x09 (Spieler Weiß) empfängt, wird die gesetzte Flagge zurückgesetzt.
#include <stdio.h> #include <string.h> #include <iostream> #include <fcntl.h> // Contains file controls like O_RDWR #include <errno.h> // Error integer and strerror() function #include <termios.h> // Contains POSIX terminal control definitions #include <unistd.h> using namespace std; int serial_port; void setup(); /************************************************************************************/ int main() { char read_buf[100]; //Array anlegen in denen die erhaltenen Daten eingelesen werden unsigned char msg1[] = { '\x08' }; //Der Befehl "DGT_CMD_CLOCK_BUTTON" in Hex unsigned char msg2[] = { '\x42' }; //Der Befehl "DGT_REQ_BOARD" in Hex unsigned char msg3[] = { '\x03' }; //Der Befehl "DGT_CMD_CLOCK_END" in Hex int ControlFlag = 1; //Kontroll Flagge als Indikator, dass das Board bereits ausgegeben wurde; am Anfang 1, um anfängliche falsch Übertragungen zu verhindern! int i = 0; //Zähler für "for" Schleifen int HexOut = 0; //Variable dient zum konvertieren "char" zu "int" int n = 0; //Variable dient zum Kontrollieren, dass informationen vom Board erhalten wurden int p = 0; //Zähler der Perioden für Spieler 2 setup(); //ermöglicht den Zugriff auf die Schnittstelle zum Board; vollständige Funktion unten! while (true) //dient als Dauerschleife zum empfangen der Daten; Abbruchbedingung bei Spielende muss noch hinzugefügt werden! { /*********************************************************/ //Do-Schleife dient zum stetigen abfragen des Spielers do { write(serial_port, msg1, sizeof(msg1)); usleep(100); n = read(serial_port, &read_buf, sizeof(read_buf)); HexOut = read_buf[9]; if (HexOut == '\x09') //Wenn Spieler1 am Zug ist wird die Kontrollflagge zurückgesetzt { ControlFlag = 0; //Reset der Kontrollflagge } } while (HexOut != '\x13'); //Abbruchbedingung ist der Spielerwechsel (wenn Spieler2 am Zug ist wird die Schleife verlassen) /*********************************************************/ if (ControlFlag == 0) //Wenn Spieler2 am Zug ist UND noch NICHT das Board ausgelesen wurde, wird das Board ausgelesen { ControlFlag = 1; //Setze Kontrollflagge auf 1 um die erneute Ausgabe des Boards im selben Zug zu unterbinden! write(serial_port, msg2, sizeof(msg2)); //Übertragung des Befehls 0x42 an Board usleep(100); //Systempause um eventuelle verzögerungen des Boards und entsprechenden Auslesefehler zu vermeiden n = read(serial_port, &read_buf, sizeof(read_buf)); //Auslesen der Informationen des Boards; n entspricht der Länge des übermittelten "char" Arrays for (i = 3; i<n; i++) //Daten werden mittels for-Schleife auf Terminal ausgegeben { HexOut = read_buf[i]; //Konvertiere char zu int if (HexOut<16) //Um durchgängig zwei Ziffern auszugeben wird bei hex-zahlen kleiner 16 noch eine 0 zuvor ausgegeben { cout << "0"; } cout << hex << HexOut << " "; //Ausgabe in Hex if ((i - 2) % 8 == 0) //erzeugt in dem Terminal ein 8x8 Feld { cout << endl; } } p = p + 1; //Zählt die Periode cout << endl << "Periode: " << dec << p; //gibt die Periode in Terminal aus cout << endl << "-------------------------------------------------" << endl; //dient der übersichtlichkeit } usleep(100); } close(serial_port); } /****************************************************************************************/ // Nichts unter dieser Linie verändern!!! /**************************************************************************************/ void setup() { serial_port = open("/dev/ttyACM0", O_RDWR); // Check for errors if (serial_port < 0) { printf("Error %i from open: %s\n", errno, strerror(errno)); } // Create new termios struct, we call it 'tty' for convention // No need for "= {0}" at the end as we'll immediately write the existing // config to this struct struct termios tty; // Read in existing settings, and handle any error // NOTE: This is important! POSIX states that the struct passed to tcsetattr() // must have been initialized with a call to tcgetattr() overwise behaviour // is undefined if (tcgetattr(serial_port, &tty) != 0) { printf("Error %i from tcgetattr: %s\n", errno, strerror(errno)); } tty.c_cflag &= ~PARENB; // Clear parity bit, disabling parity (most common) tty.c_cflag |= PARENB; // Set parity bit, enabling parity tty.c_cflag &= ~CSTOPB; // Clear stop field, only one stop bit used in communication (most common) tty.c_cflag |= CSTOPB; // Set stop field, two stop bits used in communication tty.c_cflag &= ~CSIZE; // Clear all the size bits, then use one of the statements below tty.c_cflag |= CS8; // 8 bits per byte (most common) tty.c_cflag &= ~CRTSCTS; // Disable RTS/CTS hardware flow control (most common) tty.c_cflag |= CREAD | CLOCAL; // Turn on READ & ignore ctrl lines (CLOCAL = 1) tty.c_lflag &= ~ICANON; tty.c_lflag &= ~ECHO; // Disable echo tty.c_lflag &= ~ECHOE; // Disable erasure tty.c_lflag &= ~ECHONL; // Disable new-line echo tty.c_lflag &= ~ISIG; // Disable interpretation of INTR, QUIT and SUSP tty.c_iflag &= ~(IXON | IXOFF | IXANY); // Turn off s/w flow ctrl tty.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL); // Disable any special handling of received bytes tty.c_oflag &= ~OPOST; // Prevent special interpretation of output bytes (e.g. newline chars) tty.c_oflag &= ~ONLCR; // Prevent conversion of newline to carriage return/line feed // tty.c_oflag &= ~OXTABS; // Prevent conversion of tabs to spaces (NOT PRESENT IN LINUX) // tty.c_oflag &= ~ONOEOT; // Prevent removal of C-d chars (0x004) in output (NOT PRESENT IN LINUX) tty.c_cc[VTIME] = 1; // Wait for up to 1s (10 deciseconds), returning as soon as any data is received. tty.c_cc[VMIN] = 0; // Set in/out baud rate to be 9600 cfsetispeed(&tty, B9600); cfsetospeed(&tty, B9600); // Save tty settings, also checking for error if (tcsetattr(serial_port, TCSANOW, &tty) != 0) { printf("Error %i from tcsetattr: %s\n", errno, strerror(errno)); } }
Dies sind Lauffähige C++-Programme, siehe nachfolgend in dem Kapitel Komponententest entsprechende Beispiele. Jedoch sind dies noch keine Nodes welche in ROS eingebunden werden können. Diese Skripte müssen dementsprechend noch zu Nodes konvertiert werden! Außerdem sind diese C++-Skripte Vorlagen zur ersten von vier geplanten Nodes.
Komponententest
Nach dem Erhalt der Komponenten müssen diese zunächst auf deren Funktionsfähigkeit überprüft werden. Die Funktionsfähigkeit wurde durch selbstgeschriebene C++-Skripte verifiziert. Die unten stehende Tabelle zeigt die Ergbenisse.
Komponenten | Beschreibung | Bild |
---|---|---|
DGT Smart Board | Zu sehen im rechten Bild ist die Ausgabe des Programms SampleProgramm.cpp im Terminal. Hier wird eine 8x8-Matrix, welche das Schachbrett abbildet, dargestellt. Oben links beginnend mit dem Feld A8 und unten rechts abschließend mit dem Feld H1. Hierbei wurde die Startaufstellung ausgelesen. 0x01-0x06 sind Figuren des weißen Spielers und 0x07-0x0c die des schwarzen Spielers. Zusätzlich ist ein Zähler zu sehen, der ausgibt in welcher Periode die Ausgabe statt findet. | |
DGT 3000 Schachuhr | Im rechten Bild ist die Ausgabe des Programms UhrAuslesen.cpp im Terminal zu sehen. Ausgegeben werden sieben Bytes, die als hexadezimale Zahlen dargestellt werden. Die ersten drei Zahlen zeigen die verbleibende Zeit des schwarzen Spielers an die darauffolgenden drei Bytes die verbleibende Spielzeit des weißen Spielers. Die letzte Zahl gibt an welcher Spieler aktuell am Zug ist. OBACHT! Die hexadezimale Zahle muss NICHT umgerechnet werden! Die Schachuhr zählt intern in Hex aber die dargestellten Zahlen sind wie Dezimalzahlen zu lesen. Beispiel Zeile 1 im Bild: 0x00 0x59 0x19 -> 00h 59min 19sec |
Integrationstest
noch nicht erfolgt
Systemtest
noch nicht erfolgt
Abnahmetest
noch nicht erfolgt
Literatur
weitere nützliche sonstige Literatur[19] ist unter diesem Punkt zu finden.
Zusammenfassung
Das Projekt "Schachspiel mittels kollaborierendem UR-Roboter unter ROS" ist im 7. Semester des Praktikums Produktionstechnik VII erfolgt. Hierbei konnten die im Studium erlernten Fähigkeiten angewendet werden. Mit Hilfe des V-Modells ist das neue Projekt entworfen und entwickelt worden. Das strukturierte Vorgehen des V-Modells hilft bei der Planung und Entwicklung des Projektes, jedoch können auch durch die Anwendung des Modells nicht sämtliche Probleme vorausgeahnt werden. Im unserem Projekt beispielsweise die Einbindung des Robotermodells in RViz. Allerdings kann durch das kleinschrittige und strukturierte Vorgehen der Umfang des Projektes im Vorhinein gut abgegrenzt werden. Abschließend kann gesagt werden, dass das Praktikum das eigenständige Arbeiten schult und einen gut auf den realen Berufsalltag vorbereitet.
Zusammenfassend lässt sich sagen, dass das Projekt leider nicht zu einem fertigen Abschluss gebracht werden konnte. Allerding konnten einige Schritte des V-Modells abgearbeitet werden um möglichen zukünftigen Studierenden den Abschluss des Projektes zu erleichtern. So konnten die ersten vier Schritte des V-Modells (Anforderungsdefinition, funktionaler Systementwurf, technischer Systementwurf und Komponentenspezifikation) erfolgreich bearbeitet werden. Das Projekt befindet sich nun zwischen Schritt fünf und sechs. Teile der Programmierung konnten bereits abgeschlossen werden und können als funktionsfähige C++-Skripte zur Verfügung gestellt werden. Mit Hilfe der fertigen Skripte konnten ebenfalls die beschafften Komponenten bereits auf deren Funktionstüchtigkeit überprüft werden. Wie im Ausblick beschrieben hält das Projekt noch viele spannende Aufgaben für zukünftige Jahrgänge bereit.
Ausblick
Da nicht alle Tätigkeiten die zur Umsetzung des Projektes benötigt waren umgesetzt werden konnten, werden nachfolgend einige noch offene Aufgaben aufgeführt und kurz erläutert.
- vorhandenes C++-Sktipt in ROS-Node umwandeln: Da ROS mit einzelnen Nodes arbeitet und diese mit der C++ Syntax programmiert werden können, muss das vorliegende und funktionstüchtige C++-Skript zum auslesen der Board- und Uhreninformationen in eine ROS-Node transferiert werden.
- Verbindung zwischen UR3 und RViz herstellen: Für die Bewegungsplanung über ROS/RViz muss die Verbindung zwischen UR3 und Computer über die Ethernet Schnittstelle hergestellt werden.
- Node 2 - 4 erstellen: Um das System zu vervollständigen muss die Schachengine "Stockfish" durch eine Node angesprochen und alle notwendigen Informationen der Schachengine zur Verfügung gestellt werden. Nachfolgend muss das mapping der Koordinaten durch eine Node und die Robotersteuerung durch eine Node programmiert werden.
- Weiterverfolgung des V-Modells: Nach Vervollständigung der Programmierung kann dann mit dem Intrgrations-, Systems- und Abnahmetest fortgefahren werden
Probleme und Lösungen
- ROS2 Kompatibilitätsprobleme:Unter Universal Robots ROS2 Treiber[20] finden sich diverse Treiber für die unterschiedlichen Universal Robots Greifarme zum Download. Leider war es uns nicht möglich die Treiber unter ROS2 Foxy zu installieren. Während des Schrittes "Colcon_build" kam es immer wieder an unterschiedlichsten Stellen zu einem "Fatal_Error". Der Grund könnte das noch frühe Entwicklungsstadium der ROS2 Foxy Treiber sein. Daher haben wir uns dazu entschieden die ROS Version zu wechseln. Verwendet wird nun ROS Noetic. Diese Treiber werden unter Universal Robots ROS Treiber[21] zur Verfügung gestellt. Die Installation dieser Treiber funktionierte reibungslos.
- UR3-Greiferdesign: Der bereits für den UR3 vorhandene Greifer bietet nicht die Möglichkeit die Schachfiguren aufnehmen zu können. Durch die Höhe der Figuren würde es zu Kollisionen mit zwischen Roboter und Figur kommen, sofern die Figuren am Sockel gegriffen werden sollen. Ein Greifen am Sockel der Figuren ist zu empfehlen, da die Figuren alle über einen Runden Sockel mit lediglich unterschiedlichen Durchmessern verfügen. Durch eine Dreipunktaufnahme können die Figuren so gegriffen und zentriert werden, um ein präzises Greifen und Platzieren jederzeit ermöglichen zu können. Die Entwickelten Greifer sind unter dem Punkt Entwicklung[22] zu finden.
- Einbindung von Robotermodell in RViz: Das Einbinden des UR3-Modells in RViz stellte eine große Herausforderung dar. Die Lösung lag in der korrekten Ausführung aller Befehle in der richtigen Reihenfolge wie unter "Laden des UR3-Modells in RViz Schritt für Schritt:" beschrieben. Durch das schrittweise Befolgen und Eingeben der Befehle in jeweils eigenständige Terminals kann das UR3-Modell erfolgreich in RViz geladen werden.
- passende Hardware: Eine große Frage, die sich bereits früh im Projektverlauf gestellt hatte, war die Frage nach der Art und Weise der Erkennung des Spielfelds und der Figuren. Nach eine Recherche haben wir uns an den Smart Board Hersteller DGT gewandt und nach Informationen zu den Schachbrettern gefragt. Kontakt hierfür war Herr Thijs van Zeijts des Kundensupports. Das Niederländische Unternehmen DGT war so freundlich und hat uns ein Smart Board mitsamt Spielfiguren kostenlos zur Verfügung gestellt. Außerdem wurde eine Beschreibung der Kommunikationsprotokolls mit Befehlen für das Smart Board zur Verfügung gestellt. Dieses ist unter dem Punkt "Literatur" zu finden.
Ungelöste Probleme
Problem | Beschreibung | Lösungsansatz | Ergebnis |
---|---|---|---|
Verbindung zwischen UR3 und Ubuntu-PC / ROS | Verbindungsaufbau zwischen Ubuntu-PC / ROS und dem UR3-Roboter ist nicht möglich | Anpassung der IP-Adressen von Roboter und Ubuntu-PC. Die beiden Geräte befanden sich zu Beginn nicht im selben Netzwerk, wodurch selbst ein Anpingen über das Terminal und den Befehl ping [IP-Adresse] nicht möglich war
|
Anpingen des UR3-Roboters nun erfolgreich über das Terminal möglich, jedoch weiterhin keine Verbindung in RViz möglich |
Durch den Befehl roslaunch ur_robot_driver <robot_type>_bringup.launch robot_ip:=IP-Adress soll der Treiber des UR3-Roboters gestartet werden. Gleichzeitig soll ROS durch diesen Befehl die Verbindung zum UR3 herstellen
|
Kein Verbindungsaufbau möglich. Befehl wird nach kurzer Laufzeit aufgrund von unbekannten Fehler abgebrochen | ||
Senden der Befehle an Schachuhr inkonsistent | Die Programme sind lauffähig und geben in der Regel die Informationen wie gewollt im Terminal aus. In seltenen Fällen erfolgt die Ausgabe dauerhaft, und nicht wie gewollt einmal pro Periode | Die Ursache des Problems ist unbekannt. Als Lösungsansatz kann daher nur der Neustart des Systems genutzt werden | Durch einen wiederholten Neustart des Systems kann der Fehler behoben werden |
Literaturverzeichnis
- ↑ TortoiseSVN Client
- ↑ Anforderungsdefinition
- ↑ funktionaler Systementwurf
- ↑ technischer Systementwurf
- ↑ Komponentenspezifikation
- ↑ UR3Roboter: UniversalRobots- UR3Roboter. 2022. Online im Internet: https://www.i-botics.de/wp-content/uploads/2016/08/ur3_de.pdf; Abruf: 08.01.2022
- ↑ DGT: DGT - DGT Smart Board. 2022. Online im Internet: https://digitalgametechnology.com/products/home-use-e-boards/smart-board-with-indices; Abruf: 08.01.2022
- ↑ DGT: DGT - DGT3000. 2022. Online im Internet: https://digitalgametechnology.com/products/chess-clocks/dgt3000-fide-approved; Abruf: 08.01.2022
- ↑ www.docs.ros.org
- ↑ www.docs.ros.org/en/foxy/Tutorials.html
- ↑ ROS: ROS 2 nodes. 2022. Online im Internet: https://docs.ros.org/en/foxy/Tutorials/Understanding-ROS2-Nodes.html#nodes-in-ros-2; Abruf: 08.01.2022
- ↑ ROS: ROS 2 topics. 2022. Online im Internet: https://docs.ros.org/en/foxy/Tutorials/Topics/Understanding-ROS2-Topics.html; Abruf: 08.01.2022
- ↑ ROS: ROS 2 services. 2022. Online im Internet: https://docs.ros.org/en/foxy/Tutorials/Services/Understanding-ROS2-Services.html; Abruf: 08.01.2022
- ↑ ROS: ROS 2 actions. 2022. Online im Internet: https://docs.ros.org/en/foxy/Tutorials/Understanding-ROS2-Actions.html; Abruf: 08.01.2022
- ↑ RViz and MoveIt: Getting started with MoveIT and RViz. 2022. Online im Internet: https://ros-planning.github.io/moveit_tutorials/; Abruf: 08.01.2022
- ↑ Entwicklung
- ↑ HTerm Download
- ↑ mbedded.ninja
- ↑ sonstige Literatur
- ↑ Universal Robots ROS2 Treiber https://github.com/UniversalRobots/Universal_Robots_ROS2_Driver], Abruf: 22.01.2022
- ↑ Universal Robots ROS Treiber https://github.com/UniversalRobots/Universal_Robots_ROS_Driver], Abruf: 22.01.2022
- ↑ Entwicklung