Serielle Kommunikation mit dem Arduino: Die ServoCam

Es wurde Zeit einmal über den Tellerrand zu schauen und meine Aufmerksamkeit ein wenig dem Arduino zu widmen. Das erste kleine Projekt das daraus entstand, ist eine dreh- und schwenkbare Kamera, die über das Internet zu steuern ist. Die Ansteuerung der Servos erledigt der Arduino. Das Hosten des Webservers und den Stream eine Livebild der Kamera soll der Raspberry Pi übernehmen. Einen kleinen Eindruck von dem Vorhaben, gewähren bereits die nächsten drei Bilder.

Warum zwei Geräte für diese Aufgaben?

Der ausschlaggebende Punkt war die Ansteuerung der Servomotoren. Diese funktioniert am RasPi zwar auch, allerdings mehr schlecht als recht. Die Gründe dafür sind folgende:
Der “alte” RasPi B besitzt nur einen Hardware-PWM Pin. Für meine zwei Motoren allerdings einer zu wenig. Also schrieb ich ein Python-Programm für zwei beliebige Pins mit  einem Software-PWM-Signal. Das Verstellen des Servos funktionierte zwar, aber sehr unsauber. Der Motor zuckt sogar auf der Stelle, wenn die gegenwärtige Position noch einmal angesteuert wird. Ich vermutete, dass es am unsauberen Soft-PWM-Takt liegt. Ein Test mit dem Hardware-PWM-Pin bestätigte dies dann. Hier lief der Motor sehr sauber. Da der Arduino aber von Haus aus mehr frei zugängliche, vollwertige PWM-Pins bietet, entschied ich mich dafür.

Zudem ist es einfach spannend, die beiden Platinen miteinander “reden” zu lassen.

Die Hardware:

Für dieses Projekt ist folgende Hardware nötig:

Angeschlossen werden die beiden Servos nach dem folgenden Schaltplan.

servocam

Nun sieht man, dass der Raspberry Pi nur mit seinem Tx-Pin zum Rx-Pin des Arduino verbunden wird. (Die zweite Leitung verbindet beide Massepotentiale)

Das RasPi soll dem Arduino nur sagen was zu tun ist. Eine Antwort vom Arduino wird nicht erwartet. Das hat den Vorteil, dass ich keinen Pegelwandler oder Spannungsteiler zwischen den beiden Geräten benötige. Der Rx-Pin des Arduino führt keinen 5-V-Pegel. Anders verhält es sich, wenn der Arduino Befehle an den RasPi senden soll. Der Tx-Pin des Arduino darf NICHT ohne Zwischenschritt wie Spannungsteiler oder Pegelwandler an den RasPi angeschlossen werden.

ACHTUNG: Beim Flashen des Arduino muss der RasPi auch vom Rx-Pin des Arduinos abgezogen werden! Die USB-Schnittstelle ist gleich den Tx- und Rx-Pins. Während des Flashvorganges kann auch der Rx-Pin auf ein 5-V-Level gezogen werden.

Die Software

Die Software für den Raspberry Pi besteht aus mehreren Teilen:

  • Der Webserver
  • Die Weboberfläche mit Livebild und Bedienfeld
  • Das Senden der Daten an den Arduino

Der Webserver

Als Webserver nutze ich einen Apache. Dieser ist vorab nach dieser Anleitung einzurichten.

Die Bedienoberfläche

Für meine Oberfläche habe ich ein wenig “geklaut”. Hier nutze ich als Grundgerüst das Projekt RPi Cam Web Interface, dessen Einrichtung sehr schnell und einfach im RasPi Forum erklärt ist.

Nach der Einrichtung läuft auf dem RasPi die im Forum gezeigte Oberfläche. Dort ist das Livebild und eine Vielzahl von EInstellungsmöglichkeiten zu sehen. Mir geht es hauptsächlich um das Livebild. Daher habe ich die index.php etwas abgespeckt.

Der übriggebliebene Code sieht wie folgt aus:

<!DOCTYPE html>
<?php
 define('BASE_DIR', dirname(__FILE__));
 require_once(BASE_DIR.'/config.php');
?>
<html>
 <head>
 <title>ServoCam</title>
 <script src="script.js"></script>
 </head>
 <body onload="setTimeout('init();', 100);">
 <center>
 
 <div><img id="mjpeg_dest"></div>
 <input id="video_button" type="button">
 <input id="image_button" type="button">
 <input id="timelapse_button" type="button">
 <input id="md_button" type="button">
 <input id="halt_button" type="button">
 <p><a href="preview.php">Download Videos and Images</a></p>
 
<iframe src="controls.php" align ="center" width="20%" height="200" name="controls">
</iframe>


 
 </body>
</html>

Ihr seht, dass in einem iFrame auf die Datei controls.php verwiesen wird. Das hat denn Sinn, dass ich nach einem Klick auf eine Positionsangabe kein Reload der kompletten Seite haben möchte, sondern die Bewegung der Kamera live im Bild verfolgen möchte. Durch das Einbetten des iFrames, läd also nur die controls.php im iFrame neu, die index.php samt Livebild dagegen nicht.

Zum Inhalt der controls.php komme ich später. Hier ist erst ein wenig Hintergrundwissen zum Pythoncode notwendig.

Der Pythoncode auf dem Raspberry Pi

Der Raspberry Pi soll seine ganze Energie auf das Anzeigen des Streams und des Webservers legen. Die komplette Steuerung der Motoren habe ich daher dem Arduino überlassen. Der Pythoncode fällt daher auch sehr einfach aus:

import time
import RPi.GPIO as GPIO
import serial
import sys

ser = serial.Serial('/dev/ttyAMA0', 9600)

data = sys.argv[1] # first parameter
data2 = sys.argv[2] # first parameter


ser.write(data)
time.sleep(0.01)

ser.write(data2)
time.sleep(0.01) 
 
sys.exit()

Mit den richtigen Parametern gestartet, schickt das Python-Programm zwei Werte über die UART-Schnittstelle an den Arduino. Dafür ist die im Schaltplan gezeigte Verbindung von Nöten. Der Aufruf des Programms erfolgt mit übergebenen Startparametern. Z.B. so:

$ python servocam.py 3 5

Nun wird zuerst die 3 gesendet, 10 ms später dann die 5. Das war`s auch schon. Kommen wir nun zum C-Programm auf dem Arduino um den Sinn dahinter zu verstehen.

Die Arduino-Software

Der C-Code liest sich wie folgt:

#include <VarSpeedServo.h>
VarSpeedServo schwenk; 
VarSpeedServo kipp;

int a;
int b;
char pos1 ='0';
char pos2 ='0';
char comm[2];

void setup() 
{ 
 schwenk.attach(9);
 kipp.attach(10); 
 Serial.begin(9600);
} 
 
void loop() 
{ 
if (Serial.available()) {
 Serial.readBytes(comm, 2);
 pos1 = comm[0];
 Serial.println(pos1);
 pos2 = comm[1];
 Serial.println(pos2);
  
 a = pos1-'0';
 a=a*20;
 b= pos2 -'0';
 b = b*20; 
 
 if (a != 9*20)
 { schwenk.slowmove(a,90); 
 delay(800); }
 
 if (b != 9*20) 
 { kipp.slowmove(b,90); 
 delay(800); }
 } 
}

Zur Erläuterung:

Ich nutze zum Handling der Servos nicht die mitgelieferte Servo.h-Library, sondern eine etwas modifizierte: VarSpeedServo.h aus dem Arduino-Forum. Der Vorteil an dieser Library ist, dass durch einen zusätzlichen Wert die Drehgeschwindigkeit der Servos beeinflusst werden kann. (.slowmove(servo, speed). Das ist hilfreich, möchte man keine schlagartigen Drehungen, sondern eine ruhige Kamerafahrt. Der Arduino fragt nun in jedem Durchlauf den seriellen Port ab und speichert die zuvor vom Raspberry Pi übermittelten zwei Zahlenwerte in comm[0] und comm[1] ab. Den ersten Wert habe ich dem Drehmotor zugewiesen, den zweiten Wert dem Kippmotor.

Da immer beide Werte gleichzeitig übermittelt werden, ich aber per Mausklick später nur einen der beiden Motoren bewegen kann, brauchte ich eine Möglichkeit den nicht gewählten Motor in seiner vorherigen Position zu belassen. Dies geschieht durch die “9”. Wird eine 9 empfangen, so tut der entsprechende Motor nichts. Ich habe also die Schritte von 0-8 für die Positionsangabe zur Verfügung.

Ebenfalls im Programm zu finden ist die Multiplikation des empfangenen Werten mit 20. Das hat den Hintergrund, dass die Servo-Funktion eine Gradzahl von 0-180 erwartet. Da ich die Werte von 0-8 übermittle, erreiche ich eine maximale Drehung von 160°. Ich habe absichtlich keine 180° gewählt, da meine Motoren sonst in den mechanischen Anschlag fahren. Das ist aber je nach mechanischem Aufbau variabel und sollte ausprobiert werden.

Die controls.php

Zu guter Letzt fehlt noch der Inhalt der controls.php im iFrame. Dieser liest sich so:

<div align="center"> 
<?php 
if(isset($_GET['Schwenk'])) {
$command1 = $_GET['Schwenk'];
$val = shell_exec("sudo python /python/servocam.py $command1 9");
}

elseif(isset($_GET['Kipp'])) {
$command2 = $_GET['Kipp'];
$val = shell_exec("sudo python /python/servocam.py 9 $command2");
}
?>

<div align="center"> Drehen </div>
 <--<a href="<?php print($_SERVER['PHP_SELF']); ?>?Schwenk=0" class="knopf_on" style="width: 100px">
 0</a>
 
 <a href="<?php print($_SERVER['PHP_SELF']); ?>?Schwenk=1" class="knopf_on" style="width: 100px">
 1</a>
 
 <a href="<?php print($_SERVER['PHP_SELF']); ?>?Schwenk=2" class="knopf_off" style="width: 100px">
 2</a>
 
 <a href="<?php print($_SERVER['PHP_SELF']); ?>?Schwenk=3" class="knopf_on" style="width: 100px">
 3</a>
 
 <a href="<?php print($_SERVER['PHP_SELF']); ?>?Schwenk=4" class="knopf_on" style="width: 100px">
 4</a>
 
 <a href="<?php print($_SERVER['PHP_SELF']); ?>?Schwenk=5" class="knopf_off" style="width: 100px">
 5</a>
 
 <a href="<?php print($_SERVER['PHP_SELF']); ?>?Schwenk=6" class="knopf_on" style="width: 100px">
 6</a>
 
 <a href="<?php print($_SERVER['PHP_SELF']); ?>?Schwenk=7" class="knopf_on" style="width: 100px">
 7</a>
 
 <a href="<?php print($_SERVER['PHP_SELF']); ?>?Schwenk=8" class="knopf_off" style="width: 100px">
 8</a>-->
 
 <br>
 <div align="center"> Kippen </div>
 down <a href="<?php print($_SERVER['PHP_SELF']); ?>?Kipp=0" class="knopf_on" style="width: 100px">
 0</a>
 
 <a href="<?php print($_SERVER['PHP_SELF']); ?>?Kipp=1" class="knopf_on" style="width: 100px">
 1</a>
 
 <a href="<?php print($_SERVER['PHP_SELF']); ?>?Kipp=2" class="knopf_off" style="width: 100px">
 2</a>
 
 <a href="<?php print($_SERVER['PHP_SELF']); ?>?Kipp=3" class="knopf_on" style="width: 100px">
 3</a>
 
 <a href="<?php print($_SERVER['PHP_SELF']); ?>?Kipp=4" class="knopf_on" style="width: 100px">
 4</a>
 
 <a href="<?php print($_SERVER['PHP_SELF']); ?>?Kipp=5" class="knopf_off" style="width: 100px">
 5</a>
 
 <a href="<?php print($_SERVER['PHP_SELF']); ?>?Kipp=6" class="knopf_on" style="width: 100px">
 6</a>
 
 <a href="<?php print($_SERVER['PHP_SELF']); ?>?Kipp=7" class="knopf_on" style="width: 100px">
 7</a>
 
 <a href="<?php print($_SERVER['PHP_SELF']); ?>?Kipp=8" class="knopf_off" style="width: 100px">
 8</a> up

</div>

Anfangs ein wenig unübersichtlich, aber leicht erklärt. Wie auf einem der ersten Screenshots zu erkennen, sind in dem iFrame nur zwei Zeilen mit Werten von 0-8. Die oberste Zeile dreht die Kamera, die untere Zeile kippt sie. Beim Klick auf einen der Werte wird das Pythonscript ausgeführt und jeweils der neue Wert für den gewählten Motor übertragen. Der nicht angeklickte Motor erhält eine 9 und bleibt somit auf seiner Position.

Jetzt müsst Ihr nur noch alles ordentlich Zusammenschrauben und Verkabeln und Ihr habt eine fernsteuerbare Überwachungkamera. Wie das Ganze in Aktion aussieht, seht Ihr in diesem Video

8 Gedanken zu “Serielle Kommunikation mit dem Arduino: Die ServoCam

  1. Hallo

    dein Projekt ist echt klasse.. genau sowas habe ich gesucht habe schon mit dem raspberry versucht aber es leider nicht hinbekommen. Jetzt habe ich mir auch einen arduino zulegt und wollte gleich mal dein Projekt in die tat umsetzten nur leider komme ich bei dem Punkt mit dem Arduino nicht weiter. Habe diesen über usb-angschlossen. Vllt kannst du mir weiterhelfen.

    Vielen dank

    • Hey,

      die Frage ist sehr ungenau 😉

      Zuerst einmal habe ich den Arduino über die serielle Schnittstelle (TxD bzw RxD) angeschlossen. Zudem habe ich ein separates Netzteil für den Arduino genutzt.

      Es könnte sein, dass du auch über USB seriell kommunizieren kannst. In diesem Fall, wird der Arduino jedoch durch die USB-Ports des RasPi versorgt. Demnach jedoch auch die Servos. Das würde ich eher vermeiden. Je nach RasPi-Modell ist der Strom durch die USB-Buchsen sehr begrenzt.

  2. Hey,
    dieses Tutorial ist eigentlich genau das, was ich für mein Projekt gesucht habe. Ich möchte ein Pan-Tilt-Kit vom PC aus steuern (auf einem 2WD-Car) mit einem Livestream von der Fahrt und der UUmgebung.

    Hat jemand diese Anleitung zum Laufen gebracht? Ich scheiter leider.
    Nun konkret:
    – der Arduino steuert die Servos sehr gut mit $sudo python servocam.py 2 4 vom Terminal aus
    – apache2 ist installiert
    – das Interface RPi_Cam_Web_Interface ist installiert und zeigt den Livestream (mit Unterbrechung wenn die Einzelbilder zum Video werden) und die Einstellprogramme lassen sich über die butttons öffnen.

    Soweit schön und gut.
    Nun sollen aber die “abgespeckten” Programme index.php und controls.php ran, die im Verzeichnis /var/www/html bei mir liegen. Also habe ich diese in das Verzeichnis kopiert und damit die ursprüngliche index.php überschrieben.
    Diese sollen nun im iframe über die Button 1 …. 8 die Servos ansteuern. Tut sich leider nichts. Auch ist kein Livestream mehr am PC zu sehen.

    Kann jemand helfen? Ich bin Anfänger und kann nicht sehen, ob es nun an einer fehlerhaften index.php und controls.php liegt. Ich vermute es, da ich auch nicht sehen kann, wie der Livestream am PC angezeigt werden soll. Das müsste doch eigentlich über die index.php initiert werden. Auch müssten doch eigentlich die zur Steuerung gedrückten “Zahlen” übergeben werden. Wie? Es schreibtt ja leider niemand, dass es auch funktioniert hat.

    Ich freue mich über eine Antwort.
    Gruß Ulli

  3. Hey,
    ich habe nun die originale index.php des RPi-Interfaces gekürzt. Nun ist nur noch der LiveStream und die Servo-Steuerung (iFrame) zu sehen:

    RPi Cam-Control

    ——————————————————————————————
    Bilder kann man hier ja nicht anfügen.

    Die Steuerung der Servos funktioniert aber noch nicht. Unten im Browser wird aber z.B. folgende Zeile angezeigt: (die IP des Raspi und das Verzeichnis in dem die Dateien des RPi-Interfaces liegen und der Kipp-Befehl)

    192.168.178.27/html/controls.php?Kipp=2

Kommentar verfassen