Bastelei: Das hölzerne Biest

In der ersten Ausgabe des Jahres 2016 veröffentlichte der deutsche Ableger des Make-Magazins eine Bastelanleitung für ein Miniatur-Strandbeest aus lasergecutteten MDF-Platten. Klingt interessant, hat mich aber nicht sofort mitgerissen. Bei der Suche nach einem nachwuchsgerechten Urlaubsprojekt kam mir der Holzkrabbler dann plötzlich wieder in den Sinn und auch besagter Nachwuchs äußerte Interesse am Projekt - zumal ein Exemplar bereits ausgiebig in Hannover bestaunt werden konnte. „Einfach nur nachbauen“ wäre natürlich etwas zu langweilig gewesen. Die Themen Antrieb, Ansteuerung und Stromversorgung boten jedoch reichlich Raum für eigene Forschungen und Ideen. Also…

... Vorhang auf für DAS STRANDBEEST!

Das Skelett

Die in der Make veröffentlichte Bauanleitung wurde von Joachim Haas erdacht und umgesetzt. Er ist sowohl Autor des Make-Artikels als auch der Betreiber des Blaue-Stunde-Blogs, wo er die für den Lasercutter notwendigen „Strandbeest-Files“ zum Download bereitstellt. Wer (wie ich) keinen Lasercutter sein Eigen nennt, kann eventuell einen im nächstgelegenen FabLab nutzen. Ich habe die Holzteile bei Formulor, einem Lasercutter-Dienstleister, ausschneiden lassen.

Von dessen Webseite habe ich mir zunächst Vorlagen im SVG-Vormat heruntergeladen und in diese dann mittels Inkscape die Einzelteile aus Joachims Vorlagedateien hineinkopieren und platzsparend angeordnet. Danach folgte eine fummelige Fleißarbeit, denn alle Linienstärken und -farben müssen angepasst werden: bei Formulor werden blaue Linien geschnitten und rote Linien werden graviert. Weiterhin sind Vorgaben zur Linienstärke einzuhalten. Beim Upload wird die Datei auch noch mal auf Fehler geprüft. Erst wenn alles stimmt, darf man bestellen. Und nach anderthalb Wochen wird eine MDF-Platte wie diese geliefert:

Das Strandbeest in 2D


Beim ersten Auspacken war ich wirklich fasziniert! Die braunen Umrisse der Bauteile auf der Platte sehen auf den ersten Blick wie Linien aus, es sind aber in Wirklichkeit extrem schmale Schnitte durch das Material. Leider blieb mir eine zweite Bestellung nicht erspart, aber dazu komme ich später.

Für den Zusammenhalt sorgen viele Schrauben und Muttern. Die ursprüngliche Stückliste aus dem Make-Artikel ist nicht mehr ganz aktuell, da andere Motoren zum Einsatz kommen. Für deren Befestigung benötigt man zusätzlich:

  • 4 Schrauben M3 x 30
  • 4 Muttern M3
  • 4 Muttern M4
  • 2 Unterlegscheiben 4,3 mm Lochdurchmesser (z. B. DIN 9021 Scheiben, Außendurchmesser = 3 x Lochdurchmesser)

Ich habe mir einen Schraubenversender gesucht, der so ziemlich alle Artikel auf der Liste im Werkstoff „Stahl, verzinkt“ liefern konnte. Als Schrauben habe ich Linsenkopf-Schrauben mit Torx-Antrieb gewählt. Alternativ könnte man zu Sechskantschrauben greifen, weil diese im eingebauten Zustand einfacher (mit Maulschlüssel) zugänglich sind.

Der Antrieb

Joachim hat nach dem Erscheinen des Make-Artikels eine ziemlich coole Verbesserung ausgetüftelt: Es können nun Getriebemotoren verbaut werden, die viel mehr Kraft haben, als die ursprünglich vorgesehenen modifizierten Servomotoren. Der beeindruckende Unterschied kann z. B. in diesem Video bestaunt werden. Nicht zuletzt erspart man sich den Umbau der Servos…

Die Motorachse des Getriebemotors ist sehr kurz und nicht kreisrund. Die Verbindung zur Welle kann deshalb nur per Formschluss mittels einer Scheibe erfolgen, die ein ebenso geformtes Loch in der Mitte aufweist und auf die stummelig-kurze Welle gesteckt wird. Bei meinen ersten Krabbelversuchen waren einige Gelenke noch etwas zu schwergängig eingestellt und genau diese Koppelscheibe stellte sich als Schwachstelle heraus. Anstatt einfach zu blockieren, drehte der Motor munter weiter und bohrte das Loch in der Koppelscheibe zu einem schönen Kreis. Also musste ich Formulor erneut bemühen und habe mir einen Satz Scheiben in Acrylglas anfertigen lassen.

Kraftübertragung am Strandbeest: 1 Motor, 2 Koppelscheibe (Acrylglas), 3 & 4 Distanzringe, 5 Einschlagmutter, 6 Mutternscheibe, 7 Unterlegscheibe, 8 & 9 Mutter und Kontermutter, 10 Antriebswelle


Der Aufbau der Motorkopplung ist in Joachims Blogeintrag nicht sehr detailliert beschrieben, deshalb an dieser Stelle ein paar Sätze dazu.

  • Zunächst wird eine Einschlagmutter (5) in die Mutternscheibe (6) eingepresst.
  • Danach klebt man auf die Scheibe die beiden Distanzringe (3 & 4) mit Holzleim so, dass die Einschlagmutter innen liegt.
  • Auf den oberen Distanzring wird dann mit Sekundenkleber die Koppelscheibe geklebt. Als Ergebnis erhält man einen 12 mm hohen Zylinder mit einem Loch auf jeder Seite.
  • In das Loch auf der einen Seite passt die Motorwelle (1) genau,
  • in das andere Loch mit der Einschlagmutter schraubt man die Antriebswelle (10) ein Stück ein, stützt sie mit einer Unterlegscheibe (7) und sichert das Ganze mit zwei Muttern (8 & 9).


oben: Getriebemotor mit aufgestecktem Übertragungszylinder und eingeschraubter Antriebswelle, unten: Eingebauter Zustand, die Welle wird über die Verschraubung rechts im Bild in Position gehalten.

Die Steuerung

Die Hardware

Die Steuerung erfolgt bei meinem Modell über eine Modellbau-Fernsteuerung mit sechs Kanälen. Für die Steuerung selbst sind nur die zwei Kanäle des rechten Steuerknüppels vonnöten: vertikal für die Geschwindigkeit des Beests und horizontal für die Richtung. Alternativ könnte man auch jeden Motor direkt mit einem Kanal (vertikale Achse je eines Steuerknüppels) belegen und das Gefährt wie ein Kettenfahrzeug steuern. Zusätzlich habe ich die horizontale Achse des linken Steuerknüppels mit Auf-der-Stelle-Drehen (also beide Motoren drehen gegenläufig) belegt.

Der Empfänger gibt den Status des Steuerelementes jedes Kanals per PWM-Signal aus, das von einem Arduino Nano eingelesen und ausgewertet wird. Der Arduino steuert sodann die Geschwindigkeit und die Drehrichtung der beiden Antriebsmotoren über eine Doppel-H-Brücke vom Typ L298.

Der Zusammenbau beschränkt sich im Wesentlichen auf die korrekte Verdrahtung der Komponenten und die Programmierung des Arduinos.

Die Software

Der Arduino-Sketch bewirkt in einer Endlosschleife das Folgende:

  • lese die Pulsweiten der abgefragten Kanäle ein
  • errechne daraus jeweils die Position des Steuerhebels relativ zur Mittelstellung (-100% bis +100%)
  • wandle diese Information in Geschwindigkeits- und Richtungsanweisungen für die beiden Motoren um

Im Folgenden der Quelltext mit Kommentaren zur Verdeutlichung der Funktionsweise.

strandbeest.ino
/*************************
 * 
 * Strandbeest-Steuerung
 * www.yeat.net
 * 
 *************************/
 
 
/* Pin-Zuordnung des Motortreibers
 * PIN_ML... = linker Motor
 * PIN_MR... = rechter Motor
 */
#define PIN_ML_ENABLE_FORWARD 3
#define PIN_ML_ENABLE_REVERSE 4
#define PIN_ML_SPEED 5
#define PIN_MR_ENABLE_FORWARD 7
#define PIN_MR_ENABLE_REVERSE 8
#define PIN_MR_SPEED 6
 
 
/* Pin-Zuordnung des RC-Empfängers
 * RH = Kanal rechter Hebel, horizontal
 * RV = Kanal rechter Hebel, vertikal
 * LH = Kanal linker Hebel, horizontal
 */
#define PIN_INPUT_RH A0
#define PIN_INPUT_RV A1
#define PIN_INPUT_LH A3
 
 
/* Grenzwerte der Hebel-Auslenkungen
 * durch Ausprobieren herausfinden
 * MIN_INPUT... = ganz links oder unten
 * MAX_INPUT... = ganz rechts oder oben
 */
#define MIN_INPUT_RV 1200
#define MAX_INPUT_RV 1785
#define MIN_INPUT_RH 1180
#define MAX_INPUT_RH 1815
#define MIN_INPUT_LH 1150
#define MAX_INPUT_LH 1815
 
 
/* Grenzwert, ab dem eine Bewegung erkannt wird:
 * 10% um die Mittelstellung passiert nichts ("Nullbereich")
 */
#define THRESHOLD_RV 0.1
#define THRESHOLD_RH 0.1
#define THRESHOLD_LH 0.1
 
 
/* Vorabberechnung der Bewegungsspannbreite und
 * der Mittelstellung
 */
int inputRVRange = MAX_INPUT_RV - MIN_INPUT_RV;
int inputRVMid = (double)inputRVRange / 2.0;
int inputRHRange = MAX_INPUT_RH - MIN_INPUT_RH;
int inputRHMid = (double)inputRHRange / 2.0;
int inputLHRange = MAX_INPUT_LH - MIN_INPUT_LH;
int inputLHMid = (double)inputLHRange / 2.0;
 
 
/* Setup-Routine
 * wird einmalig beim Einschalten aufgerufen
 */
void setup() {
  //Serielle Schnittstelle starten
  Serial.begin(9600);
 
  //Pins des Motor-Treibers einrichten
  pinMode(PIN_MR_ENABLE_FORWARD, OUTPUT);
  pinMode(PIN_MR_ENABLE_REVERSE, OUTPUT);
  pinMode(PIN_MR_SPEED, OUTPUT);
  pinMode(PIN_ML_ENABLE_FORWARD, OUTPUT);
  pinMode(PIN_ML_ENABLE_REVERSE, OUTPUT);
  pinMode(PIN_ML_SPEED, OUTPUT);
 
  //Pins des RC-Empfängers einrichten
  pinMode(PIN_INPUT_RV, INPUT);
  pinMode(PIN_INPUT_RH, INPUT);
}
 
 
//Initialisierung einiger globaler Variablen
boolean isOff = false;
int curMotorLSpeed = 0;
int curMotorRSpeed = 0;
 
 
/* Arbeits-Routine
 * wird so lange ausgeführt, wie der Arduino läuft
 */
void loop() {
  //Auslesen der Hebelstellungen durch Messung der Pulsweiten
  int inputRVVal = pulseIn(PIN_INPUT_RV, HIGH, 30000);
  int inputRHVal = pulseIn(PIN_INPUT_RH, HIGH, 30000);
  int inputLHVal = pulseIn(PIN_INPUT_LH, HIGH, 30000);
 
  //nur Auswerten, wenn der Sender eingeschaltet ist (und damit Pulsweiten >0 zurückgeliefert werden)
  if ((inputRVVal != 0) && (inputRHVal != 0)) {
 
    //Gemessene Pulsweiten auf Maximal- und Minimalwerte begrenzen (um Werte kleiner 0% und größer 100% zu vermeiden)
    inputRVVal = max(min(inputRVVal, MAX_INPUT_RV), MIN_INPUT_RV);
    inputRHVal = max(min(inputRHVal, MAX_INPUT_RH), MIN_INPUT_RH);
    inputLHVal = max(min(inputLHVal, MAX_INPUT_LH), MIN_INPUT_LH);
 
 
    //Absolute Position der Hebel
    int inputRVPos = MAX_INPUT_RV - inputRVVal;
    int inputRHPos = MAX_INPUT_RH - inputRHVal;
    int inputLHPos = MAX_INPUT_LH - inputLHVal;
 
 
    //Relative Position der Hebel (zwischen -1 und 1)
    double inputRVPosRel = (double)(inputRVMid - inputRVPos) / (double)inputRVMid;
    double inputRHPosRel = (double)(inputRHMid - inputRHPos) / (double)inputRHMid;
    double inputLHPosRel = (double)(inputLHMid - inputLHPos) / (double)inputLHMid;
 
 
    //Werte innerhalb des Nullbereiches werden auf 0 gesetzt
    if (abs(inputRVPosRel) < THRESHOLD_RV) inputRVPosRel = 0.0;
    if (abs(inputRHPosRel) < THRESHOLD_RH) inputRHPosRel = 0.0;
    if (abs(inputLHPosRel) < THRESHOLD_LH) inputLHPosRel = 0.0;
 
 
    //nicht größer/kleiner als +/- 1.0
    inputRVPosRel = max(min(inputRVPosRel, 1.0), -1.0);
    inputRHPosRel = max(min(inputRHPosRel, 1.0), -1.0);
 
 
    //Motorgeschwindigkeit initialisieren
    int motorLSpeed = 0;
    int motorRSpeed = 0;
 
 
    //Motorrichtungen und -geschwindigkeiten anhand Position des rechten Hebels festlegen
    if (inputRVPosRel != 0.0) {
      //Vorwärtskomponente
      motorLSpeed = inputRVPosRel * 255.0;
      motorRSpeed = inputRVPosRel * 255.0;
 
      //Seitwärtskomponente
      if (inputRHPosRel != 0.0) {
        motorLSpeed *=  2 * min(inputRHPosRel, 0.0) + 1.0;
        motorRSpeed *= -2 * max(inputRHPosRel, 0.0) + 1.0;
      }
 
 
    //Wenn rechter Hebel in Nullstellung, dann linken Hebel auswerten und ggf. auf der Stelle drehen
    } else if (inputLHPosRel != 0.0) {
      motorLSpeed = inputLHPosRel * 255.0;
      motorRSpeed = -inputLHPosRel * 255.0;
    }
 
 
    //Motor-Treiber nur behelligen, wenn sich die Geschwindigkeit eines Motors verändert hat
    if ((motorLSpeed != curMotorLSpeed) || (motorRSpeed != curMotorRSpeed)) {
 
      //linken Motor einstellen
      if (motorLSpeed != 0) {
 
        //Drehrichtung an die PIN_ML_ENABLE...-Pins ausgeben
        if (motorLSpeed > 0) {
          digitalWrite(PIN_ML_ENABLE_REVERSE, LOW);
          digitalWrite(PIN_ML_ENABLE_FORWARD, HIGH);
        } else {
          digitalWrite(PIN_ML_ENABLE_FORWARD, LOW);
          digitalWrite(PIN_ML_ENABLE_REVERSE, HIGH);
        }
      } else {
        digitalWrite(PIN_ML_ENABLE_FORWARD, LOW);
        digitalWrite(PIN_ML_ENABLE_REVERSE, LOW);
      }
      //Geschwindigkeit auf PIN_ML_SPEED schreiben
      analogWrite(PIN_ML_SPEED, abs(motorLSpeed));
 
 
      //rechten Motor einstellen (wie linker Motor, siehe oben)
      if (motorRSpeed != 0) {
        if (motorRSpeed > 0) {
          digitalWrite(PIN_MR_ENABLE_REVERSE, LOW);
          digitalWrite(PIN_MR_ENABLE_FORWARD, HIGH);
        } else {
          digitalWrite(PIN_MR_ENABLE_FORWARD, LOW);
          digitalWrite(PIN_MR_ENABLE_REVERSE, HIGH);
        }
      } else {
        digitalWrite(PIN_MR_ENABLE_FORWARD, LOW);
        digitalWrite(PIN_MR_ENABLE_REVERSE, LOW);
      }
      analogWrite(PIN_MR_SPEED, abs(motorRSpeed));
 
 
      //aktuelle Motorgeschwindigkeit speichern für späteren Vergleich mit neuen Geschwindigkeiten
      curMotorLSpeed = motorLSpeed;
      curMotorRSpeed = motorRSpeed;
 
      //Motor ist eingeschaltet.
      isOff=false;
 
 
      //aktuelle Geschwindigkeiten der Motoren über die serielle Schnittstelle ausgeben
      Serial.print("motor speed: left=");
      Serial.print(motorLSpeed);
      Serial.print(" right=");
      Serial.println(motorRSpeed);
    }
 
 
  /* kein Empfang - alles abschalten (wenn nicht bereits abgeschaltet)!
   * Das Strandbeest soll stehenbleiben, wenn die Verbindung zur Fernbedienung
   * verloren geht (und nicht ewig in die aktuelle Richtung weitermarschieren).
   */
  } else if (!isOff) {
      digitalWrite(PIN_MR_ENABLE_REVERSE, LOW);
      digitalWrite(PIN_MR_ENABLE_FORWARD, LOW);
      analogWrite(PIN_MR_SPEED, 0);
      digitalWrite(PIN_ML_ENABLE_REVERSE, LOW);
      digitalWrite(PIN_ML_ENABLE_FORWARD, LOW);
      analogWrite(PIN_ML_SPEED, 0);
 
      //auch diesen Status auf der seriellen Konsole ausgeben
      Serial.println("no connection");
 
      //Motor ist ausgeschaltet
      isOff=true;
  }
}


Sobald man alle notwendigen Komponenten zusammen hat, alles korrekt verkabelt wurde und der Arduino zur Mitarbeit bewogen werden konnte, steht ersten Experimenten nichts im Wege. Ich fand es faszinierend, wie einfach es war, dem Arduino eine Fernsteuerung abseits von Bluetooth und W-LAN zu verpassen.



Die Stromversorgung

In den genannten Quellen zum Strandbeest wird das Thema Stromversorgung nicht besonders ausführlich behandelt. Ich habe mich für zwei LiPo-Akkus mit je 3.000 mAh entschieden, die ich beim lokalen Elektronik-Händler meines Vertrauens günstig erstehen konnte. Die Akkus sind bereits mit einer eigene Elektronik ausgestattet, mit der verhindert werden soll, dass sie unter eine bestimmte Spannung entladen (dann sind sie zerstört) bzw. über eine bestimmte Spannung geladen (dann entzünden sie sich oder explodieren) werden. Die Akkus mit je 3,7 V werden zum Betrieb in Reihe geschaltet und liefern dann 7,4 V.

Die Motortreiber-Platine arbeitet mit Spannungen ab 7 V und besitzt außerdem einen Spannungswandler, der 5 V zur Versorgung des Arduinos und des RC-Empfängers liefert. Was aber tun, wenn die Akkus leer sind? Das Aufladen der Akkus kann man einer Ladeplatine („Charger“) überlassen, die man für wenige Euros in China ordern kann, oder man greift z. B. auf das Modell von Adafruit zurück. Der Adafruit USB LiIon/LiPoly charger - v1.2 ist zwar etwas teurer, dafür aber bestens dokumentiert und die Akkus können mit einem Handy-Ladegerät geladen werden.

Der Adafruit-Charger. Der parallel zum vorhandenen 2k-Widerstand eingelötete 2k-Widerstand reduziert den Gesamtwiderstand auf 1k-Ohm und stellt damit den maximalen Ladestrom auf 1A ein. Klingt kompliziert? Halbiert aber die Ladezeit.



Eine kleine Komplikation ergibt sich aus der Tatsache, dass beide Akkus zum Aufladen parallel auf den Ladeausgang des Chargers geschaltet werden müssen, für den Betrieb der Motoren jedoch eine Reihenschaltung erforderlich ist. Hierfür hat sich Lady Adas Truppe eine sehr coole Lösung ausgedacht, die mit einem 3-poligen Umschalter und ein wenig Draht auskommt. Die Schaltung sieht auf den ersten Blick reichlich kompliziert aus, funktioniert aber bestens. Für den Schalter verwendete ich einen Vierfach-Umschalter (siehe z. B. hier, hier oder hier).

Einfach genial: der 3er-Umschalter schaltet die Akkus entweder in Reihe zur Last und trennt vom Charger, oder er schaltet sie parallel zum Charger und trennt von der Last. (Bild: learn.adafruit.com, Lizenz: Creative Commons Attribution-ShareAlike)



Da ich kein großer Freund komplizierter Freiverkabelungen bin und das Holzbiest am Ende in gewissen Grenzen kinderkompatibel sein sollte, habe ich mittels Fritzing eine kleine Leiterplatte entworfen, die alle beteiligten Komponenten und Platinchen schön sauber mit Flachbandkabeln und Steckverbindern zusammenführt.

Ordnung muss sein: die Strandbeest-Platine



Wer es mir nachmachen möchte, kann sich die Datei strandbeest_platine.fzz herunterladen und in der fritzing Fab herstellen lassen. Bestückt wird das Ganze mit:

  • 1 x Arduino Nano
  • 2 x Wannenstiftleiste mit 2 x 5 Polen (zum Anschluss von Umschalter und Motor-Treiber)
  • 1 x Wannenstiftleiste mit 2 x 4 Polen (zum Anschluss des RC-Empfängers)
  • 3 x Stiftleiste mit 1 x 2 Polen (zum Anschluss der beiden Akkus und des Chargers)
  • dazu noch jeweils passende Stecker (2 x Wannenstecker mit 2 x 5 Polen, 1 x Wannenstecker mit 2 x 4 Polen, 3 x Stecker mit 2 Polen; alles Rastermaß 2,54 mm)
  • die Verkabelung erfolgt dann folgerichtig mit Flachbandkabel (am besten: regenbogenfarben zur leichteren Zuordnung) im Rastermaß 1,27 mm sowie Doppellitze


links: Der Miniatur-Kippschalter (4 x UM), rechts: Die Zuordnung der Kontakte zum Anschluss an die Platine



Der Umschalter verbindet immer zwei Kontakte einer 3er-Zeile. Je nach Schalterstellung wird entweder der Kontakt ganz links oder der Kontakt ganz rechts mit dem mittleren Kontakt verbunden.

Der Zusammenbau

Für das Zusammensetzen haben wir uns ein paar Abende Zeit genommen. Die meiste Zeit entfällt dabei auf den (etwas monotonen) Zusammenbau der sechs Beinpaare. Welch ein Glück, dass ich hier auf Hilfe zählen konnte. Für den Einbau der Beine und der Zahnräder benötigt man etwas Fingerspitzengefühl, denn es ist gar nicht so leicht, alle beteiligten Zahnräder an der richtigen Stelle in die rechte Position zu bekommen. Ganz wichtig ist ein ausreichendes Spiel an allen Achsen und Gelenken. Auch die Antriebswelle sollte so viel Spiel haben, dass man sie ganz leicht hin und her bewegen kann.

Auf Youtube gibt es ein Video, in dem man Joachim dabei zusehen kann, wie er das Beest montiert. Das hat mir sehr dabei geholfen zu verstehen, wo alle Teile am Ende hingehören und welche Arbeitsschritte notwendig sind.

Nachdem das Skelett fertiggestellt war, mussten nur noch die zahlreichen zusätzlichen Komponenten der Steuerung und Stromversorgung angebracht werden. Das Meiste (RC-Empfänger, Akkus, Motor-Treiber) habe ich dezent an der Unterseite mit schmalen Kabelbindern befestigen können. Die Arduino-Platine und den Charger habe ich, ebenfalls mit schmalen Kabelbindern, an der Oberseite befestigt.

Hier noch zu guter Letzt ein paar Schnappschüsse vom fertig montierten Holzbeest: