ROS2 + 科大讯飞 初步实现机器人语音控制

news2024/9/23 9:28:28

环境配置:

        电脑端: 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.coloradmin.cn/o/1921263.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Linux /etc/profile 详解

概述 Linux是一个多用户的操作系统。每个用户登录系统后&#xff0c;都会有一个专用的运行环境。通常每个用户默认的环境都是相同的&#xff0c;这个默认环境实际上就是一组环境变量的定义。用户可以对自己的运行环境进行定制&#xff0c;其方法就是修改相应的系统环境变量&…

python如何查看类的函数

Python非常方便&#xff0c;它不需要用户查询文档&#xff0c;只需掌握如下两个帮助函数&#xff0c;即可查看Python中的所有函数&#xff08;方法&#xff09;以及它们的用法和功能&#xff1a; dir()&#xff1a;列出指定类或模块包含的全部内容&#xff08;包括函数、方法、…

浅谈串口UART通信原理

文章目录 引言并行和串行波特率UART帧格式 引言 UART&#xff08;Universal Asynchronous Receiver/Transmitter&#xff0c;通用异步收发器&#xff09;是一种用于串行通信的硬件设备。它允许两个设备之间进行异步数据传输。是一种通用的串行、异步通信总线。该总线有两条数据…

SpringBoot整合XXL_JOB示例

XXL-JOB 是一个分布式任务调度平台&#xff0c;主要用于管理和执行定时任务。它适用于各种场景&#xff0c;例如定时任务、批处理任务、分布式任务等。XXL-JOB 提供了丰富的功能&#xff0c;使得任务调度变得简单、高效和可靠。以下是 XXL-JOB 的一些主要功能和特点&#xff1a…

Centos系统内磁盘分区

Centos系统内磁盘分区 建议如果有重要数据提前做好备份 以根目录扩容50G为例&#xff1a; 1、卸载/home目录 umount /home 2、删除逻辑卷 y确认即可 lvremove /dev/mapper/centos-home 3、df -h查询一下&#xff0c;/home目录已经不见了 4、向根目录分区追加50G容量 lv…

数据销毁境外间谍情报机关逼迫、威胁贷款学生为其窃取我国家秘密

近年来&#xff0c;随着国际形势的复杂多变&#xff0c;境外间谍情报机关的活动也日益猖獗。他们利用各种手段&#xff0c;包括通过校园贷逼迫、威胁贷款学生为其窃取我国国家秘密&#xff0c;这种行为不仅危害了国家安全&#xff0c;也严重损害了社会的公平正义。那么&#xf…

微信小程序毕业设计-汽车维修项目管理系统项目开发实战(附源码+论文)

大家好&#xff01;我是程序猿老A&#xff0c;感谢您阅读本文&#xff0c;欢迎一键三连哦。 &#x1f49e;当前专栏&#xff1a;微信小程序毕业设计 精彩专栏推荐&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; &#x1f380; Python毕业设计…

(一)高并发压力测试调优篇——MYSQL数据库的调优

前言 在实际项目开发中&#xff0c;很多业务场景下都需要考虑接口的性能要求&#xff0c;追求高并发、高吞吐量。那么对于此类问题如何入手呢&#xff1f;关注作者&#xff0c;不迷路。本节内容主要介绍在数据库db方面的优化&#xff0c;以mysql数据库为例。 关于db的优化&am…

python库(11):Box库简化字典和对象之间的转换

1Box库简介 Box是一个Python库&#xff0c;它提供了一种将数据封装在字典和列表中的方式&#xff0c;同时提供了一些额外的功能&#xff0c;比如数据验证、默认值设置等。这使得Box库非常适合用于配置管理、数据传输对象&#xff08;DTO&#xff09;的创建&#xff0c;以及任何…

PDF 中图表的解析探究

PDF 中图表的解析探究 0. 引言1. 开源方案探究 0. 引言 一直以来&#xff0c;对文档中的图片和表格处理都非常有挑战性。这篇文章记录一下最近工作上在这块的探究。图表分为图片和表格&#xff0c;这篇文章主要记录了对表格的探究。还有&#xff0c;我个人主要做日本项目&…

[C++]——同步异步日志系统(4)

同步异步日志系统 一、日志等级模块设计二、日志消息类设计 一、日志等级模块设计 定义出日志系统所包含的所有日志等级分别为&#xff1a;&#xff08;7个等级&#xff09; UNKNOW0&#xff0c;未知等级的日志DRBUG &#xff0c;调试等级的日志INFO &#xff0c;提示等级的日…

前端调试技巧(npm Link,vscode调试,浏览器调试等)

Npm Link 功能&#xff1a; 在本地开发npm模块的时候&#xff0c;我们可以使用npm link命令&#xff0c;将npm 模块链接到对应的运行项目中去&#xff0c;方便地对模块进行调试和测试 断点调试 vscode调试 Debug Vue2 Project 目标&#xff1a;在VSCode中调试项目代码…

docker拉取镜像-配置阿里云镜像加速

1、配置阿里云镜像&#xff08;用于拉取镜像加速&#xff09; sudo mkdir -p /etc/docker sudo tee /etc/docker/daemon.json <<-EOF {"registry-mirrors": ["https://xxxxxxxx.mirror.aliyuncs.com"] } EOF sudo systemctl daemon-reload sudo syst…

论文阅读【时间序列】TimeMixer (ICLR2024)

【时间序列】TimeMixer (ICLR2024) 原文链接&#xff1a;TIMEMIXER: DECOMPOSABLE MULTISCALE MIXING FOR TIME SERIES FORECASTING 代码仓库&#xff1a;https://github.com/kwuking/TimeMixer 符号定义 符号含义P用于预测的历史序列长度&#xff08;seq_len&#xff09;F预测…

debian 12 Install

debian 前言 Debian是一个基于Linux内核的自由和开放源代码操作系统&#xff0c;由全球志愿者组成的Debian项目维护和开发。该项目始于1993年&#xff0c;由Ian Murdock发起&#xff0c;旨在创建一个完整的、基于Linux的自由软件操作系统。 debian download debian 百度网盘…

LangChain之工具Tools(下)

LangChain之工具Tools SQLDatabase工具准备数据初始化数据库光标查询​字符串查询带参数查询​使用SQLAlchemy查询​使用自然语言查询数据库 使用其他工具Tavily Search工具Dall-E图像生成工具ArXiv工具 SQLDatabase工具 在 LangChain 中,SQLDatabase工具可以用来与SQL数据库进…

和Bug较劲的第n天:[Error: Unable to open snapshot file: No such file or directory]

问题描述 最近做了一个小demo&#xff0c;基于parcel的&#xff0c;在迁移仓库的时候发生了一个报错 [Error: Unable to open snapshot file: No such file or directory] 原因分析&#xff1a; 在迁移仓库的时候&#xff0c;我将项目放入了一个以中文命名的文件夹里&#xf…

在生产环境中部署Elasticsearch:最佳实践和故障排除技巧——安装篇(一)

#在生产环境中部署Elasticsearch&#xff1a;最佳实践和故障排除技巧——安装篇&#xff08;一&#xff09; 前言 关键字&#xff1a; 机器学习 人工智能 AI chatGPT 学习 实现 使用 搭建 深度 python 事件 远程 docker mysql安全 技术 部署 技术 自动化 代码 文章目录 - -…

【斯坦福因果推断课程全集】2_无混淆和倾向分1

目录 Beyond a single randomized controlled trial Aggregating difference-in-means estimators Continuous X and the propensity score 随机试验的一个最简单的扩展是无约束下的干预效果估计。从定性上讲&#xff0c;当我们想估计一种并非随机的治疗效果&#xff0c;但一…

python-小理与他的画(赛氪OJ)

[题目描述] 小理是个画家&#xff0c;他希望有一天他的画能让心仪的她看到。 只是后来她有了他&#xff0c;他却只有他的画&#xff0c;他望着他的画&#xff0c;默默的发呆。 可惜做题的你&#xff0c;画不出他画的她&#xff0c;所以&#xff0c;我们只好画点简单的画&#x…