手把手教你实现基于RT-Thread的百度语音识别(六)完结

前言

本篇是整个百度语音识别连载的第六篇,也是最后一篇了。这一次我们要把前面实现的各部分功能用线程串接起来,形成一个完整的项目,效果是这样的:

按下按键,开始录音,录音结束后自动将音频发送到百度服务器端,返回识别结果,进行数据解析,显示结果(控制外设)。

那么我们大致可以将以上功能划分为三个线程,分别是:按键线程,录音线程以及识别线程。下面开始对这三个线程进行分析:

实现详解

首先我把各部分功能拆分成了多个源文件,这样使整个工程看起来更加简洁:

main.c

语音识别 bd_speech_rcg.c

录音 wav_record.c

按键只是简单的读IO状态,就放在main.c就好了

bd_speech_rcg.c和wav_record.c里都是前面已经实现的功能函数,这里我就不做讲解,主要来看看main.c:

/* main.c */

#include <rtthread.h>
#include <rtdevice.h>
#include <board.h>
#include <dfs_posix.h>
#include <string.h>
#include <fal.h>
#include <drv_lcd.h>
#include <cn_font.h>

/* 函数声明 */	
extern int wavrecord_sample();
extern void bd();

/* 线程参数 */
#define THREAD_PRIORITY			25      //优先级
#define THREAD_STACK_SIZE		1024    //线程栈大小
#define THREAD_TIMESLICE		10      //时间片

/* 线程句柄 */
static rt_thread_t tid1 = RT_NULL;
static rt_thread_t tid2 = RT_NULL;
static rt_thread_t tid3 = RT_NULL;

/* 指向信号量的指针 */
static rt_sem_t dynamic_sem = RT_NULL;

/* 邮箱控制块 */
static struct rt_mailbox mb;
/* 用于放邮件的内存池 */
static char mb_pool[128];

/* 录音线程 tid1 入口函数 */
static void thread1_entry(void *parameter)
{
	static rt_err_t result;
    while(1)
    {
        result = rt_sem_take(dynamic_sem, RT_WAITING_FOREVER);
        if (result != RT_EOK)
        {
            rt_kprintf("take a dynamic semaphore, failed.\n");
            rt_sem_delete(dynamic_sem);
            return;
        }
        else
        {
			rt_kprintf("take a dynamic semaphore, success.\n");
            wavrecord_sample();         //获取到信号量,开始录音
			rt_mb_send(&mb, NULL);      //录音结束,发送邮件
        }
		rt_thread_mdelay(100);
    }		
}

/* 语音识别线程 tid2 入口函数 */
static void thread2_entry(void *parameter)
{
    while (1)
    {
        rt_kprintf("try to recv a mail\n");
        /* 从邮箱中收取邮件 */
        if (rt_mb_recv(&mb, NULL, RT_WAITING_FOREVER) == RT_EOK)
        {
			show_str(20, 40, 200, 32, (rt_uint8_t *)"百度语音识别", 32);
			show_str(20, 100, 200, 32, (rt_uint8_t *)"识别结果:", 32);
            rt_kprintf("get a mail from mailbox!");
            bd();       //收到邮件,进行语音识别
            rt_thread_mdelay(100);
        }
    }
    /* 执行邮箱对象脱离 */
    rt_mb_detach(&mb);
}

/* 按键线程 tid3 入口函数 */
static void thread3_entry(void *parameter)
{
		unsigned int count = 1;
		while(count > 0)
		{
			if(rt_pin_read(KEY0) == 0)
			{
				rt_kprintf("release a dynamic semaphore.\n");
                rt_sem_release(dynamic_sem);        //当按键被按下,释放一个信号量
			}
		rt_thread_mdelay(100);
		}
}

int main(void)
{
	
		fal_init();
		rt_pin_mode(KEY0, PIN_MODE_INPUT);
		rt_pin_mode(PIN_LED_R, PIN_MODE_OUTPUT);
		rt_pin_mode(PIN_LED_G, PIN_MODE_OUTPUT);
		rt_pin_mode(PIN_LED_B, PIN_MODE_OUTPUT);
		rt_pin_write(PIN_LED_R,1);
		rt_pin_write(PIN_LED_G,1);
		rt_pin_write(PIN_LED_B,1);
		
    /* 清屏 */
    lcd_clear(WHITE);

    /* 设置背景色和前景色 */
    lcd_set_color(WHITE,BLACK);

    /* 在LCD 上显示字符 */
    lcd_show_string(55, 5, 24, "RT-Thread");
		
	show_str(120, 220, 200, 16, (rt_uint8_t *)"By 霹雳大乌龙", 16);
	
    /* 创建一个动态信号量,初始值是 0 */
	dynamic_sem = rt_sem_create("dsem", 0, RT_IPC_FLAG_FIFO);
    if (dynamic_sem == RT_NULL)
    {
        rt_kprintf("create dynamic semaphore failed.\n");
        return -1;
    }
    else
    {
        rt_kprintf("create done. dynamic semaphore value = 0.\n");
    }
	
	rt_err_t result;

    /* 初始化一个 mailbox */
    result = rt_mb_init(&mb,
                        "mbt",                      /* 名称是 mbt */
                        &mb_pool[0],                /* 邮箱用到的内存池是 mb_pool */
                        sizeof(mb_pool) / 4,        /* 邮箱中的邮件数目,因为一封邮件占 4 字节 */
                        RT_IPC_FLAG_FIFO);          /* 采用 FIFO 方式进行线程等待 */
    if (result != RT_EOK)
    {
        rt_kprintf("init mailbox failed.\n");
        return -1;
    }

    /* 创建线程 */	
		tid1 = rt_thread_create("thread1",
                            thread1_entry, RT_NULL,
                            THREAD_STACK_SIZE,
                            THREAD_PRIORITY, THREAD_TIMESLICE);
    if (tid1 != RT_NULL)
        rt_thread_startup(tid1);
		
		
		tid2 = rt_thread_create("thread2",
                            thread2_entry, RT_NULL,
                            THREAD_STACK_SIZE,
                            THREAD_PRIORITY, THREAD_TIMESLICE);
    if (tid2 != RT_NULL)
        rt_thread_startup(tid2);
		
		tid3 = rt_thread_create("thread3",
                            thread3_entry, RT_NULL,
                            THREAD_STACK_SIZE,
                            THREAD_PRIORITY, THREAD_TIMESLICE);
    if (tid3 != RT_NULL)
        rt_thread_startup(tid3);
	
    return 0;
}

通过上面的源码可以看到,我采用了信号量+邮箱的通讯机制;按键线程不断读取IO状态,当按键被按下时,释放一个信号量;录音线程处于永久等待信号量的状态,当接收到一个信号量时开始录音,录音结束后发送一封邮件到邮箱中;识别线程不停尝试获取邮件,当接收到邮件时,进行语音识别,后续的解析,显示。。。都是我们之前讲过的了。

但是上面的功能实现并不是完美的,可能存在着一些问题,但大致流程是没问题的(效果亲测OK),因为整个工程也是第一次完整实现,我也还没进行优化啥的,所以有不足之处,请多多包涵。

总结

那么本系列的分享到此也就告一段落了,因为我学习RT-Thread的时间也并不算长,所以可能有些理解并不到位,再加上此次项目是第一次实现,并不完美,但是我会把完整工程放到我的GitHub上,后续我有一些优化,功能拓展啥的,大家可以在GitHub上看到,
项目地址:https://github.com/lxzzzzzxl/Baidu_Speech_base_on_RT-Thread

谢谢搭嘎~

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
RT-Thread作品秀】手语识别翻译发生装置作者:岁月触礁如梦 概述(说明应用产生的背景、实现功能)手语是聋人使用的语言,是由手形动作辅之以表情姿势由符号构成的比较稳定的表达系统,是一种靠动作/视觉交际的语言.手语识别的研究目标是让机器弄懂聋人的语言.因此我们选择基于 STM32 为主控,对手语识别进行识别和处理,再利用显示系统或者语音模块,从而实现利用 MCU 对手语翻译从而帮助发音障碍人士之间的交流。 开发环境(所采用的软、硬件方案)硬件:ART-PI+MPU6050 RT-Thread版本: 开发工具及版本:RT-Thread Studio RT-Thread使用情况概述(简要总结下应用中RT-Thread使用情况:内核部分、组件部分、软件包部分、内核、其他)使用rt-thread的i2c驱动部分驱动多个MPU6050,然后通过uart驱动发送出去 ART-PI 硬件框架(概述应用所采用的硬件方案框图,并对核心部分做介绍)MPU6050x6 电脑 STM32H750 采集来自 MPU6050 的数据,打包后通过uart模块发送到电 脑上进行数据处理 软件框架说明(介绍应用所采用的软件方案框图、流程图等,并加以解说)数据 声音单元 Sotfmax 全连接层 软件模块说明(介绍应用软件关键部分的逻辑、采用的实现方式等)使用 tensorflow 搭建模型判断手势,使识别成功率大大提高。模型包含一个输入层,两层全连接层,和一个输出的softmax层,最后比较输出结果,如果结果大于0.8则发出对应手势的声音 演示效果(演示效果请采用3张高清图片,并录制一段不少于1min视频解说应用所实现的效果,视频上传至B站或者腾讯视频或其他视频平台,给出链接即可)比赛感悟(可以围绕这次比赛学到了什么,克服了哪些困难,有哪些收获,不低于200字)这次比赛让我深入了解了rt-thread,对RT-Thread 软件包的使用有了丰富的经验。它是运行于 RT-Thread 物联网操作系统平台上,面向不同应用领域的通用软件组件,由描述信息、源代码或库文件组成。RT-Thread 提供了开放的软件包平台,这里存放了官方提供或开发者提供的软件包,该平台为开发者提供了众多可重用软件包的选择,这也是 RT-Thread 生态的重要组成部分。软件包生态对于一个操作系统的选择至关重要,因为这些软件包具有很强的可重用性,模块化程度很高,极大的方便应用开发者在最短时间内,打造出自己想要的系统。RT-Thread 已经支持的软件包数量已经达到60+,如下举例:。通过此次参赛,学习到了许多机器学习相关的知识,现在还处在入门阶段,对于过多数据的处理方法没有选对,使得手套动作的识别准确率不高,但后期一定会做更多的研究,做出一个完整的作品来
作者:Signx 概述 当前的人脸识别摄像头,价格昂贵,拟改造成stm32单片机版本的,节省成本 功能: 1.连接摄像头OV2640,用fish指令开启摄像头 2.将摄像头数据传到电脑显示图片 3.将摄像头数据传到spi显示屏显示(未完成) 4.用人脸识别算法识别人脸开锁(未完成) 5.可登录到tencent Cloud云平台,实现上云 开发环境: 硬件:Art-pi+OV2640 RT-Thread版本:4.0.2 开发工具及版本:rt-stuidio V2.0.0 RT-Thread使用情况概述: 硬件框架:art-pi官方开发板+OV2640(dma+DCMI) 软件框架说明: 见思维导图 软件模块说明: 见思维导图 该系统的思维脑图如下: 演示效果: 视频: 实物图如下: 实现的效果如图: 连接电路如图所示: 实现功能: 1.连接摄像头OV2640,用fish指令开启摄像头 2.将摄像头数据传到电脑显示图片 3.将摄像头数据传到spi显示屏显示(未完成) 4.用人脸识别算法识别人脸开锁(未完成) 5.可登录到tencent Cloud云平台,实现上云主要碰到的技术难点: 1.art-pi组建库里有ov2640的软件包,本以为直接能用的,结果发现有很多缺失 首先是io口的确实,没有定义到足够的io口,所以代码里又加上了io口的定义以及初始化: io口定义代码: typedef enum { BMP_QQVGA = 0x00, /* BMP Image QQVGA 160x120 Size */ BMP_QVGA = 0x01, /* BMP Image VGA 800*480 Size */ JPEG_160x120 = 0x02, /* JPEG Image 160x120 Size */ JPEG_176x144 = 0x03, /* JPEG Image 176x144 Size */ JPEG_320x240 = 0x04, /* JPEG Image 320x240 Size */ JPEG_352x288 = 0x05 /* JPEG Image 352x288 Size */ }ImageFormat_TypeDef; /*摄像头接口 */ //IIC SCCB //#define CAMERA_I2C I2C1 //#define CAMERA_I2C_CLK_ENABLE() __HAL_RCC_I2C1_CLK_ENABLE() // //#define CAMERA_I2C_SCL_PIN GPIO_PIN_8 //#define CAMERA_I2C_SCL_GPIO_PORT GPIOB //#define CAMERA_I2C_SCL_GPIO_CLK_ENABLE() __HAL_RCC_GPIOB_CLK_ENABLE() //#define CAMERA_I2C_SCL_AF GPIO_AF4_I2C1 // //#define CAMERA_I2C_SDA_PIN GPIO_PIN_9 //#define CAMERA_I2C_SDA_GPIO_PORT GPIOB //#define CAMERA_I2C_SDA_GPIO_CLK_ENABLE() __HAL_RCC_GPIOB_CLK_ENABLE() //#define CAMERA_I2C_SDA_AF GPIO_AF4_I2C1 //VSYNC #define DCMI_VSYNC_GPIO_PORT GPIOI #define DCMI_VSYNC_GPIO_CLK_ENABLE() __HAL_RCC_GPIOI_CLK_ENABLE() #define DCMI_VSYNC_GPIO_PIN GPIO_PIN_5 #define DCMI_VSYNC_AF GPIO_AF13_DCMI // HSYNC #define DCMI_HSYNC_GPIO_PORT GPIOH #define DCMI_HSYNC_GPIO_CLK_ENABLE() __HAL_RCC_GPIOH_CLK_ENABLE() #define DCMI_HSYNC_GPIO_PIN GPIO_PIN_8 #define DCMI_HSYNC_AF GPIO_AF13_DCMI //PIXCLK #define DCMI_PIX

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值