「リアルタイムの高度な物体認識を備えたLego Technicソーターの構築」

「リアルタイムで高度な物体認識を備えたLego Technicソーターの構築方法」

Nullspace Roboticsでのインターンシップ中に、会社の能力を向上させるプロジェクトに取り組む機会を得ました。オブジェクト検出と機械学習画像認識を統合して、リアルタイムでレゴテクニックのピースを分類するマシンを開発しました。

このブログ投稿では、遭遇した困難とこのプロジェクトを成功に導く方法をご紹介します。

私とAmos Kohは夏の’22にNullspaceのこのプロジェクトに取り組みながら、学生にプログラミングとロボティクスを教えました。記事の下に、私たちのリンクを見つけることができます。

Nullspace Roboticsはシンガポールの小学生と中学生向けにロボティクスとプログラミングの教育を提供しているリーディングカンパニーです。彼らのオペレーションの大部分は、特定のトレイに分類されたレゴテクニックのパーツでロボットを組み立てることに関わっています。8歳の遊び心が尽きない子供に、パーツをトレイに戻す助けを求めるという仕事は、悪夢のような課題です。

Nullspaceは、ロボティクスのレッスンを行う際の効率の課題のひとつを解決するために、レゴテクニックのピースを特定のカテゴリーに分類できるマシンを作るように私たちに依頼しました。

課題の定義

このプロジェクトは3つの主要なパートで構成されています:リアルタイムのオブジェクトとモーション検出、画像認識、およびマシンのハードウェアの構築。インターンシップの時間制約を考慮して、私たちは主に最初の2つの項目に焦点を当てました。

重要な課題は、動くパーツを認識し、同じフレーム内でそれらを識別することでした。2つのアプローチを考えました:オブジェクト検出カメラに機械学習画像認識を統合するか、プロセスを分離するか。

最終的に、オブジェクト検出と認識を分離することにしました。このアプローチでは、まずオブジェクトを検出した後、適切な写真を撮影し、それからモデルを実行して画像を分類します。プロセスを統合すると、検出されたすべてのオブジェクトを分類するためにほぼすべてのフレームでモデルを実行する必要があります。それらを分離することで、モデルを常に処理モードにする必要がなくなり、よりスムーズで計算効率の高い操作が可能になります。

オブジェクト検出

私たちは、記事の下に引用されているプロジェクトからアイデアを借りて、オブジェクト/モーション検出プログラムを実装し、レゴのピースにカスタマイズしました。

私たちの場合、均一な色のコンベアベルトシステムがあり、検出されるモーションはベルト上で動くレゴのピースによるものでしたので、似たようなモーション検出のコンセプトを使用しました。

私たちは、すべてのフレームにガウシアンぼかしと他の画像処理技術を適用し、前のフレームと比較しました。モーションを引き起こすアイテムを分離(境界ボックスを描画)するためにさらなる処理が行われました。以下に示します:

for f in camera.capture_continuous(rawCapture, format="bgr", use_video_port=True):        frame = f.array # grab the raw NumPy array representing the image    text = "No piece" # initialize the occupied/unoccupied text    # resize the frame, convert it to grayscale, and blur it    frame = imutils.resize(frame, width=500)    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)    gray = cv2.GaussianBlur(gray, (21, 21), 0)    # if the average frame is None, initialize it    if avg is None:        print("[INFO] starting background model...")        avg = gray.copy().astype("float")        rawCapture.truncate(0)        continue    # accumulate the weighted average between the current frame and    # previous frames, then compute the difference between the current    # frame and running average    cv2.accumulateWeighted(gray, avg, 0.5)    frameDelta = cv2.absdiff(gray, cv2.convertScaleAbs(avg))        # threshold the delta image, dilate the thresholded image to fill    # in holes, then find contours on thresholded image    thresh = cv2.threshold(frameDelta, conf["delta_thresh"], 255,        cv2.THRESH_BINARY)[1]    thresh = cv2.dilate(thresh, None, iterations=2)    cnts = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL,        cv2.CHAIN_APPROX_SIMPLE)    cnts = imutils.grab_contours(cnts)    # loop over the contours        for c in cnts:        # if the contour is too small, ignore it        if cv2.contourArea(c) < conf["min_area"]:            continue        # compute the bounding box for the contour, draw it on the frame,        # and update the text        (x, y, w, h) = cv2.boundingRect(c)        cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 2)        piece_image = frame[y:y+h,x:x+w]        text = "Piece found"        # cv2.imshow("Image", image)

レゴのブロックによる運動が実際に引き起こされたかどうかを確認するために、運動検知の安定性を評価するためにモーションカウンターを使用しました。モーションが一定数のフレームで検出されたことを確認してから、モーションが実際にレゴのブロックに起因するものであり、その他のノイズではないことを結論付けました。その後、最終画像を保存し、CNNにフィードして分類します。

if text == "ピースが見つかりました":        # バウンディングボックスの画像を保存するため        motionCounter += 1        print("motionCounter= ", motionCounter)        print("image_number= ", image_number)        # 8回以上の連続フレームでモーションが検出された場合に画像を保存します        if motionCounter >= 8:            image_number += 1            image_name = str(image_number)+"image.jpg"            cv2.imwrite(os.path.join(path, image_name), piece_image)            motionCounter = 0 #モーションカウンターをリセットします # 保存された画像をモデルで分類します(以下を参照)

モデルの作成

データセットの作成

モデルが最終的にピースを検出および分類する条件を再現するため、オンラインで見つかったレゴテクニックブロックの画像を使用せずに、私たちは自分自身で画像のデータセットを作成しました。そのために、レゴテクニックブロック自体を使用して簡単なコンベアベルトシステムを設計しました!それをレゴスパイクプライムモーターに接続してコンベアベルトを動かしました。

モデルアーキテクチャの設計

この課題の核心に対処するために、私はAladdinperssonのGitHubリポジトリで見つけた機械学習モデルを適応しました。このモデルには、画像認識を向上させるためのアーキテクチャ的な選択肢として、128から64から32から16までのシーケンスを持つ畳み込み層があります。

事前訓練済みモデルの代わりに、私たちは独自の畳み込みニューラルネットワークを設計しました。なぜなら:

  1. 画像の特徴抽出に特に深い特徴抽出は必要としなかったからです
  2. モデルを小さくし、複雑さを抑え、モデルの計算コストを削減したかったからです。これにより、Piでより効率的にtfliteモデルとしてCNNを実行できるようになります。

データの正規化は、光の差異による異なる画像でキャプチャされる値の範囲の変動に起因する一貫したトレーニング精度を確保するために重要なステップでした。

このモデルでは、ReLU、Dense、Softmax、Flattenなどのさまざまなレイヤーが重要な役割を果たしました。たとえば、ReLU活性化は画像分類において勾配消失の問題を緩和するために必要でした。一方、Dense層はTensorFlowモデルで標準的なものであり、密結合ニューラルネットワークを容易にします。Softmax活性化は、データセット内の各カテゴリの確率を計算するために使用されました。

損失関数には、マルチクラス分類タスクに適したKerasのスパースカテゴリカルクロスエントロピーを使用しました。効率的なKeras Adamオプティマイザはモデルの微調整に使用されました。

トレーニングと最適化

エポックはトレーニングと過学習のバランスをとるため、優れたモデルパフォーマンスを確保するために200未満の数を好みました。高速なモデルトレーニングのために、私たちはGoogle Colabを利用してGPUリソースにアクセスし、自分のノートパソコンと比べて大幅に高速なトレーニング速度を実現しました。

完全なモデルアーキテクチャは以下の通りです:

data_augmentation = keras.Sequential([    layers.RandomFlip("horizontal",                        input_shape=(img_height,                                    img_width,                                    1)),    layers.RandomRotation(0.2),    layers.RandomZoom(0.1),    ])model = keras.Sequential(    [        data_augmentation,            layers.Rescaling(1./255, input_shape = (img_height,img_width,1)), #データの正規化input        layers.Conv2D(128, 3, padding="same", activation='relu'),        layers.MaxPooling2D(pool_size=(2,2)),        layers.Conv2D(64, 3, padding="same", activation='relu'), #これは16または32ユニットにすべきですか?より多くのデータで試してみてください        layers.MaxPooling2D(pool_size=(2,2)),        layers.Conv2D(32, 3, padding="same", activation='relu'),        layers.MaxPooling2D(pool_size=(2,2)),                layers.Conv2D(16, 3, padding="same", activation='relu'),        layers.MaxPooling2D(pool_size=(2,2)),                layers.Dropout(0.1),        layers.Flatten(),        layers.Dense(10,activation = 'relu'),        layers.Dense(7,activation='softmax'), #出力クラスの数            ])        model.compile(    optimizer=keras.optimizers.Adam(),    loss=[keras.losses.SparseCategoricalCrossentropy(from_logits=False),],    metrics=["accuracy"],)model_history = model.fit(x_train, y_train, epochs=200, verbose=2, validation_data=(x_test,y_test), batch_size=25)  #batch sizeは25/32が最適だと思います

モデルの結果

モデルは、レゴテクニックブロックの7つのカテゴリーにわたる6000枚の画像で訓練されました。最終的なバリデーションの精度は93%です。以下にはトレーニングの進行を示す図やパフォーマンスの評価に使用される混同行列も表示されています。

Raspberry Piへのモデルの実装

ラズベリーパイ上でニューラルネットワークを実行する最も効率的な方法は、tflite(TensorFlow Lite)モデルとして実行することです。私たちはモデルをローカルに保存し、それをパイに読み込みました。

from tflite_runtime.interpreter import Interpreter# TFLiteモデルをロードし、テンソルを割り当てる.interpreter = Interpreter(model_path="lego_tflite_model/detect.tflite") # tfliteモデルへのパスを挿入するinterpreter.allocate_tensors()

上記のモーションカウンターループから続けて、適切な画像がニューラルネットワークに供給されて分類されました。

# if text == "Piece found"から続ける:            # 画像を開き、リサイズしてコントラストを上げる            input_image = Image.open('lego-pieces/'+ image_name)            input_image = ImageOps.grayscale(input_image)            input_image = input_image.resize((128,128))            input_data = img_to_array(input_image)            input_data = increase_contrast_more(input_data)            input_data.resize(1,128,128,1)                        # 画像のnp.arrayをtfliteモデルに渡します。これにより、確率ベクトルが出力されます            interpreter.set_tensor(input_details[0]['index'], input_data)            interpreter.invoke()            output_data = interpreter.get_tensor(output_details[0]['index'])                        # 確率ベクトルの中で最も高い値のインデックスを取得します。            # このインデックス値は上記で作成されたラベルベクトルに対応します(つまり、インデックス値1はラベル[1]に最も関連があることを意味します)。            category_number = np.argmax(output_data[0])            # 画像の分類ラベルを返す                classification_label = labels[category_number]                            print("Image Label for " + image_name + " is :", classification_label)                                        else:        motionCounter = 0 # 新しいオブジェクトを探すためにモーションカウンターをリセット

柔軟性は重要な考慮事項でした。モーションカウンターは、データセットを構築するための画像のキャプチャのプロセスや、画像が分類されるためにキャプチャされる閾値を設定するために調整できます。これにより、システムの汎用性が向上します。

デモンストレーション

私たちの努力の集大成は、システムの全体的な精度をサポートするための写真と操作のキャプチャを含むショーケースでした。以下のコンベアベルトセットアップがこのデモンストレーションの重要な部分でした:

今後の展望と改善領域

ソフトウェア:より高品質の画像のために、モデルは確かに優れたカメラを活用することができるでしょう。さらに、将来的には、部品を分類するための適切な画像を確保するために品質チェッカーモデルも含めることができます。

ハードウェア:私たちのテストとデモンストレーションのために一時的に作成したコンベアベルトシステムは、より多くのピースを収容するためにスケールアップする必要があります。また、複数のレゴブロックを分離し、カメラのフレーム内に1つのピースのみが表示されるようにする方法も考案し、実装する必要があります。オンラインで利用可能な類似のプロジェクトでは、可能な方法について詳細が説明されています。

結論

Nullspace Roboticsでの私の旅は、実用目的のために自分自身のニューラルネットワークを構築する初めての試みでした。過去のトレーニングコースでモデルを設計した経験がありますが、実際の製品に向けて設計するという点では、さまざまな要素(リソース、使用目的など)を考慮する必要があります。私は機械学習の旅を続け、最新のAI技術を駆使してより革新的な解決策を構築することを楽しみにしています。

このプロジェクトに取り組む機会を与えてくれたNullspaceに感謝申し上げます。ロボティクス教育の限界に挑戦する会社として、今後の展望にワクワクしています。

プロジェクトに関する詳しいリポジトリ、コードのGithubやHuggingFaceでデータセットの画像へのアクセスや詳細情報をチェックしてください:

Github: https://github.com/magichampz/lego-sorting-machine-ag-ak/

HuggingFace: https://huggingface.co/magichampz

開発者に会ってみましょう

Aveek: https://www.linkedin.com/in/aveekg00/

Amos: https://www.linkedin.com/in/ak726/

参考文献:

PythonとOpenCVを使用した基本的なモーション検出とトラッキング — PyImageSearch

このチュートリアルでは、PythonとOpenCVを使用して基本的なモーション検出とトラッキングを行う方法を紹介します。学んでみましょう…

pyimagesearch.com

OpenCVを使用したモーション検出 — 初心者のための画像解析

OpenCVを使用した動く物体の検出と分析の方法

towardsdatascience.com

Machine-Learning-Collection/ML/TensorFlow/Basics/tutorial15-customizing-modelfit.py at master ·…

機械学習とディープラーニングに関する学習リソース …

github.com

アウトテイク

特に記載がない限り、すべての画像は著者によるものです

We will continue to update VoAGI; if you have any questions or suggestions, please contact us!

Share:

Was this article helpful?

93 out of 132 found this helpful

Discover more