How can we help you today?
【運動】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";
}
