ロボット、電子工作、IoT、AIなどの開発記録

開発したい。開発したい。。。

darknetでマルチクラス学習と画像認識

今までtensorflowなどでYoLoV2をしてきましたが、今回は複数の物体判別ができるようにdarknetで多クラスの学習をしようと思います。

参考にしたのは以下の2つのHPです。貴重な情報ありがとうございます。

 

・環境

 ubuntu16.04
 gtx-1070
 python 2.7
 python 3.5
 opencv 3.1

・darknetのインストール

GitHub - pjreddie/darknet: Convolutional Neural Networks
ここからdarknetをダウンロード。Makefileを編集。

 GPU=1
 CUDNN=1
 OPENCV=1
 DEBUG=0
 OPENMP=0
 LIBSO=0
 
GPUOPENCVを1に変更。
そしたら、端末で”make”と打って実行。
 
・自分の場合以下のエラーが出ました。 
gcc -DOPENCV `pkg-config --cflags opencv` -DGPU -I/usr/local/cuda/include/ -DCUDNN -Wall -Wfatal-errors -Ofast -DOPENCV -DGPU -DCUDNN -I/usr/local/cudnn/include -c ./src/gemm.c -o obj/gemm.o
In file included from ./src/gemm.c:3:0:
./src/cuda.h:14:26: fatal error: cuda_runtime.h: そのようなファイルやディレクトリはありません
compilation terminated.
Makefile:89: ターゲット 'obj/gemm.o' のレシピで失敗しました
make: *** [obj/gemm.o] エラー 1
 
解決方法は以下のリンクにあるのですが、MakefileのCOMMONとLDFLAGSのCUDAのパスを書き換えることで解消できました。
CUDA8の人は以下のような書き方になります。

 COMMON+= -DGPU -I/usr/local/cuda/include/
 CFLAGS+= -DGPU
 LDFLAGS+= -L/usr/local/cuda/lib64 -lcuda -lcudart -lcublas -lcurand
  ↓ ↓ ↓
 COMMON+= -DGPU -I/usr/local/cuda-8.0/include/
 CFLAGS+= -DGPU
 LDFLAGS+= -L/usr/local/cuda-8.0/lib64 -lcuda -lcudart -lcublas -lcurand

problem compiling · Issue #9 · cvjena/darknet · GitHub

・学習したい画像の用意

 *画像に写っているものが何なのかを判別したいときは以下の画像で学習可能です。
f:id:weekendproject9:20180310163519p:plain
 しかし、部屋や街の中のシーン中に何が写っているかを判別したいときは以下ようなの画像を用意する必要があります。
f:id:weekendproject9:20180310163705p:plain
 
以下のプログラムはwebカメラで対象物の画像を保存するものになります。
また、ネットで集めてもいいです。

-- capture.py --

# -*- coding: utf-8 -*-
import numpy as np
import cv2

# webカメラの準備
cap = cv2.VideoCapture(0)
i=0
while(True):
	# フレームをキャプチャする
	ret, frame = cap.read()

	# 画面に表示する
	cv2.imshow('frame',frame)

	# キーボード入力待ち
	key = cv2.waitKey(1) & 0xFF

	# qが押された場合は終了する
	if key == ord('q'):
		break
	# sが押された場合は保存する
	if key == ord('s'):
		path = str(i) + ".jpg" 
		cv2.imwrite(path,frame)
		i+=1

# キャプチャの後始末と,ウィンドウをすべて消す
cap.release()
cv2.destroyAllWindows()

「操作方法」
 コマンド 「 python capture.py 」 で起動
 キーボード ”s”で1枚保存。画像名の数字は自動で増える
 キーボード ”q”で終了
 

・対象物にクラス名のラベルをつけていく

GitHub - puzzledqs/BBox-Label-Tool at multi-class
BBoxのブランチmulti-classを利用し、画像中の対象物の範囲に枠をつけ、クラス名のラベルをつけていきます。
 
以下手順  
1. main.pyの134行目と152行目の拡張子をJPGからjpgに変更する。
2. BBoxフォルダ内のclass.txtに自分が認識させたいクラス名を書く。以下例:

   car
   human
   pen
   dog
   cat 
3. BBoxのimagesとlabelsの中にフォルダを002の数値で作ります。(別の数字でもいいです。)
4. imagesに作ったフォルダの中に学習させたい画像を移動させます
5. コマンド 「 python2 main.py 」にてBBox起動 (*python2で起動してください)
6. BBox画面上部のimageDirの項目に学習画像を入れたフォルダ名を書いて”Load”をクリック
7. 右上のプルダウンでクラス名を選択して”ComfirmClass”をクリック
8. 領域を左クリックして囲んで、右に出力される結果で間違いなければ下の”Next”をクリック
  (操作を間違ったらDeleteとかClearAllをする)
9.全画像に領域をつけて終了。(NEXTで次の画像にいかないと終了と判断)
10. labelsフォルダ内の自分が作成したフォルダに画像と同じ名前のファイルがあるか確認

・学習データの水増し(必須ではありません)

何千枚、何万枚もの学習データを自分で用意するのは大変なので、プログラムで画像を増やします。
このとき、画像をぼやかしたりやノイズを与えたりしています。
(長いので一番下にプログラム記載(increase_picture.py))
 
1.BBoxのmain,pyと同じところにincrease_picture.pyを置く
2.ImagesとLabelsフォルダ内に"output"というフォルダを作成
3.コマンド 「 python increase_picture.py Images/002 Labels/002 」 (自分のフォルダ名に合わせる)
4.画像が水増しされており、元画像に加工がされているのを確認

 

・学習用データの変換

BBoxは領域とラベルをつけるだけなので、これをdarknetで学習できる形に変換します。
darknet/convert.py at master · Guanghan/darknet · GitHub
ここのプログラムを元に多クラス用に書き換えました。(長いので一番下にプログラム記載(convert.py))
1.BBoxフォルダ一番上の階層に”convert.py”でプログラムを作ります
2.BBoxフォルダ一番上の階層に"convertLabels"というフォルダを作って中に"train"フォルダを作ります
3.コマンド 「 python convert.py 」 で変換します
4.結果をconvertLabels/train内のテキストを見て、下のように一番左にクラス番号、その後に枠の座標値が割合で書かれているのを確認します

 データの並び:【カテゴリ番号 オブジェクトの中心x座標  オブジェクトの中心y座標  オブジェクトの幅  オブジェクトの高さ】
 出力結果例 : 0      0.59609375      0.16041666666666665   0.3484375   0.2791666666666667
 

・darknetで学習

1.darknetフォルダのdata内に”obj”フォルダを作成
2.How to train YOLOv2 to detect custom objects
  からprocess.pyを持ってきてobjフォルダに置く
3.objフォルダにBBOXフォルダのimages/outputの画像たちをobjフォルダにコピー
4.コマンド 「 python process.py 」 を起動させて、test.txt(評価用)とtrain.txt(学習用)ができているのを確認
5.BBoxフォルダ内のconvertLabels/train内のテキスト達をdarknet/data/obj内にコピー
6.darknet/data内に"data.names"を作成後、自分の学習させるクラス名を記入(BBoxフォルダのclass.txtの中身と一緒)

   car
   human
   pen
   dog
   cat 
7.darknet/cfgに”obj.data”を作成。中身は以下のようにする
 classes= 5
 train = data/obj/train.txt
 valid = data/obj/test.txt
 names = data/obj.names
 backup = backup/
ここでclasses= 5 は学習させるクラスの数なので自分の数値に合わせる。今回の例だと5になる。
8.darknetフォルダ内に学習結果を保存するための”backup”フォルダを作成
9.darknet/cfg/yolo-obj.cfgの内容を編集しますが、以下参考HPの内容をそのまま記述させていただきます。

cfg/yolo-obj.cfgはもとからcfgディレクトリの中にある.cfgファイルをコピーしてちょっと編集してyolo-obj.cfgとします。今回はyolo-voc.cfgファイルをコピーして使いますが、他のモデル(tiny-yolo.cfg等)が使いたかったら適宜変えてください。
コピーしてリネームしたyolo-obj.cfgを少し編集します。
3行目:batch=64 にします。学習ステップごとに使い画像の枚数です。
4行目:subdivisions=8 にします。バッチが8で除算されます。強力なGPUを積んでればこれを減らすか、バッチを増やすことができます。
244行目:classes=1 にします。クラス数です。
237行目:filters=30 にします。フィルターの大きさです。filters=(classes + 5) * 5 と決まっています?
YOLOv2を使って自前のデータを学習させて認識させるまで。 - 可変ブログ

参考HP内容から変更する部分は、”244行目:classes=1” の部分です。ここを自分のクラスの数値にして下さい。(項目の7と同じ数)
また、自分の場合はgtx1070なのでこの設定で学習できますが、もしgtx960、gtx950などのメモリ数が少ない人はメモリが足らないこともあるかと思います。
そうした場合は学習結果が悪くなりますが、以下を変更することでなんとか学習をすることができます。
3行目 batch を32とかに減らす
258行目 random=0 これは画像をいじりながら学習する設定ですが、0にすることで無しにできます。
もっとありますが試したのはこの2つです。
 
10. https://pjreddie.com/media/files/darknet19_448.conv.23 から初期データをダウンロードして,darknetフォルダ内に移動。
11.以下のコマンドで学習開始

 ./darknet detector train cfg/obj.data cfg/yolo-obj.cfg darknet19_448.conv.23
 
 
12.学習ができているか確認
画像を読み込ませて確認
 ./darknet detector test cfg/obj.data cfg/yolo-obj.cfg backup/yolo-obj_***.backup data/testimage.jpg 
                               ↑学習した数値入れる   ↑画像の場所は自分の環境にあわせる

webカメラで確認する場合
 ./darknet detector demo cfg/obj.data cfg/yolo-obj.cfg backup/yolo-obj_***.weights
                                ↑学習した数値入れる
 

・プログラム

・画像の水増し
yoloの画像を生成するまで · GitHub

-- increase_picture.py --

#!/usr/bin/env python
# -*- coding: utf-8 -*-
#

import cv2
import numpy as np
import sys
import glob
import os
import re
import numpy as np
from os import walk, getcwd

# ヒストグラム均一化
def equalizeHistRGB(src):

    RGB = cv2.split(src)
    Blue   = RGB[0]
    Green = RGB[1]
    Red    = RGB[2]
    for i in range(3):
        cv2.equalizeHist(RGB[i])

    img_hist = cv2.merge([RGB[0],RGB[1], RGB[2]])
    return img_hist

# ガウシアンノイズ
def addGaussianNoise(src):
    row,col,ch= src.shape
    mean = 0
    var = 0.1
    sigma = 15
    gauss = np.random.normal(mean,sigma,(row,col,ch))
    gauss = gauss.reshape(row,col,ch)
    noisy = src + gauss

    return noisy

# salt&pepperノイズ
def addSaltPepperNoise(src):
    row,col,ch = src.shape
    s_vs_p = 0.5
    amount = 0.004
    out = src.copy()
    # Salt mode
    num_salt = np.ceil(amount * src.size * s_vs_p)
    coords = [np.random.randint(0, i-1 , int(num_salt))
              for i in src.shape]
    out[coords[:-1]] = (255,255,255)

    # Pepper mode
    num_pepper = np.ceil(amount* src.size * (1. - s_vs_p))
    coords = [np.random.randint(0, i-1 , int(num_pepper))
              for i in src.shape]
    out[coords[:-1]] = (0,0,0)
    return out

if __name__ == '__main__':
    # ルックアップテーブルの生成
    min_table = 50
    max_table = 205
    diff_table = max_table - min_table
    gamma1 = 0.75
    gamma2 = 1.5

    LUT_HC = np.arange(256, dtype = 'uint8' )
    LUT_LC = np.arange(256, dtype = 'uint8' )
    LUT_G1 = np.arange(256, dtype = 'uint8' )
    LUT_G2 = np.arange(256, dtype = 'uint8' )

    LUTs = []

    # 平滑化用
    average_square = (10,10)

    # ハイコントラストLUT作成
    for i in range(0, min_table):
        LUT_HC[i] = 0

    for i in range(min_table, max_table):
        LUT_HC[i] = 255 * (i - min_table) / diff_table

    for i in range(max_table, 255):
        LUT_HC[i] = 255

    # その他LUT作成
    for i in range(256):
        LUT_LC[i] = min_table + i * (diff_table) / 255
        LUT_G1[i] = 255 * pow(float(i) / 255, 1.0 / gamma1)
        LUT_G2[i] = 255 * pow(float(i) / 255, 1.0 / gamma2)

    LUTs.append(LUT_HC)
    LUTs.append(LUT_LC)
    LUTs.append(LUT_G1)
    LUTs.append(LUT_G2)

    wd = getcwd()
    # 画像の読み込み
    iamge_path = sys.argv[1]
    files = glob.glob(iamge_path + '/*.jpg')
    print(files)

    for item_index, file in enumerate(files):
        img_src = cv2.imread(file, 1)

        trans_img = []
        trans_img.append(img_src)

        # LUT変換
        for i, LUT in enumerate(LUTs):
            trans_img.append( cv2.LUT(img_src, LUT))

        # 平滑化
        trans_img.append(cv2.blur(img_src, average_square))

        # ヒストグラム均一化
        trans_img.append(equalizeHistRGB(img_src))

        # ノイズ付加
        trans_img.append(addGaussianNoise(img_src))
        trans_img.append(addSaltPepperNoise(img_src))

        # 反転
        flip_img = []
        for img in trans_img:
            flip_img.append(cv2.flip(img, 1))
        trans_img.extend(flip_img)

        # 保存
        #if not os.path.exists("training_images"):
        #    os.mkdir("training_images")

        base = os.path.splitext(os.path.basename(file))[0] + "_"
        item_txt = os.path.splitext(os.path.basename(file))[0] + '.txt'
        img_src.astype(np.float64)
        for i, img in enumerate(trans_img):
            # 比較用
            # cv2.imwrite("images/003/" + base + str(i) + ".jpg" ,cv2.hconcat([img_src.astype(np.float64), img.astype(np.float64)]))
            #9以下は反転していない
            coordinate = ''
            labels_path = sys.argv[2]

            file = open(labels_path + '/' + item_txt, 'r')
            if i <= 8:
                coordinate = file.read()
            #9以上は反転している
            else:
                line = file.readline()
                coordinate = line

                while line:
                    line = file.readline()
                    words = re.split(" +", line)
                    if len(words) >= 2:
                        x = words[0]
                        y = words[1]
                        width = words[2]
                        height = words[3]
                        class_name = words[4]
                        img_width = img.shape[1]

                        x = int(img_width) - int(width)
                        width = int(img_width) - int(words[0])

                        coordinate = coordinate + ' '.join([
                            str(x).strip(),
                            str(y).strip(),
                            str(width).strip(),
                            str(height).strip(),
                            class_name
                        ]) + '\n'

            file.close()

            f = open("Labels/output/" + base + str(i) + ".txt", 'w')
            f.write(coordinate)
            f.close()

            cv2.imwrite("Images/output/" + base + str(i) + ".jpg" ,img)

 
・学習データ用に変換

-- convert.py --

# -*- coding: utf-8 -*-
"""
Created on Wed Dec  9 14:55:43 2015
This script is to convert the txt annotation files to appropriate format needed by YOLO 
@author: Guanghan Ning
Email: gnxr9@mail.missouri.edu
"""

import os
from os import walk, getcwd
from PIL import Image

classes = ["train"]

def convert(size, box):
    dw = 1./size[0]
    dh = 1./size[1]
    x = (box[0] + box[1])/2.0
    y = (box[2] + box[3])/2.0
    w = box[1] - box[0]
    h = box[3] - box[2]
    x = x*dw
    w = w*dw
    y = y*dh
    h = h*dh
    return (x,y,w,h)
    
    
"""-------------------------------------------------------------------""" 

""" Configure Paths"""   
mypath = "Labels/output/"
outpath = "convertLabels/train/"
imgpath = "/Images/output"

cls = "train"
if cls not in classes:
    exit(0)
cls_id = classes.index(cls)

wd = getcwd()
""" GET class text file list """
class_path = "class"
class_file = open('%s/%s.txt'%(wd, class_path), 'r')
class_list = class_file.read().split('\n')   #for ubuntu, use "\r\n" instead of "\n"
print(class_list)

list_file = open('%s/%s_list.txt'%(wd, cls), 'w')

""" Get input text file list """
txt_name_list = []
for (dirpath, dirnames, filenames) in walk(mypath):
    txt_name_list.extend(filenames)
    break
#print(txt_name_list)

""" Process """
for txt_name in txt_name_list:
    # txt_file =  open("Labels/stop_sign/001.txt", "r")
    
    """ Open input text files """
    txt_path = mypath + txt_name
    #print("Input:" + txt_path)
    txt_file = open(txt_path, "r")
    lines = txt_file.read().split('\n')   #for ubuntu, use "\r\n" instead of "\n"
    
    """ Open output text files """
    txt_outpath = outpath + txt_name
    #print("Output:" + txt_outpath)
    txt_outfile = open(txt_outpath, "w")
    
    
    """ Convert the data to YOLO format """
    ct = 0
    for line in lines:
        #print('lenth of line is: ')
        #print(len(line))
        #print('\n')
        if(len(line) >= 2):
            ct = ct + 1
            print(line)
            elems = line.split(' ')
            #print(elems)
            xmin = elems[0]
            xmax = elems[2]
            ymin = elems[1]
            ymax = elems[3]
            class_name = elems[4]
            #print(class_name)
            #
            img_path = str('%s/%s.jpg'%(wd+imgpath,os.path.splitext(txt_name)[0]))
            print("img_path:",img_path)
            #t = magic.from_file(img_path)
            #wh= re.search('(\d+) x (\d+)', t).groups()
            im=Image.open(img_path)
            w= int(im.size[0])
            h= int(im.size[1])
            #w = int(xmax) - int(xmin)
            #h = int(ymax) - int(ymin)
            # print(xmin)
            #print(w, h)
            b = (float(xmin), float(xmax), float(ymin), float(ymax))
            bb = convert((w,h), b)
            #print(bb)
            print('\n')
            i=0
            for class_num in class_list:
               if class_name == class_num:
                 class_id = i
               i+=1
            txt_outfile.write(str(class_id) + " " + " ".join([str(a) for a in bb]) + '\n')

    """ Save those images with bb into list"""
    if(ct != 0):
        list_file.write('%s/images/%s/%s.jpg\n'%(wd, cls, os.path.splitext(txt_name)[0]))
                
list_file.close()       

Amazon musicをUbuntuで再生できない問題

firefoxchromeなどのブラウザからAmazon musicwindowsで再生するのはエラーなくできるのですが、
Ubuntuで再生しようとすると1曲を0秒で終わらせてアルバム曲の最後まで一瞬で行ってからエラー文を吐くので
なんで?と思って調べました。

ubuntuforums.org

ここに書いてありましたが、Linuxだとダメらしいので、user-agent-switcherなるものでブラウザを
chrome61のwindows10にしてApplyするときちんと曲が再生されるようになります。
chrome.google.com

以上。

ubuntu16.04でkinect_v1

ubuntu16.04でkinect_v1をインストールからサンプルを動かすところまでやります。

ROSでの起動

ROSだと簡単に環境を整えることができます。
ここの通りにやればすぐに動かせます。
ROS → https://staff.aist.go.jp/kanezaki.asako/pdf/SSII2016_AsakoKanezaki.pdf

OpenCVでの起動

opencvが2.3や2.4の人はopencvから呼び出し可能です。
Kinect and OpenNI — OpenCV 2.4.13.2 documentation
ただし、openNiがデフォルトフォルダにインストール済みなことや、
opencvビルド時にWITH_OPENNI=ONを指定していることが前提になります。
環境が揃っている人は、opencv-2.4.13/build/binのcpp-example-openni_captureを実行することでkinectを起動できます。

ライブラリを一つずつ入れていく方法

ほとんど参考HPのままですが、一部変更点があります。
[参考HP]
Accessors Wiki | ROS / InstallingThePrimeSenseKinectSensorOnUbuntu

Install the following libraries


sudo apt-get install freeglut3-dev pkg-config build-essential libxmu-dev libxi-dev libusb-1.0-0-dev doxygen graphviz mono-complete

sudo apt-get install openjdk-8-jre ←追加
sudo apt-get install openjdk-8-jdk ←追加

 

Install OpenNI


#Create a 'kinect' folder in your home directory to store installation source of drivers:
mkdir -p ~/kinect
cd ~/kinect

#Download from the OpenNI modules. Make sure you get the 'unstable' branch:
git clone https://github.com/OpenNI/OpenNI.git
cd OpenNI
git checkout unstable
cd Platform/Linux/CreateRedist
./RedistMaker

#If that succeeds, there a Redist folder should be created with an install script. Run it:
cd ~kinect/OpenNI/Platform/Linux/Redist/OpenNI-Bin-Dev-Linux-x64-v1.5.8.5/
sudo ./install.sh

 

Install the PrimeSense drivers:


#many blogs recommend installing from https://github.com/avin2/SensorKinect.git
#This didn't work for me, but the following the following repo from ph4m worked:
cd ~/kinect
git clone https://github.com/ph4m/SensorKinect.git

#Install it
cd SensorKinect/Platform/Linux/CreateRedist
./RedistMaker

#If that succeeds, there a Redist folder should be created with an install script. Run it:
cd ~/kinect/SensorKinect/Platform/Linux/Redist/Sensor-Bin-Linux-x64-v5.1.2.1/
sudo ./install.sh

Install the NITE middleware


#I downloaded the OpenNI SDK v2.1 for Linux-x64 from http://openni.ru/openni-sdk/openni-sdk-history-2/index.html into ~/kinect.
#The latest NITE SDK from http://openni.ru/files/nite/index.html may be fine as well.
cd ~/kinect
unzip OpenNI-Linux-x64-2.1.0.tar.zip
tar -jxvf OpenNI-Linux-x64-2.1.0.tar.bz2
cd OpenNI-2.1.0-x64
sudo ./install.sh

#Run a sample simple viewer
Samples/Bin/SimpleViewer
cd ~/kinect/OpenNI/Platform/Linux/Bin/x64-Release ←変更
kinectを接続していること確認
./NiViewer ←サンプル起動

 

windows10にROS2をインストールする

公式wikiを見ながら、win10にインストール作業をしました。
  

事前条件

Visual Studio 14.0 2015 Update 3”が入っていて、かつC++も選択してインストールしていること
pythonは2017_12_10時点で最新の3.6.2ではなく3.5の代ではないと途中のtrolliusをインストールするところで入らないのでダウングレードする必要があります!
そして自分はpyenvとか使ってないから一回環境ぶち壊れたよ!(^o^)ノ
 
ダウングレードする場合は

conda install python=3.5

 

Chocolateyのインストール

Windowsのキーを押し検索で” vs2015 x64 native tools command prompt”のアプリを探し、右クリックして[管理者として実行]を選択。
f:id:weekendproject9:20171211175455p:plain

以下のコマンドを張り付けて実行

@"%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe" -NoProfile -InputFormat None -ExecutionPolicy Bypass -Command "iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))" && SET "PATH=%PATH%;%ALLUSERSPROFILE%\chocolatey\bin"

そしたら警告が出てきました
警告1.
WARNING: It's very likely you will need to close and reopen your shell
before you can use choco.

インストールしたあと使いたかったら再起動しろと。わかりました。

警告2.
WARNING: You can safely ignore errors related to missing log files when
upgrading from a version of Chocolatey less than 0.9.9.
'Batch file could not be found' is also safe to ignore.
'The system cannot find the file specified' - also safe.

これは無視できるらしい

警告3.
警告: Not setting tab completion: Profile file does not exist at
'C:\Users\\Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1'.

これはファイルがないよと言ってますが、別に使わない?ので無視
 
コマンドラインで "choco -v" でバージョン確認
0.10.8
 
一応アップデートする

choco upgrade chocolatey

 

依存関係のインストール

コマンドは vs2015 x64 native tools command promptで実行する
・gitインストール

choco install -y git

環境変数いじったよって言われる
 
・cmakeインストール

choco install -y cmake

環境変数に ”C:\Program Files\CMake\bin” を追加
 
python入れてない人は

choco install -y python

 

Boostインストール

以下からBoostをダウンロード
https://sourceforge.net/projects/boost/files/boost-binaries/1.61.0/boost_1_61_0-msvc-14.0-64.exe/download?use_mirror=jaist

exeでインストールしたあと、環境変数を追加
 BOOST_ROOT    : C:\local\boost_1_61_0
 BOOST_LIBRARYDIR  : C:\local\boost_1_61_0\lib64-msvc-14.0
またpathにも追加
 C:\local\boost_1_61_0\lib64-msvc-14.0

開発者ツールのインストール

 pip install vcstool
 choco install -y curl

 

依存関係をインストールする

Release 2017-04-04-1 · ros2/choco-packages · GitHub
ここに行き、一つずつクリックして以下をダウンロード
・asio.1.10.6.nupkg
・eigen.3.3.3.nupkg
・tinyxml-usestl.2.6.2.nupkg
・tinyxml2.4.0.1.nupkg

以下でインストール。”PATH\TO\DOWNLOADS\” はダウンロードしたフォルダ名に書き換える

choco install -y -s <PATH\TO\DOWNLOADS\> asio eigen tinyxml-usestl tinyxml2

以下pipでインストール

pip install -U setuptools pip
pip install EmPy pyparsing pyyaml
pip install nose coverage mock pytest pytest-cov pytest-runner
pip install flake8 flake8-blind-except flake8-builtins flake8-class-newline flake8-comprehensions flake8-deprecated flake8-docstrings flake8-import-order flake8-quotes pep8 pydocstyle pyflakes

↓python3.6は対応していないので注意(trollius 2.1 : Python Package Index

pip install trollius
choco install -y cppcheck

C:\Program Files\CppcheckをPATHに追加する。

opencvインストール

 公式wikiとはちょっとPathを変えてますが以下になります。
 Visual Studio 2015:
  https://github.com/ros2/ros2/releases/download/release-beta2/opencv-2.4.13.2-vc14.VS2015.zip
 解凍して移動→C:\dev\opencv\opencv-2.4.13.2-vc14.VS2015
 環境変数を追加

setx -m OpenCV_DIR C:\dev\opencv\opencv-2.4.13.2-vc14.VS2015

C:\dev\opencv\opencv-2.4.13.2-vc14.VS2015\x64\vc14\bin をPATHに追加

OpenSSLインストール

以下からWin64 OpenSSL v1.0.2をダウンロードします。Win32またはLightバージョンはダウンロードしないでください。
Shining Light Productions - Win32 OpenSSL
exe起動。基本的に進むのみをクリック。最後の寄付はチェックを外してfinish。

setx -m OPENSSL_CONF C:\OpenSSL-Win64\bin\openssl.cfg

また、C:\OpenSSL-Win64\bin\をPATHに追加。
  

ソースの取得

C:直下にファイルを作って移動する

 md \dev\ros2\src
 cd \dev\ros2 

ros2.reposクローンを作成するリポジトリを定義するファイルを取得

curl -sk https://raw.githubusercontent.com/ros2/ros2/release-latest/ros2.repos -o ros2.repos

次に、vcs、ros2.reposにリストされているリポジトリをインポートする

vcs import src < ros2.repos

 

ROS 2コードの作成

フォルダツリーを構築してビルド。”--build-tests”オプションを追加してテストできる
結構時間かかります

cd C:\dev\ros2
python src\ament\ament_tools\scripts\ament.py build
python src\ament\ament_tools\scripts\ament.py build --build-tests

 

実行

一回vs2015 x64 native tools command promptを再起動して以下実行

> call install\local_setup.bat
> ros2 run demo_nodes_py talker

↓以下実行結果↓
C:\dev\ros2>ros2 run demo_nodes_py talker
[INFO] [talker]: Publishing: "Hello World: 0"
[INFO] [talker]: Publishing: "Hello World: 1"
[INFO] [talker]: Publishing: "Hello World: 2"
[INFO] [talker]: Publishing: "Hello World: 3"
[INFO] [talker]: Publishing: "Hello World: 4"
[INFO] [talker]: Publishing: "Hello World: 5"
[INFO] [talker]: Publishing: "Hello World: 6"
[INFO] [talker]: Publishing: "Hello World: 7"
[INFO] [talker]: Publishing: "Hello World: 8"
[INFO] [talker]: Publishing: "Hello World: 9"
[INFO] [talker]: Publishing: "Hello World: 10"
[INFO] [talker]: Publishing: "Hello World: 11"
[INFO] [talker]: Publishing: "Hello World: 12"
[INFO] [talker]: Publishing: "Hello World: 13"
[INFO] [talker]: Publishing: "Hello World: 14"
[INFO] [talker]: Publishing: "Hello World: 15"
[INFO] [talker]: Publishing: "Hello World: 16"
[INFO] [talker]: Publishing: "Hello World: 17"
[INFO] [talker]: Publishing: "Hello World: 18"
[INFO] [talker]: Publishing: "Hello World: 19"
[INFO] [talker]: Publishing: "Hello World: 20"
[INFO] [talker]: Publishing: "Hello World: 21"
[INFO] [talker]: Publishing: "Hello World: 22"
[INFO] [talker]: Publishing: "Hello World: 23"
[INFO] [talker]: Publishing: "Hello World: 24"
[INFO] [talker]: Publishing: "Hello World: 25"

 
f:id:weekendproject9:20171211175612p:plain
 
とりあえずインストールと実行は完了しました。
 

 
「参考」
Windows Development Setup · ros2/ros2 Wiki · GitHub
Troubleshooting

win10にVisualStudio2015,Python3.6,CUDA(gtx1070),Tensorflow環境を入れる

人工知能系の開発環境構築

環境

win10 64bit
gtx1070

VisualStudio2015 Community のダウンロード・インストール

以下URLに行き、ページ下部の”以前のバージョン”をクリック⇒ 2015の項目のダウンロードを選択
ダウンロード | IDE、Code、Team Foundation Server | Visual Studio
すでにログインしている場合は以下から飛べます
Visual Studio Team Services | Sign In

次に
Visual Studio Community 2015 with Update 3 を探して、言語をJapaneseにしてダウンロードします。
ダウンロードしたらexeを起動してインストール開始。
 途中の”インストールの種類”の項目を”カスタム”に変更し、プログラミング言語欄の”C++”,”Python”にチェックを入れてインストールを進めます。

インストール完了後、正常に起動することを確認


Python環境をインストール

Anacondaのサイトから”Python3.6”のバージョンをダウンロード⇒起動してインストール
Downloads | Anaconda
・インストール途中にPathを追加するかの項目があるのでチェックします。
 (非推奨なため問題が起きる時があります。このときは再インストールしてください)
・インストール対象はjust Me
インストールできたらコマンドプロンプトを開き、"python"と打ってインストールできているか確認

VisualStudioにAnaconda紐づけ

VS2015起動してPythonApplicationプロジェクト作成後、ツール→オプション→Python Tools → EnvironmentOptionsを選択
Add Environmentで任意の名前を記入
PathにAnacondaのインストール先を入れる。
標準のまま入れてたら
 Path:”C:\ProgramData\Anaconda3\python.exe”
 WindowsPath:C:\ProgramData\Anaconda3\pythonw.exe
 Library Path:C:\ProgramData\Anaconda3\Lib
 Architecture:x64
 Language Version:3.5
okを押して完了

コードに

print("Hello world")

で実行(F5)して表示されるか確認

CUDA Toolkitをインストール

トップページから行くと最新の9.0のページに飛ぶので、以下の8.0に行きます。
(9.0ですとtensorflowとcuDNNでエラーがあるとの報告があったためインストールを避けました)
CUDA Toolkit 8.0 - Feb 2017 | NVIDIA Developer
ベースインストーラとパッチをダウンロードします。
インストール前にはVisualStudioを落としておきます。
ベースインストーラでインストール後、パッチのexeをあてます。

cuDNNをインストール

NVIDIA cuDNN | NVIDIA Developer
ページ中央”Download”をクリックしてLoginしてください
(登録してない人は登録してください。少し経つと確認メールが来ますが迷惑メールに入る可能性があるので注意)
ログインして入ったら、”I Agree To the Terms of the cuDNN Software License Agreement”にチェックを入れる
"Download cuDNN v6.0 (April 27, 2017), for CUDA 8.0"の項目を開く
”cuDNN v6.0 Library for Windows 10”を選択してダウンロード

展開したら、ファイルがあるのでtoolkitの方に移動する
・展開したファイルbin\cudnn64_6.dll→
C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v8.0\binの中に移動
・展開したファイルinclude\cudnn.h→
C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v8.0\includeの中に移動
・展開したファイルlib\x64\cudnn.lib→
C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v8.0\lib\x64の中に移動

環境変数
”C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v8.0”を追加

Tensorflowインストール

コマンドプロンプトでインストール
 ”pip install --upgrade tensorflow-gpu

完了したら、コマンドプロンプトに"python"と打って起動、以下のプログラムが処理できるか確認

import tensorflow as tf
hello = tf.constant('Hello, TensorFlow!')
sess = tf.Session()
print(sess.run(hello))
a = tf.constant(10)
b = tf.constant(32)
sess.run(a + b)
sess.close()

”b'Hello, TensorFlow!'”
"42"
と表示されればok

ロボットを強化学習DQN (Deep Q Network) させる

今回は強化学習DQN (Deep Q Network) を↓で学ばせてもらいました
超シンプルにTensorFlowでDQN (Deep Q Network) を実装してみる 〜導入編〜  | ALGO GEEKS
それで、もう少しどういうものかを見るために自分なりに少し変えてみました。

システムとしては、ロボットがあり、あるターゲットに近づくことができればいいといった感じです。
参考ソースとの差は、
・ロボットを上下左右に移動させる
・ターゲットは初期にランダム配置
・explorationを1.0から始め、学習ごとに減らしていく
・ロボットとターゲットの距離が1になったところで報酬=1
・任意の時間以内にターゲットに近づけなければ報酬=-1

以下15000回学習させた結果↓
青丸:ターゲット
三角:ロボット
f:id:weekendproject9:20171129002539g:plain

dqn_agent.py

from collections import deque
import os

import numpy as np
import tensorflow as tf


class DQNAgent:
    """
    Multi Layer Perceptron with Experience Replay
    """

    def __init__(self, enable_actions, environment_name):
        # parameters
        self.name = "agent"#os.path.splitext(os.path.basename(__file__))[0]
        self.environment_name = environment_name
        self.enable_actions = enable_actions
        self.n_actions = len(self.enable_actions)
        self.minibatch_size = 32
        self.replay_memory_size = 1000
        self.learning_rate = 0.001
        self.discount_factor = 0.9
        self.exploration = 1.0#どのくらいで行動に出るか、大きいと活発に動く 
        self.model_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "models")
        self.model_name = "{}.ckpt".format(self.environment_name)

        # replay memory
        self.D = deque(maxlen=self.replay_memory_size)

        # model
        self.init_model()

        # variables
        self.current_loss = 0.0

    def init_model(self):
        # input layer (8 x 8)
        #self.x = tf.placeholder(tf.float32, [None, 64, 64])
        self.x = tf.placeholder(tf.float32, [None, 8, 8])

        # flatten (64)
        #x_flat = tf.reshape(self.x, [-1, 512])
        x_flat = tf.reshape(self.x, [-1, 64])

        # fully connected layer (32)
        #W_fc1 = tf.Variable(tf.truncated_normal([512, 512], stddev=0.01))#[64, 64]
        W_fc1 = tf.Variable(tf.truncated_normal([64, 64], stddev=0.01))#[64, 64]
        #b_fc1 = tf.Variable(tf.zeros([512]))#64
        b_fc1 = tf.Variable(tf.zeros([64]))#64
        h_fc1 = tf.nn.relu(tf.matmul(x_flat, W_fc1) + b_fc1)

        # output layer (n_actions)
        #W_out = tf.Variable(tf.truncated_normal([512, self.n_actions], stddev=0.01))
        W_out = tf.Variable(tf.truncated_normal([64, self.n_actions], stddev=0.01))
        b_out = tf.Variable(tf.zeros([self.n_actions]))
        self.y = tf.matmul(h_fc1, W_out) + b_out

        # loss function
        self.y_ = tf.placeholder(tf.float32, [None, self.n_actions])
        self.loss = tf.reduce_mean(tf.square(self.y_ - self.y))

        # train operation
        optimizer = tf.train.RMSPropOptimizer(self.learning_rate)
        self.training = optimizer.minimize(self.loss)

        # saver
        self.saver = tf.train.Saver()

        # session
        self.sess = tf.Session()
        self.sess.run(tf.global_variables_initializer())

    def Q_values(self, state):
        # Q(state, action) of all actions
        return self.sess.run(self.y, feed_dict={self.x: [state]})[0]

    def select_action(self, state, epsilon):
        if np.random.rand() <= epsilon:
            # random
            return np.random.choice(self.enable_actions)
        else:
            # max_action Q(state, action)
            return self.enable_actions[np.argmax(self.Q_values(state))]

    def store_experience(self, state, action, reward, state_1, terminal):
        self.D.append((state, action, reward, state_1, terminal))

    def experience_replay(self):
        state_minibatch = []
        y_minibatch = []

        # sample random minibatch
        minibatch_size = min(len(self.D), self.minibatch_size)
        minibatch_indexes = np.random.randint(0, len(self.D), minibatch_size)

        for j in minibatch_indexes:
            state_j, action_j, reward_j, state_j_1, terminal = self.D[j]
            action_j_index = self.enable_actions.index(action_j)

            y_j = self.Q_values(state_j)

            if terminal:
                y_j[action_j_index] = reward_j
            else:
                # reward_j + gamma * max_action' Q(state', action')
                y_j[action_j_index] = reward_j + self.discount_factor * np.max(self.Q_values(state_j_1))  # NOQA

            state_minibatch.append(state_j)
            y_minibatch.append(y_j)

        # training
        self.sess.run(self.training, feed_dict={self.x: state_minibatch, self.y_: y_minibatch})

        # for log
        self.current_loss = self.sess.run(self.loss, feed_dict={self.x: state_minibatch, self.y_: y_minibatch})

    def load_model(self, model_path=None):
        if model_path:
            # load from model_path
            print(" model_path:", model_path)
            self.saver.restore(self.sess, model_path)
        else:
            # load from checkpoint
            checkpoint = tf.train.get_checkpoint_state(self.model_dir)
            if checkpoint and checkpoint.model_checkpoint_path:
                self.saver.restore(self.sess, checkpoint.model_checkpoint_path)
            print(" model_dir:", checkpoint.model_checkpoint_path)

    def save_model(self):
        self.saver.save(self.sess, os.path.join(self.model_dir, self.model_name))
robot.py

import os
import numpy as np
import matplotlib.pyplot as plt
import math
import copy

class robot:
    def __init__(self):
        fig, ax = plt.subplots(1, 1)

        # parameters
        self.name = os.path.splitext(os.path.basename(__file__))[0]
        hit = 0
        self.fieldX = 8
        self.fieldY = 8
        self.robotX =0
        self.robotY =0
        self.initrobotPositionX = 4
        self.initrobotPositionY = 4
        self.robotMove = 1

        self.enable_actions = (0, 1, 2,3,4)
        self.frame_rate = 15

        self.targetNum = 1
        self.hit = 0
        self.time = 0

        self.around = 1

        # variables
        self.reset()

    def update(self, action):
        """
        action:
            0: do nothing
            1: move up
            2: move right
            3: move down
            4: move left
        """
        # update player position
        if action == self.enable_actions[1]:
            # move up
            self.robotY = max(0, self.robotY - self.robotMove)
        elif action == self.enable_actions[2]:
            # move right
            self.robotX = min(self.robotX + self.robotMove , self.fieldX-1)
        elif action == self.enable_actions[3]:
            # move down
            self.robotY = min(self.fieldY-1, self.robotY + self.robotMove)
        elif action == self.enable_actions[4]:
            # move left
            self.robotX = max(0, self.robotX - self.robotMove)
        else:
            # do nothing
            pass

        # update 
        self.hit = self.targetUpdate(self.initrobotPositionX ,self.initrobotPositionY)


           

        # collision detection
        #reward reset
        self.reward =0
        # end flag
        self.terminal = False
        if(self.time>=5 or self.hit):
         self.terminal = True
         if self.hit:
             # hit
             self.reward = 1
         else:
             # no hit             
             self.reward = -1

    def targetUpdate(self,X,Y):
        i = 0
        self.time+=1
        print("time:",self.time)

        for i in range(self.targetNum):
            #self.rand =np.random.randint(1, 4, self.targetNum)            
            #if self.rand == 1:
            #  self.fannelPointY =  max(0, self.fannelPointY-1)
            #if self.rand == 2:
            #  self.fannelPointY =  min(self.fieldY-1, self.fannelPointY+1)
            #if self.rand == 3:
            #    self.fannelPointX =max(0,   self.fannelPointX-1)
            #if self.rand == 4:
            #    self.fannelPointX = min(self.fannelPointX+1,self.fieldX-1)


            if self.robotX - self.around <= self.fannelPointX and  self.robotX + self.around >= self.fannelPointX:
                if self.robotY - self.around <= self.fannelPointY and self.robotY+self.around >= self.fannelPointY:
                    self.hit = 1

            print("hit:" , self.hit)


            plt.clf()

            plt.plot(-2, -2, "ro")
            plt.plot(10, 10, "ro")            
            plt.plot(self.robotX, self.robotY, c='red',marker='^')
            plt.plot(self.fannelPointX, self.fannelPointY, c='blue',marker='o')
           
            plt.pause(1)

        return self.hit

      

    def draw(self):
        self.screen = np.zeros((self.fieldX, self.fieldY))
        print("robot_position:",self.robotX , "," , self.robotY )
        self.screen[self.robotX, self.robotY] = 1
        print("target_position",self.fannelPointX , "," , self.fannelPointY )

        self.screen[self.fannelPointX, self.fannelPointY] = 2
        print( self.screen)
        print("---")

    def observe(self):
        self.draw()
        return self.screen, self.reward, self.terminal

    def execute_action(self, action):
        self.update(action)

    def reset(self):
        print("robot_init")
        self.hit=0

        # reset other variables
        self.reward = 0
        self.terminal = False

        self.robotX = self.initrobotPositionX
        self.robotY = self.initrobotPositionY
        self.x =np.random.randint(0, self.fieldX, self.targetNum)
        self.y =np.random.randint(0, self.fieldY, self.targetNum)
        rand =np.random.randint(0,(self.fieldX) , self.targetNum)
        i=0

        self.fannelPointX = self.x[i]
        self.fannelPointY = self.y[i]

        if self.robotX - self.around <= self.fannelPointX and  self.robotX + self.around >= self.fannelPointX:
            if self.robotY - self.around <= self.fannelPointY and self.robotY+self.around >= self.fannelPointY:
                if rand >= self.fieldX/2:
                    self.fannelPointX = 0
                    self.fannelPointY = 0
                if rand <= self.fieldX/2:
                    self.fannelPointX = self.fieldX-1
                    self.fannelPointY = self.fieldY-1

        self.time=0

train.py

import numpy as np

from robot import robot
from dqn_agent import DQNAgent


def train():
    # parameters
    n_epochs = 15000

    # environment, agent
    env = robot()
    agent = DQNAgent(env.enable_actions, env.name)

    # variables
    win = 0

    for e in range(n_epochs):
        # reset
        frame = 0
        loss = 0.0
        Q_max = 0.0
        env.reset()
        state_t_1, reward_t, terminal = env.observe()

        while not terminal:
            state_t = state_t_1

            # execute action in environment
            action_t = agent.select_action(state_t, agent.exploration)
            env.execute_action(action_t)

            # observe environment
            state_t_1, reward_t, terminal = env.observe()

            # store experience
            agent.store_experience(state_t, action_t, reward_t, state_t_1, terminal)

            # experience replay
            agent.experience_replay()

            # for log
            frame += 1
            loss += agent.current_loss
            Q_max += np.max(agent.Q_values(state_t))
            if reward_t == 1:
                win += 1

        print("EPOCH: {:03d}/{:03d} | WIN: {:03d} | LOSS: {:.4f} | Q_MAX: {:.4f}".format(
            e, n_epochs - 1, win, loss / frame, Q_max / frame))

        agent.exploration = agent.exploration - (( 1.0-0.1)/n_epochs)

        if(n_epochs % 1000):
         agent.save_model()

    # save model
    agent.save_model()

 
今後はこうしていきたいですね↓
f:id:weekendproject9:20171129002636p:plain

[参考]
超シンプルにTensorFlowでDQN (Deep Q Network) を実装してみる 〜導入編〜  | ALGO GEEKS
[Python]強化学習(DQN)を実装しながらKerasに慣れる - Qiita
DQNをKerasとTensorFlowとOpenAI Gymで実装する

arduinoエラーについて

arduino開発で突如出たエラーについてメモ。

以下、エラー内容

/arduino/hardware/tools/avr/bin/../lib/gcc/avr/4.9.2/../../../../avr/lib/avr5/crtatmega328p.o:(.init9+0x0): undefined reference to `main'

この時はarduinoIDEのウインドウを開きすぎなので一度全部閉じましょう。
また、タブを作ってやるのもだめらしい。。