找資料?
< All Topics
Print

【說明】研習指引

簡介

學習內容不只要好玩,也要逐步引導【知識、技能、態度】等面向上的發展。

教育工作者們都知道,要讓課堂好玩,是非常容易的。但是要在快樂中帶入【知識與技能的成長】、兼顧【差異化學習的個別特質】,甚至培育出【人生的態度】…。這些教育目標如闖關打怪一般,一級比一級困難。然而,這更是彰顯教育工作者專業之處。

如何將教學目標融入任務中,形成評量。老師評的公平又輕鬆、學生學得快樂又目標明確,還兼顧課室差異化。這幾天,我們來場務實、實戰地研討其中的困難與可行性…

Day 1. 課程介紹、韌體基礎複習、PS2搖桿控制項

本次研習屬於進階課程,基礎元件與原理不再贅述,可參考線上教學資源自主複習。第一天我們進行線上自主學習資源介紹、環境設定與函數庫安裝等必要的前置教學程序之外,也讓我們逐步進入進階研習內容之內。

機器人指導器(Teaching-Box),在機聯網、智慧機械中是個重要個觀念與應用。學習階段也可以逐步導入此觀念。初階課程時我們以APP為範例、進階課程我們以PS2搖桿控制為實例,重點在於學會【如何程式化取用控制項】。這觀念理解了、技巧知道了,變化就無窮囉~

今天的課程中透過各式範例,我們反覆練習取用【YESIO學習中心】相關學習資源,複習了韌體程式設定值與硬體之間的關係。更進一步地學會了如何運用韌體程式取用PS2X搖桿的各式硬體控制項,未來老師們能夠自由地取用、組合【PS2X搖桿的各式硬體控制項】實作出屬於自我定義的機器人指導器(Teaching-Box)囉。

Day 2. ADC、循線、邊界,複合運動

今天的課程運用了微處理器重要的ADC(類比轉數位訊號,Analog to Digital)的硬體單元功能,協助我們擷取了IR紅外線感測器的電壓變化值。我們運用此電壓變化值來辦別GoSUMO機器人在運動場中的位置,並且作為控制回授的感測訊號來源之一。

今天的實作項目可作為機器人回授控制的基礎入門實作,建立相關回授控制系統的入門知識與概念。我們今天課程將由這支程式開始變化….

#include <e32DUAL.h>

void setup() {
  Serial.begin(115200);
}

void loop() {

  Serial.println(ReadTCRT(2000, 0));
  delay(200);
}
#include <e32DUAL.h>
int PWM_Speed = 700;

void setup() {  
  Serial.begin(115200);
  GoSUMOInital();  //GOSUMO馬達、超音波初始化
 }

void loop() {
  Serial.println(ObjSeeking("F", 30));
  Serial.println(ObjDistance("F"));
  Serial.println("===================");
  delay(500);
}

Day 3. 偵測、自動對戰運動模式的建立

研習期中問卷 【傳送門

簡單的問卷調查,強化課程的安排。

透過 Day 2 自主實戰演練、老師們自主研討、交流彼此程式之後,如何應對目標問題,例如:需要讓機器人自主知道在競賽場域中的位置、需要控制機器人競技運動行為而不衝出場外…等問題。針對這些設計問題進而撰寫感測、制動元件之間的功能整合。老師們有了自主開發、撰寫的體會與知識技術鏈結,這就是我們學習心理學中經常談及的Metacognition(後設認知)、Meta-Knowledge(後設知識)的啟動方式了。

Day 3 今日課程更進一步地統整相關知識、技術與經驗,我們完成一台能【自主搜尋、自主對戰】的擂台機器人。有了今天的課程基礎鷹架 (學習鷹架理論),未來更可以發展出【自主防禦】策略的擂台機器人。

教育就是提供個學習鷹架給學生們模仿、探究而逐漸自我建構。所以,Day 3 系統整合的程式架構,我們就從這鷹架開始發展…

void Duelling(int Dis){
  byte b = ReadTCRT(1000, 0);
  if(!b){
    GS_BW(PWMSpeed); delay(250);
  }
  if(ObjSeeking("F", Dis)){ GS_FW(1023); delay(150); GS_STOP();}
  else{
    GS_STOP();
    if(ObjSeeking("L", Dis)){ 
      GS_LEFT(PWMSpeed); 
      delay(150); GS_STOP();
      }   
    else if(ObjSeeking("R", Dis)){ 
      GS_RIGHT(PWMSpeed); 
      delay(150); GS_STOP();
      } 
    else{
      if (SeekCnt>4){
        SeekCnt = 0;
        GS_RIGHT(PWMSpeed); 
        delay(50); GS_STOP();
      }
      else{SeekCnt++;}
    }
  }
}
/*-- 註解區
> GoARM2023 | ESP32 | DUAL22專題範例程式
> 作者 / Author : Nick, 2023.01.29 / https://yesio.net/
--*/
/*-- 標頭檔區(程式庫) --*/
#include <e32DUAL.h>
#include "BluetoothSerial.h"
#include <Servo.h>
#include "WiFi.h"

/*-- 宣告區(全域變數) --*/
BluetoothSerial SerialBT;  //藍牙串列物件 
char BTCMD[3] = {0};  //儲存藍牙串列接收到的指令
int PWMspeed = 950;  //儲存藍牙串列接收到的PWM speed數值
int SeekCnt = 0;

#define LOW_POWER_PROTECT
unsigned long lastTime = 0;
unsigned long timerDelay = 1000; //每2秒檢查一次供電電壓
float low_power_threadhold = 6.5;//低電壓警示值設定

//Servo Motor 變數區 & 機械臂微調區 -------
//重要,詳見影片介紹:https://youtu.be/PnZQBKgZXok
Servo servoBase;
Servo servoPaw;
int homeBase = 90; //機械臂原點角度。
int homePaw = 90;  //機械爪原點角度。

int posBase = homeBase;
int posBaseMAX = 175; //機械臂最大抬升角度。
int posBaseMIN = homeBase;  //機械臂最小降低角度。
int posPaw = homePaw;
int posPawMAX = homePaw;   //機械爪最大開啟角度。
int posPawMIN = 40;   //機械爪最小閉合角度。46

/*-- 初始化區 --*/
void setup()
{
  SerialBT.begin("e32Nick");  //啟動藍牙串列並設定藍牙裝置名稱
  GoSUMOInital();             //進行GoSUMO機器人初始化, Serial baud rate = 115200
  Serial.println("The device started, now you can pair it with bluetooth!");
  Buzzer_Alarm(200, 600, 50);
  //Serial.println(WiFi.macAddress());

  //ServoMotor初始化
  servoBase.attach(23);
  servoPaw.attach(22); 
  ServoARM("Home", "");  
}

/*-- 主程式區(重複執行) --*/
void loop()
{   
#ifdef LOW_POWER_PROTECT   
  if ((millis() - lastTime) > timerDelay) {
    lastTime = millis();
    if(Battery_Value()<low_power_threadhold){Buzzer_Alarm(200, 800, 50);}
  }
#endif

  if (Serial.available()) {
    SerialBT.write(Serial.read());
  }
  if (SerialBT.available()) {
    int i;
    for (i=0; i<4; i++){ BTCMD[i] = SerialBT.read(); } 
    /*
    Serial.println(BTCMD);
    Serial.println(BTCMD[0]);
    Serial.println(atoi(BTCMD));
    Serial.println("--");
    */
  }

  if(atoi(BTCMD)>1) { PWMspeed = atoi(BTCMD);}

  if(BTCMD[0] == 'c') { BTReportADC(); BTCMD[0] = 'c'; }  
  else if(BTCMD[0] == 'F') { GS_FW(PWMspeed); }  //GoSUMO機器人向前進 
  else if(BTCMD[0] == 'B') { GS_BW(PWMspeed); }  //GoSUMO機器人向後退 
  else if(BTCMD[0] == 'L') { GS_LEFT(PWMspeed); }
  else if(BTCMD[0] == 'R') { GS_RIGHT(PWMspeed); }
  else if(BTCMD[0] == 'Z') { GS_STOP(); }
  else if(BTCMD[0] == 'H') { Buzzer_Alarm(200, 600, 50); }
/*Servo Control, start here. */  
  else if(BTCMD[0] == 'A') { ServoARM("Base", "UP");  }	  //ServoBase Rising
  else if(BTCMD[0] == 'a') { ServoARM("Base", "DOWN");  }	//ServoBase Declining
  else if(BTCMD[0] == 'P') { ServoARM("Paw", "OPEN");  }	//ServoPaw OPEN
  else if(BTCMD[0] == 'p') { ServoARM("Paw", "CLOSE");  }	//ServoPaw CLOSE
  else if(BTCMD[0] == 'z') { ServoARM("Home", "");  BTCMD[0] = 'Z';} //ARM Homing
  else if(BTCMD[0] == 'C') { 
    Serial.print("F=");
    Serial.print(ObjDistance("F"));
    delay(1000);
    Serial.print(" / L=");
    Serial.print(ObjDistance("L"));
    delay(1000);
    Serial.print(" / R=");
    Serial.print(ObjDistance("R"));
    delay(1000);
    Serial.println("--");  
    }
  else if(BTCMD[0] == 'y') { 
    byte b = ReadTCRT(1000, DEBUG);
    Serial.println(b);  
    delay(1000);  
    }
  else if(BTCMD[0] == 'Y') {
    Duelling(30);
  }
}

/*====== 副程式區,start here. ========*/

void ServoARM(String _ACTION, String _DIR){
	if(_ACTION == "Base"){
	  if((_DIR == "UP")&&(posBase < posBaseMAX )){servoBase.write(++posBase); delay(15);  Serial.println("baseUP");}
	  if((_DIR == "DOWN")&&(posBase > posBaseMIN )){servoBase.write(--posBase); delay(15); Serial.println("baseDOWN");}
	}
	
	if(_ACTION == "Paw"){
		if((_DIR == "OPEN")&&(posPaw < posPawMAX )){servoPaw.write(++posPaw); delay(15); Serial.println("PawOpen");}
		if((_DIR == "CLOSE")&&(posPaw > posPawMIN )){servoPaw.write(--posPaw); delay(15); Serial.println("PawClose");}
	}	
	
	if(_ACTION == "Home"){
    Serial.println("servoHome");
	   servoBase.write(homeBase);
     servoPaw.write(homePaw);
     posPaw = homePaw;
     posBase = homeBase;
	   delay(1000);
	}	
}

void BTReportADC()
{
  unsigned int batValue = Battery_Value();
  SerialBT.println(batValue);
  //Serial.println(batValue);
}

void BTReportPWMspeed()
{
  //SerialBT.println(PWMspeed);
  Serial.println(PWMspeed);
}

void BTReportObjDistance(String P)
{
  float dis = ObjDistance(P);
  //SerialBT.println(dis, 1);
  Serial.println(dis, 1);
}

void Duelling(int Dis){
  byte b = ReadTCRT(1000, 0);
  if(!b){
    GS_BW(PWMspeed); delay(250);
  }
  if(ObjSeeking("F", Dis)){ GS_FW(1023); delay(150); GS_STOP();}
  else{
    GS_STOP();
    if(ObjSeeking("L", Dis)){ 
      GS_LEFT(PWMspeed); 
      delay(150); GS_STOP();
      }   
    else if(ObjSeeking("R", Dis)){ 
      GS_RIGHT(PWMspeed); 
      delay(150); GS_STOP();
      } 
    else{
      if (SeekCnt>4){
        SeekCnt = 0;
        GS_RIGHT(PWMspeed); 
        delay(50); GS_STOP();
      }
      else{SeekCnt++;}
    }
  }
}

/*-- 註解區
> GoARM - PS2 | ESP32 | DUAL22專題範例程式
--*/
/*-- 標頭檔區(程式庫) --*/
#include <e32DUAL.h>
#include <Servo.h>
#include <PS2X_lib.h>

/*-- 宣告區(全域變數) --*/
int SeekCnt = 0;
unsigned long prevMillis = 0;  // 暫存經過時間(毫秒)
const long interval = 5000;     // 指定間隔時間5秒(5000毫秒)。
bool SEEK1 = false;
int TCRT_SE = 1000 ; 
//Servo Motor 變數區 & 機械臂微調區 -------
//重要,詳見影片介紹:https://youtu.be/PnZQBKgZXok
Servo servoBase;
Servo servoPaw;
int homeBase = 90; //機械臂原點角度。
int homePaw = 90;  //機械爪原點角度。

int posBase = homeBase;
int posBaseMAX = 170; //機械臂最大抬升角度。
int posBaseMIN = 90;  //機械臂最小降低角度。
int posPaw = homePaw;
int posPawMAX = 98;   //機械爪最大開啟角度。
int posPawMIN = 60;   //機械爪最小閉合角度。

//PS2X Pins & Setting-----------------
#define PS2_DAT        25  //input pin,  DI - 25
#define PS2_CMD        17  //output pin, DO - 17/TX2
#define PS2_SEL        16  //output pin, SEL- 16/RX2
#define PS2_CLK        26  //output pin, CLK- 26
#define pressures   false
#define rumble      false
PS2X ps2x; // create PS2 Controller Class
int error = -1;
byte PS2X_type = 0;
byte vibrate = 0;
int tryNum = 1;
int PWMSpeed = 800 ;
int PWMSpeed_MAX = 1023; //DC電機最大PWM值(速度)
int PWMSpeed_MIN = 150;  //DC電機最小PWM值(速度)
//-----------------

/*-- 初始化區 --*/
void setup()
{
  GoSUMOInital();  //進行GoSUMO機器人初始化, Serial baud rate = 115200
  Serial.begin(115200);
  //初始化PS2搖桿
  PS2X_INIT();

  //ServoMotor初始化
  servoBase.attach(23);
  servoPaw.attach(22);
  ServoARM("Home", "");  
}

/*-- 主程式區(重複執行) --*/
void loop()
{
   if(PS2X_type != 2){ 
    ps2x.read_gamepad(false, vibrate);  //讀取PS2命令
    //Serial.println(ps2x.Analog(PSS_RY));
    //Serial.println(ps2x.Analog(PSS_RX));
    if(ps2x.Button(PSB_RED)&&ps2x.Button(PSB_L1)){SEEK1 = true;}  //搜尋模式01
    if(ps2x.Button(PSB_BLUE)){SEEK1 = false;}

    if(SEEK1 == true){
      SEEKing_01(20);  //搜尋模式01
    }  
    if(ps2x.Analog(PSS_RY) <= 118){ GS_FW(map(ps2x.Analog(PSS_RY), 128, 0, PWMSpeed_MIN, PWMSpeed_MAX)); Serial.println("F"); }          //前進 
    if(ps2x.Analog(PSS_RY) >= 138){ GS_BW(map(ps2x.Analog(PSS_RY), 128, 255, PWMSpeed_MIN, PWMSpeed_MAX)); }        //後退    
    if(ps2x.Analog(PSS_RX) <= 118){ GS_LEFT(map(ps2x.Analog(PSS_RX), 128, 0, PWMSpeed_MIN, PWMSpeed_MAX-200)); }    //左旋轉 
    if(ps2x.Analog(PSS_RX) >= 138){ GS_RIGHT(map(ps2x.Analog(PSS_RX), 128, 255,  PWMSpeed_MIN, PWMSpeed_MAX-200)); }//右旋轉
    if(
      ((ps2x.Analog(PSS_RX) < 130)&&(ps2x.Analog(PSS_RX) > 125)) 
      && 
      ((ps2x.Analog(PSS_RY) < 130)&&(ps2x.Analog(PSS_RY) > 125))
      ){ GS_STOP(); } 


    if(ps2x.Analog(PSS_LX) <= 118){ ServoARM("Paw", "OPEN");  }    
    if(ps2x.Analog(PSS_LX) >= 138){ ServoARM("Paw", "CLOSE"); }  
    if(ps2x.Analog(PSS_LY) <= 118){ ServoARM("Base", "DOWN"); }    
    if(ps2x.Analog(PSS_LY) >= 138){ ServoARM("Base", "UP");   }   

    if(ps2x.Button(PSB_L1) || ps2x.Button(PSB_R1)) {  ServoARM("Home", "");  } //Servo復位90度

    if(ps2x.Button(PSB_L2) || ps2x.Button(PSB_R2)) {  prevMillis = millis(); SpecificAction();   } //進入指定動作模式
  }  
  delay(15); 
} //END OF LOOP

/*====== 副程式區,start here. ========*/
void SpecificAction(){ //指定動作程式區
  int _SPEED = 1000; //速度設定1000
  GS_FW(_SPEED);     delay(3000);  //前進3秒
  GS_LEFT(_SPEED);   delay(300);  //左旋0.3秒
  GS_FW(_SPEED);     delay(1000);  //前進1秒
  GS_RIGHT(_SPEED);  delay(300);  //右璇0.3秒
  GS_FW(_SPEED);     delay(1000);  //前進1秒
  GS_STOP();
}

void ServoARM(String _ACTION, String _DIR){
	if(_ACTION == "Base"){
	  if((_DIR == "UP")&&(posBase < posBaseMAX )){servoBase.write(++posBase); }
	  if((_DIR == "DOWN")&&(posBase > posBaseMIN )){servoBase.write(--posBase); }
	}
	
	if(_ACTION == "Paw"){
	  if((_DIR == "OPEN")&&(posPaw < posPawMAX )){servoPaw.write(++posPaw); }
	  if((_DIR == "CLOSE")&&(posPaw > posPawMIN )){servoPaw.write(--posPaw);}
	}	
	
	if(_ACTION == "Home"){
    Serial.println("servoHome");
	  servoBase.write(homeBase);
    servoPaw.write(homePaw);
    posPaw = homePaw;
    posBase = homeBase;
	  delay(1000);
	}	
}

/*====================================
// PS2X 搖桿設定 
=====================================*/
void PS2X_INIT(){
  while (error != 0) {
    delay(1000);// 1 second wait
    error = ps2x.config_gamepad(PS2_CLK, PS2_CMD, PS2_SEL, PS2_DAT, pressures, rumble);
    Serial.print("#try config ");
    Serial.println(tryNum);
    tryNum ++;
  }
  Serial.println(ps2x.Analog(1), HEX);

  PS2X_type = ps2x.readType();

  switch(PS2X_type) {
    case 0:
      Serial.printf(" Unknown Controller type found, type is %d\n", PS2X_type);
      break;
    case 1:
      Serial.printf(" DualShock Controller found, type is %d\n", PS2X_type);
      break;
    case 2:
      Serial.printf(" GuitarHero Controller found, type is %d\n", PS2X_type);
      break;
    case 3:
      Serial.printf(" Wireless Sony DualShock Controller found, type is %d\n", PS2X_type);
      break;
   }
}
void SEEKing_01(int Dis){
  //循線部份
  if(ReadTCRT(TCRT_SE,0) == 0){
    GS_BW(PWMSpeed); delay(250);
    if (SEEK1 == false){GS_STOP();}
  }
  if(ReadTCRT(TCRT_SE,0) == 2){
    GS_FW(PWMSpeed); delay(250);
    if (SEEK1 == false){GS_STOP();}
  }
  if(ReadTCRT(TCRT_SE,0) == 3){
    GS_RIGHT(PWMSpeed); delay(250);GS_FW(PWMSpeed);delay(150);
    if (SEEK1 == false){GS_STOP();}
  }
  if(ReadTCRT(TCRT_SE,0) == 6){
    GS_LEFT(PWMSpeed); delay(250);GS_FW(PWMSpeed);delay(150);
    if (SEEK1 == false){GS_STOP();}
  }
  //flag
  if (SEEK1 == false){GS_STOP();}

  if(ReadTCRT(TCRT_SE,0) == 7){
  //超音波部份
  if(ObjSeeking("F", Dis)){ GS_FW(PWMSpeed+100);delay(150); if (SEEK1 == false){GS_STOP();}}
  else{
    if(ObjSeeking("R", Dis)){GS_RIGHT(PWMSpeed); delay(100); GS_FW(PWMSpeed+100); delay(100);}
    if(ObjSeeking("L", Dis)){GS_LEFT(PWMSpeed); delay(300); GS_FW(PWMSpeed+100); delay(100);}
    if (SEEK1 == false){GS_STOP();}
  }
  if (SEEK1 == false){GS_STOP();}
  else {GS_FW(PWMSpeed);delay(50);GS_LEFT(PWMSpeed);delay(150);}
  }
}

Day 4. GoSUMO機器手臂

昨天,老師們已經掌握了如何撰寫【全自動擂台對戰】的機器人,我們也玩了幾場全自動對戰的趣味賽前賽。今天,我們再回頭認識如何使用e32DUAL控制MG995伺服電機,建立2個自由度的機器手臂。

Day 5. 當AI遇上EDU

期末問卷,【傳送門

您的意見,讓我們更美好。
// 權重、偏置,就是你ML訓練的成果數據(模型)
const float w[3] = {17.3, 0.6, 2.6}; // 權重值
const float b0 = 2.1; // 偏置量
  
// Sigmoid激勵函式 (輸出 0.0 ~ 1.0)
float sigmoid(float val){
  Serial.println(val);
  Serial.println(exp(val));
  return(1/(1+exp(val)));//1 / (1 + exp(val))); //傳回 x 的自然指數值 ex
}

void setup(){ 
  Serial.begin(115200);
}

void loop(){  
  float yy = 0; // 輸出值
  float sum = 0; // 加總值

  float x1 = 0.01;
  float x2 = 0.02;
  float x3 = 0.3;
  float x[3] = {x1,x2,x3}; // 輸入值


  // 加總所有輸入乘權重值
  for(int n=0; n<3; n++){
    sum += (x[n] * w[n]);
  }
  
  sum += b0; // 加上偏置量

  yy = sigmoid(sum); // 以sigmoid激勵函式作為輸出轉換

  Serial.println(yy);
  Serial.println("==============");
  delay(300);
}
Table of Contents