继卓大大发布了推文“基于RT106x电磁智能车AI算法”,并将此列为2020年智能车竞赛的比赛项目之一以后,AI算法智能车受到了广大同学和专家的热烈讨论。很多同学都想在自己的赛道上感受一下AI的魅力,但苦于没有文档和工具而无从下手。
在这里向大家介绍我们在RT106x的小车上部署实施AI的方法,以及一个base line的电磁导引神经网络模型,希望能抛砖引玉,激发同学们开动脑筋,将AI在电磁智能车上的应用发扬光大。
先期准备
硬件条件
话不多说,首先必须有电磁智能车一辆,无线透传模块用于采集训练数据(感谢逐飞科技友情赞助)。
下图是我们使用的车模,其中红色框内车身的7个电感,是AI算法中电磁车运行导向的输入电感。
蓝色框内的电感是抓取训练数据时的导航电感,在AI算法中并不使用(在模型部署成功后可以拆除)。
软件环境
- Python:3.7.3
- Keras:2.2.4
- TensorFlow:1.13.1
模型设计
模型的目的是通过车身的电感值来推导出对小车舵机的转向控制命令,这是典型的回归问题。
我们训练了简易的Baseline模型,结构如下:
如图所示,模型只使用了Dense算子——也就是全连接层,在一些文献里称之为多层感知机。模型使用最新一笔7个电感的读数,参数19,301个,内存需求很小,运算速度快,用RT1060哪怕是按16位量化也完全无压力。
不过,各位同学不要以为设计完了模型就万事大吉了,以为有了AI模型就可以忘掉经典算法中调参的痛苦。小编可以负责任地告诉你,痛苦才刚刚开始……
采集数据,训练模型
数据采集和处理
众所周知训练数据是机器学习的重中之重,没有完备的数据AI模型只不过是镜中花水中月,没有任何意义。训练数据对于模型而言就是韩信点兵多多益善,但也一定要小心不要引入错误数据,错误数据会让你出轨~啊不,是让小车出轨,然后就无迹可寻了。小编为了训练数据不知道掉了多少头发,死了多少脑细胞。
我们获取训练数据的方法是:小车运行经典导航算法,由小车的前置电感导航,实时抓取车身的电感数据,转向数据,再通过无线透传模块发送到PC端,PC上的串口工具把数据保存成文本格式。
怎么把文本格式的数据转换成训练数据呢?这就要借助强大的Python了,在脚本里读入文本文件,把7组AD值作为x_train、x_test,对应的转向值pwm作为y_train、y_test,并保存成numpy数组文件以方便训练脚本调用,建议生成四个文件x_train.npy、x_test.npy、y_train.npy、y_test.npy,注意数据格式要符合模型结构,数据范围要压缩至-128~127之间用于制作测试文件。
在数据转换时需要对AD值做一个简单滤波。以下是我们使用的滤波算法仅供参考。
模型训练
在训练脚本读取训练数据numpy文件,并把数据范围标准化至-1~+1的浮点数。并调整数据表形状(reshape)为模型输入参数的结构。
(建议把源代码的若干个相关行分自然段,每个自然段添加一行中文注释。比如reshape的,astype的)
模型函数:
模型训练部分代码(添加少量关键中文注释):
这里保存了临时文件xxx_ctx.h5,为了训练中途意外停止后,重新训练时可以支持断点续练。
优化器使用RMSprop,损失函数使用mean_squared_error。
训练时误差迅速下降,下降速度越来越慢,20轮训练后精度即已接近最终效果。这里显示了训练了120次后的结果:loss=0.0044.
各位看官读到这里是不是脑海里已经有了智能车策马奔腾的画面了,并且迫不及待的想把训练好的模型部署到小车上了?No No No,经常被生活打脸的小编告诉你这是完全不够的。为什么呢?很简单模型过拟合了。AI模型运行时会有误差,迟早导致出轨,那么有哪些误差呢:
- 模型计算的误差,逻辑回归引入的误差,无论训练多少遍一定会存在一定的误差
- 训练数据采集时的误差,前置电感和车身电感距离上的误差
- 累积误差,前两种误差会导致小车偏离轨道以至误差越来越大。积累到在训练数据里没有出现过的程度时,模型就不知该怎么办了,这是出轨的根源(说起来简单,小编其实被虐了很久才悟出来)。
知道了症结所在,就可以对症下药了:尽可能让训练数据里出现小车的各种回归正轨的模式。简单的说就是要引入从偏离轨道状态重返回轨道过程的纠正数据,我们通过两种办法获得纠正数据。
第1种方法:把小车电机配置成手动控制模式,但要保留转向控制功能和数据采集功能。把小车移动到偏离位置,手动推动小车,小车原有的控制算法会矫正方向使小车回到轨道上去。同时要抓取实时数据并保存。如下视频所示!
第2种方法:在用经典算法引导小车时,加入随机干扰,故意让小车偏离轨道一会,同时让控制算法短暂休眠,小车在此期间会出轨,待算法唤醒后赶紧再纠正回来,并且实时保存纠正的数据。如下视频所示
经典算法引导采集数据
总结一下,数据分三部分:
- 经典算法导航的正常数据。
- 手动偏移的纠正数据。
- 经典算法随机干扰的纠正数据。
把三个数据合成一套训练数据。特别注意第2种方法中可能会引入错误数据(小车过度偏离赛道,手动移动小车时小车与赛道平面角度过大或与水平面距离太远),实际操作过程中要小心。由于机器学习的不可解释特性,错误数据或不合适数据会导致不可预知的行为,所以整个过程可能需要多次尝试和调整。
模型部署
经过多轮的训练,我们得到最终的模型文件smartcar.h5。
那么怎么部署到小车上呢,这要借助“NNCU”这一套神器了。NNCU模型转换器可以点击这里从网盘下载,这里有个大礼包,里面有nnCU工具、教学视频、用户手册,还有一个MCU+AI的ppt。通过转换器把模型转换成C文件。这样就可以很方便的集成到原有的工程中了,注意还需要集成一些依赖库和算法库,可以参考性能测试工程(nncie_stub.uvproj)。从网盘中下载解压nncu_test_nighty.7z,运行“nncu_vbgui.bat”,选择模型文件,配置信息如下:
这里我们使用14位来量化模型,对于NNCU工具,9-16位都会使用16位整数来封装。
16位量化和8位量化相比,以牺牲少量模型推理时间和加倍模型数据的代价,换来几乎无损的精度。这里还有个重要的小建议:虽然使用16位整数来表达,但实际最好只使用其中的9-15位(未用的位会自动做有符号扩展),是为了防止模型运算期间的乘累加溢出。
注意输入数据移位指数7位,这意思是说数据里有7个二进制位用于表达分数(或者说是小数)部分。回想前文提到测试集和训练集中数据范围是-128~+127,而模型训练时,模型处理数据范围是-1~+1,所以量化数据里其实是放大到128倍了,也就是7个二进制分数位。
我们再看“输出后处理类型“。由于模型是做预测,后处理需要用户自己处理,不需要执行引擎后处理。中间层、输出层分数位数:由于模型输出是-1~+1之间,10位分辨率也比较够用,这里占用量化总位数14位中的10位来表达分数。大家也可以试试其它的,比如9-13,一般影响极小。点击”干活!”,会跳出后台转换脚本的命令窗口,稍候片刻会在nncu_pc目录下生成model1.nncu.c---模型执行文件。那么怎么使用以及模型量化后性能怎么样呢?
NNCU工具里提供了测试工程“test_mcu”,各位同学可以通过这个工程测试量化后的模型性能,也可以参考这个工程来进行模型移植工作。具体的操作,可以点击看这个视频(末尾部分),也可以看这篇微信文章"在MCU上实现AI深度学习,你想知道的都在这儿"以了解更多。
测试性能,首先需要制作测试数据集。我们把ad_test_dat.npy作为输入,把模型计算结果和pwm_test_label.npy值的平方差作为误差,计算平均差值来评估量化后的模型性能。
把训练集和测试集复制到”datasets\smartcar_ad”目录,命名如下:
点击工具右下角“制作测试数据”按钮。选择迷你数据集中smartcar_ad,点击”干活"。在目录nncu_pc下生成测试文件smartcar_ad.nctv.c。
下一步是测试模型性能(参照帮助视频“nncu_使用入门.mp4”)。拷贝model1.nncu.c, smartcar_ad.nctv.c到目录test_mcu\boards\evkmimxrt1060\demo_apps\nncie_stub中,覆盖原有文件:model.nncu.c,tv.nctv.c。
打开工程mdk\nncie_stub.uvprojx,在helo_world.c中的测试函数CIETest,模型计算测试集求得平均差值。平均差值越小表示模型在测试集的拟合性越高。但注意这是没有累积误差情况下,实际上需要多加入回归正轨的数据,否则即使差值小也不能保证实际效果,只是模型过拟合,在测试集上表现的很好。
CI_RunModelXIP_NoCopyOutput(pvModel, pImg + 8, (void**)&g_out);
是模型执行的函数调用,用法如下:
- pvModel – 模型数据,在model1.nncu.c定义,包含了模型的信息和参数
- pImg + 8 – x_text 也就是7个AD数值
- g_out – 模型输出buffer,根据配置输出为16bit,带有分数位的数据。需要移位得到-128~+127的数值范围才能发送给舵机。
编译工程,通过JLink连接板子,并打开串口程序,接收串口的调试信息。开始debug模式,工程默认使用sram运行程序,等待测试程序执行。执行结束后,串口会输出执行结果:即平均差值,和平均执行时间。
小编训练的模型6000次平均差值是16,每次计算时间1.87dms——0.187ms。
实战演习
经过模拟测试,模型性能符合预期,下一步就是把相关代码集成到电磁车系统中。首先集成nncie库nncie_imxrt10x0.lib,model.nncu.c,以及nncu_mcu\cmsis_nn\Source中的CMSIS_NN相关代码(参照测试工程)。其次集成模型的调用方法:
model1数组就是模型数据,ad_array是7个电感数据(如果12bit,需要移位到8bit),pwm是模型计算的转向值(范围-128~+127)。请注意,制作训练数据时,为了方便训练计算,我们把转向值范围由真实值-420~420压缩到了-128~127,所以在控制舵机时,需要把计算所得的转向值放大到-420~+420。
现在小伙伴可以在赛道上验证模型的实际性能了,可能第一次不会成功,需要多些耐心处理训练数据反复的重新训练,迭代调试了。
结果展示
这是小编训练的模型的实际运行情况(摘除了前置采集装置)
AI电磁车模实际运行情况
未来展望
以上是AI机器学习部署到电磁智能车的全过程,可以验证机器学习在电磁智能车上实施的可行性。在这个过程中遇到很多问题,主要是处理分析训练数据,经过多次迭代训练才得到一个基本可靠的模型。当然还存在一些有待改进地方:
- 小车目前还是匀速运动,没有速度变化,怎样设计更好的模型来引入速度参数?
- 数据获得方式是不是有更好的办法,通过对电磁轨道的模拟,来通过公式工具推导训练数据?
- 怎样可以引入更加丰富的赛道内容,比如交通引导线,指示灯,障碍物?等等。
相信同学们还可以提出更多好的想法,同学们和赛事参与者可以开动脑筋,让AI机器学习的潜力能尽情地发挥!
原文链接:TsinghuaJoking
作者:张岩
推荐阅读
更多嵌入式AI技术相关内容请关注嵌入式AI专栏。