大家好, 我叫james, 是一名hacker. 平时喜欢研究一些可编程的小东西.
本期的主角, m5stack unitv2是一款支持ai运算的独立摄像头. 主要特点如下:
- sigmastar SSD202D 这是联发科子公司生产的mcu, 内置双核Cortex-A7 1.2Ghz
- 通过usb2.0+rtl8188ftv实现无线网络
- 通过usb2.0+sr9900实现有线网络
- 识别办公室/宿舍出入人员是否有正常佩戴口罩
- 发现异常立即报警
- 将每日的数据上传到PC端
- 生成报表
识别办公室出入人员, 由于要在线训练数据, 考虑到隐私问题, 我使用了乐高兼容积木进行 了等效模拟.
本次任务可以细分为3个子步骤: 1. 使用AI摄像头进行Object Recognition. 2. 通过网络 方式使用AI摄像头识别的结果数据, 对数据进行解析. 3. 将解析后的数据保存/上传云端/ 生成报表.
步骤1使用了mstack5提供的基础服务, 能够非常方便的制作自己的模型, 并使用unitv2自带 web ui执行模型. 制作模型的过程中需要自己设计分类, 一开始笔者打算先识别口罩, 然 后识别人脸, 最后用距离公式确认口罩覆盖于人脸之上, 后来想想这种做法稍显复杂, 于是 直接只针对人脸进行识别, 分为带口罩和不带口罩两种. 经过实验测试, 这种直接识别人 脸的方式能够满足任务要求, 看来AI算法模型还是能够通过足够的数据猜测出来我的意图的.
步骤2遇到了一些难题, 首先unitv支持两种方法将结果返回给上位机, 一种方式是通过 uart, 但是这种方法需要一根HY 2.0的线, 还需要支持这种线的上位机. 本来想用funpack 活动第一期的那个mp157c来处理, 它也拥有两个grove接口, 但是后来分析了下, 第一期的 两个grove不是开箱即用, 可能需要做pin配置, 修改或者配置device tree才能够实现, 有 点麻烦, 所以没有采用. 另外一种方法是使用unitv2自带的usb网络. 当然这种方法也遇到 了一些问题, 但是由于我有一些web开发经验, 所以通过纯应用层软件的方式解决了. 这里 可能比较难的是, untiv的web ui输出的结果数据使用了http post+keep alive的传输方式, 这种方式一般的URL/HTTP库是不支持的, 只能拿到最终的结果后才能进行解析, 不能通过 stream的方法解析, 这也就是为什么用普通的http库或者工具要一直卡住, 没有结果显示出 来. 这里, 我使用了emacs-lisp进行编程, 修改了elisp的url库, 能够进行异步解析实时 存入一个全局变量中.
步骤3比较简单, 因为在步骤2中已经拿到了编程中很容易使用的全局变量, 于是我写了两个 timer协程, 用来读取这个全局变量, 其中一个用于告警, 另一个用于记录. 记录是记录到 sqlite数据库中, 方便后面进行统计. 最后根据记录的数据使用sql生成统计报表. 当然步 骤3也能够使用云服务, 而且非常容易, 因为有很多云服务能够直接对json进行解析, 不用 自己解析, 另外云服务也提供的数据记录的存储功能. 可能唯一不好的一点就是时间长了, 数据多了, 用习惯了, 云服务是要收费的^_^. 使用云服务的话直接创建一个新的timer协 程, 将数据使用mqtt协议传入云端即可.
ML基本步骤: 建模 -> 采数 -> 训练 -> 转换 -> 测试
全程使用mstack5 vtraing服务进行, 很简单. 网址是http://v-training.m5stack.com/build/index.html
以下为重要代码片段:
file: a.el
;;; patch url lib (defvar p/unitv-connections (make-hash-table :test 'equal :size 17) "A hash table of unitv connections. (:key process-buffer :value boolean)") (defvar p/unitv-person-count '(0 0 0) "(total masked non-masked") (defun is-unitv-connection (connection) (and (process-buffer connection) (gethash (process-buffer connection) p/unitv-connections))) (defun p/url-generic-parse-url-advice (proc data) (if (is-unitv-connection proc) (p/parse-result proc data))) (advice-mapc (lambda (a p) (print a) (advice-remove 'url-http-generic-filter a)) 'url-http-generic-filter) (advice-add 'url-http-generic-filter :after 'p/url-generic-parse-url-advice) (advice-mapc (lambda (a p) (print a)) 'url-http-generic-filter)
file: a.el
(defun p/parse-result (proc data) (and (/= (length data) 0) (condition-case err (progn (let ((data (json-read-from-string data))) ;;(print data) (setq p/unitv-person-count (cons ;; staff number (alist-get 'num data) (let ((person-list (alist-get 'obj data))) (list (seq-reduce (lambda (count person) ;; masked staff number (if (equal (alist-get 'type person) "face_masked") (+ count 1) count)) person-list 0) (seq-reduce (lambda (count person) ;; non-masked staff number (if (equal (alist-get 'type person) "face") (+ count 1) count)) person-list 0)))))) (print p/unitv-person-count)) (json-error (princ (format "%s" err))))))
(require 'emacsql-sqlite) (defvar db (emacsql-sqlite "~/tmp/test-sqlite.db")) (setq record-timer-handle (run-at-time nil 10 (lambda () ;; Insert some data: (emacsql db [:insert-into mask_record :values $v1] (vconcat (cons (truncate (float-time)) p/unitv-person-count))) )) ) (cancel-timer alert-timer-handle)
本次任务硬件部分主要是模型环境的搭建, 如下图所示:
- unitv2很好用, 用在对象识别任务上非常方便
- m5stack生产的带外壳的开发板单纯从用户体验上比没有外壳的开发板要好一些, 更适合 不会用电烙铁的朋友使用
- 积木还是很好玩儿的
- notebook切换回web ui切换需要重启
- 烫烫烫锟斤拷, 温度需要解决下
- http get+ajax不行吗? 为啥要用post, 但是比websocket强, 那个用起来感觉更麻烦