ROS2 + 科大讯飞 初步实现机器人语音控制
环境配置:
电脑端: ubuntu22.04实体机作为上位机
ROS版本:ros2-humble
实体机器人: STM32 + 思岚A1激光雷达
科大讯飞语音SDK 讯飞开放平台-以语音交互为核心的人工智能开放平台
实现步骤:
1. 下载和处理科大讯飞语音模块
(1)进入官网的控制台

(2)在左侧导航栏中选择 语音识别-> 语音听写

(3)下载语音模块


2.科大讯飞SDK的处理
新建一个工作空间,里面新建两个文件夹 src voice_ros2
将SDK压缩包解压后的文件,放入voice_ros2中,进入sample目录的iat_online_record_sample目录下,运行下面的命令
source 64bit_make.sh

在bin目录下执行对应的可执行文件了
./iat_online_record_sample

如果遇到下列问题:error while loading shared libraries: libmsc.so: cannot open shared object file: No such file or directory
就把在终端中进入下列目录中

执行命令:
sudo cp libmsc.so /usr/local/lib
sudo ldconfig
3.上位机实现


src 文件夹中放的是 两个功能包,base 中是stm32的ROS2驱动包,teleop_twist_keyboard是github上下载的键盘控制节点功能包,地址如下:
GitHub - ros2/teleop_twist_keyboard at ardent


这个目录下的文件是SDK解压后的文件,其中 红框中的voice.py是也单独编写的文件
import subprocess
import multiprocessing
import timedef run_iat_online_record_sample(queue):process = subprocess.Popen(["./bin/iat_online_record_sample"], stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.PIPE, )# Communicate with the processstdout, _ = process.communicate(input=b"0\n1\n")# Put the result into the queuequeue.put(stdout.decode('utf-8'))def main():while True:# Create a queue for communication between processesqueue = multiprocessing.Queue()# Start the processprocess = multiprocessing.Process(target=run_iat_online_record_sample, args=(queue,))process.start()# Wait for the process to finish and get the result from the queueprocess.join()result = queue.get()# Print the resultprint("Result:", result)# Save the result to a text file, clearing the file firstwith open("result.txt", "w") as f:f.write(result)# Ask user whether to continue recognitioncontinue_recognition = input("是否继续识别? (0: 结束, 1: 继续): ")if continue_recognition == "0":breakif __name__ == "__main__":main()

这个文件运行后会在当前目录生成一个result.txt文件,如下图,这个文件的内容每次识别之后都会更新,键盘节点就是通过获取这个文件的数据来通过语音控制机器人移动的


4.修改teleop_twist_keyboard.py文件
在键盘控制的代码前添加读取文件数据的代码

这里将刚刚识别到的语音过滤后存储在voice_command[0]中,以供后续使用,下面会通过判断voice_command[0]中的值来进行不同的操作

import sys
import threading
import time
import os
from std_msgs.msg import String
import geometry_msgs.msg
import rclpyif sys.platform == 'win32':import msvcrt
else:import termiosimport ttymsg = """
This node takes keypresses from the keyboard and publishes them
as Twist/TwistStamped messages. It works best with a US keyboard layout.
---------------------------
Moving around:u i oj k lm , .For Holonomic mode (strafing), hold down the shift key:
---------------------------U I OJ K LM < >t : up (+z)
b : down (-z)anything else : stopq/z : increase/decrease max speeds by 10%
w/x : increase/decrease only linear speed by 10%
e/c : increase/decrease only angular speed by 10%CTRL-C to quit
"""moveBindings = {'i': (1, 0, 0, 0),'o': (1, 0, 0, -1),'j': (0, 0, 0, 1),'l': (0, 0, 0, -1),'u': (1, 0, 0, 1),',': (-1, 0, 0, 0),'.': (-1, 0, 0, 1),'m': (-1, 0, 0, -1),'O': (1, -1, 0, 0),'I': (1, 0, 0, 0),'J': (0, 1, 0, 0),'L': (0, -1, 0, 0),'U': (1, 1, 0, 0),'<': (-1, 0, 0, 0),'>': (-1, -1, 0, 0),'M': (-1, 1, 0, 0),'t': (0, 0, 1, 0),'b': (0, 0, -1, 0),
}speedBindings = {'q': (1.1, 1.1),'z': (.9, .9),'w': (1.1, 1),'x': (.9, 1),'e': (1, 1.1),'c': (1, .9),
}def getKey(settings):if sys.platform == 'win32':# getwch() returns a string on Windowskey = msvcrt.getwch()else:tty.setraw(sys.stdin.fileno())# sys.stdin.read() returns a string on Linuxkey = sys.stdin.read(1)termios.tcsetattr(sys.stdin, termios.TCSADRAIN, settings)return keydef saveTerminalSettings():if sys.platform == 'win32':return Nonereturn termios.tcgetattr(sys.stdin)def restoreTerminalSettings(old_settings):if sys.platform == 'win32':returntermios.tcsetattr(sys.stdin, termios.TCSADRAIN, old_settings)def vels(speed, turn):return 'currently:\tspeed %s\tturn %s ' % (speed, turn)def main():settings = saveTerminalSettings()rclpy.init()node = rclpy.create_node('teleop_twist_keyboard')# parametersstamped = node.declare_parameter('stamped', False).valueframe_id = node.declare_parameter('frame_id', '').valueif not stamped and frame_id:raise Exception("'frame_id' can only be set when 'stamped' is True")if stamped:TwistMsg = geometry_msgs.msg.TwistStampedelse:TwistMsg = geometry_msgs.msg.Twistpub = node.create_publisher(TwistMsg, 'cmd_vel', 10)voice_command = [None] # Initializing as a listspinner = threading.Thread(target=rclpy.spin, args=(node,))spinner.start()speed = 0.5turn = 1.0x = 0.0y = 0.0z = 0.0th = 0.0status = 0.0twist_msg = TwistMsg()if stamped:twist = twist_msg.twisttwist_msg.header.stamp = node.get_clock().now().to_msg()twist_msg.header.frame_id = frame_idelse:twist = twist_msgtry:print(msg)print(vels(speed, turn))while True:print("当前工作路径:", os.getcwd())with open('./voice_ros2/result.txt', 'r') as f:# with open('/home/lsg/xufen3_ws/voice_ros2/result.txt', 'r') as f:for line in f:if line.startswith('Result: ['):start = line.find('[')end = line.find(']')if start != -1 and end != -1:voice_command[0] = line[start + 1:end].strip()print("voice_command", voice_command[0])# Clearing the content of result.txtopen('./voice_ros2/result.txt', 'w').close()# open('/home/lsg/xufen3_ws/voice_ros2/result.txt', 'w').close()breakkey = getKey(settings)# print("键盘控制按键输出", key)if key in moveBindings.keys():x = moveBindings[key][0]y = moveBindings[key][1]z = moveBindings[key][2]th = moveBindings[key][3]elif key in speedBindings.keys():speed = speed * speedBindings[key][0]turn = turn * speedBindings[key][1]print(vels(speed, turn))if (status == 14):print(msg)status = (status + 1) % 15elif voice_command[0] is not None:if voice_command[0] == "小车后退":print("语音控制小车前进", voice_command[0])x = moveBindings['i'][0]y = moveBindings['i'][1]z = moveBindings['i'][2]th = moveBindings['i'][3]elif voice_command[0] == "小车前进":print("语音控制小车后退", voice_command[0])x = moveBindings[','][0]y = moveBindings[','][1]z = moveBindings[','][2]th = moveBindings[','][3]elif voice_command[0] == "小车左转":print("语音控制小车左转", voice_command[0])x = moveBindings['j'][0]y = moveBindings['j'][1]z = moveBindings['j'][2]th = moveBindings['j'][3]elif voice_command[0] == "小车右转":print("语音控制小车右转", voice_command[0])x = moveBindings['l'][0]y = moveBindings['l'][1]z = moveBindings['l'][2]th = moveBindings['l'][3]elif voice_command[0] == "小车停":print("语音控制小车停", voice_command[0])x = moveBindings['k'][0]y = moveBindings['k'][1]z = moveBindings['k'][2]th = moveBindings['k'][3]voice_command[0] = Noneelse:x = 0.0y = 0.0z = 0.0th = 0.0if (key == '\x03'):breakif stamped:twist_msg.header.stamp = node.get_clock().now().to_msg()twist.linear.x = x * speedtwist.linear.y = y * speedtwist.linear.z = z * speedtwist.angular.x = 0.0twist.angular.y = 0.0twist.angular.z = th * turnpub.publish(twist_msg)# Print timestamp every secondtime.sleep(1)print("时间戳:", time.time())except Exception as e:print(e)finally:if stamped:twist_msg.header.stamp = node.get_clock().now().to_msg()twist.linear.x = 0.0twist.linear.y = 0.0twist.linear.z = 0.0twist.angular.x = 0.0twist.angular.y = 0.0twist.angular.z = 0.0pub.publish(twist_msg)rclpy.shutdown()spinner.join()restoreTerminalSettings(settings)if __name__ == '__main__':main()
5. 编译运行
// xufen3_ws工作空间下
// 终端1:
colcon build. install/setup.bashros2 launch ros2_stm32_bridge driver.launch.py// 终端2:
. install/setup.bashros2 run teleop_twist_keyboard teleop_twist_keyboard// 终端3 ~/xufen3_ws/voice_ros2$ 目录下 :python3 voice.py
然后就可以通过语音控制小车
在右侧终端按1进行语音识别,此时将识别到小车前进的命令并打印,在左侧终端按回车健获取result中的命令,将输出voice_command 小车前进,此时再按键ctrl+c,将输出语音控制小车前进 小车前进并且小车开始移动。
目前的代码需要按键才能加载进来语音的命令并控制小车移动,但好在实现了功能,后续还会继续优化。


终端3中,输入数字1 然后 语音输入指令 “小车前进” 或“ 小车后退” 或 “小车左转” 或“”小车右转”
等到终端3中,打印了语音指令后,鼠标移动到终端2,按下回车键即可小车移动。

需要按键控制,感觉发出语音指令后,要等好几秒才能移动小车,还需要按键,不过还是初步实现了语音控制,后期优化,实现更实用的语音控制
相关文章:
ROS2 + 科大讯飞 初步实现机器人语音控制
环境配置: 电脑端: ubuntu22.04实体机作为上位机 ROS版本:ros2-humble 实体机器人: STM32 思岚A1激光雷达 科大讯飞语音SDK 讯飞开放平台-以语音交互为核心的人工智能开放平台 实现步骤: 1. 下载和处理科大讯飞语音模…...
HTML5新增的input元素属性:placeholder、required、autofocus、min、max等
HTML5 大幅度地增加与改良了 input 元素的属性,可以简单地使用这些属性来实现 HTML5 之前需要使用 JavaScript 才能实现的许多功能。 下面将详细介绍这些新增的 input 元素的属性。 属性说明属性说明placeholder在输入框显示描述性或提示性文本autocomplete是否保…...
Cornerstone3D导致浏览器崩溃的踩坑记录
WebGL: CONTEXT_LOST_WEBGL: loseContext: context lost ⛳️ 问题描述 在使用vue3vite重构Cornerstone相关项目后,在Mac本地运行良好,但是部署测试环境后,在window系统的Chrome浏览器中切换页面会导致页面崩溃。查看Chrome的任务管理器&am…...
【鸿蒙学习笔记】Stage模型
官方文档:Stage模型开发概述 目录标题 Stage模型好处Stage模型概念图ContextAbilityStageUIAbility组件和ExtensionAbility组件WindowStage Stage模型-组件模型Stage模型-进程模型Stage模型-ArkTS线程模型和任务模型关于任务模型,我们先来了解一下什么是…...
Docker进入MongoDB
先是命令行开启docker镜像,然后进入docker镜像,这是两步 进入之后,开头会变成root,我的理解是进入了另一个linux系统了,直接执行相应的软件 这里直接use databse就是进入了,据说MongoDB是慢启动,…...
APP与API:魔法世界的咒语与念咒者
1. 什么是API? API,即应用程序编程接口(Application Programming Interface),就像是魔法世界中的咒语。API是两个独立软件系统之间进行通信和数据交换的桥梁。通过API,一个软件系统可以调用另一个软件系统中…...
云计算安全需求分析与安全保护工程
云计算基本概念 云计算(Cloud Computing)是一种通过互联网提供计算资源和服务的技术。它允许用户按需访问和使用计算资源,如服务器、存储、数据库、网络、安全、分析和软件应用等,而无需管理底层基础设施。以下是云计算的基本概念…...
七天.NET 8操作SQLite入门到实战 - 第二天 在 Windows 上配置 SQLite环境
前言 SQLite的一个重要的特性是零配置的、无需服务器,这意味着不需要复杂的安装或管理。它跟微软的Access差不多,只是一个.db格式的文件。但是与Access不同的是,它不需要安装任何软件,非常轻巧。 七天.NET 8操作SQLite入门到实战…...
操作系统——进程的状态与转换
...
80. UE5 RPG 实现UI显示技能冷却进度功能
在上一篇文章里,我们实现了通过GE给技能增加资源消耗和技能冷却功能。UI也能够显示角色能够使用的技能的UI,现在还有一个问题,我们希望在技能释放进去冷却时,技能变成灰色,并在技能冷却完成,技能可以再次使…...
Vue2-集成路由Vue Router介绍与使用
文章目录 路由(Vue2)1. SPA 与前端路由2. vue-router基本使用创建路由组件声明路由链接和占位标签创建路由模块挂载路由模块 3. vue-router进阶路由重定向嵌套路由动态路由编程式导航导航守卫 本篇小结 更多相关内容可查看 路由(Vue2…...
TemuAPI接口:获取商品详情功能
temu作为拼多多海外的跨境电商平台,已经在海外电商领域崭露头角,越来越多的外贸人选择temu作为发展平台。今天的接口可以用于获取temu平台的商品详情,包括价格、商品图片、规格、评论等内容,如有需要,请点击文末链接或…...
deepstream读取mp4文件及不同类型视频输入bug解决
在deepstream中使用mp4文件,与rtsp类似,使用uridecodebin即可,(可见官方test.py文件) def create_source_bin(index, uri):print("Creating source bin")# Create a source GstBin to abstract this bins c…...
Redis服务器统计和配置信息简介
Redis服务器统计和配置信息简介 首先使用INFO命令在Redis中用于获取Redis服务器的各种统计和配置信息;执行上述命令后,返回的信息分为多个部分,包括服务器信息、客户端信息、内存信息、持久化信息、统计信息、复制信息、CPU信息和键空间信息;…...
Linux Mac 安装Higress 平替 Spring Cloud Gateway
Linux Mac 安装Higress 平替 Spring Cloud Gateway Higress是什么?传统网关分类Higress定位下载安装包执行安装命令执行脚本 安装成功打开管理界面使用方法configure.shreset.shstartup.shshutdown.shstatus.shlogs.sh Higress官网 Higress是什么? Higress是基于阿里内部的…...
基于重叠群稀疏的总变分信号降噪及在旋转机械故障诊断中的应用(MATLAB)
基于振动分析的故障诊断方法基本流程主要由以下五个步骤组成,分别是信号采集、信号处理、特征提取、状态识别与诊断结果。这五个步骤中信号采集与特征提取是故障诊断中最为重要的步骤,而故障微弱特征信息又是其中最难解决的问题。“故障微弱特征信息”站…...
【YOLOv8】 用YOLOv8实现数字式工业仪表智能读数(一)
上一篇圆形表盘指针式仪表的项目受到很多人的关注,咱们一鼓作气,把数字式工业仪表的智能读数也研究一下。本篇主要讲如何用YOLOV8实现数字式工业仪表的自动读数,并将读数结果进行输出,若需要完整数据集和源代码可以私信。 目录 &…...
微信小程序---npm 支持
一、构建 npm 目前小程序已经支持使用 npm 安装第三方包,但是这些 npm 包在小程序中不能够直接使用,必须得使用小程序开发者工具进行构建后才可以使用。 为什么得使用小程序开发者工具需要构建呢❓ 因为 node_modules 目录下的包,不会参与…...
02MFC画笔/画刷/画椭圆/圆/(延时)文字
文章目录 画实心矩形自定义画布设计及使用连续画线及自定义定义变量扇形画椭圆/圆输出颜色文本定时器与定时事件 画实心矩形 自定义画布设计及使用 连续画线及自定义定义变量 扇形 画椭圆/圆 输出颜色文本 定时器与定时事件...
JavaWeb(四:Ajax与Json)
一、Ajax 1.定义 Ajax(Asynchronous JavaScript And XML):异步的 JavaScript 和 XML AJAX 不是新的编程语言,指的是⼀种交互方式:异步加载。 客户端和服务器的数据交互更新在局部页面的技术,不需要刷新…...
【算法训练营Day07】字符串part1
文章目录 反转字符串反转字符串II替换数字 反转字符串 题目链接:344. 反转字符串 双指针法,两个指针的元素直接调转即可 class Solution {public void reverseString(char[] s) {int head 0;int end s.length - 1;while(head < end) {char temp …...
三体问题详解
从物理学角度,三体问题之所以不稳定,是因为三个天体在万有引力作用下相互作用,形成一个非线性耦合系统。我们可以从牛顿经典力学出发,列出具体的运动方程,并说明为何这个系统本质上是混沌的,无法得到一般解…...
多种风格导航菜单 HTML 实现(附源码)
下面我将为您展示 6 种不同风格的导航菜单实现,每种都包含完整 HTML、CSS 和 JavaScript 代码。 1. 简约水平导航栏 <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport&qu…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...
10-Oracle 23 ai Vector Search 概述和参数
一、Oracle AI Vector Search 概述 企业和个人都在尝试各种AI,使用客户端或是内部自己搭建集成大模型的终端,加速与大型语言模型(LLM)的结合,同时使用检索增强生成(Retrieval Augmented Generation &#…...
安卓基础(aar)
重新设置java21的环境,临时设置 $env:JAVA_HOME "D:\Android Studio\jbr" 查看当前环境变量 JAVA_HOME 的值 echo $env:JAVA_HOME 构建ARR文件 ./gradlew :private-lib:assembleRelease 目录是这样的: MyApp/ ├── app/ …...
在QWebEngineView上实现鼠标、触摸等事件捕获的解决方案
这个问题我看其他博主也写了,要么要会员、要么写的乱七八糟。这里我整理一下,把问题说清楚并且给出代码,拿去用就行,照着葫芦画瓢。 问题 在继承QWebEngineView后,重写mousePressEvent或event函数无法捕获鼠标按下事…...
基于SpringBoot在线拍卖系统的设计和实现
摘 要 随着社会的发展,社会的各行各业都在利用信息化时代的优势。计算机的优势和普及使得各种信息系统的开发成为必需。 在线拍卖系统,主要的模块包括管理员;首页、个人中心、用户管理、商品类型管理、拍卖商品管理、历史竞拍管理、竞拍订单…...
华为OD机考-机房布局
import java.util.*;public class DemoTest5 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseSystem.out.println(solve(in.nextLine()));}}priv…...
给网站添加live2d看板娘
给网站添加live2d看板娘 参考文献: stevenjoezhang/live2d-widget: 把萌萌哒的看板娘抱回家 (ノ≧∇≦)ノ | Live2D widget for web platformEikanya/Live2d-model: Live2d model collectionzenghongtu/live2d-model-assets 前言 网站环境如下,文章也主…...
