弊社は「しまぱん.com (←リンク先はNSFWです。ご注意ください)」というブランドでAmazonに出品しています。FBAサービスを利用しているおかげで手間は減っていますが、販売数が伸びるにつれ、FBA倉庫への出荷作業の負担が大きくなってきました。

そこで、何かしらの対策を考えたいと思います。方法はいろいろありますが、手元には「SBC」と「カメラ付きのエッジAI」がいくつか使われずに遊んでいます。今回はその中から「Raspberry Pi4」と「Maix Amigo」を活用することにしました。

Maix Amigoとは

Maix Amigo は、Kendryte K210 を搭載したエッジAI向けの開発ボードです(Maix Amigo - Sipeed Wiki)。 Maix シリーズの中では少し前に発売された古いモデルですが、今回の用途にはちょうど良さそうなので使うことにしました。 ちょっともったいない気もしますが、眠らせておくよりは良いかなと。

選んだ理由は次のとおりです:

  • カメラ・スピーカー内蔵 → 外付けパーツなしで使える
  • ケース入り → ケースを自作しなくて良い
  • 小型 → 手に収まりやすいサイズで持ちやすい
  • シリアル通信ができる → RaspberryPiとの連携にも問題なし
  • バーコードリーダーのサンプルプログラムがある → 実装できそうな気がする

バーコードリーダーを作るための構成

構成要素は以下の3つです。

  • Maix Amigo - バーコード読み取り
  • Raspberry Pi 4 - 読み取ったバーコードをもとに出荷の処理を行う
  • ディスプレイ - 出荷作業中の確認や操作用

Maix Amigo

Maix Amigo - Sipeed Wiki

RaspberryPi 4

ディスプレイの背面に設置します。

RaspberryPi用ディスプレイ

HITH-互換性のあるタッチスクリーンHCDHdmi,5, 7インチタッチスクリーン,ヒップ1024x600,rpi 4b,Framd,opi 5,aida64,pcセカンダリディスプレイ(スピーカーなしタイプ)

イメージ

  1. USBでRaspberryPiとMaix Amigoを接続し、Maix Amigoが読み取ったバーコードを送信
  2. RaspberryPiはそのバーコードをもとにディスプレイに商品情報を表示したり、そのバーコードの商品を数えたりする

今回は、RaspberryPiにコードを送信して表示するところまでやってみようと思います。

実装 - Maix Amigo側

Maix Amigo側の処理は主に以下の3つです。

  • バーコードスキャン
  • サウンドの再生
  • データの送信

Maix Amigoのflashはそれほど大きくはないため、使用するライブラリやwavファイルはSDカードに保存しました。

ブート

boot.py

import time
import sys
sys.path.append('/sd')
import audio_handler
import scanner

BOOT_SOUND_FILE="/sd/one11.wav"
SCAN_SOUND_FILE="/sd/button26.wav"

scanner.init_camera()
audio_handler.play_wav(BOOT_SOUND_FILE)

while True:
    barcode = scanner.scan_barcode()

    if barcode:
        print("<" + barcode + ">")
        barcode = None
        audio_handler.play_wav(SCAN_SOUND_FILE)
    time.sleep(0.1)

バーコードスキャン

/sd/scanner.py

import sensor
import image
import lcd
import time

clock = time.clock()
def init_camera():
    clock = time.clock()
    lcd.init()
    sensor.reset()
    sensor.set_hmirror(1)
    sensor.set_pixformat(sensor.RGB565)
    sensor.set_framesize(sensor.QVGA)
    sensor.set_vflip(1)
    sensor.run(1)
    sensor.skip_frames(time=2000)

def scan_barcode():
    clock.tick()
    img = sensor.snapshot()
    codes = img.find_barcodes()
    fps =clock.fps()
    if len(codes) > 0:
        img.draw_string(2,2, codes[0].payload(), color=(0,128,0), scale=2)

    lcd.display(img)
    return codes[0].payload() if codes else None

音再生

/sd/audio_handler.py

読み込んでいるライブラリのes8374.pyはMaixの公式GitHubにあります。es8374.pyは単体で実行すると、内臓マイクとスピーカーを使って録音、再生するプログラムが動きます。

import sys
sys.path.append('/sd')
from es8374 import ES8374,ES_MODULE
from machine import I2C
from fpioa_manager import *
from Maix import I2S
import audio, time

i2c = I2C(I2C.I2C3, freq=600*1000, sda=27, scl=24) # amigo

def play_wav(file_path):
    player = None
    i2s = None

    try:
        dev = ES8374(i2c)

        dev.setVoiceVolume(90)

        dev.start(ES_MODULE._ES_MODULE_ADC_DAC)

        # init i2s(i2s0)
        i2s = I2S(I2S.DEVICE_0, pll2=262144000, mclk=31)

        # config i2s according to audio info # STANDARD_MODE LEFT_JUSTIFYING_MODE RIGHT_JUSTIFYING_MODE
        i2s.channel_config(I2S.CHANNEL_2, I2S.TRANSMITTER, resolution=I2S.RESOLUTION_16_BIT, cycles=I2S.SCLK_CYCLES_32, align_mode=I2S.STANDARD_MODE)

        fm.register(13,fm.fpioa.I2S0_MCLK, force=True)
        fm.register(21,fm.fpioa.I2S0_SCLK, force=True)
        fm.register(18,fm.fpioa.I2S0_WS, force=True)
        fm.register(35,fm.fpioa.I2S0_IN_D0, force=True)
        fm.register(34,fm.fpioa.I2S0_OUT_D2, force=True)

        # init audio
        player = audio.Audio(path=file_path)
        player.volume(100)

        # read audio info
        wav_info = player.play_process(i2s)
        # print("wav file head information: ", wav_info)
        i2s.set_sample_rate(wav_info[1])
        # print('loop to play audio')
        while True:
            ret = player.play()
            if ret == None:
                # print("format error")
                break
            elif ret == 0:
                break
        player.finish()
    except Exception as e:
        print("【エラー発生: play_wav()】", e)
    finally:
        if player:
            del player
        if 'i2s' in globals():
            del i2s

データの送信

MaixAmigoにはType-Cのポートが2つあり、一つはST-Link互換のシリアル通信が行えるデバッグポートになっていて、もう一つはMaixAmigoに搭載されているM1モジュールに直結したポートになっています。 今回はデバッグポートを使用しました。こちらは標準出力がRaspberryPiにそのまま送信されるため、追加のコードなしで簡単にデータを送れます。

今回のプログラムでいうデータ送信部分は、boot.pyのprint("<" + barcode + ">") の箇所になります。

実装 - RaspberryPi側

RaspberryPi側の処理は今回は以下の2つです。

  • Maix Amigoからのデータ受信
  • データの表示

Maix amigoのデバッグポートとRaspberryPiのUSBポートを接続します。今回は簡単にnode-redで受信したコードを表示させてみました。
表示にはNode-RED Dashboard 2.0を使いました。node-red-dashboardは、3週間前くらいにリリースが出てはいますが、 2024 年 6 月 27 日でプロジェクト廃止予定らしいです。 *1 node-red-dashoboard

デモ

読み取れました!🎉
実際にバーコードをスキャンすると、こんな感じで画面に表示されます。

まとめ

そこそこ実用になる気がしてきました。正直言えば、Raspberry Piに直接カメラをつないで処理させた方が楽だったんじゃないかと思うこともありましたが、都合良く余っているカメラもなかったのでとりあえず作りきってみました。調べている途中で、案外バーコードリーダーが安いことを発見してしまったので、これもまた試してみようと思います。

次は、二重登録防止や商品情報の表示、カウント部分あたりを作って、とにかく出荷作業の効率を上げていきたいと思います。

参考

最近のエントリー

ブラウザでの画面表示と印刷の描画差異に関する実践的考察

RSSリーダを作りました

Puppeteerは過去の遺物だったのでPlaywrightにした話

💥 Prisma vs Sequelize:実戦投入してわかった本当の話

第14回 人工知能は「第五の火」である

DeepSeek-R1-Distill-Qwen-14B-Japaneseの量子化ビット数による答えの精度の違い

使われていないMaix Amigo を活用して、バーコードリーダーにする

TinySwallow-1.5B-Instruct

cyberagent/DeepSeek-R1-Distill-Qwen-14B-Japanese

パスワードマネージャの一手法(公知化情報)

中華安お掃除ロボット(650円)を買ってみました

phi-4とDeepSeek-R1の翻訳性能比較

phi-4

DeepSeek-R1-Distill-Qwen-14B

新オフィスの様子

基本のパン

社会制度のこと

気候のこと

新春のお散歩

あけましておめでとうございます