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 time

def 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 process
    stdout, _ = process.communicate(input=b"0\n1\n")
    
    # Put the result into the queue
    queue.put(stdout.decode('utf-8'))

def main():
    while True:
        # Create a queue for communication between processes
        queue = multiprocessing.Queue()
        
        # Start the process
        process = multiprocessing.Process(target=run_iat_online_record_sample, args=(queue,))
        process.start()
        
        # Wait for the process to finish and get the result from the queue
        process.join()
        result = queue.get()
        
        # Print the result
        print("Result:", result)
        
        # Save the result to a text file, clearing the file first
        with open("result.txt", "w") as f:
            f.write(result)
        
        # Ask user whether to continue recognition
        continue_recognition = input("是否继续识别? (0: 结束, 1: 继续): ")
        if continue_recognition == "0":
            break

if __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 rclpy

if sys.platform == 'win32':
    import msvcrt
else:
    import termios
    import tty

msg = """
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    o
   j    k    l
   m    ,    .

For Holonomic mode (strafing), hold down the shift key:
---------------------------
   U    I    O
   J    K    L
   M    <    >

t : up (+z)
b : down (-z)

anything else : stop

q/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 Windows
        key = msvcrt.getwch()
    else:
        tty.setraw(sys.stdin.fileno())
        # sys.stdin.read() returns a string on Linux
        key = sys.stdin.read(1)
        termios.tcsetattr(sys.stdin, termios.TCSADRAIN, settings)
    return key


def saveTerminalSettings():
    if sys.platform == 'win32':
        return None
    return termios.tcgetattr(sys.stdin)


def restoreTerminalSettings(old_settings):
    if sys.platform == 'win32':
        return
    termios.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')

    # parameters
    stamped = node.declare_parameter('stamped', False).value
    frame_id = node.declare_parameter('frame_id', '').value
    if not stamped and frame_id:
        raise Exception("'frame_id' can only be set when 'stamped' is True")

    if stamped:
        TwistMsg = geometry_msgs.msg.TwistStamped
    else:
        TwistMsg = geometry_msgs.msg.Twist

    pub = node.create_publisher(TwistMsg, 'cmd_vel', 10)

    voice_command = [None]  # Initializing as a list

    spinner = threading.Thread(target=rclpy.spin, args=(node,))
    spinner.start()

    speed = 0.5
    turn = 1.0
    x = 0.0
    y = 0.0
    z = 0.0
    th = 0.0
    status = 0.0

    twist_msg = TwistMsg()

    if stamped:
        twist = twist_msg.twist
        twist_msg.header.stamp = node.get_clock().now().to_msg()
        twist_msg.header.frame_id = frame_id
    else:
        twist = twist_msg

    try:
        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.txt
                            open('./voice_ros2/result.txt', 'w').close()
                            # open('/home/lsg/xufen3_ws/voice_ros2/result.txt', 'w').close()
                            break

            key = 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) % 15
           
            elif 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] = None
            else:
                x = 0.0
                y = 0.0
                z = 0.0
                th = 0.0
                if (key == '\x03'):
                    break

            if stamped:
                twist_msg.header.stamp = node.get_clock().now().to_msg()

            twist.linear.x = x * speed
            twist.linear.y = y * speed
            twist.linear.z = z * speed
            twist.angular.x = 0.0
            twist.angular.y = 0.0
            twist.angular.z = th * turn
            pub.publish(twist_msg)

            # Print timestamp every second
            time.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.0
        twist.linear.y = 0.0
        twist.linear.z = 0.0
        twist.angular.x = 0.0
        twist.angular.y = 0.0
        twist.angular.z = 0.0
        pub.publish(twist_msg)
        rclpy.shutdown()
        spinner.join()

        restoreTerminalSettings(settings)


if __name__ == '__main__':
    main()

5. 编译运行

// xufen3_ws工作空间下
// 终端1:
colcon build

. install/setup.bash

 ros2 launch ros2_stm32_bridge driver.launch.py


// 终端2:
. install/setup.bash

ros2 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,按下回车键即可小车移动。

需要按键控制,感觉发出语音指令后,要等好几秒才能移动小车,还需要按键,不过还是初步实现了语音控制,后期优化,实现更实用的语音控制

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/801065.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

SQL Server的视图

SQL Server的视图 一、基础 SQL 视图&#xff08;Views&#xff09;是一种虚拟表&#xff0c;是基于 SQL 查询结果生成的。这些虚拟表可以包含来自一个或多个表的数据&#xff0c;并且可以像表一样查询&#xff1b;视图是一个表中的数据经过某种筛选后的显示方式&#xff0c;或…

Cornerstone3D导致浏览器崩溃的踩坑记录

WebGL: CONTEXT_LOST_WEBGL: loseContext: context lost ⛳️ 问题描述 在使用vue3vite重构Cornerstone相关项目后&#xff0c;在Mac本地运行良好&#xff0c;但是部署测试环境后&#xff0c;在window系统的Chrome浏览器中切换页面会导致页面崩溃。查看Chrome的任务管理器&am…

对话天润融通首席科学家:大模型的首要任务是为客户创造商业价值

2023年&#xff0c;AI大模型开启了企业数智化转型的新篇章。 不过前沿技术固然重要&#xff0c;但在增长见顶的存量背景下&#xff0c;先进技术带来的实用价值也尤为关键。 正如天润融通首席科学家田凤占所说&#xff1a;“现阶段最重要的是让大模型尽快和企业的业务相结合&a…

【Linux】进程间通信——消息队列和信号量

目录 消息队列&#xff08;message queue&#xff09; 信号量&#xff08;Semaphore&#xff09; system V版本的进程间通信方式有三种&#xff1a;共享内存&#xff0c;消息队列和信号量。之前我们已经说了共享内存&#xff0c;那么我们来看一下消息队列和信号量以及它们之间…

【嵌入式Linux】<总览> 网络编程(更新中)

文章目录 前言 一、网络知识概述 1. 网路结构分层 2. socket 3. IP地址 4. 端口号 5. 字节序 二、网络编程常用API 1. socket函数 2. bind函数 3. listen函数 4. accept函数 5. connect函数 6. read和recv函数 7. write和send函数 三、TCP编程 1. TCP介绍 2.…

新版本WPS不登录无法编辑的解决办法

原因分析&#xff1a;新版本的WPS因加入多种在线功能&#xff0c;建议登录账号获得更加体验 解决办法&#xff1a;首选第一种修改注册表后重启WPS&#xff0c;第二种仅作为临时满足工作需求&#xff0c;过一段时间会自动失效 方法一&#xff1a;键盘同时按下WINR键&#xff0c;…

【Python】基础语法(函数、列表和元组、字典、文件)

。一、函数 1、函数是什么 编程中的函数和数学中的函数有一定的相似之处。 数学上的函数&#xff0c;比如 y sin x&#xff0c;x 取不同的值&#xff0c;y 就会得到不同的结果。 编程中的函数是一段可以被重复使用的代码片段。 &#xff08;1&#xff09;求数列的和&…

MySQL索引特性(上)

目录 索引的重要 案例 认识磁盘 MySQL与存储 先来研究一下磁盘 扇区 定位扇区 结论 磁盘随机访问与连续访问 MySQL与磁盘交互基本单位 建立共识 索引的理解 建立测试表 插入多条记录 局部性原理 所有的MySQL的操作(增删查改)全部都是在MySQL当中的内存中进行的&am…

网友提问:HTML CSS JS很低级吗?

这话听起来就像有人在说披萨只是面团加奶酪&#xff0c;完全忽略了上面的美味配料和烘烤的艺术啊&#xff01;HTML、CSS、JS这三位可是前端开发的铁三角&#xff0c;它们一点都不“低级”&#xff0c;反而相当关键。 HTML就像是房子的骨架&#xff0c;没有它&#xff0c;网页就…

【iOS】——MRC

一、引用计数 内存管理的核心是引用计数器&#xff0c;用一个整数来表示对象被引用的次数&#xff0c;系统需要根据引用计数器来判断对象是否需要被回收。 在每次 RunLoop 迭代结束后&#xff0c;都会检查对象的引用计数器&#xff0c;如果引用计数器等于 0&#xff0c;则说明…

单链表算法 - 链表分割

链表分割_牛客题霸_牛客网现有一链表的头指针 ListNode* pHead&#xff0c;给一定值x&#xff0c;编写一段代码将所有小于x的。题目来自【牛客题霸】https://www.nowcoder.com/practice/0e27e0b064de4eacac178676ef9c9d70思路: 代码: /* struct ListNode {int val;struct List…

烧掉一个亿就为造好一把椅子,西昊如何策划人体工学椅极致产品力

2011年到2015年这4年期间&#xff0c;功能单一&#xff0c;价值雷同的椅子市场趋于饱和&#xff0c;竞争的白热化和同质化带来了全行业的价格战&#xff0c;彼时西昊的经营陷入巨大的困境&#xff0c;所有的产品和钱都变成成库存&#xff0c;现金流几乎枯竭。因此&#xff0c;西…

文心一言《使用手册》,文心一言怎么用?

一、认识文心一言 &#xff08;一&#xff09;什么是文心一言 文心一言是百度研发的 人工智能大语言模型产品&#xff0c;能够通过上一句话&#xff0c;预测生成下一段话。 任何人都可以通过输入【指令】和文心一言进行对话互动、提出问题或要求&#xff0c;让文心一言高效地…

智能家居产品公司网站源码,自适应布局设计,带完整演示数据

适合各类智能家居电子产品使用的网站源码&#xff0c;深色大气设计&#xff0c;自适应布局设计&#xff0c;pc手机均可完美适配&#xff0c;带完整演示数据。 独家原创资源。源码是asp开发的&#xff0c;数据库是access&#xff0c;主流的虚拟主机空间都支持asp&#xff0c;直…

<Rust><GUI>rust语言GUI库tauri体验:前、后端结合创建一个窗口并修改其样式

前言 本文是rust语言下的GUI库&#xff1a;tauri来创建一个窗口的简单演示&#xff0c;主要说明一下&#xff0c;使用tauri这个库如何创建GUI以及如何添加部件、如何编写逻辑、如何修改风格等&#xff0c;所以&#xff0c;这也是一个专栏&#xff0c;将包括tauri库的多个方面。…

面对人工智能发展的伦理挑战:应对策略与未来方向

✨✨ 欢迎大家来访Srlua的博文&#xff08;づ&#xffe3;3&#xffe3;&#xff09;づ╭❤&#xff5e;✨✨ &#x1f31f;&#x1f31f; 欢迎各位亲爱的读者&#xff0c;感谢你们抽出宝贵的时间来阅读我的文章。 我是Srlua小谢&#xff0c;在这里我会分享我的知识和经验。&am…

硬件开发笔记(二十六):AD21导入电感原理图库、封装库和3D模型

若该文为原创文章&#xff0c;转载请注明原文出处 本文章博客地址&#xff1a;https://hpzwl.blog.csdn.net/article/details/140437290 长沙红胖子Qt&#xff08;长沙创微智科&#xff09;博文大全&#xff1a;开发技术集合&#xff08;包含Qt实用技术、树莓派、三维、OpenCV…

SAP ERP与金蝶云星空的集成案例(新能源光伏行业)

一、项目环境 阳光新能源开发股份有限公司&#xff08;简称“阳光电源”&#xff09;是一家专注于新能源开发利用的国家高新技术企业。作为阳光电源&#xff08;股票代码&#xff1a;300274&#xff09;旗下的新能源开发投资平台&#xff0c;阳光新能源聚焦光伏、风电、风光…

暴雨让服务器不怕热҈热҈热҈热҈

在AI算力呈几何倍数增长的趋势下&#xff0c;算力逐渐朝着“高性能、高密度、高耗能“发展。在高耗能的算力下&#xff0c;AI服务器功率已逐步逼近风冷散热极限&#xff0c;而液冷作为更加高效、低能耗的制冷技术&#xff0c;逐渐成为了高密度算力散热场景的首选方案。 液冷的…

数据结构-java中链表的存储原理及使用方式

目录 链表&#xff08;线性表的链式存储&#xff09; 代码实例&#xff1a;&#xff08;链表构建&#xff0c;头插尾插&#xff09; LinkedList LinkedList的使用&#xff1a; 1、构造方法 2、操作方法 LinkedList 和 ArrayList 的区别 链表&#xff08;线性表的链式存储…