Douba/Studienarbeit/Protokoll

Stand vom 07.07.2013


Douba.de

Universität

Reisen

Projekte

Hobbies

Freunde

Links

Valid HTML 4.01 Strict CSS ist valide!

Protokoll zur Studienarbeit Katana

Hier meine aktuelle Protokolldatei. Sie enthält keine Formatierung und nur Rohdaten. Man kann hier Dinge wiederfinden, die ich mal erwähnt habe, Daten und Fakten einsehen und einen Überblick darüber bekommen, was ich so in letzter Zeit gemacht habe.

?20090513 Der Katana läßt sich derzeit nur durch das Program "motor.exe" ansteuern. xxx Ort und Bak motor.exe! Eine Verbindung zum Katana kann über eine RS232-Schnittstelle mit folgenden Einstellungen hergestellt werden: 57600 baud keine Parität 1 Stop-Bit Keine Flußkontrolle Über USB ist der Adapter zu verwenden. Installation unter Vista erfordert gesonderte Einstellungen _vor_ der Installation. Siehe Anleitung. Der Katana meldet sich bei Einschalten und Rücksetzen in menschenlesbarem ASCII. Es existiert eine Katana-CD. Diese enthält ein Dokument zur Schnittstelle zum Katana. Die darin enthaltenen Informationen sind bestenfalls rudimentär bis hin zu falsch. Auf die beispielhaft angeführten Befehle (vzeros...) reagiert der Katana nicht. Sendet man sie ohne zwischenzeitliches Rücksetzen ein zweites mal, wird dies mit der Meldung "err" vom Katana quittiert. Das Protokoll ist demnach fehlerhaft. Mit dem Oszilloskop wurde die Ausgabe des Programms motor.exe analysiert. Die Einstellungen des Oszilloskops waren: 2 V/div; 0,1 ms/div; trigger mode: norm; trigger source: ch1; mode: ch1, DC Der Anschluß erfolgte über Krokodilklemmen: Signal von pin 3, Masse von pin 5. Zwischen Krokodilklemme des Signals und den Oszilloskop-Eingang war ein Kabel mit offenem Ende (an Klemme) und verzinntem Ende (Signaleingang). Die Meßdaten befinden sich auf dem Laptop hp ohne Bezeichnung xxx Ort und Bak Meßdaten! Die Imitation erfolgt mit Hilfe meines Programms "Serial Communication.cpp" anhand der Daten: 1 24 1 88 250 1 Der Vergleich mit der Originalmessung erfolgte über (interpretierendem, möglicherweise fehlerbehaftetem) Ablesen der Messung des imitierten Signals. Ein synchroner Vergleich der Signale war technisch nicht realisierbar. Besser wäre die Verwendung eines Null-Modem-Kabels. Ein solches ist hier nicht verfügbar. Es ist ohne viel Aufwand, jedoch mit Materialverbrauch, möglich, eines zu löten. ----------------- 20090518 Auf die erwähnte Init-Sequenz antwortet der Katana mit: v?N?? n?P¶p?Q ?q?+???s?L¶??l?¦???8? ? ? ? f¦ Das Kauderwelsch ist eventuell zu interpretieren. Die Folge 1 24 1 88 250 1 scheint ein Serial-Zero-Paket ohne führende Nullen zu sein und hat das Format: 1 (unbenutzt) 24 (Adresse) 1 (Länge des Paketes) 'X' (Befehl: ?) 250 (?) 1 (?) und entspricht nicht dem dokumentierten Protokoll, indem es a) die führenden Nullen wegläßt und b) die angegebene Länge um zwei Byte überschreitet. Der Befehl 'X' sollte eigentlich die Kommandotabelle anfordern, jedoch antwortet der Katana mit einem 'v', was darauf hindeutet, daß er seine Versionsnummer zurückgeben will. 01? 18? 01? 58X fa· 01? slave #24 version 3.1, rev.3 firmware type 24.88 v?N?? n?P¶p?Q ?q?+???s?L¶??l?¦???8? ? ? ? f¦ 01? 18? 01? 58X fa· 01? v?N?? n?P¶p?Q ?q?+???s?L¶??l?¦???8? ? ? ? f¦ Obwohl in Protokoll 1 mehr byte gelesen werden, unterscheidet sich die anschließende Ausgabe nicht vom zweiten Fall. Vielleicht ist die Lesemethode fehlerhaft. Ich probiere es mit getc(). Neue Erkenntnis: Die identisch aussehenden Zeilen werden durch mehrere Wagenrücklauf-Zeichen erzeugt. Das erste Zeichen ist auszuwerten, um den Typen der Antwort zu erkennen. Ergebnis: Die Antwort ist wohl tatsächlich als command table zu verstehen: Die ersten 6 Befehle (B,X,Y,Z,K,O) scheinen dem geforderten Format zu entsprechen. Entsprechende Kleinbuchstaben erscheinen als Ack-Char. Auch ein siebter Befehl, C, wird angegeben, jedoch liefern die folgenden Daten Unsinn. Die korrekte Anzahl bytes der Antwort auf X wird aber eingehalten. 01? 18? 01? 58X fa· 01? #0: Command 'B' on address 24: ack tag: 'b' total length: 3 bytes command length: 1 bytes answer length: 3 bytes #1: Command 'X' on address 24: ack tag: 'x' total length: 3 bytes command length: 1 bytes answer length: 121 bytes #2: Command 'Y' on address 24: ack tag: 'y' total length: 3 bytes command length: 1 bytes answer length: 84 bytes #3: Command 'Z' on address 24: ack tag: 'z' total length: 3 bytes command length: 1 bytes answer length: 1 bytes #4: Command 'K' on address 24: ack tag: 'k' total length: 13 bytes command length: 11 bytes answer length: 2 bytes #5: Command 'O' on address 24: ack tag: 'o' total length: 14 bytes command length: 12 bytes answer length: 2 bytes #6: Command 'C' on address 24: ack tag: '?' total length: 7 bytes command length: 208 bytes answer length: 99 bytes #7: Command '?' on address 68: ack tag: '?' total length: 2 bytes command length: 8 bytes answer length: 100 bytes #8: Command '?' on address 85: ack tag: '?' total length: 3 bytes command length: 5 bytes answer length: 117 bytes #9: Command '?' on address 69: ack tag: '?' total length: 2 bytes command length: 18 bytes answer length: 101 bytes #10: Command '?' on address 70: ack tag: '?' total length: 12 bytes command length: 2 bytes answer length: 102 bytes #11: Command '?' on address 86: ack tag: '?' total length: 3 bytes command length: 13 bytes answer length: 118 bytes #12: Command '?' on address 78: ack tag: '?' total length: 3 bytes command length: 9 bytes answer length: 110 bytes #13: Command '¶' on address 80: ack tag: '?' total length: 18 bytes command length: 8 bytes answer length: 112 bytes #14: Command ' ' on address 81: ack tag: '?' total length: 7 bytes command length: 2 bytes answer length: 113 bytes #15: Command '?' on address 197: ack tag: '?' total length: 2 bytes command length: 18 bytes answer length: 229 bytes #16: Command '¶' on address 76: ack tag: '?' total length: 18 bytes command length: 2 bytes answer length: 108 bytes #17: Command '?' on address 204: ack tag: '?' total length: 3 bytes command length: 2 bytes answer length: 236 bytes #18: Command '?' on address 0: ack tag: '?' total length: 0 bytes command length: 0 bytes answer length: 32 bytes #19: Command '?' on address 0: ack tag: 'f' total length: 0 bytes command length: 0 bytes answer length: 32 bytes ¦ Es scheint eine Verschiebung um 1 byte stattzufinden. Darauf deutet insbesondere der wiederholte ack_char, '?', hin, der die Adresse, 24, des Katana wiedergibt. Die Ausgabe von 16 Nullen vor dem Kommando scheint keinen Einfluß auf das Ergebnis zu haben. Da der Zustand des Katana beim Start des Programms nicht definiert ist, habe ich die Routine waitForReset() implementiert, die den Benutzer auffordert, den Reset-Taster zu betätigen, auf die Rückmeldung des Katana wartet, diese mit dem erwarteten String vergleicht und die Übereinstimmung zurückgibt. Es kommen hiernach mit getCommandTable() weitere intakte Kommandos hinzu (C,D,U,E,F,V,N,P,Q,?,L,?,-,-), wobei '?' für ein seltsames Zeichen steht und '-' für protokollgerecht mit Nullen gefüllte Kommandobeschreibungen. Die Gesamtlänge der Kommandos übersteigt stets die reine Kommandolänge um 2 byte. Diese sind eine 1 und die Adresse (24) des angesprochenen Katana. Auch die "seltsamen" Kommandos sind in dieser Hinsicht plausibel. Man könnte sie als Lower-ASCII-Zeichen 'E' bzw 'L' mit zusätzlich gesetztem Bit 7 ansehen. Es werden 2 byte mehr geliefert als erwartet. Please reset the Katana now... Katana 24 exp Katana 6M Arm, Neuronics AG $Revision: 40.0 $UX Katana reset ok. 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01? 18? 01? 58X fa· 01? 0: Command 'B' on address 24: ack tag: 'b' total length: 3 bytes command length: 1 bytes answer length: 3 bytes 1: Command 'X' on address 24: ack tag: 'x' total length: 3 bytes command length: 1 bytes answer length: 121 bytes 2: Command 'Y' on address 24: ack tag: 'y' total length: 3 bytes command length: 1 bytes answer length: 84 bytes 3: Command 'Z' on address 24: ack tag: 'z' total length: 3 bytes command length: 1 bytes answer length: 1 bytes 4: Command 'K' on address 24: ack tag: 'k' total length: 13 bytes command length: 11 bytes answer length: 2 bytes 5: Command 'O' on address 24: ack tag: 'o' total length: 14 bytes command length: 12 bytes answer length: 2 bytes 6: Command 'C' on address 24: ack tag: 'c' total length: 7 bytes command length: 5 bytes answer length: 3 bytes 7: Command 'D' on address 24: ack tag: 'd' total length: 4 bytes command length: 2 bytes answer length: 8 bytes 8: Command 'U' on address 24: ack tag: 'u' total length: 5 bytes command length: 3 bytes answer length: 5 bytes 9: Command 'E' on address 24: ack tag: 'e' total length: 4 bytes command length: 2 bytes answer length: 18 bytes 10: Command 'F' on address 24: ack tag: 'f' total length: 14 bytes command length: 12 bytes answer length: 2 bytes 11: Command 'V' on address 24: ack tag: 'v' total length: 5 bytes command length: 3 bytes answer length: 13 bytes 12: Command 'N' on address 24: ack tag: 'n' total length: 5 bytes command length: 3 bytes answer length: 9 bytes 13: Command 'P' on address 24: ack tag: 'p' total length: 20 bytes command length: 18 bytes answer length: 8 bytes 14: Command 'Q' on address 24: ack tag: 'q' total length: 9 bytes command length: 7 bytes answer length: 2 bytes 15: Command '+' on address 24: ack tag: 's' total length: 4 bytes command length: 2 bytes answer length: 18 bytes 16: Command 'L' on address 24: ack tag: 'l' total length: 20 bytes command length: 18 bytes answer length: 2 bytes 17: Command '¦' on address 24: ack tag: '8' total length: 5 bytes command length: 3 bytes answer length: 2 bytes 18: Command ' ' on address 24: ack tag: ' ' total length: 2 bytes command length: 0 bytes answer length: 0 bytes 19: Command ' ' on address 24: ack tag: ' ' total length: 2 bytes command length: 0 bytes answer length: 0 bytes f¦ Versuche nun, weitere Kommandos aufzurufen, indem ich eine einheitliche Aufrufkonvention implementiere. Der Katana reagiert nicht auf die Kommandos 'C' oder 'B'. Er scheint auf weitere bytes zu warten. Der Befehl 'X' funktioniert auch nicht wie erwartet - die unerklärlichen bytes 250 und 1 am Ende scheinen entscheidend zu sein. Leider kann man mit denselben nicht die anderen Befehle ergänzen, um ein sinnvolles Ergebnis zu erhalten. Es wäre günstig, weitere Informationen über die Kommunikation zwischen motor.exe und dem Katana zu erhalten, um daraus womöglich Muster ableiten zu können. Nach zwei zusätzlich übermittelten bytes scheint der Katana zumindest die bekannte Nachricht "err" zu schicken anstatt auf weitere Eingaben zu warten. Daher führe ich nun für diverse Kommandos eine Suche durch, um die erwarteten bytes zu ermitteln. Dies geht sehr langsam vonstatten. Hier die Erfolge: Ein-Byte-Befehle: X:250, 1 .. 250, 41 - Read the Katana Command Table Z: 59,128 - Read Echo B: 49,128 .. 255,255 - Read the version and revision number of Katana's master firmware Y: 58,192 .. 255,255 - Read the identification string 20090525 ======== Die beiden Bytes als Worte: X:64001..64041;FA01..FA29 Z:15232 ;3B80 B:12672..65535;3180..FFFF Y:15040..65535;3AC0..FFFF Der Katana liefert beim Kommando 'X' zwei byte zuviel zurück. Ich vermute, daß es sich bei den beiden Bytes, die zuviel hin- und zurückgesendet werden, um einen Prüfcode handelt. Unklar ist, wie er funktioniert und warum bei einigen Kommandos offenbar viele Zahlenkombinationen einen korrekten Prüfcode darstellen. Laut MS ist das Katana Native Interface (KNI) der "Vorgänger" von Katana4D und soll angeblich mit diesem Katana funktionieren. Ich werde das prüfen und mir außerdem den Quellcode ansehen. Der Verdacht hat sich bestätigt: Das KNI bietet als serielles Protokoll nur eine CRC-Variante an; die Basisklasse ist abstrakt. Eine Verbindung zum Katana konnte mit der KNI aufgebaut werden. Um die erweiterten Funktionen nutzen zu können sind vonnöten: a) eine speziell für diesen Katana eingerichtete Konfigurationsdatei und b) eventuell Quellcode für die (inverse) Kinematik. Es ist gelungen, unter Zuhilfenahme des KNI (low level) über Serial-Zero-Protokoll Befehle zu senden, die den Motor 4 ansteuern. Es scheint sinnvoll, die KNI-API weiterhin dafür zu verwenden, anstatt den eigenen Quellcode. So könnte sich das Programm motor.exe nachbilden lassen. 20090527 ======== Im Projekt KatanaNativeInterface funktioniert die Demo KeyControl nicht - sie hängt sich aus unerfindlichen Gründen bei der Initialisierung des Protokolls anhand der Konfigurationsdatei Katana5.cfg auf. Auch wenn ich schrittweise vorgehe, findet ein "Sich-Aufhängen" statt. Ich werde das CRC-Protokoll verwenden, um die Befehle wie dokumentiert zu benutzen. Alle High-Level-Funktionen stehen damit nicht zur Verfügung oder müssen selber implementiert werden. Gegebenenfalls kann der Quellcode des KNI als Vorlage benutzt werden. Matthias Steglich hat bei Neuronics nach einer Konfigurationsdatei gefragt. Die Antwort steht aus. Beim Übersetzen meines Programms mit KNI-CRC-Protokoll tritt oft folgender Fehler auf: 1>cdlSocket.obj : error LNK2019: Verweis auf nicht aufgelöstes externes Symbol "_connect@12" in Funktion ""public: __thiscall CCdlSocket::CCdlSocket(char *,int)" (??0CCdlSocket@@QAE@PADH@Z)". etc. Ich habe ihn schon einmal beseitigt, indem ich an den Einstellungen für statische und dynamische Bindung herumgedreht habe. Dies kann ich jedoch nicht reproduzieren. Nun habe ich den Erfolg reproduziert: In einer Projektmappe habe ich zwei Projekte: ISS_Katana_Terminal und die KNI-Library-Base. Bei beiden habe ich die folgende Option eingestellt: Projekteigenschaften -> Konfigurationseigenschaften -> C/C++ -> Codegenerierung -> Laufzeitbibliothek: /MDd Außerdem habe ich in den Eigenschaften der Projektmappe eine Projektabhängigkeit von "ISS_..." zu "Lib..." eingerichtet. In der Datei "include/common/dllexport.h" habe ich fast alles auskommentiert und die Symbole DLLDIR, DLLDIR_IK und DLLDIR_LM leer definiert. Danach lief die Übersetzung reibungsfrei. Das Protokoll-Interface cplSerial hat eine Schwachstelle: Die Funktion load_tbl, die eine interne Repräsentation des Katana-Command-Tables erstellen soll, ist "hard coded": Die Kommandos mit ihren Tags und Eingabe- wie Ausgabelängen werden hier per Konstanten festgelegt. Da die Methode "load_tbl" virtuell und "protected" ist, genügt es, eine Klasse des Protokolls abzuleiten und die Methode zu ersetzen. Das abgeleitete Protkoll scheint zu funktionieren - der command table sieht sticherprobt gut aus, aber es erscheint eine CRC-Fehlermeldung. Womöglich ist diese auf die geänderte Antwortlänge zurückzuführen. Seltsam ist, daß, obwohl eine Ausnahme erzeugt wird, diese weder von meiner try-Anweisung aufgefangen wird, noch sie das Programm unterbricht. Ich werde mir die CRC-Implementation anschauen. Der CRC-Fehler ist nicht reproduzierbar. Jetzt tritt er regelmäßig nicht auf. 20090528 ======== Erfolg! Sämtliche Motoren lassen sich über mein Programm ansteuern. Ich werde mich daran machen, eine Benutzeroberfläche zu entwerfen. Danach folgt die Justierung. Mein Gedanke dabei ist, zunächst "manuell" herauszufinden, wie groß der Aktionsraum der jeweiligen Gelenke ist, um dann die Justierung entsprechend sinnvoll zu gestalten, denn beim Einschalten nimmt der Katana Standardwerte für die Positionen an, die bei ungünstiger Startposition durch eine Bewegung über- oder unterlaufen können. Daher ist ein Fahren bis zum Anschlag nötig, bei dem ein für den verfügbaren Bereich sinnvoller Positionswert neu definiert werden sollte. Ich habe Schwierigkeiten beim Entwickeln mehrerer Projekte in einer Projektmappe: Es erscheint folgender Linker-Fehler: 1>ISS Katana Control PanelDlg.obj : error LNK2019: Verweis auf nicht aufgelöstes externes Symbol ""public: __thiscall ISS_Katana_CRC::ISS_Katana_CRC(int,int)" (??0ISS_Katana_CRC@@QAE@HH@Z)" in Funktion ""public: void __thiscall CISSKatanaControlPanelDlg::OnBnClickedButtonConnect(void)" (?OnBnClickedButtonConnect@CISSKatanaControlPanelDlg@@QAEXXZ)". Ich konnte den Fehler in einer Beispiel-Projektmappe minimalistisch reproduzieren und werde das Problem in einem Internetforum bekanntgeben. Nun habe ich es doch selber hinbekommen: Ein neues Projekt (win32->Konsolenanwendung->DLL) wurde angelegt und die zu exportierenden Funktionen mit __declspec(dllexport) markiert. Sogleich funktionierte das MFC-Projekt. 20090529 ======== Habe mich mit MFC-Dialogprogrammierung herumgeschlagen. Es ist schwierig zu verhindern, daß ein Dialog durch das Drücken von Enter oder Escape beendet wird, jedoch habe ich unter der URL http://www.codeproject.com/KB/dialog/create_a_dialog.aspx ein gutes Tutorial zum Thema gefunden. Da am Dienstag über das weitere Vorgehen entschieden werden soll, werde ich wohl auf die Slider verzichten und der reinen eingabe von Zahlen den Vorzug geben. 20090609 ======== Die Klasse CMotorSlider ist nun verwendbar, aber noch ausbaubedürftig. Der Versuch, sie zur Ansteuerung des Katana zu verwenden, scheiterte am altbekannten Linkerfehler 2019 bzw 2001. Ich werde versuchen, die Projekte sauberst zu trennen und alle exportierten DLLs im Verzeichnis c:\dll unterzubringen. 20090616 ======== Bei der Justierierung des Motors #1 ist eine Abweichung des Betrages der linken und rechten Grenze von 60 Einheiten zu verzeichnen. Somit weicht der Nullpunkt um 30 Einheiten vom Soll ab. Es sollten Mittelwert und Varianz einer unterstellten Gaußverteilung untersucht und der Mittelwert ausgeglichen werden. Die Varianz sollte als Grundlage der Genauigkeit der Justierierung dienen. Ich werde eine Meßreihe mit unterschiedlichen Startpositionen starten, N>10. Die Messung ist in der Datei "C:\Users\Douba\Documents\Jobs\IESK\Projekte\Katana\Messungen\20090616 - Justierierung Motor 1 - Abweichung.ods" protokolliert. Die kontinuierliche Anzeige des Status blockiert die gesamte Programmausführung. Daher sollte sie besser über einen Timer nach Interrupt-Manier ausgeführt werden. Der Knopf könnte so durch einen Haken ersetzt werden, der selber keine Funktionalität haben muß, da die Timer-Routine nur dessen Wert abfragt. Die Idee ist umsetzbar. Probleme treten auf, wenn ich versuche, während des Abfragens eine Änderung vorzunehmen. Ich sollte das gegenseitig ausschließen oder aber zwei Sets von Slidern bauen: Eines für die Sollposition und eines für die Istposition. Außerdem kann man wider Erwarten das Polling nicht mehr abschalten. Eventuell ist das Abfrageintervall zu gering. Es lag an einer While-Schleife, die ich für den Knopf eingebaut hatte - typischer Copy&Paste-Fehler! Und richtig eingerückt hatte ich es auch nicht, daher habe ich den Fehler nicht gleich gesehen. Das Abfrageintervall sollte nicht unter einer Sekunde liegen, da die Kommunikation dafür zu unzuverlässig ist. Darüberhinaus sollte geprüft werden, ob der Timer zwischenzeitlich mehrfach gefeuert hat. In dem Fall sollten alle vorliegenden Timer-Nachrichten zusammengefaßt werden. Dies kann insbesondere bei der Justierierung auftreten, die den weiteren Ablauf blockiert. Das soll sie auch. Ich werde sie den Timer abstellen lassen. Vielleicht ist auch ein Mutex zu implementieren. Denkfehler: Ein Mutex bringt nichts bei einem Single-Thread-Programm. Die Timer werden ja aufgestaut. Ich sollte fürs Polling einen eigenen Thread starten. 20090618 ======== Aufgabenstellung formuliert. Works ist ganz gut geeignet. Werde nun die Justierung für die anderen Motoren programmieren. Die Hand scheint nicht benutzbar zu sein. Der Motor schnappt bei Überschreiten einer geringen Kraft über, und zwar in beide Richtungen. Man kann den die Hand steuernden Motor also unendlich weit drehen. Offenbar ist die Hand früher einmal einer hohen mechanischen Belastung ausgesetzt worden - die Aluminiumträger sind verbogen. Auch das Programm Motor.exe ist nicht mehr in der Lage, die Hand zu bewegen. Dies war früher jedoch der Fall, obwohl auch damals schon das Problem des "Überschnappens" auftrat. 20090622 ======== Das Kommando 'P' ist in zwei Abschnitte geteilt. Der erste, 'P',1, soll als Antwort 'p',1 liefern. Der zweite, 'P',2, soll 8+1 byte liefern. Im Command Table führt dies zu Problemen, da bei abweichender Anzahl an empfangenen Bytes eine Ausnahme erzeugt wird. Die Soll-Anzahl ist nur pro Opcode, also einmal für 'P' hinterlegt. Um den Befehl 'P' zu nutzen, muß also die Empfangsroutine ersetzt werden. Das Handproblem hat sich als mechanisches herausgestellt. Das Kronrad, das auf der Motorachse sitzt, ist dort mit einer Schneckenschraube befestigt. Diese saß geringfügig locker. Nach dem Festziehen, das keine Strecke spüren ließ, bewegte sich die Hand wieder normal. Der Techniker gab an, daß die Größe des benötigten Sechskant-Imbusschlüssels etwa 1,25mm betrage. Möglicherweise gibt es in der Achse eine Vertiefung, die die Spitze der Schraube aufnehmen soll. Um dies zu prüfen, ist ein Schlüssel vonnöten, den ich privat besorgen werde. So wird die Hand auch Kraft ausüben können, ohne daß sich die Schraube wieder löst. Die Justierung ist wiederholt fehlgeschlagen! Der Roboterarm hält früher an als gewollt, teils werden Gelenke übersprungen. 20090623 ======== Die dynamischen Begrenzungen des Katanas sind nur teils implementiert: Maximale Beschleunigung sowie maximale Geschwindigkeit in positiver und negativer Richtung funktionieren. Nicht implementiert sind Position und Motorstrom. 20090624 ======== Das Handgelenk springt manchmal wieder über. Ich werde, falls keine Vertiefung in der Achse existiert, eine solche anlegen. Dasselbe Problem tritt inzwischen mit dem Handgelenk (Torsion) auf. Auch hier ist eine Schneckenschraube zu finden, die hoffentlich die Ursache dafür ist. Ich werde morgen, sobald ich Imbusschlüssel habe, diese einsetzen. Die Justierungsroutine hatte einen Fehler: Meist wurde die Drehung schon nach einer Sekunde abgebrochen, also bevor der Anschlag erreicht wurde. Dies lag daran, daß die anfängliche Motorposition noch nicht das Setzen der angenommenen Position widerspiegelte. Dies konnte durch eine Wartezeit von 500ms behoben werden. Laut Dokumentation sollen 100ms Wartezeit reichen. Ich werde versuchen, sie entsprechend zu verkürzen. Weiterhin besteht in der Justierungsroutine das Problem, daß manche Motoren aufgrund ungünstiger Konfigurationen des Arms nicht bewegt werden können. Ob die Motoren schlicht nicht genug Moment aufbringen können oder ob dieser Fehler auf lose Schrauben zurückzuführen ist, werde ich morgen klären. Es liegt jedoch nahe, die Routine dahingehend zu ändern, daß die zu einem Zeitpunkt aktuelle Motorposition nicht nur mit der vorhergehenden verglichen wird (hier kann für jeden Motor einzeln ein Zeitintervall festgelegt werden), sondern auch mit der anfänglichen. So wird nicht nur ein Stillstand erkannt, sondern auch eine Rückwärtsbewegung. Die Justierung kann in dem Fall als gescheitert angesehen werden. Es ist einen Versuch wert, vor dem Abbruch den Versuch zu wiederholen oder vielleicht den D-Teil des Kompensators stark zu erhöhen. Das Erhöhen des D-Teils hat gezeigt, daß Motor 3 nicht in der Lage ist, den Unterarm aus der überstreckten Position zu strecken, wenn die Schwerkraft dem bei einem Winkel von ca. 30° zwischen Unterarm und Lot entgegenwirkt. Ich habe die Sensoren den Kanälen zugeordnet. Das Dokument liegt im Aktenordner. 20090629 ======== Im Headerfile der Bibliothek kmlMotBase wird unterschieden zwischen "motor command flags" und "motor status flags", je nach Datenflußrichtung. Es ist unter anderem das Flag 4 als "calibrate" bzw. "calibrating" definiert. 20090701 ======== Einem im 3. Stock des Gebäudes 03 aufgehängten Plakat ohne Quellenangaben zufolge hat der Katana die folgenden Maße: 171,0 mm Höhe (Fuß bis Gelenk) 188,0 mm Länge Oberarm (Gelenk bis Gelenk) 191,0 mm Länge Unterarm (Gelenk bis Flansch) 76,3 mm Länge Hand (Flansch bis Innenseite) 81,0 mm Länge Greifer (Innenseite der Hand bis Fingerspitze) Der Greifpunkt liegt etwa 10mm hinter der Fingerspitze, daher nehme ich die Strecke Ellenbogen bis Greifpunkt zu (191+76,3+81-10)mm=338,3mm an. 20090716 ======== Der Katana hat noch immer Probleme mit der Initialisierung. Daher sollte ich meine alte "Warten auf Reset"-Routine wiederum implementieren. Gerade ist mir dazu eingefallen, daß der Katana ja neben seinem Zugriff auf das Protokoll als Klasse auch einen direkten Zugriff zur seriellen Schnittstelle verwalten kann. Er merkt sich einfach den zur Initialisierung übergebenen Parameter. Vielleicht gibt es auch eine Routine für den Zugriff auf untergeordnete Komponenten. So kann die Routine in der Katanaklasse implementiert werden, ohne daß das Protokoll geändert wird. Es ist nur darauf zu achten, daß keine Zugriffskonflikte auftreten. Dies ist aber in einem Single-thread-Programm gegeben. Die Modellierung des Katana im Ausgangszustand funktioniert! Durch die folgenden Parameter: T.eye(); T*=DH( 0 , 17.1, 0 ,-90*deg).print();printf("\n"); T*=DH( 0 , 0 , 19.1,180*deg).print();printf("\n"); T*=DH(90*deg, 0 , 0 , 90*deg).print();printf("\n"); T*=DH(90*deg, 33.8, 0 , 0 ).print();printf("\n"); wird folgendes Frame erreicht: -0.000 -0.000 1.000 52.900 -1.000 -0.000 -0.000 -0.000 0.000 -1.000 -0.000 17.100 0.000 0.000 0.000 1.000 Dies entspricht einer Position des Greifpunktes von 52.9cm in x-Richtung in einer Höhe von 17.1cm über der Basis. Das Greiferframe ist gegenüber dem Basisframe orientiert, indem die xB-Achse der zG-Achse entspricht, die yB-Achse der umgekehrten xG-Achse und die zB-Achse der umgekehrten yG-Achse. Nun bleiben noch verschiedene andere Positionen zu untersuchen. Auch sollte von einer "wahren" Nullstellung ausgegangen werden (theta_i=M_i statt theta_i=90+M_i), um die Steuerbereiche festzulegen. Eventuell müssen die exakten Winkel der Anschlagpositionen vermessen werden, um die Winkel zwischen Zählereinheiten und Bogenmaß umzurechnen. Als Test werte ich nun die Ausgangsposition des Katanas aus. Es funktioniert auch! Die Parameter T.eye(); T*=DH( 0 , 17.1, 0 ,-90*deg).print();printf("\n"); T*=DH(-130*deg, 0 , 19.1,180*deg).print();printf("\n"); T*=DH(- 30*deg, 0 , 0 , 90*deg).print();printf("\n"); T*=DH( 90*deg, 33.8, 0 , 0 ).print();printf("\n"); führen zum Frame -0.000 0.174 0.985 21.009 -1.000 0.000 -0.000 -0.000 -0.000 -0.985 0.174 37.601 0.000 0.000 0.000 1.000 Das ist auch heurustisch in Ordnung: Der Greifermittelpunkt befindet sich ca. 21cm "vor" der Basismitte (in x-Richtung). Dies wurde mit Hilfe eines DIN-A4-Blattes bestätigt. Er befindet sich auch etwa 7cm oberhalb der Oberkante des aufrechtgestellten Blattes. Das Greiferframe hat sich gegenüber dem vorigen etwas aufwärts geneigt. Dies wird widergespiegelt in der zweiten und dritten Spalte des Frames: Statt nur in negative zB-Richtung weist yG nun auch etwas in positive xB-Richtung, und statt nur in xB-Richtung weist zG nun auch etwas in zB-Richtung. Dies entspricht einer negativen Drehung um die yB-Achse und entspricht der Realität. Nun will ich das Basis- und das Handgelenk drehen. Die Parameter T.eye(); T*=DH( 90*deg, 17.1, 0 ,-90*deg).print();printf("\n"); T*=DH(-130*deg, 0 , 19.1,180*deg).print();printf("\n"); T*=DH(- 30*deg, 0 , 0 , 90*deg).print();printf("\n"); T*=DH( 45*deg, 33.8, 0 , 0 ).print();printf("\n"); führen zu 0.707 0.707 0.000 0.000 -0.123 0.123 0.985 21.009 0.696 -0.696 0.174 37.601 0.000 0.000 0.000 1.000 Die Höhe des Greifpunktes muß gleichbleiben; dies ist der Fall. Der Abstand zur Basismitte muß auch derselbe bleiben, nun aber in yB_Richtung weisen; auch dies ist der Fall. Das Greiferframe muß in Richtung ((xB-dyB+(zB-d))/2, (xB+dyB-(zB-d))/2, (yB-d)+dzB) weisen, wobei die arithmetischen Mittel geometrische sein müssen und d ein Betrag <<1 ist. Auch dies ist der Fall! Nun folgt also die Umrechnung von Zählereinheiten ins Bogenmaß! 20090720 ======== Zum Bogenmaß: Die aktuelle Idee besteht darin, nicht zu versuchen, die Winkel zwischen den Verbindungen des Katanas in der Ausgangskonfiguration zu messen, sondern als Null-Konfiguration eine solche zu wählen, in der sich alle Gelenke in einer horizontalen Ebene befinden. Diese Konfiguration ist zwar schwieriger herzustellen, dafür entfällt aber die ungenaue Winkelmessung. Ich rechne mit wesentlich genaueren Ergebnissen. Ronald wird baldmöglichst versuchen, die Imbusschlüssel zu bestellen. Sollte dies hier zu lange dauern, werde ich sie wohl übers Internet bestellen. Man soll sie laut Tilmann auch in gut sortierten Werkzeugläden bekommen. Ich werde in Buckau anfragen. Ich werde zur Berechnung der Inversen einer Matrix eine Funktion zur Berechnung der Determinante implementieren. 20090727 ======== Ich habe in der vergangenen Woche den Unterarm des Katana auseinandergebaut. Die Hand ließ sich reparieren: Das Spiel entstand wie vermutet in dem mechanisch zugänglichen Teil am Kronrad. Die Antriebsachse hat wie vermutet eine Kerbe/Abflachung an einer Seite. Bisher war die Senkschraube nur gegen den runden Teil der Achse gepreßt worden. Bei zu starkem Widerstand glitt sie dann über die Achse. Durch Erreichen der Vertiefung erklärt sich das schnappende Verhalten der Hand in dieser Situation. Nachdem ich die Hand abmontiert hatte, habe ich die Senkschraube gelockert, um sie sodann leichtestmöglich festzuziehen. Danach habe ich die Achse mit Hilfe einer Büroklammer solange gedreht, bis der Widerstand durch die angezogene Schraube nachließ - so fand ich die Vertiefung. Die Schraube wurde wiederum leicht angezogen, bis ein Widerstand zu spüren war. Ein Wackeln und wiederholtes Anziehen zentrierte die Schraube in der Vertiefung; ein festziehen fixierte sie. Die Hand hat nun kein spürbares Spiel mehr und kann höhere Kräfte ausüben, ohne durchzudrehen. Die Kraftsensoren funktionieren innerhalb gewisser Einschränkungen: Sie sind auf den äußeren Greifzügen fest installiert. Innerhalb befinden sich geklebt eine Schaumstoffpolsterung und ein weiterer Aluminiumzug. In letzteren sind Imbus-Senkschrauben eingelassen, die auf die Kraftsensoren drücken können. Die Sensoren liefern bei Kraftfreiheit einen jeweils unterschiedlichen Grundwert. Ab einer Mindestbelastung erhöht sich dieser bis in einen Sättigungsbereich, der für alle Sensoren numerisch derselbe zu sein scheint. So haben diejenigen Sensoren einen kleineren Meßbereich, die einen höheren Grundwert liefern. Ich habe die Schrauben so justiert, daß sie bei Nichtbelastung der Greifzüge den Sensor gerade aktivieren, so daß bei der kleinsten meßbaren Berührung bereits ein Ausschlag erfolgt. Eine Kalibrierung ist notwendig, aber die Dauerhaftigkeit der Justierung in fraglich, da sich die Klebung mit der Zeit verändert. Daher sind die Kraftsensoren von Zeit zu Zeit zu prüfen und gegebenenfalls nachzujustieren und dann in jedem Fall neu zu kalibrieren. Das größere Problem ist das Handgelenk. Hier sitzt nach dem gleichen Prinzip (abgeflachte Achse, Senkschraube) eine Kupplung auf der Motorachse. Diese dient nur der Verlängerung derselben. Durch eine auf das Getriebe aufgeschraubten und dort fest verleimten, hohlen Zylinderblock, in dem die Kupplung versinkt, sind deren Madenschrauben nicht zugänglich. Er dient der Verschraubung der Motor-Getriebe-Einheit mit dem Unterarmgehäuse. Ich habe eine Seite außerhalb der Schraublöcher weggefeilt, um die Schrauben der auf die Achse aufgesteckten Kupplung zugänglich zu machen. Leider hat trotz erfolgreicher Montage diese Verbindung wiederum durchgedreht, da die Achse selber schon Ringrillen aufwies. Ein weiterer Betrieb dieser Achse ist nicht ratsam. Ich werde für das Getriebe ein Angebot beim Hersteller einholen. Fernmündlich ging man unverbindlich von einer Größenordnung um die 100 Euro aus. Der Hersteller ist: Hauptsitz: maxon motor ag Brünigstrasse 220 Postfach 263 CH-6072 Sachseln Phone: +41 41 - 666 15 00 Fax: +41 41 - 666 16 50 E-Mail: info@maxonmotor.com Deutschland Hauptsitz: maxon motor gmbh Truderinger Strasse 210 DE-81825 München Phone: +49 (89) 42 04 93 0 Fax: +49 (89) 42 04 93 40 E-Mail: info@maxonmotor.de Deutschland Vertrieb Ost (mein Kontakt): maxon motor gmbh Von-Stephan-Strasse 7 DE-01809 Heidenau Tel. + 49 (3529) 53 54 18 Fax:+ 49 (3529) 53 54 19 Handy:+49 (173) 677 03 45 jens-uwe.henke@maxonmotor.de Getriebe (alle Angaben in mm mit einer Toleranz von +-0,10mm): Durchmesser: 10,00 Länge incl. Boden und Deckel: 20,40 Kleinster Innendurchmesser des Bodengewindes: 4,95 Größter Außerdurchmesser des Gegenstücks am Motor: 5,40 Innendurchmesser der Bohrung: 5,90 Tiefe unter Druck am Boden bis zum Plättchen: 3,50 Tiefe unter Druck am Boden bis zum nächsten Sonnenrad: 6,00 Untersetzung: 4 Umlaufgetriebe in Reihe. Jeweils Untersetzung von Sonnenrad auf Planetenräder. Gemeinsames Hohlrad. Sonnenräder: 12 Zähne Planetenräder: 12 Zähne Hohlrad: -36 Zähne Daher pro Getriebe Untersetzung von 4:1 Alle Getriebe: 4^4=256:1 Kupplung: Achsdurchmesser: 2,00 Abflachung: 1,85 Länge der Abflachung: 4,95 Länge der Achse: 6,00 Außendurchmesser der Kapsel: 3,85 Länge der Kapsel: >5,70, vermutlich ebenfalls 6,00 Motor: Der Inkrementalgeber gibt 48 Impulse pro Umdrehung. Getestet wurden 100 Umdrehungen, die Anzahl der Impulse betrug 4800. Gehäuselänge: 32,40 Gehäusefront bis Spitze: 5,15 Aufsatz bis Spitze: 4,85 Gewindeende bis Spitze: 3,00 Aufsatz bis Gewindeanfang: 0,60 +-0,15 Block: Durchmesser: 10,00 Abstand der Gewindebohrungen: 6,80 1 Durchmesser der Bohrung: >4,35 20090804 ======== Das Angebot ist eingegangen. Ronald wird das Getriebe bestellen. Ich habe das Problem der inversen Kinematik des Katanas in drei Teilprobleme zerlegt: a) Winkel der vertikalen Greifebene: Hier sind durch Motor 1 fast 360° verfügbar. Das hat für fast alle Punkte im Raum zwei mögliche Winkel th_1 zur Folge. b) Planarer Zweiarm-Manipulator: Durch die Winkelbegrenzung ergibt sich eine Erreichbarkeitsfläche, von der ein Teil in zwei Konfigurationen erreicht werden kann. Hierbei war das Programm "Cinderella" für dynamische Geometrie sehr hilfreich. c) Die Orientierung des Greifers ist zum einen von der Konfiguration in a) abhängig, zum anderen nur von der Stellung des Motors Nr. 4. Was noch zu tun ist: Die genauen Grenzwinkel sind anhand der Motorzähler zu ermitteln. Damit sind auch die in der Dokumentation angegebenen Umrechnungsfaktoren zu überprüfen. Die Ergebnisse sind zur genauen Begrenzung des erreichbaren Raumes heranzuziehen. 20090805 ======== Ich durchblicke die Transformationen noch nicht. Ich muß mir darüber klarwerden, "wieherum" eine Transformationsmatrix zwischen zwei Frames genau funktioniert, und wie ihre Inverse. Beide dienen zum einen dazu, einen Punkt eines Frames im anderen darzustellen; andererseits definieren sie die Lage und die Orientierung des nächsten Frames. Am besten sollte ich die vier Fälle mit "gegeben" und "gesucht" gliedern und die zugehörigen Matrizen und ggf. Punkte auf den C-Code beziehen. Danach ist eine formale Prüfung des Codes möglich. Es wäre außerdem zunächst noch zu prüfen, ob Frames und ihre Inversen multipliziert die Einheitsmatrix ergeben. 20090506 ======== Ich habe mir einen Plan gemacht über die Beziehungen zwischen Frames und Trafo-Matrizen. Daran werde ich nun die Vorwärtskinematik ausgiebig testen. Es hat geklappt! Die Matrixmultiplikationen waren völlig in Ordnung. Vorzeichenfehler: Ich habe die Winkel in die falsche Richtung gelegt. Folgender Test war qualitativ erfolgreich: Ich habe den Katana so bewegt, daß sein Greifer den Ursprung, also eine Ecke des Brettes, greifen könnte. Dann habe ich die Winkel gemessen, in denen die Teile zueinander stehen. Dies war eine sehr ungenaue Messung (und unsicher - siehe Vorzeichenfehler!) Diese Winkel hab ich in die Vorwärtskinematik gespeist und das zugehörige Fram ausrechnen lassen. Heraus kam folgendes Frame: -0.259 0.913 -0.314 -14.256 0.966 0.245 -0.084 31.891 -0.000 -0.326 -0.946 -9.662 0.000 0.000 0.000 1.000 Die Abstände sind in Millimetern angegeben. Somit befindet sich der errechnete Frame-Ursprung etwa einen Zentimeter über dem Soll und hat zu diesem in der XY-Ebene einen Abstand von etwa 3,5 Zentimetern. Das ist im Rahmen der Meßgenauigkeit ein akzeptables Ergebnis, das die korrekte Repräsentation des Armes mittels der DH-Parameter bekräftigt. Weitere Tests sind erforderlich, um dies zu untermauern. 20090807 ======== Definiert man den Nullpunkt wie gehabt, dann gibt es im Gelenk 2 einen Überlauf. Er ist daher zumindest intern (Motoreinheiten) umzudefinieren. Die DH-Parameter können ggf. gleich bleiben, wenn die Verschiebung des Nullwinkels bei der Umrechnung von Bogenmaß in Motoreinheiten berücksichtigt wird. 20090814 ======== Das neue Getriebe ist da. Es funktioniert augenscheinlich anstandslos. Wie vermutet hat es ein Schraubgewinde. Möglicherweise kommt man ohne Kupplung aus. Die Achse steht etwa 7mm über das Gewinde, während die Kupplung üder den Block ca. 8mm steht. Da das Loch im Armgehäuse zu schmal ist, kommen nur 6,25mm der Achse zum Tragen. Die Wanddicke beträgt 1,15mm, also stehen von der Achse 5,10mm über, wenn das Getriebe auf Stoß mit dem Armgehäuse verbunden werden kann. Zu überwinden ist eine Tiefe von (-3,05+11,05-3,05+1,65)mm=6,60mm. Es fehlen also 0,35mm. Würde man das Loch im Armgehäuse vergrößern, könnte man 0,75mm gewinnen und so das Problem lösen. Erforderlich wäre es, Gewinde in den Rand des vorhandenen Gewindes zu schneiden, dieses also zuerst zu verschrauben und zu verleimen. Dies ist riskant und kann nicht rückgängig gemacht werden. Die Alternative besteht darin, die bisherige Konstruktion zu imitieren. Allerdings würde ich als Verbesserung einen Zugang zu den Madenschrauben vorschlagen, so daß die Kupplung bei Bedarf ersetzt werden kann. Beim Aufschrauben des Getriebes auf den Motor kann es passieren, daß die Zähne der Planetenräder ungünstig zu denen des Sonnenrades konfiguriert sind. Dann entwickelt das Getriebe im Betrieb laute Geräusche (Quietschen). Dies sollte durch Entfernen und erneutes Anbringen des Getriebes behoben werden, bis nur noch sehr leise Geräusche zu vernehmen sind. Die Justierung der reparierten Hand scheint mit dem gewählten System nicht zu funktionieren. Weitere Versuche sind notwendig - debuggen! 20090915 ======== Ich war heute in der Werkstatt und habe das Getriebe mit Aufsatz und Kupplung bekommen. Die Form der Kupplung muß noch geringfügig nachgearbeitet werden, da die Achse der Kupplung etwas zu dick ist. So müßte ich das Gegenstück des Katanas daraufhämmern, was sich schlecht rückgängig machen ließe, jedoch den Vorteil hätte, daß die Teile kraftschlüssig verbunden wären. Ich habe mich entschieden, lieber 1-2 Hundertstelmillimeter abtragen zu lassen und einen Anschliff fertigen zu lassen, da diese Methode an dieser Stelle auch vorher unproblematisch war. Morgen Mittag werde ich die Teile abholen und hoffentlich den Katana wieder zusammenbauen. Ich bin heute in der Praktikumsraum umgezogen. DLL-Export: Ich habe einige Tutorials im Netz gefunden, die zwei Methoden des DLL-Exports beschreiben. Zum einen gibt es die Variante, per Präprozessoranweisung zwischen "exportieren" und "importieren" umzuschalten. Dies scheint mir dedoch wegen der unterschiedlichen Compilerimplementierungen die unsicherere oder inkompatiblere Methode zu sein. Zum anderen gibt es die Möglichkeit, eine Moduldefinitionsdatei "Erweiterung .DEF" anzulegen, die Angaben über die zu exportierenden Objekte enthält. Ich muß noch herausfinden, ob dies compilerspezifisch funktioniert. Laut http://www.codeguru.com/cpp/cpp/cpp_mfc/tutorials/article.php/c9855 ist a) microsoftspezifisch und b) nicht. Ich werde also wohl Moduldefinitionsdateien verwenden, auch mit Blick auf eine zukünftige Linux-Portierung. Eine Methode, gegen eine DLL zu linken ist es, eine ".LIB"-Datei erzeugen zu lassen, und sowohl diese als auch die DLL in das Verzeichnis des verwendenden Projektes zu kopieren. Ich frage mich, ob ich in diesem Fall nicht einfach auch eine ".LIB"-Datei alleine benutzen kann. Das Header-File und die DLL-LIB werden in diesem Fall ohnehin gebraucht. Mit nur einer LIB wäre also eine Datei weniger zu kopieren. Die andere Methode, Funktionen eine DLL zu importieren ist es, sie ohne Headerdatei einzeln aus der DLL zu "holen". Das ist für die Zwecke des Lehrstuhls zu kopmliziert. Es scheint paradoxerweise sinnvoll zu sein, eine DLL zu generieren, deren eigentlicher Vorteil gegenüber der LIB die Speicherersparnis ist, die auftritt, wenn mehrere Programme gleichzeitig dieselbe Bibliothek nutzen. Es sollten keine zwei Programme gleichzeitig auf den Katana zugreifen können. Daher ergibt sich dieser Vorteil hier nicht. Dennoch ergibt sich ein anderer großer Vorteil: Da sie dynamisch eingebundene DLL ihren eigenen Speicherplatz für statische Objekte hat, kann sie Programmübergreifen ein Protokoll darüber führen, ob der Katana bereits in Verwendung ist, während dies bei Der Verwendung einer LIB nicht der Fall wäre, da hier der Speicherplatz der verschiedenen Anwendungsprogramme inklusive der statisch eingebundenen Bibliothek(en) voneinander völlig getrennt ist. So ist keine Programmübergreifende Protokollierung möglich, und ein Konflikt bei gleichzeitigem Zugriff auf den Katana wird ermöglicht. http://msdn.microsoft.com/en-us/library/hyx1zcd3(VS.80).aspx sagt: "Note that when you export a variable from a DLL with a .def file, you do not need to specify __declspec(dllexport) on the variable. However, in any file that uses the DLL, you must still use __declspec(dllimport) on the declaration of data." - Mist! Auf http://www.ibm.com/developerworks/aix/library/au-porting/index.html?S_TACT=105AGX20&S_CMP=EDU findet sich eine Besprechung der Portierung von Code zwischen Windows- und Unixoid-Compilern. Zitat: "Porting across two completely divergent systems, such ase Windows and UNIX, is never an easy task and, as such, it requires a lot of tweaking and patience." Auch die MDFs scheinen hier keine Abhilfe zu schaffen. Ich werde versuchen, das Portieren der Katana-Bibliothek auf Unix nicht als Teil meiner Studienarbeit laufen zu lassen. http://c.ittoolbox.com/groups/technical-functional/cpp-l/static-varibles-in-dll-554455 sagt: "In older days global/static variables declared in dll are shared by the processes that are using the dll. It was like Inter Process Communication. But now variables declared in dll will occupy process memory space. i.e., they will be duplicated in each process using the dll. Only code in the dll is common to the process, not the variables." - Es ist also nicht möglich, zwischen den Prozessen Instanzen zu zählen. Der Versuch, eine Klasse mit Memberfunktionen zu exportieren und in ein anderes Projekt in derselbe Projektmappe einzubinden, hat geklappt! Ich habe die Methode a) gewählt, um dieselbe .h-Datei sowohl für Export als auch für Import verwenden zu können. Dann muß ein "library search record" zum Einbinden der Importbibliothek in die sie verwendende CPP-Datei angelegt werden. Dies geschieht mit der Anweisung #pragma comment(lib,"") wobei durch Pfad und Dateiname der erzeugten .lib-Datei zu ersetzen ist. Die .h-Datei sollte normal mittels #include "
" eingebunden werden. Die DLL sollte sich zur Laufzeit in einem der folgenden Verzeichnisse befinden: - dem, in dem sich auch die EXE befindet, - dem aktuellen Verzeichnis (zunächst das, aus dem die EXE aufgerufen wird), - dem Windows-Unterverzeichnis system32 (oder system?), - dem Windows-Verzeichnis oder - einem in die Umgebungsvariable PATH aufgenommenen Verzeichnis. 20090916 ======== Heute habe ich das Getriebe eingebaut. Leider blockiert die Drehung des Armes irgendwann. Der Motor macht abhängig von seiner Orientierung zur Schwerkraft seltsame Geräusche und kann anscheinend kein großes Moment mehr aufbringen. Der Unterarm läßt sich manuell nur sehr schwer bewegen, und das Motormoment reicht manchmal nicht aus, um ihn aus einer Klemmposition fortzubewegen. Hilft man manuell nach, so wird die Bewegung ausgelöst. Möglicherweise ist Klebstoff in das Getriebe gekommen. Dann aber müßten Klemmeffekte auch auftreten, wenn das Getriebe ohne Arm auf den Motor aufgeschraubt ist. Die Konstruktion ohne angebrachtes Getriebe läßt sich leichtgängig bewegen. Das Getriebe alleine verhält sich wie das alte Getriebe. Am Motor angeschraubt scheint es sich ebenso zu verhalten. Ich könnte mir vorstellen, daß die Kupplung mit dem neuen Flansch etwas exzentrisch steht, da eines der Flanschgewinde weiter innen ist, als es sollte, so daß die Achse je nach Drehwinkel unter Scherspannung steht. Gegen diese könnte der Motor wirken müssen. Daher will ich versuchen, das alte Getriebe zu verwenden, indem ich es kleben lasse. Leider hat die Werkstatt heute schon geschlossen. Eine andere Möglichkeit wäre es, die Flanschschrauben nicht ganz anzuziehen. Sie werden durch eine konische Senkung jeweils zentriert, so daß bei angezogenen Schrauben kein Spiel mehr besteht und die Achse in die korrekte Position gebracht wird. Das Lockerlassen hätte einerseits zur Folge, daß die Achse sich an ihrem Gegenstück frei ausrichten kann, andererseits wäre ein Winkelspiel zu erwarten, daß sich direkt auf das Spiel des Unterarms auswirken würde. Ich habe die Katana-Bibliothek zu exportieren versucht. Leider sind weiterhin einige Headerdateien der KNI einzubinden. Um dies zu vermeiden, sollte vielleicht der entsprechende Code in meine Bibliothek kopiert werden. Bei dieser Gelegenheit könnte auch eine "Warten auf Reset"-Routine an entsprechender Stelle eingebunden werden. Zudem scheint in den Bibliotheken selber ein Verweis (inklusive Pfad) auf referenzierte Bibliotheken enthalten zu sein. So müssen diese auch nach der Weitergabe im selben Pfadverhältnis zueinander stehen. Das sollte vermieden werden. Besser wäre es, nur den Namen einer Biliothek an dieser Stelle zu speichern und den Linker zur Compilierzeit danach suchen zu lassen. 20090917 ======== Das alte Getriebe wurde geklebt und schien zu funktionieren. Leider hielt der Kleber die Kupplung nicht an der Achse, was am mangelden Luftabschluß liegen könnte. Ich werde morgen noch einmal in die Werkstatt gehen. Dort wurde heute versucht, neue Bohrungen in den Kopf zu machen, um die Exzentrizität zu beseitigen. Ich habe versucht, die KNI in meine Bibliothek zu integrieren. Es fehlen offenbar noch einige Implementierungen von Prototypen, denn das Projekt "KatanaImportTest" meldet Linkerfehler 2019 und 2001. Komischerweise ist das beim Katanaterminal nicht der Fall. 20090921 ======== Ich habe das neue Getriebe mit neuen Löchern eingebaut. Es sah recht zentriert aus. Leider stieß ich aber wiederum auf Widerstand. Dieser schien hauptsächlich aufzutreten, wenn man den Arm manuell gedreht hat. Der Motor schien damit wenig Probleme zu haben. Meine Vermutung, es läge an der Exzentrizität, hat sich nicht gehalten: Ich steckte Abstandhalter zwischen die beiden das Lager einschließenden Ringe, damit das Achs-Gegenstück (einer der Ringe) sich nicht zwangsläufig am Lager ausrichten muß. So hätte der Widerstand verschwinden sollen, wenn es daran gelegen hätte. Er trat jedoch weiterhin auf. Mit etwas mehr Moment habe ich dann manuell die Kupplung zerstört. Diese hatte zwischen Rohr-Teil und Achse nur eine sehr kleine ringförmige Verbindungsfläche mit einer Breite von 0,15mm. Hier scherten die beiden Teile auseinander. Der Stahl scheint auch sehr weich zu sein: Mit der Kneifzange zog ich die Achse aus dem Gegenstück. Dabei bildeten sich deutliche Vertiefungen dort, wo ich mit der Zange angriff. Herr Arndt ist laut Herrn Seluga erst am Montag wieder im Hause, so daß nun keine weiteren Versuche stattfinden können. Die größte Frage besteht darin, woher dieses Widerstandsmoment kommt. Das Getriebe selber läßt sich genauso leicht bewegen wie das alte. Die Zentrierung scheint keine Rolle zu spielen (es sei denn, daß den Abstandhalter zum Trotz die drei Schrauben, die die Ringe zusammenhalten, beide stark zentrierten). Der Widerstand tritt noch nicht dann auf, wenn man das Achsgegenstück auf die Achse aufbringt. Es wäre wünschenswert, eine neue Madenschraube zu bekommen, die die derzeitige ersetzt, denn in dieser greift der Imbusschlüssel nicht mehr. Ich werde an anderer Stelle weiterarbeiten. 20090922 ======== Heute habe ich mich daran gemacht, die Anzahl der einzubindenen Headerfiles zu reduzieren und eine Wrapper-Klasse, die mit "natürlichen" Größen wie Winkeln in Grad und Strecken in Millimetern arbeitet. Dabei habe ich gelernt, daß, wenn man eine Klasse ableitet, von der man einiges in der neuen Klasse verstecken (oder in diesem Fall abwandeln) will, man dies kann, indem man die Basisklasse zunächste 'private' ableitet und dann offenzulegende Objekte durch Nutzen von Namespaces nach außen durchreicht. Beispiel: class X { public: typedef struct { double X, Y; } TPosition int Variable; double Funktion(TPosition pos); } class Y : private X { // packt alle Member von X als private Member in Y. using X::TPosition; // reicht Objekte unverändert durch. using X::Variable; using X::Funktion; } Die durchgereichten Objekte sind danach Bestandteil beider Namensräume (X und Y). In meinem Fall unterscheiden sich alle Größentypen zwischen Roh- und Einheitenversion. Beim Verfassen einer Funktion zur Umwandlung der Datentypen ist ein unerwartetes Problem aufgetreten: Typendefinitionen in C++ mittels 'typedef' sind nicht streng. Beispiel: typedef double TPosition; typedef double TVelocity; void test(TPosition); void test(TVelocity); // Fehler Die beiden Typen werden synonym zu double behandelt. Somit haben die beiden Funktionsdeklarationen identische Signatur, was zur Ausgabe eines Fehlers führt. Dabei habe ich die Typen gerade deshalb deklariert, daß sie inkompatibel zueinander sein sollen, damit man beispielsweise nicht die Reihenfolge von Parametern versehentlich vertauschen kann. Es gibt aber eine Lösung dafür unter http://www.ddj.com/cpp/184401633. Diese ist allerdings zu schwer für dieses Projekt, also werde ich die überladene Funktion einfach umbenennen. Mist, ich dachte, C++ wäre typenstreng. 20090924 ======== Heute will ich zunächst die Umrechnung von Zählereinheiten in Winkel testen. Am besten wird es sein, sich auf das Bogenmaß festzulegen, da hier die numerische Genauigkeit am höchsten ist (Die Fließkomma-Wertedichte ist nahe 0 am höchsten). Danach ist die inverse Kinematik in Angriff zu nehmen: Die Vorwärtskinematik ist nocheinmal mit testXYZ() zu testen. Danach ist das Problem der Kinematik zu zerlegen. Es gibt für manche Punkte vier mögliche Konfigurationen. Dies ist eventuell im Protokoll zu berücksichtigen - denkbar ist die Angabe der gewünschten Konfiguration (vorwärts/rückwärts/nächste und von oben/von unten/nächste (Ellenbogenkonfiguration)); "nächste" sollte Standardparameter sein. Über Konfigurationen ist Literatur zu konsulitieren, um möglicherweise wertvolle praktische Hinweise zu erhalten. 20090925 ======== Gestern habe ich die Funktionalität der inversen Kinematik erarbeitet. Was noch fehlt, ist eine Kostenfunktion für den Abstand der aktuellen Position zur gewünschten. Diese ist wünschenswert, um bei mehreren möglichen Konfigurationen zum Erreichen eines Punktes die günstigste auszuwählen. Eine Möglichkeit wäre es, die Zeit zum erreichen der Zielposition abzuschätzen. Dazu wäre die mittlere Winkelgeschwindigkeit für jeden Motor zu ermitteln, der Abstand hierdurch zu dividieren und das Maximum der Werte zu ermitteln. Zum anderen wäre eine einfache Wichtung und Summierung der Winkelabstände denkbar. Fraglich ist, ob es sinnvoll ist, bei jedem Aufruf von gotoXYZ die aktuelle Position des Katanas abzufragen. Es besteht das Problem der Übersetzung von Winkelkoordinaten in Motoreinheiten. Die Anschlagwinkel lassen sich am Katana nicht direkt messen. Anhand von Streckenmessungen und geometrischen Überlegungen habe ich heute auf die Anschlagwinkel des Ellenbogens geschlossen. Da die Nullstellung genau in der Mitte liegt, sind die Bogenmaßwinkel eindeutig bestimmt und lassen sich unter Kenntnis der Motoreinheiten in den Anschlägen und der identischen Nullstellung in beiden Systemen ohne Offset nur über einen Faktor direkt ineinander umrechnen. Auch das Turmgelenk wurde gestern mit hinreichender Genauigkeit untersucht. Auf dem Montagebrett ließen sich Punkte abtragen, die der das Brett berührende Greifer in den Turmanschlägen einnahm. Aus dem gemessenen Abstand der Punkte zueinander und ihrem Abstand zur Mitte des Turmes (Abstand zur Oberfläche plus halber Durchmesser des Turmes) läßt sich der Öffnungswinkel des toten Bereichs bestimmen. Auch hier ist die direkte Übersetzung wie beim Ellenboge möglich. Schwieriger gesteltet sich die Untersuchung des Schultergelenks. Aufgrund der komplexen Geometrie der Anschläge lassen sich hier die reellen Winkel auch nicht indirekt berechnen. Man wird indirekt über Höhenmessungen des Ellenbogens vorgehen müssen. 20090928 ======== Es sind noch Default-Parameter für die Stellung des Handgelenks (und ggf. des Greifers) in gotoXYZ zu implementieren. Na ja, der Greifer sollte vermutlich eher über High-level-Methoden wie "open..." und "close..." angesteuert werden können. Vielleicht ist es möglich, einen Anpreßdruck anzugeben oder die Sensoren zu nutzen, um ein Objekt zu greifen und dann noch um einen angegebenen Wert weiter zu schließen. Diese Funktionalität in gotoXYZ zu integrieren wäre eher gegenintuitiv und unpraktisch. 20090929 ======== Heute hat es geklappt! Die inverse Kinematik funktioniert für die Basiskonfiguration mit beliebigen Koordinatensystemen. Die Präzision läßt noch zu wünschen übrig und muß weiter untersucht werden. Die Konfigurationen sind noch nicht wohldefiniert und müssen korrigiert werden. Die Umsetzung der Motorkoordinaten in Winkel im Bogenmaß ist vollständig, muß aber gegebenenfalls verfeinert werden, um die Genauigkeit zu erhöhen. Hierzu ist außerdem ein Programm oder eine Routine zur Kalibrierung zu entwerfen. Gegebenenfalls muß die Physik des Katanas weich kodiert werden, um Anpassungen vornehmen zu können. 20091012 ======== Ich will neben dem Verfassen eines Inhaltsverzeichnisses für die Studienarbeit heute einige Verbesserungen am KatanaPanel vornehmen. Dazu gehören eine "anklickbare" Befehlssammlung, Dialoge mit properen Tabstops und Enter-Abfrage. Eine interessante Klasse ist die CPropertyPage. Weiter habe ich Ideen zum Abfangen von Ausnahmen und zum Wiedereinbau der anfangs erwähnten Routine waitForReset() 20091021 ======== Heute habe ich einen Termin mit Herrn Arndt gegen 8:30 Uhr hier im Raum 320. Er will sich den Katana einmal ansehen - vielleicht hat er eine Idee, was man noch machen könnte. Idee: Man könnte die Justierung der Gelenke simultan vornehmen. Dazu wird ein stabiles Protokoll vorausgesetzt. 20091123 ======== Vorletzte Woche haben Herr Rodiek und ich uns drei weitere Möglichkeiten ausgedacht, wie man das Handgelenk des Katanas reparieren könnte. Grundannahme dabei war, daß man die Motorachse bzw. die Achse der daran angebrachten Kupplung steif mit dem Gegenstück verbinden könnte und danach nur sicherstellen müßte daß das Drehmoment des Motors gegenüber dem Gegenstück auf den Unterarm übertragen würde. Es sei jedenfalls hinderlich, den Motor auch mit dem Unterarm steif zu verbinden, da dies eine weitere Zwangsbedingung wäre, die wegen Ungenauigkeit der Zwangsbedingung des Gegenstücks widerspräche. So ist wohl bisher die Achse abgeschert. Die angedachten Lösungen sind: a) Befestigung wie bisher, jedoch wird eine Gummischeibe zwischen Motorstirn und Unterarm eingelegt, und die Schrauben werden nicht festgezogen. So soll ein Spiel entstehen, das Scherkräfte kompensieren kann, ohne daß eine Hysterese in der Drehung entsteht. b) Der Motor wird im Arm in eine Gummikapsel eingelegt, die Drehmomente statisch überträgt und seitliche Kräfte kompensiert. Problematisch ist hier die Plazierung der Hülse und des Motors sowie die Unterbringung der Kabel, die am Motor vorbeilaufen. c) Es wird ein dem Motor passiger Gummiring mit zwei sich außen gegenüberliegenden Verdickungen angefertigt, in die Schraubgewinde eingegossen sind. Durch seitliche Bohrungen in den Unterarm wird der Ring so gegen Drehungen im Arm gesperrt, wobei seitliche Bewegungen in den Raum zwischen Ring und Arm stattfinden können. Das Einlassen und verankern der Gewinde ist problematisch. Vielleicht ist eine direkte Verschraubung mit dem Material möglich. Herr Arndt möchte den Katana gerne in die Werkstatt nehmen, um die Konstruktion testen zu können. Ich versuche gerade die KNI-Bibliotheken komplett aus dem Projekt zu verbannen und mein eigenes Kommunikationsprotokoll zu entwickeln, da das KNI-Protokoll sehr langsam ist. Der Programmierer soll selbst bestimmen können, wie viele weitere Versuche bei einem Fehler in der Kommunikation unternommen werden. Es sind keine abstrakten Schichten für jeweils das Kommunikationsgerät (COM1), das abstrakte Protokoll, das konkrete Protokoll und das konkrete Protokoll mit CRC nötig. All diese lassen sich in einer zusammenfassen auf Kosten der Allgemeinheit für mögliche andere Verbindungen zum Katana. Da dies bisher nicht vorgesehen ist (der Aufwand wäre nicht gerechtfertigt), geht hier nichts verloren. 20091124 ======== Da ich gestern die Kommunikation nicht wieder herstellen konnte, habe ich eine alte Version von KatanaPanel.exe wiederhergestellt und werde sie so bald wie möglich auf Herrn Arndts Computer einrichten. Dazu gebe ich ihm eine Anleitung zur Bedienung. Es wäre sicher wünschenswert, ihm ein Programm zur Verfügung zu stellen, das speziell auf seine Aufgabe zugeschnitten ist, jedoch drängt die Zeit, und mit der alten Version von September ist es auch möglich, den Motor zu testen. Zur Zeit hapert es bezüglich der Kommunikation an folgenden Stellen: Der Katana antwortet auf 'X' mit 'err'. Ich nehme an, daß mir beim Übertragen der CRC-Funktion ein Fehler unterlaufen ist. In diesem Zuge ist mir aufgefallen, daß das Programm sich aufhängt, wenn es versucht, Zeichen vom Katana zu empfangen, der Puffer jedoch leer ist. Demm könnte durch einen Timeout abgeholfen werden, jedoch erschließt sich mir nicht, wie man einen Timer dazu bringt, das Programm wieder anzustoßen, wenn es in einer Warteschleife hängt. Die andere, mehrversprechende Möglichkeit besteht darin, die Zeichen einzeln abzuholen und jeweils vorher abzufrage, ob noch ein weiteres Zeichen vorhanden ist. Ich werde wohl in der KNI abgucken. zur Prüfsumme: Ich hatte fälschlicherweise zur Berechnung der Prüfsumme auch den Header miteinbezogen. Es sind nur das Kommandotag und die Daten der Prüfsumme zu unterziehen. Zum Timeout: Es gibt Windows-API-Funktionen, die die Timeouts regeln: GetCommTimeouts und SetCommTimeouts. Ich werde sehr kleine Werte wählen, um das Protokoll zu beschleunigen, oder ich werde keine Toleranz zulassen. Beim Lesen der Befehlstabelle reagiert der Katana erst nach mehr als 10ms, aber weniger als 50ms. Derzeit habe ich eine fest Wartezeit zwischen dem Senden des Befehls unf dem Empfangen der Antwort eingestellt. Besser wäre es, eine maximale Wartezeit für den Beginn der Kommunikation und eine maximale Wartezeit für die Pause zwischen den bytes anzugeben. Schließlich geht es darum, so schnell wie möglich die Antwort zu erhalten, und diese Dauer wird vom Katana vorgegeben. Wird also rechtzeitig entdeckt, daß er antwortet, und fällt die Kommunikation schnell genug aus, so kann davon ausgegangen werden, daß die Antwort komplett eintrifft. Andernfalls muß geschlossen werden, daß der Katana nicht antwortet bzw. die Antwort nicht die erwartete Länge aufweist und somit ungültig ist. Nur in diesem Fall sollte eine Ausnahme auftreten, in allen anderen Fällen, auch wenn sie langsam sind, soll die Antwort akzeptiert und mit der Ausführung fortgefahren werden. Jaaaaaaaaa! Mein Protokoll funktioniert und ist viel schneller als das mitgebrachte! Es gibt aber noch Probleme: Der 'X'-Vefehl liefert ein byte weniger als erwartet. Da ich dies einmal geändert hatte, ist es vielleicht mein Fehler. Allerdings erwarte ich für 20 Befehle auch 6 byte * 20 = 120 byte, dazu ein 'x' (ack) und 2 byte für die Prüfsumme, insgesamt also 123 byte. Es werden jedoch nur 122 gesendet. Mir fällt auf, daß wohl dasselbe byte wie am 18.5.2009 fehlt - im 7. Befehl 'C' steht statt des acks wieder die Adresse (24) des Katanas. Auch ein wiederholtes Anfordern der Tabelle ohne Stop verhindern das nicht. Der Fehler ist reproduzierbar. Es wäre zu prüfen, was sich damals verändert hat, damit es lief. Anscheinend habe ich nur auf einen reset gewartet. Ein purge() vor dem Schreiben schafft Linderung, aber keine Abhilfe: Das fehlende byte liegt jetzt im 20. Befehl, nicht mehr im 7. Viele, viele purge()s und Erhöhen der Timeouts haben zu einem Erfolg geführt, aber nicht reproduzierbar. Vielleicht muß man doch die Zeichen einzeln abholen. Ich mache Schluß für heute. 20091201 ======== Gestern habe ich den Katana zu Herrn Arndt gebracht. Merke: Wenn man einen Termin mit ihm hat, sollte man zur vereinbarten Zeit auch an anderen Orten als dem vereinbarten nachschauen. Auch sollte man beide Telefonnummern ausprobieren. Leider funktioniert meine .exe-Date nicht auf seinem Rechner. Dafür muß ich die verwendeten Bibliotheken statisch verlinken. Die entsprechenden Einstellungen führen jedoch jeweils zu weiteren Problemen. Hier mein bisheriges Vorgehen: 1) In allen Projekten unter Projekteigenschaften -> C/C++ -> Codegenerierung -> Laufzeitbibliothek auf einen Wert ohne DLL. Hier: /MTd Das führt zu LNK2005. 2) Unter Projekteigenschaften -> Allgemein -> Verwendung von MFC "statische Bibliothek" auswählen. 3) Unter Projekteigenschaften -> Linker -> Eingabe -> Bibliothek ignorieren "libcmtd.lib" und "libcmt.lib" eintragen. Das führt zu LNK2001. 4) Unter Projekteigenschaften -> Linker -> Eingabe -> Zusätzliche Abhängigkeiten "C:\Development\Microsoft Visual Studio 9.0\VC\lib\libcmtd.lib" eintragen. Das führt zu LNK1104 5) Den letzten Eintrag auch in Anführungszeichen setzen. Das führt zu LNK2005 identisch zu 1). Haha! Habe bei ISSKinematics die Verwendung von vorkompilierten Headern ausgeschaltet. Die aktuelle Konfiguration (Debug/Release) läßt sich unter den Eigenschaften der Projektmappe im Konfigurationsmanager einstellen. Vielleicht wäre es die bessere Herangehensweise, alles dynamisch zu verlinken wie vorher und dann die erforderlichen .dll-Dateien mitzugeben. Dies fand ich unter http://cboard.cprogramming.com/cplusplus-programming/97754-warning-lnk4098-defaultlib-libcmtd-conflicts-use-other-libs.html: Reusable Library Switch Library Macro(s) Defined ---------------------------------------------------------------- Single Threaded /ML LIBC (none) Static MultiThread /MT LIBCMT _MT Dynamic Link (DLL) /MD MSVCRT _MT and _DLL Debug Single Threaded /MLd LIBCD _DEBUG Debug Static MultiThread /MTd LIBCMTD _DEBUG and _MT Debug Dynamic Link (DLL) /MDd MSVCRTD _DEBUG, _MT, and _DLL Das Problem der statischen Verlinkung ist nun gelöst: In den Quelldateien selber waren Verweise auf Debug-Bibliotheken angegeben, die die Projekteinstellungen überschrieben haben. Ich habe sie entfernt und stattdessen die Release-Bibliotheken in den Linker-Einstellungen eingebunden. Ich habe noch nicht verstanden, warum die KNIBase.lib ins Projektverzeichnis und die ISSK*.libs in das Release-Verzeichnis geschrieben werden. Auf einem Windows-2000-Rechner erscheint beim Start von KatanaPanel.exe die Fehlermeldung: "Die Dynamic Link Library gdiplus.dll wurde nicht im angegebenen Pfad [...] gefunden". Das scheint nach Informationen aus dem Internet eine XP- und vista-eigene Datei zu sein, die bei W2000 nicht enthalten ist. Die neue KatanaPanel.exe belegt ca. 2,5 MByte, ist dafür aber zumindest auf KO-PC??? ausführbar. 20091202 ======== Idee: Schnittstelle klarer strukturieren, damit DH zur Kontrolle von verbotenen Bereichen eingesetzt werden kann - möglicherweise durch anlegen einer Roboterstrukturklasse, die die DH-Parameter enthält und eine Prüfung zulässt. Todo: Diagramm der Abhängigkeiten der Software. Modularität?

—Ingo Schalk-Schupp — alle Rechte vorbehalten