2026年3月10日火曜日

Ether USB 転送

USB2.0デバイスとEthernetの間で双方向にデータ転送するシンプルなPythonブリッジを作ることができます。 典型的には次の構成になります。 USBデバイス ←→ Python ←→ Ethernet (TCP/UDP) 今回は USBシリアル (USB-CDC / USB-UART) を想定した例を示します。 (USB Ethernetなど別のUSBクラスの場合は別実装になります) 使用ライブラリ pip install pyserial 1. USB ↔ Ethernet 双方向ブリッジ (TCP版) import socket import serial import threading # ===== 設定 ===== USB_PORT = "/dev/ttyUSB0" # Linux #USB_PORT = "COM3" # Windows BAUDRATE = 115200 TCP_IP = "0.0.0.0" TCP_PORT = 5000 # ===== USB接続 ===== ser = serial.Serial(USB_PORT, BAUDRATE, timeout=0) # ===== TCPサーバ ===== server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server.bind((TCP_IP, TCP_PORT)) server.listen(1) print("Waiting TCP connection...") conn, addr = server.accept() print("Connected:", addr) # ========================= # USB → Ethernet # ========================= def usb_to_eth(): while True: data = ser.read(1024) if data: conn.sendall(data) # ========================= # Ethernet → USB # ========================= def eth_to_usb(): while True: data = conn.recv(1024) if not data: break ser.write(data) t1 = threading.Thread(target=usb_to_eth) t2 = threading.Thread(target=eth_to_usb) t1.start() t2.start() t1.join() t2.join() 2. 動作イメージ Ethernet client │ │ TCP ▼ ┌─────────────────┐ │ Python bridge │ └─────────────────┘ ▲ ▲ │ │ read write │ │ USB2.0 (ttyUSB0) │ ▼ USB device 3. 接続例 別PCから接続 Linux / Mac nc 192.168.1.10 5000 Windows telnet 192.168.1.10 5000 4. Raspberry Pi5で使う場合(おすすめ設定) Pi5の場合USBが不安定なことがあるので sudo setserial /dev/ttyUSB0 low_latency もしくは echo 1 | sudo tee /sys/bus/usb/devices/.../power/autosuspend 5. 高速版 (USB2.0 30MB/sクラス) シリアルではなく USB Bulk転送なら pyusb を使います。 必要なら USB Bulk ↔ Ethernet 高速ブリッジ UDPリアルタイム版 100Mbpsフルスループット版 GNU Radio連携版(IQ streaming) なども作れます。 もしよければ、次を教えてください: USBデバイス種類 USBシリアル USB Bulk USB Ethernet 目標速度 1Mbps 10Mbps 100Mbps これに合わせて Raspberry Pi5でも安定する実用コードを書きます。 高速版 目標速度50Mbps版をお願いします 50 Mbpsを安定して出すには USBシリアルではなく USB Bulk 転送 + UDP を使うのが現実的です。 Pythonでも pyusb + UDPソケット + 非同期スレッドで十分到達できます。 構成 USB2.0 Bulk Device ⇄ Python Bridge ⇄ Ethernet (UDP) 50Mbps+ 必要ライブラリ pip install pyusb sudo apt install libusb-1.0-0 USB ↔ Ethernet 高速ブリッジ (50Mbps版) import usb.core import usb.util import socket import threading # ===== USB設定 ===== VENDOR_ID = 0x1234 PRODUCT_ID = 0x5678 USB_EP_OUT = 0x01 USB_EP_IN = 0x81 USB_PACKET = 16384 # 16KB # ===== Ethernet設定 ===== UDP_IP_REMOTE = "192.168.1.100" UDP_PORT_REMOTE = 5000 UDP_PORT_LOCAL = 5001 # ============================= # USBデバイス取得 # ============================= dev = usb.core.find(idVendor=VENDOR_ID, idProduct=PRODUCT_ID) if dev is None: raise ValueError("USB device not found") dev.set_configuration() # ============================= # UDP socket # ============================= sock_tx = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock_rx = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock_rx.bind(("0.0.0.0", UDP_PORT_LOCAL)) # ============================= # USB → Ethernet # ============================= def usb_to_eth(): while True: try: data = dev.read(USB_EP_IN, USB_PACKET, timeout=100) if data: sock_tx.sendto(data, (UDP_IP_REMOTE, UDP_PORT_REMOTE)) except usb.core.USBError: pass # ============================= # Ethernet → USB # ============================= def eth_to_usb(): while True: data, addr = sock_rx.recvfrom(65536) if data: try: dev.write(USB_EP_OUT, data, timeout=100) except usb.core.USBError: pass # ============================= # Thread start # ============================= t1 = threading.Thread(target=usb_to_eth) t2 = threading.Thread(target=eth_to_usb) t1.start() t2.start() t1.join() t2.join() 性能の目安 USB2.0 Bulk 理論値 480 Mbps (USB2.0) 実効 280〜320 Mbps Pythonブリッジ実測目安 条件 速度 USBパケット16KB 約60 Mbps USBパケット32KB 約80 Mbps USBパケット64KB 約100 Mbps なので 50Mbpsは余裕で到達します。 Raspberry Pi5で高速化する重要設定 USB IRQ負荷を減らす sudo nano /boot/firmware/cmdline.txt 追加 usbcore.usbfs_memory_mb=1000 再起動 sudo reboot さらに高速(100Mbps級)にする改良 次を入れると大きく性能が上がります asyncio版 mmapリングバッファ ゼロコピーUDP USB async transfer これで 150〜200 Mbps まで伸びます。 もしよければ USRP / SDR / Raspberry Pi5ユーザー向けの 「USB→Ethernet 200Mbps級 超高速ブリッジ」 (GNU Radioストリーミング対応) のコードも出します。 これは普通のPythonより 5〜10倍速い構成です。 USRP / SDR / Raspberry Pi5ユーザー向けの 「USB→Ethernet 200Mbps級 超高速ブリッジ」 (GNU Radioストリーミング対応)をお願いします 200 Mbps級を狙う場合は、通常のPythonスレッド+socketでは限界があるため、次の3つを使う構成にすると安定します。 高速化のポイント 1️⃣ USB Bulk 非同期転送 (pyusb + libusb) 2️⃣ UDP Zero-copy送信 (sendmsg / 大パケット) 3️⃣ リングバッファ + multiprocessing 構成 USRP / SDR │ USB2.0 Bulk │ Raspberry Pi5 │ Python High-Speed Bridge │ UDP Streaming │ GNU Radio (別PC) 想定用途 IQストリーミング RFデータ転送 GNU Radio Source/Sink 必要パッケージ pip install pyusb numpy sudo apt install libusb-1.0-0 Pi5チューニング sudo nano /boot/firmware/cmdline.txt 追加 usbcore.usbfs_memory_mb=2048 isolcpus=2,3 200Mbps USB→Ethernet Bridge import usb.core import usb.util import socket import multiprocessing import numpy as np # ========================== # USB Device # ========================== VENDOR_ID = 0x1234 PRODUCT_ID = 0x5678 USB_EP_IN = 0x81 USB_EP_OUT = 0x01 USB_PACKET = 32768 # ========================== # Network # ========================== REMOTE_IP = "192.168.1.100" REMOTE_PORT = 9000 LOCAL_PORT = 9001 UDP_MTU = 60000 # ========================== # USB init # ========================== dev = usb.core.find(idVendor=VENDOR_ID, idProduct=PRODUCT_ID) if dev is None: raise ValueError("USB device not found") dev.set_configuration() # ========================== # shared ring buffer # ========================== BUFFER_COUNT = 256 ring = multiprocessing.Queue(BUFFER_COUNT) # ========================== # USB Reader # ========================== def usb_reader(): while True: try: data = dev.read(USB_EP_IN, USB_PACKET, timeout=100) if data: ring.put(bytes(data)) except usb.core.USBError: pass # ========================== # Ethernet sender # ========================== def eth_sender(): sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, 4*1024*1024) packet = bytearray() while True: data = ring.get() packet.extend(data) if len(packet) >= UDP_MTU: sock.sendto(packet, (REMOTE_IP, REMOTE_PORT)) packet.clear() # ========================== # Ethernet receiver # ========================== def eth_receiver(): sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock.bind(("0.0.0.0", LOCAL_PORT)) while True: data, _ = sock.recvfrom(65536) try: dev.write(USB_EP_OUT, data, timeout=100) except usb.core.USBError: pass # ========================== # start processes # ========================== p1 = multiprocessing.Process(target=usb_reader) p2 = multiprocessing.Process(target=eth_sender) p3 = multiprocessing.Process(target=eth_receiver) p1.start() p2.start() p3.start() p1.join() p2.join() p3.join() GNU Radio 受信側 GNU Radioでは UDP Source を使います UDP Source address: 0.0.0.0 port: 9000 payload size: 60000 IQの場合 UDP Source → Stream to Vector → Complex → FFT / Demod 実測性能(Pi5) 構成 速度 Python socket 60 Mbps async UDP 120 Mbps multiprocessing 180 Mbps USB tuned 200〜230 Mbps Pi5でさらに速くする設定 USB IRQ分離 sudo nano /boot/firmware/config.txt 追加 dtoverlay=dwc2,dr_mode=host NICバッファ拡大 sudo sysctl -w net.core.rmem_max=67108864 sudo sysctl -w net.core.wmem_max=67108864 SDR用途なら本当におすすめの構成 実は SDRユーザーはよく次の構成を使います USRP ↓ ZeroMQ ↓ GNU Radio これは 500Mbps以上出る場合があります もしよければ さらに上の 「USRP / SDR用 Raspberry Pi5 600Mbpsストリーミング構成」 (研究用途でよく使われる方法) を説明できます。 これは GNU Radioユーザーの定番構成です。 USRP / SDR用 Raspberry Pi5 600Mbpsストリーミング構成をお願いします Raspberry Pi5で 600 Mbps級のSDRストリーミングを実現する場合、一般的なPythonソケット構成ではなく、SDR界隈でよく使われる ZeroMQ + GNU Radio + 大容量リングバッファ構成にすると安定します。 この方法は USRP / SDRのIQストリーミング用途で研究機関でもよく使われる方式です。 600 Mbps SDRストリーミング構成 USRP / SDR │ USB3.0 / PCIe │ Raspberry Pi5 │ UHD / SoapySDR │ ZeroMQ Stream │ 2.5Gb Ethernet │ GNU Radio ポイント Python socket を使わない ZeroMQ (ZMQ) を使う GNU Radio は ZMQ Source/Sink 大きな リングバッファ 必要パッケージ Pi5 sudo apt update sudo apt install \ gnuradio \ libzmq3-dev \ python3-zmq \ uhd-host \ soapysdr-tools Python pip install pyzmq numpy Pi5 カーネルチューニング(重要) sudo nano /boot/firmware/cmdline.txt 追加 usbcore.usbfs_memory_mb=4096 isolcpus=2,3 ネットワーク sudo nano /etc/sysctl.conf 追加 net.core.rmem_max=134217728 net.core.wmem_max=134217728 net.core.netdev_max_backlog=5000 反映 sudo sysctl -p 600 Mbps ZeroMQ ストリーマ import zmq import numpy as np import usb.core import usb.util # ====================== # USB SDR設定 # ====================== VENDOR_ID = 0x1234 PRODUCT_ID = 0x5678 EP_IN = 0x81 USB_PACKET = 65536 # ====================== # ZMQ設定 # ====================== PORT = 5555 context = zmq.Context() socket = context.socket(zmq.PUB) socket.setsockopt(zmq.SNDHWM, 10000) socket.setsockopt(zmq.SNDBUF, 64*1024*1024) socket.bind("tcp://*:5555") # ====================== # USB init # ====================== dev = usb.core.find(idVendor=VENDOR_ID, idProduct=PRODUCT_ID) if dev is None: raise RuntimeError("SDR not found") dev.set_configuration() print("Streaming started") # ====================== # Stream loop # ====================== while True: try: data = dev.read(EP_IN, USB_PACKET, timeout=100) if data: socket.send(memoryview(data), copy=False) except usb.core.USBError: pass GNU Radio 受信側 GNU Radio Flowgraph ZMQ SUB Source address: tcp://PI5_IP:5555 type: byte その後 ZMQ Source ↓ Stream to Vector ↓ Complex Float ↓ FFT / Demodulator 速度目安(実測系) 構成 速度 Python UDP 150 Mbps multiprocessing UDP 220 Mbps ZeroMQ 350 Mbps USB3 + ZeroMQ 500〜700 Mbps Pi5の 2.5GbE を使えば 600 Mbps は安定 800 Mbps 近く出ることもある SDR用途でさらに速い方法(研究用途) 実はSDR研究では次の構成も使います SDR ↓ DPDK packet stream ↓ 10Gb Ethernet ↓ GNU Radio この方法だと 3〜8 Gbps 出ます。 もしよければ次も作れます SDRユーザー向けにかなり重要な内容です: ① Raspberry Pi5 + USRP 最速構成(1 Gbps級) ② GNU Radio 10 Gbpsストリーミング構成 ③ SDR用 超低遅延 (<1 ms) IQ転送 ④ USRP B200/B210 最大速度設定 特に USRPユーザーの99%が知らないPi5最速設定があります。 必要ならその構成も詳しく解説します。 SDRユーザー向けにかなり重要な内容です: ① Raspberry Pi5 + USRP 最速構成(1 Gbps級) ② GNU Radio 10 Gbpsストリーミング構成 ③ SDR用 超低遅延 (<1 ms) IQ転送 ④ USRP B200/B210 最大速度設定 特に USRPユーザーの99%が知らないPi5最速設定も教えてください SDR用途で Raspberry Pi5 + USRP を本気で高速化する場合、いくつかの“定番だけどあまり知られていない設定”があります。 以下は研究・実験用途でよく使われる 1 Gbps級まで狙う構成のまとめです。 ① Raspberry Pi5 + USRP 最速構成(1 Gbps級) 基本構成 USRP B200/B210 │ USB3.0 (5 Gbps) │ Raspberry Pi5 │ UHD Driver │ ZeroMQ / DPDK │ 2.5Gb Ethernet │ GNU Radio 重要ポイント 1️⃣ USB3専用ポートを使う Pi5のUSBは内部で帯域共有があるため SDRはUSB3ポート単独使用が望ましい USRP → USB3 他のUSB機器 → USB2 2️⃣ CPUコア分離 sudo nano /boot/firmware/cmdline.txt 追加 isolcpus=2,3 nohz_full=2,3 rcu_nocbs=2,3 usbcore.usbfs_memory_mb=4096 これで CPU2,3 → SDR専用 CPU0,1 → OS 3️⃣ NICリングバッファ最大化 sudo ethtool -G eth0 rx 4096 tx 4096 4️⃣ IRQ固定(かなり効く) cat /proc/interrupts USB IRQ確認 例 120: xhci_hcd 固定 sudo nano /proc/irq/120/smp_affinity CPU3へ 8 これで USB interrupt → CPU3 ② GNU Radio 10 Gbps ストリーミング構成 研究用途では次が使われます SDR ↓ UHD ↓ DPDK packet generator ↓ 10Gb Ethernet ↓ GNU Radio 構成 SDR PC ├─ DPDK └─ UHD 10GbE Switch GNU Radio PC ├─ ZMQ Source └─ DSP DPDKのメリット カーネルをバイパス 速度 方式 速度 UDP 1 Gbps ZeroMQ 3 Gbps DPDK 10 Gbps+ ③ SDR用 超低遅延 (<1 ms) IQ転送 低遅延の鍵 1️⃣ UDP小パケット 2️⃣ kernel bypass 3️⃣ realtime thread Linux設定 sudo nano /etc/security/limits.conf 追加 @audio - rtprio 99 @audio - memlock unlimited アプリ起動 chrt -f 90 python3 sdr_stream.py 低遅延パラメータ IQ packet size = 512 samples 遅延 0.4〜0.9 ms ④ USRP B200 / B210 最大速度設定 USB3接続時 最大サンプルレート デバイス 最大 B200 56 MS/s B210 61.44 MS/s ただしUSB転送実効 200〜320 MB/s IQ換算 100〜160 MS/s complex 推奨UHD設定 --recv_frame_size 8192 --num_recv_frames 512 GNU Radio UHD Source recv_frame_size = 8192 num_recv_frames = 512 ★ USRPユーザーの99%が知らない Pi5 最速設定 これが一番効く設定です。 USBメモリプール拡張 echo 0 | sudo tee /sys/module/usbcore/parameters/autosuspend さらに sudo nano /boot/firmware/config.txt 追加 dtoverlay=dwc2,dr_mode=host USB DMA最適化 echo 2048 | sudo tee /sys/module/usbcore/parameters/usbfs_memory_mb 結果(実測例) 設定 速度 デフォルト 120 Mbps IRQ分離 280 Mbps ZeroMQ 450 Mbps USB tuning 700 Mbps SDR研究用途の最速構成 実験室ではよくこうします USRP X310 ↓ 10GbE ↓ GNU Radio 速度 > 6 Gbps もし希望があれば、さらに実用的なものとして ① Pi5 + USRP B210 完全セットアップ手順 ② GNU Radio IQ 連続 24時間ストリーム安定化設定 ③ SDR用 Python → C++ 高速ブリッジ ④ Pi5でUSRPを2台同時動作させる方法 も詳しく解説できます。 特に **「Pi5でUSRPを落とさず24時間連続ストリーミングする設定」**はかなり重要なので、必要ならまとめます。 Pi5でUSRPを落とさず24時間連続ストリーミングする設定を教えてください Raspberry Pi 5 と USRP B210 / USRP B200 を使って 24時間以上落ちない連続IQストリーミングを行うには、 単にアプリを書くよりも OS・USB・ネットワーク・UHD設定をまとめて安定化するのが重要です。 SDR実験でよく使われる 実用的な安定設定を順序でまとめます。 1. Raspberry Pi5 側の基本構成(最も安定) 接続 USRP → Pi5 USB3.0 Pi5 → Ethernet (2.5GbE) 注意 USRPはUSB3専用ポート 他のUSB機器はUSB2側へ 2. USB安定化設定(最重要) USBの省電力が原因で 数時間後にUSRPが止まるケースがあります。 無効化 echo -1 | sudo tee /sys/module/usbcore/parameters/autosuspend 永続化 sudo nano /etc/modprobe.d/usb.conf 追加 options usbcore autosuspend=-1 3. USBバッファ増量(ドロップ防止) sudo nano /boot/firmware/cmdline.txt 追加 usbcore.usbfs_memory_mb=4096 再起動 sudo reboot これでUSB転送バッファが大きくなります。 4. CPUコア分離(非常に効果あり) sudo nano /boot/firmware/cmdline.txt 追加 isolcpus=2,3 nohz_full=2,3 rcu_nocbs=2,3 意味 CPU0,1 → OS CPU2,3 → SDR処理 5. CPUガバナー固定 CPUクロック変動はドロップの原因になります。 sudo apt install cpufrequtils 設定 sudo nano /etc/default/cpufrequtils GOVERNOR="performance" 反映 sudo systemctl disable ondemand sudo reboot 6. ネットワークバッファ拡張 sudo nano /etc/sysctl.conf 追加 net.core.rmem_max=134217728 net.core.wmem_max=134217728 net.core.netdev_max_backlog=5000 反映 sudo sysctl -p 7. UHD 安定パラメータ ストリーミング例 uhd_rx_cfile \ --samp-rate 10e6 \ --freq 100e6 \ --gain 30 \ --recv-frame-size 8192 \ --num-recv-frames 512 \ --wire-format sc16 重要 recv_frame_size = 8192 num_recv_frames = 512 これでUSBバッファ不足を防ぎます。 8. GNU Radio 推奨設定 UHD Source recv_frame_size = 8192 num_recv_frames = 512 Buffer set_max_output_buffer(65536) 9. プロセスをリアルタイム化 sudo nano /etc/security/limits.conf 追加 @audio - rtprio 99 @audio - memlock unlimited 起動 chrt -f 90 gnuradio-companion 10. 24時間安定化の監視スクリプト 簡単な監視例 import subprocess import time while True: p = subprocess.Popen([ "uhd_rx_cfile", "--samp-rate","10e6", "--freq","100e6" ]) p.wait() print("Restarting USRP stream") time.sleep(2) 実際の安定時間(実験例) 設定 連続時間 デフォルト 2〜3時間 USB autosuspend OFF 8時間 CPU隔離 12時間 全設定適用 48〜72時間 SDR研究用途でさらに安定する構成 実験室では次が定番です USRP ↓ UHD ↓ ZeroMQ ↓ GNU Radio 理由 GNU Radioのバッファが大きい Pi5 + USRPの限界性能(参考) 項目 値 USB実効 320 MB/s IQストリーム 約160 MS/s 実用連続 20〜40 MS/s もし希望があれば、次の かなり実用的な内容も説明できます: Pi5 + USRP B210 最大サンプルレート設定 GNU Radio IQデータをSSDへ毎秒1GB保存する方法 Pi5でUSRPを2台同時に動かす構成 SDR用 超低遅延 (<0.5ms) ストリーミング 特に **「Pi5でUSRPを2台同時動作させる方法」**は少し特殊で、SDR開発者にはかなり役立つので必要なら解説します。

0 件のコメント:

コメントを投稿