Ich bin einer der Entwickler des Betriebssystems
Wenn Sie in einer Suchmaschine etwas wie „OpenCV auf STM32-Board“ eingeben, finden Sie etliche Leute, die daran interessiert sind, diese Bibliothek auf STM32-Boards oder anderen Mikrocontrollern zu verwenden.
Es gibt mehrere Videos, die, dem Namen nach zu urteilen, zeigen sollen, was benötigt wird, aber normalerweise (in allen Videos, die ich gesehen habe) wurde auf der STM32-Karte nur das Bild von der Kamera empfangen und das Ergebnis auf dem Bildschirm angezeigt. und die Bildverarbeitung selbst erfolgte entweder auf einem normalen Computer oder auf leistungsstärkeren Platinen (z. B. Raspberry Pi).
Warum ist es schwierig?
Die Beliebtheit von Suchanfragen erklärt sich aus der Tatsache, dass OpenCV die beliebteste Computer-Vision-Bibliothek ist, was bedeutet, dass mehr Entwickler damit vertraut sind und die Möglichkeit, Desktop-fähigen Code auf einem Mikrocontroller auszuführen, den Entwicklungsprozess erheblich vereinfacht. Doch warum gibt es noch immer keine gängigen Fertigrezepte zur Lösung dieses Problems?
Das Problem bei der Verwendung von OpenCV auf kleinen Schals hängt mit zwei Merkmalen zusammen:
- Wenn Sie die Bibliothek selbst mit einem minimalen Satz an Modulen kompilieren, passt sie aufgrund eines sehr großen Codes (mehrere Megabyte an Anweisungen) einfach nicht in den Flash-Speicher desselben STM32F7Discovery (auch ohne Berücksichtigung des Betriebssystems).
- Die Bibliothek selbst ist in C++ geschrieben, d.h
- Benötigen Sie Unterstützung für positive Laufzeit (Ausnahmen usw.)
- Geringe Unterstützung für LibC/Posix, das normalerweise in Betriebssystemen für eingebettete Systeme zu finden ist – Sie benötigen eine Standard-Plus-Bibliothek und eine Standard-STL-Vorlagenbibliothek (Vektor usw.)
Portierung nach Embox
Bevor Sie ein Programm auf das Betriebssystem portieren, sollten Sie wie üblich versuchen, es in der von den Entwicklern vorgesehenen Form zu erstellen. In unserem Fall gibt es damit keine Probleme – den Quellcode finden Sie unter
Die gute Nachricht ist, dass OpenCV sofort als statische Bibliothek erstellt werden kann, was die Portierung erleichtert. Wir sammeln eine Bibliothek mit einer Standardkonfiguration und sehen, wie viel Platz sie beanspruchen. Jedes Modul ist in einer separaten Bibliothek gesammelt.
> size lib/*so --totals
text data bss dec hex filename
1945822 15431 960 1962213 1df0e5 lib/libopencv_calib3d.so
17081885 170312 25640 17277837 107a38d lib/libopencv_core.so
10928229 137640 20192 11086061 a928ed lib/libopencv_dnn.so
842311 25680 1968 869959 d4647 lib/libopencv_features2d.so
423660 8552 184 432396 6990c lib/libopencv_flann.so
8034733 54872 1416 8091021 7b758d lib/libopencv_gapi.so
90741 3452 304 94497 17121 lib/libopencv_highgui.so
6338414 53152 968 6392534 618ad6 lib/libopencv_imgcodecs.so
21323564 155912 652056 22131532 151b34c lib/libopencv_imgproc.so
724323 12176 376 736875 b3e6b lib/libopencv_ml.so
429036 6864 464 436364 6a88c lib/libopencv_objdetect.so
6866973 50176 1064 6918213 699045 lib/libopencv_photo.so
698531 13640 160 712331 ade8b lib/libopencv_stitching.so
466295 6688 168 473151 7383f lib/libopencv_video.so
315858 6972 11576 334406 51a46 lib/libopencv_videoio.so
76510375 721519 717496 77949390 4a569ce (TOTALS)
Wie Sie der letzten Zeile entnehmen können, beanspruchen .bss und .data nicht viel Platz, aber der Code ist mehr als 70 MiB groß. Es ist klar, dass der Code weniger wird, wenn dies statisch an eine bestimmte Anwendung gebunden ist.
Versuchen wir, so viele Module wie möglich wegzuwerfen, damit ein minimales Beispiel entsteht (das beispielsweise einfach die OpenCV-Version ausgibt), also schauen wir cmake .. -LA
und schalten Sie in den Optionen alles aus, was sich ausschaltet.
-DBUILD_opencv_java_bindings_generator=OFF
-DBUILD_opencv_stitching=OFF
-DWITH_PROTOBUF=OFF
-DWITH_PTHREADS_PF=OFF
-DWITH_QUIRC=OFF
-DWITH_TIFF=OFF
-DWITH_V4L=OFF
-DWITH_VTK=OFF
-DWITH_WEBP=OFF
<...>
> size lib/libopencv_core.a --totals
text data bss dec hex filename
3317069 36425 17987 3371481 3371d9 (TOTALS)
Einerseits ist dies nur ein Modul der Bibliothek, andererseits ist dies ohne Compiler-Optimierung für die Codegröße (-Os
). ~3 MiB Code sind immer noch ziemlich viel, lassen aber bereits auf Erfolg hoffen.
Führen Sie den Emulator aus
Das Debuggen auf dem Emulator ist viel einfacher. Stellen Sie daher zunächst sicher, dass die Bibliothek auf qemu funktioniert. Als emulierte Plattform habe ich mich für Integrator/CP entschieden, weil Erstens ist es auch ARM und zweitens unterstützt Embox die Grafikausgabe für diese Plattform.
Embox verfügt über einen Mechanismus zum Erstellen externer Bibliotheken. Mit diesem fügen wir OpenCV als Modul hinzu (übergeben alle gleichen Optionen für den „minimalen“ Build in Form statischer Bibliotheken). Anschließend füge ich eine einfache Anwendung hinzu, die so aussieht:
version.cpp:
#include <stdio.h>
#include <opencv2/core/utility.hpp>
int main() {
printf("OpenCV: %s", cv::getBuildInformation().c_str());
return 0;
}
Wir bauen das System zusammen, führen es aus – wir erhalten die erwartete Ausgabe.
root@embox:/#opencv_version
OpenCV:
General configuration for OpenCV 4.0.1 =====================================
Version control: bd6927bdf-dirty
Platform:
Timestamp: 2019-06-21T10:02:18Z
Host: Linux 5.1.7-arch1-1-ARCH x86_64
Target: Generic arm-unknown-none
CMake: 3.14.5
CMake generator: Unix Makefiles
CMake build tool: /usr/bin/make
Configuration: Debug
CPU/HW features:
Baseline:
requested: DETECT
disabled: VFPV3 NEON
C/C++:
Built as dynamic libs?: NO
< Дальше идут прочие параметры сборки -- с какими флагами компилировалось,
какие модули OpenCV включены в сборку и т.п.>
Der nächste Schritt besteht darin, ein Beispiel auszuführen, vorzugsweise eines der Standardbeispiele, die von den Entwicklern selbst angeboten werden.
Das Beispiel musste leicht umgeschrieben werden, um das Bild mit dem Ergebnis direkt im Framebuffer anzuzeigen. Ich musste das tun, weil. Funktion imshow()
kann Bilder über die QT-, GTK- und Windows-Schnittstellen zeichnen, was natürlich definitiv nicht in der Konfiguration für STM32 enthalten sein wird. Tatsächlich kann QT auch auf STM32F7Discovery ausgeführt werden, dies wird jedoch in einem anderen Artikel besprochen 🙂
Nach einer kurzen Klärung, in welchem Format das Ergebnis des Kantendetektors gespeichert wird, erhalten wir ein Bild.
Originalbild
Erlebe die Kraft effektiver Ergebnisse
Läuft auf STM32F7Discovery
Auf 32F746GDISCOVERY gibt es mehrere Hardware-Speicherabschnitte, die wir auf die eine oder andere Weise nutzen können
- 320 KB RAM
- 1 MB Flash für das Bild
- 8 MB SDRAM
- 16 MB QSPI NAND-Flash
- microSD-Kartensteckplatz
Zum Speichern von Bildern kann eine SD-Karte verwendet werden, im Kontext der Ausführung eines Minimalbeispiels ist dies jedoch nicht sehr nützlich.
Das Display hat eine Auflösung von 480×272, was bedeutet, dass der Framebuffer-Speicher 522 Byte bei einer Tiefe von 240 Bit beträgt, also Dies ist mehr als die Größe des RAM, daher befinden sich der Framebuffer und der Heap (der, auch für OpenCV, zum Speichern von Daten für Bilder und Hilfsstrukturen benötigt wird) im SDRAM, alles andere (Speicher für Stacks und andere Systemanforderungen). ) wird in den RAM verschoben.
Wenn wir die Mindestkonfiguration für STM32F7Discovery nehmen (das gesamte Netzwerk, alle Befehle wegwerfen, Stacks so klein wie möglich machen usw.) und dort OpenCV mit Beispielen hinzufügen, ist der erforderliche Speicher wie folgt:
text data bss dec hex filename
2876890 459208 312736 3648834 37ad42 build/base/bin/embox
Für diejenigen, die nicht genau wissen, welche Abschnitte wohin gehören, erkläre ich: in .text
и .rodata
Anweisungen und Konstanten (grob gesagt schreibgeschützte Daten) liegen darin .data
die Daten sind veränderlich, .bss
Es gibt „nullierte“ Variablen, die dennoch einen Platz benötigen (dieser Abschnitt „geht“ in den RAM).
Die gute Nachricht ist, dass .data
/.bss
sollte passen, aber mit .text
Das Problem ist, dass nur 1 MB Speicher für das Bild vorhanden ist. Kann weggeworfen werden .text
Laden Sie das Bild aus dem Beispiel herunter und lesen Sie es beispielsweise beim Start von der SD-Karte in den Speicher, aber Fruits.png wiegt etwa 330 KiB, sodass dies das Problem nicht lösen wird: die meisten .text
besteht aus dem OpenCV-Code.
Im Großen und Ganzen bleibt nur noch eines: einen Teil des Codes auf einen QSPI-Flash zu laden (er verfügt über einen speziellen Betriebsmodus zum Zuordnen von Speicher zum Systembus, sodass der Prozessor direkt auf diese Daten zugreifen kann). In diesem Fall entsteht ein Problem: Erstens ist der Speicher eines QSPI-Flash-Laufwerks nicht sofort nach dem Neustart des Geräts verfügbar (Sie müssen den Memory-Mapping-Modus separat initialisieren), und zweitens können Sie diesen Speicher nicht mit „flashen“. ein bekannter Bootloader.
Aus diesem Grund wurde beschlossen, den gesamten Code in QSPI zu verknüpfen und ihn mit einem selbst geschriebenen Loader zu flashen, der die erforderliche Binärdatei über TFTP empfängt.
Erlebe die Kraft effektiver Ergebnisse
Die Idee, diese Bibliothek auf Embox zu portieren, entstand vor etwa einem Jahr, wurde jedoch aus verschiedenen Gründen immer wieder verschoben. Eine davon ist die Unterstützung von libstdc++ und der Standardvorlagenbibliothek. Das Problem der C++-Unterstützung in Embox geht über den Rahmen dieses Artikels hinaus, daher möchte ich hier nur sagen, dass es uns gelungen ist, diese Unterstützung im richtigen Umfang zu erreichen, damit diese Bibliothek funktioniert 🙂
Am Ende wurden diese Probleme überwunden (zumindest so weit, dass das OpenCV-Beispiel funktionierte) und das Beispiel lief. Es dauert 40 lange Sekunden, bis das Board mithilfe des Canny-Filters nach Grenzen sucht. Das ist natürlich zu lang (es gibt Überlegungen, wie man diese Angelegenheit optimieren kann, im Erfolgsfall wird es möglich sein, einen separaten Artikel darüber zu schreiben).
Das Zwischenziel bestand jedoch darin, einen Prototyp zu erstellen, der die grundsätzliche Möglichkeit der Ausführung von OpenCV auf STM32 aufzeigt bzw. dieses Ziel wurde erreicht, Hurra!
tl;dr: Schritt-für-Schritt-Anleitung
0: Laden Sie Embox-Quellen wie folgt herunter:
git clone https://github.com/embox/embox && cd ./embox
1: Beginnen wir mit der Zusammenstellung eines Bootloaders, der ein QSPI-Flash-Laufwerk „flasht“.
make confload-arm/stm32f7cube
Jetzt müssen Sie das Netzwerk konfigurieren, denn. Wir werden das Bild über TFTP hochladen. Um die Board- und Host-IP-Adressen festzulegen, müssen Sie conf/rootfs/network bearbeiten.
Konfigurationsbeispiel:
iface eth0 inet static
address 192.168.2.2
netmask 255.255.255.0
gateway 192.168.2.1
hwaddress aa:bb:cc:dd:ee:02
gateway
- Hostadresse, von der das Bild geladen wird, address
- Adresse des Vorstandes.
Danach sammeln wir den Bootloader:
make
2: Das übliche Laden des Bootloaders (sorry für das Wortspiel) auf die Platine – hier gibt es nichts Spezifisches, Sie müssen es wie bei jeder anderen Anwendung für STM32F7Discovery tun. Wenn Sie nicht wissen, wie es geht, können Sie es nachlesen
3: Kompilieren eines Bildes mit einer Konfiguration für OpenCV.
make confload-platform/opencv/stm32f7discovery
make
4: Auszug aus ELF-Abschnitten, die in QSPI in qspi.bin geschrieben werden sollen
arm-none-eabi-objcopy -O binary build/base/bin/embox build/base/bin/qspi.bin
--only-section=.text --only-section=.rodata
--only-section='.ARM.ex*'
--only-section=.data
Im conf-Verzeichnis gibt es ein Skript, das dies erledigt, sodass Sie es ausführen können
./conf/qspi_objcopy.sh # Нужный бинарник -- build/base/bin/qspi.bin
5: Laden Sie qspi.bin.bin über TFTP auf ein QSPI-Flash-Laufwerk herunter. Kopieren Sie dazu auf dem Host qspi.bin in den Stammordner des TFTP-Servers (normalerweise /srv/tftp/ oder /var/lib/tftpboot/; Pakete für den entsprechenden Server sind in den gängigsten Distributionen verfügbar, meist genannt tftpd oder tftp-hpa, manchmal muss man es tun systemctl start tftpd.service
anfangen).
# вариант для tftpd
sudo cp build/base/bin/qspi.bin /srv/tftp
# вариант для tftp-hpa
sudo cp build/base/bin/qspi.bin /var/lib/tftpboot
Auf Embox (also im Bootloader) müssen Sie folgenden Befehl ausführen (wir gehen davon aus, dass der Server die Adresse 192.168.2.1 hat):
embox> qspi_loader qspi.bin 192.168.2.1
6: Mit Befehl goto
Sie müssen in den QSPI-Speicher „springen“. Der genaue Speicherort hängt davon ab, wie das Bild verlinkt ist. Sie können diese Adresse mit dem Befehl anzeigen mem 0x90000000
(die Startadresse passt in das zweite 32-Bit-Wort des Bildes); Sie müssen auch den Stapel kennzeichnen -s
, die Stack-Adresse liegt bei 0x90000000, Beispiel:
embox>mem 0x90000000
0x90000000: 0x20023200 0x9000c27f 0x9000c275 0x9000c275
↑ ↑
это адрес это адрес
стэка первой
инструкции
embox>goto -i 0x9000c27f -s 0x20023200 # Флаг -i нужен чтобы запретить прерывания во время инициализации системы
< Начиная отсюда будет вывод не загрузчика, а образа с OpenCV >
7: Starten
embox> edges 20
und genieße die 40-sekündige Grenzsuche 🙂
Wenn etwas schief geht, schreiben Sie ein Problem
Source: habr.com