.. include:: /index.rst
:start-after: start_hello_message
:end-before: end_hello_message
.. _py_homework_grading_demo:
(Example) パン・チルトカメラ付き 宿題採点デモ
=====================================================
**はじめに**
このプロジェクトでは、コンピュータビジョン、人工知能、ロボティクスを組み合わせた対話型の **AI 宿題採点アシスタント** を作成します。システムは次のことを行います:
1. Raspberry Pi カメラで、手書きまたは印刷された宿題の問題を **撮影** します
2. OpenAI の GPT-4 Vision モデルで内容を **解析** し、解答が正しいかを判定します
3. サーボ制御のパン・チルトヘッド動作で **物理的なフィードバック** を返します:
- 正解なら *うなずく*
- 不正解なら *首を振る*
4. 1 回のボタン押下で動作する **シンプルなインタラクション** を採用しています
.. raw:: html
このデモは、AI が物理世界と連携できることを示す例であり、宿題の正誤をその場で視覚的にフィードバックする、学習向けの魅力的なツールになります。
他の LLM モジュールやハードウェア部品を組み合わせて、独自の AI 支援学習デバイスを作ることもできます。以下も参照してください:
* :ref:`py_online_llm`
* :ref:`cpn_servo`
* :ref:`cpn_camera_module`
----------------------------------------------
**必要なもの**
このプロジェクトに必要な部品は以下の通りです:
.. list-table::
:widths: 30 20
:header-rows: 1
* - COMPONENT
- PURCHASE LINK
* - :ref:`cpn_servo`
- |link_servo_buy|
* - Pan-Tilt
-
* - :ref:`cpn_camera_module`
- |link_camera_buy|
* - :ref:`cpn_fusion_hat`
- \-
* - Raspberry Pi
- \-
* - Homework sample (printed or handwritten)
- \-
----------------------------------------------
**ハードウェアのセットアップ**
カメラモジュールを便利に使うために、:ref:`assemble_fusion_hat_pan_tilt` の組み立てを推奨します。
.. note::
パン・チルトを組み立てると一部のピンが隠れる場合があります。そのため、カメラを使用するときだけ組み立てるか、組み立て後に外側へ配置することを推奨します。
.. image:: ../quick_start/img/gimbal_assemble.png
----------------------------------------------
.. include:: python_online_llms.rst
:start-after: start_setup_openai
:end-before: end_setup_openai
----------------------------------------------
**コードの実行**
#. 宿題サンプルを用意する:
- 簡単な算数問題と答えを手書き、または印刷します
- 例:"5 + 3 = 8"(正解)または "5 + 3 = 7"(不正解)
- 文字が読み取れるよう、はっきり書く/印刷してください
#. プログラムを実行する:
.. code-block:: bash
cd ~/ai-lab-kit/llm
python3 llm_openai_homework.py
#. 画面の指示に従う:
- 宿題用紙をカメラの下に置く
- Fusion HAT+ の User Button(USR)を押す
- サーボの反応を確認する
#. 想定される出力:
.. code-block:: text
HOMEWORK GRADING DEMO
==================================================
Instructions:
1. Place a homework question under the camera
2. Make sure the question AND answer are visible
3. Press the User Button (USR) on Fusion HAT to grade
4. The camera will take a photo
5. AI will grade the answer
6. Servo will nod (correct) or shake (incorrect)
==================================================
Waiting for button press...
==================================================
Button pressed - Starting grading process
Taking photo...
Photo captured
Sending to AI for grading...
AI response: CORRECT
Answer is correct - nodding head
==================================================
----------------------------------------------
**コード**
以下は宿題採点デモの Python スクリプト全体です:
.. raw:: html
.. code-block:: python
#!/usr/bin/env python3
"""
Homework Grading Demo with Pan-Tilt Camera
Press User Button to take photo, LLM grades, servo nods or shakes
"""
import time
from fusion_hat.llm import OpenAI
from fusion_hat.servo import Servo
from fusion_hat.user_button import UserButton
from picamera2 import Picamera2, Preview
# ========== LLM SETTINGS ==========
# Create a secret.py file with: OPENAI_API_KEY = "your-api-key-here"
try:
from secret import OPENAI_API_KEY
except ImportError:
print("ERROR: Please create a secret.py file with your OpenAI API key")
print("Example content: OPENAI_API_KEY = 'sk-...'")
exit()
# LLM instructions for grading
INSTRUCTIONS = """You are a homework grading assistant.
When you see a photo of a homework question with an answer,
determine if the answer is correct or incorrect.
Respond with ONLY ONE WORD:
- If the answer is CORRECT, respond: "CORRECT"
- If the answer is INCORRECT, respond: "INCORRECT"
Do not provide any other text, explanations, or justifications.
Only respond with "CORRECT" or "INCORRECT"."""
# Initialize LLM
llm = OpenAI(
api_key=OPENAI_API_KEY,
model="gpt-4o"
)
# Set LLM settings
llm.set_max_messages(5)
llm.set_instructions(INSTRUCTIONS)
# ========== HARDWARE SETTINGS ==========
PAN_CHANNEL = 2 # Horizontal servo for shaking head
TILT_CHANNEL = 3 # Vertical servo for nodding head
# Servo center positions
TILT_CENTER = 0 # Looking straight ahead
PAN_CENTER = 0 # Center position
# ========== INITIALIZE HARDWARE ==========
print("Initializing Homework Grading Demo...")
print("-" * 50)
# Initialize servos
pan_servo = Servo(PAN_CHANNEL)
tilt_servo = Servo(TILT_CHANNEL)
# Center servos
tilt_servo.angle(TILT_CENTER)
pan_servo.angle(PAN_CENTER)
time.sleep(1)
print("Servos ready")
# Initialize camera
camera = Picamera2()
camera_config = camera.create_preview_configuration(main={"size": (1280, 720)})
camera.configure(camera_config)
camera.start_preview(Preview.QT)
camera.start()
time.sleep(2)
print("Camera ready")
# Initialize user button
user_button = UserButton()
print("User button ready")
print("-" * 50)
# ========== SERVO MOVEMENT FUNCTIONS ==========
def nod_head():
"""
Nodding head movement for "correct"
"""
# Look down
tilt_servo.angle(15)
time.sleep(0.2)
# Look up
tilt_servo.angle(-10)
time.sleep(0.2)
# Return to center
tilt_servo.angle(TILT_CENTER)
def shake_head():
"""
Shaking head movement for "incorrect"
"""
# Look left
pan_servo.angle(-20)
time.sleep(0.15)
# Look right
pan_servo.angle(20)
time.sleep(0.15)
# Look left again
pan_servo.angle(-15)
time.sleep(0.15)
# Return to center
pan_servo.angle(PAN_CENTER)
# ========== GRADING FUNCTION ==========
def grade_homework():
"""
Main grading function: take photo, send to LLM, move servo
"""
print("\nTaking photo...")
# Capture image
img_path = './homework.jpg'
camera.capture_file(img_path)
print("Photo captured")
# Send to LLM for grading
print("Sending to AI for grading...")
prompt = "Look at this homework question and answer. Is the answer correct? Respond with only one word: 'CORRECT' or 'INCORRECT'."
response = llm.prompt(prompt, image_path=img_path)
response_text = response.strip().upper()
print(f"AI response: {response_text}")
# Move servo based on response
if "INCORRECT" in response_text:
print("Answer is incorrect - shaking head")
shake_head()
elif "CORRECT" in response_text:
print("Answer is correct - nodding head")
nod_head()
else:
print(f"Unexpected response: {response_text}")
# ========== BUTTON CALLBACK ==========
def on_button_click():
"""
Called when user button is pressed
"""
print("\n" + "=" * 50)
print("Button pressed - Starting grading process")
grade_homework()
print("=" * 50)
# ========== MAIN DEMO ==========
def main():
"""
Main demo function
"""
print("\nHOMEWORK GRADING DEMO")
print("=" * 50)
print("Instructions:")
print("1. Place a homework question under the camera")
print("2. Make sure the question AND answer are visible")
print("3. Press the User Button (USR) on Fusion HAT to grade")
print("4. The camera will take a photo")
print("5. AI will grade the answer")
print("6. Servo will nod (correct) or shake (incorrect)")
print("=" * 50)
print("\nWaiting for button press...")
# Set button callback
user_button.set_on_click(on_button_click)
# Keep program running
try:
while True:
time.sleep(0.1)
except KeyboardInterrupt:
print("\nDemo stopped by user")
# ========== CLEANUP ==========
def cleanup():
"""
Clean up resources
"""
print("\nCleaning up...")
# Return servos to center
tilt_servo.angle(TILT_CENTER)
pan_servo.angle(PAN_CENTER)
# Stop camera
camera.stop()
print("Demo ended")
# ========== RUN DEMO ==========
if __name__ == "__main__":
try:
main()
finally:
cleanup()
----------------------------------------------
**コードの理解**
1. LLM の設定とセットアップ
システムは Vision 機能を備えた OpenAI の GPT-4o を使用して画像を解析します:
.. code-block:: python
# Import and initialize the LLM
from fusion_hat.llm import OpenAI
llm = OpenAI(api_key=OPENAI_API_KEY, model="gpt-4o")
# Set specific instructions for consistent responses
INSTRUCTIONS = """You are a homework grading assistant..."""
llm.set_instructions(INSTRUCTIONS)
# Limit conversation history to manage tokens
llm.set_max_messages(5)
2. ハードウェアの初期化
3 つのハードウェア要素(サーボ、カメラ、ボタン)を初期化します:
.. code-block:: python
# Servo control for pan-tilt mechanism
pan_servo = Servo(PAN_CHANNEL) # Channel 2 for horizontal movement
tilt_servo = Servo(TILT_CHANNEL) # Channel 3 for vertical movement
# Camera setup with preview
camera = Picamera2()
camera_config = camera.create_preview_configuration(main={"size": (1280, 720)})
camera.configure(camera_config)
camera.start_preview(Preview.QT)
camera.start()
# User button for interaction
user_button = UserButton()
3. サーボのアニメーション関数
うなずき/首振りを自然に見せる動きです:
.. code-block:: python
def nod_head():
"""Nodding head movement for 'correct' answers"""
tilt_servo.angle(15) # Look down
time.sleep(0.2)
tilt_servo.angle(-10) # Look up
time.sleep(0.2)
tilt_servo.angle(TILT_CENTER) # Return to center
def shake_head():
"""Shaking head movement for 'incorrect' answers"""
pan_servo.angle(-20) # Look left
time.sleep(0.15)
pan_servo.angle(20) # Look right
time.sleep(0.15)
pan_servo.angle(-15) # Look left again
time.sleep(0.15)
pan_servo.angle(PAN_CENTER) # Return to center
4. 画像撮影と AI 解析
採点のメインワークフロー:
.. code-block:: python
def grade_homework():
# Capture image from camera
img_path = './homework.jpg'
camera.capture_file(img_path)
# Send image to LLM with specific prompt
prompt = "Look at this homework question and answer..."
response = llm.prompt(prompt, image_path=img_path)
response_text = response.strip().upper()
# Interpret response and trigger appropriate servo movement
if "INCORRECT" in response_text:
shake_head()
elif "CORRECT" in response_text:
nod_head()
5. ボタンイベントの処理
ユーザー操作はコールバックで完結します:
.. code-block:: python
def on_button_click():
print("Button pressed - Starting grading process")
grade_homework()
# Assign callback to button
user_button.set_on_click(on_button_click)
6. メインループ
ボタン押下を待つだけの最小構成です:
.. code-block:: python
def main():
print("Waiting for button press...")
user_button.set_on_click(on_button_click)
# Keep program running until interrupted
try:
while True:
time.sleep(0.1) # Low CPU usage wait
except KeyboardInterrupt:
print("\nDemo stopped by user")
7. リソースのクリーンアップ
終了時にサーボとカメラを安全に停止します:
.. code-block:: python
def cleanup():
# Return servos to neutral position
tilt_servo.angle(TILT_CENTER)
pan_servo.angle(PAN_CENTER)
# Stop camera
camera.stop()
----------------------------------------------
**トラブルシューティング**
- No module named ``picamera2``
必要なライブラリをインストールしてください:
.. code-block:: bash
sudo apt update
sudo apt install python3-picamera2
- Camera not detected
1. カメラの接続を確認:フラットケーブルが正しい向きで確実に挿入されているか
2. カメラが有効か確認: ``sudo raspi-config`` → Interface Options → Camera
3. カメラ単体テスト: ``libcamera-hello``
- Servos not moving
1. 電源接続を確認:サーボには 5V 電源が必要です
2. サーボのチャンネルがコードと一致しているか確認(Channels 2 と 3)
3. 単体テスト:角度指定でサーボが動くか確認してください
- AI not responding or error
1. ``secret.py`` の API キーが正しいか確認してください
2. ネット接続を確認: ``ping 8.8.8.8``
3. OpenAI アカウントにクレジットがあるか確認してください
4. モデル "gpt-4o" が利用可能か確認してください
- Incorrect servo movements
1. pan と tilt のサーボが入れ替わっていないか確認してください
2. ``nod_head()`` と ``shake_head()`` の角度値を調整してください
3. センター位置のキャリブレーションが必要な場合があります
- Image too blurry or dark
1. 宿題用紙に十分な照明を当ててください
2. 調整可能な場合はフォーカスを調整してください
3. 用紙から 15〜30cm 程度の距離にカメラを配置してください
4. 手書きは濃いペン/マーカーで書くと読み取りやすくなります
- Button not responding
1. ボタン押下時に User Button の LED が点灯するか確認してください
2. コールバックが登録されているか確認してください
3. 簡単な print を入れて押下検出を確認してください
- AI returns unexpected response
1. コード内の prompt の書式を確認してください
2. 画像に「問題」と「答え」の両方がはっきり写っているか確認してください
3. まずは簡単な四則演算でテストしてください
----------------------------------------------
この宿題採点デモは、AI のビジョンモデルが物理ハードウェアと連携して学習体験を拡張できることを示します。デジタルな知能と手触りのあるフィードバックを融合し、教育用途に魅力的な体験を提供します。