Sunt unul dintre dezvoltatorii de sisteme de operare
Dacă tastați ceva de genul „OpenCV pe placa STM32” într-un motor de căutare, puteți găsi o mulțime de oameni care sunt interesați să folosească această bibliotecă pe plăcile STM32 sau alte microcontrolere.
Există mai multe videoclipuri care, judecând după titlu, ar trebui să demonstreze ce este necesar, dar de obicei (în toate videoclipurile pe care le-am văzut) pe placa STM32, doar primind o imagine de la cameră și afișând rezultatul pe ecran, și Procesarea imaginii în sine a fost făcută fie pe un computer obișnuit, fie pe plăci mai puternice (de exemplu, Raspberry Pi).
De ce este greu acest lucru?
Popularitatea interogărilor de căutare se explică prin faptul că OpenCV este cea mai populară bibliotecă de computer vision, ceea ce înseamnă că mai mulți dezvoltatori sunt familiarizați cu ea, iar capacitatea de a rula cod pregătit pentru desktop pe un microcontroler simplifică foarte mult procesul de dezvoltare. Dar de ce nu există încă rețete populare gata făcute pentru a rezolva această problemă?
Problema cu utilizarea OpenCV pe plăci mici este legată de două caracteristici:
- Dacă compilați biblioteca chiar și cu un set minim de module, pur și simplu nu se va potrivi în memoria flash a aceluiași STM32F7Discovery (chiar fără a lua în considerare sistemul de operare) din cauza codului foarte mare (mai mulți megaocteți de instrucțiuni)
- Biblioteca în sine este scrisă în C++, ceea ce înseamnă
- Avem nevoie de suport pentru rulare pozitivă (excepții etc.)
- Există puțin suport pentru LibC/Posix, care se găsesc de obicei în sistemele de operare pentru sistemele încorporate - aveți nevoie de o bibliotecă standard plus și o bibliotecă de șabloane STL standard (vector etc.)
Portare la Embbox
Ca de obicei, înainte de a porta orice program în sistemul de operare, este o idee bună să încercați să îl compilați în forma în care dezvoltatorii l-au propus. În cazul nostru, nu există probleme cu aceasta - codul sursă poate fi găsit la
Vestea bună este că OpenCV poate fi construit din cutie ca o bibliotecă statică, ceea ce face portarea mai ușoară. Asamblam biblioteca cu configurația standard și vedem cât spațiu ocupă. Fiecare modul este asamblat într-o bibliotecă separată.
> 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)
După cum puteți vedea din ultima linie, .bss și .data nu ocupă mult spațiu, dar codul este mai mare de 70 MiB. Este clar că dacă aceasta este legată static cu o anumită aplicație, va exista mai puțin cod.
Să încercăm să aruncăm cât mai multe module pentru a crea un exemplu minim (care, de exemplu, va afișa pur și simplu versiunea OpenCV), așa că haideți să aruncăm o privire cmake .. -LA
și dezactivați în opțiuni tot ce este dezactivat.
-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)
Pe de o parte, acesta este doar un modul de bibliotecă, pe de altă parte, acesta este fără optimizare de către compilator pentru dimensiunea codului (-Os
). ~3 MiB de cod este încă destul de mult, dar deja dă speranță pentru succes.
Rulează într-un emulator
Este mult mai ușor să depanați pe un emulator, așa că mai întâi ne vom asigura că biblioteca funcționează pe qemu. Am ales Integrator/CP ca platformă emulată, pentru că... în primul rând, este și ARM și, în al doilea rând, Embbox acceptă ieșirea grafică pentru această platformă.
Embox are un mecanism pentru construirea de biblioteci externe, cu ajutorul lui adăugăm OpenCV ca modul (trecând toate aceleași opțiuni pentru asamblarea „minimală” sub formă de biblioteci statice), după care adaug o aplicație simplă care arată astfel:
version.cpp:
#include <stdio.h>
#include <opencv2/core/utility.hpp>
int main() {
printf("OpenCV: %s", cv::getBuildInformation().c_str());
return 0;
}
Asamblam sistemul, îl rulăm și obținem rezultatul așteptat.
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 включены в сборку и т.п.>
Următorul pas este să rulați un exemplu, de preferință unul standard pe care dezvoltatorii înșiși îl oferă
Exemplul a trebuit să fie rescris puțin pentru a afișa imaginea rezultată direct în memoria tampon. A trebuit să fac asta pentru că... funcţie imshow()
poate desena imagini prin interfețele QT, GTK și Windows, care, desigur, nu vor fi cu siguranță în configurația pentru STM32. De fapt, QT poate fi rulat și pe STM32F7Discovery, dar acest lucru va fi discutat într-un alt articol :)
După ce ne dăm seama pe scurt în ce format este stocat rezultatul detectorului de margini, obținem o imagine.
Poza originala
Rezultat
Rulează pe STM32F7Discovery
32F746GDISCOVERY are mai multe secțiuni de memorie hardware pe care le putem folosi într-un fel sau altul
- 320 KiB RAM
- Memorie flash de 1 MiB pentru imagine
- 8 MiB SDRAM
- Unitate flash QSPI NAND de 16 MiB
- Slot pentru card MicroSD
Un card SD poate fi folosit pentru a stoca imagini, dar în contextul rulării unui exemplu minim, acest lucru nu este foarte util.
Afișajul are o rezoluție de 480x272, ceea ce înseamnă că memoria pentru framebuffer va fi de 522 de octeți cu o adâncime de 240 de biți, adică. aceasta este mai mare decât dimensiunea RAM, astfel încât framebuffer-ul și heap-ul (care vor fi necesare, printre altele, pentru ca OpenCV să stocheze date pentru imagini și structuri auxiliare) vor fi localizate în SDRAM, orice altceva (memorie pentru stive și alte sisteme). nevoi) va merge la RAM.
Dacă luăm configurația minimă pentru STM32F7Discovery (aruncăm întreaga rețea, toate comenzile, facem stive cât mai mici posibil, etc.) și adăugăm OpenCV cu exemple acolo, memoria necesară va fi următoarea:
text data bss dec hex filename
2876890 459208 312736 3648834 37ad42 build/base/bin/embox
Pentru cei care nu sunt foarte familiarizați cu ce secțiuni merg unde, permiteți-mi să vă explic: în .text
и .rodata
conține instrucțiuni și constante (în linii mari, date numai în citire), în .data
există date modificabile în .bss
există variabile „la zero”, care, totuși, au nevoie de spațiu (această secțiune va „merge” în RAM).
Vestea bună este că .data
/.bss
ar trebui să se potrivească, dar cu .text
Problema este că există doar 1 MiB de memorie pentru imagine. Poate fi aruncat .text
imaginea din exemplu și citiți-o, de exemplu, de pe un card SD în memorie la pornire, dar fruit.png cântărește aproximativ 330 KiB, așa că acest lucru nu va rezolva problema: majoritatea .text
constă tocmai din codul OpenCV.
În general, mai rămâne un singur lucru - încărcarea unei părți a codului pe o unitate flash QSPI (are un mod de operare special pentru maparea memoriei la magistrala de sistem, astfel încât procesorul să poată accesa aceste date direct). În acest caz, apare o problemă: în primul rând, memoria unei unități flash QSPI nu este disponibilă imediat după repornirea dispozitivului (modul mapat cu memorie trebuie inițializat separat) și, în al doilea rând, este imposibil să „flash” această memorie cu bootloader obișnuit.
Drept urmare, s-a decis să se conecteze tot codul în QSPI și să-l flash cu un bootloader auto-scris, care va primi binarul necesar prin TFTP.
Rezultat
Ideea de a porta această bibliotecă pe Embox a apărut în urmă cu aproximativ un an, dar din nou și din nou a fost amânată din diverse motive. Unul dintre ele este suportul pentru libstdc++ și biblioteca standard de șabloane. Problema suportului C++ în Embbox depășește sfera acestui articol, așa că aici voi spune doar că am reușit să obținem acest suport în măsura necesară pentru ca această bibliotecă să funcționeze :)
În cele din urmă, aceste probleme au fost depășite (cel puțin suficient pentru ca exemplul OpenCV să funcționeze), iar exemplul a rulat. Placa durează 40 de secunde lungi pentru a căuta granițele folosind filtrul Canny. Acest lucru, desigur, este prea lung (există considerații cu privire la modul de optimizare a acestei chestiuni; un articol separat ar putea fi scris despre asta dacă are succes).
Totuși, scopul intermediar a fost crearea unui prototip care să arate posibilitatea fundamentală de a rula OpenCV pe STM32, așa că acest obiectiv a fost atins, hai!
tl;dr: instrucțiuni pas cu pas
0: Descărcați sursele Embbox, de exemplu astfel:
git clone https://github.com/embox/embox && cd ./embox
1: Să începem prin a asambla un bootloader care va flash unitatea flash QSPI.
make confload-arm/stm32f7cube
Acum trebuie să configurați rețeaua, deoarece... Vom descărca imaginea prin TFTP. Pentru a seta adresele IP ale plăcii și ale gazdei, trebuie să editați conf/rootfs/network.
Exemplu de configurare:
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
— adresa gazdei de unde va fi încărcată imaginea, address
- adresa bordului.
După aceea, asamblam bootloader-ul:
make
2: Încărcarea normală a bootloader-ului (scuți jocul de cuvinte) pe placă - nimic specific aici, trebuie să o faci ca pentru orice altă aplicație pentru STM32F7Discovery. Dacă nu știi cum să faci asta, poți citi despre asta
3: Compilarea unei imagini cu o configurație pentru OpenCV.
make confload-platform/opencv/stm32f7discovery
make
4: Extragerea secțiunilor din ELF care trebuie scrise în QSPI în qspi.bin
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
Există un script în directorul conf care face acest lucru, așa că îl puteți rula
./conf/qspi_objcopy.sh # Нужный бинарник -- build/base/bin/qspi.bin
5: Folosind tftp, încărcați qspi.bin.bin pe o unitate flash QSPI. Pe gazdă, pentru a face acest lucru, trebuie să copiați qspi.bin în folderul rădăcină al serverului tftp (de obicei /srv/tftp/ sau /var/lib/tftpboot/; pachetele pentru serverul corespunzător sunt disponibile în cele mai populare distribuții , numit de obicei tftpd sau tftp-hpa, uneori trebuie să faci systemctl start tftpd.service
a începe).
# вариант для tftpd
sudo cp build/base/bin/qspi.bin /srv/tftp
# вариант для tftp-hpa
sudo cp build/base/bin/qspi.bin /var/lib/tftpboot
Pe Embbox (adică în bootloader) trebuie să rulați următoarea comandă (presupunem că serverul are o adresă 192.168.2.1):
embox> qspi_loader qspi.bin 192.168.2.1
6: Folosind o comandă goto
trebuie să „săriți” în memoria QSPI. Locația specifică va varia în funcție de modul în care imaginea este legată, puteți vizualiza această adresă cu comanda mem 0x90000000
(adresa de început se încadrează în al doilea cuvânt de 32 de biți al imaginii); va trebui, de asemenea, să setați steag-ul stivei -s
, adresa stivei este 0x90000000, exemplu:
embox>mem 0x90000000
0x90000000: 0x20023200 0x9000c27f 0x9000c275 0x9000c275
↑ ↑
это адрес это адрес
стэка первой
инструкции
embox>goto -i 0x9000c27f -s 0x20023200 # Флаг -i нужен чтобы запретить прерывания во время инициализации системы
< Начиная отсюда будет вывод не загрузчика, а образа с OpenCV >
7: Lansare
embox> edges 20
și bucurați-vă de căutarea de limite de 40 de secunde :)
Dacă ceva nu merge bine, scrieți o problemă la
Sursa: www.habr.com