技術をかじる猫

適当に気になった技術や言語、思ったこと考えた事など。

Circuit Playground Express の各種機能を試す(3)

温度センサーを利用する

利用するライブラリは adafruit_thermistor.mpyでこれを lib に放り込む。

f:id:white-azalea:20200706192012p:plain

で、コードをしれっと

import time
import adafruit_thermistor
import board

thermo = adafruit_thermistor.Thermistor(board.TEMPERATURE, 10000, 10000, 25, 3950)
while True:
    temp = thermo.temperature
    print((temp,))
    time.sleep(1)

実行するとこんなかんじ

(27.9365,)
(27.9139,)
(27.959,)
(28.004,)
(27.8915,)
(27.959,)
(27.9139,)
(27.9139,)

大体 28 度位なのだが、これを手のひらで包むと

(29.4573,)
(29.5717,)
(29.7322,)
(29.7092,)
(29.824,)
(29.939,)
(30.0081,)
(30.1693,)
(30.2384,)
(30.2616,)

単位が 度というのが助かりますね。
海外だと単位が華氏で有ることも多いので…

他のライブラリを利用してみる

circuit playground のアクセスをもっと単純化したライブラリ adafruit_circuitplayground が存在したので使ってみた。

f:id:white-azalea:20200706193615p:plain

コードは

import time
from adafruit_circuitplayground import cp

while True:
    print("Temperature C:", cp.temperature)
    time.sleep(1)

うーんシンプル。

Temperature C: 27.3748
Temperature C: 27.3748
Temperature C: 27.3972
Temperature C: 27.4197
Temperature C: 27.4197
Temperature C: 27.4197

このライブラリを利用して温度を視覚化しようか

温度の視覚化

温度で LED 光らせてみようか。

import time
from adafruit_circuitplayground import cp

# LED の明るさ設定
cp.pixels.auto_write = False
cp.pixels.brightness = 0.3

# 25-30 度の間で 10 段階表示
minimum_temp = 25
maximum_temp = 30

# 平たく言えば、最小温度から最大温度までの比率を 10 段階計算
#まぁ、25 度下回ればマイナスにもなるし、30 度超えれば 10 以上になるが、ループとしては特に問題ない
def scale_range(value):
    return int((value - minimum_temp) / (maximum_temp - minimum_temp) * 10)

while True:
    peak = scale_range(cp.temperature)

    # 10 個ある LED を範囲内で発光させる
    for i in range(10):
        if i <= peak:
            cp.pixels[i] = (255, 255, 255)
        else:
            cp.pixels[i] = (0, 0, 0)

    # 発光!
    cp.pixels.show()
    time.sleep(0.5)

ということで、30 度でマックス、25 度からおよそ 0.5 度刻みで光らせるコードを書いた。
その様子がコレである。

f:id:white-azalea:20200706195029j:plain

ちなみに起動してると、次第に基盤自体に熱を持つので、27 度の室温なら気づくと 30 度とかになる…

Circuit Playground Express の各種機能を試す(2)

環境::

  • CircuitPlaygroundExpress : Bootloader 3.10.0
  • CircuitPython : 5.0

ライブラリで色彩/明度/彩度 を扱う

どちらかといえば CircuitPython の機能で、 ライブラリ からコピーして使う。

f:id:white-azalea:20200630194834p:plain

全部のライブラリをコピーはできないので、今回使うものだけ。
この上で

import time
import board
import neopixel
import adafruit_fancyled.adafruit_fancyled as fancy

npx = neopixel.NeoPixel(board.NEOPIXEL, 10, auto_write=False)

while True:
    for pp in range(10):
        h = (pp * (1.0/10))
        hsvcolor = fancy.CHSV(h, 1.0, 0.2)
        npx[pp] = hsvcolor.pack()
        npx.show()
        time.sleep(0.2)

すると

f:id:white-azalea:20200630194354j:plain

綺麗なグラデーションですね…

光学センサーで明るさ計測

import time
import analogio
import board

light = analogio.AnalogIn(board.LIGHT)

while True:
    print((light.value,))
    time.sleep(1.0)

としたとき部屋の蛍光灯でこんな感じです

(3168,)  # 蛍光灯の光
(3136,)
(800,)  # 手で隠した
(720,)
(720,)

タッチセンサー

タッチセンサーによって LED の色を変動させるプログラム

タッチセンサーは周辺の穴の空いた端子ですが、今回 A1, A2, A5, A6 を使います。
以下の写真にそれぞれの配置がプリントされてます。

f:id:white-azalea:20200630202607j:plain

A シリーズはすべて操作可能なのはわかってましたが、RGB いじりたいだけでしたので今回は避けました。

import time
import board
import neopixel
import touchio
from simpleio import map_range

# LED セットアップ
npx = neopixel.NeoPixel(board.NEOPIXEL, 10, auto_write=True)

# タッチセンサーを設定する
touch_A1 = touchio.TouchIn(board.A1)
touch_A2 = touchio.TouchIn(board.A2)
touch_A5 = touchio.TouchIn(board.A5)
touch_A6 = touchio.TouchIn(board.A6)

# 色のセット
r_in = 0
g_in = 0
b_in = 0

while True:
    # タッチするセンサーによって RGB を決定
    if touch_A1.value:
        r_in += 1
    if touch_A2.value:
        g_in += 1
    if touch_A5.value:
        b_in += 1
    if touch_A6.value:
        r_in -= 1
        g_in -= 1
        b_in -= 1
    
    # 0 - 255 までに抑える
    r_in = 0 if r_in < 0 else r_in
    g_in = 0 if g_in < 0 else g_in
    b_in = 0 if b_in < 0 else b_in
    r_in = 255 if r_in > 255 else r_in
    g_in = 255 if g_in > 255 else g_in
    b_in = 255 if b_in > 255 else b_in

    # LED に色を書き込む
    npx[7] = (r_in, g_in, b_in)
    time.sleep(0.2)

でちょっとやってみたのがこの 2 枚

f:id:white-azalea:20200630203107j:plainf:id:white-azalea:20200630203122j:plain

タッチ時間によって色が変化します。

Circuit Playground Express の各種機能を試す

環境:

  • Circuit Playground Express (Bootloader3.10.0)
  • Circuit Python v5

ボタン AB 認識

import board
import time
from digitalio import DigitalInOut, Direction, Pull

# LED definition
led = DigitalInOut(board.D13)
led.direction = Direction.OUTPUT

# A button definition
ButtonA = DigitalInOut(board.BUTTON_A)
ButtonA.direction = Direction.INPUT
ButtonA.pull = Pull.DOWN # Dont push value define.

# B BUTTON DEFINITION
ButtonB = DigitalInOut(board.BUTTON_B)
ButtonB.direction = Direction.INPUT
ButtonB.pull = Pull.DOWN

# Button change delay
delay = 0.1

# Execution loops.
while True:
    delay = 0.05 if ButtonA.value else 0.1
    delay = delay * 10 if ButtonB.value else delay
    led.value = True
    time.sleep(delay)
    led.value = False
    time.sleep(delay)

やってみると分かるけど、ボタンを押している間だけ LED の点滅速度が変化するやつ。
CircuitPython で利用できる基本 APIこのへん に落ちてた。
うんドキュメント、ないね☆

カラー LED を光らせる(簡易イルミネーション)

import time
import board
import neopixel

# neopixcel って LED の名前らしい。てか Pixcel というだけあって、RGB カラーで光らせられる
npx = neopixel.NeoPixel(board.NEOPIXEL, 10, auto_write=False)
loop = 0

# カラーテーブルを定義して
colors = [
    (128, 0, 128),
    (255, 0, 0),
    (128, 128, 0),
    (0, 255, 0),
    (0, 128, 128),
    (0, 0, 255)
]

while True:
    # カラーテーブルの順序に従って色を変える
    # 光らせる順序は反時計
    loop += 1
    loop = 0 if loop >= len(colors) else loop
    col = colors[loop]

    for pp in range(10):
        npx[pp] = col # RGB で書き込む
        npx.show()
        time.sleep(0.2)

f:id:white-azalea:20200630190229j:plain

Circuit Playground Express をいじったログ

一通り機能を触ったので、紹介記事と、初期セットアップだけ。

経緯

買って試したのはコレ

Circuit Playground Express

Circuit Playground Express

  • メディア: おもちゃ&ホビー

当初、IoT として一度ブームになったときは、RasberryPi がやたらと人気で、記事は実質それ一色だったのを覚えてる。
ただ、買って弄ってわかったことは、あれは規模の小さい PC であったという事実だ。
それで何が言いたいかって言うと

ハード設計できない人はお呼びでない ということだった。
もともと Web 屋さんで、ハードなんて大学でマイコンアセンブラ書き込んでLED と高額センサーで信号通信してみた位しかやったことがない。
まして就職して 当時 8 年目位で、今更回路設計しろって言われても無茶振りに近かった…要するにそんな余裕も金もなくて挫折した(汗
(Web屋さん業界じゃ HTML5ecmascript や各種フレームワーク打なんだで群雄割拠ヒャッハーしてたり AI たのすぃーとかいってフラフラしてた)

で、しばらく遠のいてたところを、知人にこんな本紹介されて

booth.pm

え?マジで?と調べて驚いた。

Circuit Playground Express とは

平たくいえば、ネットワーク危機こそないけど、ジャイロ、温度、音声、スピーカー、LED、タッチセンサー、赤外線と、コミコミ全盛り回路セットである。
要するに、多くのソフトやさんなプログラマが挫折するだろう箇所を全盛りで用意してくれてるという一品。

コレは軽くチートじゃないんですかね…(汗

開発準備

Bootloader

USB で繋いで、中央のスイッチを押すと USB 認識するので、ブートローダのアップデートを行う。

Mac でやってたので https://learn.adafruit.com/adafruit-circuit-playground-express/updating-the-bootloader こっからダウンロードして更新した。
update-bootloader-circuitplay_m0-vX.XX.X.uf2 を finder にドロップするだけで、中にある INFO_UF2.TXT の中に記述されるバージョンなどが上がった。
バイス的にもこの辺を事前サポートしてるのか…ううむ…。

この記事時点では v3.10.0 が最新だった。

CircuitPython

circuitpython.org

ここから CircuitPython をダウンロード & インストール。
ここでは 5.3.0 の英語版を選択。
ライブラリも Libraries から入手した。

日本語なんてドマイナー言語対応してないのはある意味仕方ないよな(汗

で、ここで気をつけるべきは、全部のライブラリは容量的にはいらないということ…
なので、 Python コード上使用しているものだけ選択して入れる必要があるという点。

ここで全パターンとライブラリ紹介始めると、時間がいくらあっても足らないので省略。

最も単純な LED 点灯

といっても写経だったりするのだが

f:id:white-azalea:20200628203254p:plain

ここの code.py がエントリポイントになってるようなので、コレを弄る。

import board
import time
from digitalio import DigitalInOut, Direction

led = DigitalInOut(board.D13)
led.direction = Direction.OUTPUT

while True:
    led.value = True
    time.sleep(0.1)
    led.value = False
    time.sleep(0.1)

色々疑問なので、import してるやつを追いかける。
こういう調査がないと応用効かない…

まず、circuit python の公式リポジトリ

GitHub - adafruit/circuitpython: CircuitPython - a Python implementation for teaching coding with microcontrollers

で、公式ドキュメント漁ると発見 https://learn.adafruit.com/welcome-to-circuitpython/creating-and-editing-code コレか。

で、やっぱそうなのかという話で、 board, time, digitalio組み込みライブラリ であるそうな。

  • board gives you access to the hardware on your board (ボードはボードに存在するハードウェアへのアクセスを提供します) *digitalio lets you access that hardware as inputs/outputs (digitalio はハードウェアへ Input/Output を提供します)

ナルホド、で、なんで D13 なん?って思ったらあっさりわかった。

f:id:white-azalea:20200628205651p:plain

あーうんなるほど…既にそこそこ抽象化してるのね(汗
じゃー何がビルトインされてんのさ?って思ったら

https://learn.adafruit.com/adafruit-circuit-playground-express/circuitpython-built-ins https://learn.adafruit.com/adafruit-circuit-playground-express/circuitpython-digital-in-out

ふむふむ。

で、 Circuit Python のライブラリには何あんのさ?って思って調べたら

circuitpython.readthedocs.io

しかもハードごとに違うってんだからコレは一瞬「うぉっ」となるよね(汗

デバッグ環境を整える

で、これなにかバグった時にデバッグ出力得られないと泣けるので

https://learn.adafruit.com/welcome-to-circuitpython/kattni-connecting-to-the-serial-console#using-something-else-2978927-11

この辺を利用する。

何が泣けるって、ここで推奨してる Mu Editor が Mac 10.15.5 で起動しないもの…セキュリティは分かるがちっとヒステリックになってきてないかと思わなくない。
時期バージョンで Arm 移行もあるし、結局「開発者<消費者」ルートで開発者を置いてけぼりにするんかね… Windows もアレだし、Ubuntu に戻ろうかな…。

現在のシリアルコネクションを確認

f:id:white-azalea:20200628210739p:plain

USB 接続なのは確実なので、/dev/tty.usbmodem144101 が結局デバイスだろうね。
てことで screen /dev/tty.usbmodem144101 115200 と叩く

f:id:white-azalea:20200628211448p:plain

これで画面が真っ黒になるので、コード中にデバッグ文字列を挿入。

f:id:white-azalea:20200628211804p:plain

やってみれば分かるが、デバイスに保存した次の瞬間には適用されてる。
CPU は 48MHz なので、Windows95 黎明期?逆に言えばそれくらいのスペックがこんなボードに乗るようになったんだなーと実感(汗

メモリが 2MB しかないというのも割とネックではあるので、ラズパイと組み合わせてやるとネットワーク連携もできて幸せになれそうな気がするよ。

機械学習で使う数学系関数…つーか数式

相変わらず Puthon 3.7.x (anaconda) で検証。

シグモイド関数

基本形はこんな数式らしい。

 f _ \theta(x) = \frac{1}{1 + e ^ {-\theta ^ Tx}}

グラフにしてみるかね

import numpy as np
import matplotlib.pyplot as plt
import math
%matplotlib inline

def sigmoid(x):
    """シグモイド関数"""
    e = math.e
    return 1/(1 + e**-x)

x = np.arange(-20, 20, 0.5)
y = sigmoid(x)

fig = plt.figure(figsize=(10.0, 5.0))

plt.plot(x, y, color='blue')

plt.grid()
plt.show()

f:id:white-azalea:20200618235120p:plain

よくニューラルネットワークニューロンにされるやつ。

まぁ要するに 0 - 1 の間、0 でちょうど 0.5 となってるので、パーセンテージを扱う処理に向いてるって話らしい。
これで単純な二値判定するなら、0.5 が当然のようにボーダーになるわけで…

0 がちょうど 50% なので、それがプラスかマイナスかで 1/ 0 と判定すればいいので


  y =
  \begin{cases}
     1 \quad (\theta ^ T \geq 0) \\
     0 \quad (\theta ^ T \leq 0) \\
  \end{cases}

という形になる。

これで長方形判定をするものと考えると。
横長である確率を求めると考えて、横軸を x _ 1 縦軸を x _ 2 として適当なパラメータを与えてみる(あくまでも適当)


 \theta =\left[
    \begin{array}{rrr}
        \theta_0 \\
        \theta_1 \\
        \theta_2
    \end{array}
  \right] = \left[
    \begin{array}{rrr}
        -50 \\
        4 \\
        2
    \end{array}
  \right], \quad x = \left[
    \begin{array}{rrr}
        1 \\
        x_1 \\
        x_2
    \end{array}
  \right]

式に直すと


    \theta ^ Tx = -50 \cdot 1 + 4x _ 1 + 2x _ 2  \geq 0 \\
    2x _ 2 \geq 50 - 4x _ 1 \\
    x _ 2 \geq 25 - 2x _ 1

図にすると

続きを読む

行列使った機械学習で、縦長/横長長方形を判定する

問題

四角が存在して、縦長か横長かを判定する。
正直それだけなら座標見れば一発だが、敢えてベクトル的に考えてみる。

横幅 縦幅
80 150 縦長
60 110 縦長
35 130 縦長
160 50 横長
160 20 横長
125 30 横長

これをプロットすると

import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline

x = np.array([80, 60, 35, 160, 160, 125])
y = np.array([150, 110, 130, 50, 20, 30])

fig = plt.figure(figsize=(5.0, 5.0)) # グラフサイズ

plt.scatter(x, y, marker = 'o')

plt.grid()
plt.show()

f:id:white-azalea:20200616231234p:plain

これを線引きする直線を求めたい。
これを行列的に求めるなら、重みベクトルを法線ベクトルとした直線 という言葉になる…らしい。

うーん難しい (;^ω^)

重みベクトルを $w$ とすると直線の方程式は

w \cdot x=0 もっというと  w_1 \cdot x _ 1 + w _ 2 \cdot x _ 2 = 0
ってなるので、 w = (1, 1) と仮定すると x _ 1+ x _ 2 = 0 ということは


x _ 1 = -x _ 2

これを図にすると

fx = lambda x : -x
x = np.arange(-10, 10, 0.5)
y = fx(x)

fig = plt.figure(figsize=(5.0, 5.0))

plt.plot(x, y)

# w = (1, 1) も作画
plt.quiver(0, 0, 1, 1, angles='xy', scale_units='xy', scale=1)

plt.grid()
plt.show()

f:id:white-azalea:20200616231306p:plain

この論理で、判定できる関数を学習で習得させる。
目的の重みベクトルを w として、学習式を以下にしてみる。


w :=
  \begin{cases}
     w+y ^ {(i)}x ^ {(i)} \quad f_w(x ^ {(i)}) \neq y ^ {(i)} \\
     w \quad\quad\quad\quad f_w(x ^ {(i)}) = y ^ {(i)} \\
  \end{cases}

縦長をこのベクトルと内積とったときには正、横長をこのベクトルと内積とったときに負と判定するとして、成功するなら何もしない。
判定に失敗したらベクトルの足し算を行うと…

from copy import copy

# 学習データ
values = [
    np.array([80, 150]),
    np.array([60, 110]),
    np.array([35, 130]),
    np.array([160, 50]),
    np.array([160, 20]),
    np.array([125, 30])
]
actuals = [
    True, True, True, False, False, False
]

# 正規化
def normalize(v):
#     c = np.linalg.norm(v)  # ベクトルの長さを求めて
    return v #(v / c)

# 学習データ
w = np.array([-1, -1])

# 学習の変遷
log = [ w ]

# 学習関数
for n in range(0, 6):
    v = normalize(values[n])
    actual = actuals[n]
    res = np.dot(w, v)
    res = float(res)
    
    print('--------')
    print('current:' + str(w))
    print('test     :' + str(v))
    print('res        :' + str(res))

    # 正常判定したら何もしない
    if (res > 0) and actual:
        continue
    elif (res < 0) and (not actual):
        continue
    
    # 判定に失敗してたら更新
    if actual:
        w = w + v
    else:
        w = w - v
    log.append(copy(w))
    print('update to:' + str(w))
    
    print(log)

出力ログこんな感じ。

    --------
    current:[-1 -1]
    test     :[ 80 150]
    res        :-230.0
    update to:[ 79 149]
    [array([-1, -1]), array([ 79, 149])]
    --------
    current:[ 79 149]
    test     :[ 60 110]
    res        :21130.0
    --------
    current:[ 79 149]
    test     :[ 35 130]
    res        :22135.0
    --------
    current:[ 79 149]
    test     :[160  50]
    res        :20090.0
    update to:[-81  99]
    [array([-1, -1]), array([ 79, 149]), array([-81,  99])]
    --------
    current:[-81  99]
    test     :[160  20]
    res        :-10980.0
    --------
    current:[-81  99]
    test     :[125  30]
    res        :-7155.0

プロットしてみるとこうなる。
ちなみに最終的な法線も入れて作画。

x = np.array([80, 60, 35, 160, 160, 125])
y = np.array([150, 110, 130, 50, 20, 30])

def nvec(v):
    # 法線ベクトル
    # 99 / -81 = -1.2222222222222223
    # 81 / 99
    return v * 0.8181818181818182

nx = np.arange(-180, 180, 1)
ny = nvec(nx)

# 正規化関数
def normalize(v):
    c = np.linalg.norm(v)
    return (v / c) * 80.0

# 計算の過程で出た重みベクトルのリスト
plotData = []
for v in log:
    plotData.append(v)

fig = plt.figure(figsize=(8.0, 8.0)) # グラフサイズ
plt.xlim(-180, 180)
plt.ylim(-180, 180)

# 学習データ
plt.scatter(x, y, marker = 'o')
# 縦長か横長かを判定する法線ベクトル
plt.plot(nx, ny, color='blue')

# 学習中に出たベクトルの変遷
# 赤を基調に、だんだん青が交じる
color = [1.0, 0.0, 0.0]
bias = 0.2
for v in plotData:
    plt.quiver(0, 0, v[0], v[1], angles='xy', scale_units='xy', scale=1, color=color)
    color[0] = color[0] - bias
    color[2] = color[2] + bias


plt.grid()
plt.show()

f:id:white-azalea:20200616231523p:plain

これで無事青の法線が引けた。
当たり前だけど、学習サンプルが完全ではないので、この法線ベクトルでは縦長か横長かを完全に判定することはできない…が、それが縦長か横長かを学習する学習機はこんな感じでやれる。

多分アヤメ判定とか、ベクトルでの二値判定で使えるはず。

ベクトルは全部グラフにしてみないとわかりにくくてたまらない(汗

テスト駆動開発

書籍

テスト駆動開発

テスト駆動開発

  • 作者:Kent Beck
  • 発売日: 2017/10/14
  • メディア: 単行本(ソフトカバー)

来たよケントベック先生(Smalltalker で XP 開発の始祖様)!

読もうと思ったきっかけ

テスト駆動開発…って聞いた時の自分の知ってる知識は

  1. ユニットテストを最初に書いて、以下を定義する
  2. どう動くべきか
  3. どう書きたいのか
  4. つまりコードが仕様書なんだ
  5. テストを通る様に実装する

でしかなかった。
要するに 我流な TDD のイメージ を持ってたわけだ。

なので、改めて勉強してみるのもいいかなと

意識 Before/After

Before

我流TDD 的に考えていれば、テストを書く前の段階からこうしておく必要があると思い込んでた

  • どういった機能が必要なのかを事前に設計しなければならない
  • インターフェースの定義や、設計の有るべき論を定義してかかるべきである

だから テスト駆動は難しい ので スキルのあるチームでないと出来ない事だ とそう 勝手に思い込んでいた

After

ああ、TDD とは 設計まで対象にリファクタを行う 開発様式だったのかと初めて知った。

基本の1サイクルは以下の通り。

  1. テストを書く
  2. コンパイルを通す(空実装)
  3. テストを走らせて失敗を起こす
  4. テストを通す
  5. 重複の排除

ただしこれを、1モジュールあたり以下のフェーズで行う。

  1. 最低限必要とすべき機能を定義する(実装は適当でいい)
  2. リファクタ(テスト上、内部変数の変化をチェックする様な使い方は微妙とか、インターフェースの見直し)
  3. オブジェクトとして見た時に、他に必要な物がないかの観点でインターフェース定義
  4. テストコードのリファクタ
  5. テストのバリエーション(こんな時はどうあるべきよ?)の作成
  6. テストから見た同一概念操作の抽出
  7. 派生型/類似型などの隠蔽化(クラスの同一化か派生クラスの削除など…)
  8. 不要となったテストの除去

何てこった、 実装だけじゃない、 インターフェース…使い方までサイクルに入ってるやんけ

ざっくり内容と見所

  1. 第一部: 金額の取り扱いクラスについてTDDの実装を行ってみる(ハンズオン)
  2. 第二部: xUnit (ユニットテストのほぼデファクトスタンダード)の構成と、考え方(ハンズオン)
  3. 第三部: テスト駆動パターン(読み物)
  4. テスト駆動で記述するテストの種類
  5. テスト手法のパターン
  6. 仮実装から本実装へのリファクタを行うテスト修正パターン
  7. xUnit の技法
  8. デザインパターン(!?まさかの設計パターン)
  9. リファクタリング手法
  10. TDD の身につけ方

という構成。
手を動かさないと退屈という自分にはすごく有り難い構成でした。

ってーか

第三部盛り過ぎでしょ(汗
消化不良ならもう何でも関連資料漁る位は当然しますけどさ…(汗

本気で第三部だけで1冊出ても良いと思いました。第三部だけでも技法技術大好きっ子には (^q^) ですよマジで…