あらきけいすけのメモ帳

あらきけいすけの雑記帳2

pigpio ライブラリのサンプルコード PCA9685.py を用いて Raspberry Pi 3B から I2C 接続経由でサーボモータを動作させる

自分用の書きかけの覚書。pigpio の i2c 関連の関数のパラメータの与え方がよくわかっていない状態だが、〔Raspberry Pi 3B/3B+/Zero W + Python + pigpio ライブラリ + pigpio コード PCA9685.py*1〕と〔PCA9685 16channel 12bit pwm サーボドライバー*2〕を使ってサーボモータを動かすことができた。これでラズパイ側は(電源を除けば)[03:GPIO02(SDA1,I2C)] [05:GPIO03(SCL1,I2C)]の2個のGPIOピンを使うだけで最大16個までのサーボを動かせるようになった*3

  • ハードウェア側(Adafluit製)
    次のようにピン接続する。ピン接続がきちんとしていて、ラズパイに電源がつながっていたら(ラズパイのOSが起動していても、いなくても電力が供給されているので)ドライバー上の赤いLEDが点灯する。
    1. ドライバー[GND]⇔RPi[06:Ground]*4
    2. ドライバー[0E] not connected
    3. ドライバー[SCL]⇔RPi[05:GPIO03(SCL1,I2C)]
    4. ドライバー[SDA]⇔RPi[03:GPIO02(SDA1,I2C)]
    5. ドライバー[VCC]⇔RPi[02:5V]
    6. ドライバー[V+]⇔サーボモータに供給する電源(RPi[04:5V]でOK)
  • プログラム側
    1. まず pigpio のサイトの Examples のページ(http://abyz.me.uk/rpi/pigpio/examples.html)から PCA9685_py.zip をダウンロードする
    2. この zip ファイルを解凍して Python コード PCA9685.py を取り出す
    3. コマンド "sudo python PCA9685.py" で実行。*5ドライバーに接続されているすべてのサーボモータが一斉に同じ動作をする
  • Pythonプログラムの自作
    • 最小限のコード:0番ポートのサーボモータを(多分、約)90度のポジションに動かす。これを"runPCA9685.py"の名前で"PCA9685.py"と同じフォルダに保存する
# -*- coding: utf-8 -*-
if __name__ == "__main__":
   # ライブラリを読み込む部分
   import PCA9685
   import pigpio

   # ライブラリを使うためのインスタンスを立ち上げる部分
   pi = pigpio.pi()
   if not pi.connected:
      exit(0)
   pwm = PCA9685.PWM(pi) # defaults to bus 1, address 0x40

   # サーボモータを動作させるPWM信号の周波数を決める部分
   pwm.set_frequency(50) # suitable for servos

   # サーボモータを動作させる部分
   ch = 0
   pw = 1500
   pwm.set_pulse_width(ch, pw)

   # プログラムの終了処理の部分
   pwm.cancel()
   pi.stop()
    • コマンド "sudo python runPCA9685.py" で実行。

*1:"pigpio" i2c PCA9685 - Google 検索; pigpio library Examples

*2:pca9685 16channel 12bit pwm サーボドライバー - Google 検索 Adafluit製のものがあちこちで売られているようだ。

*3:ただし電力の供給をラズパイに頼ると、サーボモータの数が増えたときに、ラズパイが落ちるかもしれない。
I2C通信の使い方に記載の接続法を見ると、何台でもつなげそう。

*4:GroundのピンならどれでもOK

*5:この前に "sudo pigpiod" を実行しておく。

Raspberry Pi 3BとWindows 10 PCをLANケーブル(と給電用のUSBケーブル)で直接つないで操作する

自分用の書きかけの覚書。ネット環境が無いところでラズパイをWindowsから動かすためののメモ。

要点

bonjour*1をインストールした?*2Windows 10 PC*3Raspberry Pi 3 Model BをLANケーブル(と給電用のUSB A-micro B ケーブル)で直接つないで、ウィンドウズからラズベリーパイにリモートログインできる。

しくみ

Windows 10 はリンクローカルアドレスで繋いだラズパイのホスト名(デフォルトでは"raspberrypi")を探して繋ぐことができる*4。作業の要点は

  • Raspberry Pi
    • 起動時にsshあるいはvnc(あるいはxrdp)が起動されるようにして、いつでもリモートログインできる状態にしておく。
    • Raspberry Pi の hostname (マシン名)を把握しておく(と、リンクローカルアドレスを調べなくてもつなげられる)。初期設定は raspberrypi である。これを別の名前にするには /etc/hostname と /etc/hosts を書き換える。
    • hostname がわからないときは、リンクローカルアドレスで接続する。ユーザ名は把握していないといけない。
  • Windows
    • Bonjourをインストールしておく不要[2020.1.19]
    • ラズパイとWindowsPCをケーブルを繋いで、ラズパイを起動した後で、コマンドプロンプトで"arp -a"*5コマンドを打ち込んで、有線LANが動作していることを確認する
    • Windows PCから(初期設定をいじっていないなら)"raspberrypi.local"に(sshならTeraTermなどで、vncならVNC Viewerなどで、xrdpならリモートデスクトップで)リモートログインする

ここに書いていないこと

WindowsとRPiの間の通信はできてはいるけれど、(例えばRPiでソフトのアップデート sudo apt-get update をしたいときに)RPiからインターネットにどのようにアクセスすればよいか。参考にしたサイトの「ブリッジ接続もしてみる」を参照。

参考にしたサイト

qiita.com

*1:bonjour Zeroconf - Google 検索

*2:[2019.3.31]bonjourサービスをアンインストールした状態でも動作した。169.254.*.*のリンクローカルアドレスが勝手に振られている状態でホスト名"raspberrypi.local"のIPアドレスの解決どうやっているんだろう?

*3:バージョン1803(OSビルド 17134.648)

*4:[2020.1.19]以前は以下のように書いていたが、新品のWin10PCでもBonjourなしでつながることを確認した。「Bonjourとは、自動的にインターネットのネットワークを構成する技術であるZeroconfZeroconf - Google 検索に基づいたApple社製のソフトで、ネットワークアドレスが分からないラズパイのアドレスを自動で探して繋ぐようにするために必要。」

*5:windows arp - Google 検索

Raspberry Pi Zero W(RPi4BもOK)をUSBガジェットにしてUSBケーブル1本でWindows 10マシンから操作する

自分用の書きかけの覚書。Raspberry Pi Zero WとWindows 10 ノートPCをUSBケーブル1本(A-micro B*1)で繋いで(給電も兼ねる)、Win10PCからRPi0wにリモートログインできるようにする(RPi0w側でsshが起動していればWin側はTeraTerm*2などで、RPi0w側でVNCが起動していればWin側はVNC Viewer*3などで、RPi0w側でxrdp*4が起動していればWin側はリモートデスクトップ接続でつなぐ)。要点はどうも次の2点に集約できそう:

  • ラズパイ側
    • linux USB gadget 機能を有効にする。*5Raspbian OSの/bootディレクトリのconfig.txt, cmdline.txtを書き換える。
      1. /boot/config.txt の最後尾に "dtoverlay=dwc2" と書かれた行を追加する。
      2. /boot/cmdline.txt の "rootwait" と "quiet" の間に " modules-load=dwc2,g_ether" を書き加える。
      詳細は参考にしたサイトを参照。
    • sshVNCを有効にするとか、xrdpをインストールするとかのネットワーク接続によるリモートログイン可能な状況を作る)
  • Windows

首尾よく接続出来たら、RPi0wにUSB経由でリモートログインして、Wi-Fi接続の設定をするとさらに世界が広がるwwww
参考にしたサイト:
qiita.com
gist.github.com
blog.gbaman.info

OpenCV+Pythonで動画を録画/再生する(音声はなし, Windows 10/Raspberry Pi 共通)

自分用の覚書。OpenCV*1で動画を録画するためのPythonミニマル・コード*2
サンプル1:PC/ラズパイに接続されたUSBカメラ*3の動画像を表示する。終了はCtrl+C. VideoCapture()の引数の0はビデオデバイスの番号。

import cv2
cap = cv2.VideoCapture(0)
try:
    while True:
        ret, frame = cap.read()
        if ret == True:
            cv2.imshow('a',frame)
        cv2.waitKey(1)

except KeyboardInterrupt:
    cap.release()
    cv2.destroyAllWindows()

サンプル2:PCに接続されたカメラの動画像をディスプレイに表示し、ファイル'test.m4v'に録画する。終了はCtrl+C.([2019.6.24追記]手元のRPiではデスクトップ環境上の Python3(IDLE)を使って実行すると、出力した動画ファイルが常に壊れていた。LXTerminal上で実行すると、再生可能な動画ファイルを出力した。)低速度撮影(time lapse)は2, 12, 20行頭の#を取る

import cv2
#import time

cap = cv2.VideoCapture(0)
fps    = cap.get(cv2.CAP_PROP_FPS)
width  = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))

fname  = 'test.m4v'
fmt    = cv2.VideoWriter_fourcc('m', 'p', '4', 'v')
size   = (width,height)
writer = cv2.VideoWriter(fname, fmt, fps, size)
#intrvl = 1 #[sec] set time interval of time-lapse recording

try:
    while True:
        ret, frame = cap.read()
        if ret == True:
            cv2.imshow('a',frame)
            writer.write(frame)
            #time.sleep(intrvl) # waiting (for time-lapse)
        cv2.waitKey(1)

except KeyboardInterrupt:
    cap.release()
    cv2.destroyAllWindows()

サンプル3:サンプル2で録画したファイル'test.m4v'を再生する。終了はCtrl+C.

import cv2
import time

vFile  = 'test.m4v'
cap    = cv2.VideoCapture(vFile)
fps    = cap.get(cv2.CAP_PROP_FPS)

try:
    while cap.isOpened():
        ret, frame = cap.read()
        if ret == True:
            cv2.imshow('a',frame)
            time.sleep(1/fps)
        cv2.waitKey(1)

except KeyboardInterrupt:
    cap.release()
    cv2.destroyAllWindows()

Microsoft Excel について

業務用の覚書。書きかけ。

Excelの利点

  • (多分、職場の)誰もが Excel を持っている。Excel ファイルとちょっとましなマシン(Core i7 + 8GB RAM とか)があればどこでも作業できる。(業務遂行に関して自由度が上がる)
  • (多分、実用的には、項目数が50以下、レコード数が10,000以下程度なら) WYSIWYG のデータベースとして使える。つまりクエリを投げずに直に検索, 置換, 編集ができる。とくに「フィルター*1」と「ウィンドウ枠の固定*2」は操作が直感的でいい。
  • オンプレミス*3で運用できる。(これはGoogleドライブにはできない)
  • プログラム(VBAマクロ)を書いてデータ操作ができる。(ただし Visual Basic for Application は動作がもっさりとしている。特にスレッディングができない*4。)
  • (たしかに PythonPHPExcel ファイルを操作できるライブラリはあり、そっちの方が高速ではあるのだけれど、Excel とは別のプログラミング環境を導入する必要がある…というのは、可搬性に関わる敷居が高いことを意味していると思う)
  • Excel ファイルにデータを書き出せる(データファイルとアプリケーション実行環境の「閉じた(複数にまたがらない)」エコシステムである。)
  • VBA には「連想配列( Scripting.Dictionary オブジェクト)」*5があるので Key-Value 的なコードが書けなくもない。これは重複検出に便利。
  • VBA は変数名にマルチバイト文字*6が使えるので、テーブルの項目名を変数名に使うとかできちゃったりする
  • Excel VBA 使いがいれば、外注するよりコミュニケーションの時間コストを下げることができる

コーディングの注意点

Excel VBA による Excel ワークブック/ワークシート, CSVファイル, XMLファイル等のデータ処理の進行は、HTMLベースのWebアプリケーションに似ていてイベント(Excel内のボタン操作など)によってコードが駆動される。

ラグランジュ補間(2点を通る直線の公式)、単位の換算、そのプログラミングについての覚え

授業のための覚書

2点(x_0,y_0), (x_1,y_1)を通る直線の公式:\displaystyle y=\frac{x-x_1}{x_0-x_1}y_0+\frac{x-x_0}{x_1-x_0}y_1
これは Lagrange 補間(Lagrangian interpolation*1)と呼ばれる計算技法である。この式は次のようにも書ける:
2点(x_0,y_0), (x_1,y_1)を通る直線の公式:\displaystyle y=\frac{x-x_0}{x_1-x_0}(y_1-y_0)+y_0
「直線の式」だからって、この計算式の応用範囲が「直線を描画する」といったグラフィックスだけに限られるわけではない。例えば、摂氏温度*2から華氏温度*3への換算にも使える。水の氷点が(0度C,32度F), 沸点が(100度C,212度F)なので摂氏温度がc度のときの華氏温度の値は\displaystyle f=\frac{c-100}{0-100}32+\frac{c-0}{100-0}212になる。
ここで受験数学の答案のような「筆算にしばられた発想」だと、係数を整理してf=1.8c+32とまとめるところだが、プログラミングの世界では整理すべきでない。この換算をC言語コードで書くときは、次のように書くのがベター;

// マクロ, 関数形式マクロ
#define Cfleeze   (0.0)
#define Cboil   (100.0)
#define Ffleeze  (32.0)
#define Fboil   (212.0)
#define tempC2tempF(tempC) (tempC - Cfleeze)/(Cboil - Cfleeze)*(Fboil - Ffleeze) + Ffleeze
// 定数, 関数
float tempC2tempF (float tempC) {
  const float Cfleeze =  0.0, Cboil = 100.0;
  const float Ffleeze = 32.0, Fboil = 212.0;
  tempF = (tempC - Cfleeze)/(Cboil - Cfleeze)*(Fboil - Ffleeze)
          + Ffleeze;
  return tempF;
}

このコードならば「tempC=CfleezeのときにFfleeze」「tempC=CboilのときにFboil」の値を返す tempC の1次関数を計算していると分かる。ややこしい計算はコンピュータにやらせればよい。むしろ「何の計算をコンピュータにやらせているのかはっきり分かる」コードの方がベターである*4

演算数から考えると const を使って計算の定数を予め作っておいて*5、define で「インラインで計算」するのが早いかも:

// マクロ, 関数形式マクロ
// tempC2tempF(tempC) = (tempC - Cfleeze)/(Cboil - Cfleeze)
//                      *(Fboil - Ffleeze) + Ffleeze
#define Cfleeze   (0.0)
#define Cboil   (100.0)
#define Ffleeze  (32.0)
#define Fboil   (212.0)
const float
  tempC2tempFCoeff  = (Fboil - Ffleeze)/(Cboil - Cfleeze),
  tempC2tempFOffset = -(Cfleeze)*(Fboil - Ffleeze)/(Cboil - Cfleeze)
                      + Ffleeze;
#define tempC2tempF(tempC) ((tempC)*tempC2tempFCoeff + tempC2tempFOffset)

*1:ラグランジュ補間 - Wikipedia

*2:セルシウス度 - Wikipedia

*3:華氏 - Wikipedia

*4:プログラミングの世界では、いきなり数値をソースコードに書くことは「マジックナンバー」と呼ばれ、プログラムの可読性が悪くなるので避けるべきとされている。;マジックナンバー (プログラム) - Wikipedia可読性 - Wikipedia

*5:変数名は長くなってもよいから、計算で使うデータの内容を表示した方がよい。変数名の長さは、実際のCPU上で動くマシン語には影響しない。

python のリスト/辞書/タプルに特定の値を持つ要素が存在するかどうかの確認方法

で、躓いたのでメモをする。

if 〔探索値〕 in 〔リスト名/辞書名/タプル名〕:
        〔探索値がリスト/辞書のキー/タプル中にあったときの処理〕

ここで 〔探索値〕の添字を返すメソッド

〔リスト名〕.index(探索値)

を用いると存在しない場合に ValueError を返して、例外処理を書かないとコードの進行が止まる。辞書の〔キー〕・〔値〕のペアの〔値〕の中から探索するには

if 〔探索値〕 in 〔辞書名〕.values():
        〔探索値が辞書の値の中にあったときの処理〕