电子说
本文的三位作者正阳、海洋、阿力,是来自不同公司的工程师,将 Agora SDK 与智能小车结合,开发了一款通过智能小车来实时视频远程看房的创新项目。本文将从方案设计到具体实现,详实分享他们的开发经验。三人也凭借该项目,在7月1日结束的 Agora RTC Hack 上海站编程马拉松获得大奖。
创意与构思
此前听到多很多次“黑客马拉松”这样的活动,一群来自不同地方的人聚在一起,组队、构思、开发,在48小时内做出产品雏形。我们三人抱着去听听别人的创意,重在参与的想法参加了这次比赛。对于想要做的东西,在比赛前也只是有一个大概的方向:
构思的方向依据我们擅长的部分来组合拼接,这就得说到我的两位给力队友海洋、阿力;海洋是嵌入式软件工程师,汽车电子方向,写个驱动做个小车手到擒来。阿力是后端工程师,具备处理服务器端和前端页面的能力。
于是队伍有了嵌入式和云端两部分的能力,技术构思的方向是云端为嵌入式赋能。希望有一个小车,小车可以传递回视频图像,视频图像可以实时传给多个用户,用户在得到授权之后,实现远程对小车的操控。
方案设计与分工
出于这样的构思,实现架构如上图所示。现在有了一个基本架构,也清楚了我们要实现的功能,接下来就是分工了。考虑到我们各有所长,分工如下:
从硬件开发开始
小车采用了是4轮伺服电机驱动,搭配有视频采集模块、伺服电机驱动模块、STM32控制模块和摄像头云台模块,安装后整体效果图如下:
图:效果图
用户在远程操控小车各种动作之前,需要小车通过wifi连接到互联网。用户可以通过上位机(Android App 或网页前端)控制小车前后左右移动或控制云台调整摄像头方向。
视频采集模块包含有 Wi-Fi 模块,可以连接到wifi热点为视频传输提供网络基础。也提供 HDMI 接口与显示器连接,方便用户调试。摄像头通过 USB 的方式与视频采集模块连接,我们采用免驱动的天敏6602型号摄像头,分辨率可以达到640*480,并能够自动调焦。
STM32控制模块采用 Arduino 接口与伺服电机驱动模块连接,STM32模块负责控制电机、云台信号的产生,并由伺服电机驱动模块直接驱动电机工作。伺服电机输入电压为6~12V,直流驱动。
工作原理
小车上的视频采集模块采用了定制的 Android 系统,提供网络连接、指令转发和视频流采集、传输功能。当上位机通过远程服务连接到小车后,上位机可以请求到当前小车摄像头上的视频信息;同时,视频采集模块也将上位机上传来的控制信号解析为指定格式和功能的协议数据,并通过串口发送到 STM32控制模块。
小车上的 STM32控制模块在接收到相关控制信号后,调整输出脉冲信号的占空比,由驱动板转换输出电平后直接控制伺服电机或云台模块做出相应的动作,从而完成上位机用户想要的操控功能。
控制信号协议
对于只需要实现简单的小车控制的话,我们只需要实现通过串口向 STM32控制模块发送控制信号即可,简单的控制信号协议如下:
Android SDK 的定制
开发工具
为了实现我们想要的实时视频与小车的远程控制功能,我们需要采用声网的视频通话 SDK,并运行在 Android 开发板上。开发板,我们选用了 Firefly的 RK3128平台,采用 Cortex-A7 架构四核1.3GHz 处理器、Mali-400MP2 GPU,板载千兆以太网口、2.4GHz Wi-Fi 和蓝牙4.0,支持 Android 与 Ubuntu 双系统。
定制串口驱动
为了实现 RK3128 对小车的控制,我们需要实现 RK3128 通过 USB 转串口模块与 STM32控制模块通信。因此我们首先要重新配置 RK3128 内核,使得 RK3128 支持 USB 转串口驱动程序。
首先下载完 RK3128 Android SDK 并先验证文件 MD5值:
md5sum /path/to/fireprime_android5.1_git_20180510.tar.gzfce0e6d65549939167923260142b2c1e fireprime_android5.1_git_20180510.tar.gz
确认无误后解压:
mkdir -p ~/proj/fireprimecd ~/proj/fireprimetar xvf /path/to/fireprime_android5.1_git_20180510.tar.gzgit reset --hardgit remote add bitbucket https://bitbucket.org/T-Firefly/firenow-lollipop.gitgit pull bitbucket fireprime:fireprime
配置并编译内核:
cd ~/proj/fireprime/kernelmake rk3128-fireprime_defconfigmake menuconfigmake -j8 rk3128-fireprime.img
其中 make menuconfig 这一步需要勾选上 Device Drivers —> USB support —> USB Serial Converyer support —> USB Serial Console device support / USB Generic Serial Driver,并勾选上 CP210x / CH341 / FTDI / PL2303 等常用串口工具设备。
编译 Android 系统:
cd ~/proj/fireprime. build.shmake -j8./mkimage.sh
最后编译完成后烧录分区镜像,并插入 USB 转串口工具查看系统 dmesg 是否出现以下 log 信息:
[ 2213.003173] usb 1-1.3: new full-speed USB device number 6 using rockchip_ehct[ 2213.113759] usb 1-1.3: New USB device found, idVendor=10c4, idProduct=ea60[ 2213.113839] usb 1-1.3: New USB device strings: Mfr=1, Product=2, SerialNumbe3[ 2213.113883] usb 1-1.3: Product: CP2102 USB to UART Bridge Controller[ 2213.113921] usb 1-1.3: Manufacturer: Silicon Labs[ 2213.113956] usb 1-1.3: SerialNumber: 0001[ 2213.120813] cp210x 1-1.3:1.0: cp210x converter detected[ 2213.209852] usb 1-1.3: reset full-speed USB device number 6 using rockchip_et[ 2213.320161] usb 1-1.3: cp210x converter now attached to ttyUSB0
出现串口设备附着到 ttyUSBx,即说明定制串口驱动成功。
以上为全编译 Android SDK 的方法,需要编译 Android 系统,相较于仅编译内核而言比较费时。我们可以在上述 make menuconfig 时将需要的串口驱动程序勾选为 M,通过 make modules 的方法,将驱动编译成.ko文件,然后在 Android 系统开机时自动加载驱动程序:
首先将.ko驱动程序文件复制到 Android 文件系统内
adb shellsumount -o remount ,rw /mkdir /moduleschmod 777 /moduleschown -R nobody:nobody /modulesexitexitadb push ./xxxx.ko /modules
编写启动运行脚本/data/serial.sh
#!/system/bin/shinsmod /modules/xxxx.komknod /dev/ttyUSB c 240 0
修改 init.rc 并添加运行自己的脚本
service serial /system/bin/sh /data/serial.sh user root oneshot
在 App 端实现视频传输
视频传输和信令传输的部分,我们通过声网 Agora SDK 来实现。由于涉及到与嵌入式开发板的结合,我们主要参考的是声网在 Github 提供的各种案例中的抓娃娃机 demo。示例代码中的结构图如下:
示例代码有视频传输的部分,控制信令需要参考声网信令文档自己完成。
声网 SDK 的集成
1. 首先申请 AppID
Android APP 中在 res/values/strings_config.xml 加入如下内容,将 agora_app_id 进行配置
1a486ee31a30xxxxxxxxxx
2. 将.jar 文件拷贝到libs/中
因为用到信令和视频传输两部分,需要两个.jar 文件分别为 agora-rtc-sdk.jar 和 agora-sig-sdk.jar
3. 在 src/main/jniLibs 加入 armeabi-v7a 与其中的.so文件
并在 build.gradle 中确定拥有如下描述:
dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) testCompile 'junit:junit:4.12' compile 'com.android.support:appcompat-v7:23.4.0'}
就此,使用示例代码可以顺利开启视频传输功能。
用信令让 App 控制小车
信令的实现
信令的具体使用方法见声网官网文档中心的参考,这里就不进行详尽描述了。主要使用的函数如下:
// 初始化信令 SDKm_agoraAPI = AgoraAPIOnlySignal.getInstance(context, appID);// 登录 Agora 信令系统m_agoraAPI.login2(appId, account, token, uid, deviceID, retry_time_in_s, retry_count)//////////////点对点测试/////////////// 发送点对点消息m_agoraAPI.messageInstantSend(account, uid, msg, msgID) // 设置对端收到消息回调( m_agoraAPI.onMessageInstantReceive(account, uid, msg){ //code there}/////////////频道测试///////////////// 加入频道m_agoraAPI.channelJoin(channelName)// 发送频道消息m_agoraAPI.messageChannelSend(channelName, msg, msgID)// 设置对端接收到频道消息回调m_agoraAPI.onMessageChannelReceive(channelID, account, uid, msg) { // code there}//////////////////////////////////// 退出 Agora 信令系统m_agoraAPI.logout()
Android App 操作串口
对于小车端的 Android App 得到信令之后需要串口发送数据。因此如何实现 Android App 操作串口。这里简述两种方案:
采用 Android 系统给出的架构进行处理, Android 带有串口demo代码,名称为 SerialPort。这里注意两点,此处的代码依赖于 JNI 工具和 NDK,如果没有完整安装在使用项目代码的时候会出现问题。另外,串口操作不方便使用 Android 模拟器进行测试,对于没有串口的设备,在开启串口的动作时,会报错并可能导致程序退出崩溃。
选择使用 Android 代码发送 shell 命令的方式,直接模拟 Linux 的 shell 控制代码,示例 echo ‘aa’ > /dev/ttyUSB0 将 aa 发送到串口ttyUSB0,这样做的好处时代码本身简单,串口直接调用底层。
对于短时间实现功能来说,方案2是更容易实现的方法,这里需要非常注意的一点,需要重新编译 Android 的 framework 层给 App 赋予 root 权限
当信令解析完成,串口调试通过,就可以实现远程控制小车的行进了。
最后:服务器端的部署
为了实现用户可以方便通过手机或者电脑在线实时看房,我们需要通过 Web 端连接小车的 Android App 端,获取实时传输过来的视频内容。在我们的设想中,用户可以通过远程控制小车,这样可以方便用户了解房屋各个方面的情况。综上所述,我们需要实现如下两个功能:
具有视频连接功能
具有远程遥控功能
幸运的是,通过声网提供的服务,我们可以很便捷的搭建这两个服务。在本项目中,我们使用声网的视频 SDK 实现网页端和小车 App 端的视频连接,通过信令 SDK 发送消息,去控制小车的前后左右行走和摄像头上下左右摆动。
罗列一下我们使用到的工具:
声网视频通话 Web SDK 及文档,用来实现远程的视频交互功能;
声网信令 SDK 及文档,用来实现远程遥控智能小车;
服务器,用于部署静态页面;
实现视频连接和发送消息
先在页面上引入视频和信令的 SDK。然后我们先来实现视频连接。
// 创建 AgoraRTC 实例并加入频道const client = AgoraRTC.CreateClient({mode:"interop"}) client.init(appId, function () { console.log("AgoraRTC client initialized"); client.join(channel_key, CHANNEL_NAME, null, function (uid) { console.log("User " + uid + " join channel successfully") console.log(new Date().toLocaleTimeString()) // do something }}
订阅远端的视频流并播放。
let stream = AgoraRTC.creatStream(merge(defaultConfig.config))localStream.init(() =>{ client.on('stream-added', function (evt) { var stream = evt.stream; console.log("New stream added: " + stream.getId()); console.log("Subscribe ", stream); client.subscribe(stream, function (err) { console.log("Subscribe stream failed", err); }); }); client.on('stream-subscribed', function (evt) { var stream = evt.stream; console.log("Subscribe remote stream successfully: " + stream.getId()); if ($('div#video #agora_remote' + stream.getId()).length === 0) { $('div#video').append(''); } stream.play('agora_remote' + stream.getId()); });})
通过下述方法来实现发送信息。
// 创建信令的对象const signal = Signal(appId)// 在实验条件下,不设置tokenconst token = '_no_need_token'// 登录const session = signal.login(account, token)session.onLoginSuccess = (uid) => { //发送消息给指定的账号 signal.sendMessage(reciveAcount, message)}
完成上述的步骤之后,与小车端设置相同的 appId 和 token(如有必要),设定好对应的参数,我们就可以远程控制小车并获取视频了
图:现场演示
图:48小时改装的小车
全部0条评论
快来发表一下你的评论吧 !