Raspberry Pi 備忘録 / Mbedもあるよ!

Raspberry Pi であれこれやった事の記録

Raspberry pi 3 Model B+ を USB SSD から起動する

f:id:pongsuke:20180816150408j:plain

いつの間にか、USB boot に対応していたようです。知りませんでした。

公式のガイド

www.raspberrypi.org

Rapberry pi 3 と書いてありますが、 3 B+ でもできました。

ポイント

  1. 不可逆的な変更が加わる
    USB Boot は、ONにしたら、OFFには戻せない。

  2. ファームウェアがある程度新しくないとダメ

  3. OTP (One Time Programmable) memory を設定する
    /boot/config.txtprogram_usb_boot_mode=1 を書き足して、rebootし、usb boot を ON にする

  4. 外部ストレージに、データを一式コピーする

  5. boot の際に読みに行くストレージを手動で書き換えること。

その他をまとめて、記事にしてくださっています。

jyn.jp

ファン付きのケース

f:id:pongsuke:20180816142437j:plain

Raspberry pi 3 は、熱を持ちやすいです。B+ に成って、更に熱を出しやすく成った気がします。

せっかくなので、熱暴走を防ぐために、ファン付きのケースに入れてみました。

Amazon CAPTCHA

どこに配線するか、、、ですが、選択肢は3つ。

  1. 常時給電の 5.0v
  2. 常時給電の 3.3v
  3. GPIO

必要なアンペアだとか、抵抗を挟むのが必要なのか調べるのが面倒なので、常時給電 3.3v につなぎました。

外付け HDD/SSD ケース

最終的にはこれに落ち着きました。

https://www.amazon.co.jp/gp/product/B01A56QMIC/ref=oh_aui_detailpage_o01_s00?ie=UTF8&psc=1

給電は必須だと思います。

感想

やってみた実感としては、

出来上がったのは、安価で、物理的に壊れにくく、消費電力が少なく、GPIOがついている安物パソコンというイメージ。

RPIの弱点を克服できたと同時に、だんだんパソコンに近くなってきて、小ささやファンレスというメリトッが消えてしまった。

けど、不思議な満足感がある。

次は?

POE HAT が間もなく売り出されるようなので、手に入ったらとにかく試してみたい。

www.raspberrypi.org

POE対応のルーターは、すでに購入済み!

Raspberry pi Zero で オフィス環境の可視化 その5 AM2322 に交換

AM2320 もすぐに停止するので、 AM2322 に交換してみる。

f:id:pongsuke:20180219111131j:plain

WiringPi アップデート

久しぶりにログインしたので、

$ sudo apt-get update
$ sudo apt-get upgrade

してみたら、WiringPi がエラーを出して動かなかった。 不用意なアップデートは、本当に危険。

Unable to determine hardware version. I see: Hardware : BCM2835

最新バージョンの WiringPi をコンパイルし直したところ、正常に動きました。

$ git clone git://git.drogon.net/wiringPi
$ cd wiringPi
$ ./build

配線

f:id:pongsuke:20180219110941p:plain

・・・の前に、 ピンは1.27ミリピッチです。 とのことなので、ブレッドボードにさせるように、ピッチ変換基板を利用します。

SOP8(1.27mm)DIP変換基板 金フラッシュ (9枚入): パーツ一般 秋月電子通商 電子部品 ネット通販

f:id:pongsuke:20180219143034j:plain

f:id:pongsuke:20180228112008p:plainf:id:pongsuke:20180228112011j:plain

どうにかはんだ付けしましたが、汚い・・・。

足も切りすぎました。

pigpio をセットアップする

sudo apt-get install pigpio python-pigpio python3-pigpio

コーディング

こちらを使用しました。

rpi-location-monitor/AM2322.py at master · smartperson/rpi-location-monitor · GitHub

ただ、interface は 0 では無く、1 にしました。

$ ./AM2322.pigpio.py 
20.6 66.6
20.7 66.5
20.7 66.5
20.7 66.5
20.7 66.5
20.7 66.5
20.7 66.5

今度は、止まらないと良いのですが、、、。

FLiR Dev Kit を試す その5

せっかくなので、ストリーミングしたい。

f:id:pongsuke:20171211181007p:plain

そこで、今までは Mjepg-streamer などを使ってきましたが、自分でゴリゴリいじるので、Python で実装しているサンプルを探したところ、有った!

Simple Python Motion Jpeg (mjpeg server) from webcam. Using: OpenCV,BaseHTTPServer · GitHub

まずは、USBカメラなどをさして、ストリームのチェックを行う。

おっけー

f:id:pongsuke:20171211175152p:plain

Lepton 向けに改変する

これは、 Mr Igor のスクリプトを書き換えています。

Simple Python Motion Jpeg (mjpeg server) from webcam. Using: OpenCV,BaseHTTPServer · GitHub

これね。

#!/usr/bin/python
from pylepton import Lepton

import cv2
import numpy as np
from PIL import Image
import threading
from BaseHTTPServer import BaseHTTPRequestHandler,HTTPServer
from SocketServer import ThreadingMixIn
import StringIO
import time

class CamHandler(BaseHTTPRequestHandler):
        def do_GET(self):
                if self.path.endswith('.mjpg'):
                        self.send_response(200)
                        self.send_header('Content-type','multipart/x-mixed-replace; boundary=--jpgboundary')
                        self.end_headers()
                        try:
                                with Lepton() as l:
                                        while True:
                                                data_buffer, rc = l.capture()
                                                if not rc:
                                                        continue

                                                cv2.normalize(data_buffer, data_buffer, 0, 255, cv2.NORM_MINMAX)
                                                img = cv2.cvtColor(data_buffer, cv2.COLOR_GRAY2BGR)
                                                img[:, :, 0:2] = 0
                                                img = cv2.resize(img, (320,240))

                                                imgRGB=cv2.cvtColor(img,cv2.COLOR_BGR2RGB)
                                                jpg = Image.fromarray(np.uint8(np.asarray(imgRGB)))
                                                tmpFile = StringIO.StringIO()
                                                jpg.save(tmpFile,'JPEG')
                                                self.wfile.write("--jpgboundary")
                                                self.send_header('Content-type','image/jpeg')
                                                self.send_header('Content-length',str(tmpFile.len))
                                                self.end_headers()
                                                jpg.save(self.wfile,'JPEG')
                                                time.sleep(0.2)

                        except Exception as e:
                                print('Exception', e.args);
                                brak

                if self.path.endswith('.html'):
                        self.send_response(200)
                        self.send_header('Content-type','text/html')
                        self.end_headers()
                        self.wfile.write('<html><head></head><body>')
                        self.wfile.write('<img src="http://127.0.0.1:8080/cam.mjpg"/>')
                        self.wfile.write('</body></html>')
                        return


class ThreadedHTTPServer(ThreadingMixIn, HTTPServer):
        """Handle requests in a separate thread."""

def main():
        global img
        try:
                server = ThreadedHTTPServer(('localhost', 8080), CamHandler)
                print "server started"
                server.serve_forever()
        except KeyboardInterrupt:
                server.socket.close()

if __name__ == '__main__':
        main()

結果

f:id:pongsuke:20171211181007p:plain

TJBot

ケース

Watson Maker Kits

形状データをダウンロードできる。

とりあえず、プリントしてみよう。

3Dプリンター不調で頓挫・・・しましたが、調整し直してプリントしました。 f:id:pongsuke:20180223130002j:plain パーツ

f:id:pongsuke:20180223132841j:plain 組んで

f:id:pongsuke:20180223132850j:plain 組んで

f:id:pongsuke:20180223132859j:plain こうなる

ケルトンだと、ターミネーターみたいで、なんか怖い。
特に目が!

カメラ

カメラの向き

www.youtube.com

youtube を見る限り、そして、実際にはめ込んでみたぴったり感からして、どうやら上下逆向きに(ケーブルを上向きに)設置するようだ。

逆向きに刺そうとすると、どうしてもリボンケーブルのコネクタが鑑賞して刺さらないのだ。

なので、撮影の際などには、-vf, --vflip : Set vertical flip オプションを付ける。

カメラの動作確認

$ raspistill -w 480 -h 360 -n -vf -o mod.jpg

f:id:pongsuke:20180223181149j:plain

サーボモーター(腕)

とりあえず動かしてみよう。

以前書いたコードをそのまま使ってみた。

SG92.py

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

import RPi.GPIO as GPIO
import time
import signal
import sys 

gp_out = 21

def exit_handler(signal, frame):
        print("\nExit")
        servo.ChangeDutyCycle(2.5)
        time.sleep(0.5)
        servo.stop()
        GPIO.cleanup()
        sys.exit(0)

signal.signal(signal.SIGINT, exit_handler)

GPIO.setmode(GPIO.BCM)


GPIO.setup(gp_out, GPIO.OUT)
# pwm = GPIO.PWM([チャンネル], [周波数(Hz)])
servo = GPIO.PWM(gp_out, 50) 

servo.start(0.0)

val = [2.5,3.6875,4.875,6.0625,7.25,8.4375,9.625,10.8125,12]

servo.ChangeDutyCycle(val[0])
time.sleep(0.5)
servo.ChangeDutyCycle(val[8])
time.sleep(0.5)
servo.ChangeDutyCycle(val[0])
time.sleep(0.5)

while True:
        for i, dc in enumerate(val):
                servo.ChangeDutyCycle(dc)
                print("Angle:" + str(i*22.5)+"  dc = %.4f" % dc) 
                time.sleep(0.5)
        for i, dc in enumerate( reversed(val) ):
                servo.ChangeDutyCycle(dc)
                print("Angle:" + str(180 - i*22.5)+"  dc = %.4f" % dc) 
                time.sleep(0.5)

フルカラーLED

フルカラーLEDを触るのが今回が初めてです。

手元にあるのは カソードコモン のフルカラーLEDです。

f:id:pongsuke:20180228103909p:plain

左から、緑、青、共通カソード、赤 ですね。

挿して、520Ωの抵抗を付けました。

f:id:pongsuke:20180228111311j:plain

f:id:pongsuke:20180228111318j:plain

グラデーションさせるテスト

https://making.mrlittlebig.com/raspberry-pi-2-フルカラーledをグラデーション-5e4916fda419

こちらのコードを、中断できるように改変させていただきました。

#!/usr/bin/python
# coding: utf-8
import RPi.GPIO as GPIO
import time
import signal
import sys

def exit_handler(signal, frame):
        print("\nExit")
        red.stop()
        green.stop()
        blue.stop()
        time.sleep(0.5)
        GPIO.cleanup()
        sys.exit(0)

signal.signal(signal.SIGINT, exit_handler)

Rpin = 13
Bpin = 19
Gpin = 26

GPIO.setmode(GPIO.BCM)
GPIO.setup(Rpin,GPIO.OUT)
GPIO.setup(Bpin,GPIO.OUT)
GPIO.setup(Gpin,GPIO.OUT)

red = GPIO.PWM(Rpin, 50)
green = GPIO.PWM(Gpin, 50)
blue = GPIO.PWM(Bpin, 50)

red.start(0)
green.start(0)
blue.start(0)

blue.ChangeDutyCycle(100)

COUNT = 100
T = 0.04
I = 0
time.sleep(0.5)
for _ in xrange(0, COUNT):
        red.ChangeDutyCycle(I)
        blue.ChangeDutyCycle(100 - I)
        time.sleep(T)
        I+=1
I = 0
for _ in xrange(0, COUNT):
        red.ChangeDutyCycle(100-I)
        green.ChangeDutyCycle(I)
        I+=1
        time.sleep(T)
I = 0
for _ in xrange(0, COUNT):
        green.ChangeDutyCycle(100 - I)
        blue.ChangeDutyCycle(I)
        I+=1
        time.sleep(T)

time.sleep(0.5)
red.stop()
green.stop()
blue.stop()

GPIO.cleanup()

拡散キャップをかぶせました。

マイク

$ lsusb

$ lsusb 
Bus 001 Device 004: ID 8086:0808 Intel Corp. 
Bus 001 Device 003: ID 0424:ec00 Standard Microsystems Corp. SMSC9512/9514 Fast Ethernet Adapter
Bus 001 Device 002: ID 0424:9514 Standard Microsystems Corp. SMC9514 Hub
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub

Intel Corp とのだけ認識されているけど、大丈夫かな?

$ arecord -l
**** ハードウェアデバイス CAPTURE のリスト ****
カード 1: Device [USB PnP Sound Device], デバイス 0: USB Audio [USB Audio]
  サブデバイス: 1/1
  サブデバイス #0: subdevice #0

録音テスト

$ arecord -D plughw:1,0 -d 5 test.wav
録音中 WAVE 'test.wav' : Unsigned 8 bit, レート 8000 Hz, モノラル

kiyo@raspberrypi:~ $ aplay -D plughw:0,0 test.wav 
再生中 WAVE 'test.wav' : Unsigned 8 bit, レート 8000 Hz, モノラル

なお、スピーカーが片方しかない状態(アナログout の2局のうち片方しか配線して居ない状態)なので、モノラルで進めています。

FLiR Dev Kit を試す その4

画像を表示するだけなら今のままでも良いのですが、温度の算出をしたいので、Output frame に触れましょう。

触れたあとに、加工して、最後には画像にしたい、、、ので、Python + Opencv がお手軽ではないかと思っていたところ、素敵なサンプルコードを発見しました。

Python から キャプチャするコード

GitHub - groupgets/pylepton: Quick and dirty pure python library for interfacing with FLIR lepton

サンプルコード

import numpy as np
import cv2
from pylepton import Lepton

with Lepton() as l:
  a,_ = l.capture()
cv2.normalize(a, a, 0, 65535, cv2.NORM_MINMAX) # extend contrast
np.right_shift(a, 8, a) # fit data into 8 bits
cv2.imwrite("output.jpg", np.uint8(a)) # write it!

Lepton() で何が手に入って、Lepton.capture() で何が返ってくるのかを見ましょう。

  def capture(self, data_buffer = None, log_time = False, debug_print = False, retry_reset = True):
    """Capture a frame of data.
    Captures 80x60 uint16 array of non-normalized (raw 12-bit) data. Returns that frame and a frame_id (which
    is currently just the sum of all pixels). The Lepton will return multiple, identical frames at a rate of up
    to ~27 Hz, with unique frames at only ~9 Hz, so the frame_id can help you from doing additional work
    processing duplicate frames.
    Args:
      data_buffer (numpy.ndarray): Optional. If specified, should be ``(60,80,1)`` with `dtype`=``numpy.uint16``.
    Returns:
      tuple consisting of (data_buffer, frame_id)
    """

読みやすく改変

001.py

#!/usr/bin/env python
# -*- coding: utf-8 -*
import sys
import copy
import numpy as np
import cv2
from pylepton import Lepton

def main():
        with Lepton() as l:
                data_buffer, frame_id = l.capture()

        print("---data_buffer---")
        print(data_buffer[59:, 75:])
        print(data_buffer.shape)
        print(type(data_buffer))
        data_1d = data_buffer.ravel()
        print( 'MAX:%d, MIN:%d' % (np.max(data_1d), np.min(data_1d) ))

        # 変数のコピー
        data_8bit       = copy.deepcopy(data_buffer)
        data_normalized = copy.deepcopy(data_buffer)

        # data_normalized を正規化
        cv2.normalize(data_buffer, data_normalized, 0, 65535, cv2.NORM_MINMAX) # extend contrast
        # fit to 8 bit
        np.right_shift(data_8bit, 8, data_8bit)
        np.right_shift(data_normalized, 8, data_normalized)

        print("----data_8bit----")
        print(data_8bit[59:, 75:])
        print(data_8bit.shape)
        data_1d = data_8bit.ravel()
        print( 'MAX:%d, MIN:%d' % (np.max(data_1d), np.min(data_1d) ))

        print("---data_normalized---")
        print(data_normalized[59:, 75:])
        print(data_normalized.shape)
        data_1d = data_normalized.ravel()
        print( 'MAX:%d, MIN:%d' % (np.max(data_1d), np.min(data_1d) ))

        image_original  = np.uint8(data_8bit)
        image_gray              = np.uint8(data_normalized)
        image_rgb               = cv2.cvtColor(image_gray, cv2.COLOR_GRAY2BGR)
        image_org_rgb   = cv2.cvtColor(image_original, cv2.COLOR_GRAY2BGR)

        image_rgb[:, :, 0:2] = 0
        image_org_rgb[:, :, 0:2] = 0

        print("---image_rgb---")
        print(image_rgb[59:, 75:])
        print(image_rgb.shape)

        print("---image_org_rgb---")
        print(image_org_rgb[59:, 75:])
        print(image_org_rgb.shape)

        # リサイズ
        image_original  = cv2.resize(image_original, None, fx=4, fy=4)
        image_gray              = cv2.resize(image_gray, None, fx=4, fy=4)
        image_rgb               = cv2.resize(image_rgb, None, fx=4, fy=4)
        image_org_rgb   = cv2.resize(image_org_rgb, None, fx=4, fy=4)

        cv2.imshow("Leption original", image_original)
        cv2.imshow("Leption normalize", image_gray)
        cv2.imshow("Leption RGB", image_rgb)
        cv2.imshow("Leption org RGB", image_org_rgb)

        cv2.waitKey(0)
        cv2.destroyAllWindows()

if __name__ == '__main__':
        main()

やってみる。

$ ./001.py 
---data_buffer---
[[[7915]
  [7916]
  [7919]
  [7908]
  [7904]]]
(60, 80, 1)
<type 'numpy.ndarray'>
MAX:8327, MIN:7671
----data_8bit----
[[[30]
  [30]
  [30]
  [30]
  [30]]]
(60, 80, 1)
MAX:32, MIN:29
---data_normalized---
[[[95]
  [95]
  [96]
  [92]
  [90]]]
(60, 80, 1)
MAX:255, MIN:0
---image_rgb---
[[[ 0  0 95]
  [ 0  0 95]
  [ 0  0 96]
  [ 0  0 92]
  [ 0  0 90]]]
(60, 80, 3)
---image_org_rgb---
[[[ 0  0 30]
  [ 0  0 30]
  [ 0  0 30]
  [ 0  0 30]
  [ 0  0 30]]]
(60, 80, 3)

f:id:pongsuke:20171208152356p:plain

f:id:pongsuke:20171208152401p:plain

f:id:pongsuke:20171208152410p:plain

f:id:pongsuke:20171208152415p:plain

たとえば、 output frame は 7904 でした。

0.026 * ( 7903 - 8192 ) + カメラの温度 = -7.488度 + カメラの温度

ってことかな?

正規化しないと、最小値が29で、最大値が32 なので、画像とはなりえない。ほぼ一色。
どこかのフォーラムにも書いてあったけど、範囲を決めて(摂氏 -20 ~ +100度など?)、その範囲外は 0 と 255 とみなして、120度の中範囲で 255 を使用したほうが良いかもしれない。
-20度 と +100度 における正規化 かな?

そうすれば、画像として見られたものに成って、かつ、出番の多そうな温度も拾える、、、かな?

やってみないとわからない。

-10度 +80度 を入れてみる

やってみた

強引ですが、output buffer を書き換えて、常に -10 と +80 が登場するようにして、それを 0~255 で正規化してみました。

左上の白と右上の黒が、それです。

#!/usr/bin/env python
# -*- coding: utf-8 -*
import sys
import copy
import numpy as np
import cv2
from pylepton import Lepton

def main():
        with Lepton() as l:
                data_buffer, frame_id = l.capture()

        print("---data_buffer---")
        print(data_buffer[59:, 75:])
        print(data_buffer.shape)
        print(type(data_buffer))
        data_1d = data_buffer.ravel()
        print( 'MAX:%d, MIN:%d' % (np.max(data_1d), np.min(data_1d) ))

        # 変数のコピー
        data_8bit       = copy.deepcopy(data_buffer)
        data_normalized = copy.deepcopy(data_buffer)

        # -10 +80 で上書き:基準値は 8192
        data_8bit[0,0,0]        = 7807 # -10度
        data_8bit[59,79,0]      = 11268 # +80度

        # 正規化
        cv2.normalize(data_8bit, data_8bit, 0, 255, cv2.NORM_MINMAX) # extend contrast
        cv2.normalize(data_normalized, data_normalized, 0, 255, cv2.NORM_MINMAX) # extend contrast

        print("----data_8bit----")
        print(data_8bit[59:, 75:])
        print(data_8bit.shape)
        data_1d = data_8bit.ravel()
        print( 'MAX:%d, MIN:%d' % (np.max(data_1d), np.min(data_1d) ))

        print("---data_normalized---")
        print(data_normalized[59:, 75:])
        print(data_normalized.shape)
        data_1d = data_normalized.ravel()
        print( 'MAX:%d, MIN:%d' % (np.max(data_1d), np.min(data_1d) ))

        image_original  = np.uint8(data_8bit)
        image_gray              = np.uint8(data_normalized)
        image_bgr               = cv2.cvtColor(image_gray, cv2.COLOR_GRAY2BGR)
        image_org_bgr   = cv2.cvtColor(image_original, cv2.COLOR_GRAY2BGR)

        image_bgr[:, :, 0:2] = 0
        image_org_bgr[:, :, 0:2] = 0

        print("---image_bgr---")
        print(image_bgr[59:, 75:])
        print(image_bgr.shape)

        print("---image_org_bgr---")
        print(image_org_bgr[59:, 75:])
        print(image_org_bgr.shape)

        # リサイズ
        image_original  = cv2.resize(image_original, None, fx=4, fy=4)
        image_gray              = cv2.resize(image_gray, None, fx=4, fy=4)
        image_bgr               = cv2.resize(image_bgr, None, fx=4, fy=4)
        image_org_bgr   = cv2.resize(image_org_bgr, None, fx=4, fy=4)

        cv2.imshow("Leption original", image_original)
        cv2.imshow("Leption normalize", image_gray)
        cv2.imshow("Leption RGB", image_bgr)
        cv2.imshow("Leption org RGB", image_org_bgr)
        # 保存
        cv2.imwrite("001.image_original.png", image_original)
        cv2.imwrite("001.image_gray.png", image_gray)
        cv2.imwrite("001.image_bgr.png", image_bgr)
        cv2.imwrite("001.image_org_bgr.png", image_org_bgr)

        cv2.waitKey(0)
        cv2.destroyAllWindows()

if __name__ == '__main__':
        main()

最初から 255 で正規化しました。

f:id:pongsuke:20171208161207p:plain

f:id:pongsuke:20171208161212p:plain

この2つの画像では、真っ赤(右下)なら、カメラより+80度高く、真っ黒(左上)なら、カメラより10度低い事になります。

映像としては、面白みが無い。

熱湯が入ったカップを持って、撮影してみました。

室温は20度なので、おそらくカメラも20度前後。
お湯は電気ポットから出したばかりです。

f:id:pongsuke:20171208162047p:plain

f:id:pongsuke:20171208162042p:plain

Output buffer の最大値は 9664 だった。おそらくこれはコップ。

0.026 * (9664 - 8192) = 38.272

室温の20を足すと、 だいたい60度?

こうなってきたら、もうちょっと正確に温度を用意して、キャリブレーション(傾きを算出)してみたくもなる。

でも、正確に温度のわかっている物体って、どこにあるのだろか。

修正

cv2.COLOR_GRAY2RGB を指定しても、出力が BGR に成っていたので、 cv2.COLOR_GRAY2BGR に直しました。