How can we help you today?
【OpenBuild-永續】aquaBot – 雙體船型
前言
【Wow..想再將垃圾由水裡撈回來,竟是如此困難!!】
這是每回課後,學員們經常有的共同感受。
台灣四面環洋,水域環境變化性大。我們能否善用科技工具協助探查、維護美麗的水域環境呢? 如此的科技永續思維,或許值得我們佇腳一探。水域機器人型態豐富,本專題提供一種趣味也容易執行的水域機器人(aquaBot),其產出的教學方案期望達到:
科技融入:善用學校既有教學設備(我們提供STL、雷切檔案…等自造檔案。)
SDGs融入:運用回收再造(upcycling)的概念、水域清理偵測為目標。
情境興趣:簡單可行、饒富互動,可擴展為團體合作趣味競賽的學習方案。
挑戰體驗:設計打撈水面物體(垃圾)的小機構。
btw, 設計船體時,水線(吃水深度),為何重要? 如何計算呢?
材料與工具
- dual2s-mini 或 dual2s控制器、防水盒
- 馬達:N20-1000rpm * 2、TT馬達 * 1 (或伺服馬達)
- 全浸槳 *2、延伸桿、連接器
- 雷切板件、3D列印件、M3螺釘
- 回收寶特瓶*2、束帶*2
工具:
- 十字起子、白膠、熱熔膠槍
- 鑽頭
- Android手機或平板
控制器介紹
dual2s-mini運動控制器

開源檔案
- 雷切檔案:【aquaBot雙體船 DXF】
- 3D列印檔案:【aquaBot雙體船 STL】
- dual2s / dual2s-mini 控制器程式範例:【yesioBlockly積木程式-ArduinoC】
- Android智慧裝置(手機、平板)直接下載安裝:【APK下載安裝】。
- APP公版程式完整包:【下載開發】。
程式範例 – Android模式
控制模式

yesioBlockly積木程式
宣告區塊:

主程式區塊:

Arduino C語言
Android – BT範例
#include <dual2s.h>
#include "BluetoothSerial.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_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
BluetoothSerial SerialBT; //藍牙串列物件
char BTCMD[3] = {0}; //儲存藍牙串列接收到的指令
int PWMspeed = 1000; //儲存藍牙串列接收到的PWM speed數值
int SeekCnt = 0;
//GoSUMO gs;
Buzzer bz(PIN_BUZZER, PWM_CH_BUZZER);
Power volt(PIN_BATTERY);
Motor m1(PIN_MOTO_1A, PIN_MOTO_1B, PWM_CH_M1A, PWM_CH_M1B); //馬達接口:MOTO-1
Motor m2(PIN_MOTO_2A, PIN_MOTO_2B, PWM_CH_M2A, PWM_CH_M2B); //馬達接口:MOTO-2
Motor m3(PIN_MOTO_3A, PIN_MOTO_3B, PWM_CH_M3A, PWM_CH_M3B); //馬達接口:MOTO-3
void setup(){
Serial.begin(115200);
SerialBT.begin("yesio-20"); //啟動藍牙串列並設定藍牙裝置名稱
bz.alarm();
}
void loop(){
if (Serial.available()) { SerialBT.write(Serial.read()); }
if (SerialBT.available()) {
int i;
for (i=0; i<4; i++){ BTCMD[i] = SerialBT.read(); }
}
if(atoi(BTCMD)>1) { PWMspeed = atoi(BTCMD);}
//BTCMD[0] = 'D';
if(BTCMD[0] == 'F') { m1.act(Motor::CW, PWMspeed); m2.act(Motor::CW, PWMspeed); }
else if(BTCMD[0] == 'B') { m1.act(Motor::CCW, PWMspeed); m2.act(Motor::CCW, PWMspeed); }
else if(BTCMD[0] == 'L') { m1.act(Motor::CCW, PWMspeed); m2.act(Motor::CW, PWMspeed); }
else if(BTCMD[0] == 'R') { m1.act(Motor::CW, PWMspeed); m2.act(Motor::CCW, PWMspeed); }
else if(BTCMD[0] == 'Z') { m1.stop(); m2.stop(); m3.stop();}
else if(BTCMD[0] == 'H') { bz.alarm(); }
/*Servo Control, start here. */
else if(BTCMD[0] == 'A') { m3.act(Motor::CW, PWMspeed); } //ServoBase Rising
else if(BTCMD[0] == 'a') { m3.act(Motor::CCW, PWMspeed); } //ServoBase Declining
}
PS2搖桿範例
#include <dual2s.h>
#include <PS2X_lib.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);
#define PS2_DAT 4
#define PS2_CMD 17
#define PS2_SEL 16
#define PS2_CLK 21
#define pressures false
#define rumble false
PS2X ps2x;
int error = -1;
byte PS2X_type = 0;
byte vibrate = 0;
int tryNum = 1;
Motor m1(PIN_MOTO_1A, PIN_MOTO_1B, PWM_CH_M1A, PWM_CH_M1B); //馬達接口:MOTO-1
Motor m2(PIN_MOTO_2A, PIN_MOTO_2B, PWM_CH_M2A, PWM_CH_M2B); //馬達接口:MOTO-2
Motor m3(PIN_MOTO_3A, PIN_MOTO_3B, PWM_CH_M3A, PWM_CH_M3B); //馬達接口:MOTO-3
void setup()
{
Serial.begin(115200);
bz.alarm();
PS2X_INIT(); //注意,此將占用DUAL右側超音波
}
void loop()
{
if(PS2X_type != 2){
ps2x.read_gamepad(false, vibrate); //讀取PS2命令
Serial.println(ps2x.Analog(PSS_RY));
Serial.println(ps2x.Analog(PSS_RX));
Serial.println(ps2x.Analog(PSS_LY));
Serial.println(ps2x.Analog(PSS_LX));
if(ps2x.Analog(PSS_RY) <= 118){ m1.act(Motor::CW, map(ps2x.Analog(PSS_RY),128,0,150,1023)); m2.act(Motor::CW, map(ps2x.Analog(PSS_RY),128,0,150,1023)); } //前進
if(ps2x.Analog(PSS_RY) >= 138){ m1.act(Motor::CCW, map(ps2x.Analog(PSS_RY),128,255,150,1023)); m2.act(Motor::CCW, map(ps2x.Analog(PSS_RY),128,255,150,1023)); } //後退
if(ps2x.Analog(PSS_RX) <= 118){ m1.act(Motor::CCW, map(ps2x.Analog(PSS_RX),128,0,150,1023)); m2.act(Motor::CW, map(ps2x.Analog(PSS_RX),128,0,150,1023)); } //左旋轉
if(ps2x.Analog(PSS_RX) >= 138){ m1.act(Motor::CW, map(ps2x.Analog(PSS_RX),128,255,150,1023)); m2.act(Motor::CCW, map(ps2x.Analog(PSS_RX),128,255,150,1023)); } //右旋轉
if((ps2x.Analog(PSS_RX) > 125 && ps2x.Analog(PSS_RX) < 130) && (ps2x.Analog(PSS_RY) > 125 && ps2x.Analog(PSS_RY) < 130)) { m1.stop(); m2.stop(); m3.stop(); }
if(ps2x.Analog(PSS_LX) <= 118){ }
if(ps2x.Analog(PSS_LX) >= 138){ }
if(ps2x.Analog(PSS_LY) <= 118){ m3.act(Motor::CW, map(ps2x.Analog(PSS_LY),128,0,150,1023)); }
if(ps2x.Analog(PSS_LY) >= 138){ m3.act(Motor::CCW, map(ps2x.Analog(PSS_LY),128,255,150,1023)); }
}
delay(15);
}
/*====== 副程式區,start here. ========*/
void PS2X_INIT(){
while (error != 0) {
delay(1000);
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", PS2X_type); break;
case 1: Serial.printf("DualShock Controller found, type is %d", PS2X_type); break;
case 2: Serial.printf("GuitarHero Controller found, type is %d", PS2X_type); break;
case 3: Serial.printf("Wireless Sony DualShock Controller found, type is %d", PS2X_type); break;
}
}
浮力計算

計算寶特瓶所受的浮力
根據阿基米德原理,浮力大小等於物體排開的水的重量:

已知條件
- 寶特瓶直徑:D=80D = 80D=80 mm = 0.08 m
- 寶特瓶長度:L=250L = 250L=250 mm = 0.25 m
- 沒入水中的高度:h=10h = 10h=10 mm = 0.01 m
- 水的密度:ρ水=1000 kg/m³
- 重力加速度:g=9.81g = 9.81g=9.81 m/s²
計算沒入水中的體積
寶特瓶的橫截面是圓,沒入水中的部分是圓的弦截面,其面積計算如下:

A 值為 0.0003626 平方公尺(m²)
V值 = 0.0003626(m²) * 0.25m = 9.07×10^−5立方公尺

