注釈
こんにちは、SunFounder Raspberry Pi & Arduino & ESP32 Enthusiast Community on Facebookへようこそ!他の愛好家と一緒に、Raspberry Pi、Arduino、ESP32の世界により深く入り込みましょう。
参加する理由
専門家サポート: 購入後の問題や技術的な課題を、コミュニティと私たちのチームの助けを借りて解決します。
学習と共有: ヒントやチュートリアルを交換して、スキルを向上させましょう。
限定プレビュー: 新製品の発表や先行プレビューに早期アクセスできます。
特別割引: 最新製品を特別割引でお楽しみいただけます。
季節限定キャンペーンとプレゼント: プレゼント企画やホリデーキャンペーンに参加しましょう。
👉 一緒に発見し、創造する準備はできましたか? [こちら] をクリックして、今すぐ参加しましょう!
4.11 サーボ角度メーター
はじめに
このレッスンでは、 サーボ角度メーター を作成します。 これは、ポテンショメータでサーボモーターの角度を制御し、その現在の角度を OLED 画面に表示するビジュアルインジケーターです。
ポテンショメータは Fusion HAT+ の ADC インターフェースを通じてアナログ電圧を出力します。 サーボはこの読み取り値に基づいて角度を制御され、128×64 の I2C OLED ディスプレイには数値によるサーボ角度と、画面上を滑らかに移動するグラフィカルなバーが表示されます。
ポテンショメータを回すと、サーボはおよそ -90° から +90° の範囲で動作し、OLED の表示もリアルタイムで更新されます。
必要なもの
このプロジェクトで必要なコンポーネントは以下のとおりです。
COMPONENT INTRODUCTION |
PURCHASE LINK |
|---|---|
- |
|
- |
|
Raspberry Pi |
- |
配線図
以下の配線図を参考にして、各コンポーネントを接続してください。
セットアップ手順
必要なライブラリをインストールします。
sudo pip3 install adafruit-circuitpython-ssd1306 --break
このチュートリアルで使用するすべてのサンプルコードは
ai-lab-kitディレクトリにあります。cd ~/ai-lab-kit/python/ sudo python3 4.11_ServoAngleMeter.py
スクリプトを実行すると次のように動作します。
ポテンショメータを回すと、サーボが -90° から +90° の範囲で回転します。
OLED には数値の角度と、移動するバー状のポインタが表示されます。
Ctrl+C を押すとプログラムが終了し、サーボは 0° に戻り、OLED 画面はクリアされます。
コード
以下は Servo Angle Meter の Python スクリプトです。
from fusion_hat.adc import ADC
from fusion_hat.servo import Servo
from PIL import Image, ImageDraw, ImageFont
import adafruit_ssd1306
import board, time
# ==== OLED setup ====
WIDTH, HEIGHT = 128, 64
i2c = board.I2C()
oled = adafruit_ssd1306.SSD1306_I2C(WIDTH, HEIGHT, i2c, addr=0x3C)
oled.fill(0)
oled.show()
# Framebuffer for drawing
image = Image.new("1", (WIDTH, HEIGHT))
draw = ImageDraw.Draw(image)
font = ImageFont.load_default()
def text_size(font, text):
l, t, r, b = font.getbbox(text)
return (r - l, b - t)
# ==== Servo & potentiometer ====
servo = Servo('P0') # servo on port P0
pot = ADC('A0') # potentiometer on A0 (0..4095)
def linear_map(x, in_min, in_max, out_min, out_max):
"""Map x from one range to another."""
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min
# ---- bar layout ----
BAR_TOP = 40
BAR_HEIGHT = 10
BAR_MARGINX = 6
BAR_WIDTH = WIDTH - BAR_MARGINX * 2
BAR_CENTERX = BAR_MARGINX + BAR_WIDTH // 2
def draw_bar(angle_deg):
"""Draw a centered horizontal bar and pointer for -90..90 degrees."""
draw.rectangle((0, 0, WIDTH, HEIGHT), outline=0, fill=0)
# Title
title = "Servo Angle"
tw, th = text_size(font, title)
draw.text(((WIDTH - tw) // 2, 4), title, font=font, fill=255)
# Numeric angle
txt = f"{angle_deg:>4} deg"
nw, nh = text_size(font, txt)
draw.text(((WIDTH - nw) // 2, 20), txt, font=font, fill=255)
# Bar outline
draw.rectangle(
(BAR_MARGINX, BAR_TOP, BAR_MARGINX + BAR_WIDTH - 1, BAR_TOP + BAR_HEIGHT),
outline=255, fill=0
)
# Ticks
for x in (BAR_MARGINX, BAR_CENTERX, BAR_MARGINX + BAR_WIDTH - 1):
draw.line((x, BAR_TOP - 3, x, BAR_TOP + BAR_HEIGHT + 3), fill=255)
# Map angle to pixel position
pos = int(linear_map(angle_deg, -90, 90, BAR_MARGINX, BAR_MARGINX + BAR_WIDTH - 1))
draw.line((pos, BAR_TOP - 2, pos, BAR_TOP + BAR_HEIGHT + 2), fill=255)
# Fill direction highlight
if pos >= BAR_CENTERX:
draw.rectangle((BAR_CENTERX, BAR_TOP + 1, pos, BAR_TOP + BAR_HEIGHT - 1), fill=255)
else:
draw.rectangle((pos, BAR_TOP + 1, BAR_CENTERX, BAR_TOP + BAR_HEIGHT - 1), fill=255)
try:
while True:
raw = pot.read()
angle = int(linear_map(raw, 0, 4095, -90, 90))
servo.angle(angle)
draw_bar(angle)
oled.image(image)
oled.show()
time.sleep(0.05)
except KeyboardInterrupt:
servo.angle(0)
oled.fill(0)
oled.show()
print("\nExited.")
コードの解説
Imports
ADCはポテンショメータからアナログ値を読み取りますServoはサーボモーターの回転を制御しますPILは OLED に表示するグラフィックを描画しますadafruit_ssd1306は I2C OLED ディスプレイを制御しますboardはハードウェア I/O を提供しますtimeはループ速度を制御します
OLED Setup
128×64 の SSD1306 OLED を初期化してクリアします。 描画はオフスクリーンのフレームバッファに作成され、その後ディスプレイに転送されます。
# ==== OLED setup ==== WIDTH, HEIGHT = 128, 64 i2c = board.I2C() oled = adafruit_ssd1306.SSD1306_I2C(WIDTH, HEIGHT, i2c, addr=0x3C) oled.fill(0) oled.show() # Framebuffer for drawing image = Image.new("1", (WIDTH, HEIGHT)) draw = ImageDraw.Draw(image) font = ImageFont.load_default()
Servo & Potentiometer
サーボは
P0ポートに接続ポテンショメータはアナログ入力
A0に接続ADC の範囲は
0..4095
# ==== Servo & potentiometer ==== servo = Servo('P0') # servo on port P0 pot = ADC('A0') # potentiometer on A0 (0..4095)
Mapping Values
linear_map()関数は、ポテンショメータの読み取り値を-90..90のサーボ角度に変換します。def linear_map(x, in_min, in_max, out_min, out_max): """Map x from one range to another.""" return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min
Drawing the UI
draw_bar()関数は次の処理を行います。画面をクリア
タイトルを描画
数値の角度を表示
横方向のバーと目盛りを描画
ポインタと方向を示す塗りつぶしバーを描画
def draw_bar(angle_deg): """ Draw a centered horizontal bar with a moving pointer. -90° maps to the far left, +90° to the far right. 0° is at the bar center. """ # Clear screen draw.rectangle((0, 0, WIDTH, HEIGHT), outline=0, fill=0) # Title title = "Servo Angle" tw, th = text_size(font, title) draw.text(((WIDTH - tw) // 2, 4), title, font=font, fill=255) # Numeric angle txt = f"{angle_deg:>4} deg" nw, nh = text_size(font, txt) draw.text(((WIDTH - nw) // 2, 20), txt, font=font, fill=255) # Static bar background draw.rectangle( (BAR_MARGINX, BAR_TOP, BAR_MARGINX + BAR_WIDTH - 1, BAR_TOP + BAR_HEIGHT), outline=255, fill=0 ) # Ticks: left (-90), center (0), right (+90) for x in (BAR_MARGINX, BAR_CENTERX, BAR_MARGINX + BAR_WIDTH - 1): draw.line((x, BAR_TOP - 3, x, BAR_TOP + BAR_HEIGHT + 3), fill=255) # Map angle (-90..90) to bar position pos = int(linear_map(angle_deg, -90, 90, BAR_MARGINX, BAR_MARGINX + BAR_WIDTH - 1)) # Pointer: a solid vertical line draw.line((pos, BAR_TOP - 2, pos, BAR_TOP + BAR_HEIGHT + 2), fill=255) # Optional: filled segment from center to pointer (visualize direction) if pos >= BAR_CENTERX: draw.rectangle((BAR_CENTERX, BAR_TOP + 1, pos, BAR_TOP + BAR_HEIGHT - 1), outline=0, fill=255) else: draw.rectangle((pos, BAR_TOP + 1, BAR_CENTERX, BAR_TOP + BAR_HEIGHT - 1), outline=0, fill=255)
Main Loop
プログラムは次の処理を繰り返します。
ADC を読み取る
サーボ角度を計算する
サーボを更新する
UI を描画する
OLED を更新する
while True: # Read potentiometer (0..4095) and map to angle (-90..90) raw = pot.read() angle = int(linear_map(raw, 0, 4095, -90, 90)) # Drive servo servo.angle(angle) # Draw UI and push to OLED draw_bar(angle) oled.image(image) oled.show() # Optional: print for debugging # print(f"pot={raw:4d} -> angle={angle:4d} deg") time.sleep(0.05) # ~20 FPS
Graceful Exit
Ctrl+C を押すと次の処理が行われます。
サーボが 0° に戻る
OLED 画面がクリアされる
トラブルシューティング
OLED に何も表示されない
I2C 配線を確認してください
デバイスアドレスが
0x3Cであることを確認してください必要なライブラリがインストールされていることを確認してください
サーボが動作しない
サーボの電源を確認してください
サーボが
P0に接続されていることを確認してくださいサーボの信号線が正しく接続されていることを確認してください
動作範囲が正しくない
以下のマッピング範囲を調整してください。
angle = int(linear_map(raw, 0, 4095, -90, 90))
OLED がちらつく
遅延時間を増やします。
time.sleep(0.1)
試してみよう
サーボ角度制限を追加する
機械的な過回転を防ぎます。
キャリブレーションを追加する
ポテンショメータの最小値・最大値を自動検出します。
動きを滑らかにする
イージングやローパスフィルタを適用します。
表示情報を追加する
角度とともに ADC の生データも表示します。
警告表示を追加する
角度が ±75° 付近に達したときポインタを点滅させます。
これらの拡張を行うことで、Servo Angle Meter はより高機能な入力可視化ツールへと発展させることができます。