使用树莓派实现翻译机

使用树莓派实现翻译机

只用一根网线实现树莓派和PC的链接

硬件:

  1. 树莓派套件(树莓派3B、16G内存卡、读卡器、充电器)
  2. 一台可以上网PC(我的操作系统版本为Windows10 1709)
  3. 一个USB耳机(有麦)
  4. 一根网线

软件

  1. 树莓派操作系统(我使用的是Raspbian)
  2. Putty(安装在Windows端,用来设置树莓派端)
  3. VNCViewer(安装在Windows端,用来链接树莓派端)
  4. Win32DiskImager(安装在Windows端,用来在内存卡上制作“启动盘”)

硬件和软件准备好了,我们现在正式开始翻译机的制作

安装树莓派系统

  1. 下载操作系统并解压缩
    版本信息如下
    Version:April 2018
    Release date:2018-04-18
    Kernel version:4.14
    下载地址:
    https://downloads.raspberrypi.org/raspbian_latest
    下载下来的是一个压缩包,解压缩以后是img格式的镜像文件

使用树莓派实现翻译机_第1张图片
使用树莓派实现翻译机_第2张图片
2. 制作“启动盘”
①下载Win32DiskImager并安装,可以百度,版本无所谓;
下载地址https://sourceforge.net/projects/win32diskimager/
②双击运行Win32DiskImager,设置如下;
使用树莓派实现翻译机_第3张图片
校验值选“无”,不勾选仅读取分配分区
写入完成后,一般会弹出一个让你格式化的弹框,直接叉掉就好

然后把把内存卡插到树莓派上就好
【注意!!!不是用读卡器把内存卡插到树莓派上,而是插到树莓派上面的内存卡插槽里】

OK,到这边,树莓派系统已经算是安装好了(只用插好内存卡就可以了,不需要其他操作)。

连接树莓派和电脑
①在Windows上安装Putty和VNCViewer
百度安装即可,没什么好说的;
②给树莓派通电,并用网线连接树莓派和电脑
③在Windows上设置网络为共享;如图所示
使用树莓派实现翻译机_第4张图片
使用树莓派实现翻译机_第5张图片
④Window+r打开cmd命令行,arp -a命令,获取树莓派的ip地址
在192.168.137.1栏中,192.168.137.255上面的ip地址即为树莓派的ip地址,如图(每次电脑重新开机后,这个ip地址都会变):
使用树莓派实现翻译机_第6张图片
使用树莓派实现翻译机_第7张图片
⑤打开Putty软件,填上树莓派的ip地址,点击open
使用树莓派实现翻译机_第8张图片

会弹出这样的对话框,选择否。
使用树莓派实现翻译机_第9张图片
然后登录即可,默认登录账户是:pi,密码是raspberry
使用树莓派实现翻译机_第10张图片
这样已经实现了树莓派和电脑相连,可以操作树莓派系统了(Linux)。

但是现在只能通过命令行操作,如果想要图形界面的话还需要进行一些配置。

图形化界面配置

  1. 安装tightvncserver服务
sudo apt-get update
sudo apt-get installtightvncserver
  1. 安装xrdp服务
sudo apt-get install xrdp
  1. 给树莓派安装服务包
sudo apt-get install vnc4server tightvncserver
  1. 启动tightserver服务
tightserver
#这个命令会提示你设置密码,确认密码等,一定要记住密码!

理论上现在应该可以使用Windows自带的远程桌面进行连接了,但是我的电脑好像连接不了(截止至2018.06.22还未解决),所以我又借助了其他工具进行连接。就是我们刚刚安装的VNCViewer软件,双击运行即可。【注意!!这里的地址是树莓派的ip地址加上”:1”】,然后点connect。
使用树莓派实现翻译机_第11张图片

输入刚刚设置的密码,即可连接
使用树莓派实现翻译机_第12张图片

这就是树莓派的图形化界面啦,本来是有壁纸的,后来被我搞丢了。
使用树莓派实现翻译机_第13张图片

到这里,树莓派和电脑的连接就完成啦!

下面,开始编程。

翻译机编程实现

编程主要使用Java和一些shell命令(很少,只有几句)。我们先来捋一捋,本程序要主要的模块

  1. 录音
  2. 把录下来的音频文件转换成文字
  3. 把文字从中文转换成英文
  4. 把文字转换成音频文件
  5. 播放音频文件

我们分别从这几个模块分析:

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
使用树莓派实现翻译机_第14张图片

这是百度语音识别API的开发文档
http://ai.baidu.com/docs#/ASR-Online-Java-SDK/top
使用树莓派实现翻译机_第15张图片
3.中文翻译成英文
语音识别功能我们使用的是百度的语音识别API,所以,翻译功能我们也使用百度的翻译API吧,使用方法跟语音识别API差不多,在百度AI平台上面也可以找到demo和开发文档,这里就不在赘述,非常简单。
4.文字转换成音频文件
这个功能跟语音识别API集成在一起了,所以在配置语音识别API的时候,这个也配置好了,技术文档也很简单。
5.播放音频文件
这个功能也很简单,跟录音功能类似,也是只要一条Linux里的命令就可以实现

aplay -D plughw:Device output.wav

接下来是具体实现(由于时间比较急,所以虽然功能实现了,但是代码的质量其实很差)

使用Eclipse编程,项目结构如下:

使用树莓派实现翻译机_第16张图片

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
邮箱:[email protected]
*/
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
邮箱:[email protected]
*/
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
邮箱:[email protected]
*/
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 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 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**************************

你可能感兴趣的:(学习笔记)