项目结构:
项目入口:
application 在 onCreate 初始化
CrashReport.initCrashReport(getContext(), BUGLY_APPID, true);
DemoHelper.getInstance().init(this); //环信
//全局异常捕获 防止crash 一般的是点击
// (必须onCreate执行完成之后才能捕获) 现在先别加了, 后期上线再加吧
//initNoCrash();
Utils.init(this);//工具类
initMusic(); //音乐服务
setupGreenDao(); //db
initFont();// 字体
testDebug();//debug leak
initManager(); //设备底层串口通讯、sp本地存储
initMqttService();//开启服务 初始化did 机智云
initAlarm();//开启每天晚上12点的闹钟 用来设置数据,更改数据 在EveryDayReveiver中
initJPush(); //初始化jpush 极光推送 App绑定:
在application 启动 ReceiveMqttService 服务,在服务中初始化机智云设备SDK–JGAgent。
在服务中,初始化JGAgent并设置MyCallback监听收到的信息。
startService(new Intent(this, ReceiveMqttService.class)); ReceiveMqttService 的 onCreate 函数中初始化 _JAGent_。
fileName = "gw.xml";
fileOutName = (getFilesDir().getAbsolutePath() +"/"+ fileName);
copyJson();
test.setMyCallback(new MyCallback());//设置监听收到消息
initdata(test);
//通过gw.xml 文件 初始化jagent
private void initdata(final JGAgent test) {
if(did == null){
did = test.initDevice(fileOutName, MacUtil.getMacAddress());
System.out.println("kidrobotlog did:"+did);
if(did != null){
Log.e("get the cloud did:", did);
}
System.out.println("check ota!");
test.checkOTA(did);
// run the device
test.tickLoop();
}
}
扫码:
用的是zxing,扫描页面为 ui/ 目录下 CaptureActivity,zxing的相关代码在 module/ 目录下。其中关于调用摄像头并分析采集数据的代码已经用 USB 摄像头 调用库 类 CameraCapture 中的接口替换 android Camera 中的函数 。主要修改的地方是在 zxing/ 目录下的CameraManager类中。
//初始化摄像头、开启摄像头
mCameraCap = ScanCamera.getInstance();
mCameraCap.initCameraAndStart(context,holder);
//关闭摄像头
if (mCameraCap != null) {
mCameraCap.closeCamera();
mCameraCap = null;
}
//预览
if (mCameraCap != null) {
mCameraCap.startCap();
}主页:
主页HomeActivity是6大模块、每日任务、设置的入口。
- 商品中心:已完成功能
- 任务中心:已完成功能
- 音乐中心:已完成功能(视频暂时砍掉,项目里有一个jiecaovideoplayer的库依赖,用于视频播放,这些页面暂- 时放在module里面的video/目录下)
- 成长中心:完成当前设计的UI显示,勋章、成长数据后台没有(可能会推翻当前设计)
- 拍摄中心:通过USB摄像头完成拍照,后台保存照片,录像和视频管理未做
- 游戏中心:完成一个页面主框架,各页面细分还未有设计,互动类的线下游戏设计没完成
- 每日任务:已完成功能
- 设置中心:已完成重置wifi、升级、关于等功能,其余界面UI按照目前设计已完成UI填充,内容展示目前没有内容
与App通讯:
通过机智云透传消息的接口与App进行通讯。通讯分为上行(上报)、下行(下发),通讯接口的调用都在net/ 包下。与App的通讯协议是项目根目录下文件:本体和app通讯协议.MD ,此协议目前还不完整,以后如果有新的指令需要根据协议目前的json格式再加进去。
指令上行ReportHelper
通过JGAgent的sendData接口发送状态到App端
/**
* did: 设备向云端注册得到的唯一标识
* data: 设备上报给app的数据内容
* type: 传输的类型,1:为使用机智云P0协议,2:使用透传方式传输数据内容
*/
public native int sendData(String did, String data, String type); 例:上报健康值饥饿值
String HungerAndHealthReport = "{ "key": "HungerAndHealthReport", "value":{"hungerValue":%s,"healthValue":%s}}";
String format = String.format(HungerAndHealthReport, currentHungerValue, currentHealthValue);
mJGAgent.sendData(ChildApp.getSpManager().getDid(), format, "2"); 指令下发IssuedHelperJson
通过JGAgent的setMyCallback来接收App端发来的数据byte[],然后转换成string再根据协议去解析数据指令
MyCallback() {
@Override
public void func(String pk, String did, byte action, byte[] arrays) {
int length = arrays.length;
if (length == 0) {
return;
}
String s = HexToStringUtils.HexToString(arrays);////////////这个就是发送过来的Stirng ArsII值
String jsonString = HexToStringUtils.hexStr2Str(s);// 这个就是转换的jsonstring
Message messag = Message.obtain();
messag.obj = jsonString;
hand.sendMessage(messag); //发往主线程去解析
}
});
Handler hand = new Handler(){
public void handleMessage(android.os.Message msg) {
String s = (String) msg.obj;
IssuedHelperJson.parseJson(s);
Pop.showSafe(s);
};
};
透传协议格式
通讯协议定位json格式,通过key值的变化,区分不同的指令,再解析value的具体内容
例如以下两个接口:
1.1 静默开始和结束时间
flag: Silence
说明: h.m.s暂时为Long 主要就是当app设置静默时间段的时候,需要上报给服务器 startTime: 是自 1970 年 1 月 1 日起的绝对时间 Long值 endTime: 是自 1970 年 1 月 1 日起的绝对时间 Long值
{ "key": "Silence", "value":{"StartTime":12312,"EndTime":123123}}
2.2 重启机器人
flag: RobotRestart
说明: value为boolean
{ "key": "RobotRestart", "value":true}视频通话:
用的是环信SDK,从环信官网的demo项目里面把关于初始化、注册账号、登陆、视频通话的代码扣下来完成本体与App的视频连接。环信的接口都封装在module/目录下的类DemoHelper中。
DemoHelper.getInstance().init(this); //在application ,环信初始化
//注册、登陆,再根据环信连接状态来做保持上线的操作
if (!DemoHelper.getInstance().isEmRegister()) {
DemoHelper.getInstance().registerEM(EM_NAME);
} else {
if (!DemoHelper.getInstance().isLoginIn()) {
DemoHelper.getInstance().loginEM(EM_NAME);
} else if (!DemoHelper.getInstance().isConnEmServer()) {
EMClient.getInstance().logout(true);
DemoHelper.getInstance().loginEM(EM_NAME);
}
}
//环信账号是否在线
EMClient.getInstance().addConnectionListener(emConnectListener);
//在广播接收者CallReceiver 中接收来电通知时,启动是视频通话页面VideoCallActivity
context.startActivity(new Intent(context, VideoCallActivity.class).
putExtra("username", from).putExtra("isComingCall", true).
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));由于摄像头不是android 标准的摄像头,采用的USB的摄像头来采集视频数据,所以视频通话也做了相应改变
//设置视频通话 自采集外部数据(在接通视频通话前)
EMClient.getInstance().callManager().getCallOptions().setEnableExternalVideoData(true);
//配置环信外部视频数据的分辨率,buffer为一帧数据
EMClient.getInstance().callManager().inputExternalVideoData(data,w , h, 90); 鉴于目前的摄像头分辨率不是屏幕的分辨率,以后可能会有改动采集数据数据宽 w,高h
设备控制:
设备控制功能都写在项目依赖的devicelib模块里。讯飞语音的接口也写到这个库模块ifly/目录下。设备通过串口控制硬件在control/目录下。所有设备控制的接口各自对应的有封装工具类,工具类的对象获取都在lib包名下ApiDevice中,由它来创建对象,调用接口,做到统一处理。
初始化:
// application 中初始化设备库接口管理
mApiManager = ApiDevice.newInstance(getContext());
//获取设备控制
ApiDevice apiManager = ChildApp.getApiManager();
获取各种工具接口管理示例:
mSubControl = ChildApp.getApiManager().getSubControl();//底板串口管理
mBoard = ChildApp.getApiManager().getBodyControl();//胳膊、串口管理
mUart = ChildApp.getApiManager().getUartUtils();//讯飞板唤醒窗口管理- 讯飞语音
由于调用讯飞SDK的接口结果都是在回调里面,而且接口比较多,所以我另行封装了一层。听写–类IDictate、合成语音—类ISynthetic、理解—类IUnderstand,这3个功能调用又统一在类IFlyManager公开给主模块调用。
// 听写功能,开始听写、停止听写
public void write(IDictateListener listener) {
if (mIDictate == null) {
mIDictate = new IDictate(mContext);
}
mIDictate.dictate(listener);
}
public void stopWrite() {
if (mIDictate != null)
mIDictate.stopDictate();
}
// 合成功能,开始合成、停止合成、设置合成人、合成音量
/**
* 合成语音
*
* @param content 合成内容
* @param listener 合成结束监听
*/
public void speak(String content, ISyntheticListener listener) {
if (TextUtils.isEmpty(content)) {
content = "没有合成语音内容";
}
getISynthetic();
mISynthetic.syntheticVoice(content, listener);
}
public void stopSpeak() {
if (mISynthetic != null)
mISynthetic.stopSynthetic();
}
public void setNicker(String nicker) {
getISynthetic();
mISynthetic.setNicker(nicker);
}
public void setVolume(String volume) {
getISynthetic();
mISynthetic.setVolume(volume);
}
//语义理解功能,开始、停止理解(目前是实时采集音频进行语义理解,如果需要文本语义功能,需要修改调用IUnderstand的接口,文本理解的接口也提炼在 类IUnderstand)
public void understand(ISpeechUndListener listener) {
getIUnderstand();
mIUnderstand.speechUnderstand(listener);
}
public void stopUnderStand() {
if (mIUnderstand != null)
mIUnderstand.stopUnderStand();
} - 设备控制硬件
硬件控制都是通过JNI 调用串口来实现,JNI 的源码都在招朋和郝建华那里。目前关于控制底盘轮子行走,前进、后退、转向、头部表情控制和超声等传感器状态获取的接口是郝建华封装给我的,招朋完成接下来手臂控制、头部转向、俯仰、讯飞唤醒串口通知、手环zigbee信号控制收发命令的接口。
底盘控 —
SubControlBoard
so库,JNI 层的java层的调用接口的SubBoardActivity,这个文件放在项目主模块的ui/test/目录下,我根据郝工的demo接口,稍稍修改了一下,写了一个工具类SubControlUtils,此工具类在control/包下,具体如何使用对照着代码一看就知。
手臂、头部控制 —
BodyControlBoard
demo项目源码,包括编写so库的c文件,demo名称bodyMoveControlBoard1,其接口和SubControlBoard类似,为了简化调用也已稍稍封装接口–类名BodyControlUtils
讯飞板唤醒串口通知Java层 —
UartRead
demo 项目源码,包括编写so库的c文件,demo名称voiceUart,接口与之前类似,简化后封装工具类UartUtils
……
其一般调用方式:
// 创建对象,初始化打开串口
mBoard = new SubControlBoard();
int ret = mBoard.subBoard_hardware_init();
isInitBoard = ret == 0 ? true : false;
if (isInitBoard) {
Log.e(TAG, "initBoard: success");
}
// 通过串口发送协议指令
int ret = mBoard .subBoard_msg_send(mBoard .CHASSIC_BOARD, mBoard .CHASSIC_CMD_SET_BACKUP, param1, 2);
//关闭串口
mBoard .subBoard_hardware_exit(); 拍摄:
由于用USB摄像头替换标准摄像头,android SDK 的类Camera 不能使用,拍照功能需要通过 JNI 的方式去使用USB摄像头库来的接口来采集数据,生成照片。拍摄中心 — ShotCenterActivity
对中航王晶王工提供的库进行封装工具类 – CameraPreview和CameraPreview2 使用。
//获取展示的SurfaceView关联的Holder
mHolder1 = mVideo1.getHolder();
mHolder1.addCallback(new SurfaceHolder.Callback() {
@Override
public void surfaceCreated(SurfaceHolder holder) {
mHolder1 = holder;
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
mHolder1 = null;
mCamera1.stop();
}
});
mHolder1.setType(SurfaceHolder.SURFACE_TYPE_NORMAL);
//开启摄像头1
mCamera1 = new CameraPreview.Builder()
.handler(mHandler)
.isPreview(true)
.holder(mHolder1)
.build();
mCamera1.open();
//开始摄像头2
mCamera2 = new CameraPreview2.Builder()
.handler(mHandler)
.isPreview(true)
.holder(mHolder2)
.build();
mCamera2.open();
//停止摄像头使用
mCamera1.stop();
mCamera2.stop();
//拍照
final String path = Environment.getExternalStorageDirectory().getAbsolutePath() + "/" + "left" + ".jpg";
mCamera1.takePicture(path, new PicListener() {
@Override
public void onSuccess() {
Log.i(TAG, "onSuccess: ");
//上传文件
uploadAndRecordPictureToCloud(path);
}
@Override
public void onFailed() {
Log.i(TAG, "onFailed: ");
}
}); 游戏:
游戏分为线上游戏和线下互动游戏.线上游戏是指直接在设备上通过触屏就可以玩的游戏;互动游戏是指需要小孩儿完成某些动作的游戏。
- 线上游戏
目前根据中航之前提供的UI和资源写了一个过家家游戏,其页面都在ui/activity/game/ 目录下。但是根据中航王工说UI设计未通过,他们会重新设计,加一些动态效果… - 线下游戏
与中航边部沟通时他的想法
游戏标准:有根据“完成时间长短”(可每10分钟为完成差距)获取分数,区分完成差异
游戏要有趣,不需要特别精准
- 木头人:
1) 宠物运动检测 —– 识别小孩运动变化
2)人脸识别 —— 识别人脸框位置变化,识别人脸表情变化,识别相似度变化
** 边部提出说王总之前承诺过可找到识别资源
** 我未找到免费可应用于项目的表情识别库,识别人脸框也是库直接画在屏幕上,没有实际的位置信息,相似度的功能已找到一个
** 王晶试过百度识别人脸,有表情变化,需要企业认证开发者账号,已在认证,得3-5天认证成功才能下载
sdk和demo - 跟屁虫:
双目检测——–双目检测深度变化,1s内,3-5次,取最近点的深度变化(提出找不到就找不到)
我提出:双摄像头获取的数据的帧率不一样(一个摄像头可能是7-12,一个摄像头只有2-4);双目不一定能够检测到“腿”这个障碍物;做不到识别前后次照片同一个“腿” 这个障碍物所表示的数据
游戏意外情况——-机器人在桌子上
** 红外防跌落预警时,实在无法跟踪,就语音提示跟不上
捉迷藏:
zigbee手环串口通信——–试探式移动,根据手环信号强度变化确定方位,行进途中自主避障
完成条件:识别到人脸(孩子的人脸图片先预设好)
找不到:语音提示跟我做
机器人做动作,音效提示,图片提示,一定时间后,就算完成
最近几周我的尝试:
- 木头人,用宠物的库试过,没试成功;百度人脸识别用其android SDK采集数据解析不了,和王工沟通应该是usb摄像头采集数据格式 nv21 ,而百度的SDK不是识别的这种格式。也尝试过王工说的Java SDK 的人脸识别,这个是通过识别照片文件来采集数据了,好像是可以拿到识别信息。必须有网,而且这个识别信息获取的时间很慢大概最少10s时间,不知道是否能满足要求
- 跟屁虫,应该是无法用以上方案(双目视觉算法)去实现效果
- 捉迷藏,zigbee手环通信刚刚实现通信控制,信号强度没在手环发送的数据里面看到
- 跟我做,从音效和画面,肢体动作的丰富来弥补互动的缺失,需要设计一个详细的执行流程和炫酷的页面效果,需要中航给出具体的方案和ui设计
- 本文作者: 风追着云
- 本文链接: https:/github.com/tuyrt7/tuyrt7.github.io/2017/07/19/本体项目文档介绍/
- 版权声明: 本博客所有文章除特别声明外,均采用 MIT 许可协议。转载请注明出处!

SongTaste