Künstliche neuronale Netze auf einem Mikrocontroller

Einleitung

Wo man auch hinschaut, alles spricht von künstlicher Intelligenz (KI, eng: AI). Jene die sich mit dem Thema etwas eingehender beschäftigen betitelten es eher als machine learning bzw. machinelles Lernen.

Einen kleinen Überblick gab es diese Woche auch auf 3sat, beim Wissenschaftsmagazin nano, das ich nur allen wärmsten empfehlen kann. Nicht unbedingt nur diese Folge, sondern allgemein ist diese Magazin sehr gut umgesetzt.

Die wirklich interessanten Einsatzgebiete sind jedoch zu uninteressant, um diese im TV einem allgemeinen Publikum aufzubereiten. Große Datenmengen analysieren, zuordnen oder durch Regression Vorhersagen treffen.

Beispiele:

  • Suchanfragen an den Benutzer anpassen: Wenn ich zuvor wieder und wieder etwas über das Programmieren gesucht habe, dann werde ich bei der Eingabe von „C Aufbau“ nicht die Note meinen, da ich anscheinend eher weniger musikalisch bin. Ich bin also an der Programmiersprache interessiert. Dazu müssen logischerweise möglichst viele Daten über mich gesammelt werden. Natürlich kann das Netzwerk was meine Daten aufbereitet auch andere Anfragen bearbeiten: Hobbys? Beruf? Interessen? -> Welche Werbung soll präsentieren werden? Insgesamt also eine Win-Win Situation?! Ein potenzieller Arbeitgeber der Zugriff auf diese Daten hätte, könnte meine C-Kenntnisse von der KI abfragen lassen. Dann lande ich im Ranking mit der Anfrage „C Aufbau“ auf den hinteren Plätzen. Wobei man mit „C function pointer callback“ in den vorderen Plätzen landet. Wer nicht glaubt wie einfach das ist, sollte mal folgendes Spiel probieren: http://20q.net/
  • Spracheingabe und Sprachausgabe: Siri und co. lassen grüßen und das in einer sehr guten Qualität. Zur Sprachausgabe werden bisher Laute aneinander gehängt, dass könnte sich auch bald ändern. Es gibt schon Netzwerke die direkt Binärdaten bearbeiten. Mit WaveNet lassen sich auch Emotionen in die Aussprache bringen. Leider ist der Rechenaufwand noch zu groß.
  • Bilderkennung: Ein gutes Beispiel von Klassifikation? Auch! PCs die Katzen- von Hundebilder unterscheiden können und bei den sozialen Netzwerken werden sofort die hochgeladenen Fotos mit den richtigen Freunden markiert. Jedoch kann der PC auch von den Bildern lernen und selbst tätig werden (von Kreativität möchte ich nicht sprechen). Ein bekanntes Beispiel ist googles DeepDream oder das Beispiel aus der Nano-Sendung.

Für die meisten Aufgaben könnte man die unterschiedlichsten machine learning -Verfahren verwenden. Durchgesetzt hat sich gerade bei sehr komplexen Aufgaben das künstliche neuronale Netzwerk. Dabei gibt es viele verschiedene Netzwerkstrukturen, die auch weiterhin Forschungsschwerpunkte bleiben werden. Welche Netzwerkstruktur kann welches Problem am besten lösen? Sind es die rückführenden Netzwerke, die Convolutional Neural Networks oder eine Kombination daraus die mein Aufgabenstellung lösen können? Vom eigentlichem Deep Learning wird immer dann gesprochen wenn das Netzwerk sehr viele Layer und Neuronen besitzt.

Was wohl immer zutrifft: Je komplexer die Aufgabe desto größer die nötige Rechenleistung oder desto cleverer der Entwurf des Netzwerkes.

Deep Learning auf dem Mikrocontroller?

Wenn man mit einem neuronalem Netzwerk soo viele Probleme lösen kann, wäre es doch praktisch diese Möglichkeit auf einem „kleinen“ CortexM Mikrocontroller ebenfalls verfügbar zu haben. Da Speicher und Prozessorleistung sehr begrenzt ist würde man wohl eher nicht vom Deep Learning sprechen. Für einfache „Multilayer-Feedforward-Netzwerke“ reicht es aber allemal.

Dafür habe ich die C-Bibliothek FANN (Fast Artificial Neural Network Library) auf den CortexM4 (mit FPU) portiert und verschiedene Benchmark-Untersuchungen unternommen. Diese werde ich evtl. später noch aufbereiten und auf dem Blog präsentieren.

Fazit war jedoch: Trainieren kann man ja auf dem PC und Ausführen auf dem Mikrocontroller, da alleine die Trainigdateien über der verfügbaren Flash-Größe liegen können. Dazu muss natürlich dem Mikrocontroller das angelernte Netzwerk übergeben werden. Das geschieht bei FANN über das Dateisystem, mit Hilfe von Konfigurationsdateien. Um keine Veränderungen an FANN selbst vornehmen zu müssen, habe ich dieses kleine Read-only Dateisystem entworfen.

Letztendlich habe ich mit der System Workbench FANN für den STM32F4 mit FPU (Hardware FPU Befehlssatz) cross-compiliert. Die entsprechende lib ist Bestandteil des Beispielprojektes und kann für weitere Versuche auf dem STM32F4 Discovery-Board oder anderen CortexM mit FPU verwendet werden. Später werde ich dem original FANN Github-Fork ein Static Lib – System Workbench Projekt hinzufügen.

Hello World or Blinky Neural Network Project

Ziel (sehr einfach): Ein einfaches Netzwerk wird trainiert, um eine der vier LEDs des Discovery-Boards nach der Trainingsphase blinken zu lassen. Der Benutzer kann sich durch zwei Buttons die LED aussuche. Aufbau KNN:

  • Vier Eingänge: der aktuelle Status jeder LED -> -1.0 = LED ist aus; +1.0 = LED ist an
  • Vier Ausgänge: soll LED an oder ausgeschalten sein -> -1.0 = LED aus; +1.0 = LED an
  • ein Hidden Layer mit 12 Neuronen (unnötig viel 😉 )

Ziel (einfach): Ein einfaches Netzwerk wird trainiert, um ein Blinkmuster auf den LEDs darzustellen. Dazu muss das Netzwerk auf die Übergänge zwischen den LEDs achten. Es darf keine LED doppelt aufleuchten, jedoch dürfen auch LEDs weggelassen werden. Der Benutzer kann sich durch zwei Buttons das Blinkmuster innerhalb der Spezifikation aussuche. Aufbau KNN:

  • Vier Eingänge: der aktuelle Status jeder LED -> -1.0 = LED ist aus; +1.0 = LED ist an
  • Vier weitere Eingänge: der vorhergehende Status jeder LED -> -1.0 = LED war aus; +1.0 = LED war an
  • Vier Ausgänge: soll LED an oder ausgeschalten sein -> -1.0 = LED aus; +1.0 = LED an
  • ein Hidden Layer mit 12 Neuronen (unnötig viel 😉 )

Es gibt unterschiedliche Arten, wie man ein neuronales Netzwerk trainieren kann. Mit Trainingsdaten wäre dieses „Hello World“ Projekt recht langweilig geworden. Ohne Trainingsdaten funktioniert es über das Reinforment Learning ganz ausgezeichnet. Dafür braucht es einen Agent. Dieser belohnt oder bestraft das Netzwerk, wenn die Aktion gerade nach Wunsch oder auch nicht stattfand. Den Agent könnte man sich programmieren, wenn die Möglichkeiten viel zu groß sind oder (wie in diesem Beispiel) man übernimmt  selbst die Aufgabe des Agents.

Da FANN keine Routinen für das bestärkende Lernen zur Verfügung gestellt, musste ich diese selbst implementieren. Ich habe mich für den Q-Learning Algorithmus entschieden und habe hier sehr gute Informationen und auch Implementierungsbeispiele erhalten:

http://outlace.com/Reinforcement-Learning-Part-3/

Einfach ausgedrückt funktioniert es so: Die Lernphase hat eine festgelegte Anzahl an Trainingszyklen. Am Anfang steht das untrainierte Netz. Es wird eine Zufallsaktion ausgeführt und auf die Bewertung gewartet. Die Bewertung ist schlecht, wenn der Fehler zwischen der erwarteten Aktion und der ausgeführten Aktion groß ist. Der Fehler wird durch einen Gradientenabstiegsverfahren (Backpropagation) Rückwärts durch das Netzwerk „geschickt“ und die Gewichtungen der Neuronenverbindungen werden angepasst. Nach und nach werden die Zufallsaktionen durch Vermutungen/Ausgaben die das Netzwerk trifft ersetzt. Bis am Ende fast ausschließlich Aktionen des Netzwerkes präsentiert werden.

Ein Problem besteht darin, innerhalb der Zufallsaktionen möglichst viele Möglichkeiten des Systems abzudecken, um das Netzwerk ausreichend trainieren zu können. Das Verhalten spiegelt sich auch in dem Hello World Projekt wieder: Nur eine LED blinken lassen benötigt nur ca. 10 Trainingszyklen, weil der Zufall hoffentlich jede LED einmal aufleuchten lassen hat. Für die Übergänge der LEDs gibt es viel mehr Möglichkeiten und daher werden auch mindestens 75 besser jedoch 100 Trainingszyklen gebraucht. Was natürlich für recht viel Arbeit sorgt und man muss selbst hochkonzentriert die Aktion bewerten. Daher ist mein Video auch weniger informationsreich geworden als geplant, weil ich mich stark auf die LED-Übergänge konzentrieren musste.

Genug der vielen Theorie, hier das Video des Projektes:

Das komplette Projekt gibt es wie immer auf meiner Github-Seite:

https://github.com/Counterfeiter/ANN-QLearning-CortexM4

Es ist darauf zu achten, dass FANN mit dynamischer Speicherreservierung arbeitet und das damit der Heap im Auge behalten werden sollte.

Gehen wir etwas in den Quellcode. Ein Netzwerk wird mit Standardwerten angelegt. Diese können aber durch einzelne FANN-Funktionen verändert werden. In diesem Beispiel wird die Aktivierungsfunktion aller Layer angepasst:

//create ann -> 1 hidden layer & (one input and one output layer)
 struct fann *ann = fann_create_standard(3, num_inputs, 12, num_outputs);

//use symmetric to deal with -1.0 and +1.0 -> normal for 0.0 to 1.0
 fann_set_activation_function_hidden(ann, FANN_SIGMOID_SYMMETRIC_STEPWISE);
 fann_set_activation_function_output(ann, FANN_SIGMOID_SYMMETRIC_STEPWISE);

Mit fann_run wird das Netzwerk ausgeführt. Dazu muss natürlich der Input-Vektor übergeben werden. Dieser muss selbstverständlich so viele Werte besitzen wie es Input-Neuronen im Netzwerk gibt. Die Rückgabe ist ein Pointer auf den Output-Vektor, der natürlich so lang ist wie es Neuronen am Ausgang gibt.

Da es beim Reinforcement Learning meist nur einen Datensatz gibt, wird dies effektiv mit fann_train angelernt. Da wir trainieren, müssen zu den Inputs auch die passenden Outputs übergeben werden. Q-Learning sorgt dabei dafür, dass im Ausgang der eingetretene Fehler „markiert“ wurde.

Wie man sieht, die FANN-lib nimmt erstaunlich viel Arbeit ab. Es sind gerade mal fünf verschiedene Funktionen angewendet wurden, um mit neuronalen Netzwerken arbeiten zu können. Klasse!

Wer sich selbst versuchen möchte, mein Vorschlag um die Funktionen weiter voranzutreiben: Es gibt durch die beschränkte Anzahl an Eingabevektoren keine Anhaltspunkt darauf wie oft eine LED vorher geblinkt hat. Das heißt, es ist nicht möglich das eine LED zwei mal aufleuchtet, bevor es zur nächsten LED übergeht. Die Lösung wäre für jede LED eine Zählvariable (normiert auf -1.0 bis 1.0) mit in den Input zu geben. Dies resultiert in ein beliebig komplexes Blinkmuster. Da die Möglichkeiten weiter steigen, müsste zielführend ein programmierter Agent das Netzwerk anleiten und über mehrere 100 bis 1000 Trainingszyklen führen. Die Anzahl der Hidden Layer und Neuronen wird, je nach Komplexität des gewünschten Blinkmusters, ansteigen müssen.

Ich wünsche happy coding mit neuronalen Netzen. Ich würde mich freuen, wenn ihr eure Ergebnisse in den Kommentaren hinterlasst.

 

 

Veröffentlicht unter Allgemein | 3 Kommentare

Kleines Dateisystem für Mikrocontroller

Beim portieren einer C-Bibliothek vom PC auf einen Mikrocontroller bin ich auf ein Problem gestoßen: Die Bibliothek arbeitet mit Konfigurationsdateien die zuvor geladen werden müssen. Das Umschreiben der Lib wäre sehr aufwändig gewesen und der Quellcode danach nicht mehr kompatibel, also nicht mehr wartbar, wenn eine neuere Version herauskommen.

Eine andere Lösung ist es die verwendeten Dateifunktionen (fopen, fscanf, fclose) zu implementieren und ein „virtuelles Dateisystem“ vorzugaukeln. Wobei doch irgendwie jedes Dateisystem virtuell und abstrahiert daher kommt…

Als erstes habe ich natürlich das Netz nach bereits umgesetzten Lösungen abgesucht und leider nichts gefunden (Tipps gern per Kommentar). Also musste ich mir was eigenes schreiben.

Verwendet man einen CortexM Mikrocontroller, kann man auf den Luxus einer bereits portierten newlib zurückgreifen und die POSIX-konformen Dateiaufrufe verebben in den sogenannten syscalls. Die syscalls können auch als Backend für die libc-Funktionen betrachten werden und die Funktion der syscalls werden von einem Betriebssystem auf entsprechende Ressourcen umgeleitet.

Die syscalls stellen z.B. einen bequemen Weg bereit über den printf-Aufruf Debugnachrichten auf ein UART-Interface umzuleiten. Dazu sollte die Datei syscalls.c mit der Minimal-Variante in das Projekt eingefügt werden (Linkereinstellungen der newlib beachten!). Anschließend werden in der write-Funktion die übergebenen Zeichen auf die gewünschte UART weitergeleitet.

Ganz ähnlich funktioniert die read-Funktion. Es wird nach einer bestimmten Anzahl von Zeichen gefragt. Diese sind aus dem übergebenen Datei-Diskriptor zu laden. Für stdin, stdout und stderr sind die Diskriptoren festgelegt und müssen nicht geöffnet werden, bevor gelesen oder geschrieben wird. Eine geöffnete Datei muss ebenfalls vom Betriebssystem einen Diskriptor zugewiesen bekommen, also einen unverwechselbaren File-Handle.

Nun ist es jedoch so, dass es kein Dateisystem auf dem Mikrocontroller gibt und damit auch keine Implementierung die auf ein fopen etwas anderes als ein -1, also Fehler beim Öffnen, zurückliefern kann. Dazu die Minimal-Implementierung der newlib syscalls:

int open(const char *name, int flags, int mode) {
    return -1;
}

Nun sind die nötigen Funktionen schon fast ausfindig gemacht. Weiterhin wichtig für ein Dateihandling, ist eine Funktion um in einer Datei den Lese-/Schreibpointer zu setzen: lseek. Diese wird nicht nur durch manuelles setzen des Pointers angesprungen (fseek), sondern auch durch den Aufruf von fscanf.

Der Ablauf zum Lesen einer Datei ist also folgendermaßen:

Wir rufen fopen auf und wollen eine Datei mit dem gegebenen Pfad/Dateinamen öffnen. Der Befehl landet in der open-Funktion der syscalls.c.

Implementierung open: Vergleichen des Dateinamens mit einer internen Dateiliste und beim Vorhandensein der Datei wird ein File-Pointer zurückgegeben (ungleich 0, 1 oder 2). Ein interner Zeiger wird auf den Anfang der Datei/Speicherstelle gesetzt.

Mit fscanf wird ein Parameter aus der Datei eingelesen.

Implementierung read: Vergleichen ob der übergebene File-Pointer einer geöffneten Datei angehört. Lesen der Datei/Speicherstelle ab der zuletzt bekannten Position.

Implementierung lseek: Vergleichen ob der übergebene File-Pointer einer geöffneten Datei angehört. Setzen des File-Pointers unter Berücksichtigung der Dateigrenzen.

Mit fclose wird die Datei wieder geschlossen.

Implementierung close: Vergleichen ob der übergebene File-Pointer einer geöffneten Datei angehört. Die virtuelle Datei wird als geschlossen markiert.

Mit fwrite in eine Datei schreiben.

Implementierung write: Soweit habe ich das kleine Dateisystem nicht getrieben. Da ein Flash-Page Write und Errase besonders Handling benötigt, würde diese Implementierung auch etwas aufwändiger ausfallen, als nur vom Flash zu lesen. Damit ist das Dateisystem bisher read-only!

Die Datei auf dem Mikrocontroller abzulegen ist übrigens recht einfach. Zum Beispiel auf dieser Seite kann eine Datei in ein const char array gewandelt werden. Dies wird dann in eine Header-Datei abgelegt und der Pointer auf das Array und die Länge der Datei wird in einer Struktur eingearbeitet. Diese Struktur repräsentiert das Dateisystem, im Grunde eine sehr simple File Allocation Table (FAT).

Das Hinzufügen einer neuen Datei erfolgt so:

  1. Datei in char-Array wandeln: http://fuxx.h1.ru/bin2c/index.en.php
  2. Datei/char-Array in einer Header-Datei speichern (im Github-Beispiel: my_config_files.h)
  3. Dateiname, -pointer und -größe in flash_fs.c Struktur eintragen und define mit der Anzahl an Dateien inkrementieren:
#define FILE_FS_NUMBER_OF_FILES 2
static const char *file_names[FILE_FS_NUMBER_OF_FILES] =
{
 "config.txt",
 "config_dbg.txt"
};

flash_file my_files[FILE_FS_NUMBER_OF_FILES] =
{
 {
 config_txt, sizeof(config_txt), -1, 0
 },
 {
 config_dbg_txt, sizeof(config_dbg_txt), -1, 0
 }
};

Anschließend kann die Datei im Programm, ähnlich wie von einem PC gewohnt, verarbeitet werden:

FILE *cfg = fopen(„config.txt“, „r“);
int reading;
fscanf(cfg,“PWMDutyCycle=%d\n“, &reading);
fclose(cfg);

Das komplette Demo-Projekt für das STM32F4 Discovery-Board gibt es auf meinem Github-Account:

https://github.com/Counterfeiter/CortexM-read-only-FileSystem

Limitierungen und mögliche Drawbacks des aktuellen Dateisystems:

  • kein binäres Dateihandling möglich
  • kein Schreiben von Dateien möglich
  • die newlib reserviert dynamischen Speicher im Hintergrund

Vorteile:

  • benötigt nur sehr wenig Flash/RAM (Demoprojekt: 14 KByte Flash)
  • wird das Dateisystem nicht benötigt, wird in der syscalls.c die Headerdatei (flash_fs.h) einfach nicht inkludiert
  • stdin/stdout/stderr können weiterhin parallel verwendet werden (Debug printf)
  • Bibliotheken die für den PC geschrieben wurden und auf Dateien lesen möchten können einfacher für den Mikrocontroller portiert werden.

Wer möchte kann das Dateisystem gern weiter optimieren. Meine Ideen wären:

  • Schreiben von Dateien als RAM-Kopie oder direkt in den Flash
  • Erstellen eines PC-Programms, um alle Dateien eines Ordners schnell in eine Header-Datei zu überführen und die nötige Struktur automatisch zu erstellen -> Einbindung in den Build-Prozess
  • Dateien als verkette Liste ablegen -> ersten 4 Byte nicht 0, dann ist es Pointer auf nächste Datei

Tipp: Wer mit der newlib Fließkommazahlen lesen und schreiben will, muss folgende Linker-Flags setzen:

-u _printf_float -u _scanf_float
Veröffentlicht unter Allgemein | 1 Kommentar

Experimente mit Ferrofluid

Ich bin es gewohnt, dass jedes meiner Projekte irgendwie seine gewünschte Funktion erfüllt (irgendwann). Zum einen liegt es wohl daran, dass ich nicht einfach darauf los bastle, sondern schon etwas länger ein Konzept ausarbeite, aber auch viel simuliere, bevor ich die ersten Bauteile bestelle. Da ich auch beruflich als Elektronikentwickler arbeite, ist das natürlich das übliche Vorgehen im Ingenieurbüro.

Dabei ist gegen das reine Basteln natürlich auch nichts einzuwenden. Also einfach darauf los bauen und wenn etwas nicht geht, einen Workaround finden, damit es funktioniert. Basteln ist also die kreativere Arbeit bei der man noch viel lernen kann und wenn in beispielsweise 5 % der Fälle ein besserer Workaround entsteht, als es ein gutes vorgedachtes Konzept abdeckt, ist es um so erstaunlicher. Für Private ist also eine gesunde Mischung aus „darauf los basteln“ und kleine Konzepte entwickeln am besten geeignet. Beruflich kann man sich es nicht leisten eine Entwicklung in die falsche Richtung zu lenken. Hier sind 30 % der Zeit für eine Definitions- und Konzeptphase Pflicht. Wer mehr über meinen Arbeitsalltag wissen möchte, kann ja einen Kommentar da lassen- evtl. kann ich dann in diese Richtung was bloggen.

Zum Thema! Dieses Projekt ist nicht so perfekt verlaufen, wie ich es mir vorgenommen habe.

Ferrofluid ist eine Flüssigkeit die größtenteils aus Öl und Nano-Ferromagnetischen-Partikeln besteht. Ob Nano hier nur ein Werbeargument ist und es einfach nur feiner Eisenstaub ist, kann ich nicht beurteilen. Es gibt aber definitiv sehr unterschiedliche Flüssigkeiten mit verschiedenen Eigenschaften. Angeregt durch ein paar schöne Ferrofluid-Videos musste ich das auch haben!

Beispiele:

Mein Gedanke dazu: „Kann ja nicht so schwer sein mit einer Spule/Spulenmatrix ein Magnetfeld zu erzeugen.“

Ferrorfluid kostet aber leider ein kleines Vermögen und wie viel braucht man davon überhaupt? Der kleine „Shot“ (20 ml) kostet schon 18 €. Reicht das denn für mein Vorhaben?

Mein Vorhaben war es ein aus Stahl gelasertes Herz mit zwei innenliegenden Buchstaben von Ferrofluid interaktiv umfließen zu lassen. Es sollte ein Hochzeitsgeschenk für meine Schwester und ihren Freund werden.

Da Ferrofluid so aggressive Flecken wie Rost hinterlässt und auch von der Haut nicht so leicht abwaschbar ist, habe ich ein kleines Aquarium bei Ebay-Kleinanzeigen erstanden. Es stellte sich aber schnell heraus, dass für die erste Version ein deutlich kleinere transportablere Variante gebaut werden muss. Nun habe ich im Supermarkt ein rechteckiges Glas gesucht, dass damit bessere optische Eigenschaften besitzt als ein rundes. Es war gar nicht so leicht ein solches Glas zu finden. Zum Schluss gab es eins mit Himbeermarmelade. Lecker!

Das Beseitigen der Etiketten vom Glas war allerdings sehr aufwändig, weil ich vorher nicht gegoogelt hatte. Ich kannte zwei Möglichkeiten: Mit einem Föhn den Aufkleber erwärmen und dann in einem Stück abziehen. Das ging leider nicht. Nun habe ich mit Lösungsmittel wie Aceton gekämpft. Es wollte einfach nicht sauber abgehen. Bis ich mich informiert habe und es je nach Klebstoffart noch eine weitere Möglichkeit gab. Öl! Ich wollte es zwar nicht so recht glauben, aber als ich den Aufkleber mit Sonnenblumenöl und etwas Druck abreiben konnte, war ich baff.

IMG_2785

Mit Speiseöl konnte ich den Aufkleber vom Glas sauber entfernen.

In Vorbereitung auf die doch größere Investitionssumme habe ich mir das kostenlose Programm FEMM 4.2 besorgt und meine ersten Experimente damit unternommen. Das Programm lässt sich nicht sehr intuitive bedienen und kann leider nur zweidimensionale Finite-Elemente-Probleme simulieren. Mit zwei YouTube-Videos zur Bedienung habe ich dann den Einstieg doch recht schnell hinbekommen.

Mit Tinkercad, ein online 3D-Tool mit eingeschränkten Funktionsumfang, konnte ich eine Laserschablone im *.dxf Format erstellen und diese auch direkt als Vorlage ins FEMM importieren. Die Simulation zeigte, dass das Ferrofluid Stacheln am Herzrand erzeugen sollte. Das Simulationsergebnis der Magnetfeldlinien habe ich direkt in das Design der „Plattform“ mit einfließen lassen:

IMG_2807

Der Elektromagnet unter dem Metallherz ist eine Weichenspule von Reichelt, die möglichst viele Windungen, also Induktivität, haben sollte. Ein starker Draht sorgt für eine geringe Verlustleistung. Bei welchem Strom die Spule in die Sättigung geht, weiß ich leider nicht. Darüber gibt es keine Angaben durch den Hersteller.

Der Eisenkern der Spule bündelt das Magnetfeld und „schickt“ es direkt in das darüber stehende Metall, sagt die Simulation. Meine zweite Option war es, den Eisenkern der gekauften Spule herauszuschlagen. Das habe ich jedoch bis heute nicht probiert.

Es soll ja interaktiv werden. Also brauchte ich einen einstellbaren Elektromagneten. Die Stärke eines Elektromagneten stellt man über den Strom ein. Lösung: eine stellbare Stromquelle. Der Sollwert wird in meiner Version durch einen Entfernungsmesser von Sharp vorgegeben. Da der Sollwert bereits analog vorliegt, wollte ich die Schaltung zeitsparend ohne Mikrocontroller aufbauen. Wieder habe ich simuliert, diesmal eine geeignete Schaltung mit LTSpice. Herausgekommen ist folgende reine OPV-Schaltung:

OPV_Schaltung

Ein OPV in Schmitt-Trigger-Schaltung steuert mit einem P-Kanal-MOSFET die Induktivität L1 über eine 12 V Versorgungsspannung an. Das langsame Sinussignal V2 simuliert dabei das wechselnde Ausgangssignal des Entfernungsmessers und R8 ist der Messshunt der die Stromstärke rückkoppelt. Die Widerstandswerte müssen nur so angepasst werden, dass sich der gewünschte Strom an der Spule einstellt. Mit der gezeigten Widerstandskonfiguration ist ein maximaler Strom von 1,5 A mit dem Sharp-Sensor möglich. Einen NTC-Widerstand mit einzubinden, der den Strom reduziert, wenn die Schaltung sich zu stark erwärmt, ist möglich, wurde aber von mir wieder verworfen. An die Grenzbereiche des machbaren Stromes bin ich nicht gegangen, die Spule erwärmt sich allerdings schon deutlich auf ca. 45 °C. Was jedoch nicht kritisch ist.

Leider wird mit dieser Schaltung auch der P-MOS recht schnell warm, da er vom OPV nicht so sauber getrieben wird, wie ich es erhofft hatte. Eine weitere Alternative ist eine Stromquelle mit dem MC34063 aufzubauen. Dabei gibt es keine Ausgangslast (Kurzschluss), da nur der Strom durch die Spule interessant ist. Das Problem des MC34063 ist, dass die Sollwertvorgabe über eine interne Referenz von 1,25 V erfolgt. Zum Glück gibt es eine Version des Schaltreglers (z.B. LM78S40) wo alle Komponenten einzeln zugänglich sind und diese selbstständig verbunden werden müssen. Statt der Referenzspannung wird einfach die heruntergeteilte Spannung der Lichtschranke am Comperator angeschlossen und fertig.

Was mir noch fehlte war also das Ferrofluid. Einer der wenigen Hersteller ist die US-Firma FerroTec, die auch eine deutsche Niederlassung besitzt. Warum also die teuren Umwege über Zwischenhändler? Direkt beim Hersteller ordern ist doch viel billiger! Ein kurzes Telefonat ergab: Mindestabnahmemenge 1 Liter. Zum Preis von 230 € brutto. Das ist zwar im Vergleich zu den üblichen Mindermengen ein guter Preis, aber viel zu viel Fluid. Also mein Vorhaben und die Sache mit der Hochzeit erklärt. Die freundlichen Mitarbeiter haben dann eine Ausnahme gemacht, da es noch eine 0,6 Liter Flasche im Lager gab. Diese habe ich in der Erwartung gekauft, dass ich doch noch so viel damit vor habe…

Die ersten Versuche mit dem Fluid waren allerdings schon sehr ernüchternd. Der Elektromagnet hatte eine sehr schwache Wirkung auf das Fluid. Um das Fluid etwas in der Bewegung zu unterstützen habe ich das Glas mit Wasser aufgefüllt. Schon etwas besser! Ein weiteres Problem war, die Scheibe des Glases war sofort vom Ferrofluid dreckig und man konnte kaum noch durchschauen. Die Lösung war etwas Spülmittel im Wasser. Spülmittel löst bekanntlich Fette, das Fluid besteht aus Öl. Das Ergebnis ist eine sauberes Glas, jedoch ein Fluid das eher dazu neigt kleine Tropfen zu bilden, statt im ganzen zu agieren.

Wie eingangs erwähnt, ist es kein kompletter FAIL gewesen, jedoch ist es das Projekt mit dem schlechtesten Preis-/Leistungsverhältnis das ich jemals durchgeführt habe.

Im Vergleich zu den schönen Videos oben, ist meine Variante eher ein Tropfen auf den heißen Stein:

Hier noch ein paar Bilder vom Aufbau:

 

Eine weitere bessere Möglichkeit ist übrigens starke Dauermagneten zu verwenden. Diese können auch interaktiv verwendet werden, wenn sie mit starken Servomotoren bewegt werden. Mein Interesse ist aber erst einmal gesättigt. 😉

Wie aus dem Beitrag hervor geht, sitze ich noch auf einem riesigem „Topf“ Ferrofluid. Wer mindestens 100 ml abnehmen möchte, darf mich gern anschreiben. Ich kann einen guten Preis machen!

Veröffentlicht unter Allgemein | 11 Kommentare

WeddingBot / Nespresso® Bot

Wir heiraten heute am 01.07.2016 und als Gastgeschenke für unsere Gäste soll es etwas ganz Persönliches geben. Da meine (zukünftige) Frau Mandy gern mit Papier und Schere bastelt und ich natürlich gern an Elektronik, haben wir beides kombiniert.

Herausgekommen ist eine kleiner Roboter in schicker Verpackung für Hochzeiten oder andere Festlichkeiten, der „singen“ und mit den Augen funkeln kann. Außerdem geht er schlafen, wenn es dunkel wird. Das der Bot, wenn man ihn mit seinem Besteck oder etwas anderem leitfähigem aktiviert, schon so für Stimmung sorgt, ist durch die unterschiedlichen Töne und ganzen Lieder sichergestellt. Wir hätten hier klassisch „Für Elise“ oder passend zum Thema „Mendelssohn – Wedding March“. Zwischen drin kommt aber auch mal die „Batman Theme“ und andere kurze Sweeps oder Fanfahren, damit es nicht zu „steif“ auf der Feier zugeht.

Damit die Party in Schwung kommt, sollten die Gäste am besten mit Gruppenspielen beschäftigt werden, aber mögen das Alle? Für unseren ganz persönlichen Gruß an unsere lieben Hochzeitsgäste hat der Wedding Bot auch noch eine weitere Funktion. In zufälligen Intervallen wird „unsichtbar“ über die LED-Augen eine ID ausgegeben. Diese kann von einer Basisstation = Raspberry Pi mit Lichtsensor interpretiert werden. Ein kleines Python-Programm stellt passend zur ID ein Bild auf einem Monitor dar. Der Kreativität des Hochzeitspärchens sind nun keine Grenzen gesetzt.

Wir haben uns für folgende Variante entschieden: Kein Hochzeitsgast bekommt den richtigen Bot und jeder Bot sollte seinen Besitzer finden, indem die Gäste zur Basisstation gehen und sich ein Bild des Besitzers ansehen. Danach muss dieser natürlich aufgesucht werden und der Bot getauscht. Die Bilder können witzig oder förmlich sein, alt oder neu. Das kann alles individuell entschieden werden. Man könnte durchaus auch Rätselfragen präsentieren, wenn man das möchte.

Damit überhaupt Interesse an einem Tausch entsteht, müssen alle (nennen wir sie an der Stelle wieder) NespressoBots auch unterschiedlich aussehen. Also personalisiert sein. Dazu hatten wir die Idee, als günstiges und praktisches Gehäuse, was zudem noch super schick aussehen kann, eine leere Nespresso-Kapsel zu nehmen. Dazu haben wir diese einfach nach dem trinken vom Kaffee befreit. Zusätzlich habe ich noch alle Bots eine individuelle handgemalte Frisur verpasst. Tada: ein Unikat.

Weiter oben steht wie der Bot angeht. Wie geht er eigentlich aus? 60 Gäste mit trötendem Bot kann auf die Dauer auch nerven ;). Der Bot macht es eigentlich wie der Mensch, wenn es dunkel wird schläft er nach ca. 2 bis 3 Minuten (alles zufallabhängig) wieder ein. Also einfach zurück in die Schachtel packen. Ob man das den Gästen sagt oder ob die es lieber selbst herausfinden sollen, da sind wir uns auch noch uneinig. 😀

Wenn man über 60 Gastgeschenke baut, dann sucht man schon nach Möglichkeiten das Budget und vor allem den Aufwand möglichst klein zu halten. Das heißt: wenig Bauteile, einfach zusammen zubringen.

Nach einigen Optimierungsschritten besteht die Minimalbeschaltung aus nur acht Elektronikbauteilen und der Kapsel und PCB:

  • zwei LEDs
  • drei gleiche Vorwiderstände
  • Mikrocontroller ATTiny45
  • Batteriehalter
  • Piezo-Speaker
  • Nespresso-Kapsel, Platine

Hier der Aufbau für die Hochzeit, mit noch deutlich mehr Bauteilen (+ 3 x NPN Transistoren).

Hier noch der Aufbau der Basisstation:

Der Monitor wird natürlich noch etwas festlicher dekoriert! Das Zuhause des Bots in passenden Mottofarben rundet dann das gesamte Design ab.

IMG_3543

Im laufe der Vorbereitungen gab es noch weitere Optimierungsschritte und man kann auch weiterhin Dinge verbessern. Die Iterationen sind auch bei diesem kleinen Projekt nicht zu unterschätzen. In der neusten Variante sind durch Einsatz einer kleineren und leider teureren Batterie alle Komponenten auf der Rückseite. Da man die PCB so gut bemalen oder mit kleinen Stickern bekleben kann, habe ich das Design sehr allgemein gehalten. Mit unterschiedlichen LED-Farben habe ich ebenfalls experimentiert.

nespresso

Veröffentlicht unter Allgemein, WeddingBot | 6 Kommentare

ATTiny, ATMega UART Bootloader

Für ein kleines Projekt war in der Startphase ein Bootloader ganz praktisch. Unhandlich ist es mit Atmel ISP bzw. SPI Header auf der Platine einen Mikrocontroller zu programmieren. ISP braucht einfach zu viele Signale (6) -> VCC, GND, Clock, Reset, MISO, MOSI.

Die Lösung war der Bootloader von Peter Dannegger. Der Bootloader ist klein, schnell und unterstützt sogar den OneWire-Betrieb. Leider gibt es auf dem Mirkocontroller.net Forum ein ziemlich Wildwuchs an unterschiedlichsten Versionen, Dokumentation und auch PC-Tools, sowohl für Windows als auch Linux.

Trotz der zahlreichen Wiki-Seiten hat es mich Stunden gekostet hier durchzusteigen und die Versionen und Hardwareaufbauten zu finden die zusammen passen. Hier die Wiki-Seiten, sollte ihr diese nach meiner Kurzanleitung noch benötigen:

http://www.mikrocontroller.net/articles/AVR_Bootloader_FastBoot_von_Peter_Dannegger

http://www.mikrocontroller.net/articles/AVR_Bootloader_FastBoot_von_Peter_Dannegger/Tutorial_ATtiny13

Dazu gibt es noch einige seeeehr lange Forumsbeiträge, die man sich ergooglen kann.

Ich möchte hier nur auf den OneWire-Betrieb mit Windows eingehen und es kurz und schmerzlos ohne viel Prosa machen:

  • das meiner Meinung nach einzige funktionierende Windowstool für OneWire ist der UpdateLoader: https://luani.de/projekte/updateloader/
    • Password sollte standard „Peda“ sein -> Achtung keine beliebige Passwortauswahl möglich, da es mit dem Autobauding zusammenhängt
    • Bei „Erweitert“ natürlich OneWire aktivieren
  • Bootloader richtig konfigurieren und kompilieren
    • nur die Datei BOOTLOAD.ASM bearbeiten
    • passende include zum Prozessor einkommentieren
    • TX und RX PIN muss für OneWire gleich angegeben werden
    • der Pin ist frei wählbar, da Software-UART
    • der Bootloader wartet nach dem Prozessorstart kurz und wenn keine Programmieranfrage kommt, wird das Hauptprogramm geladen. Wollt ihr die Wartezeit beeinflussen (kein Muss) oder habt einen stark abweichenden Clock zu 8 MHz, dann in der FASTLOAD.H -> BootDelay bearbeiten
  • Programmierhardware
    • Schaltung für RS232-Wandler (+/- 12 bis 3 Volt Pegel): http://www.mikrocontroller.net/attachment/24979/bootloader.pdf
    • wer einen USB/UART Wandler wie ich benutzen möchte, muss zwingend die RX und TX Signale invertieren. Bei einem FTDI kann dies ganz einfach mit dem Windows-Konfigtool des FTDIs erledigt werden. Bei einem billigem Wandler wie ich ihn habe, musste ein 74HTC04 Inverter zwischen USB/UART und Mikrocontroller gebracht werden.
    • Die weitere Beschaltung habe ich im Mikrocontroller-Forum leider nirgends gefunden, daher meine Version:
    • onewire_fboot
    • mindestens 19,2 kbaud sollten mit dieser „langsam“ wirkenden Schaltung möglich sein

Für die Programmierhardware habe ich mir noch etwas besonderes einfallen lassen. Einfach am Rand der PCB zwei 2 mm große Testpunkte einfügen und diese mit GND und Programmierpin verbinden. Nun habe ich eine Klammer in der Mitte aufgesägt und zwei schräg abgeschnittene 1,5 mm² Kupferader befestigt. Durch die Halbierung der Klammer, können die Pins getrennt voneinander federn.

Im Bild habe ich eine Beispiel-PCB verwendet. Die Pins sind natürlich von einem USB-Connector.

Insgesamt ist es keine schwierige Sache mit dem Bootloader, nur die Informationen sind sehr verstreut.

Veröffentlicht unter Allgemein | 2 Kommentare

srecord und Firmware-Bin’s

Zuletzt hatte ich folgende Aufgabenstellung zu bewältigen:

Eine Firmware sollte eine fortlaufende ansteigende Variable pro geflashten Mikrocontroller enthalten. Also eine sogenannte UID. Es durfte aber nicht irgendeine sein, sondern diese musste zwingend bei 0 beginnen und ca. 100 PCBs würden mir reichen. Irgendwelche internen UIDs des Mikrocontrollers konnte ich demnach nicht verwenden.

Es gibt dafür einen Guten und ein paar weniger gute Ansätze.

Jetzt könnte man meinen für 100 PCBs könnte ich die Variable auch manuell erhöhen und immer wieder neu kompilieren. Problematisch wird es nur, wenn eine neue Firmwareversion erstellt werden muss, weil in der Alten Bugs enthalten waren oder weitere Optimierungen hinzugekommen sind. Dann geht das Spielchen wieder von vorn los.

Also lieber gleich den richtigen Weg gehen. Mit dem Tool srec für Windows und Unix kann eine (nahezu) beliebige binäre Firmwaredatei verändert werden. Das gebräuchlichste Format ist wohl Intel-Hex. Da ich srec am Beispiel von Atmel Studio zeigen möchte, ist dies unser Zielformat. Es wird aber ebenso bei ARM GCC und allen Arduino’s eingesetzt.

Vorbereitungen im Atmel Studio

Um eine Variable gezielt in der Binärdatei zu finden und zu verändern, sollte diese natürlich immer an der selben Speicherstelle stehen. Da Compiler und Linker das ohne Vorgaben für sich entscheiden, sollte dies natürlich angepasst werden.

Die Variable muss beim gcc mit dem attribute section deklariert werden. Die Sektion kann frei gewählt werden. z.B.

const uint8_t my_id __attribute__ ((section (".my_segment"))) = 0xFA;

Ich habe als Wert absichtlich 0xFA gewählt, so ist der Wiedererkennungswert höher als mit 0x00 oder 0xFF.

Nachdem die Variable im Quellcode deklariert ist, muss dem Linker gesagt werden, welche Sektion adressiert werden soll. Da man sich beim Atmel Studio nicht mit einem Linkerscript herumschlagen muss, gibt es unter Properties-> Toolchain -> AVR/GNU Linker -> Memory Settings die passenden Optionen. Bei „FLASH segment“ kann ein neuer Eintrag vorgenommen werden. Für einen ATTINY startet der Flash bei 0x0 und wird Wortweise adressiert. Die Variable an die letzte verfügbare Flashadresse zu legen, macht am meisten Sinn. Bei einem 4 KB ATTINY45 sieht die Berechnung wie folgt aus:

4 KB = 0xFFF -> Wortadressiert -> 0xFFF / 2 = 0x7FF

Wir fügen also folgenden Eintrag hinzu:

.my_segment=0x0007FF

Das Projekt kann jetzt kompiliert und gelinkt werden.

srec_cat anwenden

Download und ausführlichste Informationen gibt es hier: http://srecord.sourceforge.net/

Trotz der ausführlich scheinenden Doku ist das Tool so umfangreich, dass ich nach einigen Funktionen lange suchen musst und mich dann mehr oder weniger mit Try-und Error durchgeschlagen habe.

Als erstes wäre es schön zu sehen, ob ein 0xFA an der erhofften Speicherstelle liegt. Sollte man dies nicht direkt aus dem Intel-Hex ablesen wollen, kann auch eine Binärdatei aus der Intel-Hex-Datei ableitet und sich im Hexeditor ansehen werden.

srec_cat firmware.hex -intel -o firmware_binary.bin -binary

Weiterhin kann die Größe der erzeugten Datei als weitere Verifikation benutzt werden. Da an die letzte Flash-Adresse geschrieben wurde, bei einem 4 KB Flash, muss die Datei nun genau 4 KB – 1 Byte groß sein, da nur 8 Bit der 16 Bit großen Speicherzelle verwendet wurden! Der Aufruf ist an dieser Stelle noch recht simpel. Die einzulesende Datei wird als Intel-hex bekannt gemacht und die Ausgabedatei wird binär erwünscht.

Nun soll das 0xFA überschrieben werden. Dazu wird der Parameter -generate verwendet. Dieser gibt erstellt mit -constan-l-e eine gewünschte Konstante an der richtigen Flash-Adresse. Achtung: Die Adresse wird nun wieder Byte-Adressiert eingegeben.

Wir versuchen einen Trockenlauf:

srec_cat -generate 0x0FFE 0x0FFF -constan-l-e 7 1 firmware.hex -intel -o output.hex -intel

Der Befehl schlägt fehl, weil die Adresse an der die 1 Byte große Variable „7“ geschrieben werden soll schon belegt ist.

srec_cat: firmware.hex: 184: multiple 0x00000FFE values (previous =
 0x07, this one = 0xFA)

Wird 0xFA angezeigt, sollte die richtige Speicherstelle gefunden sein. Das Überschreiben des Wertes gelingt mit dem Parameter -exclude:

srec_cat -generate 0x0FFE 0x0FFF -constan-l-e 7 1 firmware.hex -intel -exclude 0x0FFE 0x0FFF -o output.hex -intel -line-length=44 -address-length=2

Um die Erklärung etwas abzukürzen, habe ich direkt zwei weitere Parameter angehangen. Diese bewirken, dass der Adressraum nur mit 16 Bit angegeben wird und das auf eine Intel-hex Zeile nur eine begrenzte Anzahl an Zeichen eingefügt werden darf. In diesem Fall also 44 Zeichen pro Zeile. Einige Bootloader sind recht einfach geschrieben und benötigen (zu recht) keinen komplexen Parser. Sollte der Bootloader eurer Wahl also Probleme mit der genierten Datei bekommen, solltet ihr die Darstellung der Eingangs- und Ausgangsdatei vergleichen und evlt. die letzten Parameter anpassen.

Um einen hohen Automatisierungsgrad zu erreichen, habe ich noch eine Windows-Batchdatei erstellt, um mir 100 Hexdateien mit aufsteigender UID zu genieren. Einmal ausgeführt und schon sind 100 Dateien im Ausgabeordner:

for /l %%x in (0, 1, 100) do (
srec_cat -generate 0x0FFE 0x0FFF -constan-l-e %%x 1 Debug\firmware.hex -intel -exclude 0x0FFE 0x0FFF -o bins\output%%x.hex -intel -line-length=44 -address-length=2
)

 

 

Veröffentlicht unter Allgemein | Kommentar hinterlassen

mit Biss

Zu Dingen, die einem viel zu selten in die Hände fallen, gehört eindeutig der eigene Gebissabdruck als Negativ und in Gips.

Diese Woche beim Zahnarzt habe ich von meinem Unterkiefer gleich zwei Abdrücke geschenkt bekommen. Okay, die passen jetzt nicht wirklich perfekt aufeinander, aber man kann damit schon ein Gebiss formen.

Zutaten:

  • IR-Näherungssensor von Sharp
  • kleiner Servomotor
  • Tiva-C Launchpad mit Energia Sketch
  • Trinkhalm, Hölzer,  Zahnstocher, Pappkarton, Geschenkpapier und viel Heißkleber
  • und natürlich zwei Gebissabdrücke

Aufbau:

Wirklich schwierig ist es irgendetwas an den Gipsabdrücken zu befestigen. Wenn ihr keinen Zwei-Komponenten-Harz zur Hand habt, wie ich, dann nehmt vieeelll Heißkleber.

Eine Holzkonstruktion dient als Halterung des Gesamtkunstwerkes, daran wird der Servo und der Näherungssensor befestigt. Der Näherungssensor muss gut ausgerichtet werden, so dass er auch mit halb geschlossenem Gebiss noch gut nach draußen „sehen“ kann. Die Trinkhalme habe ich als Scharnier verwendet, vielleicht fällt euch auch noch was besseres ein. Der Faden wird am Oberkiefer befestigt und durch den Servo geführt.

Eine Pappschachtel soll das Arduino / Energia Board beherbergen. Ich habe leider nur das große Tiva-C da gehabt. Der Karton sollte wenigstens ein bisschen nach was aussehen, daher habe ich es in Geschenkpapier eingepackt.

Der Sketch ist so primitiv, der kommt nicht auf meinen Github-Account:

#include <Servo.h> 
 
Servo myservo; // create servo object to control a servo 
int pos = 0; // variable to store the servo position 
 
void setup() 
{ 
 myservo.attach(63); // attaches the servo on pin 9 to the servo object 
 Serial.begin(9600); // setup serial
}

#define MIN_POS 20
#define MAX_POS 120

void loop() 
{ 
 int analog = analogRead(23);
 Serial.println(analog);
 if(analog > 2000 && analog < 2600) {
 analog -= 2000;
 analog /= 30;
 analog += MIN_POS;
 myservo.write(analog); 
 delay(10); 
 
 } else if(analog >= 2600) {
 myservo.write(120);
 delay(2000); 
 for(pos = MAX_POS; pos >= MIN_POS; pos--) 
 { 
 myservo.write(pos); 
 delay(5); 
 } 
 }
}

Elektrisch ist nicht viel zu tun. Sowohl der Servo als auch der Näherungssensor werden mit 5 Volt versorgt. Servo an möglichen Servo-Ausgang und Näherungssensor an einen ADC-Eingang. Fertig!

Wenn der mechanische Aufbau nicht seine Tücken gehabt hätte, wäre alles in 30 Minuten fertig gewesen. So hat es doch zwei Stunden bis zum gewünschten Ergebnis gedauert.

Hier nun das Ergebnis:

 

Veröffentlicht unter Allgemein | Kommentar hinterlassen