Son un dos desenvolvedores do sistema operativo
Se escribes algo como "OpenCV na placa STM32" nun motor de busca, podes atopar bastantes persoas que estean interesadas en usar esta biblioteca en placas STM32 ou noutros microcontroladores.
Hai varios vídeos que, a xulgar polo nome, deberían demostrar o que se necesita, pero normalmente (en todos os vídeos que vin) na placa STM32, só se recibiu a imaxe da cámara e o resultado mostrábase na pantalla, e o propio procesamento da imaxe realizouse nun ordenador normal ou en placas máis potentes (por exemplo, Raspberry Pi).
Por que é difícil?
A popularidade das consultas de busca explícase polo feito de que OpenCV é a biblioteca de visión por ordenador máis popular, o que significa que máis desenvolvedores están familiarizados con ela e que a capacidade de executar código preparado para o escritorio nun microcontrolador simplifica moito o proceso de desenvolvemento. Pero por que aínda non hai receitas populares preparadas para resolver este problema?
O problema de usar OpenCV en chales pequenos está relacionado con dúas características:
- Se compilas a biblioteca mesmo cun conxunto mínimo de módulos, simplemente non caberá na memoria flash do mesmo STM32F7Discovery (mesmo sen ter en conta o SO) debido a un código moi grande (varios megabytes de instrucións)
- A propia biblioteca está escrita en C++, o que significa
- Necesita soporte para un tempo de execución positivo (excepcións, etc.)
- Pouco soporte para LibC/Posix, que normalmente se atopa no sistema operativo para sistemas integrados: necesitas unha biblioteca estándar plus e unha biblioteca de modelos STL estándar (vector, etc.)
Portando a Embox
Como é habitual, antes de portar calquera programa ao sistema operativo, é unha boa idea tentar construílo na forma na que o pretendían os desenvolvedores. No noso caso, non hai problemas con isto - o código fonte pódese atopar en
A boa noticia é que OpenCV pódese construír como unha biblioteca estática fóra da caixa, o que facilita a portabilidade. Recollemos unha biblioteca cunha configuración estándar e vemos canto espazo ocupan. Cada módulo está recollido nunha biblioteca separada.
> 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)
Como podes ver na última liña, .bss e .data non ocupan moito espazo, pero o código supera os 70 MiB. Está claro que se esta está ligada de forma estática a unha aplicación específica, o código será menor.
Tentemos tirar tantos módulos como sexa posible para que se reúna un exemplo mínimo (que, por exemplo, simplemente sairá a versión OpenCV), así que miramos cmake .. -LA
e desactiva nas opcións todo o que se apague.
-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)
Por unha banda, este é só un módulo da biblioteca, por outra banda, este é sen optimización do compilador para o tamaño do código (-Os
). ~3 MiB de código aínda son bastante, pero xa dá esperanza de éxito.
Executar no emulador
É moito máis doado depurar o emulador, polo que primeiro asegúrate de que a biblioteca funciona en qemu. Como plataforma emulada, escollín Integrator / CP, porque en primeiro lugar, tamén é ARM e, en segundo lugar, Embox admite a saída de gráficos para esta plataforma.
Embox ten un mecanismo para construír bibliotecas externas, usándoo engadimos OpenCV como módulo (pasando todas as mesmas opcións para a compilación "mínima" en forma de bibliotecas estáticas), despois engado unha aplicación sinxela que se ve así:
version.cpp:
#include <stdio.h>
#include <opencv2/core/utility.hpp>
int main() {
printf("OpenCV: %s", cv::getBuildInformation().c_str());
return 0;
}
Montamos o sistema, executámolo: obtemos a saída esperada.
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 включены в сборку и т.п.>
O seguinte paso é executar algún exemplo, preferiblemente un dos estándar que ofrecen os propios desenvolvedores.
O exemplo tivo que reescribirse lixeiramente para mostrar a imaxe co resultado directamente no búfer de fotogramas. Tiven que facelo, porque. función imshow()
pode debuxar imaxes a través das interfaces QT, GTK e Windows, que, por suposto, definitivamente non estarán na configuración de STM32. De feito, QT tamén se pode executar en STM32F7Discovery, pero isto falarase noutro artigo 🙂
Despois dunha pequena aclaración en que formato se almacena o resultado do detector de bordos, obtemos unha imaxe.
imaxe orixinal
Resultado
Executándose en STM32F7Discovery
No 32F746GDISCOVERY hai varias seccións de memoria de hardware que podemos usar dun xeito ou doutro
- 320 KiB de RAM
- Flash de 1 MiB para imaxe
- 8 MiB SDRAM
- Flash QSPI NAND de 16 MiB
- ranura para tarxetas microSD
Unha tarxeta SD pódese usar para almacenar imaxes, pero no contexto de executar un exemplo mínimo, isto non é moi útil.
A pantalla ten unha resolución de 480×272, o que significa que a memoria do framebuffer será de 522 bytes a unha profundidade de 240 bits, é dicir. isto é máis que o tamaño da RAM, polo que o framebuffer e o heap (que serán necesarios, incluso para OpenCV, para almacenar datos de imaxes e estruturas auxiliares) estarán situados en SDRAM, todo o demais (memoria para pilas e outras necesidades do sistema). ) irá á memoria RAM.
Se tomamos a configuración mínima para STM32F7Discovery (botar toda a rede, todos os comandos, facer as pilas o máis pequenas posible, etc.) e engadir alí OpenCV con exemplos, a memoria necesaria será a seguinte:
text data bss dec hex filename
2876890 459208 312736 3648834 37ad42 build/base/bin/embox
Para os que non estean moi familiarizados con que seccións van a onde, explicarei: en .text
и .rodata
as instrucións e as constantes (en grosso modo, datos de só lectura) atópanse .data
os datos son mutables, .bss
hai variables "anuladas", que, con todo, precisan dun lugar (esta sección "irá" á RAM).
A boa noticia é que .data
/.bss
debería caber, pero con .text
o problema é que só hai 1 MiB de memoria para a imaxe. Pódese botar fóra .text
a imaxe do exemplo e léaa, por exemplo, desde a tarxeta SD na memoria ao iniciar, pero fruits.png pesa uns 330 KiB, polo que isto non resolverá o problema: a maioría .text
consiste no código OpenCV.
En xeral, só queda unha cousa: cargar unha parte do código nun flash QSPI (ten un modo de operación especial para asignar a memoria ao bus do sistema, para que o procesador poida acceder a estes datos directamente). Neste caso, xorde un problema: en primeiro lugar, a memoria dunha unidade flash QSPI non está dispoñible inmediatamente despois de reiniciar o dispositivo (cómpre inicializar por separado o modo de asignación de memoria) e, en segundo lugar, non pode "flash" esta memoria con un cargador de arranque familiar.
Como resultado, decidiuse ligar todo o código en QSPI e flashear cun cargador de auto-escrito que recibirá o binario necesario a través de TFTP.
Resultado
A idea de portar esta biblioteca a Embox apareceu hai aproximadamente un ano, pero unha e outra vez aprazouse por diversos motivos. Un deles é o soporte para libstdc++ e a biblioteca de modelos estándar. O problema do soporte de C++ en Embox está fóra do alcance deste artigo, polo que aquí só direi que conseguimos este soporte na cantidade adecuada para que esta biblioteca funcione 🙂
Ao final, estes problemas superáronse (polo menos o suficiente para que funcione o exemplo de OpenCV) e o exemplo funcionou. O taboleiro tarda 40 segundos en buscar límites usando o filtro Canny. Isto, por suposto, é demasiado longo (hai consideracións sobre como optimizar este asunto, será posible escribir un artigo separado sobre isto en caso de éxito).
Non obstante, o obxectivo intermedio era crear un prototipo que mostrase a posibilidade fundamental de executar OpenCV en STM32, respectivamente, este obxectivo conseguiuse, ¡hurra!
tl;dr: instrucións paso a paso
0: Descarga as fontes de Embox, como esta:
git clone https://github.com/embox/embox && cd ./embox
1: Comecemos por montar un cargador de arranque que "flasheará" unha unidade flash QSPI.
make confload-arm/stm32f7cube
Agora cómpre configurar a rede, porque. Subiremos a imaxe a través de TFTP. Para configurar os enderezos IP do taboleiro e do host, cómpre editar o conf/rootfs/network.
Exemplo de configuración:
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
- enderezo de host desde onde se cargará a imaxe, address
- enderezo da xunta.
Despois diso, recollemos o cargador de arranque:
make
2: A carga habitual do cargador de arranque (perdón polo xogo de palabras) no taboleiro: non hai nada específico aquí, cómpre facelo como para calquera outra aplicación para STM32F7Discovery. Se non sabes como facelo, podes ler sobre el
3: Compilación dunha imaxe cunha configuración para OpenCV.
make confload-platform/opencv/stm32f7discovery
make
4: Extracto das seccións ELF que se escribirán en QSPI en 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
Hai un script no directorio conf que fai isto, polo que pode executalo
./conf/qspi_objcopy.sh # Нужный бинарник -- build/base/bin/qspi.bin
5: Usando tftp, descargue qspi.bin.bin nunha unidade flash QSPI. No servidor, para iso, copie qspi.bin no cartafol raíz do servidor tftp (normalmente /srv/tftp/ ou /var/lib/tftpboot/; os paquetes para o servidor correspondente están dispoñibles na maioría das distribucións populares, normalmente chamadas tftpd ou tftp-hpa, ás veces tes que facelo systemctl start tftpd.service
comezar).
# вариант для tftpd
sudo cp build/base/bin/qspi.bin /srv/tftp
# вариант для tftp-hpa
sudo cp build/base/bin/qspi.bin /var/lib/tftpboot
En Embox (é dicir, no cargador de arranque), cómpre executar o seguinte comando (supoñemos que o servidor ten o enderezo 192.168.2.1):
embox> qspi_loader qspi.bin 192.168.2.1
6: Con mando goto
cómpre "saltar" á memoria QSPI. A localización específica variará dependendo de como se vincule a imaxe, podes ver este enderezo co comando mem 0x90000000
(o enderezo de inicio encaixa na segunda palabra de 32 bits da imaxe); tamén terás que marcar a pila -s
, o enderezo da pila é 0x90000000, exemplo:
embox>mem 0x90000000
0x90000000: 0x20023200 0x9000c27f 0x9000c275 0x9000c275
↑ ↑
это адрес это адрес
стэка первой
инструкции
embox>goto -i 0x9000c27f -s 0x20023200 # Флаг -i нужен чтобы запретить прерывания во время инициализации системы
< Начиная отсюда будет вывод не загрузчика, а образа с OpenCV >
7: Lanzamento
embox> edges 20
e goza da busca de fronteiras de 40 segundos 🙂
Se algo sae mal, escriba un problema
Fonte: www.habr.com