嵌入式AI快速入门课程-K510篇 (第三篇 环境搭建及开发板操作)

2024-08-22 16:24:03 浏览数 (2)

1.配置VMware使用桥接网卡

1.1 vmware设置

​ 使用桥接模式下,虚拟主机与真实主要在VMnet0构成的局域网内通信,同时通过真实主机中的网关与外网通信,即可实现Ubuntu与开发板进行文件传输。

设置网络适配器为桥接模式。

图1.1 进入VMware虚拟机设置

点击网络适配器 NAT,将网络连接方式选择为桥接模式。

图1.2 修改网络适配器

1.2 虚拟网络编辑器设置

在开始菜单搜索“虚拟网络编辑器”,点击“以管理员身份运行”打开虚拟网络编辑器:

图1.3 更爱虚拟网络编辑器

参考图 1.4,点击“VMnet0”,选择“桥接模式”,在桥接模式下的“已桥接至”下拉框中,选中你电脑中实际使用WIFI网卡,最后点击确定即可完成vmware配置。

注意:必须是“VMnet0”,如果没有“VMnet0”可以点击“添加网络”。

图1.4 桥接实际的wifi网卡

2.安装软件

2.2 安装 Windows 软件

“<开发板配套资料>2_DongshanPI-Vision_配套工具”

在网盘资料包的上述目录中,可以得到一系列的安装软件,建议全部安装。有如下软件:

表 2‑1 Windows中需要安装的软件

软件名

说明

VMwareWorkstation

虚拟机软件,安装时需要用到管理员权限

SD Card Formatter

SD卡格式化工具

MobaXterm

串口工具、远程登录工具

Filezilla

文件传输工具,在Windows和Ubuntu之间传输文件

Source insight

阅读、编写源码的工具,即装即用;推荐初学者使用

2.3 使用MobaXterm远程登录Ubuntu

先确认Ubuntu的IP,在Ubuntu终端执行ifconfig命令确定桥接网卡IP(注意:这个IP过一段时间会发生变化,那就使用新IP重新连接),如图 2.3所示:

图 2.3 ifconfig查看网络IP

安装、运行MobaXterm,如下建立Session:

图 2.4 建立ssh

按图 2.4操作后,在MobaXterm左侧就可以看到图 2.5这项,双击它就可以登录Ubuntu(第1次登录时会提示你输入密码,密码是ubuntu),然后就可以执行各种Linux命令了:

图 2.5 Mobaxterm登陆上了Ubuntu

2.4 使用FileZilla在Windows和Ubuntu之间传文件

使用MobaXterm既可以ssh登录又可以传输文件,不过Mobaxterm在传输文件时使用效率上没有 FileZilla高,所以我们推荐Windows和Ubuntu互相传输文件时使用FileZilla。

双击打开FileZilla后,按图 2.6操作:

图 2.6 FileZilla连接Windows和Ubuntu

第1次连接时,会有如图 2.7所示的提示,选择“总是信任”:

图 2.7 FileZilla建立连接

在Filezilla中,左边是Windows文件,右边是Ubuntu的文件,如图 2.8:

图 2.8 FileZilla下Windows和Ubuntu互传文件

2.5编程示例:Ubuntu上的Hello程序

本节演示如何在Windows编写程序、上传到Ubuntu,在Ubuntu中编译、执行。只涉及一个简单的Hello程序,使用命令行编译,不涉及Makefile等知识,这些知识在后面的应用基础中讲解。

2.5.1 用Source Insight编写hello.c

启动Source Insight,点击“File”->“New”,新建文件,如图 2.9:

图 2.9 SI新建文件

接下来编写代码,保存文件,如图 2.10所示:

图 2.10 SI编写代码并保存

hello.c的源码如下:

代码语言:javascript复制
#include <stdio.h>
int main(int argc, char **argv)
{
	printf("hello, world!n");
	return 0;
}
2.5.2 用FileZilla上传源码

如图 2.11操作:

图 2.11 从windows传文件到Ubuntu

1.1.1 编译、运行程序

如图 2.12操作,对于gcc命令的用法在后面讲到应用开发基础时再细讲,这里只是体验一下:

图 2.12 体验Hello World!

3.DongshanPI-Vision开发板基本操作

3.1 DongshanPI-Vision开发板硬件资源简介

​ DongshanPI-Vision开发板是围绕着嘉楠 勘智K510芯片构建的,采用64位双核RISC-V架构 ,主频为:800MHz,支持双精度FPU扩展。采用64位DSP扩展,主频为:800MHz。芯片内置通用神经网络引擎KPU,拥有2.5TFLOPS BF16/2.5TOPS INT8算力,支持TensorFlow、PyTorch、ONNX等多种框架的算子库。

特征

描述

处理器

嘉楠 勘智K510

内存

512MB LPDDR3

存储

4GB EMMC

WIFI/蓝牙

天线:2.4GHz

视频输出

MIPI显示(MIPI DSI)/HDMI显示(HDMI)/SPI显示(SPI DSI)

视频输入

两路MIPI摄像头(MIPI CSI *2)/DVP摄像头(DVP Canera)

音频输入

MIC咪头

音频输出

扬声器/耳机

其他连接器

·TF卡槽·2个USB连接器· JTAG调试口·电池接口

开发板组成位置

​ 本章节介绍开发板中关键的元器件及位置功能介绍如下所示,各个标号对应的硬件在板子上都写有名字。

开发板正面图:

开发板背面图:

3.2 DongshanPI-Vision开发板软件资源介绍
3.2.1 开发开发环境

图 3.3 开发板开发环境

3.2.2 核心软件

图3.4 核心软件

3.2.3 文件系统

图3.5 文件系统

3.3 启动方式选择

板子上的拨码开关用来设置启动方式,支持这3种方式:EMMC启动、SD卡启动、串口烧写。启动方式如表3-1所示:

BOOT MODEL

SW1(BOOT0)

SW2(BOOT1)

EMMC

OFF

OFF

SD

OFF

ON

串口烧录

ON

ON

这3种启动方式的设置示意图 3.7如下:

图 3.7 拨码开关与启动方式

注意:设置为串口烧录时,不能插上SD卡、TF卡;上电之后才可以插卡。刚出厂的板子在EMMC上烧写了系统,你可以设置为EMMC启动方式。

3.4 开发板硬件连接
3.4.1 天线连接

要使用WIFI,您需要连接DongshanPI-Vision盒子中提供的2.4GHz天线,下面是将天线连接到DongshanPI-Vision开发板的指南。

3.4.2 连接摄像头

​ 要使用摄像头获取图像数据,如果您只单独购买了DongshanPI-Vision开发板,可能还需另外购买MIPI摄像头。下面图片是将MIPI摄像头连接到DongshanPI-Vision开发板的指南。

3.4.3 连接显示屏

​ 要使用显示屏显示摄像头获取的图像,如果您只单独购买了DongshanPI-Vision开发板,可能还需另外购买MIPI显示屏,或者也可直接使用HDMI线连接电脑显示器。下面是将MIPI显示屏连接到DongshanPI-Vision开发板的指南。

3.4.4串口及OTG连接

​ 通过Type-C线将板连接到PC电脑,您可以使用DongshanPI-Vision盒子中的的两条Type-C线。连接指南如下所示:

注意:请直接将两条数据线连接到电脑端,请勿使用USB HUB连接两条Type-C数据线。如果您的电脑没有多余的USB口,可使用5V1A的电源适配器,连接到OTG口进行供电。

一旦开发板套件通电后,核心板上会亮起红灯,红灯位置如下图中蓝色箭头所示:

3.5 查看串口端口号

​ 在接好串口处的Type-C数据线连接电脑和开发板后,打开电脑的设备管理器,并展开端口(COM和LPT)列表。可以看到下图中,连接后的端口号为COM37。开发板上的USB串口芯片可能是CP210x或CH9102,它们的性能是一样的。你电脑上显示的COM序号可能不一样,记住你电脑显示的数字。

如果电脑没有显示出端口号,就需要手动安装驱动,已经将驱动安装包放入网盘资料中了:

图 3.10 USB串口驱动

如果电脑中没有自动安装驱动,在“设备管理器”会有黄色感叹号提示当前连接的是哪种类型的串口芯片,根据提示选择驱动安装。如果提示中有“CP210x”字样则选择“CP210x_Windows_Drivers.zip”,否则就选择另外一个驱动安装。

3.6 使用MobaXterm软件打开串口

​ 打开MobaXterm,点击左上角的“Session”,在弹出的界面选中“Serial”,如下图所示选择端口号(前面设备管理器显示的端口号COM17或COM19)、波特率(Speed 115200)、流控(Flow Control: none),最后点击“OK”即可。步骤如图 3.11所示。

注意:流控(Flow Control)一定要选择none,否则你将无法在MobaXterm中向串口输入数据。

随后显示一个黑色的窗口, 此时打开板子的电源开关,将收到板子串口发过来的数据,如图 3.12所示。

4.进入串口调试控制台后,如果开发板正在启动uboot或者kernel则会不断打印输出信息直到系统完全启动,如果开发板已经完全启动则不会打印信息,可直接按下回车键,进入开发板系统控制台。

3.7 通过串口操作开发板

在串口看到root@canaan这类登录的提示信息时,输入回车即可,然后就可以执行各种Linux命令了,如图 3.14所示:

3.8 开机自启应用程序

​ 当系统启动后,如果您正常连接两个摄像头和显示屏,系统会在uboot系统启动阶段显示canaan官方logo,效果图如下所示:

当系统完全启动后会自动运行实时预览程序,使用V4L2抓取摄像头图像并实时显示在显示屏上。效果图如下所示:

如果您想结束掉该进程或者想运行其他需要使用摄像头和显示屏的程序,您可以通过在串口终端控制台输入ps查看所以进程,并找到进程名.为v4l2_drm.out所对对应的进程号,如下所示:

代码语言:javascript复制
188 root      0:00 ./v4l2_drm.out -f video_drm_1920x1080.conf -e 1 -s

可以看到最前面的为进程号,可能您的进程号和我不一致,这里我的进程号为188,此时我应该输入:

代码语言:javascript复制
kill -9 188

如果您的进程号不是188,需要将188修改为您实际的进程号才能正常结束该应用程序。

3.9 更新系统
3.9.1 更新EMMC系统

硬件要求:

  • DongshanPI-Vision开发板
  • Type-c数据线 x2

软件要求:

  • DongshanPI-Vision开发板EMMC镜像:https://dongshanpi.cowtransfer.com/s/5482c150ff6147
  • KendryteBurningTool 烧录工具:https://dongshanpi.cowtransfer.com/s/b3709a719d2342

开始前请下载DongshanPI-Vision开发板EMMC镜像 ,并记住它在计算机中保存的位置。

3.9.1.1 硬件操作

​ 将下图中的拨码开关的boot0和boot1都向上拨,使开发板进入下载模式。使用两条Type-C线连接开发板端和电脑端,用于给开发板进行供电和使用串口进行烧录EMMC系统。

3.9.1.2 安装串口驱动
  • 串口驱动安装程序:2_DongshanPI-Vision_配套工具/【Windows】串口驱动工具

安装前说明:每台计算机安装一次即可。

打开串口驱动安装软件CH341SER.EXE,打开后会进入如下界面:

点击安装

等待安装完成即可。

3.9.1.3 烧录镜像

下载EMMC镜像并记住它在计算机中的位置。打开KendryteBurningTool 烧录工具,进入KendryteBurningToolbin目录下,双击打开BurningTool.exe,如下所示的文件。

注意:在使用KendryteBurningTool 烧录工具时需要关闭串口软件和虚拟机,防止串口被占用。

打开BurningTool.exe程序后会进入如下界面:

点击选择文件,选择下载好的EMMC镜像。选择完成后点击保存,操作步骤如下所示:

保存后需要在串口选择中选择开发板的串口号,当我们将开发板和PC电脑端通过Type-C连接起来后,可以在BurningTool软件中点击红色箭头处查看我开发板的端口号,选择开发板的串口端口号。(我们也可以在设备管理器中确认开发的端口号)

选择完成后,点击开始烧录烧录。如果您不是第一次进行烧录,此时等待成功烧录完成即可。如果您是第一次进行烧录请继续阅读下面的内容。第一次烧录步骤如下所示:

当PC电脑首次进行烧录时,第一个进度条结束后,会显示下图中的错误信息。此时需要安装驱动。

3.9.1.4 安装烧录驱动
  • zadig-2.4烧录驱动安装文件:``

安装前说明:每台计算机安装一次即可。

打开zadig-2.4软件,进入如下界面

点击Option中的选择List All Devices(列出所有设备),具体操作如下所示:

上述操作完成后,可以看到在虚线框内出现了设备名,我们需要切换设备为 Mass storage devices,具体操作如下所示:

点击Replace Driver替换驱动程序,此时会弹出一个确认窗口,点击

安装完成后会弹出以下窗口点击close

到此烧录驱动成功安装。

3.9.1.5 完整烧录镜像

​ 安装完成烧录镜像后,再次打开BurningTool.exe烧录工具软件,按照1.3章节中的操作进行烧录即可。完整烧录步骤如下所示:

3.9.1.6 启动EMMC系统

​ 将下图中的拨码开关的boot0和boot1都向下拨,使开发板进入EMMC启动模式。使用两条Type-C线连接开发板端和电脑端,用于给开发板进行供电和使用串口登录开发板控制台。

使用串口软件查看串口控制台,成功启动后会进入开发板控制台。

3.9.2 制作SD卡镜像

硬件要求:

  • DongshanPI-Vision开发板
  • microSD卡(建议最小8G)
  • Type-c数据线 x2

软件要求:

  • DongshanPI-Vision开发板SD卡镜像:https://dongshanpi.cowtransfer.com/s/bac8fbdce7c046
  • SD卡格式化工具:SD Memory Card Formatter
  • SD卡刷机工具:ETCHER

开始前请下载DongshanPI-Vision开发板SD卡镜像,并记住它在计算机中保存的位置。

3.9.2.1 格式化microSD卡

将您的SD卡使用读卡器通过USB口插入您的PC电脑,使用SD卡格式化工具SD Memory Card Formatter格式化您的SD卡。点击下图中红框位置,开始格式化内存卡。

点击完成后会弹出下图所示的提示框,该提示警告我们格式化将清空卡中的所有数据,询问我们是否继续,这里点击

等待格式化完成后,会弹出以下对话框,提示我们格式化后的文件系统为FAT32以及内存大小可用空间,点击确定即可完成SD卡的格式化。

3.9.2.2 使用Etcher烧录镜像

​ 使用Etcher将DongshanPI-Vision开发板SD卡镜像写入您的microSD卡。

​ 下载Etcher烧写工具并安装它。启动Etcher应用程序,启动后界面如下图所示:

点击Flash from file,如下图所示,点击下图红框处。

此时会弹出文件资源管理器,选择您刚刚下载的DongshanPI-Vision开发板SD卡镜像。

选择完成后会,显示下面的界面,点击下图中红框处Select target,选择要写入的目标microSD卡。

点击完成后会弹出选择目标,此时选择您通过读卡器插入电脑中的microSD卡。

选择完成后,会显示以下界面,点击Flash后即可开始烧写。

如下图所示等待烧写完成即可。

使用Etcher烧写完成后,Windows可能会不知道如何读取您的microSD卡,会弹出如下图所示警告,点击取消后拔出microSD卡即可。

3.9.2.3 启动SD卡系统

​ 将下图中的拨码开关的boot0向下拨和boot1向上拨,使开发板进入SD卡启动模式。将SD卡插入开发板的卡槽中,步骤如下图所示:

使用两条Type-C线连接开发板端和电脑端,用于给开发板进行供电和使用串口登录开发板控制台。

使用串口软件查看串口控制台,成功启动后会进入开发板控制台。

3.10 使用wifi连接网络
3.10.1 结束开启联网脚本

安装启动开发板完成后,打开串口终端进入开发板控制台。由于开发板启动后会启动联网脚本,我们第一次配网时需要手动结束联网脚本。输入ps,查看进程,找到下面所示的两个进程。

代码语言:javascript复制
  159 root      0:00 wpa_supplicant -D nl80211 -i wlan0 -c /etc/wpa_supplicant.
  178 root      0:00 udhcpc -R -n -p /var/run/udhcpc.eth0.pid -i eth0 -b

通过上述信息可以发现,我们需要手动结束159和178这两个进程,您的进程号可能和我不一致,按您开发板上世纪的进程操作。输入:

代码语言:javascript复制
kill -9 <PID>

假设我使用的开发板中wpa_supplicantudhcpc进程号分别为159和178,此时我应该输入以下命令

代码语言:javascript复制
kill -9 159
kill -9 178

手动结束后使用ps查看是否还存在对应进程。

3.10.2 填写WIFI信息

修改/etc/wpa_supplicant.conf文件,填写wifi名称和密码,输入

代码语言:javascript复制
vi /etc/wpa_supplicant.conf

进入vi编辑器后会显示以下信息

代码语言:javascript复制
ctrl_interface=/var/run/wpa_supplicant
update_config=1
ap_scan=1

在文件末尾增加网络信息

代码语言:javascript复制
network={
               ssid="<wifi名称>"
               psk="<密码>"
        }

假设我的WiFi名称为Programmers,密码为123456,则实际添加的网络信息为:

代码语言:javascript复制
network={
               ssid="Programmers"
               psk="12345678"
        }

添加完成后保存并退出vi编辑器。

3.10.3 连接WiFi

连接到 SSID,输入:

代码语言:javascript复制
wpa_supplicant -B -iwlan0 -c /etc/wpa_supplicant.conf

执行完成后,如下所示

获取ip地址,输入:

代码语言:javascript复制
udhcpc -i  wlan0

获取完成后即为成功连接互联网。

测试WiFi是否可以访问互联网,输入ping www.baidu.com,输入后如下所示:

代码语言:javascript复制
[root@canaan ~ ]$ ping www.baidu.com
PING www.baidu.com (14.119.104.189): 56 data bytes
64 bytes from 14.119.104.189: seq=0 ttl=55 time=10.241 ms
64 bytes from 14.119.104.189: seq=1 ttl=55 time=16.292 ms
64 bytes from 14.119.104.189: seq=2 ttl=55 time=15.699 ms
64 bytes from 14.119.104.189: seq=3 ttl=55 time=12.508 ms

在后续启动开发板中,开发板会自动连接到SSID,您只需要重新获取ip地址即可访问互联网。

3.11 使用TFTP服务在Ubuntu和开发板传输文件
3.11.1 Ubuntu安装TFTP服务

在Ubuntu中执行以下命令安装TFTP服务:

代码语言:javascript复制
sudo apt-get install tftp-hpa tftpd-hpa

然后,创建TFTP服务工作目录,并打开TFTP服务配置文件,如下:

代码语言:javascript复制
mkdir -p /home/ubuntu/tftpboot
chmod 777 /home/ubuntu/tftpboot
sudo gedit /etc/default/tftpd-hpa

在配置文件/etc/default/tftpd-hpa中,将原来的内容删除,修改为:

代码语言:javascript复制
TFTP_USERNAME="tftp"
TFTP_ADDRESS=":69"
TFTP_DIRECTORY="/home/ubuntu/tftpboot"
TFTP_OPTIONS="-l -c -s"

最后,重启TFTP服务:

代码语言:javascript复制
sudo service tftpd-hpa restart

查看tftp服务是否在运行,运行如下命令,即可查看是否在后台运行。

代码语言:javascript复制
ubuntu@ubuntu2004:~/Desktop$ ps -aux | grep “tftp”
ubuntu      4555  0.0  0.0   9040   652 pts/0    S    02:33   0:00 grep --color=auto “tftp”
3.11.2 开发板通过tftp传输文件

首先确保Ubuntu或Windows的tftp服务目录内,有需要下载到板子上的文件,比如:

代码语言:javascript复制
ubuntu@ubuntu2004:~$ ls /home/ubuntu/tftpboot/
object_detect

确认Ubuntu的网络IP,例如

比如下载Ubuntu服务器下的zImage 文件,则在开发板上执行如下命令(Ubuntu的桥接网卡IP是192.168.0.197):

代码语言:javascript复制
[root@canaan ~ ]$ tftp -g -r object_detect 192.168.0.197

下载过程如下所示:

代码语言:javascript复制
[root@canaan ~ ]$ tftp -r object_detect -g 192.168.0.197
object_detect        100% |********************************|  894k  0:00:00 ETA

下载完成后,可以在当前目录找到该文件

代码语言:javascript复制
[root@canaan ~ ]$ ls
data           emmc           object_detect

如何从开发板上传文件到Ubuntu?比如我们现在开发板家目录下创建一个1.txt 的文本文件,然后写入111111….

代码语言:javascript复制
[root@100ask:~]$ tftp -p -l 1.txt  192.168.5.197
1.txt                100% |********************************|     8  0:00:00 ETA

此时我们查看Ubuntu服务器的tftp服务目录下,即可看到之前在开发板上创建的1.txt:

代码语言:javascript复制
ubuntu@ubuntu2004:~$ ls /home/ubuntu/tftpboot/
1.txt  object_detect
3.12 配置开发板SSH功能
3.12.1 修改ssh配置文件

使用Mobaxterm终端工具访问开发板的串口控制台,等待系统启动后,进入/etc/ssh目录下

代码语言:javascript复制
[root@canaan ~ ]$ cd /etc/ssh/
[root@canaan /etc/ssh ]$ ls
moduli       ssh_config   sshd_config

进入ssh目录可以看到有三个文件,您需要修改sshd_config,使用vi命令进行修改

代码语言:javascript复制
[root@canaan /etc/ssh ]$ vi sshd_config

输入后,进入vi编辑器,修改下图中红框处的两项。

配置SSH登录设置

将红框处两项取消注释,并将参数设置为yes

代码语言:javascript复制
PermitRootLogin yes
PermitEmptyPasswords yes

修改完成后如下所示:

配置SFTP服务

修改sshd_config中的sftp配置项

修改前:

代码语言:javascript复制
Subsystem      sftp    /usr/libexec/sftp-server

修改后:

代码语言:javascript复制
Subsystem      sftp    internal-sftp

修改完成后,按下esc,输入:wq,保存并退出编辑器。

3.12.2 启动ssh

​ 当您修改完成配置文件后后,需要手动重启ssh,在终端输入/etc/init.d/S50sshd restart

代码语言:javascript复制
[root@canaan ~ ]$ /etc/init.d/S50sshd restart
Stopping sshd: killall: sshd: no process killed
OK
Starting sshd: OK

输入完成后,ssh会重新启动。

3.12.3 连接ssh

开始前请注意:

  • 您的PC电脑需要和开发板连接同一个WIFI

​ 在开发板串口终端输入udhcpc -i wlan0,获取您的开发板连接WIFI的IP地址

代码语言:javascript复制
[root@canaan ~ ]$ udhcpc -i wlan0
udhcpc: started, v1.31.1
udhcpc: sending discover
udhcpc: sending select for 192.168.0.154
udhcpc: lease of 192.168.0.154 obtained, lease time 122
deleting routers
adding dns 192.168.0.1
adding dns 192.168.0.1

获取完成后,输入ifconfig wlan0 ,查看开发板所连接WIFI的IP地址

代码语言:javascript复制
[root@canaan ~ ]$ ifconfig wlan0
wlan0     Link encap:Ethernet  HWaddr 8C:F7:10:47:9B:6E
          inet addr:192.168.0.154  Bcast:192.168.0.255  Mask:255.255.255.0
          inet6 addr: fe80::8ef7:10ff:fe47:9b6e/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:438 errors:0 dropped:2 overruns:0 frame:0
          TX packets:21 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:34571 (33.7 KiB)  TX bytes:2446 (2.3 KiB)

可以看到wlan0选项中的IP地址为:192.168.0.154

3.12.4 使用MobaXterm传输文件

使用MobaXterm终端工具,点击会话Session,如下图中红框所示

点击完成后会弹出以下界面,点击使用SSH,如下图红框所示:

进入ssh配置界面后,远程主机Remote host的框输入开发板的IP地址,勾选指定用户名Specify username前的框,在后面填写开发板的用户名root,填写完成后,点击OK即可。如下图所示

点击完成后就会打开一个新的终端,如下图所示:

您可以看到这里也可以访问开发的串口终端。您可以看到左边选项卡中的开发板对应的文件系统,例如data/emmc,您在选项卡中可以对文件夹中内容进行下载或者上传。如下图所示,这里我进入/root/emmc/p3/app/ai/exe/目录中,选择face_detect文件,单击右键后弹出选项栏,选择Download即可开始下载该文件。

如果您想上传文件到当前目录,在左侧选项卡的空白处,点击右键,选择Upload to current folder上传到当前文件夹即可。

4.系统功能及AI应用初体验

4.1 系统功能体验指南
4.1.1 ai demo程序

说明

nncase 的demo程序源码位于SDK目录下的package/ai目录,目录结构如下:

代码语言:javascript复制
$ tree -L 2 ai
ai
├── ai.hash
├── ai.mk
├── code
│   ├── build.sh
│   ├── cmake
│   ├── CMakeLists.txt
│   ├── common
│   ├── face_alignment
│   ├── face_detect
│   ├── face_expression
│   ├── face_landmarks
│   ├── face_recog
│   ├── hand_image_classify
│   ├── head_pose_estimation
│   ├── imx219_0.conf
│   ├── imx219_1.conf
│   ├── license_plate_recog
│   ├── object_detect
│   ├── object_detect_demo
│   ├── openpose
│   ├── person_detect
│   ├── retinaface_mb_320
│   ├── self_learning
│   ├── shell
│   ├── simple_pose
│   ├── video_192x320.conf
│   ├── video_object_detect_320.conf
│   ├── video_object_detect_320x320.conf
│   ├── video_object_detect_432x368.conf
│   ├── video_object_detect_512.conf
│   ├── video_object_detect_640.conf
│   └── video_object_detect_640x480.conf
└── Config.in

可以参考retinaface_mb_320的源码和CMakeLists.txt添加新的nncase 的demo程序。

模型的编译参见nncase_demo.mk里面定义的POST_INSTALL_TARGET_HOOKS

代码语言:javascript复制
NNCASE_DEMO_DEPENDENCIES  = mediactl_lib nncase_linux_runtime opencv4 libdrm
define NNCASE_DEMO_COMPILE_MODEL
    mkdir -p $(TARGET_DIR)/app/ai/kmodel/kmodel_compile/retinaface_mb_320
    cd $(@D) && /usr/bin/python3 retinaface_mb_320/rf_onnx.py --quant_type uint8 --model ai_kmodel_data/model_file/retinaface/retinaface_mobile0.25_320.onnx
    cp $(@D)/rf.kmodel $(TARGET_DIR)/app/ai/kmodel/kmodel_compile/retinaface_mb_320/rf_uint8.kmodel
    cd $(@D) && /usr/bin/python3 retinaface_mb_320/rf_onnx.py --quant_type bf16 --model ai_kmodel_data/model_file/retinaface/retinaface_mobile0.25_320.onnx
    cp $(@D)/rf.kmodel $(TARGET_DIR)/app/ai/kmodel/kmodel_compile/retinaface_mb_320/rf_bf16.kmodel

NNCASE_DEMO_POST_INSTALL_TARGET_HOOKS  = NNCASE_DEMO_COMPILE_MODEL

模型的编译需要nncase环境,关于nncase环境的搭建,参考k510_nncase_Developer_Guides.md。以后nncase有更新,buildroot sdk会同步更新到nncase。

retinaface

功能:人脸检测,人脸特征点检测

程序路径: /app/ai/shell 运行: 执行非量化模型,./retinaface_mb_320_bf16.sh 执行uint8量化模型,./retinaface_mb_320_uint8.sh

脚本里面有关于QOS的设置,下面的两个demo的设置一样。

代码语言:javascript复制
#devmem phyaddr width value
devmem 0x970E00fc 32 0x0fffff00
devmem 0x970E0100 32 0x000000ff
devmem 0x970E00f4 32 0x00550000

跑demo时,需要优先保证屏幕显示正常,即调整显示相关的QoS为高优先级。 QOS_CTRL0.ax25mp write QoS = 5 QOS_CTRL0.ax25mp read QoS = 5 QOS_CTRL2.ispf2k write QoS = 0xf QOS_CTRL2.ispf2k read QoS = 0xf QOS_CTRL2.ispr2k write QoS = 0xf QOS_CTRL2.ispr2k read QoS = 0xf QOS_CTRL2.isp3dtof write QoS = 0xf QOS_CTRL3.display read QoS = 0xf QOS_CTRL3.display write QoS = 0xf

QOS 控制寄存器0(QOS_CTRL0) offset[0x00f4]

qos ctrl0qos ctrl0

QOS 控制寄存器1(QOS_CTRL1) offset[0x00f8]

qos ctrl1qos ctrl1

QOS 控制寄存器2(QOS_CTRL2) offset[0x00fc]

qos ctrl2qos ctrl2

QOS 控制寄存器3(QOS_CTRL3) offset[0x0100]

qos ctrl3qos ctrl3

模型的编译安装详见文件package/ai/ai.mk:

编译脚本路径: 100ask_base-aiApplication-demo/code/retinaface_mb_320/rf_onnx.py

object_detect

功能:物体分类检测,80分类

程序路径: /app/ai/shell

运行: 执行非量化模型,./object_detect_demo_bf16.sh 执行uint8量化模型,./object_detect_demo_uint8.sh

模型的编译安装详见文件package/ai/ai.mk

编译脚本路径: 100ask_base-aiApplication-demo/code/object_detect_demo/od_onnx.py

4.1.2 ffmpeg

ffmpegffmpeg-4.4开源代码上进行移植,0001-buildroot-ffmpeg-0.1.patch为补丁包,增加了

  • ff_k510_video_demuxer:控制isp输入,引用了libvideo.so
  • ff_libk510_h264_encoder:控制h264硬件编码,引用了libvenc.so

可以通过help指令查看可配置参数

代码语言:javascript复制
ffmpeg -h encoder=libk510_h264 #查看k510编码器的参数
ffmpeg -h demuxer=libk510_video #查看demuxer的配置参数

详细运行说明参考K510_Multimedia_Developer_Guides.md

4.1.3 alsa_demo

alsa demo程序放在/app/alsa_demo目录下:

运行准备:

  1. 插上耳机

使用ALSA UTILS测试。

4.1.4 TWOD demo

运行 rotation 使用方法:

代码语言:javascript复制
cd /app/twod_app
./twod-rotation-app

将ouput.yuv 拷到yuv显示器上设置尺寸1080 x 1920,显示格式nv12,结果如下

scaler 使用方法

代码语言:javascript复制
cd /app/twod_app
./twod-scaler-app

将ouput.yuv 拷到yuv显示器上设置尺寸640x480,显示格式nv12,结果如下

运行 rgb2yuv 使用方法:

代码语言:javascript复制
cd /app/twod_app
./twod-osd2yuv-app

将ouput.yuv 拷到yuv 显示器上设置尺寸320x240,显示格式nv12,结果如下

运行 yuv2rgb 使用方法:

代码语言:javascript复制
cd /app/twod_app
./twod-scaler-output-rgb888-app

将ouput.yuv 拷到rgb888显示器上设置尺寸640x480,显示格式rgb24,结果如下

运行 输出yuv上叠加osd 使用方法:

代码语言:javascript复制
cd /app/twod_app
./twod-scaler-overlay-osd-app

将ouput.yuv 拷到显示器上设置尺寸640x480,显示格式nv12,结果如下

API:

代码语言:javascript复制
/* 创建内存 */
twod_create_fb()
/* 配置原图片参数 */   
twod_set_src_picture()
/* 配置输出图片参数 */ 
twod_set_des_picture()
/* 设置 scaler */     
twod_set_scaler()
/* 等待操作完成 */     
twod_wait_vsync()
/* Invali cache */   
twod_InvalidateCache()
/* flash cache */     
twod_flashdateCache()
/* 释放内存*/     
twod_free_mem()
/* 设置旋转 */  
twod_set_rot()
4.1.5 RTC demo

RTC驱动会注册生成/dev/rtc0设备节点。

应用层遵循Linux系统中的标准RTC编程方法调用驱动,在运行参考例程之前,建议通过shell 控制台关闭内核信息打印。

代码语言:javascript复制
echo 0 > /proc/sys/kernel/printk

进入/app/rtc目录,输入如下命令启动rtc应用程序。

代码语言:javascript复制
cd /app/rtc
./rtc 2021-11-3 21:10:59

程序的执行结果为:

RTC demo程序的主要代码片段如下,详细请参考package/rtc 文件夹下的代码。

代码语言:javascript复制
/*解析参数,获取当前年月日、时分秒*/
if(argc !=3) {
    fprintf(stdout, "useage:t ./rtc year-month-day hour:minute:secondn");
    fprintf(stdout, "example: ./rtc 2021-10-11 19:54:30n");
    return -1;
}

sscanf(argv[1], "%d-%d-%d",  &year, &month, &day);
sscanf(argv[2], "%d:%d:%d",  &hour, &minute, &second);

/*打开RTC设备,设备节点是:/dev/rtc0 */
fd = open("/dev/rtc0", O_RDONLY);
if (fd == -1) {
    perror("/dev/rtc0");
    exit(errno);
}

/* 设置RTC时间。*/
retval = ioctl(fd, RTC_SET_TIME, &rtc_tm);
if (retval == -1) {
    perror("ioctl");
    exit(errno);
}

/* 休眠 2秒。 */
sleep(2);

/* 读取RTC当前时间。*/
retval = ioctl(fd, RTC_RD_TIME, &rtc_tm);
if (retval == -1) {
    perror("ioctl");
    exit(errno);
}

/* 打印 RTC当前时间。*/
fprintf(stdout, "nRTC date/time: %d/%d/%d d:d:dn",
        rtc_tm.tm_mday, rtc_tm.tm_mon   1, rtc_tm.tm_year   1900,
        rtc_tm.tm_hour, rtc_tm.tm_min, rtc_tm.tm_sec);
4.1.6 WDT demo

K510一共有三个看门狗,WDT驱动会注册生成/dev/watchdog0、/dev/watchdog1、/dev/watchdog2 设备节点。

应用层遵循 Linux系统中的标准WDT编程方法调用驱动,wathdog应用程序第一个参数可为0、1,分别代表watchdog0、watchdog1,第二个参数表示可设置的超时时间(单位秒),例如如下命令表示启动watchdog0,watchdog0溢出时间40秒。

代码语言:javascript复制
cd /app/watchdog
./watchdog 0 40

程序启动后将每间隔1秒喂一次看门狗,当shell终端中输入stop字符后,应用程序停止喂狗,看门狗将在设置超时时间溢出后复位设备重启,详细请参考package/watchdog文件夹下的代码。

程序的执行结果为:

注意:当前k510看门狗模块的工作时钟频率为757575Hz,以秒为单位的超时时间需要转换成看门狗实际的工作时钟频率的超时时间,计算公式是2^n/757575,因此实际的超时时间会大于等于输入的超时时间。

实际超时时间的计算过程是:

  1. 输入40,2^25/757575=44 > 40,2^24/757575=22 < 40,因此设置为44秒;
  2. 输入155,2^27/757575=177 > 155,因此设置为177秒;
  3. 输入2000,2^31/757575=2834 > 2000,因此设置为2834秒;
4.1.7 UART demo

K510一共有4个串口,当前驱动中串口2、3没有使能,串口0驱动会注册生成/dev/ttyS0设备节点。

应用层遵循Linux系统中的标准UART编程方法调用驱动。uart应用程序第一个参数可为0、1,分别代表uart0、uart1。

将开发板使用有线网连接到路由器,使得开发板和调试PC在一个网络中,当开发板上电后将自动获取IP,在开发板的shell串口终端中输入ifconfig命令获取IP地址,调试PC利用此IP通过telent连接开发板打开一个telent窗口。例如调试PC通过MobaXterm使用telent连接开发板的操作如下图。

telent终端窗口中输入如下命令启动串口0工作。

代码语言:javascript复制
cd /app/uart
./uart 0

在telent窗口中输入要发送的内容,可以在shell串口终端窗口看到接收到的数据,详细请参考package/crb_demo/uart文件夹下的代码。 例如,telent窗口的输入:

对应的Shell串口终端窗口显示:

4.1.8 ETH demo

应用层遵循Linux系统中的标准ETH编程方法调用驱动。

Client

设备作为client端,进入/app/client目录,输入如下命令启动client应用程序,ETH应用程序第一个参数表示要建立TCP链接的服务器ip地址,例如输入如下命令表示启动ETH程序与10.20.5.1.13的server建立通信。

代码语言:javascript复制
cd /app/client
./client 10.20.1.13

通过tcp协议连接server进行通信,在另一台ubuntu机器上运行server程序,详细代码请参考package/app/client文件夹下内容。

设备端显示日志:

Server

设备作为server端,进入/app/server目录,例如输入如下命令表示启动server程序。

代码语言:javascript复制
cd /app/server
./server

在另一台ubuntu机器上运行client程序,通过tcp协议连接server进行通信,详细代码请参考package/crb_demo/server文件夹下内容。

设备端显示日志:

4.1.9 SDMMC demo

K510一共有3个SDMMC主控制器,开发板上SDMMC0用于接eMMC,SDMMC1用于WIFI模块,SDMMC2控制器用于接sdcard。

SDMMC驱动会注册生成/dev/mmcblk0,EMMC驱动会注册成/dev/mmcblk1设备节点。

SD卡在系统启动后会自动挂载到/root/data ,进入/app/write_read_file目录,SDMMC应用程序第一个参数表示要进行读写操作的文件,如SD卡挂载到/root/data,可对/root/data/目录下的文件进行读写操作,先写后读,输入如下命令启动SDMMC应用程序对SD卡进行读和写的操作并计算读写速度(单位m/s)。

代码语言:javascript复制
cd /app/write_read_file
./write_read_file /root/data/test.txt

开启对SD卡进行1G数据的读写,代码请参考package/app/write_read_file文件夹下的内容。

4.1.10 SHA/AES demo

SHA/AES demo 使用Linux 内核导出 AF_ALG 类型的 Netlink 接口,在用户空间使用内核加密 API。详细信息请参考https://www.kernel.org/doc/html/latest/crypto/userspace-if.html。

参数: -h 打印帮助信息 -t 算法类型:hash、skcipher -n 算法名称:sha256、ecb(aes)、cbc(aes) -x 解密操作 -k AES KEY(16进制字符串) -v AES IV(16进制字符串)

sha256 test:

代码语言:javascript复制
cd /app/crypto
echo -n "This is a test file, hello world" > plain.txt
./crypto -t hash -n "sha256" plain.txt sha256.txt
xxd -p -c 32 sha256.txt
sha256sum plain.txt

ecb(aes) 128 test:

代码语言:javascript复制
cd /app/crypto
echo -n "This is a test file, hello world" > plain.txt
./crypto -t skcipher -n "ecb(aes)" -k 00112233445566778899aabbccddeeff plain.txt ecb_aes_en.bin
./crypto -t skcipher -n "ecb(aes)" -k 00112233445566778899aabbccddeeff  -x ecb_aes_en.bin ecb_aes_de.bin
cmp ecb_aes_de.bin plain.txt
cat ecb_aes_de.bin

cbc(aes) 128 test

代码语言:javascript复制
cd /app/crypto
echo -n "This is a test file, hello world" > plain.txt
./crypto -t skcipher -n "cbc(aes)" -k 00112233445566778899aabbccddeeff -v 00112233445566778899aabbccddeeff plain.txt cbc_aes_en.bin
./crypto -t skcipher -n "cbc(aes)" -k 00112233445566778899aabbccddeeff -v 00112233445566778899aabbccddeeff -x cbc_aes_en.bin cbc_aes_de.bin
cmp cbc_aes_de.bin plain.txt
cat cbc_aes_de.bin

aes-ecb-128和aes-cbc-128加密时要求明文要16字节对齐,不足会自动补0。

4.1.11 TRNG demo

TRNG demo通过读取/dev/hwrng字符设备产生指定长度的随机数,按16进制字符串输出。

./trng的输入参数含义:

-h 打印帮助信息

-b 指定输出随机数长度,单位byte

4.1.12 DRM demo

drm demo展示了VO硬件多图层功能。

VO共有8个layer:

  1. 背景层,可配置背景色。
  2. layer0是video层,支持YUV422和YUV420,支持NV12和NV21格式,大小端可配,支持硬件scaling up和scaling down。
  3. layer1-layer3是video层,支持YUV422和YUV420,支持NV12和NV21格式,大小端可配。
  4. layer4-layer6是OSD层,支持多种ARGB格式。

开发板启动后进入/app/drm_demo目录,输入命令:

代码语言:javascript复制
cd /app/drm_demo
./drm_demo
4.1.13 V4L2_DRM demo

v4l2_drm demo展示了摄像头输入和显示的功能。

开发板启动后进入/app/mediactl_lib目录,输入命令:

代码语言:javascript复制
cd /app/mediactl_lib
./v4l2_drm.out -f video_drm_1080x1920.conf
或者
./v4l2_drm.out -f video_drm_1920x1080.conf
或者
./v4l2_drm.out -f imx385_video_1920x1080.conf  

imx385 demo : 这个需要修改配置,具体参照 K510_V4l2_Developer_Guides.md,运行命令如下:

代码语言:javascript复制
./v4l2_drm.out -f imx385_video_1920x1080.conf  

启动v4l2_drm.out应用程序,v4l2_drm.out显示效果:

4.1.14 LVGL demo

进入/app/lvgl,运行以下命令:

代码语言:javascript复制
cd /app/lvgl
./lvgl

显示效果如下:

4.1.15 PWM demo

PWM驱动会注册生成/sys/class/pwm/pwmchip0和/sys/class/pwm/pwmchip3设备节点。

本例程可分别对pwm0和pwm1进行配置和使能,进入/app/pwm目录,pwm应用程序第一个参数表示设置pwm的周期,单位为ns,第二个参数设置pwm一个周期中“ON”的时间,单位为ns,第三个参数可以为0、1,分别代表pwm0和pwm1,例如输入如下命令表示使能pwm0,周期为1s,占空比为1000000000/500000000*100% = 50%,详细代码请参考package/app/pwm文件夹下的内容。

代码语言:javascript复制
cd /app/pwm
./pwm 1000000000 500000000 0

程序的执行结果为:

通过示波器连接K510 CRB5.1.2开发板J15的28号引脚,可以示波器上观察到一个周期为1秒,占空比为50%的波形图。

4.1.16 WIFI demo

WiFi模块驱动加载后会生成无线网卡wlan0,遵循标准网口驱动,正常参考TCP/IP socket编程。

  1. 在笔记本打开“移动热点”,然后设置热点的名称和密码
  2. 在笔记本上启动NetAssist,配置协议类型、本地主机IP、本地主机端口、接收设置、发送设置及需要发送的数据,如下图:
  1. wifi测试程序的参数格式为:
代码语言:javascript复制
./wifi <AP name> <password> <local ip> <server ip>

例如进入/app/wifi目录,输入启动wifi测试程序命令,程序的执行结果如下图:

4.1.17 GPIO_KEYS demo

按键驱动使用linux kernel自身集成的基于input子系统的通用gpio-keys驱动,驱动加载后在/dev/input目录下生成事件监控节点eventX,X为事件节点序号,可以通过cat /proc/bus/input/devices查看

gpio-keys例程阻塞式读取按键上报事件并打印事件信息,其信息包括按键编码和按键动作,按键编码标识按键身份,按键动作分为pressed和released,在按键release时例程会计算按键按下的持续时间

程序执行结果如下图所示:

4.2 AI应用体验指南

​ 开发板系统中内置了丰富的AI应用示例程序,对每个示例程序我们都提供有对应的脚本文件,脚本文件中已经设置需要运行AI应用程序和对应的参数。

​ 在开始演示AI应用指南前,需要确保您已经正确连接摄像头和显示屏并正常上电启动开发板。启动开发板后可以发现会自动运行摄像头获取图像并在显示屏上实时预览程序,需要手动结束该应用程序。使用方法如下:

输入ps查看应用程序查看进程号,例如实时预览程序进程端口号为189。

代码语言:javascript复制
189 root      0:01 ./v4l2_drm.out -f video_drm_1920x1080.conf -e 1 -s

输入kill -9 <进程号>,结束实时预览程序进程,例如我查看的端口号为189,则应该输入

代码语言:javascript复制
kill -9 189

结束实时预览程序后,显示屏会显示白屏,即代表成功结束摄像头获取图像并在显示屏上实时预览程序。

本章节介绍开发板内置的AI应用程序运行脚本都位于/app/ai/shell/目录中。进入该目录,查看是否有对应脚本文件。

代码语言:javascript复制
[root@canaan ~ ]$ cd /app/ai/shell/
[root@canaan /app/ai/shell ]$ ls
face_alignment.sh            object_detect_demo_bf16.sh
face_detect.sh               object_detect_demo_uint8.sh
face_expression.sh           open_pose.sh
face_landmarks.sh            person_detect.sh
face_recog.sh                retinaface_mb_320_bf16.sh
hand_image_classify.sh       retinaface_mb_320_uint8.sh
head_pose_estimation.sh      self_learning.sh
license_recog.sh             simple_pose.sh
object_detect.sh

下面介绍每个脚本对应的AI应用程序的运行指南,在进行以下演示时需要确保您的串口终端控制台已进入/app/ai/shell/目录下。

4.2.1人脸对齐

人脸对齐,可得到图像或视频中的每个人脸估计出来的depth或者pncc信息。其中pncc信息为三维人脸形状上的顶点,不仅包含这一点的三维坐标信息,还包含此处的RGB取值。

论文链接:https://sci-hub.et-fine.com/10.1109/TPAMI.2017.2778152

将3D平均人脸的顶点坐标和RGB取值进行归一化操作,即NCC操作。如下图所示,下图取自论文中的截图。

Face Alignment 人脸对齐任务是基于一定量的训练集,得到一个模型,使得该模型对输入的一张任意姿态下的人脸图像能够进行特征点(landmark)标记。Face Alignment 任务一般的呈现方式是人脸特征点的检测与标记。

运行人脸对齐演示示例,在终端输入:

代码语言:javascript复制
./face_alignment.sh

效果图如下所示:

4.2.2人脸检测

人脸检测采用了retina-face网络结构,backbone选取0.25-mobilenet。

论文链接:https://arxiv.org/pdf/1905.00641.pdf

GitHub链接:https://github.com/deepinsight/insightface

下图为单阶段逐像素密集人脸定位方法。

使用该应用时,可得到图像或视频中的每个人脸检测框以及每个人脸的左眼球/右眼球/鼻尖/左嘴角/右嘴角五个landmark。使用方法如下所示,输入:

代码语言:javascript复制
./face_detect.sh
人脸检测器-bf16

执行非量化模型。使用方法如下所示,输入:

代码语言:javascript复制
./retinaface_mb_320_bf16.sh
人脸检测器-uint8

执行uint8量化模型。使用方法如下所示,输入:

代码语言:javascript复制
./retinaface_mb_320_uint8.sh
4.2.3人脸表情识别

人脸表情识别中需要用到两个模型一个模型用于检测人脸;一个模型用于进行人脸表情识别。人脸表情识别采用了人脸表情分类的方式,使用该应用可得到图像或视频中的每个人脸属于以下表情的概率。使用方法如下所示,输入:

代码语言:javascript复制
./face_expression.sh
4.2.4人脸关键点检测

人脸关键点检测采用了PFLD(practical facial landmarks detection)。

论文链接:https://arxiv.org/pdf/1902.10859.pdf

使用该应用,可得到图像或视频中的每个人脸轮廓的106个关键点。使用方法如下所示,输入:

代码语言:javascript复制
./face_landmarks.sh
4.2.5人脸识别

人脸识别采用了人脸特征提取后比对的方式,相同的人的特征会尽可能的像,而不同的人的特征则会有较大差距,使用该应用可得到图像或视频中的每个人脸与人脸底库中的人脸的相似度,以进行人脸识别任务。使用方法如下所示,输入:

代码语言:javascript复制
./face_recog.sh

执行完成后会进入人脸检测模式,当摄像头检测到人脸后会,按下开发板的Key1,位置如下所示:

按下之后,可以在串口控制台中看到以下输出信息

代码语言:javascript复制
>>>>> key code: 30, action: pressed <<<<<
total took 55.4448 ms
total took 56.2784 ms
Please Enter Your Name to Register:
>>>>> key code: 30, action: released <<<<<

上述输出信息提示您需要在串口终端输入人脸的登记名称,可直接输入录入人脸的信息,输入完成后按下回车即可。注意:目前登记信息仅支持英文输入。假设我这里输入登记名称为A,如下所示:

输入完成后会继续进入人脸检测模式,此时如果您刚刚登记的人脸再次被检测,则会在检测时标注出该人脸的登记信息。 您可重复进行登记人脸信息,登记后可以在人脸检测模式中进行人脸识别,区分登记不同信息的人脸。但当检测到没有登记的人脸信息则不会在检测时标注出人脸信息。

5.2.6人形检测

使用该应用时,可得到图像或视频中人体的检测框。使用方法如下所示,输入:

代码语言:javascript复制
./person_detect.sh
人体关键点检测-openpose

人体关键点检测主要有两种检测方式,一个是自上而下,一种是自下而上。本应用采用了自下而上的模型openpose。使用该应用,可得到图像或视频中的每个人体的17个关键点。使用方法如下所示,输入:

代码语言:javascript复制
./open_pose.sh
人体关键点检测-YOLOV5S

人体关键点检测主要有两种检测方式,一个是自上而下,一种是自下而上。本应用采用了自上而下的模型采用了YOLOV5S进行人体检测,然后使用simplepose进行关键点回归。使用该应用,可得到图像或视频中的每个人体的17个关键点。使用方法如下所示,输入:

代码语言:javascript复制
./simple_pose.sh
4.2.7指尖指定区域识别

指尖指定区域识别主要包含3个流程,手掌检测 手掌关键点检测 图像识别。其中,手掌检测使用了512x512分辨率的 tiny-yolov3;手掌关键点检测使用了256x256分辨率的squeezenet1.1;图像识别使用了基于imagenet训练出来的mobilenetv2。通过手部关键点检测,利用两个食指尖,框定待识别区域。利用imagenet分类模型,确定待识别区域。使用方法如下所示,输入:

代码语言:javascript复制
./hand_image_classify.sh
4.2.8头部态角估计

头部态角估计,可得到图像或视频中的每个人脸的roll/yaw/pitch。roll代表了人头歪的程度;yaw代表了人头左右旋转的程度;pitch代表了人头低头抬头的程度。使用方法如下所示,输入:

代码语言:javascript复制
./head_pose_estimation.sh
4.2.9车牌识别

车牌识别的整体流程实际上包含了车牌检测 车牌识别两个流程。车牌检测采用了retinanet,车牌识别采用了lprnet。使用该应用,可得到图像或视频中的每个车牌的内容。使用方法如下所示,输入:

代码语言:javascript复制
./license_recog.sh
4.2.10YOLOV5目标检测

目标检测采用了YOLOV5,使用该应用,可得到图像或视频中属于以下标签的目标的检测框。

代码语言:javascript复制
"person", "bicycle", "car", "motorcycle", "airplane", "bus", "train", "truck",
"boat", "traffic light", "fire hydrant", "stop sign", "parking meter", "bench", "bird", "cat",
"dog", "horse", "sheep", "cow", "elephant", "bear", "zebra", "giraffe",
"backpack", "umbrella", "handbag", "tie", "suitcase", "frisbee", "skis", "snowboard",
"sports ball", "kite", "baseball bat", "baseball glove", "skateboard", "surfboard", "tennis racket", "bottle",
"wine glass", "cup", "fork", "knife", "spoon", "bowl", "banana", "apple",
"sandwich", "orange", "broccoli", "carrot", "hot dog", "pizza", "donut", "cake",
"chair", "couch", "potted plant", "bed", "dining table", "toilet", "tv", "laptop",
"mouse", "remote", "keyboard", "cell phone", "microwave", "oven", "toaster", "sink",
"refrigerator", "book", "clock", "vase", "scissors", "teddy bear", "hair drier", "toothbrush"

使用方法如下所示,输入:

代码语言:javascript复制
./object_detect.sh
YOLOV5目标检测-bf16

执行非量化模型,使用方法如下所示,输入:

代码语言:javascript复制
./object_detect_demo_bf16.sh
YOLOV5目标检测-uint8

执行uint8量化模型。使用方法如下所示,输入:

代码语言:javascript复制
./object_detect_demo_uint8.sh
4.2.11自学习KNN算法

自学习借鉴的是KNN(k-Nearest Neighbors)的思想。该算法的思想是: 一个样本与数据集中的k个样本最相似, 如果这k个样本中的大多数属于某一个类别, 则该样本也属于这个类别。使用方法如下所示,输入:

代码语言:javascript复制
./self_learning.sh

执行完成后会在显示屏上出现一个绿色的框。

该AI示例需要用到按键(Key 1)和按键(Key 2),两个按键的位置如下所示:

​ 将需要识别的物体放在摄像头范围内,使该物体可以显示在绿色框中的正中间,此时需要手动标记按下Key1开始标记class0,按下Key1时会串口终端会提示以下信息:

代码语言:javascript复制
>>>>> key code: 30, action: pressed <<<<<
Please press UP or DOWN button, UP: confirm, DOWN: switch!
>>>>> key code: 30, action: released <<<<<

上述信息为提示您,如果按下Key1则为标注图像,按下Key2则为切换模式。

继续按下Key1可开始自动标记该物体,此时串口终端会输出如下信息:

代码语言:javascript复制
>>>>> key code: 30, action: pressed <<<<<
Pressed UP button!
class_0 : 1
>>>>> key code: 30, action: released <<<<<

此时再按下Key1会继续继续让您选择继续标注图像还是切换。

代码语言:javascript复制
>>>>> key code: 30, action: pressed <<<<<
Please press UP or DOWN button, UP: confirm, DOWN: switch!
>>>>> key code: 30, action: released <<<<<

如果需要重复标记多次图像,可以继续按下Key1标记该物体。如下所示:

当标注完类别1(class 0)后按下Key2,串口终端会输出以下内容:

代码语言:javascript复制
>>>>> key code: 48, action: released <<<<<
>>>>> key code: 48, action: pressed <<<<<
Pressed DOWN button!
switch to class_1

此时会切换标注类别1(class 1),继续按下Key 1进行标注类别1,选择功能。

代码语言:javascript复制
>>>>> key code: 30, action: pressed <<<<<
Please press UP or DOWN button, UP: confirm, DOWN: switch!
>>>>> key code: 30, action: released <<<<<

继续按下Key 1标注类别1物体

代码语言:javascript复制
>>>>> key code: 30, action: pressed <<<<<
Pressed UP button!
class_1 : 1
>>>>> key code: 30, action: released <<<<<

如果需要重复标记多次图像,可以继续按下Key1标记该物体,与上面类别0(class 0)标注方法一直。

在标注完成后,在不进入功能选择时,按下key 2结束标注,如下图所示。

代码语言:javascript复制
>>>>> key code: 30, action: pressed <<<<<
Pressed UP button!
class_1 : 8
>>>>> key code: 30, action: released <<<<<
>>>>> key code: 48, action: released <<<<<
>>>>> key code: 48, action: pressed <<<<<
Pressed DOWN button!
Enter recog!

将刚才标注过的物体放在绿框内,此时会开发板会在显示屏上自动检测识别的物品,识别到物体时,左上角会显示对应物体的类别以及检测的精准度。

5.编写一个简单的helloword程序

5.1 编写helloword应用程序

​ 打开Ubuntu虚拟机,打开终端进入Home目录中,新建helloword文件夹

代码语言:javascript复制
ubuntu@ubuntu2004:~$ mkdir helloword

进入 helloword文件夹下,新建hello.c程序。

代码语言:javascript复制
ubuntu@ubuntu2004:~$ cd helloword/
ubuntu@ubuntu2004:~/helloword$ touch helloword.c

在终端内gedit工具会打开一个 文本编辑器,进入文本编辑器内, 将helloword程序代码粘贴进去,粘贴完成后,直接按下 ctrl s 保存,最后点击 右上角的 x 关 闭文本编辑器:

代码语言:javascript复制
ubuntu@ubuntu2004:~/helloword$ gedit helloword.c 

helloword程序代码:

代码语言:javascript复制
#include <stdio.h>
int main (void)
{ 
 printf("hello word!n");
 return 0;
} 

编写完成后,保存到 helloword.c 之后。我们需要指定存放交叉编译需要使用的库文件头文件的文件夹。如果需要在任意位置或者终端都可以使用交叉编译工具链,则需要单独 增加 工具链bin的路径至 ubuntu PATH环境变量里,可以添加至 ~/.bashrc也可以在每一个终端单独配置。在终端执行以下命令:

代码语言:javascript复制
export PATH=$PATH:~/100ask_base-aiApplication-demo/riscv64-buildroot-linux-gnu_sdk-buildroot/bin/

输入完成后,需要验证是否添加成功,在终端输入:

代码语言:javascript复制
riscv64-linux-gcc -v

输入后可以正常打印gcc version 7.3.0 (2019-11-20_nds64le-linux-glibc-v5d-6c120106e03)即可。

5.2.编写Makefile编译规则
代码语言:javascript复制
ubuntu@ubuntu2004:~/helloword$ touch Makefile
ubuntu@ubuntu2004:~/helloword$ gedit Makefile

将下面的程序复制到Makefile文件中

代码语言:javascript复制
CC := riscv64-linux-gcc
helloword:  helloword.c
	${CC} -o helloword helloword.c
clean:
	rm helloword

填写完成后,按下Crtl s保存Makefile文件。

5.3 交叉编译hellowrod程序

查看当前目录下是否存在helloword.c和Makefile:

代码语言:javascript复制
ubuntu@ubuntu2004:~/helloword$ ls
helloword.c  Makefile

确定文件存在后执行编译命令make进行编译,如下所示:

代码语言:javascript复制
ubuntu@ubuntu2004:~/helloword$ make
riscv64-linux-gcc -o helloword helloword.c

编译完成后会生成可执行文件hellword

可以执行 file hellword 命令 来查看编译出来的文件类型 是否是 riscv 架构类型。

代码语言:javascript复制
ubuntu@ubuntu2004:~/helloword$ file helloword
helloword: ELF 64-bit LSB executable, UCB RISC-V, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-riscv64-lp64d.so.1, for GNU/Linux 4.15.0, with debug_info, not stripped
5.2 上传程序到开发板上运行

​ 把编译生成的 hello 文件通过 ssh或者TFTP拷贝到开发板上运行,这里使用TFTP传输,将生成的hello程序拷贝到tftpboot目录下:

代码语言:javascript复制
ubuntu@ubuntu2004:~/helloword$ cp helloword /home/ubuntu/tftpboot

拷贝完成后,查看Ubuntu当前IP地址,输入ifconfig后,获得IP地址为192.168.0.163

进入开发板的串口终端界面,获取tftpboot目录中helloword文件。

代码语言:javascript复制
[root@canaan ~ ]$ tftp -g -r helloword 192.168.0.163
helloword            100% |********************************| 11680  0:00:00 ETA

为helloword程序增加可执行权限

代码语言:javascript复制
[root@canaan ~ ]$ chmod 777 helloword

在开发板端执行helloword程序。

代码语言:javascript复制
[root@canaan ~ ]$ ./helloword
hello word!

6.编译一个简单的AI demo程序

6.1 编译hello AI的demo程序

在任意目录中使用终端获取Hello AI的demo程序,输入以下命令获取

代码语言:javascript复制
git clone https://e.coding.net/weidongshan/dongsahnpi-vision/100ask_hello-ai_demo.git

例如:

如果你无法下载,可访问百度网盘资料中的<开发板名称>/5_DongshanPI-Vision_嵌入式AI应用开发资料/2.Hello-AI_demo程序,该目录有demo程序压缩包,传入Ubuntu并解压即可。

下载完成后进入/home/ubuntu/100ask_base-aiApplication-demo/code,新建hello_ai_demo文件夹

代码语言:javascript复制
ubuntu@ubuntu2004:~$ cd /home/ubuntu/100ask_base-aiApplication-demo/code/
ubuntu@ubuntu2004:~/100ask_base-aiApplication-demo/code$ mkdir hello_ai_demo

进入hello_ai_demo目录下,将获取Hello AI的demo程序拷贝到当前的hello_ai_demo目录下

代码语言:javascript复制
cp /home/ubuntu/100ask_hello-ai_demo/* ./ -rf

拷贝完成后,可以看到hello-ai-demo文件夹下的文件如下所示:

代码语言:javascript复制
ubuntu@ubuntu2004:~/100ask_base-aiApplication-demo/code/hello_ai_demo$ ls
CMakeFiles           cv2_utils.cc  format   main.cc           object_detect.h
cmake_install.cmake  cv2_utils.h   images   Makefile          README.md
CMakeLists.txt       file          include  object_detect.cc

拷贝完成,返回上一级的code目录下,修改code目录下的CMakeLists.txt文件

代码语言:javascript复制
ubuntu@ubuntu2004:~/ai_demo/100ask_base-aiApplication-demo/code/hello_ai_demo$ cd ../
ubuntu@ubuntu2004:~/100ask_base-aiApplication-demo/code$ gedit CMakeLists.txt

在文件末尾增加一句add_subdirectory(hello_ai_demo),修改后的CMakeLists.txt如下所示:

代码语言:javascript复制
cmake_minimum_required(VERSION 3.2)
project(ai C CXX)

#set(DEMO_ROOT "${PROJECT_SOURCE_DIR}/../crb_demo")
set(CMAKE_C_FLAGS_DEBUG "-O0 -g3")
set(CMAKE_C_FLAGS_RELEASE "-O2 -s")
set(CMAKE_CXX_FLAGS_DEBUG "-O0 -g3")
set(CMAKE_CXX_FLAGS_RELEASE "-O2 -s")
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fopenmp")

set(input imx219_1080x1920_0.conf imx219_0.conf imx219_1.conf video_192x320.conf video_object_detect_320.conf video_object_detect_432x368.conf video_object_detect_512.conf video_object_detect_640x480.conf video_object_detect_320x320.conf video_object_detect_480x640.conf video_object_detect_640.conf)
install(PROGRAMS ${input} DESTINATION exe)

add_subdirectory(face_detect)
add_subdirectory(face_landmarks)
add_subdirectory(object_detect)
add_subdirectory(face_alignment)
add_subdirectory(face_expression)
add_subdirectory(head_pose_estimation)
add_subdirectory(face_recog)
add_subdirectory(simple_pose)
add_subdirectory(openpose)
add_subdirectory(person_detect)
add_subdirectory(hand_image_classify)
add_subdirectory(license_plate_recog)
add_subdirectory(self_learning)
add_subdirectory(object_detect_demo)
add_subdirectory(retinaface_mb_320)
add_subdirectory(hello_ai_demo)

修改完成后,按下Crtl s保存修改后的文件。

在code目录的终端下激活环境,输入:

代码语言:javascript复制
ubuntu@ubuntu2004:~/100ask_base-aiApplication-demo/code$ source build.sh

激活环境完成,进入hello_ai_demo目录,开始进行编译Hello AI的demo程序

代码语言:javascript复制
ubuntu@ubuntu2004:~/100ask_base-aiApplication-demo/code$ cd hello_ai_demo/
ubuntu@ubuntu2004:~/100ask_base-aiApplication-demo/code/hello_ai_demo$ make

编译完成,即可在hello_ai_demo目录下查看编译完成的可执行程序,如下图红框中所示文件:

6.2 上传程序到开发板运行

把编译生成的 hello 文件通过 ssh或者TFTP拷贝到开发板上运行,这里使用TFTP传输,将生成的hello_ai_demo程序拷贝到tftpboot目录下:

代码语言:javascript复制
ubuntu@ubuntu2004:~/100ask_base-aiApplication-demo/code/hello_ai_demo$ cp hello_ai_demo ~/tftpboot/

将待检测的图像传入开发板,待测图像已经提前准备在hello_ai_demo目录下的images文件夹下

代码语言:javascript复制
ubuntu@ubuntu2004:~/100ask_base-aiApplication-demo/code/hello_ai_demo$ cd images/
ubuntu@ubuntu2004:~/100ask_base-aiApplication-demo/code/hello_ai_demo/images$ ls
01_person.bmp  02_cat.bmp

将两张图像拷贝到tftpboot目录下:

代码语言:javascript复制
ubuntu@ubuntu2004:~/100ask_base-aiApplication-demo/code/hello_ai_demo/images$ cp ./* ~/tftpboot/

拷贝完成后,查看Ubuntu当前IP地址,输入ifconfig后,获得IP地址为192.168.0.163

进入开发板的串口终端界面,获取tftpboot目录中helloword文件。

代码语言:javascript复制
[root@canaan ~ ]$ tftp -g -r hello_ai_demo  192.168.0.163
hello_ai_demo        100% |********************************|  818k  0:00:00 ETA
[root@canaan ~ ]$ tftp -g -r 01_person.bmp  192.168.0.163
01_person.bmp        100% |********************************|  300k  0:00:00 ETA
[root@canaan ~ ]$ tftp -g -r 02_cat.bmp  192.168.0.163
02_cat.bmp           100% |********************************|  300k  0:00:00 ETA

由于该程序需要使用模型文件对待测图像进行预测,我们可以拷贝开发板中模型文件使用

代码语言:javascript复制
[root@canaan ~ ]$ cp /app/ai/kmodel/kmodel_release/object_detect/yolov5s_320/yol
ov5s_320_sigmoid_bf16_with_preprocess_output_nhwc.kmodel ./

拷贝完成后可以在当前目录看到对应文件。

代码语言:javascript复制
[root@canaan ~ ]$ ls
01_person.bmp
02_cat.bmp
data
emmc
hello_ai_demo
yolov5s_320_sigmoid_bf16_with_preprocess_output_nhwc.kmodel

为hello_ai_demo程序增加可执行权限

代码语言:javascript复制
[root@canaan ~ ]$ chmod 777 hello_ai_demo

在开发板端执行hello_ai_demo程序。

代码语言:javascript复制
./hello_ai_demo ./yolov5s_320_sigmoid_bf16_with_preprocess_output_nhwc.kmodel ./01_person.bmp

执行结果如下所示:

例子1:

代码语言:javascript复制
[root@canaan ~ ]$ ./hello_ai_demo ./yolov5s_320_sigmoid_bf16_with_preprocess_out
put_nhwc.kmodel ./01_person.bmp
hello 100ask AI demo!
==> run inference finished!
image inference result: person:0.89

程序识别出01_person.bmp文件中,有人,概率为0.89。

例子2:

代码语言:javascript复制
[root@canaan ~ ]$ ./hello_ai_demo ./yolov5s_320_sigmoid_bf16_with_preprocess_out
put_nhwc.kmodel ./02_cat.bmp
hello 100ask AI demo!
==> run inference finished!
image inference result: cat:0.86

它识别出02_cat.bmp图片文件中,有猫,概率为0.86。

注意:您可以传入自定义的图像进行预测,需要保证图像格式为bmp,尺寸为320x320。

6.3 hello AI程序讲解
6.3.1 程序框架
6.3.2 程序讲解

从BMP文件提取RGB分量

代码语言:javascript复制
int get_rgb_from_bmp(char *file, uint8_t *rbuf, uint8_t *gbuf, uint8_t *bbuf)
{	
    int err;
	unsigned int *p;
	unsigned int rgb32;
	int r_pos = 0, g_pos = 0, b_pos = 0;

	/* 点阵数据 */
	T_PixelDatas bmpPixelDatas;

	/* 解析BMP文件 */
	err = GetPixelDatasForIcon(file, &bmpPixelDatas);
	if (err)
	{
		DebugPrint("Can not get digit bmp file: %sn", file);
		return -1;
	}

	/* 把里面的RGB单独拆出来 */
	for (int y = 0; y < MIN(320, bmpPixelDatas.iHeight); y  )
	{
		p = (unsigned int *)(bmpPixelDatas.aucPixelDatas   y * bmpPixelDatas.iLineBytes);
		r_pos = g_pos = b_pos = y*320;
		
		for (int x = 0; x < MIN(320, bmpPixelDatas.iWidth); x  )
		{
			rgb32 = p[x];
			/*将RGB三通道的数据分别存入3个buf*/
			rbuf[r_pos  ] = (rgb32 >> 16) & 0xff;
			gbuf[g_pos  ] = (rgb32 >> 8)  & 0xff;
			bbuf[b_pos  ] = (rgb32 >> 0)  & 0xff;
		}
	}

	return 0;
	
}

初始化模型

代码语言:javascript复制
objectDetect od(0.5, 0.45, 320, {320, 320});//置信度、nms阈值、模型尺寸、图像尺寸
od.load_model(argv[1]); //加载模型 yolov5s_320_sigmoid_bf16_with_preprocess_output_nhwc.kmodel
od.prepare_memory(); // memory allocation 准备内存

使用模型

代码语言:javascript复制
	/* 把RGB数据复制进od里 */
	od_r = (uint8_t *)od.virtual_addr_input[0]; 
	od_g = od_r   320*320;
	od_b = od_g   320*320;

	memcpy(od_r, rbuf, 320*320);
	memcpy(od_g, gbuf, 320*320);
	memcpy(od_b, bbuf, 320*320);


	od.set_input(0);//设置输入
	od.set_output();//设施输出
	
	od.run();//运行推理
	
	od.get_output();//获得推理结果

后处理

代码语言:javascript复制
	/*后处理:将推理结果转换为可使用的数据*/
	std::vector<BoxInfo> result;
	od.post_process(result);//运行后处理	

	for (auto r : result)
	{
		std::string text = od.labels[r.label]   ":"   std::to_string(round(r.score * 100) / 100.0).substr(0,4);
		cout<<"image inference result: ";
		cout<<text<<endl;
	}

7.嵌入式开发

7.1编译工具链

工具链是一组编程工具,用于开发软件、创建软件产品。工具链通常是另一个计算机程序或一组相关程序。通常,工具链里有多个工具,前一个工具的输出结果,是下一个工具的输入,也就是说前一个工具处理完,再交给下一个工具处理。

一个简单工具链可能由三部分组成:编译器和链接器(将源代码转换为可执行程序)、库(为操作系统提供接口)和调试器(用于测试和调试创建的程序)。一个复杂的软件产品,如视频游戏,需要准备音效、音乐、纹理、3 维模型和动画的工具,以及将这些资源组合成成品的附加工具。

**GNU ** 工具链 是一个广泛收集的、遵守GNU协议的、众多编程工具。这些工具形成一个工具链,用于开发应用程序和操作系统。

GNU 工具链在Linux、一些BSD系统和嵌入式系统软件的开发中起着至关重要的作用。

7.2 交叉编译工具链

**交叉编译器:**在平台A上使用它能够生成程序,这个程序时运行在平台B上的。例如,在PC上运行程序,但这个程序是在Android 智能手机上运行的,这个编译器就是交叉编译器。

在PC上为其他平台(目标平台)编译代码时,需要交叉编译器。能否直接在目标平台上编译程序?比如在ARM板上编译程序?大多时候不可行,因为ARM板资源很可能受限。

交叉编译器的基本用途是将构建环境与目标环境分开。这在几种情况下很有用:

  • 设备资源极其有限的嵌入式计算机。例如,微波炉将有一个非常小的计算机来读取它的键盘和门传感器,向数字显示器和扬声器提供输出,并控制烹饪食物的机器。这台计算机通常不够强大,无法运行编译器、文件系统或开发环境。
  • 为多目标编译。例如,公司可能希望用同一套代码,支持多个不同的操作系统。通过使用交叉编译器,可以设置单个构建环境,为每个目标系统单独编译程序。
  • 在服务器上编译。服务器性能强大,很多公司都是在服务器上为其他平台编译程序。
  • 引导到新平台。在为新平台开发软件时,人们使用交叉编译器来编译必要的工具,例如操作系统和本地编译器。
7.3 获得合适的交叉编译工具链
  • 获取现成的
    • 来自发行版系统内: Ubuntu 和 Debian 有许多现成的交叉编译器。
  • 来自不同组织:
    • 芯片原厂提供的交叉编译工具链(一般包含在配套的BSP内)。
    • Bootlin社区提供的各种架构工具链 。
    • ARM 官方提供的aarch32 aarch64工具链。
    • Linaro 提供 ARM 和 AArch64 工具链,以及一些早期版本工具链。
    • gnutoolchains 提供可以在windows上运行的交叉编译工具链。
    • riscv-collab 提供的riscv GNU 工具链。
  • 自己编译构建
    • Crosstool-NG,专门构建交叉编译工具链的工具。 迄今为止拥有最可配置选项/并支持多种功能的。
    • 嵌入式Linux构建系统一般都知道如何构建交叉编译工具链:Yocto/OpenEmbedded、Buildroot、OpenWRT等。
  • 参考文档
    • Crosstool-NG 文档,https://github.com/crosstool-ng/crosstool-ng/blob/master/docs/
    • GCC 文档,https://gcc.gnu.org/onlinedocs/
    • Binutils 文档,https://sourceware.org/binutils/docs/
7.4 Makefile构建工具
7. 5 CMake构建工具

CMake 是一个开源的跨平台工具系列,旨在构建、测试和打包软件。 CMake 用于使用简单的平台和编译器独立配置文件来控制软件编译过程,并生成可在您选择的编译器环境中使用的本机 makefile 和工作区。 CMake 工具套件是由 Kitware 创建的,旨在响应 ITK 和 VTK 等开源项目对强大的跨平台构建环境的需求。

CMake 是 Kitware 的商业支持开源软件开发平台集合的一部分。

0 人点赞