ラズパイにて、ラジコンカーを自作する－その３

# はじめに

ラズベリーパイにて、自作のラジコンカーをつくったので紹介します。
今回は、ネットワーク越しのラジコン制御になります。


![output_img_0008.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/559938/741b3a2a-e687-8f1a-83aa-993149d8b72c.gif)

## ラジコンカー関連の記事
- [ラズパイにて、ラジコンカーを自作する－その１](https://qiita.com/kotaproj/items/11d72758b9b21e453216)
- [ラズパイにて、ラジコンカーを自作する－その２](https://qiita.com/kotaproj/items/f1f93d598dbf99785bb4)
- ラズパイにて、ラジコンカーを自作する－その３　←　本記事


# 紹介する内容

- ラズパイ側のwebサーバ側の作成
- 外部PCからの制御
- iPhone(Pythonista)からの制御


# 作るものの全体

![rajikon-qiita3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/559938/601b8fbd-39a2-d9e8-5153-6dce4c85d047.png)

ラズパイ側のサーバーおよび外部制御プログラムです。


# 作業環境

- ラズベリーパイ3
    - Raspbian Buster with desktop and recommended software
        - Version:February 2020
        - Release date:2020-02-13
        - Kernel version:4.19
        - Python3.7.3
- ホストPC
    - windows10 64bit
        - Python3.7.4
- iPhone
    - Pythonista



# 作業手順

今回も、すべてPythonで実装します。
まずは、ラズパイ側のサーバを作ります。
※動く程度のものなので、書き方として間違っている場合があります


## ラズパイ側のサーバ - responder

Python WebFrameworkである[responder]を使用します。
responder自体の記事は、
[人間のためのイケてるPython WebFramework「responder」、そして作者のKenneth Reitzについて](https://blog.ikedaosushi.com/entry/2018/12/01/195512)
がすごくわかりやすいです。

注意点として、responderは、python3.6.1以上でないと動作しません。
Raspbianの古いものは、python3.5がインストールされていたかと思います。
必要に応じて、アップデートしてください。

### responderのインストール

```
$ pip install responder
```

以上。


### サーバー側の仕様

以下のような動作にします。

- 送信側
  - GETリクエスト : http://[ラズパイIPアドレス]:5042/pi_car/[動作コマンド]
- 受信側
  - レスポンス : "ok [動作コマンド]!"

具体例は、以下の通りです。

- 送信側
  - GETリクエスト : http://192.168.11.29:5042/pi_car/forward
- 受信側
  - レスポンス : "ok forward"
  - =>ラジコンカーが前進する

### サーバー側のコード

[前回記事](https://qiita.com/kotaproj/items/f1f93d598dbf99785bb4)のコードはそのまま使用します。
ファイル構成は、下記のとおりです。

```
$ ls
picar.py  rc_server.py  ta7291p.py
```

コードは以下となります。

ファイル名：rc_server.py

```
import responder
import picar

api = responder.API()
pi_car = picar.PiCar()

@api.route("/picar/{prm}")
def control_picar(req, resp, *, prm):

    if "turn_left" in prm:
        pi_car.turn_left()
    if "turn_right" in prm:
        pi_car.turn_right()
    if "forward" in prm:
        pi_car.forward()
    if "back" in prm:
        pi_car.back()
    if "brake" in prm:
        pi_car.brake()
    if "acceloff" in prm:
        pi_car.acceloff()
    if "debug_proc" in prm:
        pi_car.debug_proc()

    resp.text = f"ok, {prm}!"


@api.route("/picar/speed/{prm}")
def control_picar_speed(req, resp, *, prm):
    pi_car.speed(int(prm))
    resp.text = f"ok_speed, {prm}!"


if __name__ == '__main__':
    api.run(address='0.0.0.0', port=5042)

```

※address='0.0.0.0'は、外部アクセス可とするため


### 実行と動作確認

実行は下記のとおりです。

```
$ python rc_server.py 
INFO:     Started server process [3392]
INFO:     Uvicorn running on http://0.0.0.0:5042 (Press CTRL+C to quit)
INFO:     Waiting for application startup.
INFO:     Application startup complete.
```

外部PCより、GETリクエストをなげて確認します。
※本環境では、chromeを使用しました。

- 外部PC - 送信側
  - chromeのアドレスバーに以下を入力し、実行
  ```
  http://192.168.11.29:5042/picar/forward
  ```
  - →![302.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/559938/3dfb2cbd-4ca1-8ca0-15eb-f3e91db870ef.png)


- ラズパイ側
  - 下記のログが表示される
```
INFO:     192.168.11.17:63850 - "GET /picar/forward HTTP/1.1" 200 OK
forward : run
forward : over
```
  - →ラジコンカーが前進する

以上でサーバ側の動作は完了です。


## クライアント側(ホストPC)

GETのリクエストを投げるだけなので、さらっと。

### コード

以下がコードとなります。
※IPアドレスは必要に応じて変更してください。

ファイル名：car_test.py

```
import requests
import time

url = 'http://192.168.11.29:5042/picar/'
cmds = ["forward", "back", "brake", "turn_left", "turn_right", "acceloff"]

def main():
    for cmd in cmds:
        curl = url + cmd
        print(cmd)
        response = requests.get(curl)
        time.sleep(3)


if __name__ == '__main__':
    main()

```

### 実行

```
> python .\car_test.py
forward
back
brake
turn_left
turn_right
acceloff
```

前進→バック→ブレーキ→左旋回→右旋回→→停止
の順に動作したかと思います。


## クライアント側(iPhone)

せっかくラジコンをつくったので、やっぱり手元で手軽に動かしたいです。
今回は、iPhoneからPython経由で制御します。

### Pythonistaについて

Pythonista は、iOS上で動作するPythonの統合開発環境です。
有料アプリではありますが、とても気に入っています。
Pythonistaの詳細は、下記の記事が参考になります。

[iPhoneでiPhoneアプリを作ろう](https://qiita.com/taznica/items/34fb3395f085e5e11f6e)

AppStoreでのアイコンは以下です。

![302_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/559938/51f5f6fa-99f7-4391-c413-87ee68ff6da5.png)

### UIと動作仕様

UIは以下となります。
![303.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/559938/33d29121-2c9f-994a-71d7-0cc392899cf2.png)

- [forward]のボタンをタップ -> ラジコン前進
- [back]のボタンをタップ -> ラジコンバック
- [turn_left]のボタンをタップ -> ラジコン左旋回
- [turn_right]のボタンをタップ -> ラジコン右旋回
- [acceloff]のボタンをタップ -> ラジコン停止



早速コードに移ります。

### コード

以下がコードとなります。

ファイル名：car_remo.py

```
from objc_util import *
import ui
import console

import requests
import time

url = 'http://192.168.11.29:5042/picar/'

def btn_turn_left_tapped(sender):
    curl = url + "turn_left"
    response = requests.get(curl)

def btn_turn_right_tapped(sender):
    curl = url + "turn_right"
    response = requests.get(curl)

def btn_forward_tapped(sender):
    curl = url + "forward"
    response = requests.get(curl)

def btn_brake_tapped(sender):
    curl = url + "brake"
    response = requests.get(curl)

def btn_back_tapped(sender):
    curl = url + "back"
    response = requests.get(curl)

def btn_acceloff_tapped(sender):
    curl = url + "acceloff"
    response = requests.get(curl)


def main():
    # メイン画面の作成
    main_view = ui.View(frame=(0, 0, 375, 667))
    main_view.name = 'test view'
    main_view.background_color = ('white')

    width_unit = main_view.width // 4
    height_unit = main_view.width // 4

    # ボタンの追加
    btn_forward = ui.Button()
    btn_forward.frame = (1*width_unit, 0*height_unit, width_unit, height_unit)
    btn_forward.title = 'forward'
    btn_forward.action = btn_forward_tapped
    btn_forward.background_color = (0, 0, 0, 0.5)
    btn_forward.tint_color = ('white')
    main_view.add_subview(btn_forward)

    # ボタンの追加
    btn_acceloff = ui.Button()
    btn_acceloff.frame = (1*width_unit, 1*height_unit, width_unit, height_unit)
    btn_acceloff.title = 'acceloff'
    btn_acceloff.action = btn_acceloff_tapped
    btn_acceloff.background_color = (0, 0, 0, 0.5)
    btn_acceloff.tint_color = ('white')
    main_view.add_subview(btn_acceloff)

    # ボタンの追加
    btn_back = ui.Button()
    btn_back.frame = (1*width_unit, 2*height_unit, width_unit, height_unit)
    btn_back.title = 'back'
    btn_back.action = btn_back_tapped
    btn_back.background_color = (0, 0, 0, 0.5)
    btn_back.tint_color = ('white')
    main_view.add_subview(btn_back)

    # ボタンの追加
    btn_turn_left = ui.Button()
    btn_turn_left.frame = (0*width_unit, 1*height_unit, width_unit, height_unit)
    btn_turn_left.title = 'turn_left'
    btn_turn_left.action = btn_turn_left_tapped
    btn_turn_left.background_color = (0, 0, 0, 0.5)
    btn_turn_left.tint_color = ('white')
    main_view.add_subview(btn_turn_left)

    # ボタンの追加
    btn_turn_right = ui.Button()
    btn_turn_right.frame = (2*width_unit, 1*height_unit, width_unit, height_unit)
    btn_turn_right.title = 'turn_right'
    btn_turn_right.action = btn_turn_right_tapped
    btn_turn_right.background_color = (0, 0, 0, 0.5)
    btn_turn_right.tint_color = ('white')
    main_view.add_subview(btn_turn_right)

    main_view.present('sheet')
    main_view.wait_modal()

if __name__ == '__main__':
    main()
```


### 実行

iPhone - PythonistaのUIよりプログラムを実行します。

![305.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/559938/a6c8747b-48ad-b159-03ad-5ef854ef2d04.png)


UIが起動しますので、各ボタンをタップして確認します。
![303.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/559938/33d29121-2c9f-994a-71d7-0cc392899cf2.png)


# さいごに

とりあえず、イメージした動きになってよかったです。
余裕があれば、カメラを搭載して、自動で動作するようにしたいです。
あと、ラズパイゼロ or ESP32に置き換えて、完全コードレスにしようと思います。


# 参考

* [ラズパイで動くモノを作る](https://note.com/takasi7/n/ncfc48ab9b851)

* [人間のためのイケてるPython WebFramework「responder」、そして作者のKenneth Reitzについて](https://blog.ikedaosushi.com/entry/2018/12/01/195512)

* [iPhoneでiPhoneアプリを作ろう](https://qiita.com/taznica/items/34fb3395f085e5e11f6e)