开发板:主人,别折磨我了😣😣😣
我:闭嘴🤐,你也别折磨我了😡😡😡
我做了什么 我在开发板上基于腾讯ncnn框架实现了目标检测计算。
ncnn是腾讯的一个神经网络前向计算框架。
ncnn is a high-performance neural network inference computing framework
optimized for mobile platforms. ncnn is deeply considerate about deployment and uses on mobile phones from the beginning of design. ncnn does not have third party dependencies. It is cross-platform, and runs faster than all known open source frameworks on mobile phone cpu. Developers can easily deploy deep learning algorithm models to the mobile platform by using efficient ncnn implementation, create intelligent APPs, and bring the artificial intelligence to your fingertips. ncnn is currently being used in many Tencent applications, such as QQ, Qzone, WeChat, Pitu and so on.
ncnn 是一个为手机端极致优化的高性能神经网络前向计算框架
。ncnn 从设计之初深刻考虑手机端的部署和使用。无第三方依赖,跨平台,手机端 cpu 的速度快于目前所有已知的开源框架。基于 ncnn,开发者能够将深度学习算法轻松移植到手机端高效执行,开发出人工智能 APP,将 AI 带到你的指尖。ncnn 目前已在腾讯多款应用中使用,如 QQ,Qzone,微信,天天P图等。
https://github.com/Tencent/ncnn
为什么选择做这个 接触了不少单片机的应用案例,在大部分案例中,单片机在做“控制”类的工作,即:控制外围设备以完成指定任务。也有一小部分案例中,单片机在做“计算”类的工作,例如:我在STM32单片机上跑神经网络算法 。
前者更多地是专业电子工程师 的工作,我作为软件开发工程师 ,对后者更感兴趣。
通常情况下,为了让物联网设备更智能,我们需要利用互联网和云计算。物联网设备负责采集并上传数据,这些数据通过互联网传递到云计算中心,云计算中心计算后再将计算结果通过互联网返回给物联网设备。
将机器学习引入单片机后,我们既能赋予物联网设备更高的智能,又能减少数据上传,从而更好地保护用户隐私。同时也减少对互联网的依赖和数据在互联网上传输带来的延迟。
谷歌的TensorFlow Lite就支持在单片机上运行机器学习模型。
我是如何做的 将开发板连接到电脑 我使用串口调试功能实现两者之间的连接。
这一步中使用到了下面的器件:
开发板
杜邦线
micro-usb转 TTL 模块
usb to micro-usb转接线
typec to usb转接器
我使用MobaXterm 软件来进行串口调试。
从ncnn的repo中下载ncnn.tar.gz,然后上传到开发板并对其进行解压 1 2 tar zxf ncnn.tar.gz ls ncnn ncnn.tar.gz
安装依赖包 1 2 sudo apt update sudo apt install -y build-essential git cmake libprotobuf-dev protobuf-compiler libopencv-dev
编译ncnn 1 2 3 4 mkdir build cd build cmake -DCMAKE_TOOLCHAIN_FILE=../toolchains/aarch64-linux-gnu.toolchain.cmake -DNCNN_SIMPLEOCV=ON -DNCNN_BUILD_EXAMPLES=ON .. make -j$(nproc)
编译完成后,项目的目录结构如下所示。其中,build
目录下的内容为编译结果。后续步骤中会使用到build
目录下的nanodet
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 tree -L 2 . ├── benchmark │ ├── alexnet.param │ ├── benchncnn.cpp │ ├── blazeface.param │ ├── CMakeLists.txt │ ├── efficientnet_b0.param │ ├── efficientnetv2_b0.param │ ├── googlenet_int8.param │ ├── googlenet.param │ ├── mnasnet.param │ ├── mobilenet_int8.param │ ├── mobilenet.param │ ├── mobilenet_ssd_int8.param │ ├── mobilenet_ssd.param │ ├── mobilenet_v2.param │ ├── mobilenetv2_yolov3.param │ ├── mobilenet_v3.param │ ├── mobilenet_yolo.param │ ├── nanodet_m.param │ ├── proxylessnasnet.param │ ├── README.md │ ├── regnety_400m.param │ ├── resnet18_int8.param │ ├── resnet18.param │ ├── resnet50_int8.param │ ├── resnet50.param │ ├── shufflenet.param │ ├── shufflenet_v2.param │ ├── squeezenet_int8.param │ ├── squeezenet.param │ ├── squeezenet_ssd_int8.param │ ├── squeezenet_ssd.param │ ├── vgg16_int8.param │ ├── vgg16.param │ ├── yolo-fastest-1.1.param │ ├── yolo-fastestv2.param │ └── yolov4-tiny.param ├── build │ ├── benchmark │ ├── CMakeCache.txt │ ├── CMakeFiles │ ├── cmake_install.cmake │ ├── examples │ ├── Makefile │ └── src ├── build-android.cmd ├── build.sh ├── cmake │ ├── ncnn_add_layer.cmake │ ├── ncnn_add_shader.cmake │ ├── ncnnConfig.cmake.in │ ├── ncnn_generate_arm82_source.cmake │ ├── ncnn_generate_avx512_source.cmake │ ├── ncnn_generate_avx_source.cmake │ ├── ncnn_generate_fma_source.cmake │ ├── ncnn_generate_msa_source.cmake │ ├── ncnn_generate_rvv_source.cmake │ ├── ncnn_generate_shader_comp_header.cmake │ ├── ncnn_generate_shader_spv_header.cmake │ └── run_test.cmake ├── CMakeLists.txt ├── codeformat.sh ├── CONTRIBUTING.md ├── docs │ ├── application-with-ncnn-inside.md │ ├── benchmark │ ├── developer-guide │ ├── faq.md │ ├── Home.md │ ├── how-to-build │ └── how-to-use-and-FAQ ├── examples │ ├── CMakeLists.txt │ ├── fasterrcnn.cpp │ ├── mobilenetssd.cpp │ ├── mobilenetv2ssdlite.cpp │ ├── mobilenetv3ssdlite.cpp │ ├── nanodet.cpp │ ├── nanodetplus_pnnx.cpp │ ├── p2pnet.cpp │ ├── peleenetssd_seg.cpp │ ├── retinaface.cpp │ ├── rfcn.cpp │ ├── rvm.cpp │ ├── scrfd.cpp │ ├── scrfd_crowdhuman.cpp │ ├── shufflenetv2.cpp │ ├── simplepose.cpp │ ├── squeezencnn │ ├── squeezenet_c_api.cpp │ ├── squeezenet.cpp │ ├── squeezenetssd.cpp │ ├── squeezenet_v1.1.bin │ ├── squeezenet_v1.1.caffemodel │ ├── squeezenet_v1.1.param │ ├── squeezenet_v1.1.param.bin │ ├── squeezenet_v1.1.prototxt │ ├── synset_words.txt │ ├── yolact.cpp │ ├── yolov2.cpp │ ├── yolov3.cpp │ ├── yolov4.cpp │ ├── yolov5.cpp │ ├── yolov5_pnnx.cpp │ └── yolox.cpp ├── glslang │ ├── Android.mk │ ├── BUILD.bazel │ ├── BUILD.gn │ ├── build_info.h.tmpl │ ├── build_info.py │ ├── build_overrides │ ├── CHANGES.md │ ├── ChooseMSVCCRT.cmake │ ├── CMakeLists.txt │ ├── CODE_OF_CONDUCT.md │ ├── _config.yml │ ├── DEPS │ ├── External │ ├── glslang │ ├── gtests │ ├── hlsl │ ├── known_good.json │ ├── known_good_khr.json │ ├── kokoro │ ├── license-checker.cfg │ ├── LICENSE.txt │ ├── ndk_test │ ├── OGLCompilersDLL │ ├── parse_version.cmake │ ├── README.md │ ├── README-spirv-remap.txt │ ├── SPIRV │ ├── StandAlone │ ├── standalone.gclient │ ├── Test │ ├── update_glslang_sources.py │ └── WORKSPACE ├── images │ ├── 128-ncnn.png │ ├── 16-ncnn.png │ ├── 256-ncnn.png │ ├── 32-ncnn.png │ └── 64-ncnn.png ├── Info.plist ├── LICENSE.txt ├── package.sh ├── pyproject.toml ├── python │ ├── CMakeLists.txt │ ├── examples │ ├── ncnn │ ├── pybind11 │ ├── README.md │ ├── requirements.txt │ ├── setup.py.i │ ├── src │ └── tests ├── README.md ├── setup.py ├── src │ ├── allocator.cpp │ ├── allocator.h │ ├── benchmark.cpp │ ├── benchmark.h │ ├── blob.cpp │ ├── blob.h │ ├── c_api.cpp │ ├── c_api.h │ ├── CMakeLists.txt │ ├── command.cpp │ ├── command.h │ ├── convert_ycbcr.comp │ ├── cpu.cpp │ ├── cpu.h │ ├── datareader.cpp │ ├── datareader.h │ ├── gpu.cpp │ ├── gpu.h │ ├── layer │ ├── layer.cpp │ ├── layer_declaration.h.in │ ├── layer.h │ ├── layer_registry.h.in │ ├── layer_shader_registry.h.in │ ├── layer_shader_spv_data.h.in │ ├── layer_shader_type_enum.h.in │ ├── layer_shader_type.h │ ├── layer_type_enum.h.in │ ├── layer_type.h │ ├── mat.cpp │ ├── mat.h │ ├── mat_pixel_affine.cpp │ ├── mat_pixel_android.cpp │ ├── mat_pixel.cpp │ ├── mat_pixel_drawing.cpp │ ├── mat_pixel_drawing_font.h │ ├── mat_pixel_drawing_font.png │ ├── mat_pixel_resize.cpp │ ├── mat_pixel_rotate.cpp │ ├── modelbin.cpp │ ├── modelbin.h │ ├── net.cpp │ ├── net.h │ ├── option.cpp │ ├── option.h │ ├── paramdict.cpp │ ├── paramdict.h │ ├── pipelinecache.cpp │ ├── pipelinecache.h │ ├── pipeline.cpp │ ├── pipeline.h │ ├── platform.h.in │ ├── simpleocv.cpp │ ├── simpleocv.h │ ├── simpleomp.cpp │ ├── simpleomp.h │ ├── simplestl.cpp │ ├── simplestl.h │ ├── stb_image.h │ ├── stb_image_write.h │ └── vulkan_header_fix.h ├── tests │ ├── CMakeLists.txt │ ├── prng.h │ ├── test_absval.cpp │ ├── test_batchnorm.cpp │ ├── test_bias.cpp │ ├── test_binaryop.cpp │ ├── test_c_api.cpp │ ├── test_cast.cpp │ ├── test_clip.cpp │ ├── test_command.cpp │ ├── test_concat.cpp │ ├── test_convolution1d.cpp │ ├── test_convolution3d.cpp │ ├── test_convolution.cpp │ ├── test_convolutiondepthwise1d.cpp │ ├── test_convolutiondepthwise3d.cpp │ ├── test_convolutiondepthwise.cpp │ ├── test_cpu.cpp │ ├── test_crop.cpp │ ├── test_deconvolution1d.cpp │ ├── test_deconvolution3d.cpp │ ├── test_deconvolution.cpp │ ├── test_deconvolutiondepthwise1d.cpp │ ├── test_deconvolutiondepthwise3d.cpp │ ├── test_deconvolutiondepthwise.cpp │ ├── test_deepcopy.cpp │ ├── test_dequantize.cpp │ ├── test_dropout.cpp │ ├── test_eltwise.cpp │ ├── test_elu.cpp │ ├── test_expanddims.cpp │ ├── test_flatten.cpp │ ├── test_gelu.cpp │ ├── test_gemm.cpp │ ├── test_groupnorm.cpp │ ├── test_gru.cpp │ ├── test_hardsigmoid.cpp │ ├── test_hardswish.cpp │ ├── test_innerproduct.cpp │ ├── test_instancenorm.cpp │ ├── test_interp.cpp │ ├── test_layernorm.cpp │ ├── test_lrn.cpp │ ├── test_lstm.cpp │ ├── test_matmul.cpp │ ├── test_mat_pixel_affine.cpp │ ├── test_mat_pixel.cpp │ ├── test_mat_pixel_drawing.cpp │ ├── test_mat_pixel_resize.cpp │ ├── test_mat_pixel_rotate.cpp │ ├── test_memorydata.cpp │ ├── test_mish.cpp │ ├── test_multiheadattention.cpp │ ├── test_noop.cpp │ ├── test_normalize.cpp │ ├── test_packing.cpp │ ├── test_padding.cpp │ ├── test_permute.cpp │ ├── test_pixelshuffle.cpp │ ├── test_pooling1d.cpp │ ├── test_pooling3d.cpp │ ├── test_pooling.cpp │ ├── test_prelu.cpp │ ├── test_priorbox.cpp │ ├── test_quantize.cpp │ ├── test_reduction.cpp │ ├── test_relu.cpp │ ├── test_reorg.cpp │ ├── test_requantize.cpp │ ├── test_reshape.cpp │ ├── test_rnn.cpp │ ├── test_roialign.cpp │ ├── test_roipooling.cpp │ ├── test_scale.cpp │ ├── test_selu.cpp │ ├── test_shufflechannel.cpp │ ├── test_sigmoid.cpp │ ├── test_slice.cpp │ ├── test_softmax.cpp │ ├── test_softplus.cpp │ ├── test_squeeze.cpp │ ├── test_squeezenet.cpp │ ├── test_swish.cpp │ ├── test_tanh.cpp │ ├── test_tile.cpp │ ├── test_unaryop.cpp │ ├── testutil.h │ └── test_yolov3detectionoutput.cpp ├── toolchains │ ├── aarch64-linux-gnu-c.toolchain.cmake │ ├── aarch64-linux-gnu.toolchain.cmake │ ├── arm-linux-gnueabi-c.toolchain.cmake │ ├── arm-linux-gnueabihf.toolchain.cmake │ ├── arm-linux-gnueabihf-vfpv3-d16.toolchain.cmake │ ├── arm-linux-gnueabi.toolchain.cmake │ ├── c906.toolchain.cmake │ ├── c906-v222.toolchain.cmake │ ├── c906-v223.toolchain.cmake │ ├── himix100.toolchain.cmake │ ├── himix200.toolchain.cmake │ ├── hisiv300.toolchain.cmake │ ├── hisiv500.toolchain.cmake │ ├── hisiv600.toolchain.cmake │ ├── host-c.clang.toolchain.cmake │ ├── host-c.gcc.toolchain.cmake │ ├── host.clang-m32.toolchain.cmake │ ├── host.gcc-c++03.toolchain.cmake │ ├── host.gcc-m32.toolchain.cmake │ ├── host.gcc.toolchain.cmake │ ├── iossimxc.toolchain.cmake │ ├── iossimxc-x64.toolchain.cmake │ ├── ios.toolchain.cmake │ ├── iosxc-arm64.toolchain.cmake │ ├── iosxc.toolchain.cmake │ ├── jetson.toolchain.cmake │ ├── loongarch64-linux-gnu.toolchain.cmake │ ├── mips32r2-linux-gnu.toolchain.cmake │ ├── mips64el-linux-gnuabi64.toolchain.cmake │ ├── mipsel-linux-gnu.toolchain.cmake │ ├── mipsisa32r6el-linux-gnu.toolchain.cmake │ ├── mipsisa64r6el-linux-gnuabi64.toolchain.cmake │ ├── mips-mti-linux-gnu.toolchain.cmake │ ├── pi3.toolchain.cmake │ ├── powerpc64le-linux-gnu.toolchain.cmake │ ├── riscv32-unknown-elf.toolchain.cmake │ ├── riscv64-linux-gnu.toolchain.cmake │ ├── riscv64-unknown-elf.toolchain.cmake │ ├── riscv64-unknown-linux-gnu.toolchain.cmake │ └── v831.toolchain.cmake └── tools ├── caffe ├── CMakeLists.txt ├── darknet ├── keras ├── mlir ├── modelwriter.h ├── mxnet ├── ncnn2mem.cpp ├── ncnnmerge.cpp ├── ncnnoptimize.cpp ├── onnx ├── plugin ├── pnnx ├── pytorch ├── quantize └── tensorflow 49 directories, 320 files
使用NanoDet进行目标检测 NanoDet 是一个速度超快和轻量级的移动端 Anchor-free 目标检测模型。
NanoDet 的具体位置为ncnn/build/examples/nanodet
。
新建一个 nanodet_demo 的文件夹,并将编译生成的 nanodet 可执行程序复制到 nanodet_demo 文件夹中
1 2 mkdir nanodet_demo cp ncnn/build/examples/nanodet nanodet_demo/
将nanodet模型文件 (nanodet_m.bin
、nanodet_m.param
)和用于检测的图片上传到 nanodet_demo 文件夹中。
使用 nanodet 进行目标检测:
检测完成的结果保存在名为 image.png 中。
展望 本开发版的接口是比较丰富的,后续会考虑通过GPIO口连接各种传感器,实现对周边环境的感知。
本开发版具备无线通信模块,可以较为方便地和其他设备联动,后面有机会的话会利用这方面的优势去做一些创新性的设计。
据我了解的情况,现在嵌入式领域有两个趋势:
使用更高级的语言(Python、JavaScript等)进行嵌入式开发,充分利用高级语言面向对象等优秀特性简化开发工作,提升代码质量。
引入嵌入式操作系统来进行资源管理。
在这样的趋势下,软件开发工程师、算法工程师也能参与到嵌入式开发中了。我会多去了解这些趋势,并将自己过往所学应用到该产品的后续升级中。
总结 这次课程设计让我受益良多。因为我并非专业的电子工程师,所以对于电子领域更多只是有个宏观的了解,但借助这次课程设计,我对电路、IO接口、gcc编译工具链等有了更加深入的认识。
在具体操作过程中,经常搞得焦头烂额,根据网上的教程一步一步走但就是复现不出结果。但好在最终做出了一个能跑起来的版本。整个过程让我对下面这句话有了更加深刻的认识:想要让OS为你提供服务,你得先伺候好OS。
总之,本次课程设计中,我综合运用了嵌入式和过往所学知识(C/C++、计算机组成原理、计算机网络、Git等),做出了个能跑起来的版本,这个过程是辛苦的,但也是收获颇丰的。