From fcd5dd64d2a7538ff45c41ad3c08c627a59598b9 Mon Sep 17 00:00:00 2001 From: Erik Andresen Date: Sat, 8 Dec 2018 17:44:51 +0100 Subject: [PATCH] Added pocketsphinx script --- data/keywords.kws | 1 + data/robot.jsgf | 15 ++++++ scripts/pocketsphinx.rb | 105 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 121 insertions(+) create mode 100644 data/keywords.kws create mode 100644 data/robot.jsgf create mode 100755 scripts/pocketsphinx.rb diff --git a/data/keywords.kws b/data/keywords.kws new file mode 100644 index 0000000..35c2c31 --- /dev/null +++ b/data/keywords.kws @@ -0,0 +1 @@ +wild thumper /1e-11/ diff --git a/data/robot.jsgf b/data/robot.jsgf new file mode 100644 index 0000000..388d1f5 --- /dev/null +++ b/data/robot.jsgf @@ -0,0 +1,15 @@ +#JSGF V1.0; + +grammar robot; + + = (on | off); + = minus* (zero | one | two | three | four | five | six | seven | eight | nine | ten | eleven | twelve | thirteen | fourteen | fifteen | sixteen | seventeen | eighteen | nineteen | twenty | thirty | forty | fifty | sixty | seventy | eighty | ninety | hundred | thousand | million); + + = (light | lights) []; + = (stop | forward | backward | increase speed | decrease speed); + = get (temp | temperature | light | voltage | current | pressure | mute | mic | silence | speed | velocity | position | angle | compass | motion | secure | engine | odom | humidity); + = go (forward | backward) + (meter | meters | centimeter | centimeters); + = turn (left | right | (to | by) + [(degree | degrees)]); + = set+ speed + | set default speed; + +public = | | | | | ; diff --git a/scripts/pocketsphinx.rb b/scripts/pocketsphinx.rb new file mode 100755 index 0000000..c2a87fb --- /dev/null +++ b/scripts/pocketsphinx.rb @@ -0,0 +1,105 @@ +#!/usr/bin/ruby -w + +require 'gst' +require 'pry' +require 'logger' +require 'ros' +require 'std_msgs/String' + +class Speak + def initialize(node) + @logger = Logger.new(STDOUT) + @publisher = node.advertise('asr_result', Std_msgs::String) + @pipeline = Gst.parse_launch('alsasrc device="plughw:1,0" ! audio/x-raw,format=S16LE,channels=1,rate=16000 ! cutter leaky=true name=cutter'\ + ' ! tee name=jsgf ! queue leaky=downstream ! valve name=valve_jsgf drop=true ! pocketsphinx name=asr_jsgf ! fakesink async=false jsgf.'\ + ' ! pocketsphinx name=asr_kws ! fakesink async=false'\ + ) + # Ignore everything below the configured volume + cutter = @pipeline.get_by_name('cutter') + cutter.set_property('threshold-dB', -20) + cutter.set_property('pre-length', 100000000) # pocketsphinx needs about 0.1s before start + cutter.set_property('run-length', 1300000000) + + asr_jsgf = @pipeline.get_by_name('asr_jsgf') + asr_jsgf.set_property('hmm', 'pocketsphinx/adapt/cmusphinx-en-us-5.2') + asr_jsgf.set_property('mllr', 'pocketsphinx/adapt/mllr_matrix') + asr_jsgf.set_property('jsgf', 'data/robot.jsgf') + + asr_kws = @pipeline.get_by_name('asr_kws') + asr_kws.set_property('hmm', 'pocketsphinx/adapt/cmusphinx-en-us-5.2') + asr_kws.set_property('mllr', 'pocketsphinx/adapt/mllr_matrix') + asr_kws.set_property('kws', 'data/keywords.kws') + + bus = @pipeline.bus() + bus.add_watch do |bus, message| + case message.type + when Gst::MessageType::EOS + loop.quit + when Gst::MessageType::ERROR + p message.parse_error + binding.pry # open console + loop.quit + when Gst::MessageType::ELEMENT + if message.src.name == "asr_kws" and message.structure.get_value(:final).value + keyword_detect(message.structure.get_value(:hypothesis).value, message.structure.get_value(:confidence).value) + elsif message.src.name == "asr_jsgf" + if message.structure.get_value(:final).value + final_result(message.structure.get_value(:hypothesis).value, message.structure.get_value(:confidence).value) + end + elsif message.src.name == "cutter" + if message.structure.get_value(:above).value + @logger.debug "Start recording.." + else + @logger.debug "Stop recording" + end + end + end + true + end + + @pipeline.play + end + + # Enables/Disables the jsgf pipeline + def enable_jsgf(bEnable) + valve = @pipeline.get_by_name('valve_jsgf') + valve.set_property("drop", !bEnable) + end + + # Result of jsgf pipeline + def final_result(hyp, confidence) + @logger.info "final: " + hyp + " " + confidence.to_s + enable_jsgf(false) + + # Publish pocketsphinx result as ros message + msg = Std_msgs::String.new + msg.data = hyp + @publisher.publish(msg) + end + + def keyword_detect(hyp, confidence) + @logger.debug "Got keyword: " + hyp + enable_jsgf(true) + end + + def stop + @pipeline.stop + end +end + +if __FILE__ == $0 + node = ROS::Node.new('wild_thumper/pocketsphinx') + app = Speak.new(node) + loop = GLib::MainLoop.new(nil, false) + begin + Thread.new { + loop.run + } + node.spin + rescue Interrupt + ensure + app.stop + node.shutdown + loop.quit + end +end -- 2.39.2