#author("2020-04-19T01:18:49+00:00","default:mizutu","mizutu")
[[私的AI研究会]] > AIbot Proj.1

* AI Robot Project [#f0f94bee]
#ref(画像一覧/20200408_142232_001s.jpg,right,around,25%,20200408_142232_001s.jpg)
#contents
#clear

** ハードウェア外観と設計図 [#z273e640]
#ref(画像一覧/20200303_080643_001s.jpg,left,around,25%,20200303_080643_001s.jpg)
#ref(画像一覧/20200303_080517_001s.jpg,left,around,25%,20200303_080517_001s.jpg)
#ref(画像一覧/20200310_160731_001s.jpg,left,around,25%,20200310_160731_001s.jpg)
#ref(画像一覧/20200326_132657_001s.jpg,left,around,25%,20200326_132657_001s.jpg)
#clear
#ref(画像一覧/raspi_box_s.jpg,left,around,54%,raspi_box_s.jpg)
#ref(画像一覧/raspi_case2s.jpg,left,around,30%,raspi_case2s.jpg)
#clear

** GPIO 関連回路図 [#j7d595bf]
#ref(画像一覧/20200308_085842_001s.jpg,left,around,25%,20200308_085842_001s.jpg)
#ref(画像一覧/20200319_081123_001s.jpg,left,around,12.5%,20200319_081123_001s.jpg)
#ref(画像一覧/20200407_164609_001s.jpg,left,around,25%,20200407_164609_001s.jpg)
#ref(画像一覧/20200407_164632_001s.jpg,left,around,25%,20200407_164632_001s.jpg)
#clear
#ref(画像一覧/fig6s.jpg,left,around,25%,fig6s.jpg)
#ref(画像一覧/fig9m.jpg,left,around,25%,fig9m.jpg)
● fig.6~
 LED・スイッチ・スピーカー・オーディオアンプ~
→ [[ケースの作成・基板実装・配線>外装ハードウェア]]
~
● fig.9~
 LED・DCモーター~
→ [[キャタピラーロボットの組み立て>ハードウェア2]]

#clear

** モーター駆動の基本プログラム [#sf49b649]
- 左右のDCモーター回路の基本動作と正面2個のLEDとタクトスイッチ組み込みのLEDを制御する。~
- GPI制御ライブラリ[[「WiringPi API」>WiringPi API]] をアクセスするクラス基本クラスとしてモータードライバDRV8835 とLED をアクセスするクラスを別々に作成。。~
- LEDの点滅制御をモーターの駆動時間との兼ね合いから基本0.5秒(0.25+0.25)で1回の点滅とする。~

 ''aibot.py''
 #!/usr/bin/env python
 # -*- coding: utf-8 -*-
 
 # aibot.py
 # AI Robot Controle Library on Raspberry Pi4
 #
 #   DC motor DRV8835 controle
 #       A-IN1 : GPIO 14 (BCM)              B-IN1 : GPIO 15 (BCM)
 #       A-IN2 :      23                    B-IN2 :      24
 #       LED-S :      16                    LED-L :      17
 #       LED-R :      18
 #
 #   Tact switch
 #       ext pullup : GPIO 20
 #
 
 import wiringpi as pi
 import sys
 import time
 
 class MotorCtrl_base:
     def __init__(self, a_phase, a_enbl, led):
         pi.wiringPiSetupGpio()
         
         if a_phase > 0:
             self.a_phase = a_phase
             pi.pinMode(self.a_phase, pi.OUTPUT)
     
         if a_enbl > 0:
             self.a_enbl = a_enbl
             pi.pinMode(self.a_enbl, pi.OUTPUT)
     
         if led > 0:
             self.led = led
             pi.pinMode(self.led, pi.OUTPUT)
 
     def fwd(self):                          # 回転(正転)
         pi.digitalWrite(self.a_phase, 1)
         pi.digitalWrite(self.a_enbl, 1)
         
     def back(self):                         # 回転(逆転)
         pi.digitalWrite(self.a_phase, 0)
         pi.digitalWrite(self.a_enbl, 1)
         
     def stop(self):                         # 停止
         pi.digitalWrite(self.a_phase, 0)
         pi.digitalWrite(self.a_enbl, 0)
         
     def led_on(self):                       # ON
         pi.digitalWrite(self.led, 1)
         
     def led_off(self):                      # OFF
         pi.digitalWrite(self.led, 0)
     
     def reset(self):
         pi.digitalWrite(self.a_phase, 0)
         pi.digitalWrite(self.b_phase, 0)
         pi.digitalWrite(self.led, 0)
     
 class MotorCtrl:
     def __init__(self):
         self.dcmotor1 = MotorCtrl_base(a_phase=14, a_enbl=23, led=0)    # 右モーター
         self.dcmotor2 = MotorCtrl_base(a_phase=15, a_enbl=24, led=0)    # 左モーター
         self.led_sw = MotorCtrl_base(a_phase=0, a_enbl=0, led=16)       # switch LED
         self.led_right = MotorCtrl_base(a_phase=0, a_enbl=0, led=18)    # 右LED
         self.led_left = MotorCtrl_base(a_phase=0, a_enbl=0, led=17)     # 左LED
     
     def stop(self):
         self.dcmotor1.stop()
         self.dcmotor2.stop()
         self.led_off()
         self.led_sw.led_off()
         
     def forword(self):
         self.dcmotor1.fwd()
         self.dcmotor2.fwd()
         
     def wait_forword(self, cn):
         for i in range(cn):
             self.fwd_led()
             
     def fwd_led(self):
         self.led_right.led_on()
         self.led_left.led_on()
         time.sleep(0.5)
         
     def back(self):
         self.dcmotor1.back()
         self.dcmotor2.back()
         
     def wait_back(self, cn):
         for i in range(cn):
             self.back_led()
     
     def back_led(self):
         self.led_right.led_on()
         self.led_left.led_on()
         time.sleep(0.25)
         self.led_right.led_off()
         self.led_left.led_off()
         time.sleep(0.25)
     
     def turn_left(self):
         self.dcmotor1.fwd()
         self.dcmotor2.back()
         
     def wait_turn_left(self, cn):
         for i in range(cn):
             self.turn_left_led()
     
     def turn_left_led(self):
         self.led_left.led_on()
         self.led_right.led_on()
         time.sleep(0.25)
         self.led_right.led_off()
         time.sleep(0.25)
         
     def turn_right(self):
         self.dcmotor1.back()
         self.dcmotor2.fwd()
         
     def wait_turn_right(self, cn):
         for i in range(cn):
             self.turn_right_led()
     
     def turn_right_led(self):
         self.led_right.led_on()
         self.led_left.led_on()
         time.sleep(0.25)
         self.led_left.led_off()
         time.sleep(0.25)
     
     def left(self):
         self.dcmotor1.fwd()
         self.dcmotor2.stop()
         
     def wait_left(self, cn):
         for i in range(cn):
             self.left_led()
     
     def left_led(self):
         self.led_left.led_off()
         self.led_right.led_on()
         time.sleep(0.25)
         self.led_right.led_off()
         time.sleep(0.25)
         
     def right(self):
         self.dcmotor1.stop()
         self.dcmotor2.fwd()
         
     def wait_right(self, cn):
         for i in range(cn):
             self.right_led()
     
     def right_led(self):
         self.led_right.led_off()
         self.led_left.led_on()
         time.sleep(0.25)
         self.led_left.led_off()
         time.sleep(0.25)
     
     def led_off(self):
         self.led_left.led_off()
         self.led_right.led_off()
     
 if __name__ == '__main__':
     motor = MotorCtrl()
     motor.stop()
     motor.led_off()
     
     args = sys.argv
     if 2 <= len(args):
         duration = sys.argv[2]
         if sys.argv[1] in {"stop"}:
             motor.stop()
         elif sys.argv[1] in {"forword"}:
             motor.forword()
             motor.wait_forword(int(duration))
             motor.stop()
         elif sys.argv[1] in {"back"}:
             motor.back()
             motor.wait_back(int(duration))
             motor.stop()
         elif sys.argv[1] in {"turn-left"}:
             motor.turn_left()
             motor.wait_turn_left(int(duration))
             motor.stop()
         elif sys.argv[1] in {"turn-right"}:
             motor.turn_right()
             motor.wait_turn_right(int(duration))
             motor.stop()
         elif sys.argv[1] in {"left"}:
             motor.left()
             motor.wait_left(int(duration))
             motor.stop()
         elif sys.argv[1] in {"right"}:
             motor.right()
             motor.wait_right(int(duration))
             motor.stop()
         else:
             print("Need Argument;forword, right, left, turn-right, turn-left, back or stop")
            
     else:
         print("Command Format: aibot [forword|right|left|turn-right|turn-left|back|stop] [count(0.5sec)]")

- コマンドラインからの起動時は引数により各駆動動作を実行する。~
''python aibot.py [forword|right|left|turn-right|turn-left|back|stop] [count(0.5sec)]''~

-- 前進:
$ python aibot.py forword 6
-- 後退:
$ python aibot.py back 6
 
** 端末ターミナルからリモートコントロール [#jf4cd9bc]
#ref(画像一覧/key_setm.jpg,right,around,25%,key_setm.jpg)
- 端末ターミナルから起動しキーボード入力でキャタピラーロボットをリモート制御する。
- キーを1度押すと次のキー入力まで現在の動作を継続する。
- なお、このソフトウェアは原理上端末ターミナルからIDEデバッグはできない。
-- 前進  : 'i','A'
-- 後退  : 'm','B'
-- 左   : 'j','C'
-- 右   : 'l','D'
-- 左に回転: 'u','1'
-- 右に回転: 'o','2'
-- 停止  : 'space','s','k'
-- 終了  : 'q'
#clear

 ''aibotrm.py''
 #!/usr/bin/env python
 # -*- coding: utf-8 -*-
 
 # aibotrm.py  <<このソースコードは IDE デバッグはできない>>
 # AI Robot Remort Program on Raspberry Pi4
 #
 #   DC motor DRV8835 controle
 #       A-IN1 : GPIO 14 (BCM)              B-IN1 : GPIO 15 (BCM)
 #       A-IN2 :      23                    B-IN2 :      24
 #       LED-S :      16                    LED-L :      17
 #       LED-R :      18
 #
 #   Tact switch
 #       ext pullup : GPIO 20
 #
 
 import wiringpi as pi
 from kbhit import *
 import aibot
 import sys
     
 if __name__ == '__main__':
     atexit.register(set_normal_term)
     set_curses_term()
     
     motor = aibot.MotorCtrl()
     motor.stop()
     motor.led_off()
     ledmod = 0
     kb = ' '
     print("AIbot Control Ok! 'i,m,j,l,k,,u,o' or 'q'")
 
     while 1:
         # key push check & Motor control
         if kbhit():
             kb = getch()
             if kb == 'q':
                 print(" ## Break       ##")
                 motor.stop()
                 motor.led_off()
                 ledmod = 9
             elif kb == 'A' or kb == 'i':
                 print(" ** Forword     **")
                 motor.forword()
                 ledmod = 1
             elif kb == 'B' or kb == 'm':
                 print(" ** Bsck        **")
                 motor.back()
                 ledmod = 2
             elif kb == 'C' or kb == 'j':
                 print(" ** Left        **")
                 motor.left()
                 ledmod = 3
             elif kb == 'D' or kb == 'l':
                 print(" ** Right       **")
                 motor.right()
                 ledmod = 4
             elif kb == '2' or kb == 'o':
                 print(" ** Right turn  **")
                 motor.turn_right()
                 ledmod = 5
             elif kb == '1' or kb == 'u':
                 print(" ** Left turn   **")
                 motor.turn_left()
                 ledmod = 6
             elif kb == 's' or kb == 'k' or kb == ' ':
                 print(" !! Stop        !!")
                 motor.stop()
                 ledmod = 0
             else:
                 print("AiBot Standby OK !!!!")
             
         # LED mode set & wait 0.5sec
         if ledmod == 0:
             motor.led_off()
         elif ledmod == 1:
             motor.fwd_led()
         elif ledmod == 2:
             motor.back_led()
         elif ledmod == 3:
             motor.left_led()
         elif ledmod == 4:
             motor.right_led()
         elif ledmod == 5:
             motor.turn_right_led()
         elif ledmod == 6:
             motor.turn_left_led()
         elif ledmod == 9:
             break
         else:
             pass
         
     ledmod = 0
     print("Program Finished !!!")

** 自立走行の準備 [#m3601fab]
*** タクトスイッチの入力待ちプログラム [#dcd2e42a]
- 付属のタクトスイッチの入力待ちで停止し、LEDを点滅する。
- スイッチが押されたらLED を消してプログラム終了。

 ''wait.py''
 # -*- cording: utf-8 -*-
 
 import time
 import RPi.GPIO as GPIO
 
 LED = 16
 BUTTON  = 20
 
 GPIO.setwarnings(False)
 GPIO.setmode(GPIO.BCM)
 GPIO.setup(LED, GPIO.OUT)
 GPIO.setup(BUTTON, GPIO.IN)
 
 GPIO.add_event_detect(BUTTON, GPIO.FALLING)
 
 while True:
     if GPIO.event_detected(BUTTON):
         print ("Raspberry Pi Switch ON !!")
         break
     else:
         GPIO.output(LED, GPIO.HIGH)
         time.sleep(0.5)
         GPIO.output(LED, GPIO.LOW)
         time.sleep(0.5)
  
 GPIO.cleanup()
 print ("Start OK !!!")

*** 自動起動のためのシェルスクリプト [#o4eaef1e]
-「Raspberry Pi」の電源ON で起動し、タクトスイッチの入力待ちとなり、スイッチが押されるとデモアクションを実行し、タクトスイッチの入力待ちに戻る。
- デモアクションは基本ライブラリのコンソール入力機能を利用し、一通りの動作を実行して止まる。
-- 3秒間前進(左右LED点灯)
-- 3秒間後退(左右LED点滅)
-- 2秒間左前進(左LED点滅、右LED点灯)
-- 2秒間右前進(右LED点灯、左LED点滅)
-- 1.5秒間左回転(右LED点灯、左LED消灯)
-- 1.5秒間右回転(右LED消灯、左LED点灯)

 ''aibot_boot.sh''
 #!/bin/bash
 
 cd ~/Programs/aibot
 
 python wait.py
 
 
 echo "Motor Test Program Start..."
 python aibot.py forword 6
 python aibot.py back 6
 python aibot.py left 4
 python aibot.py right 4
 python aibot.py turn-left 3
 python aibot.py turn-right 3
 python motor3.py stop 1
 
 echo "Aibot Start..."
- 実行権限を付加する。

 $ chmod u+x aibot_boot,sh
- 実行してみる。

 $ ./aibot_boot.sh

*** 自動起動の設定 [#b39d8fe0]
 1. 自動起動ファイルを作成する。~

 ''aibot_boot.service''
 Description=Aibot boot
 [Service]
 ExecStart=/bin/bash /home/pi/Programs/aibot/aibot_boot.sh
 WorkingDirectory=/home/pi/Programs/aibot/
 Restart=always
 User=pi
 [Install]
 WantedBy=multi-user.target
 2. 自動起動用のサービスファイルをコピーする。~
 $ sudo cp aibot_boot.service /etc/systemd/system/
 3. systemdにユニットファイルを追加・更新したことを通知する~
 $ sudo systemctl daemon-reload
 4. 自動起動を登録する。~
 $ sudo systemctl enable aibot_boot.service
 $ sudo systemctl start aibot_boot.service
 5. 登録を確認する。~
 $ sudo systemctl status aibot_boot.service
  active となっていたら登録完了。

*** 自動起動したサービスの停止 [#af148f78]
-「Raspberry Pi」起動後、自動実行ファイルはタクトスイッチの入力待ちで実行中のまま。
- 停止するには
 $ sudo systemctl stop aibot_boot.service
- 停止を確認する。~
 $ sudo systemctl status aibot_boot.service

*** 自動起動したサービスの削除 [#na3b64ac]
- 自動起動を削除するには
 $ sudo systemctl stop aibot_boot.service
 $ sudo systemctl disable aibot_boot.service
 $ sudo systemctl status aibot_boot.service

** cronを使って起動時にプログラムを走らせる [#j14d5fc2]
- crontabを修正。(初回のみエディタの選択を要求される)
 $ crontab -e
 no crontab for pi - using an empty one
 
 Select an editor.  To change later, run 'select-editor'.
   1. /bin/nano        <---- easiest
   2. /usr/bin/vim.basic
   3. /usr/bin/vim.tiny
   4. /bin/ed
 
 Choose 1-4 [1]: 2
下記を追記する。
 @reboot                  /home/pi/Programs/aibot/aibot_boot.sh
起動時にaibot_boot.sh が実行される。

** Pythonでリアルタイムにキーボード入力を処理する方法 [#zecc8b23]
> Pythonではreadcharというライブラリを使う。

- インストール
 $ pip install readchar
- サンプルプログラム
 #!/usr/bin/env python
 import readchar
 import sys
 
 while 1:
     kb = readchar.readchar()
 #   sys.stdout.write(kb)
     print(ord(kb))
     if kb == 'q':
         print("")
         break
- IDE環境では実行できない
 Python error: (25, 'Inappropriate ioctl for device')

* 参考資料 [#c99761a3]
- [[Python: input from Keyboard without Enter>+http://web.sfc.keio.ac.jp/~yama/nos/archives/comp/soft/5326]]
-[[pythonでコンソールから1文字入力>+https://emotionexplorer.blog.fc2.com/blog-entry-126.html]]
- [[python3にもkbhit()とgetch()が欲しい>+https://westgate-lab.hatenablog.com/entry/2019/12/28/123451]]
- [[【Python】文字列と数値(asciiコード)の変換まとめ>+https://qiita.com/ell/items/6eb48e934a147898d823]]
- [[Python Snippets>+https://python.civic-apps.com/char-ord/]]
- [[Pythonでコマンドライン引数を受け取る>+https://qiita.com/taashi/items/07bf75201a074e208ae5]]
- [[ASCIIコード表>+http://www9.plala.or.jp/sgwr-t/c_sub/ascii.html]]
- [[Raspberry Piでプログラムを自動起動する5種類の方法を比較・解説>+https://qiita.com/karaage0703/items/ed18f318a1775b28eab4]]
#br