CMSIS-DSPをラズピコ2で使う

ラズピコ2の特徴の一つである、FPUとGPUを活用するのにちょうど良さそうなのがARM系プロセッサ向けに用意されているCMSIS-DSP、ラズピコのハードにも最適化されているので、例えばArduinoライブラリよりもFFT処理が高速化できるし、MNISTで使っている事例もあります

ラズピコに比較しておよそ5倍程度の高速化というような情報もあります

以下、VScodeでラズピコ2の環境を用意したので、コードからCMSIS-DSPを使えるようにします

https://isehara-3lv.sakura.ne.jp/blog/2025/05/27/ラズピコ2の開発環境vscode-raspberry-pi-pico/

<実行環境>

ラズピコ2W + デバッグボード、M4 MacBookVScode + Raspberry pi pico extension

参考になったのは、やはりRaspberryPI財団のページで、

https://forums.raspberrypi.com/viewtopic.php?t=365053

手順を述べると、

① ラズピコにカスタマイズされたスタティックライブラリを作成する

作成されるのは、libCMSISDSP.aという名称になります

② main.cからは①で作成したスタティックライブラリと、CMSIS関連ヘッダファイル群を呼び出せるようにinclucdeする

1-1 CMSIS-DSPのライブラリを作成するためのディレクトリ構成

ビルドの例は以下の通り、

mkdir build
cd build
cmake ..
make -j4

1-2 サンプルプログラムとCMSIS-DSPライブラリの組み込み

https://forums.raspberrypi.com/viewtopic.php?t=365053

にあるFFTを実行するサンプルプログラムをそのまま使用しました

“arm_math.h”がCMSIS-DSPを使用するための宣言です

VScodeで作成されたCMakeファイルに、以下の🔴部分の3行を追加します

これでRASPBERRY PI PICO PROJECTのCompile ProjectでUF2ファイルが作成されて、Flushすればラズピコ上で動作開始します

③ 実行結果

USBシリアルのポートは二つ見えますが、常時見えているのがデバッガー側、ラズピコ2側は瞬間でしか見えないので要注意、というかデバッガ使わないと見ることはできないと思う

コンソールに印字された結果(256個出てきますが、re/imの組み合わせなので実質128個、それの二乗平均しています)をコピーしてリニア軸で図にすると、

さらに、対数軸で見てみると、

それらしいグラフになっています

 

admin

Sonic PiのOSC制御

Sonic PiにOSC制御のインターフェースがMIDIと一緒に併記されていて、OSCって何のことと思いましたが、Open Sound Controlの略でUDP(速度大事だろうから)で送られる定義された制御インターフェースです

例えばSonic Pi側で以下のように待ち受けしておいて、

live_loop :osc_trigger do
  use_real_time
  note = sync "/osc:127.0.0.1:8001/trigger/synth"
  synth :prophet, note: note[0], release: 1.0
end

Node.jsから以下のようなコードでA~Kのキーを叩いで電文を送信すれば、Sonic Piで一オクターブ(半音なし)の合成をします、値はMIDIに相当する値ですが他のルールに則っても良いらしい

同じパソコンからなのでループバックアドレスになってますが、これは無論ネットワーク次第、ポートの4560は固定になってます

// original https://qiita.com/youtoy/items/a158b847b142f0a134a6
//
const osc = require("osc");
const keypress = require("keypress");

var udpPort = new osc.UDPPort({
  localAddress: "0.0.0.0",
  localPort: 8001,
  remoteAddress: "127.0.0.1",
  remotePort: 4560,
  metadata: true,
});

udpPort.on("message", function (oscMsg, timeTag, info) {
  console.log("An OSC message just arrived!", oscMsg);
  console.log("Remote info is: ", info);
});

udpPort.on("ready", function () {
  console.log("ready");

  keypress(process.stdin);
  process.stdin.on("keypress", (ch, key) => {
    if ((key && key.ctrl && key.name === "c") || (key && key.name === "q")) {
      process.exit();
    }
    switch (key.name) {
      case "a":
        sendValue(60, key.name);
        break;
      case "s":
        sendValue(62, key.name);
        break;
      case "d":
        sendValue(64, key.name);
        break;
      case "f":
        sendValue(65, key.name);
        break;
      case "g":
        sendValue(67, key.name);
        break;
      case "h":
        sendValue(69, key.name);
        break;
      case "j":
        sendValue(71, key.name);
        break;
      case "k":
        sendValue(72, key.name);
        break;
      default:
        break;
    }
  });

  process.stdin.setRawMode(true);
  process.stdin.resume();
});

function sendValue(inputValue, inputText) {
  udpPort.send({
    address: "/trigger/synth",
    args: [
      {
        type: "f",
        value: inputValue,
      },
    ],
  });
  console.log(`key.name: ${inputText}`);
}

udpPort.open();

キーボードは演奏家にとって大事だろうから、シンセサイザーとは別に色々なデバイスが選択できるのは良さそうです、MIDIとは共存する立ち位置のようですが

 

admi

ラズピコ2の開発環境(VScode + Raspberry Pi Pico)

Arduino IDEはもっさり感もありますが、機能も不足していて本格的に使おうとするとイマイチ

じゃVScode + PlatformIOどうなのと言うと、ラズパイ財団の関係からラズピコは公式サポートなし、と言うわけなのでラズパイ財団おすすめのVScodeの拡張機能(Raspberry Pi Pico)を使う、名前そのままですがVScodeの拡張機能で出てくるのでそれをインスト

プロジェクト管理画面から、作成、ビルド、書き込み、デバッグができます

定番のHello worldコードがデフォルトで作成(一部変更しています)されるので、

#include 
#include "pico/stdlib.h"


int main()
{
    stdio_init_all();
    int a = 10;
    while (true) {
        printf("Hello, world! a = %d\n", a);
        sleep_ms(1000);
        a += 1;
    }
}

ビルドしてラズピコ2にデバッガ経由で転送、

シリアル出力の有効化は、

% ls /dev/tty.*
/dev/tty.Bluetooth-Incoming-Port	/dev/tty.usbmodem1202
/dev/tty.debug-console			/dev/tty.usbmodem1301

usbmodemが二個見えてますが、どちらか一方はデバッガー

二つしかないから、交互に選択してみると、

USB-Serialでコンソールに出力されています

デバッガは、

こんな感じで普通に動作、変数aはコンパイラの最適化でprintf文中で使わないと落とされます、デバッガ機能確認用に定義したがprintfで使わないとデバッガで見えない

 

admi

USBインターフェースのSSDアダプタのベンチマーク

USBのSSD(2.5)ケースが不調なので買い替えついでにベンチマーク(AmorphousDiskMark)してみました

<測定条件>

本体はM4 MacBook Pro、①はポートリプリケーター経由USB Type-Aで接続、②、③はType-C本体直結

① USB3.0 SATA SSD用ケースが二台

② USB3.2 SATA用ケースが一台

③ USB3.2 M2.形状Mvne用アダプタが一台

④ MacBook本体内蔵SSD(比較値)

結果

① 二台実施してますが、一台はUSB2.0でしかつながらない、USB Type-Aコネクタを深く挿入するとドライブ認識しないから、緩く挿入だと2.0になる

② ドライブは①の最初のドライブを移設

SATAドライブではこの程度が限界らしい、速度差ほぼないから実はUSB3.0でSATA SSDなら十分かもしれない

③ Mvne SSDだと最大値はほぼ倍になった、でもそれだけかも知れない

④ 本体内蔵は流石に高速、しかしアプリレベルの体感(おそらくボトムの数値 + αがそれに該当するだろうから)ではそれほどのことはない

<測定結果からの結論>

  1. SATA SSDは格段の事情がない限り、もはや使うべきではない
  2. 外部SSDで使うならUSBは3.2程度で体感的な差は頭打ちになるだろう、あえてUSB4.0とかThunderBirdとかの必要性は感じない

 

admin

 

玄関ドアの鍵シリンダ交換とスマートキー化

玄関のシリンダー錠にキーがスッと入らなくなったので、寿命もあるしセキュリティもあるからシリンダ交換

ドアの型格から検索してネットでオーダー

<現物>

事前のブツチェックで分解してたので交換は簡単

<交換後>

シリンダ部分は新品になった

次にスマート化は、

・価格

・複数のキー制御

・アプリの使いやすさ

を考慮してsesameシリーズをオーダー、スマホにアプリインストしてペアリングしてやれば使えます

指紋認証でも解錠できるようにsesame touchもオーダー、mifareカードなどでも認証可能だからホテルのようなカードキー的な使い方もできる

今の所解錠方法は、アプリと指紋認証だけの設定だけど、追加しておくと便利そうなのはPASMO(要はスマホタッチ)かな、もちろん物理キーは非常用に持っておくべき、電池切れや故障時への対応用として

アタッチメントはアジャスタブルになっているのである程度は汎用性ある、但しアタッチメントだけではメイン側のキーは相手先の物理形状の制限で取り付けがネジ2本しか使えないのでイマイチ、機能はしますが

<外観>

タイマー機能で解錠後にある時間経由したら自動で施錠するような設定にしています、Wi-Fiへの接続はつまりネットに交換するということになるからセキュリティ的には脆弱になるだろうからやらない、家の外で鍵の情報見てもねと思うので

 

あとドア開放の時に自動で施錠しないような開放センサーはあったほうが良いかと思い始め

 

admin

ラズピコ2 W

ラズピコ2もサポートが整ってきたと思うので購入(ピンなしでdebug端子含めて後付け)、ラズピコに比較すると例えばIoTなどのセキュアアプリケーション構築には向きそうです

<セットアップ>

Arduino IDE(2.3.5)での設定、w/ debug probe

と、

既存のrp2040用のコードをコンパイル(そのままコンパイル可能)してアップロードすると、

デバッガーも使えました

<性能比較>

HUB75のLEDアレイのスキャンタイム(ラズピコがほぼ14ms:リフレッシュサイクルでおよそ70Hzでラズピコ2が10ms切るぐらい)はラズピコでのRustの実行速度とほぼ同じだから、ハードの進化は偉大なり

使い道は今のところ未定、例えばTelloのコントローラーとしてはコマンド制御はともかくも、動画のストリーミング処理にはメモリもCPU能力も足りないし、当然ライブラリもない

 

admin

あと550円支払う?

今の回線はNuroの2Gbpsですが、NuroのWi-Fiルーター(規格ac:WiFi5)で頭打ちになっています

有線とWi-Fiの速度測定の結果は、

こんな感じで、有線だとG-LANのほぼ性能限度までの速度が出てるから上昇の余地があるということ

Wi-Fiルーターは契約を変えればaxバージョンが入手可能ですが、月額550円がプラスされるから、別に今で速度問題はないんだからそのままでは良いと言えば良いのだけど、接続されるデバイスがほぼax(WiFi6)対応になっているのに、わざわざ遅い環境のままにするのもね、という思いもありだよね

追加のax規格のWi-Fiルーターをブリッジ接続というのも有り得るけど、それだと今の1.5倍ぐらいにしかならないから、明らかに投資効果あると思えるのは倍だろうから中途半端な感じ

 

admin

3Dプリンタ(Flashforge3)不調と原因

しばらく前から、3Dプリンタでラフトの整形(PLAフィラメント)がうまくいかない、さらに造形物も空間埋めの六角形の成形が変dという状況でした

こんな感じで、ラフトがうまく整形できなから、剥がすと造形物に張り付く

色々試行してもうまくいかないので、ヘッドを新規に調達

右従来使ってたもの、左新品、実は従来使用してたのは温度が265℃で今回調達品は240℃品、やれてはいるけど見かけそんな劣化してるようには見えない

ヘッドの寿命はメーカーによると公称200時間と言うけど、プリンタの使用時間がそもそも200時間に達してない、ヘッドは三個目ですが

ヘッド交換してフィラメント押し出すと、なんとABSのフィラメントが吐き出された、おそらくこいつはPLAフィラメントの温度条件では溶けないから造形の邪魔になっていたと予測されます、どこにあったんだろう?

ともかくもキャリブレション後に造形すると、新品ノズルらしく造形面は綺麗です(センターがそれ)、左右は以前のテスト造形サンプルで左は塗装してます

復旧したようで、フィラメント送りモーターからの脱調音も出なくなった、結局つかえてたんだね、フィラメントは防湿収納して完了

 

admin

ラズパイでAvahiは外部サービスの状態をモニターしている

WSGIやFlaskサーバーでサービスを再起動すると、なぜmDNSが再度実行されるかというとAvahi(mDNSのラズパイ実装)がサービスをモニターしていて、再起動されるとCache flushを実行して、結果としてクライアントは再びmDNSを実行するようです

<tcpdumpの実行ログ>

MacBookからtfliteサービスのリクエストを出した時のログ、一度Cache flushが発行されると、タイムアウト(TTL)するまでは発行されない

% sudo tcpdump -i en0 udp port 5353 and src host 192.168.1.19

Password:
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on en0, link-type EN10MB (Ethernet), snapshot length 524288 bytes

(サービス立ち上げ後にクライアントから最初にリクエスト)
09:36:44.585023 IP 192.168.1.19.mdns > 224.0.0.251.mdns: 0*- [0q] 1/0/0 (Cache flush) A 192.168.1.19 (39)
09:36:44.585025 IP 192.168.1.19.mdns > 224.0.0.251.mdns: 0*- [0q] 1/0/0 (Cache flush) AAAA 240d:1a:896:8300:99fc:1d59:78ab:a156 (51)


(サービスの再起動後のリクエスト)
09:39:08.361077 IP 192.168.1.19.mdns > 224.0.0.251.mdns: 0*- [0q] 1/0/0 (Cache flush) AAAA 240d:1a:896:8300:99fc:1d59:78ab:a156 (51)
09:39:08.361545 IP 192.168.1.19.mdns > 224.0.0.251.mdns: 0*- [0q] 1/0/0 (Cache flush) A 192.168.1.19 (39)

Avahiはdaemonで動作しています

$ ps -aux|grep avahi
avahi        655  0.0  0.0   7696  2672 ?        Ss   09:25   0:00 avahi-daemon: running [rasp5.local]

Avahiがどのようなロジックで外部サービスの状態をモニターしているのかはソース読まないとわからないけど

https://github.com/avahi/avahi

READMEには

AVAHI SERVICE DISCOVERY SUITE

とあるから、mDNSの実装よりもこちらがメインのように思える

 

admin

tfliteをマルチコア(ラズパイ5)で動かした時の性能改善

従来のコードからの変更箇所、

    1. サーバーにFlaskではなく正式運用推奨のWSGIサーバーに変更
    2. スレッド数をsingleからラズパイ5のコア数である4に変更(num_threads)
# work on Flask server
# server will be activated only when client request is occured
#
#
from flask import Flask, jsonify
import cv2
import numpy as np
import tflite_runtime.interpreter as tflite
from picamera2 import Picamera2
from waitress import serve

app = Flask(__name__)

# モデル・ラベル初期化
interpreter = tflite.Interpreter(model_path="efficientdet_lite0.tflite", num_threads=4)
interpreter.allocate_tensors()
input_details = interpreter.get_input_details()
output_details = interpreter.get_output_details()
with open("coco_labels.txt", "r") as f:
    labels = [line.strip() for line in f.readlines()]

# カメラ初期化
picam2 = Picamera2()
picam2.preview_configuration.main.size = (640, 480)
picam2.preview_configuration.main.format = "RGB888"
picam2.preview_configuration.align()
picam2.configure("preview")
picam2.start()

def preprocess_image(image):
    resized = cv2.resize(image, (320, 320))
    resized = resized[:, :, [2, 1, 0]]  # BGR→RGB
    return np.expand_dims(resized, axis=0).astype(np.uint8)

def postprocess_results(boxes, scores, classes, count, image_shape, labels):
    detections = []
    for i in range(count):
        if scores[i] > 0.4:
            ymin, xmin, ymax, xmax = boxes[i]
            left, right, top, bottom = (
                int(xmin * image_shape[1]),
                int(xmax * image_shape[1]),
                int(ymin * image_shape[0]),
                int(ymax * image_shape[0])
            )
            detections.append({
                'box': [left, top, right, bottom],
                'class_id': int(classes[i]),
                'score': float(scores[i]),
                'label': labels[int(classes[i])] if int(classes[i]) < len(labels) else f"id:{int(classes[i])}"
            })
    return detections

def detect_once():
    frame = picam2.capture_array()
    input_data = preprocess_image(frame)
    interpreter.set_tensor(input_details[0]['index'], input_data)
    interpreter.invoke()
    boxes = interpreter.get_tensor(output_details[0]['index'])[0]
    classes = interpreter.get_tensor(output_details[1]['index'])[0]
    scores = interpreter.get_tensor(output_details[2]['index'])[0]
    count = int(interpreter.get_tensor(output_details[3]['index'])[0])
    detections = postprocess_results(boxes, scores, classes, count, frame.shape, labels)
    return detections

@app.route('/detect', methods=['GET'])
def detect_route():
    detections = detect_once()
    return jsonify(detections)

if __name__ == '__main__':
	# when you limit access only from the local machine, use a loopback address instead of 0.0.0.0
    #app.run(host='0.0.0.0', port=5000)
    serve(app, host='0.0.0.0', port=5000)

時間測定用のスクリプトは、

#
# test script for image detect function(tflite server)
#

import requests
import time

if __name__ == '__main__':
    time1 = time.time()
    result = requests.get('http://rasp5.local:5000/detect').json()
    time2 = time.time()
    print('func1: {:.3f} sec'.format(time2 - time1))
    # 'label'が'person'を含んでいるかを判定
    person_detected = any(item['label'] == 'person' for item in result)
    
    if person_detected:
        print("Person detected!")
    else:
        print("No person detected.")

測定結果、

    1. 最初のアクセスはmDNSが動作して遅くなる、以降はキャッシュが有効になるのでアドレス引きの時間は見えなくなる
    2. マルチコアでの改善率は2.5倍ぐらい高速化だから、こんなもんかのレベルで良くてもせいぜい3倍ぐらいかと思っていたので
    3. サーバーにWSGI使っても見かけレスポンス時間が早くなったとは感じない

70m secぐらいでレスが返るということは、efficientdet_lite0.tfliteモデル使えばラズパイ5で十数フレームぐらいの動作は可能ということが言える

 

admin