أنا أحد مطوري نظام التشغيل
إذا كتبت شيئًا مثل "OpenCV on STM32 board" في محرك بحث ، فيمكنك العثور على عدد غير قليل من الأشخاص المهتمين باستخدام هذه المكتبة على لوحات STM32 أو وحدات تحكم دقيقة أخرى.
هناك العديد من مقاطع الفيديو التي ، بناءً على الاسم ، يجب أن توضح ما هو مطلوب ، ولكن عادةً (في جميع مقاطع الفيديو التي رأيتها) على لوحة STM32 ، تم استلام الصورة فقط من الكاميرا وتم عرض النتيجة على الشاشة ، وتمت معالجة الصور نفسها إما على جهاز كمبيوتر عادي أو على لوحات أكثر قوة (على سبيل المثال ، Raspberry Pi).
لماذا هو صعب؟
تفسر شعبية استعلامات البحث من خلال حقيقة أن OpenCV هي مكتبة رؤية الكمبيوتر الأكثر شيوعًا ، مما يعني أن المزيد من المطورين على دراية بها ، والقدرة على تشغيل تعليمات برمجية جاهزة لسطح المكتب على متحكم دقيق يبسط إلى حد كبير عملية التطوير. ولكن لماذا لا توجد حتى الآن وصفات شعبية جاهزة لحل هذه المشكلة؟
ترتبط مشكلة استخدام OpenCV على الشالات الصغيرة بميزتين:
- إذا قمت بتجميع المكتبة حتى مع الحد الأدنى من مجموعة الوحدات النمطية ، فلن تتناسب ببساطة مع ذاكرة الفلاش الخاصة بنفس STM32F7Discovery (حتى بدون مراعاة نظام التشغيل) بسبب رمز كبير جدًا (عدة ميغا بايت من التعليمات)
- المكتبة نفسها مكتوبة بلغة 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 و. البيانات مساحة كبيرة ، لكن الرمز أكثر من 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 MiB من الكود لا يزال كثيرًا ، ولكنه يعطي الأمل بالفعل في النجاح.
تشغيل في المحاكي
من الأسهل بكثير تصحيح الأخطاء على المحاكي ، لذا تأكد أولاً من أن المكتبة تعمل على 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 كيلو بايت رام
- 1 ميجا بايت فلاش للصورة
- 8 ميغا بايت SDRAM
- فلاش 16 ميجا بايت QSPI NAND
- منفذ كارت الذاكرة الصغيرة
يمكن استخدام بطاقة SD لتخزين الصور ، ولكن في سياق تشغيل مثال بسيط ، فإن هذا ليس مفيدًا جدًا.
تبلغ دقة الشاشة 480 × 272 ، مما يعني أن ذاكرة التخزين المؤقت للإطار ستكون 522،240 بايت على عمق 32 بت ، أي. هذا أكبر من حجم ذاكرة الوصول العشوائي ، لذا فإن ذاكرة التخزين المؤقت (frameebuffer) والكومة (التي ستكون مطلوبة ، بما في ذلك OpenCV ، لتخزين البيانات للصور والهياكل المساعدة) ستكون موجودة في SDRAM ، وكل شيء آخر (ذاكرة للأكوام واحتياجات النظام الأخرى ) إلى ذاكرة الوصول العشوائي.
إذا أخذنا الحد الأدنى من التكوين لـ STM32F7Discovery (تخلص من الشبكة بالكامل ، وجميع الأوامر ، وجعل الأكوام صغيرة قدر الإمكان ، وما إلى ذلك) وأضفنا OpenCV مع أمثلة هناك ، فستكون الذاكرة المطلوبة كما يلي:
text data bss dec hex filename
2876890 459208 312736 3648834 37ad42 build/base/bin/embox
بالنسبة لأولئك الذين ليسوا على دراية بالأقسام التي تذهب إلى أين ، سأشرح: in .text
и .rodata
التعليمات والثوابت (تقريبًا ، بيانات للقراءة فقط) تكمن في .data
البيانات قابلة للتغيير ، .bss
هناك متغيرات "nulled" والتي ، مع ذلك ، تحتاج إلى مكان (هذا القسم سوف "ينتقل" إلى RAM).
الخبر السار هو ذلك .data
/.bss
يجب أن تتناسب ، ولكن مع .text
المشكلة هي أنه لا يوجد سوى 1 ميغا بايت من الذاكرة للصورة. يمكن التخلص منها .text
الصورة من المثال وقراءتها ، على سبيل المثال ، من بطاقة SD إلى الذاكرة عند بدء التشغيل ، ولكن تزن الفواكه. png حوالي 330 كيلوبايت ، لذلك لن يحل هذا المشكلة: معظم .text
يتكون من كود OpenCV.
بشكل عام ، لم يتبق سوى شيء واحد - تحميل جزء من الكود على فلاش QSPI (له وضع تشغيل خاص لتعيين الذاكرة إلى ناقل النظام ، بحيث يمكن للمعالج الوصول إلى هذه البيانات مباشرة). في هذه الحالة ، تظهر مشكلة: أولاً ، لا تتوفر ذاكرة محرك فلاش QSPI فور إعادة تشغيل الجهاز (تحتاج إلى تهيئة الوضع المعين للذاكرة بشكل منفصل) ، وثانيًا ، لا يمكنك "وميض" هذه الذاكرة باستخدام محمل مألوف.
نتيجة لذلك ، تقرر ربط كل الكود في QSPI ، ووميضه بمحمل مكتوب ذاتيًا يتلقى الملف الثنائي المطلوب عبر TFTP.
نتيجة
ظهرت فكرة نقل هذه المكتبة إلى Embox منذ حوالي عام ، ولكن تم تأجيلها مرارًا وتكرارًا لأسباب مختلفة. أحدها هو دعم libstdc ++ ومكتبة القوالب القياسية. تعد مشكلة دعم C ++ في Embox خارج نطاق هذه المقالة ، لذلك سأقول هنا فقط أننا تمكنا من تحقيق هذا الدعم بالقدر المناسب لهذه المكتبة للعمل 🙂
في النهاية ، تم التغلب على هذه المشكلات (على الأقل بما يكفي لكي يعمل مثال OpenCV) ، ونفذ المثال. تستغرق اللوحة 40 ثانية طويلة للبحث عن الحدود باستخدام مرشح Canny. هذا ، بالطبع ، طويل جدًا (هناك اعتبارات حول كيفية تحسين هذا الأمر ، سيكون من الممكن كتابة مقال منفصل حول هذا الأمر في حالة النجاح).
ومع ذلك ، كان الهدف الوسيط هو إنشاء نموذج أولي يُظهر الإمكانية الأساسية لتشغيل OpenCV على STM32 ، على التوالي ، تم تحقيق هذا الهدف ، رائع!
TL ؛ د: تعليمات خطوة بخطوة
0: تنزيل مصادر Embox ، مثل هذا:
git clone https://github.com/embox/embox && cd ./embox
1: لنبدأ بتجميع محمل إقلاع يعمل على "وميض" محرك أقراص فلاش QSPI.
make confload-arm/stm32f7cube
أنت الآن بحاجة إلى تكوين الشبكة ، لأن. سنقوم بتحميل الصورة عبر TFTP. لتعيين عناوين IP للوحة والمضيف ، تحتاج إلى تحرير conf / rootfs / network.
مثال على التكوين:
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
- عنوان المجلس.
بعد ذلك ، نجمع محمل الإقلاع:
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 (على سبيل المثال في أداة تحميل التشغيل) ، تحتاج إلى تنفيذ الأمر التالي (نفترض أن الخادم لديه العنوان 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 ثانية 🙂
إذا حدث خطأ ما - اكتب مشكلة في
المصدر: www.habr.com