< All Topics
Print

【運動】P3x搖桿(鎖址式) | dual2s (2025)

簡介

GoX系列機器人(GoSUMO、GoARM、GoJoga足球….)的核心控制器為dual2s。我們提供的程式範例中,控制GoX系列機器人的通訊模式主要有三種:【APP控制模式、PS2x控制模式(速配式)、PS3x控制模式(鎖址式)】,如圖所示。

注意:P3x搖桿走藍牙通訊,耗電量較高,請使用電量充足的鋰電池(隨時充飽電力)。當電力不足時,將出現機器人控制不穩定、誤動作…等不正常行為。

👉本單元將針對【P3x搖桿模式(鎖址式)】提供程式範例


函數庫

檔案下載

PS3x搖桿函數庫下載:載點


GoX輪型運動基礎概念

本單元程式範例提供兩組運動模式:抓地胎、麥克納姆輪。抓地胎共4種運動模式、麥克納姆輪共有10種運動模式。如下圖表。


搖桿按鍵功能介紹

P2x / P3x搖桿,兩種相同。此處以P3x搖桿為範例。

橘色表示P3x搖桿才具有之功能。


麥克納姆輪組裝


範例程式一、抓地胎

GoX在【抓地胎】模式時,目前有4種運動方向:前進、後退、左旋轉、右旋轉。運動速度由【搖桿推值】決定。

積木程式範例 – 抓地胎模式

本範例下載傳送門,👉【XML檔案】。


Arduino C – 抓地胎模式

#i
#include <dual2s.h>
#include <Ps3Controller.h>

//DC電機GPIO
#define PIN_MOTO_1A  14
#define PIN_MOTO_1B  32
#define PIN_MOTO_2A  12
#define PIN_MOTO_2B  33
#define PIN_MOTO_3A  25
#define PIN_MOTO_3B  26
#define PIN_MOTO_4A  23
#define PIN_MOTO_4B  22

//蜂鳴器GPIO
#define PIN_BUZZER   15

//超音波GPIO
#define PIN_USC_FRONT_ECHO 13
#define PIN_USC_FRONT_TRIG 27
#define PIN_USC_RIGHT_ECHO 5
#define PIN_USC_RIGHT_TRIG 4
#define PIN_USC_LEFT_ECHO 19
#define PIN_USC_LEFT_TRIG 18

//短距紅外線GPIO
#define PIN_IR_LEFT    34
#define PIN_IR_MIDDLE  35
#define PIN_IR_RIGHT   39

//供電偵測GPIO
#define PIN_BATTERY    36

//預設使用的PWM通道
#define PWM_CH_BUZZER 7
#define PWM_CH_M1A    8
#define PWM_CH_M1B    9
#define PWM_CH_M2A    10
#define PWM_CH_M2B    11
#define PWM_CH_M3A    12
#define PWM_CH_M3B    13
#define PWM_CH_M4A    14
#define PWM_CH_M4B    15

GoSUMO gs;

Buzzer bz(PIN_BUZZER, PWM_CH_BUZZER);
unsigned long lastRecTime_P3x = 0;  // 記錄上次收到 PS3 訊號的時間

//連線成功時之處理函數...
void onConnect() {
  bz.alarm();
}

// 描述此函式...
void notify() {
  if (Ps3.data.analog.stick.ry <= -5 && Ps3.data.analog.stick.rx == 0) {
    gs.act(GoSUMO::FORWARD, map(Ps3.data.analog.stick.ry,-5,-128,100,1023));
  }
  if (Ps3.data.analog.stick.ry >= 5 && Ps3.data.analog.stick.rx == 0) {
    gs.act(GoSUMO::BACKWARD, map(Ps3.data.analog.stick.ry,5,127,100,1023));
  }
  if (Ps3.data.analog.stick.rx <= -5 && Ps3.data.analog.stick.ry == 0) {
    gs.act(GoSUMO::GO_RIGHT, map(Ps3.data.analog.stick.rx,-5,-128,100,1023));
  }
  if (Ps3.data.analog.stick.rx >= 5 && Ps3.data.analog.stick.ry == 0) {
    gs.act(GoSUMO::GO_LEFT, map(Ps3.data.analog.stick.rx,5,127,100,1023));
  }
  if (Ps3.data.analog.stick.rx >= -5 && Ps3.data.analog.stick.rx <= 5 && Ps3.data.analog.stick.ry >= -5 && Ps3.data.analog.stick.ry <= 5) {
    gs.stop();
  }
}

void setup()
{
  Ps3.begin("20:00:00:00:11:22");  // 啟動 P3x搖桿的藍牙位址
  Ps3.attach(notify); //設定按鍵變更時的回呼函式
  bz.alarm();
}


void loop(){}

範例程式二、麥克納姆輪

GoX在【抓地胎】模式時,目前有10種運動方向:前進、後退、左旋轉、右旋轉、右前橫移、左前橫移、右後橫移、左後橫移、右橫移、左橫移。運動速度由【搖桿推值】決定。

積木程式範例 – 麥克納姆輪

本範例下載傳送門,👉【XML檔案】。


Arduino C – 麥克納姆輪模式


#include <dual2s.h>
#include <Ps3Controller.h>

//DC電機GPIO
#define PIN_MOTO_1A  14
#define PIN_MOTO_1B  32
#define PIN_MOTO_2A  12
#define PIN_MOTO_2B  33
#define PIN_MOTO_3A  25
#define PIN_MOTO_3B  26
#define PIN_MOTO_4A  23
#define PIN_MOTO_4B  22

//蜂鳴器GPIO
#define PIN_BUZZER   15

//超音波GPIO
#define PIN_USC_FRONT_ECHO 13
#define PIN_USC_FRONT_TRIG 27
#define PIN_USC_RIGHT_ECHO 5
#define PIN_USC_RIGHT_TRIG 4
#define PIN_USC_LEFT_ECHO 19
#define PIN_USC_LEFT_TRIG 18

//短距紅外線GPIO
#define PIN_IR_LEFT    34
#define PIN_IR_MIDDLE  35
#define PIN_IR_RIGHT   39

//供電偵測GPIO
#define PIN_BATTERY    36

//預設使用的PWM通道
#define PWM_CH_BUZZER 7
#define PWM_CH_M1A    8
#define PWM_CH_M1B    9
#define PWM_CH_M2A    10
#define PWM_CH_M2B    11
#define PWM_CH_M3A    12
#define PWM_CH_M3B    13
#define PWM_CH_M4A    14
#define PWM_CH_M4B    15

GoSUMO gs;

Buzzer bz(PIN_BUZZER, PWM_CH_BUZZER);
unsigned long lastRecTime_P3x = 0;  // 記錄上次收到 PS3 訊號的時間

// 描述此函式...
void notify() {
  if (Ps3.data.button.circle) {
    gs.act(GoSUMO::GO_RIGHT, 800);
  }
  if (Ps3.data.button.square) {
    gs.act(GoSUMO::GO_LEFT, 800);
  }
  if (Ps3.data.analog.stick.ry <= -5 && Ps3.data.analog.stick.rx == 0) {
    gs.act(GoSUMO::FORWARD, map(Ps3.data.analog.stick.ry,-5,-128,100,1023));
  }
  if (Ps3.data.analog.stick.ry <= -5 && Ps3.data.analog.stick.rx >= 5) {
    gs.act(GoSUMO::RF_TVL, map(Ps3.data.analog.stick.rx,5,127,100,1023));
  }
  if (Ps3.data.analog.stick.ry <= -5 && Ps3.data.analog.stick.rx <= -5) {
    gs.act(GoSUMO::LF_TVL, map(Ps3.data.analog.stick.rx,-5,-128,100,1023));
  }
  if (Ps3.data.analog.stick.ry >= 5 && Ps3.data.analog.stick.rx == 0) {
    gs.act(GoSUMO::BACKWARD, map(Ps3.data.analog.stick.ry,5,127,100,1023));
  }
  if (Ps3.data.analog.stick.ry >= 5 && Ps3.data.analog.stick.rx >= 5) {
    gs.act(GoSUMO::RB_TVL, map(Ps3.data.analog.stick.rx,5,127,100,1023));
  }
  if (Ps3.data.analog.stick.ry >= 5 && Ps3.data.analog.stick.rx <= -5) {
    gs.act(GoSUMO::LB_TVL, map(Ps3.data.analog.stick.rx,-5,-128,100,1023));
  }
  if (Ps3.data.analog.stick.rx <= -5 && Ps3.data.analog.stick.ry == 0) {
    gs.act(GoSUMO::R_TVL, map(Ps3.data.analog.stick.rx,-5,-128,100,1023));
  }
  if (Ps3.data.analog.stick.rx >= 5 && Ps3.data.analog.stick.ry == 0) {
    gs.act(GoSUMO::L_TVL, map(Ps3.data.analog.stick.rx,5,127,100,1023));
  }
  if (Ps3.data.analog.stick.rx >= -5 && Ps3.data.analog.stick.rx <= 5 && Ps3.data.analog.stick.ry >= -5 && Ps3.data.analog.stick.ry <= 5) {
    gs.stop();
  }
}

// 描述此函式...
void onConnect() {
  bz.alarm();
}

void setup()
{
  Ps3.attach(notify);;        //設定按鍵變更時的回呼函式
  Ps3.attachOnConnect(onConnect);//設定成功連線時的回呼函式
  Ps3.begin("20:00:00:00:11:22");  // 啟動 P3x搖桿的藍牙位址
  bz.alarm();
}


void loop(){}

//======================================
//GoARM in PS3 mode, 2025-5-15
//Modified:
//======================================
#include <Ps3Controller.h>
#include <dual2s.h>
#include <Servo.h>

//DC電機GPIO
#define PIN_MOTO_1A  14
#define PIN_MOTO_1B  32
#define PIN_MOTO_2A  12
#define PIN_MOTO_2B  33
#define PIN_MOTO_3A  25
#define PIN_MOTO_3B  26
#define PIN_MOTO_4A  23
#define PIN_MOTO_4B  22

//蜂鳴器GPIO
#define PIN_BUZZER   15

//超音波GPIO
#define PIN_USC_FRONT_ECHO 13
#define PIN_USC_FRONT_TRIG 27
#define PIN_USC_RIGHT_ECHO 5
#define PIN_USC_RIGHT_TRIG 4
#define PIN_USC_LEFT_ECHO 19
#define PIN_USC_LEFT_TRIG 18

//短距紅外線GPIO
#define PIN_IR_LEFT    34
#define PIN_IR_MIDDLE  35
#define PIN_IR_RIGHT   39

//供電偵測GPIO
#define PIN_BATTERY    36

//預設使用的PWM通道
#define PWM_CH_BUZZER 7
#define PWM_CH_M1A    8
#define PWM_CH_M1B    9
#define PWM_CH_M2A    10
#define PWM_CH_M2B    11
#define PWM_CH_M3A    12
#define PWM_CH_M3B    13
#define PWM_CH_M4A    14
#define PWM_CH_M4B    15

GoSUMO gs;
Buzzer bz(PIN_BUZZER, PWM_CH_BUZZER);
IR3CH  ir(PIN_IR_LEFT, PIN_IR_MIDDLE, PIN_IR_RIGHT);

/*-- 宣告區(全域變數) --*/
int SeekCnt = 0;
unsigned long lastRecTime_P3x = 0;  // 記錄上次收到 PS3 訊號的時間
const unsigned long disconnectTimeout = 5000;  // 5 秒未收到資料則判定為斷線
const long interval = 5000;     // 指定間隔時間5秒(5000毫秒)。

int PWM_Speed = 700;

//Servo Motor 變數區 & 機械臂微調區 -------
Servo servo18;  // MG995 #1
//Servo servo2;  // MG995 #2
#define SvoMAX 150
#define SvoMIN 70
#define SERVO18_PIN 18

bool Do_Catapult = false;


void notify() {
    lastRecTime_P3x = millis();  // **每當收到 PS3 搖桿訊號,更新時間**

    // 顯示按鍵狀態
    //if (Ps3.data.button.cross) Serial.println("按下:X 按鈕");
    if (Ps3.data.button.circle){  gs.act(GoSUMO::GO_RIGHT, PWM_Speed); delay(10);} //Serial.println("按下:O 按鈕");
    //if (Ps3.data.button.triangle) Serial.println("按下:△ 按鈕");
    if (Ps3.data.button.square){  gs.act(GoSUMO::GO_LEFT, PWM_Speed); delay(10);}  //Serial.println("按下:□ 按鈕");

    if (Ps3.data.button.up){ Do_Catapult = true; /*ServoARM("Base", "UP");*/   }    //上鍵:手臂抬起
    //if (Ps3.data.button.down){ ServoARM("Base", "DOWN"); }  //下鍵:手臂放下
    //if (Ps3.data.button.left){ ServoARM("Paw", "OPEN");  }  //左鍵:開爪
    //if (Ps3.data.button.right){ ServoARM("Paw", "CLOSE"); } //右鍵:閉爪

 

    // 顯示搖桿數值
    //Serial.printf("左搖桿 X: %d, Y: %d\n", Ps3.data.analog.stick.lx, Ps3.data.analog.stick.ly);
    //Serial.printf("右搖桿 X: %d, Y: %d\n", Ps3.data.analog.stick.rx, Ps3.data.analog.stick.ry);
      if (Ps3.data.analog.stick.ry <= -5 && Ps3.data.analog.stick.rx == 0) { gs.act(GoSUMO::FORWARD, map(Ps3.data.analog.stick.ry,-5,-128,100,1023));  }
      if (Ps3.data.analog.stick.ry >= 5 && Ps3.data.analog.stick.rx == 0)  { gs.act(GoSUMO::BACKWARD, map(Ps3.data.analog.stick.ry,5,127,100,1023));   }
      if (Ps3.data.analog.stick.lx <= -5 && Ps3.data.analog.stick.ly == 0) { gs.act(GoSUMO::GO_LEFT, map(Ps3.data.analog.stick.lx,-5,-128,100,1023)); }
      if (Ps3.data.analog.stick.lx >= 5 && Ps3.data.analog.stick.ly == 0)  { gs.act(GoSUMO::GO_RIGHT, map(Ps3.data.analog.stick.lx,5,127,100,1023));    }

          //左右搖桿皆靜止, 則呼叫車體停止運動。
    if ((Ps3.data.analog.stick.rx >= -5 && Ps3.data.analog.stick.rx <= 5 && Ps3.data.analog.stick.ry >= -5 && Ps3.data.analog.stick.ry <= 5) &&
       (Ps3.data.analog.stick.lx >= -5 && Ps3.data.analog.stick.lx <= 5 && Ps3.data.analog.stick.ly >= -5 && Ps3.data.analog.stick.ly <= 5))
    {  gs.stop();  } 
}

void onConnect() {
  for(int i=0; i<3; i++){ bz.alarm(); delay(200);}

  Serial.println("PS3 控制器已連接!");
  lastRecTime_P3x = millis();  // **成功連線時,記錄時間**
}

void setup() {
  Serial.begin(115200);
  bz.alarm();

  Ps3.attach(notify);              // 設定按鍵變更時的回呼函式
  Ps3.attachOnConnect(onConnect);  // 設定成功連線時的回呼函式
  Ps3.begin("20:00:00:00:90:00");  // 啟動 P3x搖桿的藍牙位址

  servo18.attach(SERVO18_PIN);
  servo18.write(90);
  delay(2000); 

  Do_Catapult=false;
}

void loop() {

  if(Do_Catapult){ Catapult("do"); Do_Catapult=false;}

}

/*====== 副程式區,start here. ========*/
void Catapult(String _ACTION){
	if(_ACTION == "do"){
    for (int angle = SvoMIN; angle <= SvoMAX; angle += 10) {
      servo18.write(angle);
      //Serial.printf("Servo1=%d°, Servo2=%d°\n", angle, SvoMAX - angle);
      delay(100);
    }

    for (int angle = SvoMAX; angle >= SvoMIN; angle -= 10) {
      servo18.write(angle);
      //Serial.printf("Servo1=%d°, Servo2=%d°\n", angle, SvoMAX - angle);
      delay(100);
    }
	}
  _ACTION == "none";
}
Tags:
Table of Contents