FischerPi Teil 4: RaspiFastCamD

TL;DR: Wer einfach nur RaspiFastCamD haben will: Hier gibt es die (mglw. veraltete Binärversion).

Wow, der letzte Teil ist schon wieder lange her. Aber ich war nicht untätig, für die weitere Arbeit an meinem Roboter musste erst ein Software-Problem behoben werden:

Ich habe mir ja die Rasperry Camera gekauft, und diese sollte für die Navigation benutzt werden. Dazu muss natürlich irgendwie das Kamera-Bild ausgewertet werden. Nach ein wenig Suchen habe ich dann OpenCV gefunden, eine Bibliothek, die genau dafür gemacht wurde (CV steht für „Computer Vision“). Nur wie das Bild der Kamera in OpenCV bekommen? Bei USB-Kameras, die V4L (Video for Linux) unterstützen, ist das recht einfach, dort kann OpenCV die Bilder einfach abholen.

Leider unterstützt die Rasperry Camera überhaupt kein standardisiertes Protokoll, sondern es werden nur zwei Anwendungen mitgeliefert, die mit der Kamera sprechen können, raspistill und raspivid. Die erste macht JPEG-Fotos, die zweite H.264-Videos. Das funktioniert auch tadellos, ist aber nicht wirklich zu gebrauchen: Ein komplettes Video mit 30fps kann ich nicht gebrauchen, ich brauch auf Anfrage genau einen Frame. Leider ist raspistill aber viel zu langsam: Bei jeden Foto wird die Verbindung zu Kamera neu aufgebaut, und danach wieder getrennt. Zudem muss jedes mal die Belichtung neu berechnet werden. Damit kommt man maximal auf ein Bild pro Sekunde, und das ist einfach zu wenig.

Auftritt RaspiFastCamD: Doch der Source-Code von raspistill ist auf GitHub, also machen wir es einfach selber besser! Ich habe mich also die letzte Zeit damit beschäftigt, raspistill umzuschreiben, damit es meinen Anforderungen besser entsprach. Ich hatte vor allem zwei Anforderungen:

  • Nicht jedes Mal die Verbindung zur Kamera neu aufbauen
  • Möglichst viele Bilder pro Sekunde, aber auf Abruf, nicht automatisch

Das Konzept hinter RaspiFastCamD ist recht einfach:

  • Das Programm läuft im Hintergrund (als Dämon, daher das „d“ im Namen) und hält die Verbindung zu Kamera offen
  • Wenn der Benutzer ein USR1 Signal an den Prozess schickt wird ein Foto gemacht
  • Bei einem KILL oder INT Signal wird die Verbindung abgebaut und der Prozess beendet.

Ich habe RaspiFastCamD als Fork von RaspiStill entwickelt, im Grunde habe ich nur eine Menge unnötiger Funktionen ausgebaut (wie EXIF-Tags), und das Programm so umgeschrieben, dass es dauerhaft im „Timeleapse“ Modus ist (der schon in raspistill enthalten ist), aber vor jedem Foto auf ein USR1 Signal wartet. Wer sich dafür interessiert, wie das genau funktioniert möge sich den Code ansehen, der unten verlinkt ist. Ich weise darauf hin, dass ich keine große Ahnung von C habe, das meiste ist trial und error gewesen… (Das war sogar mein erstes Linux Programm!)

Dennoch finde ich die Ergebnisse recht beeindruckend: Um die Latenzzeit des Programms zu bestimmen, habe ich mit einem kleinen C# Programm über SSH den Befehl zum Foto machen an die Kamera geschickt, und gleichzeitig eine Uhr auf dem Bildschirm hoch zählen lassen. Auf diese Weise konnte ich auf dem Foto ungefähr die Latenz der Kamera ablesen. Die Ergebnisse:

  • RaspiStill: ~500ms
  • RapiFastCamD: <10ms

Test mit raspistillTatsächlich war RaspiFastCamD so schnell, dass der Zähler noch gar nicht gestartet war! Da ich die Latenz von C#->Bildschirm auf ~10ms schätzen würde (eher weniger) und davon noch die Latenz der Auslösung, die über SSH und WLAN(!) passierte, abgeht, ist die Verzögerung schon beeindruckend gering. Mit besserem Equipment könnte man die sicher noch besser messen. Vielleicht sind hier sogar noch ganz andere Anwendungszwecke möglich?

So, genug geredet, wo ist das Ding den nun? Der komplette Code ist ist auf BitBucket als Fork des userland-Repos zu finden, und zwar hier: RaspiFastCamD-Source. Wer den Code selber übersetzen möchte, benutze diese Schritte (auf dem Pi):

sudo apt-get install git gcc build-essential cmake
git clone https://bitbucket.org/niklas_rother/rasperry-pi-userland.git
cd userland
mkdir build
cd build
sudo cmake -DCMAKE_BUILD_TYPE=Release ..
sudo make

Das ganze dauert ca. ein halbe Stunde, danach liegt die Executable unter ./bin. (Die nötigen Änderungen, damit sich der Code auf dem Pi kompilieren lässt, sind bereits drin). Leider war ich nicht sehr intelligent, und habe direkt im master-Branch weiter gemacht, daher gibt es momentan evtl. ein kleines Problem weil es ein paar Änderungen in den #include Dateien im Repo auf GitHub gab (die ich schon abgerufen habe). In diesem Fall einfach die Binärversion von unten benutzen, oder im Git ein paar Commits zurückgehen 🙂

Wer RaspiFastCamD einfach nur benutzen möchte: Im Repo liegt auch eine (getestete), direkt lauffähige Binärversion, zusammen mit ein paar Helper-Scripts.

Die Binär-Version auf BitBucket.

Wow, das war mal wieder ein langer Blog-Eintrag. Ich habe auch lange an RaspiFastCamD gesessen, vor allem, weil ich immer nur sporadisch Zeit hatte, aber ich denke wirklich, dass mir da etwas nützliches gelungen ist. Ich weiß, dass viele Leute versuchen, OpenCV mit der Raspberry Camera zusammen zu bringen, aber ich habe noch nicht wirklich viele positive Ergebnisse im Netz gesehen. Nur Pierre Raufast scheint da schon etwas geschafft haben (womit er es schon auf den Raspberry Pi Blog geschafft hat!). Auch er hat eine der Demo-Anwendung umgeschrieben, in seinem Fall RaspiVid. Bei seinem Ansatz werden die Daten direkt im Speicher von MMAL an OpenCV weitergereicht, was wohl recht gut funktioniert (auch wenn mal noch die Farbräume auf der CPU konvertieren muss). Das ganze hat aber den Nachteil, dass der komplette OpenCV Code direkt mit RaspiVid verbunden wird, und daher in C geschrieben werden muss. Ich wollte das ganze lieber ein wenig modularer haben, auch weil ich OpenCV gerne über Python ansteuern möchte. Vielleicht lassen sich die beiden Ansätze auch noch kombinieren, bis jetzt habe ich noch nicht ausprobiert, wie viele FPS ich wirklich schaffe (10 sollten aber drin sein, ich empfehle die Daten nach /dev/shm zu schreiben, dann landen sie direkt im RAM und nicht erst auf der SD Karte).

Ich würde mich über eine Rückmeldung freuen, wenn etwas aus meiner Software wird! Patches welcome 🙂

14 Gedanken zu „FischerPi Teil 4: RaspiFastCamD

  1. Hallo Niklas,

    vielen Dank für deine Arbeit an raspifastcamd.
    Es geht wirklich viel schneller als raspistill, ich habe auch bisher keine Probleme das Script beim Starten ausführen zu lassen.

    Leider habe ich aber auch das Problem mit den schwarzen Bildern, wie peewee2 aus dem Forum.
    Teilweise habe ich sogar 5 schwarze Bilder hintereinander.
    Hast du noch eine Idee wie ich das lösen könnte?

    Gruß
    Philipp

  2. Ok hat sich erledigt..
    Gerade ist mir aufgefallen das ich die Bilder direkt auf den Stick schreibe, welche auch nicht gerade der schnellste ist.
    Jetzt schreibe ich ins home Verzeichnis und verschiebe die Bilder dann sofort auf den Stick -> keine schwarzen Bilder mehr 🙂

    1. Dann hat sich das Problem ja schon gelöst 🙂
      Noch schneller müsste es sein, die Bilder erst nach /dev/shm zu schreiben, das liegt direkt im RAM (ist also nach einen Neustart auch wieder leer, und natürlich nicht beliebig groß…)

  3. Hallo

    schicke Idee leider scheint dein Script mit –exposure night –ev 10 nichts anfangen zu können 🙁 Das normale RaspiStill macht Fotos mit 1/4 Sekunde und ISO 800. Bei deinen Script entsteht (gleiche Helligkeit) da ein Foto mit 1/30 und ISO1250 was leider für meinen Anwendungsfall zu dunkel ist 🙁 Kannst du das evtl. normal überprüfen? Ich hab direkt die BIN von BitBucket verwendet.

    Meines wissens gibt –exposure night die Möglichkeit die Belichtungszeit auf 1/4 Sek zu reduzieren ohne –exposure night ist 1/30 die Grenze, sieht also so aus als würde dein Script –exposure night einfach übersehen.

    mfg

    Chris

    1. Hallo Christian,

      ich vermute mal, dass diese beiden Optionen etwas neuer sind? Leider ist der Code von raspifastcamd dem von raspistill inzwischen ziemlich hinterher, und die kompilierte Version ist noch mal ein ganzes Stück älter. 🙁

      Inzwischen ist meine Idee aber auch schon in den offiziellen Code von raspistill eingezogen: Wenn man raspistill mit –signal startet lauscht die Kamera auch auf USR1 Signale.

      Aber Achtung: Leider ist die Version die über die Paketquellen verteilt wird schon relativ alt, daher ist der Code dafür momentan nur im git-Repository vorhanden. Selber kompilieren ist aber recht einfach:

      cd
      mkdir new_cam
      cd new_cam
      git clone https://github.com/raspberrypi/userland.git
      mkdir install
      ./buildme ./install #Pfad weglassen, um global zu installieren

      Das dauert etwa eine halbe Stunde, und danach hast du in ~/new_cam/install eine neue Version von raspistill, die eben auch den –signal Parameter kennt. Wenn man den Parameter von buildme weglässt, wird es systemweit installiert (ersetzt also das alte raspistill). Ich hab das jetzt gerade nicht ausprobiert, aber so ungefähr sollte es funktionieren.

      Inzwischen ist raspifastcamd also schon praktisch überholt. Ich hoffe ja immer noch, dass endlich mal ein Update für raspistill rauskommt, damit man nicht immer die Version selber kompilieren muss…

      Ich hoffe, dass hilft dir weiter,
      Niklas

      UPDATE: Hier ist noch der Link zum Code von raspistill: https://github.com/raspberrypi/userland/tree/master/host_applications/linux/apps/raspicam

      1. Hallo

        vielen dank für den Tipp. habs mal ausgeführt und komm soweit auch zurecht. Bei deinen Befehlen fehlt noch ein cd userland damit man im richtigen Ordner landet war aber kein Problem.

        Dafür hab ich ein anderes Problem:

        Ich hab dein start_camd.sh auf das neue raspistill umgemodelt:

        #!/bin/bash
        #Helper script to start RaspiFastCamD

        #We would like to write this to /var/run, but this need root privileges…
        pid_file=/tmp/raspifastcamd.pid

        if [ -e $pid_file ]; then
        echo „Error: The pid file $pid_file exists, looks like raspifastcamd is already running.“
        echo „If this is not the case delete the file.“
        exit 1
        fi

        output_file=${1-/dev/shm/tmp.jpg}

        echo „Output will be written to $output_file“

        #This will make pictures of 200x200px feel free to change.
        raspistill -w 1024 -h 768 -s -o $output_file –ev 10 –exposure night &
        pid=$!

        echo „Pid of raspifastcamd is $pid“

        echo $pid > $pid_file

        exit 0

        Grundsätzlich scheint raspistill nun zu laufen. Mit deinem
        do_capture.sh lassen sich auch (sau schnell ;)) Bilder erstellen. Blöderweise scheint raspistill nach einigen Bildern einfach abzustürzen.

        Ruf ich keine Bilder ab, bleibt der Service im Hintergrund am laufen (mit ps ax gut zu sehen).

        nach ein paar mal aufrufen mit dem do_ Script wird der Service aber einfach beendet. Mal sind es 3 Bilder manchmal 5 aber viel mehr bekomm ich nicht am Stück zusammen.

        Wo könnt ich nachgucken warum raspistill dann einfach weg ist?

        mfg

        Chris

  4. Hallo Herr Rother,
    ihr geniales Programm RaspifastcamD interessiert mich wirklich sehr!

    Nach stundenlangem frustranem Herumprobieren bitte ich Sie um Unterstützung.
    Ich verwende Python 2.7 oder 3.2 und würde gerne Raspistill beschleunigen.
    Die Binärversion habe ich in einen Ordner auf meinem Raspi heruntergeladen. Aber wie starte ich nun und beende ich RaspifastcamD, am besten über Python?
    Ich habe es mit os.system(„/ordner/raspifascamd“) versucht. Die Dateien start_cam.sh und stop_cam.sh und do_capture.sh lassen sich auch nicht öffen.
    Ich kann nur Visual C#, leider kein C++.
    Haben Sie vielleicht einen Python-Beispielcode zur Verfügung?
    Ich würde mich auch gerne erkenntlich zeigen.

    Es wäre super, wenn Sie mit weiterhelfen könnten!!
    viele Grüße
    Hans Franz

    1. Hallo,

      also Python ich auch nicht gerade die Sprache meiner Wahl, ich komme auch mehr aus der C#-Welt. Lässt sich raspifastcamd denn aus der Shell über die drei Skripte benutzen? Eventuell kann ein chmod u+x [Dateiname] für die drei Dateien helfen…

      Inzwischen hat das normale raspicam aber die Idee von mir auf aufgegriffen: Mit -t 0 wartet es endlos lange, und mit -s wartet es zwischen den Aufnahmen auf eine USR1 Signal (oder mit -k auf einen Tastendruck; siehe Doku). Ich würde inzwischen dazu raten, raspicam direkt zu benutzen.

      Ansonsten gibt es auch noch eine direkte Python-API für die Kamera, aber ich habe keine Ahnung, ob die ein „schnelles“ auslösen von Fotos unterstützt, es könnte aber sein.

      Ansonsten einfach noch mal fragen (bei speziellen Fragen gerne auch per Mail!)

      -Niklas

  5. Hallo Niklas,

    vielen Dank für das tolle Projekt. Ich habe raspifastcamd erstellt und konnte auch mit do_capture.sh via pi2 + cam ein Bild aufnehmen. Kannst du mir verraten, wo ich z.B. die Auflösung oder Belichtung steuern kann?

    Danke u. Gruß

    1. ach gefunden… vor lauter Sonnenfinsternis mal wieder die Brille vergessen 😉 für alle anderen die Suchen = start_camd.sh

  6. Mit der aktuellsten Version von raspistill und der SIGUSR1 Vorgehensweise

    raspistill -o /dev/shm/image%04d.jpg -s -t 0 -l latest.jpg -w 640 -h 480 &
    while :
    do
    kill -USR1 $pid
    done

    kreig ich nur durschnittlich alle 591ms ein Foto.
    Was kann der Grund sein, dass das so langsam ist?
    Ich würd auch sehr gerne unter 10ms kommen…

    1. Hi,

      Die Variante mit SIGUSR1 reduziert nur die Verzögerung vor einem Foto, nicht den Durchsatz. Wenn du mehr als 2 Bilder pro Sekunde haben willst, wirst du vermutlich auf Videos umsteigen müssen. Mit dieser Variante ist es nur möglich, sehr schnell, nachdem ein Trigger kam, eine Aufnahme zu machen, die Zeit zwischen zwei Aufnahmen wird damit nicht wesentlich kleiner… Hilft dir das weiter?

      Du hast bei der Variante übrigens noch bald das Problem, dass dein Arbeitsspeicher voll ist (/dev/shm liegt im RAM!). Es ist vermutlich besser, nur das letzte Bild zu speichern (also das %04d weglassen).

  7. Eine Frage was macht die foilgende Datei?

    https://bitbucket.org/niklas_rother/rasperry-pi-userland/raw/master/host_applications/linux/apps/raspicam/raspifastcamd_scripts

    raspifastcamd

    In der start_camd.sh steht ein ./raspifastcamd
    So richtig verstehe ich nicht was der Teil macht.

    Führe ich nacheinander die folgenden bash files aus:
    start_camd.sh
    do_caputure.sh
    stop_camd.sh

    kriege ich folgende Meldungen:

    Output will be written to /root/tmp_%d.jpg
    PID of raspifastcamd is 7788
    /home/pi/RaspiFastCamD/start_camd.sh: line 18: ./raspifastcamd: No such file or directory
    /home/pi/RaspiFastCamD/do_caputure.sh: line 7: unexpected EOF while looking for matching `“‚
    /home/pi/RaspiFastCamD/do_caputure.sh: line 12: syntax error: unexpected end of file
    Error: This pid file /tmp/raspifastcamd.pid does not exist, looks like raspifastcamd is not running

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.