LD3320语音识别模块的简单应用

文章目录


一、 前言

最近复刻一个桌面宠物-小呆项目用到了LD3320语音识别模块,简单的分享下使用。

LD3320 是一颗基于非特定人语音识别芯片。芯片上集成了高精度的 A/D 和 D/A 接口,不再需要外接辅助的Flash 和 RAM,即可以实现语音识别/声控/人机对话功能。并且,识别的关键词语列表是可以动态编辑的。

这里介绍下什么是非特定人语音识别?

非特定人语音识别是一种不针对特定发音人的语音识别技术。这种技术不分年龄、性别,只要发音人说的是相同的语言就可以进行识别。它与特定人语音识别技术形成对比,后者是专门针对一个特定人的声音进行识别,而非特定人语音识别则更加灵活,能够满足不同人的语音识别需求,适合广泛人群应用。通俗点说,只要是拼音可以拼出的发音都是可以输入芯片进行识别的。


二、硬件

1.原理图

如下图所示,原理图主要由几部分组成

这里的主控IC是STC11L08XE,它是一种STC11系列的51单片机。这里主要采用的是串口通信的方式。

另外一个IC就是语音芯片LD3320,它内置一个麦克风放大器,它可以对周围的声音进行采集,并将采集到的音频信号送入芯片内部的语音信号处理器。

电源输入方面主要是5V输入,经过AMS1117-3.3进行降压为3.3V,3.3V再给两个IC供电。

这里主要讲一个点,如果要控制喇叭音量,可以通过外部电路的电阻来实现,例如下图中电阻 R9 和 R8 的阻值分别为

33K 和 10K,那么 33/10=3.3,声音被放大了约 3 倍。而如果给 R9 接入可变电阻,就可以手动调节音量了。

管脚 12(MBS)是麦克风偏置,保证能输出一个浮动电压给麦克风。

2.产品参数

产品参数

规格:43*29.7MM

供电电压:DC5V

IO口输出:高电平3.3V

通信方式:串口通信(3.3V TTL电平,不支持max232,RS485)

单片机:STC11L08XE、flash-->8K、SRAM-->1280、eeprom-->32K

三、软件

1.语音识别原理

把通过MIC输入的声音进行频谱分析->提取语音特征->和关键词语列表中的关键词语进行对比匹配->找出得分最高的关键词语作为识别结果输出。

语音识别芯片能在两种情况下给出识别结果:

  1. 外部送入预定时间的语音数据后(比如5秒钟的语音数据),芯片对这些语音数据运算分析后,给出识别结果
  2. 外部送入语音数据流,语音识别芯片通过端点检测VAD(voice activity detection)检测出用户停止说话,把用户开始说话到停止说话之间的语音数据进行运算分析后,给出识别结果

这里简单介绍下VAD的工作原理:VAD(Voice Activity Detection) 技术是在一段语音数据流中,判断出哪个时间点是人声的开始,哪个时间点是人声的结束。判断的依据是,在背景声音的基础上有了语音发音,则视为声音的开始。而后,检测到一段持续时间的背景音(比如600毫秒),则视为人声说话结束。

2.用户使用模式

用户可以通过编程,设置两种不同的用户使用模式:"触发识别模式"和"循环识别模式"。

2.1 触发识别模式

系统的主控MCU在接受到外界一个触发后(比如用户按动某个按键),启动LD3320芯片的一个定时识别过程(比如5秒钟),要求用户在这个定时过程中说出要识别的语音关键词语。过了这个过程后,需要用户再次触发才能再次启动一个识别过程。

2.2 循环识别模式

系统的主控MCU反复启动识别过程。如果没有人说话没有识别结果,则每次识别过程的定时到时后再启动一个识别过程;如果有识别结果,则根据识别作相应处理后(比如播放某个声音作为回答)再启动一个识别过程。

一般来说,触发识别适合于识别精度要求比较高的场合。外界触发后,产品可以播放提示音或者其他方式来提示用户在接下来的几秒钟内说出要识别的内容,这样来引导用户在规定的时间内只说出要识别的内容,从而保证比较高的识别率。

而循环识别比较适合于需要始终进行语音监控的场合,或者没有按键等其他设备控制识别开始的场合。而这种状态,识别准确度会有一定下降,在循环识别的过程中,用户的其他说话声音,或者外界的其他声音,都有可能被识别引擎误识别出错误的结果,需要产品的控制逻辑都作相应的处理。

3.语音识别程序

3.1 并行方式读写

LD 芯片的四种读写方式,分别是串行 SPI 的软、硬方式和并行 8 位总线的软、硬方式。这里主要讲下用到的并行方式--直接读写 (硬件实现并行读写方式 硬件实现并行读写方式),其他三种方式感兴趣的小伙伴可以自行去了解下。

控制串行/并行的管脚:ICR_MODE 连接 LD3320 芯片的 MD ,高电平为 SPI 方式,低电平为并行方式。

c 复制代码
	LD_MODE = 0;		//	设置MD管脚为低,并行模式读写

由于设计硬件电路板时,考虑到了双方芯片读写的时序特征,那么在合理连接的基础上,通过 2 条语句就可实现对 LD 芯片的操作。这种方式代码简练,执行速度最快。这是因为 STC 的单片机 STC10L08XE 自身带有硬件的并口方式,STC10L08XE 有单独的 WR和 RD 端口,可以在读写并行总线时,自动产生 WR 和 RD 信号。

c 复制代码
#define LD_INDEX_PORT		(*((volatile unsigned char xdata*)(0x8100)))
#define LD_DATA_PORT		(*((volatile unsigned char xdata*)(0x8000)))

void LD_WriteReg( unsigned char address, unsigned char dataout )
{
  LD_INDEX_PORT  = address;
  LD_DATA_PORT = dataout;
}

unsigned char LD_ReadReg( unsigned char address )
{
  LD_INDEX_PORT = address;
  return (unsigned char)LD_DATA_PORT;
}

3.2 初始化

语音识别用初始化(包括通用初始化)→写入识别列表→开始识别,并准备好中断响应函数,打开中断允许位。这里需要说明一下,如果不用中断方式,也可以通过查询方式工作。在"开始识别"后,读取寄存器 B2H 的值,如果为 21H 就表示有识别结果产生。在此之后读取候选项等操作与中断方式相同。

c 复制代码
/************************************************************************
功能描述: LD模块命令初始化
入口参数: none
返 回 值: none
其他说明: 该函数为出厂配置,一般不需要修改; 有兴趣的可对照开发手册根据需要自行修改。
**************************************************************************/
void LD_Init_Common()
{
	LD_ReadReg(0x06);
	LD_WriteReg(0x17, 0x35);
	delay(10);
	LD_ReadReg(0x06);

	LD_WriteReg(0x89, 0x03);
	delay(5);
	LD_WriteReg(0xCF, 0x43);
	delay(5);
	LD_WriteReg(0xCB, 0x02);

	/*PLL setting*/
	LD_WriteReg(0x11, LD_PLL_11);

	LD_WriteReg(0x1E, 0x00);
	LD_WriteReg(0x19, LD_PLL_ASR_19);
	LD_WriteReg(0x1B, LD_PLL_ASR_1B);
	LD_WriteReg(0x1D, LD_PLL_ASR_1D);
	delay(10);

	LD_WriteReg(0xCD, 0x04);
//	LD_WriteReg(0x17, 0x4c);
	delay(5);
	LD_WriteReg(0xB9, 0x00);
	LD_WriteReg(0xCF, 0x4F);
	LD_WriteReg(0x6F, 0xFF);
}

/************************************************************************
功能描述: 	 LD模块 ASR功能初始化
入口参数:	 none
返 回 值: 	 none
其他说明:	 该函数为出厂配置,一般不需要修改;有兴趣的可对照开发手册根据需要自行修改。
**************************************************************************/
void LD_Init_ASR()
{
	LD_Init_Common();
	LD_WriteReg(0xBD, 0x00);
	LD_WriteReg(0x17, 0x48);
	delay( 10 );
	LD_WriteReg(0x3C, 0x80);
	LD_WriteReg(0x3E, 0x07);
	LD_WriteReg(0x38, 0xff);
	LD_WriteReg(0x3A, 0x07);
	LD_WriteReg(0x40, 0);
	LD_WriteReg(0x42, 8);
	LD_WriteReg(0x44, 0);
	LD_WriteReg(0x46, 8);
	delay( 1 );
}

这里注意,下面三个寄存器,会随晶振频率变化而设置不同,请根据使用的晶振频率修改参考程序中的 CLK_IN。

c 复制代码
#define CLK_IN   		    22.1184	/* 用户注意修改输入的晶振时钟大小 */
#define LD_PLL_ASR_19 		(uint8)(CLK_IN*32.0/(LD_PLL_11+1) - 0.51)
#define LD_PLL_ASR_1B 		0x48
#define LD_PLL_ASR_1D 		0x1f
	
	LD_WriteReg(0x19, LD_PLL_ASR_19);
	LD_WriteReg(0x1B, LD_PLL_ASR_1B);
	LD_WriteReg(0x1D, LD_PLL_ASR_1D);

3.3 写入识别列表

列表的规则是,每个识别条目对应一个特定的编号(1 个字节),不同的识别条目的编号可以相同,而且不用连续。本芯片最多支持 50个识别条目,每个识别条目是标准普通话的汉语拼音(小写),每 2个字(汉语拼音)之间用一个空格间隔。编号可以相同,可以不连续,但是数值要小于 256(00H~FFH)。

先介绍一个读取 0xB2 寄存器的函数,如果在以后的 ASR 命令函数前不能够读取到正确 Idle 状态,说明芯片内部可能出错了。经拷机测试,当使用的电源电压/电流出现不稳定有较大波动时,有小概率会出现这种情况。出现这种情况时,建议 Reset LD3320 芯片,重新启动设置芯片。

c 复制代码
/************************************************************************
功能描述:  检测LD模块是否空闲
入口参数:	none
返 回 值: 	flag:1-> 空闲
其他说明:	none
**************************************************************************/
uint8 LD_Check_ASRBusyFlag_b2()
{
	uint8 j;
	uint8 flag = 0;
	for (j = 0; j < 10; j++)
	{
		if (LD_ReadReg(0xb2) == 0x21)
		{
			flag = 1;
			break;
		}
		delay(10);
	}
	return flag;
}

/************************************************************************
功能描述: 	 复位LD模块
入口参数:	 none
返 回 值: 	 none
其他说明:	 none
**************************************************************************/
void LD_Reset()
{
	RSTB = 1;
	delay(5);
	RSTB = 0;
	delay(5);
	RSTB = 1;

	delay(5);
	CSB = 0;
	delay(5);
	CSB = 1;
	delay(5);
}

/************************************************************************
功能描述: 向LD模块添加关键词
入口参数: none
返 回 值: flag:1->添加成功
其他说明: 用户修改.
					 1、根据如下格式添加拼音关键词,同时注意修改sRecog 和pCode 数组的长度
					 和对应变了k的循环置。拼音串和识别码是一一对应的。
					 2、开发者可以学习"语音识别芯片LD3320高阶秘籍.pdf"中
           关于垃圾词语吸收错误的用法,来提供识别效果。
					 3、"xiao dai " 为口令,故在每次识别时,必须先发一级口令"小呆"
**************************************************************************/
uint8 LD_AsrAddFixed()
{
	uint8 k, flag;
	uint8 nAsrAddLength;
#define DATE_A 50   /*数组二维数值*/
#define DATE_B 20		/*数组一维数值*/
	uint8 code sRecog[DATE_A][DATE_B] =
	{
		"xiao dai", \
		"li zheng", \
		"qi li", \
		"qi shen", \
		"zhan qi lai", \
		"qian jin", \
		"zou", \
		"hou tui", \
		"zuo zhuan", \
		"you zhuan", \
		"pa xia", \
		"wo xia",\
		"zuo xia",\
		"ca lian",\
		"zuo xia ca lian",\
		"shen lan yao",\
		"tai tou",\
		"da zhao hu",\
		"da ge zhao hu",\
		"ha lou",\
		"hai",\
		"yao bai",\
		"tai shou",\
		"tiao wu",\
		"shui jiao",\
		"nan shou",\
		"biao yan bu kai xin",\
		"biao bai",\
		"yuan su zhou qi biao",\
		"xiao xun",\
		"wei shen me",\
		"ni kai xin ma",\
		"cha kan kai xin zhi",\
		"lei bu lei",\
		"cha kan ti li zhi",\
		"cha kan zhi shu",\
	};	/*添加关键词,用户修改*/
	uint8 code pCode[DATE_A] =
	{
		CODE_CMD, \
		CODE_1, \
		CODE_2, \
		CODE_2, \
		CODE_2, \
		CODE_3, \
		CODE_3, \
		CODE_4, \
		CODE_5, \
		CODE_6, \
		CODE_7, \
		CODE_8, \
		CODE_9, \
		CODE_9, \
		CODE_9, \
		CODE_10, \
		CODE_11, \
		CODE_12, \
		CODE_12, \
		CODE_12, \
		CODE_12, \
		CODE_13, \
		CODE_14, \
		CODE_16, \
		CODE_17, \
		CODE_17, \
		CODE_18, \
		CODE_19, \
		CODE_20, \
		CODE_21, \
		CODE_22, \
		CODE_23, \
		CODE_23, \
		CODE_24, \
		CODE_24, \
		CODE_25, \
		CODE_26, \
		CODE_27, \
		CODE_28, \
		CODE_29, \
		CODE_30, \
		CODE_31, \
		CODE_32, \
		CODE_33, \
		CODE_34, \
		CODE_35, \
	};	/*添加识别码,用户修改*/
	flag = 1;
	for (k = 0; k < DATE_A; k++)
	{

		if(LD_Check_ASRBusyFlag_b2() == 0)
		{
			flag = 0;
			break;
		}

		LD_WriteReg(0xc1, pCode[k] );
		LD_WriteReg(0xc3, 0 );
		LD_WriteReg(0x08, 0x04);
		delay(1);
		LD_WriteReg(0x08, 0x00);
		delay(1);

		for (nAsrAddLength = 0; nAsrAddLength < DATE_B; nAsrAddLength++)
		{
			if (sRecog[k][nAsrAddLength] == 0)
				break;
			LD_WriteReg(0x5, sRecog[k][nAsrAddLength]);
		}
		LD_WriteReg(0xb9, nAsrAddLength);
		LD_WriteReg(0xb2, 0xff);
		LD_WriteReg(0x37, 0x04);
	}
	return flag;
}

3.4 开始识别

设置几个相关的寄存器,就可以控制 LD3320 芯片开始语音识别。值得注意:单片机程序中,一般会用一个全局变量控制当前状态,(例如:LD_ASR_RUNING 状态或者 LD_ASR_FOUNDOK 状态),在编程时一定要把对该状态的设置放在正式 LD3320 芯片开始识别以前。

c 复制代码
/************************************************************************************/
//	nAsrStatus 用来在main主程序中表示程序运行的状态,不是LD3320芯片内部的状态寄存器
//	LD_ASR_NONE:		表示没有在作ASR识别
//	LD_ASR_RUNING:		表示LD3320正在作ASR识别中
//	LD_ASR_FOUNDOK:		表示一次识别流程结束后,有一个识别结果
//	LD_ASR_FOUNDZERO:	表示一次识别流程结束后,没有识别结果
//	LD_ASR_ERROR:		表示一次识别流程中LD3320芯片内部出现不正确的状态
/***********************************************************************************/
uint8 idata nAsrStatus = 0;

nAsrStatus = LD_ASR_NONE;		//	初始状态:没有在作ASR

//
	while(1)
	{
		switch(nAsrStatus)
		{
		case LD_ASR_RUNING:
		case LD_ASR_ERROR:
			break;
		case LD_ASR_NONE:
		{
			nAsrStatus = LD_ASR_RUNING;
			if (RunASR() == 0)	/*	启动一次ASR识别流程:ASR初始化,ASR添加关键词语,启动ASR运算*/
			{
				nAsrStatus = LD_ASR_ERROR;
			}
			break;
		}
		case LD_ASR_FOUNDOK: /*	一次ASR识别流程结束,去取ASR识别结果*/
		{
			nAsrRes = LD_GetResult();		/*获取结果*/
			User_handle(nAsrRes);//用户执行函数
			nAsrStatus = LD_ASR_NONE;
			break;
		}
		case LD_ASR_FOUNDZERO:
		default:
		{
			nAsrStatus = LD_ASR_NONE;
			break;
		}
		}// switch
	}// while		

ASR识别流程的代码如下所示:

c 复制代码
/************************************************************************
功能描述: 	运行ASR识别流程
入口参数:	none
返 回 值:  asrflag:1->启动成功, 0--->启动失败
其他说明:	识别顺序如下:
						1、RunASR()函数实现了一次完整的ASR语音识别流程
						2、LD_AsrStart() 函数实现了ASR初始化
						3、LD_AsrAddFixed() 函数实现了添加关键词语到LD3320芯片中
						4、LD_AsrRun()	函数启动了一次ASR语音识别流程
						任何一次ASR识别流程,都需要按照这个顺序,从初始化开始
**************************************************************************/
uint8 RunASR(void)
{
	uint8 i = 0;
	uint8 asrflag = 0;
	for (i = 0; i < 5; i++)			//	防止由于硬件原因导致LD3320芯片工作不正常,所以一共尝试5次启动ASR识别流程
	{
		LD_AsrStart();
		delay(50);
		if (LD_AsrAddFixed() == 0)
		{
			LD_Reset();			//	LD3320芯片内部出现不正常,立即重启LD3320芯片
			delay(50);			//	并从初始化开始重新ASR识别流程
			continue;
		}
		delay(10);
		if (LD_AsrRun() == 0)
		{
			LD_Reset();			//	LD3320芯片内部出现不正常,立即重启LD3320芯片
			delay(50);			//	并从初始化开始重新ASR识别流程
			continue;
		}
		asrflag = 1;
		break;					//	ASR流程启动成功,退出当前for循环。开始等待LD3320送出的中断信号
	}

	return asrflag;
}

/************************************************************************
功能描述: 	启动ASR
入口参数:	none
返 回 值: 	none
其他说明:	none
**************************************************************************/
void LD_AsrStart()
{
	LD_Init_ASR();
}
/************************************************************************
功能描述: 	运行ASR
入口参数:	none
返 回 值: 	1:启动成功
其他说明:	none
**************************************************************************/
uint8 LD_AsrRun()
{
	EX0 = 0;
	LD_WriteReg(0x35, MIC_VOL);
	LD_WriteReg(0x1C, 0x09);
	LD_WriteReg(0xBD, 0x20);
	LD_WriteReg(0x08, 0x01);
	delay( 1 );
	LD_WriteReg(0x08, 0x00);
	delay( 1 );

	if(LD_Check_ASRBusyFlag_b2() == 0)
	{
		return 0;
	}
//	LD_WriteReg(0xB6, 0xa); //识别时间	 1S
//	LD_WriteReg(0xB5, 0x1E); //背景音段时间 300ms
//	LD_WriteReg(0xB8, 10); //结束时间

//	LD_WriteReg(0x1C, 0x07); //配置双通道音频信号做为输入信号
	LD_WriteReg(0x1C, 0x0b); //配置麦克风做为输入信号


	LD_WriteReg(0xB2, 0xff);
	delay( 1);
	LD_WriteReg(0x37, 0x06);
	delay( 1 );
	LD_WriteReg(0x37, 0x06);
	delay( 5 );
	LD_WriteReg(0x29, 0x10);

	LD_WriteReg(0xBD, 0x00);
	EX0 = 1;
	return 1;
}

3.5 响应中断

如果麦克风采集到声音,不管是否识别出正常结果,都会产生一个中断信号。而中断程序要根据寄存器的值分析结果。读取 BA 寄存器的值,可以知道有几个候选答案,而 C5 寄存器里的答案是得分最高、最可能正确的答案。

c 复制代码
功能描述: 	中断处理函数
入口参数:	 none
返 回 值: 	 none
其他说明:	当LD模块接收到音频信号时,将进入该函数,
						判断识别是否有结果,如果没有从新配置寄
            存器准备下一次的识别。
**************************************************************************/
void ProcessInt0(void)
{
	uint8 nAsrResCount = 0;

	EX0 = 0;
	ucRegVal = LD_ReadReg(0x2B);
	LD_WriteReg(0x29, 0) ;
	LD_WriteReg(0x02, 0) ;
	if((ucRegVal & 0x10) &&
	    LD_ReadReg(0xb2) == 0x21 &&
	    LD_ReadReg(0xbf) == 0x35)			/*识别成功*/
	{
		nAsrResCount = LD_ReadReg(0xba);
		if(nAsrResCount > 0 && nAsrResCount <= 4)
		{
			nAsrStatus = LD_ASR_FOUNDOK;
		}
		else
		{
			nAsrStatus = LD_ASR_FOUNDZERO;
		}
	}/*没有识别结果*/
	else
	{
		nAsrStatus = LD_ASR_FOUNDZERO;
	}

	LD_WriteReg(0x2b, 0);
	LD_WriteReg(0x1C, 0); /*写0:ADC不可用*/

	LD_WriteReg(0x29, 0) ;
	LD_WriteReg(0x02, 0) ;
	LD_WriteReg(0x2B,  0);
	LD_WriteReg(0xBA, 0);
	LD_WriteReg(0xBC, 0);
	LD_WriteReg(0x08, 1);	 /*清除FIFO_DATA*/
	LD_WriteReg(0x08, 0);	/*清除FIFO_DATA后 再次写0*/


	EX0 = 1;
}

/************************************************************************
功能描述: 	获取识别结果
入口参数:	none
返 回 值: 	LD_ReadReg(0xc5 );  读取内部寄存器返回识别码。
其他说明:	none
**************************************************************************/
uint8 LD_GetResult()
{
	return LD_ReadReg(0xc5 );
}

值得注意:获取识别结果LD_ReadReg(0xba); 多少条候选识别结果,值 1~4 说明是有正确的识别结果。

4 个候选结果的读取:根据 0xba 决定读取几个识别结果。

c 复制代码
LD_ReadReg(0xc5);
LD_ReadReg(0xc7);
LD_ReadReg(0xc9);
LD_ReadReg(0xcb);

在目前的程序中,只读取了最优候选。在其他使用场合,如果需要读取其他候选,用户可以自己编程实现。

另外LD3320还支持像MP3一样播放的声音,这些声音是事先在PC机上录制好的MP3文件,在PC机上合成到一个文件 voice.dat中

可以把这个voide.dat存储到用户系统中的存储芯片中,诸如 spi-flash中,在需要播放时,用户的主控MCU可以到spi-flash中根据要播放文件的起始地址无读取MP3数据,并送入LD3320进行播放。这里不详细介绍,感兴趣的小伙伴可以自己去了解下,最后讲下如何烧写程序。

4.烧写程序

如果没有烧写软件,可以去这里stc-isp 单片机烧录软件安装与使用下载

4.1 硬件连接

用串口调试工具连接LD3320语音识别模块,连接如下:

串口调试工具 LD3320语音识别模块
GND GND
RXD TXD
TXD RXD
3V3 3.3V

注意 RXD/TXD 必须交叉连接

4.2 打开STC-ISP软件

4.3 按要求配置软件

  1. 选择单片机型号
  2. 串口号一般会直接识别,识别不到就根据设备管理器的端口选择串口号
  3. 打开程序文件,选择程序hex文件
  4. 点击下载/编程进行烧写程序,这时候需要重新给模块通上电源(拔下 GND 连接线,再重新连接,即可以重新上电,下载程序时冷启动也是一样的操作)

4.4 烧写成功


四、总结

今天主要讲了LD3320语音识别模块的简单应用。

感谢你的观看!

相关推荐
有Li2 分钟前
跨视角差异-依赖网络用于体积医学图像分割|文献速递-生成式模型与transformer在医学影像中的应用
人工智能·计算机视觉
新加坡内哥谈技术23 分钟前
Mistral推出“Le Chat”,对标ChatGPT
人工智能·chatgpt
风尚云网27 分钟前
风尚云网前端学习:一个简易前端新手友好的HTML5页面布局与样式设计
前端·css·学习·html·html5·风尚云网
GOTXX31 分钟前
基于Opencv的图像处理软件
图像处理·人工智能·深度学习·opencv·卷积神经网络
IT古董36 分钟前
【人工智能】Python在机器学习与人工智能中的应用
开发语言·人工智能·python·机器学习
CV学术叫叫兽1 小时前
快速图像识别:落叶植物叶片分类
人工智能·分类·数据挖掘
WeeJot嵌入式1 小时前
卷积神经网络:深度学习中的图像识别利器
人工智能
脆皮泡泡1 小时前
Ultiverse 和web3新玩法?AI和GameFi的结合是怎样
人工智能·web3
机器人虎哥1 小时前
【8210A-TX2】Ubuntu18.04 + ROS_ Melodic + TM-16多线激光 雷达评测
人工智能·机器学习
码银2 小时前
冲破AI 浪潮冲击下的 迷茫与焦虑
人工智能