猜拳機器人

實作 小組

電子電路 期末小組專題報告 猜拳機器人

背景

搞不懂影像辨識的運作原理、影像辨識在生活中有什麼應用?或是不知道Arduino能做什麼,覺得Arduino很無聊,沒什麼用處?怕跟不上最新AI發展,被時代淘汰嗎?

以上一連串的問題都能輕鬆解決!我們打造出一款好玩、有趣又能學到Arduino和影像辨識的「RPS AI猜拳機器人」!

這是一款能和它玩剪刀石頭布的機器人,透過手機影像辨識技術,RPS AI猜拳機器人能「看見」你出了剪刀、石頭還是布,然後判斷誰輸誰贏。

用到的模組及功能

選用開發板:TTGO T-Display

功能 模組功能 模組 通訊/腳位
偵測是否有人靠近 超音波測距 HC-SR04 25,26
播放音效 發出聲響 有源蜂鳴器 2
顯示機器人出拳及狀態 顯示資訊 TFT螢幕 附於TTGO T-Display
和手機連線 藍牙通訊 BLE模組 附於TTGO T-Display

系統架構及介面

以下是系統架構圖

使用者介面

系統介紹

手機 App 設計

這個應用程式使用了Ionic移動框架。通過Capacitor js套件,我們可以快速獲得原型並使用網頁技術。利用這個技術堆疊,我們受益匪淺,還能夠使用藍牙連接來連接TTGO T-Display微控制器。

由於我們選擇使用網頁技術來實現這個應用程式,我們可以使用像Mediapipe這樣的套件來進行手勢識別工作。整合該套件後,應用程式能夠辨識你的手勢並在檢測到後執行相應操作。

ESP32 開發板

這次我們使用的MCU是TTGO T-Display。這使我們可以提供一個緊湊的設備,因為它自帶一個小型TFT螢幕。ESP32板的一個好處是我們有藍牙連接,不需要再購買另一個藍牙模塊。

然後,為了讓機器人檢測是否有手靠近以開始遊戲,我們選擇集成HC-SR04模塊。一旦傳感器檢測到距離小於20公分,ESP32將改變BLE連線的值,以便應用程式知道玩家想要再次開始遊戲。

我們還在電路中加入了一個蜂鳴器,以便當機器人贏或輸時發出一些聲音。

使用流程

部分程式碼

開發板程式碼

Setup

#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEServer.h>

#include <TFT_eSPI.h>

#include <Ultrasonic.h>

#define SERVICE_UUID        ""
#define CHARACTERISTIC_UUID ""

String robotStatus = "boot";
String robotInPleaseStart = "false";
String receivedTemp = "";

int buzzerPin = 2;

Ultrasonic ultrasonic(25, 26);
long cm;

TFT_eSPI tft = TFT_eSPI(); 

// import images
#include "img1.h"
#include "img2.h"
#include "img3.h"

BLECharacteristic *pCharacteristic;

void setup() {
  Serial.begin(115200);
  pinMode(buzzerPin, OUTPUT);

  tft.begin();
  tft.setRotation(1);
  tft.fillScreen(TFT_BLACK);
  tft.setFreeFont(&FreeSerifBold24pt7b);
  tft.setCursor(30, 60);
  tft.setTextColor(TFT_WHITE);
  tft.setTextSize(1);
  tft.printf("Ready to connect");

  Serial.println("Starting BLE work!");

  BLEDevice::init("RPS Hand");
  BLEServer *pServer = BLEDevice::createServer();
  BLEService *pService = pServer->createService(SERVICE_UUID);
  pCharacteristic = pService->createCharacteristic(
                                         CHARACTERISTIC_UUID,
                                         BLECharacteristic::PROPERTY_READ |
                                         BLECharacteristic::PROPERTY_WRITE
                                       );

  pCharacteristic->setValue("boot");
  pService->start();
  // BLEAdvertising *pAdvertising = pServer->getAdvertising();  // this still is working for backward compatibility
  BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
  pAdvertising->addServiceUUID(SERVICE_UUID);
  pAdvertising->setScanResponse(true);
  pAdvertising->setMinPreferred(0x06);  // functions that help with iPhone connections issue
  pAdvertising->setMinPreferred(0x12);
  BLEDevice::startAdvertising();
  Serial.println("Characteristic defined! Now you can read it in your phone!");
}

Loop
void loop() {
  std::string value = pCharacteristic->getValue();
  Serial.print("The new characteristic value is: ");
  Serial.println(value.c_str());
  receivedTemp = String(value.c_str());
  if (receivedTemp != robotStatus) {
    if (receivedTemp == "show0"){
      tft.fillScreen(TFT_BLACK);
      tft.pushImage(50, 0, 140, 135, img1);
    } else if (receivedTemp == "show1"){
      tft.fillScreen(TFT_BLACK);
      tft.pushImage(50, 0, 140, 135, img2);
    } else if (receivedTemp == "show2"){
      tft.fillScreen(TFT_BLACK);
      tft.pushImage(50, 0, 140, 135, img3);
    } else if (receivedTemp == "test string"){
      tft.fillScreen(TFT_BLACK);
      digitalWrite(buzzerPin, HIGH);
      delay(300);
      digitalWrite(buzzerPin, LOW);
      delay(500);
      pCharacteristic->setValue("idle");
    } else if (receivedTemp == "idle") {
      // idle
      tft.fillScreen(TFT_BLACK);
    }
    robotStatus = receivedTemp;
  }
  cm = ultrasonic.read();
  Serial.println(cm);
  if (cm <= 20){
    pCharacteristic->setValue("pleaseStart");
  }
  delay(200);
}

APP程式碼

藍牙連接

export async function connect(device): Promise<void> {
  if (device === null || device === undefined) return
  try {
    await BleClient.initialize();

    // connect to device, the onDisconnect callback is optional
    await BleClient.connect(device.deviceId, (deviceId) => onDisconnect(deviceId));
    console.log('connected to device', device);
  } catch (error) {
    console.error(error);
  }
}

export async function read(device){
  const result = await BleClient.read(device.deviceId, ESP32_SERVICE, ESP32_CHARACTERISTIC);
  return result
}

開始遊戲

const startGame = () => {
  setShowGameResult(false);
  console.log("開始猜拳");
  const computerPlayer = Math.floor(Math.random() * 3);
  if (!countDown) {
    setComputerPlayerResult(computerPlayer);
    setCountDown("3");
    setTimeout(() => {
      setCountDown("2");
    }, 1000);
    setTimeout(() => {
      setCountDown("1");
    }, 2000);
    setTimeout(() => {
      console.log(resultRef.current);
      if (resultToNum(resultRef.current) === null) {
        setCountDown("出拳失敗");
        setTimeout(() => {
          setCountDown(null);
        }, 1500);
      } else {
        const playerResultNow = resultToNum(resultRef.current);
        setPlayerResult(playerResultNow);
        const whoWin = whoWinNumHnadler(playerResultNow, computerPlayer);
        setCountDown(numToWhoWinStr(whoWin));
        setShowGameResult(true);
        setTimeout(() => {
          setCountDown(null);
        }, 1000);
      }
    }, 3000);
  }
};
上: 中文寫作與表達 評論 下: 電腦動畫

sk5s project 為 samko5sam 為紀錄學習而搭建的個人網站,因為網站內容不便完全公開,所以請勿將網址隨意分享,網站也已經加上noindex。