Rokid Kamino18踩坑记录

发布于 2019-08-13  34 次阅读


折腾了一天的Rokid Kamino18,大体了解了下,记录下来以备后用

概要

拿到的是Rokid AI Dev Board V3.31,该系统采用的是YodaOS系统,可通过usb烧录

官方文档:https://developer.rokid.com/docs/rokidos-linux-docs/Dev_3.31/13_ROKID_AI_Dev_Board_HW_UserGuide_v3.31.html

烧写固件

在折腾yodart的过程中崩了几次,崩了后重新烧写固件就行了= =

固件下载地址:https://yodaos.rokid.com/downloads/,也可通过yodaos源码编译出来

1.将其中的bootx放到/usr/local/bin中,加上可执行权限

2.用usb将板连接到电脑上,直接执行./download.sh all即可,中途根据提示按板上的boot键

编译yodaos

官方文档:https://developer.rokid.com/docs/5-enableVoice/rokid-vsvy-sdk-docs/yodaosSystem/compile-run.html

源码有十几个G,下载要耐心等待。编译时间也挺长,目前还没编译成功,有报错= =

技能开发

官方文档:https://developer.rokid.com/docs/2-RokidDocument/1-SkillsKit/platform-introduction.html

实例

随便做了一个测试了下功能

新建技能

新建技能,技能信息随便填,我用的是私有-云端技能

语音交互

意图定义

{
	"intents": [
		{
			"intent": "what",
			"slots": [],
			"user_says": [
				"是什么垃圾",
				"什么垃圾"
			]
		}
	]
}

参考文档中的意图,词表,用户语句等

通俗点将,意图(intent)就是一种类型的请求,就取个名字,当用户说的话匹配用户语句(user_says)时,即可发起该请求。然后user_says中可以使用$xxx,对应slots中的一项,发起请求时会带上所有slots。slots的配置中有个type,即对应一个词表,自定义词表在页面下方配置

需要的功能是能处理任何垃圾,而词表只能是指定的一个列表(不清楚,还没找到用任意词的方法),所以没有用slots,服务端直接对原始的语句进行处理

配置

直接用的Rokid Force,可通过npm的request方法请求指定地址完成具体功能。通过Rokid对象能获取一些值,如语句,intent等。

exports.handler = function(event, context, callback) {
    var rokid = Rokid.handler(event, context,callback);
    rokid.registerHandlers(handlers);
    rokid.execute();
};
    
var handlers = {
    'ROKID.INTENT.WELCOME':function() {
        try {
            this.setTts({tts:'垃圾'});
            this.emit(':done');
        } catch (e) {
            this.emit(':error', e);
        }
    },
    'what':function() {
        var ttsRes = '', self = this;
        Rokid.request({
            method: 'POST',
            url: '××××××××××',
            headers:{
                'User-Agent': 'request'
            },
            form:{
                'sentence':Rokid.param.request.content.sentence,
                'intent':Rokid.param.request.content.intent
            }
        }, function (error, response, body) {
            if (error) {
                self.emit(':error', error);
            }
            self.setTts({ tts: body });
            self.emit(':done');
        });
    },
    'ROKID.INTENT.EXIT':function() {
        try {
            console.log('技能退出成功');
            this.emit(':done');
        } catch (e) {
            this.emit(':error', e);
        }
    }
};

只需要再具体实现接口即可

集成测试

可通过手机绑定设备,再通过“添加账号下绑定设备”添加测试设备

访问和传输文件

可通过adb,访问板上的文件

首先挂载系统为可读

 adb shell mount -o remount,rw /

然后通过adb pushadb pull即可传输文件,还可使用adb shell执行命令

YodaRT

yodaos中的js层,https://github.com/yodaos-project/yodart

  • apps 集成在系统中的本地app,包括【蓝牙音乐】、【配网】、【音量】等
  • apps/cloudappclient 云端App的本地通用客户端,处理云端App下发的逻辑,包括【若琪音乐】、【天气】、【新闻】等
  • include 构建时依赖的头文件
  • packages 通用模块接口,用于与系统底层服务交互,包括【日志】、【蓝牙】、【音频】、【按键】等
  • res 资源文件包括灯光和音效
  • runtime YODAOS的核心服务
    • activation 激活服务,在设备被激活时播放唤醒词
    • lightd 提供灯光渲染服务
    • multimediad 提供多媒体播放服务
    • otad 提供OTA升级服务
    • ttsd 提供TTS播放服务
    • vuid VUI交互服务,处理用户的NLP
  • test 单元测试
  • tools 调试工具

测试时需要安装adb。刚开始一直各种报错,后来更换了yodart的版本(用的版本是apilevel-0.6),就能用了,不过代码也做了一些改动。

tools/runtime-install,主要是把相对路径修改为绝对路径,并把push中的*号去掉

#!/usr/bin/env bash
set -e

help="
Usage:
  -c          Install configuration files.
  -t          Only includes tests.
  -r          Only includes resources.
  -a          Include tests and resources.
  --no-app    Exclude apps.
  --no-os     Exclude runtime and packages.

  -s          Select which device to be installed on if multiple devices presents.

Runtime installation by default, includes all packages, runtime, **no tests and resources**.

Example:
  $ ./tools/runtime-install
  $ ./tools/runtime-install -s 0502031835000257
"

os="YES"
test="NO"
configs="NO"
resources="NO"
exclude_app='NO'
sn=""

while [ $# -gt 0 ]; do
  case "$1" in
    -c)
      configs="YES"
      ;;
    -t)
      test="YES"
      ;;
    --no-os)
      os="NO"
      ;;
    -r)
      resources="YES"
      os="NO"
      ;;
    -a)
      test="YES"
      resources="YES"
      ;;
    --no-app)
      exclude_app="YES"
      ;;
    -s)
      sn="$2"
      shift
      ;;
    -h|--help)
      printf "$help"
      exit
      ;;
    --*)
      echo "Illegal option $1"
      ;;
  esac
  shift $(( $# > 0 ? 1 : 0 ))
done

function shell() {
  if test "$sn" != ""; then
    adb -s "$sn" shell $1
  else
    adb shell $1
  fi
}

function push() {
  echo "installing from $1 to $2"
  if test "$sn" != ""; then
    adb -s "$sn" push $1 $2 >/dev/null
  else
    adb push $1 $2 >/dev/null
  fi
}

function install_os() {
  # yoda runtime
  shell "rm -rf /usr/yoda/*"
  push "/home/likole/Documents/WebstormProjects/yodart-apilevel-0.6/runtime/" "/usr/yoda/"

  # node_modules
  shell "mkdir -p /usr/lib/node_modules/@yoda"
  push "/home/likole/Documents/WebstormProjects/yodart-apilevel-0.6/packages/" "/usr/lib/node_modules"
}

function install_test() {
  shell "rm -rf /usr/lib/node_modules/tape"
  shell "mkdir -p /usr/lib/node_modules/tape"
  push "/home/likole/Documents/WebstormProjects/yodart-apilevel-0.6/node_modules/tape/" "/usr/lib/node_modules/tape"
  push "/home/likole/Documents/WebstormProjects/yodart-apilevel-0.6/node_modules/@yoda/" "/usr/lib/node_modules/@yoda"

  shell "rm -rf /data/workspace/test"
  shell "mkdir -p /data/workspace/test"
  push "/home/likole/Documents/WebstormProjects/yodart-apilevel-0.6/test/" "/data/workspace/test"
}

function install_resources() {
  shell "rm -rf /opt/media"
  shell "rm -rf /opt/light"
  push "./res/light" "/opt/"
  push "./res/media" "/opt/"
}

function install_apps {
  # apps
  shell "rm -rf /opt/apps/*"
  push "/home/likole/Documents/WebstormProjects/yodart-apilevel-0.6/apps/" "/opt/apps/"
}

function install_configs() {
  # etc config
  shell "rm -rf /etc/yoda/*"
  push "/home/likole/Documents/WebstormProjects/yodart-apilevel-0.6/etc/" "/etc/yoda/"
}

shell "mount -o remount,rw /"

if test "$os" = "YES"; then
  install_os
fi

if test "$resources" = "YES"; then
  install_resources
fi

if test "$test" = 'YES'; then
  install_test
fi

if test "$exclude_app" = 'NO'; then
  install_apps
fi

if test "$configs" = 'YES'; then
  install_configs
fi

tools/test去掉第一行的-i

然后执行npm test就能运行测试,npm restart可进行部署

目前还没有研究怎么开发应用,不过可以更改自带的一些应用,挺好玩的= =

Webhook

正常交互时都需要用户主动唤醒,而通过webhook可以通过接口直接使用音响tts播报、播放音乐流媒体及asr指令的能力

官方文档:https://developer.rokid.com/docs/rokid-homebase-docs/webhook/

请求参数中的序列号可以从APP中“更多设置-》设备信息”查看

利用asr实现tts的功能

不懂为啥tts使用不了,然后text以tts开头也用不了,所以就自己利用前置拦截器实现了下

1.在“产品管理-》服务工具-》拦截器设置”中设置前置拦截器

2.拦截器内容

<?php
header('Content-Type: application/json;charset=utf-8');

$post = json_decode(file_get_contents('php://input'), true);
if(strpos($post["sentence"], "tts:") === 0){
 ?>
{
"slots": {
  "tts": {
    "type": "tts",
    "value": "<?=substr($post['sentence'],4)?>"
  },
  "from": {
    "type": "from",
    "value": "likole"
  },
  "asr": {
    "type": "asr",
    "value": "你好"
  }
},
"app_id": "E33FCE60E7294A61B84C43C1A171DFD8",
"intent": "chat"
}
<?php
}
?>

3.然后按指定格式写text就行了

{
  "type": "asr",
  "devices": {
    "sn": "××××××××××××"
  },
  "data": {
    "text": "tts:××××××"
  }
}

创建产品&烧写sn和seed

创建产品挺简单的,然后创建完成后就有10个测试sn了(其实折腾了好久才知道这样能将板子和网页上的控制台连接起来,然后就能使用拦截器之类的实现更多功能了)

然后烧写这边折腾了挺久,需要三个码,不然无法配网

进入产品,在SDK下载中获取设备TypeID(一开始不知道有这个id,一直配网失败),然后在设备SN中的导入记录中可以获取sn和seed(页面上没有seed,需要下载下来才有)

adb shell,然后

  • factory 0 0 ---进入ftm模式,系统会重启,待系统重启后再执行下面步骤;

再次adb shell,然后

  • factory 9 1 xxx ---写入seed,xxx是具体seed number
  • factory 9 3 xxx ---写入sn,xxx是具体sn number
  • factory 9 5 xxx ---写入devicetypeid,xxx是具体devicetypeid number
  • factory 0 3 ---重启并进入正常模式;

拦截器

官方文档:https://rokid.github.io/docs/3-ApiReference/rokid-interceptor.html

响应结果的优先级为前置拦截器最高(如果前置拦截器有正常响应结果)。如果前置拦截器,自带的nlp都没有结果时,使用后置拦截器的结果