Ես օպերացիոն համակարգի մշակողներից եմ
Եթե որոնման համակարգում մուտքագրեք «OpenCV on STM32 տախտակի» պես մի բան, կարող եք գտնել բավականին շատ մարդկանց, ովքեր հետաքրքրված են օգտագործել այս գրադարանը STM32 տախտակների կամ այլ միկրոկառավարիչների վրա:
Կան մի քանի տեսանյութեր, որոնք, դատելով անունից, պետք է ցույց տան, թե ինչ է անհրաժեշտ, բայց սովորաբար (բոլոր տեսանյութերում, որոնք ես տեսա) STM32 տախտակի վրա միայն պատկերն էր ստացվում տեսախցիկից և արդյունքը ցուցադրվում էր էկրանին, իսկ պատկերի մշակումն ինքնին կատարվել է կամ սովորական համակարգչի վրա, կամ ավելի հզոր տախտակների վրա (օրինակ՝ Raspberry Pi):
Ինչու՞ է դա դժվար:
Որոնման հարցումների հանրաճանաչությունը բացատրվում է նրանով, որ OpenCV-ն համակարգչային տեսողության ամենատարածված գրադարանն է, ինչը նշանակում է, որ ավելի շատ ծրագրավորողներ ծանոթ են դրան, իսկ աշխատասեղանի համար պատրաստի ծածկագիրը միկրոկոնտրոլերի վրա գործարկելու հնարավորությունը զգալիորեն հեշտացնում է զարգացման գործընթացը: Բայց ինչու դեռևս չկան այս խնդիրը լուծելու հայտնի պատրաստի բաղադրատոմսեր:
Փոքր շալերի վրա OpenCV-ի օգտագործման խնդիրը կապված է երկու առանձնահատկությունների հետ.
- Եթե գրադարանը կազմեք նույնիսկ մոդուլների նվազագույն հավաքածուով, ապա այն պարզապես չի տեղավորվի նույն STM32F7Discovery-ի ֆլեշ հիշողության մեջ (նույնիսկ առանց OS-ն հաշվի առնելու) շատ մեծ կոդի պատճառով (մի քանի մեգաբայթ հրահանգներ):
- Գրադարանը ինքնին գրված է C++-ով, ինչը նշանակում է
- Պահանջվում է աջակցություն դրական գործարկման համար (բացառություններ և այլն)
- LibC/Posix-ի փոքր աջակցություն, որը սովորաբար հայտնաբերվում է ներկառուցված համակարգերի ՕՀ-ում. Ձեզ անհրաժեշտ է ստանդարտ գումարած գրադարան և ստանդարտ STL ձևանմուշ գրադարան (վեկտոր և այլն):
Տեղափոխում Embox
Ինչպես միշտ, նախքան որևէ ծրագիր օպերացիոն համակարգ տեղափոխելը, լավ գաղափար է փորձել այն կառուցել այն ձևով, որով մշակողները նախատեսել են: Մեր դեպքում դրա հետ կապված խնդիրներ չկան. սկզբնաղբյուրը կարելի է գտնել այստեղ
Լավ նորությունն այն է, որ OpenCV-ն կարող է ստեղծվել որպես ստատիկ գրադարան, ինչը հեշտացնում է տեղափոխումը: Մենք հավաքում ենք գրադարան ստանդարտ կոնֆիգուրով և տեսնում ենք, թե որքան տեղ են դրանք զբաղեցնում: Յուրաքանչյուր մոդուլ հավաքվում է առանձին գրադարանում:
> 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)
Ինչպես տեսնում եք վերջին տողից, .bss-ը և .data-ն շատ տեղ չեն զբաղեցնում, բայց կոդը 70 ՄԲ-ից ավելի է: Հասկանալի է, որ եթե սա ստատիկորեն կապված է կոնկրետ հավելվածի հետ, կոդը կպակասի։
Եկեք փորձենք հնարավորինս շատ մոդուլներ դուրս գցել, որպեսզի նվազագույն օրինակ հավաքվի (որը, օրինակ, պարզապես թողարկի OpenCV տարբերակը), այնպես որ մենք նայում ենք. cmake .. -LA
և ընտրանքներում անջատիր այն ամենը, ինչ անջատվում է։
-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)
Մի կողմից, սա գրադարանի միայն մեկ մոդուլ է, մյուս կողմից, սա առանց կոդերի չափի կոմպիլյատորների օպտիմալացման (-Os
). ~3 ՄԲ կոդը դեռ բավականին շատ է, բայց արդեն հաջողության հույս է տալիս:
Գործարկեք էմուլյատորում
Էմուլյատորի վրա վրիպազերծելը շատ ավելի հեշտ է, ուստի նախ համոզվեք, որ գրադարանն աշխատում է qemu-ի վրա: Որպես ընդօրինակված հարթակ, ես ընտրեցի Integrator / CP, քանի որ նախ՝ այն նաև ARM է, և երկրորդ՝ Embox-ն աջակցում է այս հարթակի գրաֆիկական ելքին։
Embox-ն ունի արտաքին գրադարաններ կառուցելու մեխանիզմ, որի միջոցով մենք ավելացնում ենք OpenCV-ն որպես մոդուլ (անցնելով «նվազագույն» կառուցման բոլոր նույն տարբերակները՝ ստատիկ գրադարանների տեսքով), որից հետո ես ավելացնում եմ պարզ հավելված, որն ունի հետևյալ տեսքը.
version.cpp:
#include <stdio.h>
#include <opencv2/core/utility.hpp>
int main() {
printf("OpenCV: %s", cv::getBuildInformation().c_str());
return 0;
}
Մենք հավաքում ենք համակարգը, գործարկում ենք այն - ստանում ենք ակնկալվող արդյունքը:
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 включены в сборку и т.п.>
Հաջորդ քայլը որոշ օրինակներ գործարկելն է, ցանկալի է ստանդարտներից մեկը, որն առաջարկվում է հենց մշակողների կողմից:
Օրինակը պետք է մի փոքր վերաշարադրվեր, որպեսզի պատկերն արդյունքով ցուցադրվի անմիջապես շրջանակի բուֆերում: Ես ստիպված էի դա անել, քանի որ. ֆունկցիան imshow()
կարող է նկարներ նկարել QT, GTK և Windows ինտերֆեյսների միջոցով, որոնք, իհարկե, հաստատ չեն լինի STM32-ի կոնֆիգուրացիայի մեջ։ Փաստորեն, QT-ն կարող է գործարկվել նաև STM32F7Discovery-ով, բայց դա կքննարկվի մեկ այլ հոդվածում 🙂
Կարճ պարզաբանումից հետո, թե որ ձևաչափով է պահվում եզրային դետեկտորի արդյունքը, ստանում ենք պատկեր։
օրիգինալ նկար
Արդյունք
Աշխատում է STM32F7Discovery-ով
32F746GDISCOVERY-ում կան ապարատային հիշողության մի քանի բաժիններ, որոնք մենք կարող ենք օգտագործել այս կամ այն կերպ
- 320 ԿԲ RAM
- 1 ՄԲ ֆլեշ պատկերի համար
- 8 ՄԲ SDRAM
- 16 MiB QSPI NAND Flash
- microSD քարտի բնիկ
SD քարտը կարող է օգտագործվել պատկերներ պահելու համար, բայց նվազագույն օրինակ գործարկելու համատեքստում դա այնքան էլ օգտակար չէ:
Էկրանի թույլատրելիությունը 480×272 է, ինչը նշանակում է, որ շրջանակային բուֆերային հիշողությունը կլինի 522 բայթ 240 բիթ խորության վրա, այսինքն. սա ավելին է, քան RAM-ի չափը, ուստի framebuffer-ը և կույտը (որը կպահանջվի, ներառյալ OpenCV-ի համար, պատկերների և օժանդակ կառույցների տվյալները պահելու համար) կտեղակայվեն SDRAM-ում, մնացած ամեն ինչ (կույտերի հիշողություն և համակարգի այլ կարիքներ): ) կգնա RAM:
Եթե վերցնենք STM32F7Discovery-ի նվազագույն կոնֆիգուրգը (դուրս նետենք ամբողջ ցանցը, բոլոր հրամանները, հնարավորինս փոքրացնել կույտերը և այլն) և այնտեղ ավելացնել OpenCV օրինակներով, ապա պահանջվող հիշողությունը կլինի հետևյալը.
text data bss dec hex filename
2876890 459208 312736 3648834 37ad42 build/base/bin/embox
Նրանց համար, ովքեր այնքան էլ ծանոթ չեն, թե որ բաժինները որտեղ են գնում, ես կբացատրեմ .text
и .rodata
հրահանգները և հաստատունները (կոպիտ ասած, միայն կարդալու տվյալներ) գտնվում են .data
տվյալները փոփոխական են, .bss
կան «զրոյացված» փոփոխականներ, որոնց, այնուամենայնիվ, տեղ է պետք (այս բաժինը «կգնա» RAM):
Լավ նորությունն այն է, որ .data
/.bss
պետք է համապատասխանի, բայց հետ .text
Խնդիրն այն է, որ պատկերի համար կա ընդամենը 1 ՄԲ հիշողություն: Կարող է դուրս նետվել .text
նկարը օրինակից և կարդացեք այն, օրինակ, SD քարտից հիշողության մեջ գործարկման պահին, բայց fruits.png-ը կշռում է մոտ 330 ԿԲ, ուստի դա չի լուծի խնդիրը. .text
բաղկացած է OpenCV կոդից:
Մեծ հաշվով, մնում է միայն մեկ բան՝ կոդի մի մասը բեռնել QSPI ֆլեշի վրա (այն ունի աշխատանքի հատուկ ռեժիմ՝ հիշողությունը համակարգային ավտոբուսին քարտեզագրելու համար, որպեսզի պրոցեսորը կարողանա ուղղակիորեն մուտք գործել այս տվյալներ): Այս դեպքում խնդիր է առաջանում. նախ, QSPI ֆլեշ կրիչի հիշողությունը հասանելի չէ սարքը վերագործարկելուց անմիջապես հետո (դուք պետք է առանձին նախաստորագրեք հիշողության քարտեզագրված ռեժիմը), և երկրորդ, դուք չեք կարող «թարթել» այս հիշողությունը: ծանոթ bootloader.
Արդյունքում որոշվեց կապել բոլոր ծածկագրերը QSPI-ում և այն թարթել ինքնուրույն գրված բեռնիչով, որը կստանա անհրաժեշտ երկուականը TFTP-ի միջոցով։
Արդյունք
Այս գրադարանը Embox-ում տեղափոխելու գաղափարը ի հայտ եկավ մոտ մեկ տարի առաջ, բայց նորից ու նորից այն հետաձգվում էր տարբեր պատճառներով։ Դրանցից մեկը libstdc++-ի և ստանդարտ ձևանմուշների գրադարանի աջակցությունն է։ Embox-ում C++-ի աջակցության խնդիրը դուրս է այս հոդվածի շրջանակներից, ուստի այստեղ ես միայն կասեմ, որ մեզ հաջողվեց հասնել այս աջակցությանը ճիշտ չափով, որպեսզի այս գրադարանն աշխատի 🙂
Ի վերջո, այս խնդիրները հաղթահարվեցին (գոնե բավարար է OpenCV օրինակի համար), և օրինակը վազեց: Տախտակի համար պահանջվում է 40 երկար վայրկյան՝ Canny ֆիլտրի միջոցով սահմաններ որոնելու համար: Սա, իհարկե, չափազանց երկար է (կան նկատառումներ, թե ինչպես կարելի է օպտիմալացնել այս հարցը, հաջողության դեպքում հնարավոր կլինի առանձին հոդված գրել այս մասին):
Այնուամենայնիվ, միջանկյալ նպատակն էր ստեղծել նախատիպ, որը ցույց կտա OpenCV-ն STM32-ի վրա գործարկելու հիմնարար հնարավորությունը, համապատասխանաբար, այս նպատակը իրականացավ, հուր:
tl;dr: քայլ առ քայլ հրահանգներ
0: Ներբեռնեք Embox աղբյուրները, այսպես.
git clone https://github.com/embox/embox && cd ./embox
1. Սկսենք հավաքելով bootloader, որը «կթափի» QSPI ֆլեշ կրիչը:
make confload-arm/stm32f7cube
Այժմ դուք պետք է կարգավորեք ցանցը, քանի որ. Մենք կվերբեռնենք պատկերը TFTP-ի միջոցով: Տախտակի և հոսթի IP հասցեները սահմանելու համար հարկավոր է խմբագրել conf/rootfs/ցանցը։
Կազմաձևման օրինակ.
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
- հյուրընկալողի հասցեն, որտեղից կբեռնվի պատկերը, address
- խորհրդի հասցեն.
Դրանից հետո մենք հավաքում ենք bootloader.
make
2. Բեռնախցիկի սովորական բեռնումը (կներեք բառախաղի համար) տախտակի վրա. այստեղ որևէ կոնկրետ բան չկա, դուք պետք է դա անեք այնպես, ինչպես STM32F7Discovery-ի ցանկացած այլ հավելվածի համար: Եթե չգիտեք, թե ինչպես դա անել, կարող եք կարդալ դրա մասին
3. OpenCV-ի համար կոնֆիգուրով պատկերի կազմում:
make confload-platform/opencv/stm32f7discovery
make
4. Քաղվածք ELF բաժիններից, որը պետք է գրվի QSPI-ին 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
Conf գրացուցակում կա սկրիպտ, որն անում է դա, այնպես որ կարող եք գործարկել այն
./conf/qspi_objcopy.sh # Нужный бинарник -- build/base/bin/qspi.bin
5. Օգտագործելով tftp, ներբեռնեք qspi.bin.bin-ը QSPI ֆլեշ կրիչում: Հոսթում, դա անելու համար, qspi.bin-ը պատճենեք tftp սերվերի արմատային թղթապանակում (սովորաբար /srv/tftp/ կամ /var/lib/tftpboot/; համապատասխան սերվերի փաթեթները հասանելի են ամենատարածված բաշխումներում, սովորաբար կոչվում են. tftpd կամ tftp-hpa, երբեմն դուք պետք է անեք systemctl start tftpd.service
սկսել).
# вариант для tftpd
sudo cp build/base/bin/qspi.bin /srv/tftp
# вариант для tftp-hpa
sudo cp build/base/bin/qspi.bin /var/lib/tftpboot
Embox-ում (այսինքն՝ bootloader-ում), դուք պետք է կատարեք հետևյալ հրամանը (ենթադրում ենք, որ սերվերն ունի 192.168.2.1 հասցեն).
embox> qspi_loader qspi.bin 192.168.2.1
6: Հրամանով goto
դուք պետք է «ցատկեք» QSPI հիշողության մեջ: Կոնկրետ գտնվելու վայրը կտարբերվի՝ կախված նրանից, թե ինչպես է կապված պատկերը, դուք կարող եք տեսնել այս հասցեն հրամանով mem 0x90000000
(սկզբնական հասցեն տեղավորվում է պատկերի երկրորդ 32-բիթանոց բառի մեջ); Ձեզ անհրաժեշտ կլինի նաև դրոշակավորել կույտը -s
, դարակի հասցեն է 0x90000000, օրինակ՝
embox>mem 0x90000000
0x90000000: 0x20023200 0x9000c27f 0x9000c275 0x9000c275
↑ ↑
это адрес это адрес
стэка первой
инструкции
embox>goto -i 0x9000c27f -s 0x20023200 # Флаг -i нужен чтобы запретить прерывания во время инициализации системы
< Начиная отсюда будет вывод не загрузчика, а образа с OpenCV >
7: Գործարկում
embox> edges 20
և վայելեք 40 վայրկյան սահմանային որոնումը 🙂
Եթե ինչ-որ բան սխալ է, գրեք խնդիրը
Source: www.habr.com