ゆるエンジニアはいろいろ遊びたい

FAエンジニアが週末にいろいろ遊ぶブログです

Ultralytics YOLOを使って画像認識してみた

4脚ロボットShelPiにはラズパイのカメラモジュールV2を搭載しています。
slowtech.hateblo.jp
slowtech.hateblo.jp
mjpg-streamerでカメラの映像をストリーミングしているので、
メインPCで画像を取得し、AIによる物体認識をしてShelPiの行動を制御することが可能です。
今回はメインPCでUltralytics YOLOを使用して画像認識ができるかテストしてみました。
使用した言語はPythonです。
環境を汚さないようにvenvで仮想環境を作成します。

python3 -m venv yolo
source yolo/bin/activate

仮想環境下で、YOLOをインストールします。

pip install ultralytics

サンプルコードは以下

from ultralytics import YOLO

model = YOLO("yolov8n.pt")  # 軽量モデル
results = model("test.jpg")
results[0].show()

pythonファイルと同じディレクトリに適当な画像をいれ、test.jpgと名前を変更したら実行します。
初回実行時は何かダウンロードが始まりますので少し待ちます。
成功すれば画像がプレビューされます。
今回使用したYOLO8nは軽量モデルで、80種類のオブジェクトを認識できるようです。

from ultralytics import YOLO

model = YOLO("yolov8n.pt")
print(model.names)

これで、検出可能なリストが出てきます。

 0: person
 1: bicycle
 2: car
 3: motorcycle
 4: airplane
 5: bus
 6: train
 7: truck
 8: boat
 9: traffic light
10: fire hydrant
11: stop sign
12: parking meter
13: bench
14: bird
15: cat
16: dog
17: horse
18: sheep
19: cow
20: elephant
21: bear
22: zebra
23: giraffe
24: backpack
25: umbrella
26: handbag
27: tie
28: suitcase
29: frisbee
30: skis
31: snowboard
32: sports ball
33: kite
34: baseball bat
35: baseball glove
36: skateboard
37: surfboard
38: tennis racket
39: bottle
40: wine glass
41: cup
42: fork
43: knife
44: spoon
45: bowl
46: banana
47: apple
48: sandwich
49: orange
50: broccoli
51: carrot
52: hot dog
53: pizza
54: donut
55: cake
56: chair
57: couch
58: potted plant
59: bed
60: dining table
61: toilet
62: tv
63: laptop
64: mouse
65: remote
66: keyboard
67: cell phone
68: microwave
69: oven
70: toaster
71: sink
72: refrigerator
73: book
74: clock
75: vase
76: scissors
77: teddy bear
78: hair drier
79: toothbrush

Raspberry Pi ZERO 2Wや、RaspberryPi5では、処理速度が遅くてあまりリアルタイムで制御することが難しそうでしたので、
メインPCでAIを動かすことにしました。ShelPiは画像を送信し、PCから結果を受け取るだけの簡単なお仕事。
これを使えば、ShelPiが物体認識できるようになりますね。

ShelPiのサーボをSG90→MG90Dに換装しました

先日完成したShelPiですが、テスト駆動を重ねているうちに、ボディ側のサーボに使用しているSG90のコピーモデルのジッターが激しくなってきました。
非励磁時にはギヤが空転するものもあり、よりトルクのあるモーターに変更する必要があったので、秋月電子さんでメタルギヤのMG90Dを購入しました。

@1,350×4個で、合計¥5,400

SG90のコピーモデルは4個で¥1,000なので5倍の値段です。
仕様を比較すると、

項目 SG90 (アナログ) MG90D (デジタル) 備考
ギア素材 プラスチック (POM) メタル (6061-T6 アルミ) MG90Dは耐久性が圧倒的に高い
動作タイプ アナログ デジタル MG90Dは保持力とレスポンスが良い
トルク (4.8V) 1.8 kg·cm 2.1 kg·cm MG90Dの方がパワフル
スピード (4.8V) 0.10 ~ 0.12 sec/60° 0.10 sec/60° MG90Dの方が安定して高速
動作電圧 4.8V (〜6.0V) 4.8V 〜 6.6V MG90DはLiFe電池(6.6V)にも対応
ベアリング なし (ブッシュ) ダブルボールベアリング MG90Dは軸ブレが少なくスムーズ
重量 約 9g 約 13g メタルギアの分、MG90Dが重い
デッドバンド 7μs ~ 10μs 1μs MG90Dの方が微細な制御が可能

といった感じです。
ストール時の電流がSG90と比較すると高いため、ダイソーのモバイルバッテリーで動くか不安でしたが、試運転の際は問題ありませんでした。
使ってみて、明らかに剛性が高く、ギヤのバックラッシュも気にならず、かなりガッシリしている印象があります。


動かした感じだとあまり変化はありませんが、励磁時に負荷を与えた際の保持力が全く違います。
寸法はほぼ同じなのですが、取り付け部分の高さや、全高などが少し異なるためそのまま交換はできません。
フレームから再設計して換装する事になりました。
設計、3Dプリント、換装と、休日が一瞬で溶けました。
先端側のサーボはまだ樹脂ギヤですが、これもMG90Dに変更してもいいかもしれません。
今のところは在庫もあるので壊れたら交換して対応しましょう。
電子工作を始めて4か月ぐらい経ちました。
最初は安いモーターで動けばいいや。ぐらいの気持ちでやっていましたが、倒立振子やラズパイカーを作り、3Dプリンタを購入し、ついにロボットまで作るようになってくると徐々にいいものが欲しくなってきます。
着々とノウハウが積み重なってきているのを実感します。

ただ、お金がかかるのが・・・

RaspberryPi zero 2wで16CHサーボドライバPCA9685を使うときの注意

ShellPiをいじっていたらラズパイの設定がおかしくなってしまい、再度OSからインストールしなおしたのですが、その際にサーボドライバのPCA9685がうまく動かずに苦戦しました。
サーボドライバのライブラリは以下の2つあるようです。

adafruit-circuitpython-pca9685
adafruit-pca9685

circuitpythonとつく方が現在のライブラリ名で、

pip install adafruit-circuitpython-pca9685

でインストールします。
サンプルコードは

import time
import board
import busio
from adafruit_pca9685 import PCA9685

# I2C 初期化
i2c = busio.I2C(board.SCL, board.SDA)

# PCA9685 初期化
pca = PCA9685(i2c)
pca.frequency = 50  # サーボは 50Hz

def set_servo_angle(channel, angle):
    # 角度 → パルス幅(ms)
    pulse_min = 1.0   # 0°
    pulse_max = 2.0   # 180°
    pulse = pulse_min + (pulse_max - pulse_min) * (angle / 180)

    # duty_cycle に変換
    duty = int((pulse / 20.0) * 65535)
    pca.channels[channel].duty_cycle = duty

# テスト
while True:
    set_servo_angle(0, 0)
    time.sleep(1)
    set_servo_angle(0, 90)
    time.sleep(1)
    set_servo_angle(0, 180)
    time.sleep(1)

circuitpythonが付かない方は旧ライブラリで、

pip install adafruit-pca9685

でインストールします。
サンプルコードはこちら

import Adafruit_PCA9685
import time

# PCA9685初期設定
pwm = Adafruit_PCA9685.PCA9685()
pwm.set_pwm_freq(60)


def main():
    for i in range(1):
        print(f"{i}+番目のサーボモーター動作")
        pwm.set_pwm(i, 0, 450)
        time.sleep(0.5)
        pwm.set_pwm(i, 0, 250)
        time.sleep(0.5)
    pwm.close()
    
if __name__ == '__main__':
    main()

初期設定も制御方法も全然違います。
AIにコーディングしてもらうと、旧式を使ってみたり、新式を使ってみたりと気まぐれなので注意が必要でした。

ShelPi(シェルピー)完成

ここ最近ずっと作っているクモ型ロボ改めカメロボなんですが、ようやく完成形になりました。
これまですこし大きくてボテッとしたデザインでしたので、ボディをコンパクトに設計変更しました。
以前の記事はこちら↓
slowtech.hateblo.jp
初号機

弐号機

参号機

初号機はセンサーがなく、脚部のモーターのみ。
弐号機で足回りを対象に設置。超音波センサーとカメラを搭載しました。
その影響で上のカバーが取り付けられなくなりました。
参号機は足回りをよりコンパクトに設計変更し、超音波センサーとカメラの取り付けを前提とした構成にしています。
カバーもよりコンパクトにして、それまで横に並んでいたサーボドライバとラズパイゼロ2Wを縦に配置。
省スペース化に貢献しています。
カバー上面はV字に穴を空け、ラズパイ内部のLEDの点灯状況を確認できるようにしています。デザイン的にも良き。

このロボ、これからはShelPi(シェルピー)と呼ぶことにします。
Shell(甲羅)のような堅牢な外観と、Raspberry Piの"Pi"を組み合わせ、愛嬌のあるしぐさで楽しませてくれる事から、シェルピー。
いい名前です。

エネルギー源はダイソーのモバイルバッテリー5000mAhで、大きな電流は流せないのですが容量が結構あるのでかなりずっと起動していられます。
これからカメラをAIで処理して自立して行動できるような制御をしてみたいです。

カメロボにカメラを付けてみた

以前ラズパイカーを作った時に、ラズパイのカメラモジュールV2を使ったのですが、ラズパイカーは現在カメロボになってしまったので、カメラだけ余っている状況です。
思えばカメラの実装はかなり大変で、結局ラズパイゼロ2WのOSバージョンをBullsayeにしたり、チャッピーでは解決できなかったのでいろいろなサイトを検索したりして苦労の末実装できたのはいい思い出。
slowtech.hateblo.jp
せっかくなので、カメロボにも実装して人物の認識とカメラの追従をしていみたいと思います。
まずはラズパイにカメラを接続するのですが、これが結構大苦戦しました。
カメラモジュールのフラットケーブルはハリがある素材なので、カメロボ内の込み入った配線に対して取りまわしが非常に難しい。
せっかく接続してもラズパイ側のロック部分が弱すぎてすぐ外れます。
なんとかがんばって取り付けても、なんとカメラを認識しません。
ここで大きな罠があったのですが、カメラのレンズ側に基盤と接続するコネクタがあり、これがちょっと浮いていたのでした。

↑この黒いやつが浮いていた・・・
ひとしきり焦り散らかして、ネットにこの部分が浮いていて認識しなかったという記事を見つけてようやく解決。
カメラの上下機構はMiuzeiの安いサーボでOK。3Dプリンタで取り付けステーを作りました。


次はラズパイ側です。
モジュールはOpenCVを使います。

sudo apt install python3-opencv

続いて、顔検出の学習データを取得し、python実行ファイルと同じディレクトリに保存します。

wget https://raw.githubusercontent.com/opencv/opencv/master/data/haarcascades/haarcascade_frontalface_default.xml

顔検出のプログラムを記述します。

import cv2
import time

cap = cv2.VideoCapture(0)
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 320)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 240)

if not cap.isOpened():
    print("カメラ開けない")
    exit()

face_cascade = cv2.CascadeClassifier(
    "haarcascade_frontalface_default.xml"
)

print("顔検出スタート")

while True:
    ret, frame = cap.read()
    if not ret:
        continue

    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    faces = face_cascade.detectMultiScale(gray, 1.3, 5)

    if len(faces) > 0:
        print("顔検出!", faces[0])

    time.sleep(0.1)

実行すると

顔検出! [262 107  55  55]

のように認識できていることが確認できました。
これをロボに組み込みたいと思います。
今回はここまで。

カメ型ロボに超音波センサーを取り付けた

クモ型ロボ改め、カメ型ロボを製作しています。
slowtech.hateblo.jp
今回は、超音波センサーを用いて障害物を検知できるようにします。
超音波センサーはHC-SR04という電子工作ではよく見るやつです。
配線はこちら

HC-SR04は5V駆動なので、Vccをラズパイの5V端子につなぎます。
信号はTrigとechoがあり、Trigを16番(GPIO23)、echoを15番(GPIO22)に接続するのですが、
echo端子からは5V出力が出るらしく、そのまま繋ぐとラズパイが飛びます。
1kΩと2kΩの抵抗を使い、echo端子からの電圧を分圧して3V程度に下げてやる必要があります。
この辺の計算は電気工学の基礎、オームの法則によるものです。
Bambu Lab A1 miniで取り付け用のステーを作り、実装しました。

超音波センサーが目に見えてしまう現象は不可避です。
動作確認のサンプルプログラムはこちら

from gpiozero import DistanceSensor
from time import sleep

sensor = DistanceSensor(echo=22, trigger=23, max_distance=2.0)

while True:
    dist_m = sensor.distance  # in meters
    dist_cm = round(dist_m * 100, 1)
    print(f"Distance: {dist_cm} cm")
    sleep(0.5)

Arduinoの場合はプログラムは1つしか動かせないので、サンプルプログラムを転送してしまうとロボのプログラムを再度入れなおす必要がありますが、ラズパイはいろんなプログラムを保存しておいて好きなタイミングで起動、修正できるので手返しがバツグンにいいです。

Distance: 3.3 cm
Distance: 1.9 cm
Distance: 4.0 cm
Distance: 9.4 cm
Distance: 3.8 cm
Distance: 7.1 cm
Distance: 22.3 cm

実行するとこのように障害物を検知すると距離が変化します。
カメ型ロボには、この距離が一定範囲内に入ったらバックして方向転換するようにプログラムをすると、壁を避けるようなモーションをさせることができます。


超音波センサーは障害物に対して直角でないとちゃんと測定値が安定しません。
斜めに壁に向かっていくと激突する事が多くてなかなか難しかったです。

クモ型ロボを作っていたらカメ型ロボになってた話

Miuzeiの10個入りマイクロサーボと、3Dプリンタを購入したのでロボットを自作してみようと思います。
2足歩行ロボットは難易度が高そうだったので今回は2関節4脚のクモ型ロボットを作ります。
ハードウェアの構成は以下の通り

  • マイコン RaspberryPi Zero 2W ¥3,190(2025/12 購入) 秋月電子
  • マイクロサーボ Miuzei マイクロサーボ 9g 180° 10個入り @260×4個 ¥1,040 Amazon
  • マイクロサーボ YFFSFDC マイクロサーボ SG90型 4個入り ¥999 Amazon
  • サーボドライバ KKHMF PCA9685 ¥999 Amazon
  • モバイルバッテリー 5000mha ¥700 ダイソー
  • USBケーブル2個(タイプB) ¥100 ダイソー

合計:7,028
こう見てみるとラズパイが高いですね。金額の半分を占めています。
秋月電子さんでは現在入荷未定となっており、Amazonでは¥700ほど高い状況です。
サーボについては、Miuzeiのマイクロサーボは重量がかかる部分での定常状態のジッターが激しかったため、
急遽以前購入していたSG90を使用しました。
電源はダイソーのモバイルバッテリーですが、ラジコン用のリポバッテリーの方が出力的には安心だと思います。
今回は安価に済ませるためにダイソーさんを使用しました。
設計はAutoDeskのFusionを使います。個人利用に限り無料で使用できます。Fusionを使用して利益を出す場合は使えません。
当ブログは収益化していませんし、図面及び製作物の販売等はしませんのでOKだと思います。
脚部の構造はこちら

青いモーターがSG90で、旋回用です。Hipと呼びます。
白いモーターはMiuzeiで、足の上げ下げ。Kneeと呼びます。
ベースの構造はこちら

モーターとバッテリーを取り付けます。
この上にラズパイとドライバを取り付けるプレートが乗ります。

組立図はこちら

なかなかいい感じです。
実は最初はプロトタイプを設計し、作ってみたのですが全ての足をMiuziのサーボにすると先ほど申し上げたジッターが発生したので、再設計しました。
その際に脚部の位置がセンターからオフセットしていることに気づき、ついでにモーターの取り付け方法から見直しました。
プロトタイプはこちら

Hipのモーターの取り付け方法が異なっています。
これはこれでかっこいい。
これを歩かせる訳ですが、4脚ロボットは1脚上げるととたんに不安定になることがわかりました。
3つの足が接している点を結んだ三角形が、重心の半分以上を占めていないと上げた足側に倒れてしまいます。
そのため、1本の足を上げるためにはまず隣の足を動かす必要がありました。
そして、進行方向に1本の足を移したら、他3本の足を滑らせるように後ろに移動させて前方に状態を移します。
これを左右交互に繰り返すと、カメが這いずるような動きで歩く事ができます。
この動きをクロール歩容と呼ぶらしいです。


これはプロトタイプで、HipにMiuzeiのサーボを使っています。足を動かす時は発生していないのですが、止めた時にジッターが発生しました。
SG90に変えたところジッターは発生しなくなりました。
完成までの道のりをショートムービーにしてみました。
ダンスさせたりもできました。個人的に伏せのモーションがツボです。
構想時はクモ型ロボだったのですが、もうどう見てもカメです。
でも、かわいいからOK。