AI智能语音机器人
原标题:AI智能语音机器人
来源:CSDN博客 链接:https://blog.csdn.net/C15191504149/article/details/100062953
项目简介
使用C++编写一个智能AI对话和语音命令执行的语音管理工具
其中可执行命令支持配置
项目技术点
C++ STL
http第三方库
图灵机器人
百度语音识别和语音合成
Linux系统/网络编程
各种第三方库和第三方工具的安装与使用
项目演示
程序启动之后,加载命令配置文件,启动本地录音工具,开始录音
程序准备识别, 将特定格式的录音推送到百度语音识别平台进行识别,返回识别完毕之后的文字信息
对比识别之后的输入文本,辨别是命令还是普通对话信息
如果是命令,进入系统命令执行模块,执行完毕,看到执行结果,一次交互完成
如果是对话信息,推送给图灵机器人,图灵机器人会智能相应对话,得到图灵机器人响应的文本,调用百
度语音合成,在本地合成语音,然后程序启动播放器,播放语音信息,完成对话信息
支持语言退出
演示项目效果
1. 使用图灵机器人
注册图灵机器人,官网:http://www.turingapi.com/
注册完毕时候,需要身份认证,安装提示来就行【图灵平台在2019-6-5号开始,需要个人进行身份认证】登录之后,就可以看看图灵官网的参考手册,也可以点击链接:https://www.kancloud.cn/turing/www-tuling123-com/718227
根据官网手册可以看出,请求是依赖http协议,请求和相应的数据都是json格式,所以,我们必须的1. 能够发起http的POST请求 2. 能够使用json进行数据序列号和反序列化
http我们不在单独写了,使用第三方库,可以选择httplib,但是后面的百度语音识别自带了httpclient,我们后面可以使用。
json进行数据序列号和反序列化功能需要我们完成,我们选择一个开源的C++库:jsoncpp
2. 使用百度语音识别
注册百度语音识别,上官网:http://ai.baidu.com/
应用的创建,全部默认即可
技术文档我们选择C++SDK
3. 准备需要的第三方库和sdk
因后续开发需要,需要先安装高版本cmake,gcc
cmake更新: https://blog.csdn.net/fancyler/article/details/78009812
安装高版本gcc:
# sudo yum install centos-release-scl
# sudo yum install devtoolset-6
# scl enable devtoolset-6 bash
a. 下载百度C++语音识别SDK
在http://ai.baidu.com/sdk下载,识别、合成 RESTful API C++ SDK,后面的工具,基本都要支持C++11以上
b. 安装jsoncpp,要求:>1.6.2版本,0.x版本将不被支持
json官网:http://www.json.org/json-zh.html
注意,推荐使用https://github.com/open-source-parsers/jsoncpp/releases 1.8.3版本,其他最新版本可能会有中文编码的问题
下载好安装包,解压
# cd jsoncpp-1.8.3
# make build
# cd build
# cmake ..
# make
# sudo make install
c. 安装openssl
openssl轻易不要卸载,之间更新开发包即可,如果不行,需要结合实际情况进行安装
# sudo yum install openssl-devel
d. 安装libcurl(需要支持https)
官网:<https://curl.haxx.se/download.html
下载curl-7.64.1.tar.gz版本
# tar xzf curl-7.64.1.tar.gz
# cd curl-7.64.1
# ./configure
# make
# sudo make install
4. 准备Centos 7录音和播放工具
语音录制与播放
采用arecord进行语音录制,使用vlc/cvlc进行播放
arecord系统默认自带,可以直接输入arecord看看你的平台有没有安装,如果没有,就会告诉你“bash:XXX: command not found...”,网上查阅进行安装即可,参考链接:https://blog.csdn.net/outstanding_yzq/article/details/8126350
使用arecord进行录音
语音采样样例:
# arecord -t wav -c 1 -r 16000 -d 5 -f S16_LE demo.wav
// -t: 设置文件类型,我们采用wav格式
// -c: 设置通道号
// -r: 设置频率
// -d: 设置持续时间,单位为秒
// -f: 设置采样格式.格式包括:S8 U8 S16_LE(S16_LE: little endian signed 16 bits)
// S16_BE U16_LE U16_BE S24_LE S24_BE U24_LE U24_BE S32_LE S32_BE
// U32_LE U32_BE FLOAT_LE FLOAT_BE FLOAT64_LE FLOAT64_BE
// IEC958_SUBFRAME_LE IEC958_SUBFRAME_BE MU_LAW A_LAW IMA_ADPCM MPEG GSM
//我们为何这样采用,主要是因为百度语音识别的要求,具体可以参照百度语音识别文档
运行上面的命令,会在当前目录下录制demo.wav格式的语音,测试播放可以使用自带的
# aplay demo.wav
播放你录制的音频.
安装vlc/cvlc播放器
我们在语音合成之后,直接使用aplay播放会有一定问题,我们采用第三方工具vlc/cvlc实现MP3文件播放
# sudo rpm -Uvh http://li.nux.ro/download/nux/dextop/el7/x86_64/nux-dextop-release-0-5.el7.nux.noarch.rpm
# sudo yum install vlc -y
代码如下:
1、项目目录:
build.sh //构建项目的shell脚本,简单实现
command.etc //语音命令配置文件
Jarvis.cc //Jarvis 源文件
Jarvis.hpp //Jarvis 核心代码
Makefile
speech //引入百度语音识别SDK
temp_file //保存临时语音文件目录
2、Jarvis.hpp
#ifndef _Jarvis_HPP_
#define _Jarvis_HPP_
#include<iostream>
#include<cstdio>
#include<string>
#include<map>
#include<unordered_map>
#include<memory>
#include<sstream>
#include<unistd.h>
#include<pthread.h>
#include<json/json.h>
#include"base/http.h"
#include"speech.h"
using namespace std;
#define PLAY_FILE "temp_file/play.mp3"
#define VOICE_FILE "temp_file/voice.wav"
#define CMD_ETC "command.etc"
class Util{
private:
static pthread_t tid;
public:
//在Linux中执行指定命令,以及是否需要打印出命令
static bool Exec(string command, bool is_print){
if(!is_print){
//不打印
command += ">/dev/null 2>&1";
}
FILE* fp = popen(command.c_str(), "r");
if(NULL == fp){
cerr << "popen error" << endl;
return false;
}
if(is_print){
char c;
cout << "--------start--------" << endl;
while(fread(&c, 1, 1, fp) > 0){
cout << c;
}
cout << endl;
cout << "---------end---------" << endl;
}
pclose(fp);
return true;
}
//让等待的过程动态化
static void* Move(void* arg){
string message = (char*)arg;
const char* lable = "......";
const char* blank = " ";
int i = 5;
int j = 0;
while(1){
cout << message << lable+i << "r";
fflush(stdout);
i--;
j++;
if(i < 0){
i = 5;
cout << message << blank << "r";
}
usleep(500000);
}
}
//开始展示消息
static void BeginShowMessage(string message){
pthread_create(&tid, NULL, Move, (void*)message.c_str());
}
//结束展示
static void EndShowMessage(){
pthread_cancel(tid);
}
};
pthread_t Util::tid;
class SpeechRec{
private:
static string app_id;
static string api_key;
static string secret_key;
aip::Speech *client;//SDK对应的语音识别客户端
public:
//语音识别
SpeechRec()
{
client = new aip::Speech(app_id, api_key, secret_key);
}
//语音识别
string ASR(const string &voice_bin){
Util::BeginShowMessage("正在识别");
map<string, string> options;//以中文显示
options["dev_pid"]= "1536";
string file_content;
aip::get_file_content(voice_bin.c_str(), &file_content);//获取语音文件
Json::Value result = client->recognize(file_content, "wav", 16000, options);//开始识别,需要语音文件,格式是"wav",频率是16k
Util::EndShowMessage();
int code = result["err_no"][0].asInt();
if(code != 0){
cerr << "code: " << code << "err_msg: " << result["err_msg"].asString() << endl;
return "";
}
return result["result"][0].asString();
}
//语音合成,文本转语音
void TTS(string &text, string voice_tts){
ofstream ofile;
string ret;
map<string, string> options;
options["spd"] = "5";
options["per"] = "4";
options["vol"] = "15";
Util::BeginShowMessage("正在合成");
ofile.open(voice_tts, ios::out | ios::binary);
//语音合成,文本转语音,放到指定目录,形成指定文件
Json::Value result = client->text2audio(text, options, ret);
if(ret.empty()){
cerr << result.toStyledString() << endl;
}
else{
ofile << ret;
}
ofile.close();
Util::EndShowMessage();
}
~SpeechRec()
{
delete client;
client = nullptr;
}
};
string SpeechRec::app_id = "16685272";
string SpeechRec::api_key = "PHf8eAYFmVGkOGkcaETg6QgG";
string SpeechRec::secret_key = "62V1w1U36y5WkfC742woxOwZGhQpCUX9";
class Robot{
private:
string url = "openapi.tuling123.com/openapi/api/v2";
string api_key = "efe2cac8a7104897a01ef250bccdcc50";
string user_id = "1";//用户ID,可以随意指定
aip::HttpClient client;//使用百度语音识别自带的HttpClient发起相关请求
public:
Robot()
{}
//获取Json串,序列化,多转一
string MakeJsonString(const string &message){
Json::Value root;
Json::Value item;
Json::Value item1;
Json::Value item2;
item["apiKey"] = api_key;
item["userId"] = user_id;
root["reqType"] = 0;
root["userInfo"] = item;
item1["text"] = message;
item2["inputText"] = item1;
root["perception"] = item2;
Json::StreamWriterBuilder wb;
ostringstream os;
unique_ptr<Json::StreamWriter> jw(wb.newStreamWriter());
jw->write(root, &os);
return os.str();
}
//利用HttpClient自带的http协议发起post请求
string PostRequest(string &body){
string response;
int code = client.post(url, nullptr, body, nullptr, &response);
if(code != CURLcode::CURLE_OK){
return "";
}
return response;
}
//反序列化,一转多
string ParseJson(string &response){
JSONCPP_STRING errs;
Json::Value root;
Json::CharReaderBuilder rb;
unique_ptr<Json::CharReader> const rp(rb.newCharReader());
bool res = rp->parse(response.data(), response.data()+response.size(), &root, &errs);
if(!res || !errs.empty()){
cerr << "Jsonparse error" << errs << endl;
return "";
}
Json::Value item = root["result"][0];
Json::Value item1 = item["values"];
return item1["text"].asString();
}
//文本交流,传入一个字符串,经过序列化与反序列化后,返回一个字符串
string Talk(string &message, string &result){
string body = MakeJsonString(message);
string response = PostRequest(body);
result = ParseJson(response);
}
~Robot()
{}
};
class Jarvis{
private:
SpeechRec sr;
Robot rt;
unordered_map<string, string> cmd_map;
public:
//使用arecord工具进行录音,语音转文本
bool RecordVoice(){
Util::BeginShowMessage("正在录音");
bool ret = true;
string command = "arecord -t wav -c 1 -r 16000 -d 5 -f S16_LE ";
command += VOICE_FILE;
if(!Util::Exec(command, false)){
cerr << "Record error" << endl;
ret = false;
}
Util::EndShowMessage();
return ret;
}
public:
Jarvis()
{
//加载命令执行配置文件command.etc
ifstream in(CMD_ETC);
if(!in.is_open()){
cerr << "open error" << endl;
exit(1);
}
char buffer[128];
string sep = ":";
while(in.getline(buffer, sizeof(buffer))){
string str = buffer;
size_t pos = str.find(sep);
if(string::npos == pos){
cerr << "Load Etc Error" << endl;
exit(2);
}
string k = str.substr(0, pos);
string v = str.substr(pos + sep.size());
k += "。";
cmd_map.insert(make_pair(k, v));
in.close();
}
}
//判断一条消息是否是命令,如果是命令,就执行,不需要交给机器人进行对话
bool IsCommand(const string &text, string &out_command){
auto iter = cmd_map.find(text);
if(iter != cmd_map.end()){
out_command = iter->second;
return true;
}
out_command = "";
return false;
}
//使用百度语音合成接口,文本转语音,再使用cvlc进行本地播放
void PlayVoice(string voice_file){
string cmd = "cvlc --play-and-exit ";//播放完毕
cmd += voice_file;
Util::Exec(cmd, false);//执行播放
}
void Run(){
string voice_bin = VOICE_FILE;
volatile bool is_quit = false;
string cmd;
while(!is_quit){
cmd = "";
if(RecordVoice()){
string text = sr.ASR(voice_bin);//进行录音
cout << "Me# "<< text << endl;
if(IsCommand(text, cmd)){//判定是否是命令
cout << "[luvky@bogon Jarvis]$ " << cmd << endl;
Util::Exec(cmd, true);
}
else{
string message;
if(text == "退下吧。"){
message = "臣告辞";
is_quit = true;
}
else{//不是命令就交给机器人
rt.Talk(text, message);
cout << "Robot# " << message << endl;
}
string voice_tts;
sr.TTS(message, PLAY_FILE);//使用百度语音合成
PlayVoice(PLAY_FILE);//播放语音
}
}
}
}
~Jarvis()
{}
};
#endif
3、Jarvis.cc
#include"Jarvis.hpp"
using namespace std;
int main(){
Jarvis js;
js.Run();
return 0;
}
4、command.etc
查看内存:free
退出:exit
显示当前文件:ls -al
查看硬盘:df -h
关闭防火墙:systemctl stop firewalld
打开防火墙:systemctl start firewalld
查看进程:ps aux
5、Makefile
Jarvis:Jarvis.cc
g++ -std=c++11 -o Jarvis Jarvis.cc -Ispeech -ljsoncpp -lcurl -lcrypto -lpthread
.PHONY:clean
clean:
rm -f Jarvis
6、build.sh
#!/bin/bash
make clean
make
项目总结:
整体来讲,这个项目的难度不大,核心点在于对各个工具,平台的收悉程度,我们主要从这个项目中吸收的东西是: 学会使用第三方平台,第三方工具,就能完成很有意思的功能
————————————————
版权声明:本文为CSDN博主「C15191504149」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/C15191504149/article/details/100062953
一THE END一
免责声明:本文来自互联网新闻客户端自媒体,不代表本网的观点和立场。
合作及投稿邮箱:E-mail:editor@tusaishared.com
上一篇:语音识别框架
热门资源
国内人才报告:机...
近日,BOSS 直聘职业科学实验室 &BOSS 直聘研究院...
AI使物联网更智能...
看到微软对物联网和人工智能的结合感兴趣是一个明...
推荐一批学习自然...
这里推荐一批学习自然语言处理相关的书籍,当然,...
安防智能化大势下...
大部分传统安防设备不仅拍摄视野有限,而且无法事...
20亿创业基金、10...
近日,杭州举办了建设国家新一代人工智能创新发展...
智能在线
400-630-6780
聆听.建议反馈
E-mail: support@tusaishared.com