スティックコンピュータにミニディスプレイをつけた

ミニディスプレイをつけた。解像度は、高くても対応できるのだけれど字が小さくて見えなくなるので、800X600にしている。キーボードとマウスは、その両者ついているbluetoothのミニパッドがつながる。

あとは、sshを有効にして、パソコンからコントールできる。

Macのmariadbが動かなくなって

デスクトップMacのmariadbが

Can't read dir of '/usr/local/etc/my.cnf.d'

というエラーを吐いて動かなくなった。確かにそんなフォルダはない。え〜、突然!と思ったが、その前にtelnetをインストールするのにbrewをいじっていた。多分、

$ brew prune

コマンドなんかをやったからのように思うが、よくわからない。結局,

$ brew install mariadb

とやっても、すでに入っていますよと言われ、

$ brew update mariadb

で、バージョンアップしたら、治ってくれた。バージョンが最新だったらどうなったんだろうと思う。

JDeeplearningのプログラム概要(1)

https://github.com/toyowa/jautoencoder
で公開しているプログラムの覚書を書いておこうと思う。これから、ロボットのセンサーを増やす作業に入るので、どういうプログラムだったか忘れてしまいそうなので。この(1)では、全体的なことを書いておく。
1。データに関して
データのフォーマットは、
https://github.com/huangzehao/SimpleNeuralNetwork
のサイトのプログラムを参考にさせてもらった。(プログラムは、独立に私が自分で書いたものだ。プログラムは参考にするほど理解できなかったが、自分で書かないと使えないだろうと思ったから)
データの最初の1行は、
topology: 784 400 10
のようにネットワーク構造を書く。ここで、784が入力レイヤーのニューロン数。以下出力ユニットまで、にゅう論数を書いていく。レイヤーの数にもニューロン数にも制限はない。
その後に続いて、in:とout:の接頭語に続いて、交互に、入力データと出力データを空白を区切って書いていく。
MNISTの手書き数字データに関しては、データの読み取りは、
http://nonbiri-tereka.hatenablog.com/entry/2014/09/18/100439
を参考にさせていただいて、それを上記のデータフォーマットに書き直したものだ。
https://github.com/toyowa/jautoencoder/tree/master/MNIST
に変換用のプログラムをおいてある。
データプログラムは、実行時オプション -dataで指定できる。
2。ウェイトファイル
学習後にウェイトデータを吐き出し、テストの時はそのファイルを実行時オプション -weights で指定して読み込むことになる。ただし、ウェイトファイル名は、wgtのさフィックスがないと拒否するようにしている。データと間違わないように。データのさフィックスは特にチェックせずに読み込む。
吐き出されたウェイトファイル名には、そのファイルが作成された日時が秒までつくので、識別できると思う。ウェイトファイルの頭の部分に、その学習のネットワーク構造やパラメータや、繰り返し数などが書かれている。
-------------------
File name: [ weight_170603192205.wgt ]
Iteration: 179997
InputData: trainingData.txt
Topology: 784 300 150 10
Adjusting: 0.15
Label: Pre_neuron No. ->  Layer No. : Neuron No. = Weight
Weight: 0 -> 1 : 0 =    0.0003876341
Weight: 1 -> 1 : 0 =    0.0011203298
Weight: 2 -> 1 : 0 =    0.0004425993
Weight: 3 -> 1 : 0 =    0.0007930749
...........
...........
---------------------------------
なお、テストじゃない時にウェイトファイルを指定すると、そのウェイトを読み込んで、そこに書かれているそれまでの学習の実行の続きをやることになる。何回かに分けてやりたいとか、中間状態を見たい時には、そのような方法もできる。プログラムのデバッグの時は、一回だけ学習させるということもやった。実行時ぷションの、-maxiterで1を指定すると一回だけやる。
なお、-maxiterを指定しないと、データがある限り学習を続ける。
3。テスト
テストの時は、できたウェイトファイルとテスト用データの両方を実行時オプションで指定して、-test オプションを付け加えれば良い。当たり前だが、ウェイトファイルとテスト用データは、ネットワーク構造に関して整合的でなければならない。-weightsでウェイトを指定しないと、当然だが、ランダムに与えたウェイトでテストしてしまうので、テストは無意味だ。チェックして排除するようにすべきだったかもしれない。テスト結果はコンソールに出力される。
4。オートエンコーダー(Autoencoder):深層学習
実行時オプションで -auto をつけると、オートエンコーダーとバックプロパゲーションで学習する。オートエンコーダーとは、隠れ層へのウェイトを1層ずつ、一つの圧縮符号化器のように作っていくことだ。例えば、MNISTで、入力784ニューロン、300と150の隠れ層、出力層が10ニューロンだとしよう。それぞれのレイヤー(層)にA,B,C,Dという名前をつけよう。
まず、ABにA'というAと同じニューロン数のレイヤーをつけて、A'の学習データとして、Aと同じものを使うという作業をする。そして、ウェイトをバックプロパゲーションで形成するという作業をする。一見無意味なようだが、よく考えてみると重要な意味を持っている。AがA'で再現されるんだが、BはAよりもニューロン数が少ない。それが再現されるということは、Bの出力は、Aから入力されるデータの特色をすでになんらかの形で組み込んでいるということである。つまり、ニュー力データだけで、すでに学習してしまっているのである。これでAからBへのウェイトをまず作る。次にそこで最終的に得た出力を利用して、BCB'というネットワークを作成し、その先のネットワーク出力で、自己学習させるのである。Bの入力と、学習用のB'のデータはまた同じである。もちろんニューロン数も同じである。そうすると、BC間のウェイトがまたCで特徴が凝縮されるように形成される。最終的にこのようにできたAB間のウェイトBC間のウェイトを使って、元のABCDのネットワークを元々の学習用出力で学習させる。この時,CD間のウェイトは、私の場合、ランダムに作成したものにしているが、正解かどうかの確信はない。でも多分正しいだろう。これが、オートエンコーダーである。
少なくとも、MNISTのデータについては、うまく機能している。
-autoを指定すると、元のネットワークトポロジーが、5層でもそれ以上でも、同じようにやってくれる。試してないが、はずである。

C++とJAVA

本当に、JAVAの速さに驚いた動揺は未だに冷めない。784X300X150X10のネットワークをサクサクと学習する。ウェイトが、784X300+300X150+150*10=281700個ある。この順方向の計算と誤差逆伝搬によるウェイトの修正を一つのデータに関して行うのにかかる時間は1.67ミリ秒なのである。つまり、60000個のデータを約100秒で修正し終えるので、1サイクル28万個のウェイトを使った一個のデータの計算と修正には、0.00167秒、つまり、1.67ミリ秒しかかからないのである。
本来、JAVAに乗り換えたのは、スレッドの並列計算を導入しやすくする目的だったが、今の所、必要ないので1スレッドで動かしている。いずれ、使用スレッドを指定できるようにするつもりだ。
それはそうと、書いておきたかったのは、JAVAにして改めて、C++の違いに「えっ!」と気づいたことがあったからだ。C++とJAVAは、とてもよく似ていて、書き換えはスムーズにできる。今回、やってみて、クラスを配列に入れた時の振る舞いが違う。
例えば、仮にNeuronというクラスを作成して、この複数のインスタンス(オブジェクトというべきか)を配列neuronsに入れるとしよう。
C++の場合は、vectorを使って、インスタンス neuronを
vector<Neuron> neurons;
neuron.push_back(neuron);
としてvector配列に入れる。
この配列から、
Neuron neuron = neurons.at(1);
という感じで取り出して、このneuronの何かのメンバー変数の値を変更したら、
neuron.at(1) = neuron;
として戻してやらなければ、配列の中に保持されているインスタンスのメンバー変数の値は変わらない。
しかし、JAVAはクラス配列のインスタンスを取り出して、その値を変更すると、元の配列の中のインスタンスそのものの値も変更されているのだ。
だから、埋め戻しが不要だ。
C++愛好家とJAVA愛好家との間では、参照渡しなのか値渡しなのかがよく議論になるが、それと同じようなことだ。もちろん、JAVAのような仕様の方が助かる。なんだから、時間ロスも少ないような気がする。

DC-DCコンバーターをセットしたが

センサーのでんげんが3.3Vなので、確かに、RaspberrPiからも取れるのだが、せっかく5V4アンペアの別電源を用意したのだから、そこから3.3Vを取り出そうと思って、DC-DCコンバーターM78AR033-0.5(380円)も買ってきた。取り付けたのだが、なんだか、ジャイロ用の電源が取り出せない。おかしいと思って、テスターで調べたら、なんと、極性が逆だった。元の、5Vから逆だったのだ。逆だったら、逆向きで降圧していた。ちょっと笑っちゃうね。
真ん中がデバイスで、裏側の配線で右の二つのピンに出しているが、プラスとマイナスが逆だった。
(その後、極性を正しく直した。ピンの左がGRNDで、みぎが3.3V。2017年4月22日)

RaspberryPiでサーボモーターを動かす

アキバで、サーボモーター(回転型)GWSS35STDを2機買ってきた。サーボモーターをチャンネルで動かすためのドライバボードPCA9685も買う。
モーターを動かすための別電源を用意した方がいいので、先日、RspberryPiの上に乗せるボードに5ボルト入力コネクタをつけれたので、これもまた先日買ってきた5V4アンペアの電源ユニットをつないで、そこから配線することにした。
写真の右端にコネクタはつけてそこからドライバボードにつなげている。繋げると、LEDの青が光る。
ボードはこちらからみて、右側にRspberryPiのピンからの入力を入れる。そこは半田付けをコレクタに、ピンを刺している。
ボードの上側に、サーボからの3線入力をさす。ここでは、0チャンネルと1チャンネルの二つを使って2台を制御する。全部で16台繋げるが、最初の4つのコネクタしか半田付けしなかった。
電源は、付いたものを半田付けしたが、ネジで差し込んだ線を固く止めている。LEDの左側が電源だ。
配線とプログラムは、
http://qiita.com/shigeru-yokochi/items/ac2138feb74a7ef7ffc6
を参照させていただいた。ほとんどそのまま使えます。RaspberryPi2のピン配置だが、ここでつなぐピンに関しては、RaspberryPi3と同じなので。
それだけです。
ツイッターに動画を上げています。


 

RaspberryPi 3からキャラクタディスプレイ(LCD)を制御する

RaspberryPi 3のGPIOピンをうまく使ってやろうと思った。いずれ、自分でロボットを作りたいという気持ちもあるので。さしあたっての試みだ。
LCDは、秋月電子で500円で売っていたACM0802C-NLW-BBH、8文字2行のちっちゃいやつだ。
最終的にうまくいったので、記録しておく。
(1)ハード作り
R7に付いている100オームの抵抗をつける。R8は何もしない。R9は、抵抗の切った線を使って直結する。抵抗は小さくつけたが、もう少し脚長でつけてもよかった。
右側に、ピンがさせるようにコネクタをつける。この写真では裏側になって半田付けしか見えないが。
10Kの半固定ボリュームも必需品だ(別売、秋月電子で買っておく)。つけないと表示されない。足三本が、三角に配置されたものを買った。足は三角の右から1,2,3となっている。半固定ボリュームのピン1をLCDのピンの1,2をピン3、3をピンの2に配置する感じになるはず(自分で確認すること)。足が短かったので、抵抗の線をつぎはぎして半田付けした。ショートに気をつける。

(2)結線
ピンを節約するために、4ビットモードにする。文字は、1バイトで作られているので、データを4ビットずつ2度送らないという面倒さはあるが、コンピュータ側のピンの節約の方が大事である。
結線は複雑にしない方が良いので、電源関係以外は、LCDのピン番号を、RaspberryPiのgpio番号に対応させるというポリシーにする。
電源:
LCD1→RSP2 (5.5V)
LCD2→RSP6 (GRD)
LCD3→半固定ボリューム2
制御:
LCD4→GPIO4 (RS) データ、コマンドの切り替え
LCD5→GPIO5 (RWは使わないので、GRD:どれでも良いがピン25にした)
LCD6→GPIO6 (E)
LCD11→GPIO11 (DATA)
LCD12→GPIO12 (DATA)
LCD13→GPIO13 (DATA)
LCD14→GPIO14 (DATA)
これで結線終わり。バックライトが光るはずだ。
(3)ソフトウェア
まず、以下のページを参考にして、WiringPiを使えるようにする。
https://tool-lab.com/2013/12/raspi-gpio-controlling-command-2/
このCライブラリを使わせていただき制御する。
基本的に、WiringPiを初期化し、LCDを初期化し、書き込む。ただし、パルスを送るタイミングが複雑で微妙なのだ。そこで、
http://amahime.main.jp/lcd/main.php?name=lcd
にPICのCでACM0802C-NLW-BBHを制御したプログラムが記載されているので、それを参考にさせていただきながらRaspberryPi用に書き換える。
まず、ライブラリ的な、liblcd.c  である。
/*
キャラクタディスプレイACM0802C-NLW-BBHをRaspberryPi 3 から制御する
liblcd.c
2017年4月19日
*/
#include <wiringPi.h>
#include <stdio.h>
//LCD関数プロトタイピング
void lcd_init(void);
void lcd_str(char* str);
void lcd_data(char data, int cmd);
void lcd_out(char code, int flag);
// LCD 初期化
void lcd_init(void){
//
lcd_out(0x00, 1); // Displayをクリアしたい
//
delay(30);  // 30ms
lcd_out(0x02, 1); // Function set
lcd_out(0x02, 1);
lcd_out(0x08, 1);
delayMicroseconds(39);// 関数作成する必要あり
lcd_out(0x00, 1); // Display ON/OFF
lcd_out(0x0C, 1); //
delayMicroseconds(39);
lcd_out(0x00, 1); // Display Clear
lcd_out(0x01, 1); //
delayMicroseconds(1530);
lcd_out(0x00, 1); // Entry Mode Set
lcd_out(0x06, 1); //
}
// 文字列出力
void lcd_str(char *str){
while(*str != 0x00){
lcd_data(*str,0); //文字出力
str++;
}
}
// dataは送信内容 cmd は[0] ならデータ、[1]ならコマンド
void lcd_data(char data, int cmd){
//printf("lcd_data 1 [ %c ] の書き込み開始 \n",data);
//printf("lcd_data 2 上位ビットの書き込み開始 \n");
lcd_out(data>>4,cmd);// 上位4ビット出力
//printf("lcd_data 3 下位ビットの書き込み開始 \n");
lcd_out(data, cmd);// 下位4ビット出力
if(cmd == 1){
if((cmd & 0x03) != 0) delayMicroseconds(50);
}else{
delayMicroseconds(50);
}
}
// ビットチェンジ及び信号制御
void lcd_out(char code, int flag){
int gpio11 = (code & 0x01);
int gpio12 = (code & 0x02);
int gpio13 = (code & 0x04);
int gpio14 = (code & 0x08);
//printf("lcd_out 1 [ %c ] の書き込み開始 \n",code);
digitalWrite(11, gpio11);
digitalWrite(12, gpio12);
digitalWrite(13, gpio13);
digitalWrite(14, gpio14);
//printf("lcd_out 2 データORコマンド\n");
if(flag == 0){
digitalWrite(4, 1);// データ
}else{
digitalWrite(4, 0);// コマンド
}
//printf("lcd_out 3 書き込み\n");
delayMicroseconds(50);
digitalWrite(6, 1);
//nanodelay(220); // 0 でいい
digitalWrite(6, 0);
//printf("lcd_out 4 書き込み終了\n");
}
次にメインモジュールである。
/*
キャラクタディスプレイACM0802C-NLW-BBHをRaspberryPi 3 から制御する
lcdtest.c
2017年4月19日
*/
#include <stdio.h>
#include <wiringPi.h>
#include <string.h>
char sendChar[] = "My n";
//char sendChar0[] = "LCD TEST";
//char sendChar1[] = "WASSIISG";
void lcd_init(void);
void lcd_str(char* str);
void lcd_data(char data, int cmd);
void lcd_out(char code, int flag);
int main(int argc, char** argv){
if(argc <= 1){
printf("表示すべき文字が指定されていません!\n");
return 1;
}
strcpy(sendChar, argv[1]);
printf("gpioを初期化します\n");
// Initialize WiringPi
if(wiringPiSetupGpio() == -1) return 1;
printf("ポートを出力モードにします\n");
// Set GPIO pin 4-14to output mode
pinMode(4, OUTPUT);
pinMode(5, OUTPUT);
pinMode(6, OUTPUT);
pinMode(11, OUTPUT);
pinMode(12, OUTPUT);
pinMode(13, OUTPUT);
pinMode(14, OUTPUT);
printf("少し待ちます\n");
delayMicroseconds(250);
printf("lcdを初期化します\n");
lcd_init();
printf("データを書き込みます\n");
int len = strlen(sendChar);
int i;
if(len > 8){
// 8文字より長い場合は
// 2行にまたがって記述する
for(i=0;i<8;i++){
lcd_data(0x80+i,1);
lcd_data(sendChar[i],0);
}
for(i=8;i<len;i++){
lcd_data(0xc0+i-8,1);
lcd_data(sendChar[i],0);
}
for(i=len;i<16;i++){
lcd_data(0xc0+i-8,1);
lcd_data(0x20,0);
}
}else{
for(i=0;i<len;i++){
lcd_data(0x80+i,1);
lcd_data(sendChar[i],0);
}
for(i=len;i<8;i++){
lcd_data(0x80+i,1);
lcd_data(0x20,0);
}
for(i=0;i<8;i++){
lcd_data(0xc0+i,1);
lcd_data(0x20,0);
}
}
printf("表示を終了しました\n");
//while(1){
//}
return 0;
}
色々苦労したが、最も苦労したのは、タイミングの管理である。なの秒単位の管理があるのだが、cのnanosleepだったかを使ったら、ものすごく時間がかかる表示になった。
delayMicroseconds
を使って治った。即表示するようになった。1μs以下のタイミングがあるのだが。0タイミングにした。

Docomo API 音声出力結果Jsonファイルのパース

Docomo APIの音声出力は、Jsonファイルとして返される。それをJavaで構文解析をする。使わせていただいたのは、以下のサイトにあるパーサー。
https://www.tutorialspoint.com/json/json_java_example.htm
要領とサンプルを掲載しておく。
(1)サイトから、ソースをダウンロードし、ライブラリ用のjarファイルを作成する。JsonSimple.jar
(2)以下のソースのライブラリに加える。jsonと配列の処理が頭の中でごちゃごちゃになる。(なお、上記サイトのデコードサンプルは、そのままではエラーになる。なんでそんなものを?)

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import org.json.simple.JSONObject;
import org.json.simple.JSONArray;
import org.json.simple.parser.ParseException;
import org.json.simple.parser.JSONParser;
public class JsonParserTest {

    String readJsonFile() {
        // 出力結果をファイルから読み込む場合
        // 通常は、httpレスポンスをそのまま、次のparseJasonで解析すれば良い
        String json = "";
        try {
            File file = new File("/path/to/docomo_output.json");
            BufferedReader br = new BufferedReader(new FileReader(file));
            String str;
            while ((str = br.readLine()) != null) {
                json += str;
            }
            br.close();
        } catch (FileNotFoundException e) {
            System.out.println(e);
        } catch (IOException e) {
            System.out.println(e);
        }
        return json;
    }

    void parseJson(String json) {
        JSONParser parser = new JSONParser();
        try {
            JSONObject obj0  = (JSONObject
            )parser.parse(json);
            //認識テキストの出力
            //出力テキストの全体は、textタグを読み取れば良い
            System.out.println("出力テキスト");
            System.out.println(obj0.get("text"));
            // 以上で良いのだが、分かち書きされた分析結果も受け取るようにして見る
            // resultの値は、配列になっているので、まずその配列を受け取る
            JSONArray results_array = (JSONArray) obj0.get("results");
            // 配列の最初の要素を取り出す
            JSONObject obj1_tokens  = (JSONObject
            )results_array.get(0);
            // 配列の最初の要素が"tokens"というJsonになっているので、それをうけとる
            // そのtokensの値が配列になっているので、配列として受け取る
            JSONArray array1_tokens = (JSONArray) (obj1_tokens.get("tokens"));
            // tokensの配列をループにして回す
            for (Object ob : array1_tokens) {
                JSONObject job = (JSONObject) ob;
                String written = (String) job.get("written");
                System.out.print("書き方:" + written + ",");
                double confidence = (double) job.get("confidence");
                System.out.print("信頼性:" + confidence + ",");
                String spoken = (String) job.get("spoken");
                System.out.println("読み:" + spoken);
                // 他の要素は省略
            }
        } catch (ParseException pe) {
            System.out.println("position: " + pe.getPosition());
            System.out.println(pe);
        }
    }

    public static void main(String[] args) {
        JsonParserTest jparser = new JsonParserTest();
        jparser.parseJson(jparser.readJsonFile());
    }
}