草庐IT

在地平线X3上部署车牌识别系统

wx632bfd5ba3ff6 2023-03-28 原文

在地平线X3上部署车牌识别系统

一、 项目介绍

本项目属于AI达人创造营三期项目,主要探索如何将PaddleOCR训练的车牌识别模型部署在地平线X3的板子上,并实现实时推理。

项目地址:​https://aistudio.baidu.com/aistudio/projectdetail/4730476​

二、技术要点

PaddlePaddle之于Arm对应的平台为paddle-lite。paddle-lite主要为c语言,开发及部署难度较大,虽然支持fpga、npu计算加速,但是并不支持本次项目所用到的地平线X3的bpu。
故为实现本项目,需要将paddleocr -> onnx -> onnx bpu的转换路线。

三、项目展示

<iframe src="​​https://player.bilibili.com/player.html?aid=986567373&bvid=BV1U44y1f7mz&cid=859852040&page=1​​" scrolling="no" border="0" frameborder="no" framespacing="0" allowfullscreen="true" width="640" height="360"> </iframe>

四、实现教程

本项目涉及的技术栈非常的多,且越往后越难,笔者已实现在地平线使用CPU的推理,但FPS只有1帧。BPU方面虽然转换模型成功了,并貌似实现能推理。但是笔者能力有限和精力有限,并不清楚BPU的推理结果如何转换为可视化结果。
最终并未如笔者所期望,实现完整BPU加速程序。
接下来,笔者将会步步拆解,从易到难(到自己也不懂),还原实现过程。

一)amd64/arm CPU推理篇

本篇主要介绍如何将PaddleOCR模型转换为ONNX模型,转化后的模型均可直接部署于PC和板子上。

  1. PaddleOCR模型训练
    读者可以从此项目 ​​基于PaddleOCR实现的车牌识别​​ 中,获取车牌识别所需的数据集、训练教程,以及hubserving的部署。该教程产出的文本检测及文字识别模型,均为1 eporch,读者可继续优化,本篇不再复述。
  2. PaddleOCR转ONNX
    PaddleOCR已提供一个非常完整的转换教程,见PaddleOCR内的教程 ​​paddle2onnx​​。
    如果是体验arm的cpu,读者完全可以按官方教程,转换上面两个模型即可。后面的bpu部分则需要修改一些参数,才能正常转换为onnx bpu,详见后续说明。
  3. 推理代码
    PaddleOCR官方教程虽然提供了转换教程,但是它展示时用的推理代码,与paddlepaddle耦合在一起,无法直接使用在arm上。正当笔者头痛是要将predict_system.py代码重构,还是走回paddle-lite的老路时,使用了面向百度编程的大招,找到了全网唯一一篇paddleocr转onnx后的推理代码,见 ​​PaddleOCR转ONNX模型(推理部分)​​。
    值得注意,favorxin大佬的代码可能是有点旧,会将rec时的图片resize[3, 32, 100],会导致精度有所损失。本篇教程所用的模型,训练时shape为[3, 48, 320],读者可自行修改。
    至此,几乎不用自己写代码,就已完成大部分工作量,可用于pc、arm环境的demo,前期非常幸运(再次剧透,后期比较痛苦)。
  4. 完整程序代码
    因为增加了摄像头的功能,需读者自行下载data文件夹内的onnx_v1.zip到你本地运行。
unzip onnx_v1.zip
cd /home/aistudio/onnx_v1
pip install -r requirements.txt
# 0是摄像头的索引,不填时默认为0
python3 main.py 0
# PS: 按q退出,按空格暂停
实际上除了paddle2onnx的模型转换可以在ai studio上执行,其他代码都无法运行

二)地平线X3硬件篇

本篇开篇前,想吐槽一下笔者本次项目的霉运体质。
第一块的板子是Edgeboard,据助教说板子只支持1.8.5的模型,跟着PaddleOCR上的lite教程跑了一遍,果然不行,编译不了。
第二块板子是地平线X3,跟着教程刷机失败,接着让国晟大佬远程帮忙看看咋回事,确定板子是坏的……
第三块也是X3,终于正常了,刷机一气呵成。

  1. 前置准备
    得先说明一下,官方各资源下载点,并不是很统一,得自己仔细寻找,多逛论坛。
    如果只想简单体验,到onnx cpu部署的篇章即可。
    如果想尝试BPU开发,需下载20G+的Docker镜像和工具包。
    以下是本次项目需用到各种软件:
  • 串口驱动,3个
    藏在天工开物的工具包中,这个包6G+……建议从百度网盘里下载。
  • SD卡烧录工具 balenaEtcher
  • EMMC烧录工具hbupdate
    同样藏在天工开物的工具包中。
  • 旭日X3派系统镜像(桌面版)(内含EMMC Rom)
  • X3SDB Ubuntu 系统镜像(这个没Desktop,和上面的二选一即可)
  • 天工开物工具包,使用FileZilla匿名打开地址 ​​ftp://vrftp.horizon.ai/Open_Explorer_gcc_9.3.0/2.3.3/​
  • Docker交叉编译镜像,15G
  • 天工开物工具包,6G
  • 另一大佬准备好的百度网盘资源包 ,作用基本同上
  • 链接:s/1e8UISrjka2rqns21uv7tQg
  • 提取码:mfz5
  1. 烧录系统
  • 安装3个usb转串口驱动,重启
  • 烧录EMMC
    有三种方式可以烧录EMMC,分别为网络、Micro Usb,串口。当板子变砖头了,则需要使用串口方式,本篇以此为例。
    如下图接好串口转Usb线。

    如果驱动、数据线均无问题,会在设备管理器中看到设备COM口。
  • 打开MobaXterm,点击Session -> Serial,设置端口号,波特率921600,Flow control None,保存后双击打开。

    打开Session后,接上电源(如果接了就拔掉重接),此时会看串口输出系统开机信息。
    如果你已经装好了系统,最后会显示linux的命令行登录窗口。
    关闭Session,准备烧录EMMC。
  • EMMC Rom和SD卡系统的选择
    由于一些历史原因,笔者刷机刷了x3 sdb emmc + x3 pi Ubuntu desktop。读者也可尝试x3 pi 的emmc,省点下载时间。
  • 打开hbupdate,按下面红框修改设置,设好后点击开始升级。
    升级时,首先会提示你拔掉电源,拔掉后点击确定,会继续提示接上电源,接上后按确定即可。如无意外,几分钟后会提示安装成功。
  • 烧录SD卡
    此时你需要一张SD卡及读卡器。
    打开balenaEtcher.exe,选择x3 pi 镜像及SD,点击Flash。
  • 登录系统
    烧录完毕后,拔掉板子电源,插入SD,再次打开MoboXterm,接上电源,输入账号密码root/root。
  • 修改静态IP
    此时默认IP是192.168.1.10,可以按此方式修改IP。
# 修改设置文件
vim /etc/network/interfaces
# 保存后重启网络
/etc/init.d/networking restart


* 安装vnc(可选)

安装命令如下

# a安装软件包
apt install x11vnc
# 设置密码
# 设置后会有一个保存路径,如果读者用的不是root账号,下面脚本内容要相应修改。
x11vnc -storepasswd
# 创建开机服务
vim /lib/systemd/system/x11vnc.service
开机脚本

[Unit]
Description=Start x11vnc at startup.
After=multi-user.target
[Service]
Type=simple
ExecStart=/usr/bin/x11vnc -auth guess -forever -loop -capslock -nomodtweak -noxdamage -repeat -rfbauth /root/.vnc/passwd -rfbport 5900 -shared
[Install]
WantedBy=multi-user.target
启动服务

systemctl daemon-reload
systemctl enable x11vnc.service
systemctl start x11vnc.service
systemctl status x11vnc.service
操作完毕后,继续使用MOBAXterm,新建Session -> VNC

端口在开机脚本中,要一致。



需要注意,桌面的默认登录账号密码为sunrise/sunrise。

故,在这里输入某些命令,是需要加sudo的,不要和上面串口的root账号搞混。

三)ONNX实机测试

笔者以下命令是通过vnc打开板子里的终端操作的,不是串口。
先使用FileZilla上传onnx_v1的代码。
笔者用的是usb 摄像头,使用以下命令查找摄像头。

# 这里会有很多个设备,如果不确定是哪个,先拔掉输入一次命令,再插入输入一次
ls /dev/video*
# 改为你上传的路径
cd /home/sunrise/LPR/onnx_v1/ppocr_onnx
# 改为你的摄像头索引
python3 main.py 8
至此笔者能完整运行的部分,已说明完毕。
下面的BPU篇,笔者能力有限,并未实现一个完整的落地方案。

四)BPU加速推理篇

先重新说明一下转换路线,paddleocr -> onnx -> onnx bpu。
onnx转onnx bpu需要在天工开物的docker交叉编译环境中进行。
首先,强烈建议你读完这两篇文章中的bpu模型转换部分再来操作,否则可能会有点一头雾水。
[BPU部署教程] 一文带你轻松走出模型部署新手村
[BPU部署教程] 教你搞定YOLOV5部署 (版本_ 6.2)
在这两篇文章中,你需要搞懂:

  • 如何用docker搭建交叉编译环境。
    下面是笔者的命令。
# 笔者用的是Ubuntu虚拟机里运行docker,和上面文章里的不要一样
# 第一个-v参数,为了方便上传文件到宿主后,不用再复制进docker容器
# 第二个-v参数,是天工开物工具包的文件路径
sudo docker run -it --rm \
-v /home/walker/horizon:/horizon \
-v /home/walker/horizon/horizon_xj3_open_explorer_v2.3.3_20220727:/open_explorer \
hub.hobot.cc/aitools/ai_toolchain_centos_7_xj3:v2.3.3
  • bpu转换模型中yaml配置文件里的参数含义。
    下面是笔者的det和rec的yaml文件,参数含义放在后续结合实例再说。。
# convert_det.yaml
model_parameters:
onnx_model: 'model.onnx'
output_model_file_prefix: 'model'
march: 'bernoulli2'
input_parameters:
input_type_train: 'bgr'
input_layout_train: 'NCHW'
input_type_rt: 'nv12'
norm_type: 'data_scale'
scale_value: 0.003921568627451
input_layout_rt: 'NCHW'
calibration_parameters:
cal_data_dir: './calibration_data'
calibration_type: 'max'
max_percentile: 0.9999
compiler_parameters:
compile_mode: 'latency'
optimize_level: 'O3'
debug: False
core_num: 2
# convert_rec.yaml
model_parameters:
onnx_model: 'model.onnx'
output_model_file_prefix: 'model'
march: 'bernoulli2'
input_parameters:
input_type_train: 'bgr'
input_layout_train: 'NCHW'
input_type_rt: 'nv12'
norm_type: 'data_scale'
scale_value: 0.003921568627451
input_layout_rt: 'NCHW'
calibration_parameters:
cal_data_dir: './calibration_data'
calibration_type: 'max'
max_percentile: 0.9999
compiler_parameters:
compile_mode: 'latency'
optimize_level: 'O3'
debug: False
core_num: 2
其次,你需要重新回顾paddle2onnx的内容。因为paddleocr的例子,输入的sharp是动态的,需要将sharp修改为静态。
:exclamation:以下命令在paddlepaddle的环境里执行。

# 转化det模型
paddle2onnx --model_dir ./det/LPR_db_mv3_det/inference ^
--model_filename inference.pdmodel ^
--params_filename inference.pdiparams ^
--save_file ./det/LPR_db_mv3_det/det_onnx/model.onnx ^
--opset_version 10 ^
--input_shape_dict="{'x':[1,3,480,640]}" ^
--enable_onnx_checker True ^
--enable_dev_version False
# 转化rec模型
paddle2onnx --model_dir ./rec/LPR_svtr_mv1e_rec/inference ^
--model_filename inference.pdmodel ^
--params_filename inference.pdiparams ^
--save_file ./rec/LPR_svtr_mv1e_rec/rec_onnx/model.onnx ^
--opset_version 10 ^
--input_shape_dict="{'x':[1,3,48,320]}" ^
--enable_onnx_checker True ^
--enable_dev_version False
按PaddleOCR官方说明,改为静态精度会有所降低。
一些说明:

  • 不同版本的paddle2onnx中,支持的参数不一样,笔者用的是1.0.1
  • opset_version,必须小于11,版本过高无法转换为bpu模型。
  • det的input_shape_dict{'x':[1,3,480,640]},对应bpu转换yaml文件的NCHW模式,即{batchsize,颜色,高,宽}。其中高宽是笔者的摄像头分辨率,读者可按实际情况修改。
    <mark>摄像头分辨率是2K、4K什么的就按比例缩小吧,大图不现实,严重影响推理性能。</mark>
  • det的input_shape_dict{'x':[1,3,48,320]},这个是v3模型训练时输入的图像大小。
按笔者的理解,这两个sharp估计不会减少转换的精度。

:exclamation:以下命令在天工开物的环境操作(docker)执行。
由于笔者没有完整实现,就不放测试代码了,因为比较混乱可能会误导读者。这里以onnx_v1为例。

  • 先把onnx_v1上传到docker里的/horizon文件夹
  • 检查模型是否支持BPU加速。
# 先cd到output父目录再执行
hb_mapper checker --model-type onnx --march bernoulli2 --model output/det/LPR_db_mv3_det/det_onnx/model.onnx
hb_mapper checker --model-type onnx --march bernoulli2 --model output/rec/LPR_svtr_mv1e_rec/rec_onnx/model.onnx
最后没报错即可。

  • 转换onnx bpu模型
    把上面的yaml文件对应放到det_onnx和rec_onnx里。
    det的转换:
# 先cd到det_onnx
hb_mapper makertbin --config convert_det.yaml --model-type onnx
成功转接的截图,bin模型文件会输出在model_output里。


同理rec的转化:

# 先cd到rec_onnx
hb_mapper makertbin --config convert_det.yaml --model-type onnx
截图

:exclamation:以下命令在paddlepaddle的环境里执行

  • 上板测试
  • det测试(注意需要sudo权限)

  • rec测试
虽然无法确定结果正不正确,但从性能方面分析,det≈220ms,rec<100ms,预估性能从cpu的1帧每秒提升到2~3帧每秒吧。

五、结语

对笔者来说,是首次接触arm方面的编程,过程也是一波三折,借了3块板子才做到现在的程度。只可惜最后还是能力有限,没完成后处理部分,略感遗憾。
至于车牌识别这个立项,是笔者毕业那年所在公司的主营业务。当时笔者作为一个新手,并未接触甚至完全不清楚其中用到什么技术。现在能用自己的技术经验,做个技术路线不一样的车牌识别demo出来,也算是人生的一个回顾吧。

有关在地平线X3上部署车牌识别系统的更多相关文章

  1. 报告回顾丨模型进化狂飙,DetectGPT能否识别最新模型生成结果? - 2

    导读语言模型给我们的生产生活带来了极大便利,但同时不少人也利用他们从事作弊工作。如何规避这些难辨真伪的文字所产生的负面影响也成为一大难题。在3月9日智源Live第33期活动「DetectGPT:判断文本是否为机器生成的工具」中,主讲人Eric为我们讲解了DetectGPT工作背后的思路——一种基于概率曲率检测的用于检测模型生成文本的工具,它可以帮助我们更好地分辨文章的来源和可信度,对保护信息真实、防止欺诈等方面具有重要意义。本次报告主要围绕其功能,实现和效果等展开。(文末点击“阅读原文”,查看活动回放。)Ericmitchell斯坦福大学计算机系四年级博士生,由ChelseaFinn和Chri

  2. 电脑0x0000001A蓝屏错误怎么U盘重装系统教学 - 2

      电脑0x0000001A蓝屏错误怎么U盘重装系统教学分享。有用户电脑开机之后遇到了系统蓝屏的情况。系统蓝屏问题很多时候都是系统bug,只有通过重装系统来进行解决。那么蓝屏问题如何通过U盘重装新系统来解决呢?来看看以下的详细操作方法教学吧。  准备工作:  1、U盘一个(尽量使用8G以上的U盘)。  2、一台正常联网可使用的电脑。  3、ghost或ISO系统镜像文件(Win10系统下载_Win10专业版_windows10正式版下载-系统之家)。  4、在本页面下载U盘启动盘制作工具:系统之家U盘启动工具。  U盘启动盘制作步骤:  注意:制作期间,U盘会被格式化,因此U盘中的重要文件请注

  3. 【鸿蒙应用开发系列】- 获取系统设备信息以及版本API兼容调用方式 - 2

    在应用开发中,有时候我们需要获取系统的设备信息,用于数据上报和行为分析。那在鸿蒙系统中,我们应该怎么去获取设备的系统信息呢,比如说获取手机的系统版本号、手机的制造商、手机型号等数据。1、获取方式这里分为两种情况,一种是设备信息的获取,一种是系统信息的获取。1.1、获取设备信息获取设备信息,鸿蒙的SDK包为我们提供了DeviceInfo类,通过该类的一些静态方法,可以获取设备信息,DeviceInfo类的包路径为:ohos.system.DeviceInfo.具体的方法如下:ModifierandTypeMethodDescriptionstatic StringgetAbiList​()Obt

  4. [Vuforia]二.3D物体识别 - 2

    之前说过10之后的版本没有3dScan了,所以还是9.8的版本或者之前更早的版本。 3d物体扫描需要先下载扫描的APK进行扫面。首先要在手机上装一个扫描程序,扫描现实中的三维物体,然后上传高通官网,在下载成UnityPackage类型让Unity能够使用这个扫描程序可以从高通官网上进行下载,是一个安卓程序。点到Tools往下滑,找到VuforiaObjectScanner下载后解压数据线连接手机,将apk文件拷入手机安装然后刚才解压文件中的Media文件夹打开,两个PDF图打印第一张A4-ObjectScanningTarget.pdf,主要是用来辅助扫描的。好了,接下来就是扫描三维物体。将瓶

  5. ruby-on-rails - 在 heroku 的 .fonts 文件夹中包含自定义字体,似乎无法识别它们 - 2

    Heroku支持人员告诉我,为了在我的Web应用程序中使用自定义字体(未安装在系统中,您可以在bash控制台中使用fc-list查看已安装的字体)我必须部署一个包含所有字体的.fonts文件夹里面的字体。问题是我不知道该怎么做。我的意思是,我不知道文件名是否必须遵循heroku的任何特殊模式,或者我必须在我的代码中做一些事情来考虑这种字体,或者如果我将它包含在文件夹中它是自动的......事实是,我尝试以不同的方式更改字体的文件名,但根本没有使用该字体。为了提供更多详细信息,我们使用字体的过程是将PDF转换为图像,更具体地说,使用rghostgem。并且最终图像根本不使用自定义字体。在

  6. kvm虚拟机安装centos7基于ubuntu20.04系统 - 2

    需求:要创建虚拟机,就需要给他提供一个虚拟的磁盘,我们就在/opt目录下创建一个10G大小的raw格式的虚拟磁盘CentOS-7-x86_64.raw命令格式:qemu-imgcreate-f磁盘格式磁盘名称磁盘大小qemu-imgcreate-f磁盘格式-o?1.创建磁盘qemu-imgcreate-fraw/opt/CentOS-7-x86_64.raw10G执行效果#ls/opt/CentOS-7-x86_64.raw2.安装虚拟机使用virt-install命令,基于我们提供的系统镜像和虚拟磁盘来创建一个虚拟机,另外在创建虚拟机之前,提前打开vnc客户端,在创建虚拟机的时候,通过vnc

  7. ruby-on-rails - 没有这样的文件或目录 - 用 Mini Magick 识别 - 2

    在我让另一个人重做我的前端UI之前,我的Rails应用程序运行平稳。我已经尝试解决此错误3天了。这是错误:Nosuchfileordirectory-identifyExtractedsource(aroundline#59):575859606162@post=Post.find(params[:id])authorize@postif@post.update_attributes(post_params)flash[:notice]="Postwasupdated."redirect_to[@topic,@post]else{"utf8"=>"✓","_method"=>"patc

  8. ruby - 在没有基准或时间的情况下用 Ruby 测量用户时间或系统时间 - 2

    因为我现在正在做一些时间测量,我想知道是否可以在不使用Benchmark类或命令行实用程序time的情况下测量用户时间或系统时间。使用Time类只显示挂钟时间,而不显示系统和用户时间,但是我正在寻找具有相同灵active的解决方案,例如time=TimeUtility.now#somecodeuser,system,real=TimeUtility.now-time原因是我有点不喜欢Benchmark,因为它不能只返回数字(编辑:我错了-它可以。请参阅下面的答案。)。当然,我可以解析输出,但感觉不对。*NIX系统的time实用程序也应该可以解决我的问题,但我想知道是否已经在Ruby中实

  9. ruby - 以毫秒为单位获取当前系统时间 - 2

    在Ruby中,以毫秒为单位获取自纪元(1970)以来的当前系统时间的正确方法是什么?我试过了Time.now.to_i,好像不是我想要的结果。我需要结果显示毫秒并且使用long类型,而不是float或double。 最佳答案 (Time.now.to_f*1000).to_iTime.now.to_f显示包含十进制数字的时间。要获得毫秒数,只需将时间乘以1000。 关于ruby-以毫秒为单位获取当前系统时间,我们在StackOverflow上找到一个类似的问题:

  10. ruby-on-rails - 如何构建复杂的 Rails 系统 - 2

    关闭。这个问题需要更多focused.它目前不接受答案。想改进这个问题吗?更新问题,使其只关注一个问题editingthispost.关闭8年前。Improvethisquestion我们有以下(以及更多)系统,我们将数据从一个应用推送/拉取到另一个:托管CRM(InsideSales.com)Asterisk电话系统(内部)横幅广告系统(openx,我们托管)潜在客户生成系统(自行开发)电子商务商店(spree,我们托管)工作板(本土)一些工作网站抓取+入站工作提要电子邮件传送系统(如Mailchimp,自主开发)事件管理系统(如eventbrite,自主开发)仪表板系统(大量图表和

随机推荐