Ja sam jedan od programera operativnog sustava , a u ovom ću članku objasniti kako sam uspio pokrenuti OpenCV na STM32746G ploči.
Ako u pretraživač upišete nešto poput „OpenCV na STM32 ploči“, možete pronaći priličan broj ljudi koji su zainteresirani za korištenje ove biblioteke na STM32 pločama ili drugim mikrokontrolerima.
Postoji nekoliko videa koji, sudeći po naslovu, trebaju pokazati ono što nam treba, ali obično (u svim videima koje sam vidio) na STM32 ploči se samo prikazivala slika s kamere i rezultat na ekranu, dok se sama obrada slike odvijala ili na običnom računalu ili na snažnijim pločama (na primjer, Raspberry Pi).
Zašto je to teško?
Popularnost pretraživačkih upita objašnjava se time što je OpenCV najpopularnija biblioteka za računalni vid, što znači da su mnogi programeri upoznati s njom, a mogućnost pokretanja gotovog koda za radnu površinu na mikrokontroleru značajno olakšava proces razvoja. No, zašto još uvijek nema nekih popularnih gotovih recepata za rješavanje ovog problema?
Problem korištenja OpenCV-a na malim pločama povezan je s dvije osobitosti:
- Ako se biblioteka kompajlira čak i s minimalnim skupom modula, jednostavno neće stati u flash memoriju iste STM32F7Discovery (čak i bez uzimanja u obzir OS) zbog jako velikog koda (nekoliko megabajta instrukcija).
- Sama biblioteka napisana je na C++, što znači
- Potrebna je podrška za plus runtime (izuzeci i sl.)
- Nedovoljna je podrška za LibC/Posix, koja obično postoji u operativnim sustavima za ugrađene sustave — potrebna je standardna biblioteka plus i standardna biblioteka predloška STL (vector i sl.).
Portiranje na Embox
Kao i obično, prije portiranja bilo kakvih programa u operativni sustav, dobro je pokušati ga sastaviti u obliku u kojem su ga zamislili programeri. U našem slučaju s ovim nema problema — izvorni kodovi mogu se pronaći na , biblioteka se sastavlja s GNU/Linux uobičajenim cmake-om.
Od dobrih vijesti — OpenCV se inače može sastaviti kao statička biblioteka, što olakšava portiranje. Sastavljamo biblioteku sa standardnom konfiguracijom i gledamo koliko prostora zauzimaju. Svaki modul se sastavlja u zasebnu biblioteku.
> 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)Kao što se može vidjeti iz posljednjeg retka, .bss i .data ne zauzimaju toliko puno prostora, ali kod ima više od 70 MiB. Jasno je da će, ako se to statički poveže s određenom aplikacijom, koda biti manje.
Pokušat ćemo ukloniti što je više modula moguće da bismo dobili minimalni primjer (koji, na primjer, jednostavno ispisuje verziju OpenCV), pa da vidimo. cmake .. -LA i isključujemo sve što se može isključiti u opcijama.
-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)S jedne strane, to je samo jedan modul biblioteke, ali s druge strane, to je bez optimizacije kompajlera za veličinu koda (-Os). ~3 MiB koda — to je još uvijek dovoljno puno, ali već pruža nadu za uspjeh.
Pokretanje u emulatoru
Debugging na emulatoru je puno jednostavnije, pa se najprije uvjeravamo da biblioteka radi na qemu. Kao emulirana platforma odabrao sam Integrator/CP, jer, prvo, to je također ARM, a drugo, Embox podržava ispis grafike za ovu platformu.
U Emboxu postoji mehanizam za izradu vanjskih biblioteka, njegovom pomoći dodajemo OpenCV kao modul (proslijedivši sve iste opcije za 'minimalnu' izradu u obliku statičkih biblioteka), a nakon toga dodajem najjednostavniju aplikaciju koja izgleda ovako:
version.cpp:
#include
#include
int main() {
printf("OpenCV: %s", cv::getBuildInformation().c_str());
return 0;
}Gradimo sustav, pokrećemo — dobijamo očekivani izlaz.
root@embox: /#opencv_version
OpenCV:
Opća konfiguracija za OpenCV 4.0.1 =====================================
Kontrola verzije: bd6927bdf-dirty
Platforma:
Vrijeme: 2019-06-21T10:02:18Z
Host: Linux 5.1.7-arch1-1-ARCH x86_64
Cilj: Opći arm-unknown-none
CMake: 3.14.5
CMake generator: Unix Makefiles
CMake alat za izgradnju: /usr/bin/make
Konfiguracija: Debug
CPU/HW značajke:
Osnovno:
zatraženo: DETECT
onemogućeno: VFPV3 NEON
C/C++:
Izgrađeno kao dinamičke biblioteke?: NESljedeći korak — pokrenuti neki primjer, najbolje neki standardni od onih koji nude sami programeri . Odabrao sam .
Primjer je trebalo malo prepraviti kako bi se slika s rezultatom direktno prikazivala u frame bufferu. To je bilo potrebno jer funkcija imshow() može prikazivati slike putem QT-a, GTK-a i Windows-a, kojih, naravno, neće biti u konfiguraciji za STM32. U stvari, QT se može pokrenuti na STM32F7Discovery, ali o tome će biti riječi u drugom članku 🙂
Nakon kratkog istraživanja u kojem formatu se čuva rezultat rada detektora rubova, dobivamo sliku.

Izvorna slika

Rezultat
Pokretanje na STM32F7Discovery
Na 32F746GDISCOVERY postoji nekoliko hardverskih dijelova memorije koje možemo koristiti
- 320KiB radne memorije
- 1MiB flash memorije za sliku
- 8MiB SDRAM-a
- 16MiB QSPI NAND-flash
- Utikač za microSD karticu
SD-karticu možemo koristiti za pohranu slika, ali u kontekstu pokretanja minimalnog primjera, to nije previše korisno.
Zaslon ima rezoluciju 480×272, što znači da će memorija za frame buffer iznositi 522 240 bajtova pri dubini od 32 bita, tj. to je više od veličine radne memorije, tako da ćemo frame buffer i hrpu (koja će biti potrebna, između ostalog, za OpenCV da drži podatke za slike i pomoćne strukture) smjestiti u SDRAM, dok će sve ostalo (memorija za stekove i ostale sistemske potrebe) otići u RAM.
Ako uzmemo minimalnu konfiguraciju za STM32F7Discovery (izbacimo cijelu mrežu, sve naredbe, napravimo stekove što manjim itd.) i dodamo OpenCV s primjerima, potrebna će memorija biti sljedeća:
text data bss dec hex filename
2876890 459208 312736 3648834 37ad42 build/base/bin/emboxZa one koji nisu upoznati s tim koja se odjeljenja gdje nalaze, objašnjavam: u .text i .rodata se nalaze upute i konstante (grobno rečeno, readonly-podaci), u .data se nalaze promjenjivi podaci, u .bss se nalaze «nula» varijable kojima je ipak potrebno mjesto (ovo odjeljenje će «otići» u RAM).
Dobra vijest je da .data/.bss moraju se smjestiti, ali .text problem - za sliku ima samo 1MiB memorije. Može se izbaciti iz .text primjera i čitati je, na primjer, s SD kartice u memoriju pri pokretanju, ali fruits.png teži otprilike 330KiB, tako da to ne rješava problem: veći dio .text se sastoji upravo od koda OpenCV.
U suštini, ostaje samo jedno - učitati dio koda na QSPI-flash (ima poseban način rada za mapiranje memorije na sistemsku sabirnicu, tako da procesor može izravno pristupiti tim podacima). Međutim, javlja se problem: prvo, QSPI-flash memorija nije dostupna odmah nakon ponovnog pokretanja uređaja (potrebno je zasebno inicijalizirati mode mapirane memorije), drugo, ovu memoriju nije moguće «programirati» uobičajenim bootloaderom.
Na kraju je odlučeno da se cijeli kod poveže u QSPI, a da se ugradi vlastitim bootloaderom koji će dobiti potrebni binarni fajl putem TFTP-a.
Rezultat
Ideja o portovanju ove biblioteke na Embox pojavila se još prije otprilike godinu dana, ali se to iz različitih razloga neprekidno odgađalo. Jedan od njih je podrška za libstdc++ i standardnu biblioteku predložaka. Problem podrške za C++ u Emboxu prelazi okvire ovog članka, stoga ću ovdje samo reći da smo uspjeli postići tu podršku u potrebnoj mjeri za rad ove biblioteke 🙂
Na kraju su ti problemi prevaziđeni (barem u dovoljnoj mjeri za rad primjera OpenCV), i primjer je pokrenut. 40 dugih sekundi traje pretraživanje granica filtriranjem Canny-ja. To je, naravno, predugo (postoje razmišljanja o tome kako to optimizirati, o tome bi se moglo napisati poseban članak u slučaju uspjeha).

Ipak, među ciljevima je bio razvoj prototipa koji će pokazati principijelnu mogućnost pokretanja OpenCV-a na STM32, i stoga je taj cilj postignut, hura!
tl;dr: upute korak po korak
0: Preuzimamo izvorne kodove Embox, na primjer ovako:
git clone https://github.com/embox/embox && cd ./embox1: Počinjemo sa izradom bootloadera koji će «programirati» QSPI-flash.
make confload-arm/stm32f7cubeSada je potrebno konfigurirati mrežu, jer ćemo učitati sliku putem TFTP-a. Da bismo postavili IP adrese za ploču i domaćina, potrebno je promijeniti datoteku conf/rootfs/network.
Primjer konfiguracije:
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:02gateway — adresa domaćina sa kojeg će se slika učitati, address — adresa ploče.
Nakon toga sastavljamo učitavač:
make2: Uobičajeno učitavanje učitavača (ispričavam se na igri riječi) na ploču — ovdje nema ništa specifično, potrebno je to učiniti kao za bilo koju drugu aplikaciju za STM32F7Discovery. Ako ne znate kako to učiniti, možete pročitati o tome .
3: Kompilacija slike s konfiguracijom za OpenCV.
make confload-platform/opencv/stm32f7discovery
make4: Izdvajanje iz ELF sekcija koje je potrebno zapisati u QSPI, u 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=.dataU direktoriju conf nalazi se skripta koja to radi, stoga je možete pokrenuti
./conf/qspi_objcopy.sh # Potreban binarni -- build/base/bin/qspi.bin5: Putem tftp učitajte qspi.bin na QSPI-flash. Na domaćinu je potrebno kopirati qspi.bin u root mapu tftp poslužitelja (to je obično /srv/tftp/ ili /var/lib/tftpboot/; paketi za odgovarajući poslužitelj postoje u većini popularnih distribucija, obično se zove tftpd ili tftp-hpa, ponekad je potrebno systemctl start tftpd.service za pokretanje).
# вариант для tftpd
sudo cp build/base/bin/qspi.bin /srv/tftp
# вариант для tftp-hpa
sudo cp build/base/bin/qspi.bin /var/lib/tftpbootNa Emboxu (tj. u učitavaču) potrebno je izvršiti ovakav naredbu (pretpostavljamo da je adresa poslužitelja 192.168.2.1):
embox> qspi_loader qspi.bin 192.168.2.16: Pomoću naredbe goto potrebno je 'skočiti' u QSPI memoriju. Konkretna lokacija će varirati ovisno o tome kako se slika spaja, taj adresa možete vidjeti naredbom mem 0x90000000 (adresa početka nalazi se u drugom 32-bitnom wordu slike); također, potrebno je postaviti stack s oznakom -s, adresa stacka se nalazi na adresi 0x90000000, primjer:
embox>mem 0x90000000
0x90000000: 0x20023200 0x9000c27f 0x9000c275 0x9000c275
↑ ↑
to je adresa to je adresa
stacka prve
instrukcije
embox>goto -i 0x9000c27f -s 0x20023200 # Oznaka -i je potrebna da se prekidanja onemoguće tijekom inicijalizacije sustava7: Pokrećemo
embox> edges 20i uživamo u 40-sekundnoj potrazi za rubovima 🙂
Ako nešto pođe po zlu — pišite issue u , ili na mailing listu embox-devel@googlegroups.com, ili u komentarima ovdje.
Izvor: habr.com
