ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

如何利用VisionSeed+树莓派,实现智能小车实时图传系统?

2021-03-31 23:53:53  阅读:219  来源: 互联网

标签:树莓 Pi VisionSeed ffplay 延时 图传 Raspberry 推流


一、概念介绍



1. Raspberry Pi


树莓派[1]其实不用笔者过多介绍,这应该是做的最成功的开源硬件芯片,深受技术和数码爱好者们的拥护。下图摘自淘宝某店家的中文说明图,总之第四代比第三代功能强了很多,而且好多接口都与时俱进了。
图片

2. VisionSeed


VisionSeed[2] 是腾讯优图推出的一款具备 AI 功能的摄像头模组,产品如下图所示。它的体型很小,有点类似 Raspberry Pi Zero,不过麻雀虽小五脏俱全。
右边是整块 VisionSeed 的核心模块,包括 2 个摄像头(一个 UVC 摄像头,一个红外摄像头),剩下一整块都是 AI 计算单元;左边是控制板块,主要是对外的接口,如串口、TypeC 接口。两块直接通过 FP C连接起来。

VisionSeed 模组可以搭载很多 CV 的 AI 能力,目前官方已经推出的有疲劳驾驶监测仪[3],笔者目前参加的智能小车就是正在孵化的另一个项目,期待越来越多的 AI 爱好者们参与进来,把 VisionSeed “玩出花”。


二、系统搭建



本文所介绍的是利用 VisionSeed 和 Raspberry Pi(4B) 搭建的一套基于 FFMPEG 编码+SRS+WIFI 协议+RTMP 协议+FFPLAY 解码播放的完整图传系统,该实时图传全过程示意图如下所示:

1. RTMP推流服务器
推流服务器怎么选择?其实推流服务器有很多种选型,具体该选择哪种比较好?笔者结合自身经验给出 nginx-rtmp 服务和 srs 服务的使用心得和实际对比:
图片
补充说明一下延时这块:首先笔者给出的具体延迟时间并非真正服务器推流的延迟,而是端(VisionSeed 采集到视频)到端(播放设备播放视频)的延迟。
这中间涉及到的环节多且复杂:VisionSeed 的 UVC 视频采集,FFMPEG 编码、推流服务器推流、FFPLAY 解码、以及显示器显示。其中推流服务器推流还包括服务器内部 buffer 缓存、网络数据包拼接和组装、以及网络包的传输等。
2. FFMPEG编码和推流
那么,该怎么捕捉 VisionSeed 摄像头的视频呢
VisionSeed 上的摄像头是 UVC。UVC 全称 Usb Video Class,是一种标准的 USB 视频设备协议,也就是传说中的免驱摄像头。也就是说,这款摄像头可以通过 USB 即插即用,不需要安装驱动。
于是,我们用一根 TypeC 数据线把 VisionSeed 和树莓派连接在一起。如下图所示:
图片
登录 Raspberry Pi,打开 Terminal,查看 UVC 的状态:$ lsusb。如果出现下图红框的部分,就说明 UVC 被系统识别了。
图片
然后,查看 UVC 被挂在哪个节点上:$ ls /dev/video*:
图片
也可以用 v4l2-ctl 进一步仔细查看 /dev/video* 的信息,如命令 v4l2-ctl --device=/dev/video0 --all 可以查看到摄像头的细节信息,命令 v4l2-ctl --list-devices 查看系统中所有的设备等。
图片
”Linux 系统一切皆文件“,也就是说,系统是直接把 /dev/video0 当作文件来进行处理,也就是在 ffmpeg 的 -i 的参数。
那么,怎么用 FFMPEG 做编码呢
虽然本地可以直接用 ffplay 播放 UVC 的原始流,但是要走 RTMP 协议做图传的话,必须要使用编码流。因此必须在搭载 VisionSeed 的 Raspberry Pi 上做编码,然后推流出去。
FFMPEG CMD 功能强大:
  • -i :指定文件输入路径;

  • -an :指不处理音频数据;

  • -vcodec:指定视频 codec;

  • -f:指定视频输出格式。


下述命令就是把从 /dev/video0 获取到的原始视频流编码成 flv,并保存成本地文件 test.flv。


ffmpeg -i /dev/video0 -an -vcodec h264 -f flv rtmp://localhost:${port}/${api}

综上,FFMPEG 对 UVC 原始视频流做编码就完成了。
3. FFPLAY 收流
必须要确保客户端设备和推流端的 Raspberry Pi 处于同一个局域网下,互相可以联通。
笔者因为项目需要,客户端也选择了 Raspberry Pi +显示屏。但是实际上,选择手上的笔记本或者台式机就可以,只要确保安装了 FFMPEG(FFMPEG、FFPLAY和FFPROBE是打包的)。
那么,如何用 FFPLAY 实时显示 RTMP 视频呢
笔者原本以为播放端出不了什么问题,万万没想到 FFPLAY 的问题竟然无比的“坑”。首先,很多类似的网站教程提供的播放命令大多是:ffplay -i rtmp://${ip}:${port}/${api}。
这个命令存在相当大的延时,导致笔者最开始就走错了方向。ffplay 内部有 buffer,因此看到的播放画面其实是好几十秒之前的!
ffplay 播放时间长了会有累积延时,也就是越播放到后来,延时越大。并且画面时不时出现卡顿、有时候还会发现画面帧率不稳定,时快时慢。当推流端/服务端断开时,ffplay画面就卡住了,超过 2 min 也并不会退出。
这些问题该怎样解决呢?下文将会来详细讨论。


三、优化延时



按照以上的流程搭建好之后,就可以在客户端上看到 VisionSeed 的视频画面了。但是,延时巨大,主观感受至少有10s的延迟,所以还需要进一步做优化。
1.  FFMPEG 硬解码
细心的同学在使用 FFMPEG 做编码的时候,应该发现实际编码推流的帧率大约在 18 左右,运行到后来大概稳定在 10 左右,笔者这边的情况如下图所示:

但是,VisionSeed(关闭算法功能后)的原始视频帧率是 28 fps,分辨率是1280x720。由此可见 Raspberry Pi 4B 的 CPU 编码速率跟不上,必须要优化。
虽然这个 CMD 没有开启多线程,但是根据笔者经验来看,即使多线程开满了,也很难满足性能要求。
要知道 Raspberry Pi 4B 是有专用编解码模块的,官方号称性能是 1080p@30fps 的编码能力,而端侧开发就是要“榨干”每一个芯片模组的过程。
查了资料后了解到,Raspberry Pi 的硬解码支持 OPENMAX 标准[4],这是一种类似 vaapi 等多媒体硬件加速的统一接口,因此可以直接用 h264_max 来调用底层硬解码,然后再推送到推流服务器上,命令如下:


ffmpeg -i /dev/video0 -an -vcodec h264_omx -f flv rtmp://localhost:${port}/${api}

此外,对实时性要求再高一点的同学,不妨再多了解些 FFMPEG 的参数,参见 FFmpeg Formats Documentation[5] 和 H.264 Video Encoding Guide[6],笔者下面摘录一些跟效率相关的参数,大家可以选择使用(如果有更多未列出来的,欢迎大家留言补充):







### 延时相关- fflags nobuffer # 减少由于buffer带来的延时,能够做到即时处理。- fflags flush_packets # 马上把packets刷出来。(实际好像没有对降低延时带来作用)- analyzeduration ${整型值|时间} # 流分析时间,数值越长,得到的流信息越多、准确,但是延时上升,默认值是5秒。(对编码流会起作用,原始流应该没啥作用)- max_delay ${整型值|时间} # 设置(解)封装的最大延时。(对封装格式的编码流有作用,原始流应该没啥作用)- framerate ${整型值|时间} # 输入视频的码率,默认值是25。(建议可以用-re,这个是用输入视频的码率)

而笔者最后使用的命令如下:





ffmpeg -r 28 -fflags nobuffer -fflags flush_packets -i /dev/video0 -vf fps=fps=28 -an -vcodec h264_omx -preset slower -tune zerolatency -max_delay 10 -r 28 -video_size 1280x720 -g 50 -b:v 8192k -f flv "rtmp://{RTMPIP}:{RTMPPORT}/live/1"

2. 用 srs 推流服务器,开启优化参数
从最终结果上来看,替换了 srs 服务器之后,时延确实比用 nignx-rtmp 提升了 400 ms。
但是这到底是因为 srs 确实比 nginx-rtmp 优秀呢,还是因为笔者打开 nginx-rtmp 方式不正确,还有待讨论。对这块有了解的大佬们,欢迎留言告知,不胜感激!
修改 srs.conf 如下:










































listen              1935; # rtmp端口 !!可以修改为自己的端口号!!max_connections     1000;srs_log_tank        file;srs_log_file        ./objs/srs.log;http_api {    enabled         on;    listen          1985;}http_server {    enabled         on;    listen          80;    dir             ./objs/nginx/html;}stats {    network         0;    disk            sda sdb xvda xvdb;}vhost __defaultVhost__ {    #最小延迟打开,默认是打开的,该选项打开的时候,mr默认关闭。    min_latency     on;    #Merged-Read,针对RTMP协议,为了提高性能,SRS对于上行的read使用merged-read,即SRS在读写时一次读取N毫秒的数据    mr {        enabled     off;        #默认350ms,范围[300-2000]        #latency     350;    }    #Merged-Write,SRS永远使用Merged-Write,即一次发送N毫秒的包给客户端。这个算法可以将RTMP下行的效率提升5倍左右,范围[350-1800]    mw_latency      100;    #enabled         on;    #https://github.com/simple-rtmp-server/srs/wiki/v2_CN_LowLatency#gop-cache    gop_cache       off;    #配置直播队列的长度,服务器会将数据放在直播队列中,如果超过这个长度就清空到最后一个I帧    #https://github.com/simple-rtmp-server/srs/wiki/v2_CN_LowLatency#%E7%B4%AF%E7%A7%AF%E5%BB%B6%E8%BF%9F    queue_length    10;    #http_flv配置    http_remux {      enabled     on;      mount [vhost]/[app]/[stream].flv;      hstrs  on;    }}

配置修改之后的确实时性得到了很大的提升。
3. 优化 FFPLAY
上文出现的问题,在这里也为大家一一解答。

问题一:ffplay 内部有 buffer,因此看到播放的画面是好几十秒之前的。


解决方法:关闭 buffer!
参考 ffplay Documentation[7],参数 -fflags nobuffer(FFMPEG命令里面也有这个参数)是最关键的。笔者最后采用了:





ffplay -autoexit -fflags nobuffer -fflags flush_packets -flags low_delay -noframedrop -strict very -analyzeduration 600000 -i rtmp://192.168.1.1:2020/live/1

问题二:ffplay 播放时间长了会有累积延时,也就是越播放到后来,延时越大。并且画面时不时出现卡顿、有时候还会发现画面帧率不稳定,时快时慢。


解决方法:自从用了 srs,累积延时的问题就没有了。至于画面不稳定的问题,可能和网络有关,笔者后面也会提到怎么在树莓派上搭建无线 AP 来提供专有无线局域网。
替换到无线 AP 之后,画面卡顿的情况会好很多。但是经过长时间的观察,还是会有帧率不稳定的情况。

问题三:当推流端/服务端断开时,ffplay 画面就卡主了!超过 2 min 也并不会退出。


解决方法:这个其实就是 FFPLAY 的bug !其实,ffplay 提供了几个参数,一个是 -autoexit,但是它对 RTMP 还有 RTSP 都不起作用,当流断开或者网断开的时候, ffplay 还是卡住的。
要想解决就必须修改源码,重新编译 ffplay。修改办法可以参考文档[8] 。
另一个是 -timeout 参数,但是一旦加上它,ffplay 就跑不起来,具体原因参考文章[9]。
4. Raspberry Pi 上搭建无线 AP
怎么基于 Raspberry Pi 搭建无线 AP 是有官方教程的:How to use your Raspberry Pi as a wireless access point[10]。但是,官方教程是有坑的,下文将重点介绍哪些坑需要避开。
无线 AP,全称 Wireless Access Point,其实就是常说的 WIFI 热点,生活中的路由器也是一种无线 AP 设备。
在我们的环境中,可能会在没有无线网环境下,甚至在网络条件很糟糕的环境下。另外Raspberry Pi 4B 本身自带了无线网卡,因此不妨用它搭建无线 AP,客户端直接接入它的网络就可以接受它的流数据。
而且,从网络链路上来说,无线 AP 还能在原来基础上减少路由器转发的环节。网络拓扑图如下图所示,优化的链路环节是把蓝色虚线替换了红色虚线和实线。
图片
接下来,跟着官方教程一步步走:
(1)升级 apt-get 工具
建议国内的小伙伴们替换一下源,笔者早就已经替换到了清华源,教程网上很多,推荐树莓派 3b 更换国内源[11] 。
/etc/apt/sources.list 修改为:




deb http://mirrors.tuna.tsinghua.edu.cn/raspbian/raspbian/ stretch main contrib non-free rpideb-src http://mirrors.tuna.tsinghua.edu.cn/raspbian/raspbian/ stretch main contrib non-free rpi

/etc/apt/sources.list.d/raspi.list修改为:




deb http://mirror.tuna.tsinghua.edu.cn/raspberrypi/ stretch main uideb-src http://mirror.tuna.tsinghua.edu.cn/raspberrypi/ stretch main ui

(2)安装 hostapd 和 dnsmasq
hostapd 就是对外提供热点的主要服务,dnsmasq 则是负责 dns 和 dhcp 作用的。如果操作完毕后,没有搜索到自己设置的热点 WIFI,大概率是 hostapd 的问题(原因:hostapd 没有工作,因此没有对外提供 AP);如果搜到了热点网络,但是一直连不上的话,大概率是 dnsmasq 的问题(原因:dnsmasq 没有工作,没办法为客户端分配 ip)。
Raspberry Pi 为自己分为静态 IP。笔者这里的设置如下:






interface wlan0static ip_address=192.168.1.1/24 # 官方给的是192.168.0.10/24,这个自己灵活配置,这个是这台树莓派在它提供出去的AP网络里面的ip地址denyinterfaces eth0denyinterfaces wlan0

(3)配置 DHCP
笔者这里的配置如下:



interface=wlan0  dhcp-range=192.168.1.6,192.168.1.12,255.255.255.0,24h # ip范围自定义就可以

(4)修改 hostapd 配置
这一个步骤是最容易出问题的步骤,而且官方提供的配置在笔者的环境中并不能起作用。笔者给出自己的配置,并在注释中说明为什么这么配置:



















interface=wlan0#bridge=br0    # 这个一定要去掉,因为笔者不需要做桥接(不需要外网)。country_code=CNhw_mode=a    # g-2.4GHZ;a-5GHZchannel=149    # 5G的信道,网上有的写36有的是0各种,推荐查看下面的信道示意图wmm_enabled=1macaddr_acl=0auth_algs=1ignore_broadcast_ssid=0wpa=2wpa_key_mgmt=WPA-PSKwpa_pairwise=TKIPrsn_pairwise=CCMPssid=${YOUR_NETWORK_NAME} # 对外暴露的wifi名称,请注意不要和已有网络重名wpa_passphrase=${YOUR_NETWORK_PSWD} # wifi的密码ieee80211n=1ieee80211d=1ieee80211ac=1


图片
其实做到这一步之后,基本就达到目标了。一个可用的无线 AP 就搭建好了。客户端只需要连接到刚刚设置的网络,就可以和这台 Raspberry Pi 通信。
终于,笔者把端到端的延时从十几秒优化到 400 ms 左右(一把辛酸泪)!最后,如果您有更多的优化方案,欢迎留言与我讨论~


标签:树莓,Pi,VisionSeed,ffplay,延时,图传,Raspberry,推流
来源: https://blog.51cto.com/15060467/2678882

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有