使用树莓派实现翻译机
只用一根网线实现树莓派和PC的链接
硬件:
- 树莓派套件(树莓派3B、16G内存卡、读卡器、充电器)
- 一台可以上网PC(我的操作系统版本为Windows10 1709)
- 一个USB耳机(有麦)
- 一根网线
软件
- 树莓派操作系统(我使用的是Raspbian)
- Putty(安装在Windows端,用来设置树莓派端)
- VNCViewer(安装在Windows端,用来链接树莓派端)
- Win32DiskImager(安装在Windows端,用来在内存卡上制作“启动盘”)
硬件和软件准备好了,我们现在正式开始翻译机的制作
安装树莓派系统
- 下载操作系统并解压缩
版本信息如下
Version:April 2018
Release date:2018-04-18
Kernel version:4.14
下载地址:
https://downloads.raspberrypi.org/raspbian_latest
下载下来的是一个压缩包,解压缩以后是img格式的镜像文件
2. 制作“启动盘”
①下载Win32DiskImager并安装,可以百度,版本无所谓;
下载地址https://sourceforge.net/projects/win32diskimager/
②双击运行Win32DiskImager,设置如下;
校验值选“无”,不勾选仅读取分配分区
写入完成后,一般会弹出一个让你格式化的弹框,直接叉掉就好
然后把把内存卡插到树莓派上就好
【注意!!!不是用读卡器把内存卡插到树莓派上,而是插到树莓派上面的内存卡插槽里】
OK,到这边,树莓派系统已经算是安装好了(只用插好内存卡就可以了,不需要其他操作)。
连接树莓派和电脑
①在Windows上安装Putty和VNCViewer
百度安装即可,没什么好说的;
②给树莓派通电,并用网线连接树莓派和电脑
③在Windows上设置网络为共享;如图所示
④Window+r打开cmd命令行,arp -a命令,获取树莓派的ip地址
在192.168.137.1栏中,192.168.137.255上面的ip地址即为树莓派的ip地址,如图(每次电脑重新开机后,这个ip地址都会变):
⑤打开Putty软件,填上树莓派的ip地址,点击open
会弹出这样的对话框,选择否。
然后登录即可,默认登录账户是:pi,密码是raspberry
这样已经实现了树莓派和电脑相连,可以操作树莓派系统了(Linux)。
但是现在只能通过命令行操作,如果想要图形界面的话还需要进行一些配置。
图形化界面配置
- 安装tightvncserver服务
sudo apt-get update
sudo apt-get installtightvncserver
- 安装xrdp服务
sudo apt-get install xrdp
- 给树莓派安装服务包
sudo apt-get install vnc4server tightvncserver
- 启动tightserver服务
tightserver
#这个命令会提示你设置密码,确认密码等,一定要记住密码!
理论上现在应该可以使用Windows自带的远程桌面进行连接了,但是我的电脑好像连接不了(截止至2018.06.22还未解决),所以我又借助了其他工具进行连接。就是我们刚刚安装的VNCViewer软件,双击运行即可。【注意!!这里的地址是树莓派的ip地址加上”:1”】,然后点connect。
输入刚刚设置的密码,即可连接
这就是树莓派的图形化界面啦,本来是有壁纸的,后来被我搞丢了。
到这里,树莓派和电脑的连接就完成啦!
下面,开始编程。
翻译机编程实现
编程主要使用Java和一些shell命令(很少,只有几句)。我们先来捋一捋,本程序要主要的模块
- 录音
- 把录下来的音频文件转换成文字
- 把文字从中文转换成英文
- 把文字转换成音频文件
- 播放音频文件
我们分别从这几个模块分析:
1.录音
这个功能其实很简单,只要调用一条shell命令即可
arecord -D plughw:Device -d 5 -r 16384 -f S16 input.wav
#注解
#arecord是Linux的录音功能,树莓派里面自带,不需要安装
#-D plughw:Device是指选择USB耳机作为音频输入
#-d 5是指录五秒的音,可以选择其他的
#-r 16384是指采码率是16k,最好就用这个
#-f S16是指位深16位,就用这个吧
#input.wav是录音保存在哪,这个随便写
2.音频文件转换成文字
这个功能一开始我是准备使用Google的speechAPI的,后来因为某些众所周知的原因,我选择了百度的语音识别API,也可以用科大讯飞的API,这里以百度的语音识别API为例(因为是免费的),使用起来非常简单
可以在这里下载SDK,有各种语言版本的,我选择的是Java版本,下载下来以后里面是3个jar文件,利用Eclipse使用很简单,下面的开发文档讲到非常详细。
http://ai.baidu.com/sdk
这是百度语音识别API的开发文档
http://ai.baidu.com/docs#/ASR-Online-Java-SDK/top
3.中文翻译成英文
语音识别功能我们使用的是百度的语音识别API,所以,翻译功能我们也使用百度的翻译API吧,使用方法跟语音识别API差不多,在百度AI平台上面也可以找到demo和开发文档,这里就不在赘述,非常简单。
4.文字转换成音频文件
这个功能跟语音识别API集成在一起了,所以在配置语音识别API的时候,这个也配置好了,技术文档也很简单。
5.播放音频文件
这个功能也很简单,跟录音功能类似,也是只要一条Linux里的命令就可以实现
aplay -D plughw:Device output.wav
接下来是具体实现(由于时间比较急,所以虽然功能实现了,但是代码的质量其实很差)
使用Eclipse编程,项目结构如下:
jars文件夹
jars文件夹里面是百度语音识别和百度语音合成的SDK。懒的去官网找的可以从我的云盘里下载。
①下载下来之后在项目里面新建文件夹jars
②然后把jar包复制到文件夹内
③选中这四个jar包,右击build path
https://download.csdn.net/download/s1455364690/10493784
链接: https://pan.baidu.com/s/1aQ77vyXeCDP_A7wauezxMQ 密码: ve2g
main包
package main;
/*
时间:2018/06/22
描述:树莓派翻译机主类
版本:1.0
作者:WHU_Sun
邮箱:1455364690@qq.com
*/
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.util.Scanner;
import org.json.JSONException;
import org.json.JSONObject;
import speechToLec.Sample;
import tools.Audioplay;
import tools.Tools;
import translate.TransApi;
public class Main {
//Linux相关命令
static String arecord = "arecord -D plughw:Device -d 5 -r 16384 -f S16 input.wav";
static String convert = "mpg321 -w output.wav output.mp3";
static String aplay = "aplay -D plughw:Device output.wav";
static String aplayDing = "aplay -D plughw:Device ding.wav";
static String aplayStart = "aplay -D plughw:Device startRecord.wav";
static String aplayEnd = "aplay -D plughw:Device endRecord.wav";
static String aplayResTip = "aplay -D plughw:Device resTip.wav";
static String aplayNext = "aplay -D plughw:Device next.wav";
static String aplayOpen = "aplay -D plughw:Device open.wav";
static String aplayError = "aplay -D plughw:Device error.wav";
// 在平台申请的APP_ID 详见 http://api.fanyi.baidu.com/api/trans/product/desktop?req=developer
private static final String APP_ID = "这里填自己的百度翻译APPID";
private static final String SECURITY_KEY = "这里填自己的百度翻译KEY";
#APPID和KEY需要到百度翻译开放平台上面去申请,免费的
public static void main(String[] args){
//Tools类是工具类,目前我只写了一个调用Linux命令的工具
Tools tools = new Tools();
String a;
Scanner reader = new Scanner(System.in);
tools.runShell(aplayOpen);
do{
try {
//用来暂停
a = reader.nextLine();
//叮
tools.runShell(aplayDing);
//开始录制
tools.runShell(aplayStart);
System.out.println("开始录制");
//录制..
tools.runShell(arecord);
//录制完毕
tools.runShell(aplayEnd);
System.out.println("录制完毕");
TransApi api = new TransApi(APP_ID, SECURITY_KEY);
Sample sample = new Sample();
//调用语音识别API
String query = sample.change("input.wav");
System.out.println("您说:"+query);
//调用翻译API
JSONObject obj = new JSONObject(api.getTransResult(query, "auto", "en"));
String temp = obj.get("trans_result").toString();
JSONObject res = new JSONObject(temp.substring(1,temp.length()-1));
String resStr = res.get("dst").toString();
System.out.println("翻译结果为:"+resStr);
//调用语音合成API
sample.say(resStr);
tools.runShell(convert);
//翻译结果
tools.runShell(aplayResTip);
//播放结果
tools.runShell(aplay);
try{
Thread.sleep(1000);
}catch (Exception e) {
// TODO: handle exception
}
tools.runShell(aplayNext);
} catch (Exception e) {
tools.runShell(aplayError);
continue;
// TODO: handle exception
}
}while(true);
}
}
speechToLec包
package speechToLec;
/*
时间:2018/06/22
描述:百度语音识别API
版本:1.0
作者:WHU_Sun
邮箱:1455364690@qq.com
*/
import java.io.IOException;
import org.json.JSONObject;
import com.baidu.aip.speech.AipSpeech;
import com.baidu.aip.speech.TtsResponse;
import com.baidu.aip.util.Util;
public class Sample {
//设置APPID/AK/SK
public static final String APP_ID = "这里是百度语音识别API的APPID";
public static final String API_KEY = "这里是百度语音识别API的KEY";
public static final String SECRET_KEY = "这里是百度语音识别API的SECRET_KEY";
#这里是自己去申请的百度语音识别API的相关数据,免费的
public String change(String path) {
// 初始化一个AipSpeech
AipSpeech client = new AipSpeech(APP_ID, API_KEY, SECRET_KEY);
// 可选:设置网络连接参数
client.setConnectionTimeoutInMillis(2000);
client.setSocketTimeoutInMillis(60000);
// 可选:设置代理服务器地址, http和socket二选一,或者均不设置
//client.setHttpProxy("proxy_host", proxy_port); // 设置http代理
//client.setSocketProxy("proxy_host", proxy_port); // 设置socket代理
// 可选:设置log4j日志输出格式,若不设置,则使用默认配置
// 也可以直接通过jvm启动参数设置此环境变量
//System.setProperty("aip.log4j.conf", "path/to/your/log4j.properties");
// 调用接口
//JSONObject res = client.asr("test.pcm", "pcm", 16000, null);
JSONObject res = client.asr(path, "wav", 16000, null);
String temp1 = res.get("result").toString();
String[] temp2 = temp1.split(",");
return temp2[0].substring(2);
}
public void say(String sth){
// 初始化一个AipSpeech
AipSpeech client = new AipSpeech(APP_ID, API_KEY, SECRET_KEY);
// 可选:设置网络连接参数
client.setConnectionTimeoutInMillis(2000);
client.setSocketTimeoutInMillis(60000);
// 可选:设置代理服务器地址, http和socket二选一,或者均不设置
//client.setHttpProxy("proxy_host", proxy_port); // 设置http代理
//client.setSocketProxy("proxy_host", proxy_port); // 设置socket代理
// 可选:设置log4j日志输出格式,若不设置,则使用默认配置
// 也可以直接通过jvm启动参数设置此环境变量
//System.setProperty("aip.log4j.conf", "path/to/your/log4j.properties");
// 调用接口
TtsResponse res = client.synthesis(sth, "zh", 1, null);
byte[] data = res.getData();
JSONObject res1 = res.getResult();
if (data != null) {
try {
Util.writeBytesToFileSystem(data, "output.mp3");
} catch (IOException e) {
e.printStackTrace();
}
}
if (res1 != null) {
//System.out.println(res1.toString(2));
}
}
}
tools包
package tools;
/*
时间:2018/06/22
描述:调用Linux命令的工具类
版本:1.0
作者:WHU_Sun
邮箱:1455364690@qq.com
*/
import java.io.BufferedReader;
import java.io.InputStreamReader;
public class Tools {
public void runShell(String cmd) {
try {
Process ps = Runtime.getRuntime().exec(cmd);
BufferedReader br = new BufferedReader(new InputStreamReader(ps.getInputStream()));
StringBuffer sb = new StringBuffer();
String line;
while ((line = br.readLine()) != null) {
sb.append(line+"\n");
}
String result = sb.toString();
System.out.println(result);
} catch (Exception e) {
e.printStackTrace();
}
}
}
translate包
这个包是百度翻译的API相关类,直接复制就好
package translate;
import java.io.BufferedReader;
import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLEncoder;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Map;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
class HttpGet {
protected static final int SOCKET_TIMEOUT = 10000; // 10S
protected static final String GET = "GET";
public static String get(String host, Map<String, String> params) {
try {
// 设置SSLContext
SSLContext sslcontext = SSLContext.getInstance("TLS");
sslcontext.init(null, new TrustManager[] { myX509TrustManager }, null);
String sendUrl = getUrlWithQueryString(host, params);
// System.out.println("URL:" + sendUrl);
URL uri = new URL(sendUrl); // 创建URL对象
HttpURLConnection conn = (HttpURLConnection) uri.openConnection();
if (conn instanceof HttpsURLConnection) {
((HttpsURLConnection) conn).setSSLSocketFactory(sslcontext.getSocketFactory());
}
conn.setConnectTimeout(SOCKET_TIMEOUT); // 设置相应超时
conn.setRequestMethod(GET);
int statusCode = conn.getResponseCode();
if (statusCode != HttpURLConnection.HTTP_OK) {
System.out.println("Http错误码:" + statusCode);
}
// 读取服务器的数据
InputStream is = conn.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is));
StringBuilder builder = new StringBuilder();
String line = null;
while ((line = br.readLine()) != null) {
builder.append(line);
}
String text = builder.toString();
close(br); // 关闭数据流
close(is); // 关闭数据流
conn.disconnect(); // 断开连接
return text;
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (KeyManagementException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return null;
}
public static String getUrlWithQueryString(String url, Map<String, String> params) {
if (params == null) {
return url;
}
StringBuilder builder = new StringBuilder(url);
if (url.contains("?")) {
builder.append("&");
} else {
builder.append("?");
}
int i = 0;
for (String key : params.keySet()) {
String value = params.get(key);
if (value == null) { // 过滤空的key
continue;
}
if (i != 0) {
builder.append('&');
}
builder.append(key);
builder.append('=');
builder.append(encode(value));
i++;
}
return builder.toString();
}
protected static void close(Closeable closeable) {
if (closeable != null) {
try {
closeable.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 对输入的字符串进行URL编码, 即转换为%20这种形式
*
* @param input 原文
* @return URL编码. 如果编码失败, 则返回原文
*/
public static String encode(String input) {
if (input == null) {
return "";
}
try {
return URLEncoder.encode(input, "utf-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return input;
}
private static TrustManager myX509TrustManager = new X509TrustManager() {
@Override
public X509Certificate[] getAcceptedIssuers() {
return null;
}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
};
}
package translate;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
/**
* MD5编码相关的类
*
* @author wangjingtao
*
*/
public class MD5 {
// 首先初始化一个字符数组,用来存放每个16进制字符
private static final char[] hexDigits = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd',
'e', 'f' };
/**
* 获得一个字符串的MD5值
*
* @param input 输入的字符串
* @return 输入字符串的MD5值
* @throws UnsupportedEncodingException
*
*/
public static String md5(String input) throws UnsupportedEncodingException {
if (input == null)
return null;
try {
// 拿到一个MD5转换器(如果想要SHA1参数换成”SHA1”)
MessageDigest messageDigest = MessageDigest.getInstance("MD5");
// 输入的字符串转换成字节数组
byte[] inputByteArray = input.getBytes("utf-8");
// inputByteArray是输入字符串转换得到的字节数组
messageDigest.update(inputByteArray);
// 转换并返回结果,也是字节数组,包含16个元素
byte[] resultByteArray = messageDigest.digest();
// 字符数组转换成字符串返回
return byteArrayToHex(resultByteArray);
} catch (NoSuchAlgorithmException e) {
return null;
}
}
/**
* 获取文件的MD5值
*
* @param file
* @return
*/
public static String md5(File file) {
try {
if (!file.isFile()) {
System.err.println("文件" + file.getAbsolutePath() + "不存在或者不是文件");
return null;
}
FileInputStream in = new FileInputStream(file);
String result = md5(in);
in.close();
return result;
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
public static String md5(InputStream in) {
try {
MessageDigest messagedigest = MessageDigest.getInstance("MD5");
byte[] buffer = new byte[1024];
int read = 0;
while ((read = in.read(buffer)) != -1) {
messagedigest.update(buffer, 0, read);
}
in.close();
String result = byteArrayToHex(messagedigest.digest());
return result;
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
private static String byteArrayToHex(byte[] byteArray) {
// new一个字符数组,这个就是用来组成结果字符串的(解释一下:一个byte是八位二进制,也就是2位十六进制字符(2的8次方等于16的2次方))
char[] resultCharArray = new char[byteArray.length * 2];
// 遍历字节数组,通过位运算(位运算效率高),转换成字符放到字符数组中去
int index = 0;
for (byte b : byteArray) {
resultCharArray[index++] = hexDigits[b >>> 4 & 0xf];
resultCharArray[index++] = hexDigits[b & 0xf];
}
// 字符数组组合成字符串返回
return new String(resultCharArray);
}
}
package translate;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.Map;
public class TransApi {
private static final String TRANS_API_HOST = "http://api.fanyi.baidu.com/api/trans/vip/translate";
private String appid;
private String securityKey;
public TransApi(String appid, String securityKey) {
this.appid = appid;
this.securityKey = securityKey;
}
public String getTransResult(String query, String from, String to) throws UnsupportedEncodingException {
Map<String, String> params = buildParams(query, from, to);
return HttpGet.get(TRANS_API_HOST, params);
}
private Map<String, String> buildParams(String query, String from, String to) throws UnsupportedEncodingException {
Map<String, String> params = new HashMap<String, String>();
params.put("q", query);
params.put("from", from);
params.put("to", to);
params.put("appid", appid);
// 随机数
String salt = String.valueOf(System.currentTimeMillis());
params.put("salt", salt);
// 签名
String src = appid + query + salt + securityKey; // 加密前的原文
params.put("sign", MD5.md5(src));
return params;
}
}
测试运行
在树莓派上新建一个文件夹,然后把在网上下载的一些音频文件放在里面(wav格式)
我放的是如下的一些文件
(其实可以随便在网上下载一些wav音频文件,随便命名,只要跟Main.java保持一致就好)
#提示音命名为
ding.wav
#开始录制提示音
startRecord.wav
#结束录制提示音
endRecord.wav
#结果提示音
resTip.wav
#继续翻译提示音
next.wav
#开机提示音
open.wav
#错误提示音
error.wav
然后在Eclipse导出项目为Translator.jar文件,也放在上面那个文件夹里面。
打开Linux终端,输入以下命令
sudo java -jar Translator.jar
即可测试运行
************编辑于2018/06/22**************************