У меня есть настройка диалогового потока на Raspberry Pi для голосового помощника, и пока вся система работает, я сталкиваюсь с необычной проблемой.
Детали среды
- ОС: Raspbian Stretch - Raspberry Pi 3B +
- Node.js версия: 8.15.0
- npm версия: 5.6.0
dialogflow
версия: 0.8.0
Когда я изначальнозагрузите систему и отправьте аудиопоток с микрофона в диалоговое окно для обработки в реальном времени; результат возвращается через ~ 21 секунду.Последующие аудио-запросы возвращаются правильно в течение 1-2 секунд.Это поведение также появляется после определенного периода бездействия, т. Е. Если система уже запущена, и я не делаю голосовой запрос в течение ~ 3-5 минут.
Кроме того, задержка первого ответа последовательно воспроизводится вплоть домиллисекунда почти!Я добавил время, чтобы убедиться в этом.Изображение показывает, что первый ответ (красные стрелки) последовательно занимает 21.xx секунд, чтобы вернуться.Последующие ответы находятся в диапазоне 1-2 с.
Я принудительно закрываю поток через 4000 мс.Интересно то, что во время моей регистрации, когда поток принудительно отключен, событие unpipe
запускается, но событие finish
не запускается сразу, даже если .end()
правильно вызывается в потоке в событии unpipe
обработчик.Это происходит только при первом запросе или после периода бездействия, а событие финиша срабатывает только после того, как диалоговое окно отправит результат обратно через ~ 21 секунду.Это как если бы поток принудительно оставался открытым и вызов .end () для него не имеет никакого эффекта.
Вот мой код:
const record = require('node-record-lpcm16')
const path = require('path')
const os = require('os')
const config = require(path.join(process.cwd(),"app","config","config"))
var FileWriter = require('wav').FileWriter
// SNOWBOY WAKEWORD DETECTOR
const Detector = require('snowboy').Detector;
const Models = require('snowboy').Models;
const through2 = require('through2')
const models = new Models();
var fs = require('fs')
models.add({
file: path.join(process.cwd(), 'app','config', config.speech.model),
sensitivity: config.speech.sensitivity,
hotwords: 'peeqo'
})
const snowboyDetector = new Detector({
resource: path.join(process.cwd(), 'app','config', 'common.res'),
models: models,
audioGain: 2.0
})
const snowboyOn = true
// NODE RECORD
var recorder = (os.arch()=='arm')?'arecord':'rec'
const opts = {
verbose: false,
threshold: 0,
recordProgram: recorder,
sampleRateHertz: 16000
}
const dialogflow = require('dialogflow')
const speech = new dialogflow.SessionsClient({
projectId: config.speech.projectId,
keyFilename: path.join(process.cwd(), 'app','config', config.speech.dialogflowKey)
})
const sessionPath = speech.sessionPath(config.speech.projectId, "test-app")
const request = {
session: sessionPath,
queryParams: {
session: speech.sessionPath(config.speech.projectId, "test-app")
},
queryInput:{
audioConfig:{
audioEncoding: "AUDIO_ENCODING_LINEAR_16",
sampleRateHertz: 16000,
languageCode: 'en-US'
}
},
singleUtterance: true,
interimResults: true
}
// DIALOGFLOW CLASS
class GoogleSpeech {
constructor(request){
this.request = request
this.stream = speech.streamingDetectIntent()
this.result = ''
this.unpipeTimeout = null
this.listenFor = 4000 //cut off stream after this duration
this.stream.write(this.request)
}
startStream(){
var self = this
this.stream.on('pipe', function(){
console.log('PIPING > GOOGLE')
self.unpipeTimeout = setTimeout(()=>{
// cut off listening since time limit exceeded
console.log("UNPIPING GOOGLE MYSELF")
self.unpipeTimeout = null
tstream.unpipe(self.stream)
mic.unpipe(tstream)
}, self.listenFor)
})
this.stream.on('error', function(err){
console.error('ERROR > GOOGLE', err)
})
this.stream.on('close', function(){
console.log('GOOGLE PIPE > CLOSED')
})
this.stream.on('data', function(data){
if(data.recognitionResult){
console.log(`Interim result: ${data.recognitionResult.transcript}`)
}
if(data.queryResult != null){
self.result = data.queryResult.queryText
tstream.unpipe(self.stream)
mic.unpipe(tstream)
}
})
this.stream.on('unpipe', function(src){
console.log('UNPIPING > GOOGLE')
tstream.end()
self.stream.end()
})
this.stream.on('finish', function(){
console.log('FINISHED > GOOGLE')
if(self.unpipeTimeout != null){
// clear timer if it is running but result has returned already
clearTimeout(self.unpipeTimeout)
self.unpipeTimeout = null
console.log("CLEARING TIMEOUT > RESULT RETURNED")
}
if(self.result){
console.log(`RESULT: ${self.result}`)
}
// pipe back to snowboy for hotword detection
mic.pipe(snowboyDetector)
})
var tstream = through2.obj((obj,_,next) => {
next(null, {inputAudio: obj})
})
// pipe to dialogflow
mic
.pipe(tstream)
.pipe(this.stream)
}
}
// SNOWBOY EVENTS
snowboyDetector.on('unpipe', function(src){
console.log('STOPPED PIPING > SNOWBOY')
})
snowboyDetector.on('pipe', function(src){
console.log('PIPING > SNOWBOY')
})
// EVENT ON WAKEWORD DETECTION
snowboyDetector.on('hotword', function(index, hotword){
// unpipe stream from hotword detection
// and pipe to dialogflow
mic.unpipe(snowboyDetector)
// pass in request config
const gNew = new GoogleSpeech(request)
gNew.startStream()
})
const mic = record.start(opts)
// START LISTENING FOR HOTWORD
mic.pipe(snowboyDetector)
Примечание: это поведение болеепроизносится на пи.21-секундная задержка, кажется, происходит на Пи.Хотя на моем Mac такое же поведение, задержка ответа после первого запроса намного меньше (~ 7 секунд).Нет проблем с сетевым подключением, потому что результат всегда возвращается, сразу после огромного разрыва.Я также пытался с другим подключением к Интернету.Эта проблема также возникает, когда я просто использую облачную речь Google.
Чтобы проверить, была ли это какая-то проблема с потоками, я создал фиктивный поток, в котором просто выводил запись с микрофона в файл .wav.Это работало нормально, и на этот раз .end () вызывается немедленно и правильно из события unpipe.Я пошел на шаг дальше и направил этот WAV-файл в диалоговый поток, а не из микрофона, и затем поведение вернулось!21.xx секунд !!!
Я также прилагаю GIF журнала.Если вы посмотрите все это (в середине длинный промежуток, продолжайте смотреть :)), вы увидите проблему, о которой я говорю.Как вы увидите, первый результат занимает много времени, а последующие запросы возвращаются мгновенно.
Любая помощь будет принята с благодарностью!Это сводит меня с ума!