FC2カウンター FPGAの部屋 Zybot
FC2ブログ

FPGAやCPLDの話題やFPGA用のツールの話題などです。 マニアックです。 日記も書きます。

FPGAの部屋

FPGAの部屋の有用と思われるコンテンツのまとめサイトを作りました。Xilinx ISEの初心者の方には、FPGAリテラシーおよびチュートリアルのページをお勧めいたします。

ZYBOtを家のコースで走らせてみた

ZYBOt の白線間走行用CNNを含んだVivado 2018.2 プロジェクト2”でビットファイルが生成できた。
生成されたビットファイルを”白線追従用CNNを使用したZYBOtの白線追従走行1(準備編)”を参考にして、fpga-bit-to-bin.py を使用して、ZYBO_0_wrapper.bit を ZYBO_0_wrapper.bin に変換した。
python fpga-bit-to-bin.py --flip ZYBO_0_wrapper.bit ZYBO_0_wrapper.bin
ZYBOt_Keras_69_181008

ZYBOt のZYBO にFTP して書き換えた ZYBO_0_wrapper.bin をアップロードした。

sudo cp ZYBO_0_wrapper.bin /lib/firmware/
で、ZYBO_0_wrapper.bin を /lib/firmware/ ディレクトリにコピペした。

まずは、ホーム・ディレクトリから cd zybot/wl_tracing_cnn を実行して、wl_tracing_cnn ディレクトリに移動した。

参照ブログ”白線追従用CNNを使用したZYBOtの白線追従走行2(準備編2)
sudo dtbocfg.rb -i --dts devicetree.dts wl_tracing_cnn
を実行し、デバイスツリーをロードして、ビットファイルのダウンロード、クロックの設定も行った。
次に、/dev/uio* と /dev/udmabuf0 をすべての人がR/W できるように設定した。
sudo chmod 666 /dev/uio*
sudo chmod 666 /dev/udmabuf0

これで、すべてのユーザーが書き込みできるので、ユーザーモードで wl_tracing_cnn を起動できる。
cd build
./wl_tracing_cnn

これで ZYBOt が走り出した。コースを走らせてみた。
1つ目はバックグランド音楽を付けてみたので、音量注意。




それなりにコースを走れてはいるが、コースを外れてしまう。学習が良くなかったかもしれない?手前から曲がるように学習させてしまったかもしれない?
1回カーブを曲がるようなときはごまかしが効くが、複雑なコースを走るときにはごまかしが効かない。ごまかしというのは
私が操縦してラジコンのように走れせて、それをZYBOt に学習させたほうが良いのではないか?と思う。
  1. 2018年10月09日 05:29 |
  2. Zybot
  3. | トラックバック:0
  4. | コメント:0

ZYBOt の白線間走行用CNNを含んだVivado 2018.2 プロジェクト2

ZYBOt の白線間走行用CNNを含んだVivado 2018.2 プロジェクト1”の続き。

前回は、白線間走行用CNNを course_conv_nn2_axi3 に変更してVivado プロジェクトを論理合成、インプリメンテーションしたが、タイミングエラーになってしまった。今回は、Vivado HLS に戻ってIP を再合成して、タイミングエラーを解消しよう。

Vivado HLS 2018.2 の course_conv_nn2_axi3_k に戻って、Solution Settings -> Synthesis の Uncertainty を 8 ns まで、 1 ns 刻みに変更していったが、Export RTL の CP achieved post-implementation は 9.847 ns で変わらなかった。つまり回路が変わらなかったようだ。下にUncertainty が 6 ns のときの結果を示す。
ZYBOt_Keras_57_181006
ZYBOt_Keras_58_181006

Vivado HLS 2017.4 でも同様にSolution Settings -> Synthesis の Uncertainty を 2 ns から 8 ns まで、 1 ns 刻みに変更していったが、Export RTL の CP achieved post-implementation は 8.221 ns で変化がなかった。Uncertainty がデフォルト値の下に示す IP をVivado で論理合成、インプリメンテーションしてみたが、やはりタイミングエラーだった。
ZYBOt_Keras_60_181006
ZYBOt_Keras_61_181006

ZYBOt_Keras_62_181006

どうやら、このC ソースコードでは、Uncertainty を変更しても回路に変更が無いようだ。こんな時に状況を打開できるのは指示子だと思う。積極的に指示子を入れていって回路を変更してみよう。とりあえずは影響の少なそうなところにPIPELINE 指示子を入れた。
ZYBOt_Keras_63_181008

Uncertainty が 7 ns のときを示す。
ZYBOt_Keras_64_181008
ZYBOt_Keras_65_181008

Latency は約 3.09 ms だが、問題ない。

ZYBOt_Keras_66_181008

Export RTL の CP achieved post-implementation は 7.670 ns なので、良さそう。
この IP をVivado プロジェクトの IP と入れ替えた。
論理合成、インプリメンテーション、ビットストリームの生成を行った。
ZYBOt_Keras_67_181008

Project Summary を示す。
ZYBOt_Keras_68_181008

Timing も問題ない。
  1. 2018年10月08日 06:22 |
  2. Zybot
  3. | トラックバック:0
  4. | コメント:0

ZYBOt の白線間走行用CNNを含んだVivado 2018.2 プロジェクト1

ZYBOt の白線間走行用CNNをVivado HLS 2018.2 でIP化2”で作製した白線間走行用CNN IP を”Kerasで学習した重みとバイアスを使用した白線間走行用CNNのVivado プロジェクト1”の curve_conv_nn2_axis3 _0 を消去して、今回作成した course_conv_nn2_axi3 を Add IP した。

ZYBO_0_182_9_k ディレクトリの ZYBO_0_153 プロジェクトを示す。
ZYBOt_Keras_54_181006

camera_interface モジュールで、curve_conv_nn2_axis3 _0 を消去して、今回作成した course_conv_nn2_axi3 を Add IP した。
ZYBOt_Keras_55_181006

これで、論理合成、インプリメンテーション、ビットストリームの生成を行った。
結果を示す。
ZYBOt_Keras_56_181006

Timing を見ると Worst Negative Slace (WNS) が -0.297 ns で、タイミング・エラーになっていることがわかった。Total Negative Slack (TNS) も -1.752 ns なので、他の所でもタイミングエラーになっているのが分かる。

Open Implemented Design をクリックして、Timing を見ると course_conv_nn2_axis3_0 の内部の Intra-clock Path がエラーになっているのが分かった。
ZYBOt_Keras_53_181006

これは、Vivado HLS での合成結果が 100 MHz で動作しないということなので、Vivado HLS に戻って再合成をしてみようと思う。
  1. 2018年10月07日 11:37 |
  2. Zybot
  3. | トラックバック:0
  4. | コメント:0

ZYBOt の白線間走行用CNNをVivado HLS 2018.2 でIP化2

ZYBOt の白線間走行用CNNをVivado HLS 2018.2 でIP化1”の続き。

前回は、DMA 付きのZYBOt の白線間走行用CNNのC シミュレーションとC コードの合成を行った。今回は、C/RTL 協調シミュレーションとExport RTL を行って、IP 化する。

(2018/10/06 : 修正)画像を増やすスクリプトが間違っていたので、間違いを修正してやり直しました。

まずは、C/RTL 協調シミュレーションを行った。
ZYBOt_Keras_36_181004

Latency は 178925 クロックだった。100 MHz クロックだとすると約 1.79 ms となる。

C/RTL 協調シミュレーションの波形を見てみよう。
まずは全体波形を見てみよう。
ZYBOt_Keras_37_181004

ins(axis) の ins_TREADY は最初のころ1 - 0 を行き来しているようだ。ここを拡大する。
ZYBOt_Keras_38_181004

細かく 1 になっているのが分かる。まあ、ここのスループットを改善するには、リソースが大量に必要だし、処理速度は 1.79 ms と十分なので、問題ない。60 fps でも 16.7 ms の処理時間で十分間に合う。

最後に Export RTL を行った。
ZYBOt_Keras_52_181006

LUT は 786 個、FF が 690 個、DSP は 4 個、BRAM は 15 個だった。
CP achieved post-implementation は 9.847 ns だった。
  1. 2018年10月05日 04:49 |
  2. Zybot
  3. | トラックバック:0
  4. | コメント:0

ZYBOt の白線間走行用CNNをVivado HLS 2018.2 でIP化1

ZYBOt の白線間走行用CNNをVivado HLS 2018.2 で試しに実装した”で、ZYBOt の白線間走行用CNNの精度が検証できたので、今度はIP化を行う。今回はC シミュレーションとC コードの合成を行う。

(2018/10/06 : 修正)画像を増やすスクリプトが間違っていたので、間違いを修正してやり直しました。

まずは、”Kerasで学習した重みとバイアスを使用した白線間走行用CNNをIPにする1”と”Kerasで学習した重みとバイアスを使用した白線間走行用CNNをIPにする2”を参照して、Vivado HLS 2018.2 の course_conv_nn2_axis3_k プロジェクトを作成した。
ZYBOt_Keras_32_181004

C シミュレーションを実行した。
ZYBOt_Keras_48_181006

レポートを示す。

INFO: [SIM 2] *************** CSIM start ***************
INFO: [SIM 4] CSIM will launch GCC as the compiler.
   Compiling ../../../course_conv_nn2_axis3_tb.cpp in debug mode
   Compiling ../../../course_conv_nn2_axis3.cpp in debug mode
   Generating csim.exe
test/straight_test/bmp_file231.bmp
test/straight_test/bmp_file185.bmp
test/straight_test/bmp_file238.bmp
test/straight_test/bmp_file184.bmp
test/straight_test/bmp_file101.bmp
test/straight_test/bmp_file198.bmp
test/straight_test/bmp_file72.bmp
test/straight_test/bmp_file115.bmp
test/straight_test/bmp_file65.bmp
test/straight_test/bmp_file100.bmp
test/straight_test/bmp_file102.bmp
test/straight_test/bmp_file117.bmp
test/straight_test/bmp_file74.bmp
test/straight_test/bmp_file113.bmp
test/straight_test/bmp_file73.bmp
test/straight_test/bmp_file76.bmp
test/straight_test/bmp_file103.bmp
test/straight_test/bmp_file68.bmp
test/straight_test/bmp_file75.bmp
test/straight_test/bmp_file240.bmp
test/straight_test/bmp_file232.bmp
test/straight_test/bmp_file235.bmp
test/straight_test/bmp_file104.bmp
test/straight_test/bmp_file187.bmp
test/straight_test/bmp_file201.bmp
test/straight_test/bmp_file233.bmp
test/straight_test/bmp_file197.bmp
test/straight_test/bmp_file199.bmp
test/straight_test/bmp_file116.bmp
test/straight_test/bmp_file239.bmp
test/straight_test/bmp_file186.bmp
test/straight_test/bmp_file188.bmp
test/straight_test/bmp_file234.bmp
test/straight_test/bmp_file67.bmp
test/straight_test/bmp_file114.bmp
Straight error is 0

test/left_test/bmp_file203.bmp
test/left_test/bmp_file94.bmp
correct data = 0, outs = 1
dot2[0] = -2.724609 dot2[1] = 3.486328 dot2[2] = -3.287109 
test/left_test/bmp_file89.bmp
test/left_test/bmp_file214.bmp
test/left_test/bmp_file84.bmp
test/left_test/bmp_file112.bmp
test/left_test/bmp_file105.bmp
test/left_test/bmp_file96.bmp
correct data = 0, outs = 2
dot2[0] = 1.197266 dot2[1] = -7.605469 dot2[2] = 9.322266 
test/left_test/bmp_file109.bmp
test/left_test/bmp_file97.bmp
test/left_test/bmp_file125.bmp
test/left_test/bmp_file123.bmp
test/left_test/bmp_file78.bmp
test/left_test/bmp_file82.bmp
test/left_test/bmp_file107.bmp
test/left_test/bmp_file85.bmp
test/left_test/bmp_file122.bmp
test/left_test/bmp_file124.bmp
test/left_test/bmp_file237.bmp
test/left_test/bmp_file108.bmp
test/left_test/bmp_file70.bmp
test/left_test/bmp_file95.bmp
test/left_test/bmp_file215.bmp
test/left_test/bmp_file88.bmp
test/left_test/bmp_file87.bmp
test/left_test/bmp_file79.bmp
test/left_test/bmp_file120.bmp
test/left_test/bmp_file111.bmp
test/left_test/bmp_file121.bmp
test/left_test/bmp_file80.bmp
test/left_test/bmp_file99.bmp
test/left_test/bmp_file86.bmp
test/left_test/bmp_file213.bmp
test/left_test/bmp_file118.bmp
test/left_test/bmp_file98.bmp
Left error is 2

test/right_test/bmp_file210.bmp
correct data = 2, outs = 0
dot2[0] = 3.501953 dot2[1] = 3.076172 dot2[2] = -9.761719 
test/right_test/bmp_file93.bmp
test/right_test/bmp_file196.bmp
test/right_test/bmp_file192.bmp
test/right_test/bmp_file208.bmp
test/right_test/bmp_file207.bmp
test/right_test/bmp_file219.bmp
test/right_test/bmp_file204.bmp
test/right_test/bmp_file77.bmp
test/right_test/bmp_file119.bmp
correct data = 2, outs = 1
dot2[0] = -6.904297 dot2[1] = 4.335938 dot2[2] = -1.699219 
test/right_test/bmp_file236.bmp
test/right_test/bmp_file221.bmp
test/right_test/bmp_file229.bmp
test/right_test/bmp_file71.bmp
correct data = 2, outs = 1
dot2[0] = -4.986328 dot2[1] = 2.332031 dot2[2] = 0.953125 
test/right_test/bmp_file205.bmp
test/right_test/bmp_file242.bmp
test/right_test/bmp_file206.bmp
test/right_test/bmp_file225.bmp
test/right_test/bmp_file195.bmp
test/right_test/bmp_file226.bmp
test/right_test/bmp_file228.bmp
test/right_test/bmp_file209.bmp
test/right_test/bmp_file211.bmp
correct data = 2, outs = 1
dot2[0] = -1.001953 dot2[1] = 2.556641 dot2[2] = -2.494141 
test/right_test/bmp_file216.bmp
test/right_test/bmp_file202.bmp
test/right_test/bmp_file217.bmp
test/right_test/bmp_file106.bmp
test/right_test/bmp_file246.bmp
test/right_test/bmp_file193.bmp
test/right_test/bmp_file194.bmp
test/right_test/bmp_file223.bmp
test/right_test/bmp_file230.bmp
test/right_test/bmp_file191.bmp
test/right_test/bmp_file218.bmp
test/right_test/bmp_file241.bmp
Right error is 4

INFO: [SIM 1] CSim done with 0 errors.
INFO: [SIM 3] *************** CSIM finish ***************


2 つ同じファイル名が続いているところがある。どうもこのフォルダのファイルをリストするコードが良くないのかもしれない?
とりあえず、精度を確認してみると、(105 - 6) / 105 * 100 = 94.3 % だった。

C コードの合成を行った。レポートを示す。
ZYBOt_Keras_49_181006
ZYBOt_Keras_50_181006

Estimated は 8.313 ns でLatency は 178833 クロックだった。100 MHz クロックだと、約 1.79 ms となる。
リソース使用量は BRAM_18K が 15 個、DSP48E が 8 個、FF が 970 個、LUT が 2805 個となった。

curve_conv_nn2_axis3.cpp を貼っておく。

// curve_conv_nn2_axis3.cpp
// 2017/09/09 by marsee
// 畳み込み層のカーネル数 2
// AXI4 Stream入力 番号出力
// 2017/09/18 : dot2[3]の出力も追加
// 2017/12/13 : 直線に加えてカーブのデータも使用して学習した
// 2018/10/03 : 家のZYBOtコースでKerasを使用して学習した
//

#include <ap_fixed.h>
#include <hls_stream.h>
#include <ap_axi_sdata.h>

#include "conv1_weight.h"
#include "conv1_bias.h"
#include "af1_weight.h"
#include "af1_bias.h"
#include "af2_weight.h"
#include "af2_bias.h"

#define REDUSED_ROW     45
#define REDUSED_COULMN  60
#define NUM_OF_KERNELS  2
#define COULMN_PIXELS   56
#define ROW_PIXELS      10
#define ALL_PIXELS      560
#define NUM_OF_OUTPUT   3

int max_ap_fixed(ap_fixed<16, 7, AP_TRN_ZERO, AP_SAT> out[NUM_OF_OUTPUT], ap_uint<2> &out_num);

int course_conv_nn2_axis3(hls::stream<ap_axiu<32,1,1,1> >& ins, ap_uint<2> &outs,
        ap_fixed<16, 7, AP_TRN_ZERO, AP_SAT> dot2[NUM_OF_OUTPUT]){
#pragma HLS INTERFACE s_axilite port=dot2
#pragma HLS INTERFACE s_axilite port=return
#pragma HLS INTERFACE s_axilite port=outs
#pragma HLS INTERFACE axis register both port=ins
    ap_ufixed<8, 0, AP_TRN_ZERO, AP_SAT> buf[ROW_PIXELS][COULMN_PIXELS];
    ap_fixed<13, 6, AP_TRN_ZERO, AP_SAT> conv_out[NUM_OF_KERNELS][ROW_PIXELS-4][COULMN_PIXELS-4];
    ap_fixed<13, 6, AP_TRN_ZERO, AP_SAT> pool_out[NUM_OF_KERNELS][(ROW_PIXELS-4)/2][(COULMN_PIXELS-4)/2];
    ap_fixed<16, 7, AP_TRN_ZERO, AP_SAT> dot1[100];
    ap_axiu<32,1,1,1> pix;

    do {
#pragma HLS LOOP_TRIPCOUNT min=1 max=1 avg=1
    // user が 1になった時にフレームがスタートする
        ins >> pix;
    } while(pix.user == 0);

    // 10 x 56 に整形
    buf_copy1: for(int i=0; i<REDUSED_ROW; i++){
        buf_copy2: for(int j=0; j<REDUSED_COULMN; j++){
            if (!(i==0 && j==0))    // 最初の入力はすでに入力されている
                ins >> pix; // AXI4-Stream からの入力

            if((i>=33 && i<33+ROW_PIXELS) && (j>=2 && j<2+COULMN_PIXELS)){
                buf[i-33][j-2] = (ap_ufixed<8, 0, AP_TRN_ZERO, AP_SAT>)((ap_ufixed<16, 8, AP_TRN_ZERO, AP_SAT>)(pix.data & 0xff) / 256);
            }
        }
    }

    // Convolutional Neural Network 5x5 kernel, Stride = 1, Padding = 0
    // + ReLU
    CONV1: for(int i=0; i<NUM_OF_KERNELS; i++){ // カーネルの個数
        CONV2: for(int j=0; j<ROW_PIXELS-4; j++){
            CONV3: for(int k=0; k<COULMN_PIXELS-4; k++){
                conv_out[i][j][k] = 0;
                CONV4: for(int m=0; m<5; m++){
                    CONV5: for(int n=0; n<5; n++){
                        conv_out[i][j][k] += buf[j+m][k+n] * conv1_weight[i][0][m][n];
                    }
                }
                conv_out[i][j][k] += conv1_bias[i];

                if(conv_out[i][j][k]<0) // ReLU
                    conv_out[i][j][k] = 0;
            }
        }
    }

    // Pooling Kernel = 2 x 2, Stride = 2
    POOL1: for(int i=0; i<NUM_OF_KERNELS; i++){
        POOL2: for(int j=0; j<ROW_PIXELS-4; j += 2){
            POOL3: for(int k=0; k<COULMN_PIXELS-4; k += 2){
                POOL4: for(int m=0; m<2; m++){
                    POOL5: for(int n=0; n<2; n++){
                        if(m==0 && n==0){
                            pool_out[i][j/2][k/2] = conv_out[i][j][k];
                        } else if(pool_out[i][j/2][k/2] < conv_out[i][j+m][k+n]){
                            pool_out[i][j/2][k/2] = conv_out[i][j+m][k+n];
                        }
                    }
                }
            }
        }
    }

    af1_dot1: for(int col=0; col<100; col++){
        dot1[col] = 0;
        af1_dot2: for(int i=0; i<NUM_OF_KERNELS; i++){
            af1_dot3: for(int j=0; j<(ROW_PIXELS-4)/2; j++){
                af1_dot4: for(int k=0; k<(COULMN_PIXELS-4)/2; k++){
                    dot1[col] += pool_out[i][j][k]*af1_weight[i*((ROW_PIXELS-4)/2)*((COULMN_PIXELS-4)/2)+j*((COULMN_PIXELS-4)/2)+k][col];
                }
            }
        }
        dot1[col] += af1_bias[col];

        if(dot1[col] < 0)   // ReLU
            dot1[col] = 0;
    }

    af2_dot1: for(int col=0; col<NUM_OF_OUTPUT; col++){
        dot2[col] = 0;
        af2_dot2: for(int row=0; row<100; row++){
            dot2[col] += dot1[row]*af2_weight[row][col];
        }
        dot2[col] += af2_bias[col];
    }

    max_ap_fixed(dot2, outs);

    return(0);
}

int max_ap_fixed(ap_fixed<16, 7, AP_TRN_ZERO, AP_SAT> out[NUM_OF_OUTPUT], ap_uint<2> &out_num){
    int max_id;
    ap_fixed<16, 7, AP_TRN_ZERO, AP_SAT> max;

    for(int i=0; i<NUM_OF_OUTPUT; i++){
        if(i == 0){
            max = out[0];
            max_id = 0;
        }else if(out[i]>max){
            max = out[i];
            max_id = i;
        }
    }
    out_num = (ap_uint<2>)max_id;

    return(0);
}



course_conv_nn2_axis3_tb.cpp を貼っておく。(2018/10/06:変更、バグフィックス)

// course_conv_nn2_axis3_tb.cpp
// 2018/10/03 by marsee
// Keras を使用して学習した
//

#include <iostream>
#include "hls_opencv.h"
#include "ap_axi_sdata.h"
#include "hls_video.h"
#include <dirent.h>

#define MAX_HEIGHT  600
#define MAX_WIDTH   800

typedef hls::stream<ap_axiu<32,1,1,1> > AXI_STREAM;
typedef hls::Mat<MAX_HEIGHT, MAX_WIDTH, HLS_8UC3> RGB_IMAGE;
typedef hls::Mat<MAX_HEIGHT, MAX_WIDTH, HLS_8UC1> GRAY_IMAGE;

using namespace cv;

#define NUM_OF_OUTPUT   3

//#define LOOP_COUNT        35  // test_images
#define LOOP_COUNT    1    // for C/RTL Co-Simulation

//#define STRAIGHT_IMAGE_NAME     "train/straight"
//#define LEFT_IMAGE_NAME    "train/left"
//#define RIGHT_IMAGE_NAME   "train/right"
#define STRAIGHT_IMAGE_NAME     "test/straight_test"
#define LEFT_IMAGE_NAME    "test/left_test"
#define RIGHT_IMAGE_NAME   "test/right_test"

const char CUR_DIR[] = ".";
const char PAR_DIR[] = "..";

int course_conv_nn2_axis3(hls::stream<ap_axiu<32,1,1,1> >& ins, ap_uint<2> &outs,
        ap_fixed<16, 7, AP_TRN_ZERO, AP_SAT> dot2[NUM_OF_OUTPUT]);
int resize_gray(AXI_STREAM& ins, AXI_STREAM& outs);
int main_output_loop(char *buf, int loop_count, int correct_data);

int main () {
    char buf[200];

    sprintf(buf, "%s", STRAIGHT_IMAGE_NAME);
    main_output_loop(buf, LOOP_COUNT, 1);

    sprintf(buf, "%s", LEFT_IMAGE_NAME);
    main_output_loop(buf, LOOP_COUNT, 0);

    sprintf(buf, "%s", RIGHT_IMAGE_NAME);
    main_output_loop(buf, LOOP_COUNT, 2);

    return(0);
}

int main_output_loop(char *buf, int loop_count, int correct_data){
    char bmp_file_name[200];
    ap_uint<2> outs;
    AXI_STREAM src_axi, dst_axi;
    Mat src;
    ap_fixed<16, 7, AP_TRN_ZERO, AP_SAT> dot2[NUM_OF_OUTPUT];
    int err_num = 0;
    DIR *dir;
    struct dirent *dp;

    if((dir=opendir(buf)) == NULL){
        fprintf(stderr, "%s Open Error\n", buf);
        exit(1);
    }

    for(int i=0; i<loop_count;){
        if((dp=readdir(dir)) == NULL){
            fprintf(stderr, "readdir(dir) == NULL\n");
            exit(1);
        }

        if(strcmp(CUR_DIR, dp->d_name)!=0 && strcmp(PAR_DIR, dp->d_name)!=0){
            sprintf(bmp_file_name, "%s/%s", buf, dp->d_name);
        }else{
            continue;
        }
        printf("%s\n", bmp_file_name);

        // OpenCV で 画像を読み込む
        src = imread(bmp_file_name);

        // BGR から RGBへ変換
        Mat src_rgb;
        cvtColor(src, src_rgb, CV_BGR2RGB);

        // Mat フォーマットから AXI4 Stream へ変換
        cvMat2AXIvideo(src_rgb, src_axi);

        // resize_gray() 関数をコール
        resize_gray(src_axi, dst_axi);

        course_conv_nn2_axis3(dst_axi, outs, dot2);

        if((int)outs != correct_data){
            //printf("*%s\n", bmp_file_name);
            printf("correct data = %d, outs = %d\n", correct_data, (int)outs);
            for(int i=0; i<NUM_OF_OUTPUT; i++)
                printf("dot2[%d] = %f ", i, (float)dot2[i]);
            printf("\n");
            err_num++;
        }
        i++;
    }
    if(correct_data == 1)
        printf("Straight error is %d\n\n", err_num);
    else if(correct_data == 0)
        printf("Left error is %d\n\n", err_num);
    else // if(correct_data == 2)
        printf("Right error is %d\n\n", err_num);

    closedir(dir);
    return(0);
}

int resize_gray(AXI_STREAM& ins, AXI_STREAM& outs){

    RGB_IMAGE org_img(600, 800);
    GRAY_IMAGE org_img_g(600, 800);
    GRAY_IMAGE resize_img_g(45, 60);
    RGB_IMAGE resize_img(45, 60);

    hls::AXIvideo2Mat(ins, org_img);
    hls::CvtColor<HLS_RGB2GRAY>(org_img, org_img_g);
    hls::Resize(org_img_g, resize_img_g);
    hls::CvtColor<HLS_GRAY2RGB>(resize_img_g, resize_img);
    hls::Mat2AXIvideo(resize_img, outs);

    return(0);
}

  1. 2018年10月04日 05:33 |
  2. Zybot
  3. | トラックバック:0
  4. | コメント:0

ZYBOt の白線間走行用CNNをVivado HLS 2018.2 で試しに実装した

ZYBOt のコースをKeras で学習した”の続き。

(2018/10/03:変更)
(2018/10/06 : 修正)画像を増やすスクリプトが間違っていたので、間違いを修正してやり直しました。


前回は、今まで作ってきたトレーニング・ファイルやラベル・ファイル、テスト・ファイルやラベル・ファイルを使用して、Keras で学習させ、重みやバイアスをC ヘッダ・ファイルに出力した。今回は、その重みやバイアスのC ヘッダ・ファイルを使用してVivado HLS 2018.2 でプロジェクトを作成して、量子化ビット長を設定し、C シミュレーションとC コードの合成を行う。

まずは、Vivado HLS 2018.2 の course_conv_nn2 プロジェクトを示す。
ZYBOt_Keras_28_180930

course_conv_nn2.cpp を貼っておく。

// course_conv_nn2.cpp
// 2018/09/30 by marsee
// 畳み込み層のカーネル数 2
//

#include <ap_fixed.h>

#include "conv1_weight.h"
#include "conv1_bias.h"
#include "af1_weight.h"
#include "af1_bias.h"
#include "af2_weight.h"
#include "af2_bias.h"

#define NUM_OF_KERNELS 2
#define COULMN_PIXELS 56
#define ROW_PIXELS 10
#define ALL_PIXELS 560
#define NUM_OF_OUTPUT 3

int course_conv_nn(ap_ufixed<8, 0, AP_TRN_ZERO, AP_SAT> in[ALL_PIXELS], ap_fixed<12, 7, AP_TRN_ZERO, AP_SAT> out[NUM_OF_OUTPUT]){
    ap_ufixed<8, 0, AP_TRN_ZERO, AP_SAT> buf[ROW_PIXELS][COULMN_PIXELS];
    ap_fixed<13, 3, AP_TRN_ZERO, AP_SAT> conv_out[NUM_OF_KERNELS][ROW_PIXELS-4][COULMN_PIXELS-4];
    ap_fixed<13, 3, AP_TRN_ZERO, AP_SAT> pool_out[NUM_OF_KERNELS][(ROW_PIXELS-4)/2][(COULMN_PIXELS-4)/2];
    ap_fixed<16, 5, AP_TRN_ZERO, AP_SAT> dot1[100];
    ap_fixed<16, 7, AP_TRN_ZERO, AP_SAT> dot2[NUM_OF_OUTPUT];

    buf_copy1: for(int i=0; i<ROW_PIXELS; i++)
        buf_copy2: for(int j=0; j<COULMN_PIXELS; j++)
            buf[i][j] = in[i*COULMN_PIXELS+j];

    // Convolutional Neural Network 5x5 kernel, Stride = 1, Padding = 0
    // + ReLU
    CONV1: for(int i=0; i<NUM_OF_KERNELS; i++){ // カーネルの個数
        CONV2: for(int j=0; j<ROW_PIXELS-4; j++){
            CONV3: for(int k=0; k<COULMN_PIXELS-4; k++){
                conv_out[i][j][k] = 0;
                CONV4: for(int m=0; m<5; m++){
                    CONV5: for(int n=0; n<5; n++){
                        conv_out[i][j][k] += buf[j+m][k+n] * conv1_weight[i][0][m][n];
                    }
                }
                conv_out[i][j][k] += conv1_bias[i];

                if(conv_out[i][j][k]<0) // ReLU
                    conv_out[i][j][k] = 0;
            }
        }
    }

    // Pooling Kernel = 2 x 2, Stride = 2
    POOL1: for(int i=0; i<NUM_OF_KERNELS; i++){
        POOL2: for(int j=0; j<ROW_PIXELS-4; j += 2){
            POOL3: for(int k=0; k<COULMN_PIXELS-4; k += 2){
                POOL4: for(int m=0; m<2; m++){
                    POOL5: for(int n=0; n<2; n++){
                        if(m==0 && n==0){
                            pool_out[i][j/2][k/2] = conv_out[i][j][k];
                        } else if(pool_out[i][j/2][k/2] < conv_out[i][j+m][k+n]){
                            pool_out[i][j/2][k/2] = conv_out[i][j+m][k+n];
                        }
                    }
                }
            }
        }
    }

    af1_dot1: for(int col=0; col<100; col++){
        dot1[col] = 0;
        af1_dot2: for(int i=0; i<NUM_OF_KERNELS; i++){
            af1_dot3: for(int j=0; j<(ROW_PIXELS-4)/2; j++){
                af1_dot4: for(int k=0; k<(COULMN_PIXELS-4)/2; k++){
                    dot1[col] += pool_out[i][j][k]*af1_weight[i*((ROW_PIXELS-4)/2)*((COULMN_PIXELS-4)/2)+j*((COULMN_PIXELS-4)/2)+k][col];
                }
            }
        }
        dot1[col] += af1_bias[col];

        if(dot1[col] < 0)   // ReLU
            dot1[col] = 0;
    }

    af2_dot1: for(int col=0; col<NUM_OF_OUTPUT; col++){
        dot2[col] = 0;
        af2_dot2: for(int row=0; row<100; row++){
            dot2[col] += dot1[row]*af2_weight[row][col];
        }
        dot2[col] += af2_bias[col];

        out[col] = dot2[col];
    }

    return(0);
}


C シミュレーションを行った。
ZYBOt_Keras_45_181006

ハードウェアのエラーは 98.9 % 、ソフトウェアのエラーは 99.0 % だった。これは 1000 個のデータでの精度なので、他の部分ではもっと精度が悪いことが考えられる。というのも、”ZYBOt のコースをKeras で学習した”では、テスト・データでの精度は、約 92.4 % だからだ。
すべてのログを示す。

INFO: [SIM 2] *************** CSIM start ***************
INFO: [SIM 4] CSIM will launch GCC as the compiler.
   Compiling ../../../course_conv_nn2_tb.cpp in debug mode
   Compiling ../../../course_conv_nn2.cpp in debug mode
   Generating csim.exe
id = 38, max_id_ref = 0, max_id_hw = 2
id = 38, max_id_ref = 0, max_id_sw = 2
id = 45, max_id_ref = 2, max_id_hw = 1
id = 45, max_id_ref = 2, max_id_sw = 1
id = 47, max_id_ref = 0, max_id_hw = 1
id = 47, max_id_ref = 0, max_id_sw = 1
id = 74, max_id_ref = 0, max_id_hw = 1
id = 74, max_id_ref = 0, max_id_sw = 1
id = 114, max_id_ref = 2, max_id_hw = 1
id = 114, max_id_ref = 2, max_id_sw = 1
id = 117, max_id_ref = 0, max_id_hw = 1
id = 117, max_id_ref = 0, max_id_sw = 1
id = 124, max_id_ref = 0, max_id_hw = 2
id = 124, max_id_ref = 0, max_id_sw = 2
id = 133, max_id_ref = 2, max_id_hw = 1
id = 133, max_id_ref = 2, max_id_sw = 1
id = 137, max_id_ref = 2, max_id_hw = 1
id = 137, max_id_ref = 2, max_id_sw = 1
id = 139, max_id_ref = 1, max_id_hw = 2
id = 142, max_id_ref = 2, max_id_hw = 1
id = 142, max_id_ref = 2, max_id_sw = 1

hw_err_cnt = 11, sw_err_cnt = 10
hw accuracy = 98.900002%, sw accuracy = 99.000001%

INFO: [SIM 1] CSim done with 0 errors.
INFO: [SIM 3] *************** CSIM finish ***************



次に C コードの合成を行った。ここでは、あくまで目安の値を確認することを目的としている。実際に使用するIP はDMA を付ける必要があるので、後で実装する。
ZYBOt_Keras_46_181006
ZYBOt_Keras_47_181006

Estimated は 8.25 ns だった。Latency は 171774 クロックで、100 MHz クロックでは、約 1.72 ms となる。
リソース使用量は、BRAM_18K が 13 個、DSP48E が 8 個、FF が 726 個、LUT が 2531 個だった。
  1. 2018年10月02日 05:01 |
  2. Zybot
  3. | トラックバック:0
  4. | コメント:0

ZYBOt のコースをKeras で学習した

ZYBOt のテスト用画像ファイルの作成(テスト・ファイルとラベル・ファイル)”の続き。

今まで作ってきたトレーニング・ファイルやラベル・ファイル、テスト・ファイルやラベル・ファイルを使用して、Keras で学習させた。

(2018/10/06 : 修正)画像を増やすスクリプトが間違っていたので、間違いを修正して学習をやり直しました。

まずは、course_line というディレクトリを作成し、その下にJupyter Notebook のファイルが入る course_nn2_line ディレクトリを作成した。そして、 train_course_run_image と train_course_run_label 、test_course_run_image と test_course_run_label と画像ファイルをロードする「ゼロから作るDeep Learning」のPython ファイルを使用した course_dataset.py が入っている dataset_course ディレクトリを作成した。
ZYBOt_Keras_21_180930

今までと同じネットワークで学習した。
ZYBOt_Keras_40_181006

学習画像での精度は 99.0 % だが、テスト画像での精度は 92.1 % だった。かなり離れてしまっている。

Train on 55350 samples, validate on 55800 samples
Epoch 1/3
55350/55350 [==============================] - 7s 134us/step - loss: 0.4229 - acc: 0.8291 - val_loss: 0.3622 - val_acc: 0.9065
Epoch 2/3
55350/55350 [==============================] - 7s 133us/step - loss: 0.0917 - acc: 0.9723 - val_loss: 0.4185 - val_acc: 0.9184
Epoch 3/3
55350/55350 [==============================] - 7s 135us/step - loss: 0.0459 - acc: 0.9871 - val_loss: 0.4027 - val_acc: 0.9240



グラフを示す。
ZYBOt_Keras_41_181006

モデルのサーマリを示す。

_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
conv2d_1 (Conv2D)            (None, 6, 52, 2)          52        
_________________________________________________________________
activation_1 (Activation)    (None, 6, 52, 2)          0         
_________________________________________________________________
max_pooling2d_1 (MaxPooling2 (None, 3, 26, 2)          0         
_________________________________________________________________
flatten_1 (Flatten)          (None, 156)               0         
_________________________________________________________________
dense_1 (Dense)              (None, 100)               15700     
_________________________________________________________________
activation_2 (Activation)    (None, 100)               0         
_________________________________________________________________
dense_2 (Dense)              (None, 3)                 303       
_________________________________________________________________
activation_3 (Activation)    (None, 3)                 0         
=================================================================
Total params: 16,055
Trainable params: 16,055
Non-trainable params: 0


畳み込み層の統計情報を示す。

conv_layer_bias = [-0.47363704 -0.1307075 ]
np.max(conv_layer_weight) = 0.5713015794754028
np.min(conv_layer_weight) = -0.42728084325790405
np.max(abs_conv_layer_weight) = 0.5713015794754028
np.min(abs_conv_layer_weight) = 0.0053939069621264935
np.max(conv_layer_bias) = -0.1307075023651123
np.min(conv_layer_bias) = -0.47363704442977905
np.max(abs_conv_layer_bias) = 0.47363704442977905
np.min(abs_conv_layer_bias) = 0.1307075023651123
conv_output = (55800, 6, 52, 2)
np.std(conv_output) = 0.8600595593452454
np.max(conv_output) = 3.77016544342041
np.min(conv_output) = -2.09549617767334
np.max(abs_conv) = 3.77016544342041
np.min(abs_conv) = 6.556510925292969e-07


畳み込み層の重みとバイアスのグラフを示す。
ZYBOt_Keras_42_181006

全結合層1層目の統計情報を示す。

np.max(dence_layer1_weight) = 0.4407622218132019
np.min(dence_layer1_weight) = -0.46452072262763977
np.max(abs_dence_layer1_weight) = 0.46452072262763977
np.min(abs_dence_layer1_weight) = 2.4440741981379688e-06
np.max(dence_layer1_bias) = 0.2699412703514099
np.min(dence_layer1_bias) = -0.16888734698295593
np.max(abs_dence_layer1_bias) = 0.2699412703514099
np.min(abs_dence_layer1_bias) = 0.00015306042041629553
dence_layer1_output = (55800, 100)
np.std(dence_layer1_output) = 1.624572515487671
np.max(dence_layer1_output) = 10.883496284484863
np.min(dence_layer1_output) = -11.47065258026123
np.max(abs_dence_layer1_output) = 11.47065258026123
np.min(abs_dence_layer1_output) = 1.2665987014770508e-07


全結合層1層目の重みとバイアスのグラフを示す。
ZYBOt_Keras_43_181006

全結合2層目の統計情報を示す。

dence_layer2_bias = [ 0.04589511 -0.04674032 0.02472589]
np.max(dence_layer2_weight) = 0.995283305644989
np.min(dence_layer2_weight) = -1.0141152143478394
np.max(abs_dence_layer2_weight) = 1.0141152143478394
np.min(abs_dence_layer2_weight) = 0.0014004269614815712
np.max(dence_layer2_bias) = 0.04589511454105377
np.min(dence_layer2_bias) = -0.046740319579839706
np.max(abs_dence_layer2_bias) = 0.046740319579839706
np.min(abs_dence_layer2_bias) = 0.024725893512368202
dence_layer2_output = (55800, 3)
np.std(dence_layer2_output) = 7.085215091705322
np.max(dence_layer2_output) = 31.220308303833008
np.min(dence_layer2_output) = -24.410385131835938
np.max(abs_dence_layer2_output) = 31.220308303833008
np.min(abs_dence_layer2_output) = 1.2490898370742798e-05


全結合2層目の重みとバイアスのグラフを示す。
ZYBOt_Keras_44_181006

各層の重みやバイアスの C ヘッダ・ファイルを出力し、1000個のテスト画像を出力した C ヘッダ・ファイル course_line_data.h を出力した。
ZYBOt_Keras_27_180930
  1. 2018年09月30日 04:46 |
  2. Zybot
  3. | トラックバック:0
  4. | コメント:0
»