激安ラジコン(RC)の自動運転化計画※プログラムは6/25掲載予定

目的:総計1万円でラジコンの自動運転化をすること

使ったもの

ハード

  • ラジコン:軽トラRC(Amazon CAPTCHA
  • ラズベリーパイ
  • モータードライバー:L298n
  • カメラ:LOGICOOL C270
  • ソフト
  • 言語:python
  • DLライブラリ:Keras(on Tensorflow)
  • Opencv,numpy,paho-mqtt...

    システムの概要

    今回は言語をpython限定にした. f:id:kobakenkken:20180624180334p:plain

  • RCの行動の種類
  • 前,右,少し右,左,少し左の5種類

    画像転送部分

    • Opencvwebカメラから取得した画像をsocket通信によりサーバ側(PC)へ転送を行う.今回はUDP及びデータを圧縮した. *プログラム

    モータ制御部分

    • L298nによりモータの制御を行う.サーバ(PC)側の分類結果からそれに対応する制御信号をMQTTにより受信し,モータの駆動させる. *プログラム

    学習・検証部分

    • KerasによりCNN部分の実装を行う. *プログラム
    • 使用したCNNモデル:vgg16(転移学習)

    結果・考察

    全体として1万円以内に収めることができた. しかし、画像の転送部分での課題があり,Picamera及びffmpeg等でストリーミングを行うことで解決する予定である.

    L298Nを使ってモータの制御

    L298Nとは


    2つのモータを独立して駆動でき、正転逆転制御などに最適です。 制御は各モーターに対して、イネーブル(回転する/しない)と回転方向の指定を2線でおこないます。 マイコンでの制御のほか、スイッチなどによってマニュアル制御も簡単におこなえます。

    L298N使用 2Aデュアルモーターコントローラー: マイコン関連 秋月電子通商 電子部品 ネット通販

    http://akizukidenshi.com/catalog/g/gM-06680/   f:id:kobakenkken:20180515171831p:plain

    Pin Description

    • ENA Motor A Speed Control

    • IN1 Motor A Direction Pin 1

    • IN2 Motor A Direction Pin 2

    • IN3 Motor B Direction Pin 1

    • IN4 Motor B Direction Pin 2

    • ENB Motor B Speed Control

    Terminal Description

    • VMS 5-35V Input

    • GND 0V

    • 5V 5V input/output

    • OUT1 Motor A

    • OUT2 Motor A

    • OUT3 Motor B

    • OUT4 Motor B

    RaspiとPythonによるモータ制御

    import RPi.GPIO as gpio
    import time
     
    def init():
     gpio.setmode(gpio.BCM)
     gpio.setup(17, gpio.OUT)
     gpio.setup(22, gpio.OUT)
     gpio.setup(23, gpio.OUT)
     gpio.setup(24, gpio.OUT)
     
    def forward(tf):
     init()
     gpio.output(17, True)
     gpio.output(22, False)
     gpio.output(23, True) 
     gpio.output(24, False)
     time.sleep(tf)
     gpio.cleanup()
     
    def reverse(tf):
     init()
     gpio.output(17, False)
     gpio.output(22, True)
     gpio.output(23, False) 
     gpio.output(24, True)
     time.sleep(tf)
     gpio.cleanup()
     
    print "forward"
    forward(4)
    print "backward"
    reverse(2)
    
    #!/usr/bin/python3.4
    #MKerbachi November 6th, 2015
    #Python code to control two motors with Rpi A+ with the H bridge l298n
    
    import RPi.GPIO as GPIO # always needed with RPi.GPIO  
    import time
    import curses
    
    
    # get the curses screen window
    screen = curses.initscr()
     
    # turn off input echoing
    curses.noecho()
     
    # respond to keys immediately (don't wait for enter)
    curses.cbreak()
     
    # map arrow keys to special values
    screen.keypad(True)
    
    
    
    #If the two GND (PI + l298n) are not interconnected that won't work !
    #For all Keyboard symbols:
    #https://docs.python.org/2/library/curses.html
      
    GPIO.setmode(GPIO.BCM)  # choose BCM or BOARD numbering schemes. I use BCM  
    
    #################################################################
    #          Variables               #
    #################################################################
    
    #For Motor #1
    GPIO.setup(18, GPIO.OUT)# set GPIO 01 as an output Enabler
    GPIO.setup(24, GPIO.OUT)# set GPIO 05 as an output.
    GPIO.setup(23, GPIO.OUT)# set GPIO 04 as an output.
      
    p24 = GPIO.PWM(24, 100)
    p23 = GPIO.PWM(23, 100)
    p18 = GPIO.PWM(18, 100)    # create an object p for PWM on port 18 at 50 Hertz
                            # you can have more than one of these, but they need
                            # different names for each port
                            # e.g. p1, p2, motor, servo1 etc.
    
    #For Motor #2
    GPIO.setup(13, GPIO.OUT)# set GPIO 03 as an output Enabler
    GPIO.setup(27, GPIO.OUT)# set GPIO 02 as an output.
    GPIO.setup(17, GPIO.OUT)# set GPIO 0  as an output.
    
    p27 = GPIO.PWM(27, 100)
    p17 = GPIO.PWM(17, 100)
    p13 = GPIO.PWM(13, 100)    # create an object p for PWM on port 18 at 50 Hertz  
                            # you can have more than one of these, but they need  
                            # different names for each port   
                            # e.g. p1, p2, motor, servo1 etc.  
    
    LastKey = ""
    
    #################################################################
    #          Functions               #
    #################################################################
    
    def Stop():
                p18.start(0)
                p23.start(0)
                p24.start(0)
    
                p13.start(0)
                p27.start(0)
                p17.start(0)
                time.sleep(0.3)
                #GPIO.cleanup()
                print ("Stop executed")
                #exit()
    
    def Left():
                if LastKey != 'left' : Stop()
                p18.start(60)
                p23.start(0)
                p24.start(100)
    
                time.sleep(0.4)
                p13.start(60)
                p27.start(0)
                p17.start(100)
    
    #            time.sleep(0.3)
                #Stop()
    
    def Right():
                if LastKey != 'right' : Stop()
                p18.start(60)
                p23.start(100)
                p24.start(0)
    
                time.sleep(0.4)
                p13.start(60)
                p27.start(100)
                p17.start(0)
    
    #            time.sleep(0.3)
                #Stop()
    
    def Up():
                #if LastKey != 'up' : Stop()
                p18.start(60)
                p23.start(100)
                p24.start(0)
    
                time.sleep(0.3)
                p13.start(60)
                p27.start(0)
                p17.start(100)
    
                time.sleep(0.3)
                #Stop()
    
    def Down():
                #if LastKey != 'down' : Stop()
                p18.start(60)
                p23.start(0)
                p24.start(100)
    
                time.sleep(0.3)
                p13.start(60)
                p27.start(100)
                p17.start(0)
    
                time.sleep(0.3)
                #Stop()
    
    
    try:
        while True:
            char = screen.getch()
            print ('you entred')
            print (char)
            if char == ord('q'):
                break
            #elif char == curses.KEY_ENTER:
            elif char == ord(' '):
                # print doesn't work with curses, use addstr instead
                #screen.addstr(0, 0, 'right')
                if not ( LastKey == "enter" ) : print ('Last key was not Enter, it was %s \n' % LastKey)
                LastKey="enter"
                print ('enter\n')
                Stop()
    
            elif char == curses.KEY_RIGHT:
                # print doesn't work with curses, use addstr instead
                #screen.addstr(0, 0, 'right')
                if not ( LastKey == "right" ) : print ('Last key was not right, it was %s \n' % LastKey)
                LastKey="right"
                print ('right\n')
                Right() 
               
            elif char == curses.KEY_LEFT:
                #screen.addstr(0, 0, 'left ')       
                if not ( LastKey == "left" ) : print ('Last key was not left, it was %s \n' % LastKey)
                LastKey="left"
                print ('left\n')
                Left()
    
            elif char == curses.KEY_UP:
                #screen.addstr(0, 0, 'up   ')       
                if not ( LastKey == "up" ) : print ('Last key was not up, it was %s \n' % LastKey)
                LastKey="up"
                print ('up\n')
                Up()
    
            elif char == curses.KEY_DOWN:
                #screen.addstr(0, 0, 'down ')
                if not ( LastKey == "down" ) : print ('Last key was not down t was %s \n' % LastKey)
                LastKey="down"
                print ('down\n')
                Down()
            else:
                print ('Nothing Entred!\n')
    
    finally:
        # shut down cleanly 
        print ('In the finally section now')
        curses.nocbreak(); screen.keypad(0); curses.echo()
        curses.endwin()
        p13.stop()                # stop the PWM output
        p17.stop()
        p27.stop()
    
        p23.stop()                # stop the PWM output
        p24.stop()
        p18.stop()
    
        GPIO.cleanup()          # when your program exits, tidy up after yours
    
    
    
    
    p13.stop()                # stop the PWM output  
    p17.stop()
    p27.stop()
      
    p23.stop()                # stop the PWM output  
    p24.stop()
    p18.stop()
    
    GPIO.cleanup()          # when your program exits, tidy up after yours
    

    メモ

    アナログ出力では周波数とデューティ比を指定して、モータ制御などに使うPWM制御ができます。

    まずピンに対して周波数を設定してpwmオブジェクトを取得します。

    pwm = GPIO.PWM([チャンネル], [周波数(Hz)]) 次にpwmオブジェクトに対してデューティ比を指定して出力を開始します。

    pwm.start([デューティ比]) 例えば、ピン18に周波数1KHz、デューティ比50%でPWM出力する場合は以下のように書きます。

    pwm = GPIO.PWM(18, 1000) pwm.start(50) 途中で周波数を変更する場合は以下の関数を使用します。

    pwm.ChangeFrequency([周波数(Hz)]) 途中でデューティ比を変更する場合は以下の関数を使用します。

    pwm.ChangeDutyCycle([デューティ比]) PWM出力を停止する場合は以下の関数を実行します。

    pwm.stop() スクリプト終了時にはちゃんと停止しておきましょう。

    request python まとめ

    what is request

    requestsとはサードパーティ製のhttp通信を行うためのライブラリ これを使用すると、webサイトのデータのダウンロードやrestapiの使用が可能 install cmd pip install requests

    example

    ヤフーのニュース一覧ページのhtmlを取得 import requests url = "https://news.yahoo.co.jp/topics" r = requests.get(url) print(r.text)

    urlから画像ダウンロード

    import urllib.error import urllib.request headers = { "User-Agent": "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:47.0) Gecko/20100101 Firefox/47.0", }

    def download_image(url, dst_path, headers): try: # request = urllib.request.Request(url=url, headers=headers) # data = urllib.request.urlopen(request)

        data = urllib.request.urlopen(url,headers).read()
        with open(dst_path, mode="wb") as f:
            f.write(data)
    except urllib.error.URLError as e:
        print(e)
    

    url = 'URL' dst_path = 'lena_square.png'

    dst_dir = 'data/src'

    dst_path = os.path.join(dst_dir, os.path.basename(url))

    download_image(url, dst_path, headers)

    urlからhtmlコンテンツダウンロード

    coding:utf-8

    import urllib.request

    url = "URL" headers = { "User-Agent": "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:47.0) Gecko/20100101 Firefox/47.0", }

    request = urllib.request.Request(url=url, headers=headers) response = urllib.request.urlopen(request) html = response.read().decode('utf-8') print(html)

    参考

    http://www.python.ambitious-engineer.com/archives/974#requests

    imgaugライブラリを使った機械学習用のdata augmentation

    install

    通常版

    sudo pip install imgaug
    

    最新版

    pip install git+https://github.com/aleju/imgaug
    

    必要なもの

    • six
    • numpy
    • scipy
    • scikit-image (pip install -U scikit-image)
    • OpenCV (i.e. cv2)

    使い方

    すべてのDA手法をお試しするならgenerate_example_images.pyを実行すればよし

    DAの種類

    kerasで実装できないものをまとめてみました。

    ペッパー

    f:id:kobakenkken:20180121160934j:plain

    ガウシアンノイズ

    f:id:kobakenkken:20180121155930j:plain

    ソルト

    f:id:kobakenkken:20180121155842j:plain

    ペッパー

    f:id:kobakenkken:20180121155820j:plain

    ソルト&ペッパー

    f:id:kobakenkken:20180121155803j:plain

    piece wise affine(区分積分アフィン?)

    f:id:kobakenkken:20180121155636j:plain

    透視変換(perspective transform)

    f:id:kobakenkken:20180121155611j:plain

    crop(トリミング)

    f:id:kobakenkken:20180121155536j:plain

    平滑化フィルタ

    median blur

    f:id:kobakenkken:20180121154958j:plain

    gaussian blur

    f:id:kobakenkken:20180121155022j:plain

    bilateral blur

    f:id:kobakenkken:20180121155241j:plain

    averageblur

    f:id:kobakenkken:20180121155420j:plain

    coarseシリーズ

    coarse ソルト

    f:id:kobakenkken:20180121160157j:plain

    coarse ペッパー

    f:id:kobakenkken:20180121160358j:plain

    coarse Dropout

    f:id:kobakenkken:20180121160414j:plain

    coarse ソルト&ペッパー

    f:id:kobakenkken:20180121160435j:plain

    contrast normalization

    f:id:kobakenkken:20180121160842j:plain

    frequency noisealpha

    f:id:kobakenkken:20180121160815j:plain

    multiply (ピクセル演算)

    f:id:kobakenkken:20180121160744j:plain

    参考

    https://towardsdatascience.com/image-augmentation-for-deep-learning-using-keras-and-histogram-equalization-9329f6ae5085

    keras 学習済モデルの取り扱い全般まとめ

    keras公式の学習済モデル読み込み方法

    from keras.applications.inception_v3 import InceptionV3
    
    InceptionV3 = InceptionV3(include_top=False, weights='imagenet', input_tensor=input_tensor)
    

    kerasで利用可能なモデル

    ImageNetで学習した重みをもつ画像分類のモデル:

    • Xception
    • VGG16
    • VGG19
    • ResNet50
    • InceptionV3
    • InceptionResNetV2
    • MobileNet
    • NASNet

    参照 https://keras.io/ja/applications/

    学習済モデルを利用した学習方法

    • 重み読み込み版(finetuning) weights='imagenet'を指定
    from keras.applications.inception_v3 import InceptionV3
    
    InceptionV3 = InceptionV3(include_top=False, weights='imagenet', input_tensor=input_tensor)
    
    • 重み読み込みなし版 weights='None'を指定
    from keras.applications.inception_v3 import InceptionV3
    
    InceptionV3 = InceptionV3(include_top=False, weights='None', input_tensor=input_tensor)
    
    • オリジナルの学習済モデルの場合(finetuning)
    model.load_weights("./weight.19-0.70.hdf5", by_name=True)
    

    model.load_weights(filepath, by_name=False): (save_weightsによって作られた) モデルの重みをHDF5形式のファイルから読み込む

    デフォルトはモデルの構造は不変であることが前提

    (いくつかのレイヤーが共通した)異なる構造に対して重みを読み込む場合,by_name=Trueを使うことで,同名のレイヤーにのみ読み込み可能

    学習モデルの保存方法

    model.save_weights(os.path.join('PATH', 'InceptionV3_scratch2.h5'))
    

    ModelCheckpointを使った各エポック終了後にモデルの保存のしかた

    keras.callbacks.ModelCheckpoint(filepath, monitor='val_loss', verbose=0, save_best_only=False, save_weights_only=False, mode='auto', period=1)
    

    引数

    • filepath: モデルファイルを保存するパス.
    • monitor: 監視する値.
    • verbose: 冗長モード, 0 または 1.
    • save_best_only: save_best_only=Trueの場合,監視しているデータによって最新の最良モデルが上書きされない.
    • mode: {auto, min, max}の内の一つが選択されます.save_best_only=Trueならば,現在保存されているファイルを上書きするかは,監視されている値の最大化か最小化によって決定.

    val_accの場合,この引数はmax

    val_lossの場合はmin

    autoモードでは,この傾向は自動的に監視されている値から推定します. * save_weights_only: Trueなら,モデルの重みが保存されます (model.save_weights(filepath)),そうでないなら,モデルの全体が保存されます (model.save(filepath)). * period: チェックポイント間の間隔(エポック数).

    参考にしたもの

    https://employment.en-japan.com/engineerhub/entry/2017/04/28/110000

    ubuntu16.04でのTensorFlow環境構築でのメモ

    TensorFlowのインストール

    「libcupti-dev」を入れます。

    sudo apt-get install libcupti-dev 「これはNVIDIA CUDAプロファイルツールインタフェースです。このライブラリは高度なプロファイリングのサポートを提供します。」だそうです。(TensorFlowより)

    次にvirtualenvを入れます。これは入れといたほうがいいです(condaがあるならそれでいいですけど)。TensorFlowはバージョンアップするごとに仕様が少し変わるので、違うバージョンを試してみたいということは多々起こります。

    一行目が2系用、二行目が3系用です。Pythonのバージョンに合わせて実行。

    sudo apt-get install python-pip python-dev python-virtualenv sudo apt-get install python3-pip python3-dev python-virtualenv

    インストール後は以下。こちらも一行目が2系、二行目が3系。

    virtualenv --system-site-packages ~/tensorflow virtualenv --system-site-packages -p python3 ~/tensorflow

    「~/tensorflow」のとこは別に自分の好きなディレクトリ名で結構ですが、まあ、これから変更する理由は特に無いと思います。

    次に環境をアクティベートします。

    source ~/tensorflow/bin/activate # bash, sh, ksh, or zsh source ~/tensorflow/bin/activate.csh # csh or tcsh

    使っているターミナルに合わせて選んでください。ubuntuの入れたばかりならbashです。アクティベートされたらいよいよTensorFlowをインストール。

    (tensorflow)$ pip install --upgrade tensorflow # for Python 2.7 (tensorflow)$ pip3 install --upgrade tensorflow # for Python 3.n (tensorflow)$ pip install --upgrade tensorflow-gpu # for Python 2.7 and GPU (tensorflow)$ pip3 install --upgrade tensorflow-gpu # for Python 3.n and GPU

    TensorFlowが利用できるか確認 TensorFlowを利用するときはアクティベートが必要です。

    以下のコマンドでできます。

    source ~/tensorflow/bin/activate # bash, sh, ksh, or zsh source ~/tensorflow/bin/activate.csh # csh or tcsh

    逆に終了する場合は

    deactivate

    参考にしたもの

    s0sem0y.hatenablog.com