2022年2月20日 星期日

零基础入门学用Arduino-MeArm机械臂篇--重點整理

零基础入门学用Arduino-MeArm机械臂篇 - YouTube

重點整理:

2022.02.20
  • 關於MeArm機械臂的學習做個總結:
    • 買了兩座機械臂,第一座組裝時弄斷了一片部件,以快速膠黏上後可用,但組裝後前臂始終卡卡的,因此再買了一套,小心奕奕地組好後,前臂仍是卡卡的,導致操作不順。猜測應是所買的這套 MeArm 公差太大,也有可能是出在五金零件(螺絲)的公差。如果重來(雖然不可能),絕對去買太极创客在淘寶的套件:mearm机械臂 (含电源) / 太极创客 / 零基础入门学用Arduino教程-淘宝网
    • 要遙控,HC-06藍芽模塊是非買不可。雙搖桿模塊則是必買的部件(賣家所附的範例程式太完整,沒什麼改動就能上傳直接使用,程式碼值得好好學習),能精簡排線,甚至還預留接腳可以再插上HC-06藍芽模塊。
    • Arduino UNO R3 一定要買官方版,學習上才不會造成困擾。開發版雖然較便宜,這次就碰到問題學到了教訓。
    • 零基础入门学用Arduino-MeArm机械臂篇 - YouTube 系列循序漸進的教學,是自我學習最佳的教學影片。
2022.02.19
  • 紅外線遙控版的確可以作動,只是感應不夠靈敏,改以 IR感測器的範例卻又不會,最後才注意到  IR感測器的接收情形有異常:IR感測器的範例只有按遙控器的按鍵時IR感測器上的 Led 燈才會閃一下,表示接收到訊號,但改用零基础入门学用Arduino-MeArm机械臂篇-16 开发机械臂程序-3 - YouTube 附的範例程式修改紅外線遙控版,就看到IR感測器上的 Led 燈一直在閃,表示一直在接收訊號,問題是---沒有按遙控器啊!
  • 沒有按遙控器卻一直有訊號進來,程式需要不斷解碼,導致按鍵的反應會不靈敏,顯然是程式有 Bug 或者是其他的問題。
  • 經過了大半天時間最後發現是這段程式碼造成的原因:
      base.attach(11);     // base 伺服舵机连接引脚11 舵机代号'b'
      
      rArm.attach(10);     // rArm 伺服舵机连接引脚10 舵机代号'r'
     
      fArm.attach(9);      // fArm 伺服舵机连接引脚9  舵机代号'f'
      
      claw.attach(5);      // claw 伺服舵机连接引脚6  舵机代号'c'
      
    
  • 這意味著舵機與 IR 感測器是無法共存的(這大概就是教學影片為何使用的是 HC-06 藍芽模塊的原因吧)。由於學習到這裡,該了解的也差不多都會了,HC-06 藍芽模塊也不需要再購買,這個階段的學習到此結束,可以進入下一階段了。
2022.02.19
  • 利用 零基础入门学用Arduino-MeArm机械臂篇-16 开发机械臂程序-3 - YouTube 附的範例程式配合 IR感測器的範例很容易就改成紅外線遙控版本:

  • 這只是初步階段,目前還無法如前述能夠記錄動作並重播,需要從賣家附的程式拷貝如何記錄動及重播的部分程式碼,當然需要加以修改,確定都沒問題後,才能算是完成這個階段的學習。
2022.02.18
  • 雙搖桿到貨後,把賣家附的程式略為修改就能很方便地使用:
    • 主要的修改在四個舵機的最大與最小極限值,而這些都是在之前學習時已測出並記錄下來。
    • 因為沒有使用 hc06 bluetooth 模塊,所以把程式碼裡相關的部分註解掉。
    • 底下是程式碼:
      #include <Servo.h> 
      #include <SoftwareSerial.h>
      
      SoftwareSerial mySerial(12, 13);     // hc06 bluetooth rx/tx
      const int SERVOS = 4, MAXSPEED = 10; // servo pin 11/10/9/6 base/left/right/gripper
      int PIN[SERVOS], value[SERVOS], currentAngle[SERVOS], MIN[SERVOS], MAX[SERVOS], INITANGLE[SERVOS];
      Servo myservo[SERVOS];
      int aOrg[] = {90, 90, 90};  // initial angle for base/left/right arm arm
      int demoActions[9][4] = { { 90,  55, 165, 30},    // demo mode by pressing right button when boot 
                                { 45, 145,  90,  0},
                                { 90,  55, 165,  0},
                                {135, 145,  90, 30},
                                { 90,  55, 165, 30},
                                {135, 145,  90,  0},
                                { 90,  55, 165,  0},
                                { 45, 145,  90, 30},
                                { 90,  55, 165, 30}                          
                             };
                            
      int DELAYTIME = 200;
      int servo_moving[SERVOS] = {0,0,0,0};
      boolean stringComplete = false;
      int bt_servo = 0;
      int bt_move = 0;
      int idle[] = {0,0,0,0};
      boolean learningMode = false, autoPlayMode = false, autoDemoMode = false;
      boolean clawClosed = false, bootbuttonPressed = false;
      int const maxAutoActions = 100;
      int autoAction[maxAutoActions][SERVOS];  // can record up to 20 actions
      int actionIndex = 0;  // total steps of an movement
      int buttonPreState = 0;
      int buttonL = 2, buttonR = 4;
      int threshL = 300, threshR = 700;
      int clawOpenangle = 10, clawCloseangle = 95;
      int demospeed = 1;  //1 for 1x, 2 for 2x...
      
      void setup() {
        Serial.begin(9600);    // RX/TX = D0/D1
        pinMode(2, INPUT_PULLUP);  //2 to memorize 
        pinMode(4, INPUT_PULLUP);  //4 to start actions
        pinMode(3, OUTPUT);  
        digitalWrite(3, HIGH);
        Serial.print(digitalRead(buttonL));
        Serial.print(" , ");
        Serial.println(digitalRead(buttonR));
        
        // mySerial.begin(38400); // RX/TX = D12/D13 for bluetooth 
        
        init_Pins();
        cutcut();              
      
        // long press left button for recording mode
        if (digitalRead(buttonL)) { //這裡 ! 拿掉直接進入記錄模式,不然就無法進入。
          learningMode = true;
          buttonPreState = 1;
          delay(500);   //原為 1000 ,有點長,改成 500 剛剛好。
          digitalWrite(3, LOW);
          Serial.println("learningMode!!");
        }
        
        // long press right button for demo mode
        if (!digitalRead(buttonR)) { //記錄完畢後,按下這個鍵就可以重覆播放記錄的動作
          autoDemoMode = true;
          buttonPreState = 1;
          auto_mode();
        }
      }
      
      void loop() {
        // move_bt();            // move based on instruction from bluetooth
        move_joy();           // move based on instruction from joystick
        move_mem();           // move based on instruction from recording
      }
      
      void init_Pins(){       //這裡需要更改為之前測的各個舵機的最大、最小值
        PIN[0] = 11;          // pin to attach servo
        MIN[0] = 10;           // minimal angle of this servo based on mechanic structure
        MAX[0] = 170;         // maximum angle of this servo based on mechanic structure
        INITANGLE[0] = 90;    // initial angle at start up
        PIN[1] = 10;
        MIN[1] = 45;
        MAX[1] = 180;
        INITANGLE[1] = 90;
        PIN[2] = 9;
        MIN[2] = 15;
        MAX[2] = 125;
        INITANGLE[2] = 90;
        PIN[3] = 6;
        MIN[3] = 0;
        MAX[3] = 195;
        INITANGLE[3] = 60;
        
        //set motors to initial angles
        initMotors();
        
        // reset autoaction angles to record
        for (int i = 0 ; i < maxAutoActions; i++){
          for (int j = 0 ; j < SERVOS; j++){
            autoAction[i][j] = 0;
          }
        }
        // set autoAction[0][] = aOrg[];
        for (int i = 0 ; i < SERVOS-1; i++){
          autoAction[0][i] = aOrg[i];
        }
        autoAction[0][SERVOS-1] = clawOpenangle;
      }
      /* 未來這裡修改為 IR 紅外遙控的方式
      void move_bt(){ 
        checkSoftSerial();  // read bluetooth and set servo_moving[i] for each servo, format +1/0/-1
      
        for (int i = 0; i < SERVOS; i++){
          currentAngle[i] = myservo[i].read();  // get existing serveo angle
          
          if (servo_moving[i] != 0){
            currentAngle[i] += servo_moving[i];  // +/- angles based on the info from bluetooth
            currentAngle[i] = currentAngle[i] > MAX[i] ? --currentAngle[i] : currentAngle[i];  // ensure target angle within safe range 
            currentAngle[i] = currentAngle[i] < MIN[i] ? ++currentAngle[i] : currentAngle[i];
            myservo[i].write(currentAngle[i]);  // rotate each servo
            delay(20);
          }
        }
      }
      
      // read from bluetooth and illustrate into angle change
      // the format is 2 digit like "31", the first digit is servo , the 2nd digit is 1 or 2 for +/- angle
      void checkSoftSerial() {
        String str = "";
        
        if (mySerial.available()){
          for (int i = 0 ; i < 2; i++){
            str += (char)mySerial.read(); 
          }
          // the servo to move
          int value = str.toInt();
          bt_servo = value / 10;
          
          // the direction to move
          int angle = value % 10;
          if (angle == 2) bt_move = 1;
          else if (angle == 1) bt_move = -1;
          else bt_move = 0;
      
          servo_moving[bt_servo] = bt_move;  // so we set +1/0/-1 into array to be used in move_bt() 
        }
      }
      */
      void move_joy(){
        boolean joyChanged = false;
        
        for (int i = 0; i < SERVOS; i++){
          value[i] = analogRead(i);              // read the joystick value from analog port 0~3
          delay(2);
          currentAngle[i] = myservo[i].read();   // get existing angles    
             
          if (value[i] > threshR) {
            idle[i] = 0;      // reset idle because someone touch the joystick
            joyChanged = true;
            if (currentAngle[i] > MIN[i]) --currentAngle[i]; // adjust the angle according to joystick direction
            if (i == SERVOS-1) currentAngle[i] = clawCloseangle;            // for claw, only open/close mode
          } else if (value[i] < threshL) {
            idle[i] = 0;
            joyChanged = true;
            if (currentAngle[i] < MAX[i]) ++currentAngle[i]; 
            if (i == SERVOS-1) currentAngle[i] = clawOpenangle;
          } else {
            ++idle[i];
          }
          
          if (idle[i] >= 100){
            //myservo[i].detach();  // optional: detach servo after idle for a while
            idle[i] = 0;
          }
        }      
        if (joyChanged){  
          for (int i = 0 ; i < SERVOS; i++){
            // before write angle , let's attach servo if not
            if (!myservo[i].attached()) myservo[i].attach(PIN[i]);      
            myservo[i].write(currentAngle[i]);
          }  
        }
        delay(20);
      }
      
      // memorize the actions
      void move_mem(){
        if (!autoPlayMode){
          if (learningMode){
            //Serial.println("learningMode");
            // if one of any button pressed, memorize the angles
            if (!digitalRead(buttonR)) {
              // done recoding, going to auto play
              autoPlayMode = true;
              return;
            }
            int buttonPressed = !digitalRead(buttonL);
            //Serial.print("buttonPressed = ");
            //if (buttonPressed) Serial.println("buttonPressed");
            delay(1);
            
            
            if (buttonPreState && buttonPressed) return; // repeated count of the same press
            else if (!buttonPreState && buttonPressed){  // Left button pressed to record
            actionIndex++;
              buttonPreState = true;
              if (actionIndex < maxAutoActions){
                Serial.print("actionIndex = ");
                Serial.println(actionIndex);
                for(int i = 0; i < SERVOS; i++){
                  int tmp = myservo[i].read();
                  Serial.print(tmp);
                  Serial.print(" , ");
                  autoAction[actionIndex][i] = tmp;
                }
                Serial.println("");
                
                
                if (actionIndex == maxAutoActions) autoPlayMode = true;
              }
            } else buttonPreState = buttonPressed;
            
            if (!digitalRead(buttonR)) { // Right button pressed
              autoPlayMode = true;
            }
          }
        } else {
          // auto Playing Mode
          autoPlay();
        }
        //delay(100);
      }
      
      // open and close the claw 2 times
      void cutcut(){
        delay(1000);
        for (int i = 0; i < 2; i++){
          closeclaw(true);
          delay(250);
          closeclaw(false);
          delay(250);
        }
      }
      
      // auto mode for demo purpose, can be paused any time if joystick is touched
      void auto_mode(){
        int elements = sizeof(demoActions) / (4*sizeof(int));
        Serial.print("elements = ");
        Serial.println(elements);
        cutcut();
        while(1){
          for (int i = 0; i < elements-1; i++){
            if (buttonPressed()) {
              learningMode = false;
              autoPlayMode = false;
              autoDemoMode = false;
              return;  
            }
            armfromto(demoActions[i], demoActions[i+1]);
          }
        } 
      }
      
      //play recorded mode
      void autoPlay(){
        digitalWrite(3, HIGH);
        // moving from current to the first position
        //Serial.print("autoPlay: last one to 0 : total ");
        //Serial.println(actionIndex);
        //armfromto(autoAction[actionIndex], autoAction[0]);
        initMotors();
        delay(1000);
        cutcut();  
        
        while(1){    
          for (int i = 0; i < actionIndex; i++){     
            Serial.println("start playing...");
            
            // pause if joystick touched, resume if touch again
            if (buttonPressed()) {
              learningMode = false;
              autoPlayMode = false;
              autoDemoMode = false;
              return;
            }
            Serial.print("autoplay : ============="); 
            Serial.println(i);
            armfromto(autoAction[i], autoAction[i+1]);
          }
          delay(500/demospeed);
          armfromto(autoAction[actionIndex], autoAction[0]);  //back to the first action
          delay(500/demospeed);
        }
      }
      
      // rotate all servos at a time smoothly by using "for" loop and delay(30) 
      void armfromto(int *arrf, int *arrt){
        int maxAngles = 0;
        float lp[4];
      
        // adjust speed
        adjustSpeed();
            
        maxAngles = max(max(abs(arrt[0] - arrf[0]), abs(arrt[1] - arrf[1])), abs(arrt[2] - arrf[2]));
        maxAngles /= demospeed;
        maxAngles = maxAngles < 1 ? 1 : maxAngles;  
        Serial.print("speed = ");
        Serial.println(demospeed); 
        
        for (int i = 0; i < SERVOS-1; i++){  
         // Serial.print(arrt[i] - arrf[i]);
         // Serial.print(" "); 
        }
        //Serial.println(" === Delta");
        
        for (int i = 0; i < SERVOS-1; i++){    
          lp[i] = float(arrt[i] - arrf[i])/float(maxAngles);
          //Serial.print(lp[i]);
          //Serial.print(" "); 
          //Serial.println();
        }
        
        for (int j = 1; j <= maxAngles; j++){
      
          for (int i = 0; i < SERVOS-1; i++){
            
            //Serial.print(arrf[i]+j*lp[i]); 
            //Serial.print(" "); 
            myservo[i].write(arrf[i]+j*lp[i]);      
          }
         // Serial.print(" === "); 
         // Serial.println(j); 
          
          delay(20);
        } 
        // the claw
        myservo[SERVOS-1].write(arrt[SERVOS-1]);
        delay(20);  
          
        if (autoPlayMode || autoDemoMode){
          //Serial.print("claw angle: ");
          //Serial.println(clawCloseangle);
          //myservo[SERVOS-1].write(arrt[SERVOS-1]);
          //delay(20);  
        }
      }
      
      // close the claw
      void closeclaw(boolean op){
        if (op){
          myservo[SERVOS-1].write(clawCloseangle);
        } else {
          myservo[SERVOS-1].write(clawOpenangle);
        }
      }
      
      // check if joystick touched to stop the auto mode
      boolean joystickTouched(){
        int tmp = 500;
        
        for (int i = 0; i < SERVOS; i++){
          tmp = analogRead(i);
          delay(1);
          if (tmp > threshR || tmp < threshL) return true;
        }
        
        return false;
      }
      
      void initMotors(){
        // attach servos to pins
        for (int i = 0; i < SERVOS; i++){
          myservo[i].attach(PIN[i]);
          myservo[i].write(INITANGLE[i]);
          value[i] = 0;
          idle[i] = 0;
        }  
      }
      
      void adjustSpeed(){
        //if (learningMode) {
          for (int i = 0; i < SERVOS; i++){
            if (analogRead(i) > threshR) demospeed++;
            if (analogRead(i) < threshL) demospeed--;
          }
          demospeed = demospeed < 1 ? 1 : demospeed;
          demospeed = demospeed > MAXSPEED ? MAXSPEED : demospeed;
        //}
      }
      
      boolean buttonPressed(){
        //return true when any button pressed and not previously pressed
        if(!buttonPreState && (!digitalRead(buttonR) || !digitalRead(buttonL))) {
          buttonPreState = 1;
          return true;
        } else if (digitalRead(buttonR) && digitalRead(buttonL)){
          buttonPreState = 0;
        }
        return false;  
      }
  • 關於操作:
    • 接上電源後,機器手臂即處於「記錄」模式,例如影片中要將物體從左邊拿到右邊的動作,會需要記錄許多「關鍵」的 Action,每個Action 要記錄就要按一下左搖桿,便會把這個 Action 完後四個舵機的位置記錄在一個陣列中(本程式最多記錄 20 次的  Action )。
    • 記錄完後,按一下右搖桿便開始重播剛剛記錄的動作。
    • 要重新記錄新的動作,需要按 UNO 板子上的 Reset 。
  • 現在要重覆的動作不用於程式中寫死,可以在每次使用時先記錄要重覆的動作,然後重播,就可以反覆的執行:

  • 影片中行動電源上的遙控器底下壓著的是已連好的紅外線感測器,下一個目標是把這個裝置改成以紅外線遙控。
  • 由於本次的改動完全沒有動到程式核心的部分,因此要改成紅外線遙控才是真正的挑戰開始。
  • 看了 IR規格,目前先把紅外線感測器接在 Pin 5 上,用一條 M-M杜邦線接在搖桿板子上的 D5, 先以 IR 的範例測試,底下為遙控器按鍵對應的控制碼:
        
    
    #include "IRremote.h"
    
    int receiver = 5; // Signal Pin of IR receiver to Arduino Digital Pin 11
    
    /*-----( Declare objects )-----*/
    IRrecv irrecv(receiver);     // create instance of 'irrecv'
    decode_results results;      // create instance of 'decode_results'
    
    /*-----( Function )-----*/
    void translateIR() // takes action based on IR code received
    
    // describing Remote IR codes 
    
    {
    
      switch(results.value)
    
      {
      case 0xFFA25D: Serial.println("1"); break;
      case 0xFFE21D: Serial.println("3"); break;
      case 0xFF629D: Serial.println("2"); break;
      case 0xFF22DD: Serial.println("4");    break;
      case 0xFF02FD: Serial.println("5");    break;
      case 0xFFC23D: Serial.println("6");   break;
      case 0xFFE01F: Serial.println("7");    break;
      case 0xFFA857: Serial.println("8");    break;
      case 0xFF906F: Serial.println("9");    break;
      case 0xFF9867: Serial.println("0");    break;
      case 0xFFB04F: Serial.println("#");    break;
      case 0xFF6897: Serial.println("*");    break;
      case 0xFF18E7: Serial.println("UP");    break;
      case 0xFF10EF: Serial.println("LEFT");    break;
      case 0xFF38C7: Serial.println("OK");    break;
      case 0xFF5AA5: Serial.println("RIGHT");    break;
      case 0xFF4AB5: Serial.println("DOWN");    break;
      case 0xFFFFFFFF: Serial.println(" REPEAT");break;  
    
      default: 
        Serial.println(" other button   ");
    
      }// End Case
    
      delay(500); // Do not get immediate repeat
    
    
    } //END translateIR
    void setup()   /*----( SETUP: RUNS ONCE )----*/
    {
      Serial.begin(9600);
      Serial.println("IR Receiver Button Decode"); 
      irrecv.enableIRIn(); // Start the receiver
    
    }/*--(end setup )---*/
    
    
    void loop()   /*----( LOOP: RUNS CONSTANTLY )----*/
    {
      if (irrecv.decode(&results)) // have we received an IR signal?
    
      {
        translateIR(); 
        irrecv.resume(); // receive the next value
      }  
    }/* --(end main loop )-- */
    
  • 未來再來修改程式碼讓各個按鍵對應四個舵機的動作,目前的規劃:
    • Base 舵機往左⟺⬅︎
    • Base 舵機往右⟺➡︎
    • fArm 舵機往上⟺⬆︎
    • fArm 舵機往下⟺⬇︎
    • rArm 舵機往前⟺2
    • rArm 舵機往後⟺8
    • claw  舵機閉合⟺4
    • claw  舵機張開⟺6
    • 記錄模式⟺*
    • 重播模式⟺#
2022.02.17
  • 雙搖桿到貨,安裝容易,板子上已有各個舵機的接腳,只是爪子舵機的排線不夠長,還需要 F-M杜邦線,先前買的套裝有十根,做時鐘時被 ESP8266 用掉了九根,只剩一根,只好用三根短的 F-F及M-M來做延長線,不然排線就更精簡:

  • 四個舵機直接插在雙搖桿板子上的相對應的 D6 D9 D10 D11, 也不用再接麵包板拉出電源,之前寫的程式就能直接用:

  • 接下來就是改寫程式讓雙搖桿生效,直接由搖桿控制,最後再加入「記錄」動作並重播的功能,這樣才算 meArm 的學習完成。
2022.02.15
  • 等雙搖桿到貨,寫好程式,沒問題後就可以來嚐試遙控,不過不買教學影片裡的 HC-06 藍芽模塊,來試試買的套裝裡的紅外線遙控方式。
2022.02.14
  • 雙搖桿仍未到貨,閒著也是閒著,在 Mac  Boot Camp 裡的 Windows 10 安裝了 Arduino IDE 及 CH340 驅動,並上傳前述的程式到後來購買的 Arduino UNO R3 開發版,結果還是會當掉,有時還會來個大迴轉把線都捲在一起,而接另一片官方版的 R3 跑個幾十遍都沒問題,這才想起在 零基础入门学用Arduino-基础知识篇 - YouTube 一開始就提到初學者最好買官方版,免得問題多多,所以之前最先買的就是官方套裝版,使用上一直都沒有問題。也好在因為有了這片官方版可以做對比才找出問題的所在,假如只有這片開發版,大概會被它搞死,因為會以為是自己的程式有問題,說不定就半途而廢了。
  • 把上述問題與賣家說明,並提出願加差價換成官方版,賣家也爽快答應,真不錯!隔天就來把開發版寄回。
  • 未來購買,不是官方版不買。
2022.02.13
  • 程式很快就改寫好了,真的就如工廠的機器人手臂一樣,可以放到生產線了:

  • 程式的改寫其實只有一點點,幾乎全部都抄自範例,但至少說明範例程式讀通了,底下是程式碼:
        /*
    本程式主要是改寫 MeArm机械臂控制程序-5(by 太极创客 (2017-06-02)
    www.taichi-maker.com 而成
    */ 
    #include <Servo.h> 
     
    Servo base, rArm, fArm, claw ;  //建立4个舵机对象
    
    int DSD = 30; //Default Servo Delay (默认电机运动延迟时间)
                  //此变量用于控制电机运行速度.增大此变量数值将
                  //降低电机运行速度从而控制机械臂动作速度。
    
    //存储电机极限值(const指定该数值为常量,常量数值在程序运行中不能改变)
    const int baseMin = 0;
    const int baseMax = 180;
    const int rArmMin = 25;
    const int rArmMax = 180;
    const int fArmMin = 35;
    const int fArmMax = 90;
    const int clawMin = 0;
    const int clawMax = 115;
    
    
    
    void setup() 
    { 
      base.attach(11);     // base 伺服舵机连接引脚11 舵机代号'b'
      delay(200);          // 稳定性等待
      rArm.attach(10);     // rArm 伺服舵机连接引脚10 舵机代号'r'
      delay(200);          // 稳定性等待
      fArm.attach(9);      // fArm 伺服舵机连接引脚9  舵机代号'f'
      delay(200);          // 稳定性等待
      claw.attach(6);      // claw 伺服舵机连接引脚6  舵机代号'c'
      delay(200);          // 稳定性等待
     
      base.write(90); 
      delay(DSD);
      fArm.write(90); 
      delay(DSD);
      rArm.write(90); 
      delay(DSD);
      claw.write(90);  
      delay(DSD); 
     
    } 
     
    void loop(){
      
         Demo();
         armIniPos();
    }
     
    
    void servoCmd(char servoName, int toPos, int servoDelay){ //從範例直接搬過來的函數,完全未改
      Servo servo2go;  //创建servo对象
     
      int fromPos; //建立变量,存储电机起始运动角度值
       
      switch(servoName){
        case 'b':
          if(toPos >= baseMin && toPos <= baseMax){
            servo2go = base;
            fromPos = base.read();  // 获取当前电机角度值用于“电机运动起始角度值”
            break;
          } 
      
        case 'c':
          if(toPos >= clawMin && toPos <= clawMax){    
            servo2go = claw;
            fromPos = claw.read();  // 获取当前电机角度值用于“电机运动起始角度值”
            break;
          } 
     
        case 'f':
          if(toPos >= fArmMin && toPos <= fArmMax){
            servo2go = fArm;
            fromPos = fArm.read();  // 获取当前电机角度值用于“电机运动起始角度值”
            break;
          } 
              
        case 'r':
          if(toPos >= rArmMin && toPos <= rArmMax){
            servo2go = rArm;
            fromPos = rArm.read();  // 获取当前电机角度值用于“电机运动起始角度值”
            break;
          } 
      }
     
      //指挥电机运行
      if (fromPos <= toPos){  //如果“起始角度值”小于“目标角度值”
        for (int i=fromPos; i<=toPos; i++){
          servo2go.write(i);
          delay (servoDelay);
        }
      }  else { //否则“起始角度值”大于“目标角度值”
        for (int i=fromPos; i>=toPos; i--){
          servo2go.write(i);
          delay (servoDelay);
        }
      }
    }
    
    void Demo(){ //這部份只是簡單改寫 armIniPos() 函數而已
      
      int robotDemoArray[11][3] = { //利用範例記錄下各個位置然後寫入陣列
        {'c', 45, DSD},             
        {'b', 55, DSD},
        {'r', 142, DSD},
        {'f', 80, DSD},
        {'c', 110, DSD},
        {'r', 90, DSD},
        {'b', 115, DSD},
        {'r', 135, DSD},
        {'c', 45, DSD},
        {'f', 65, DSD},
        {'r', 90, DSD}
      };   
     
      for (int i = 0; i < 11; i++){
        servoCmd(robotDemoArray[i][0], robotDemoArray[i][1], robotDemoArray[i][2]);
      }
    }  
    
    
    void armIniPos(){
      Serial.println("+Command: Restore Initial Position.");
      int robotIniPosArray[4][3] = {
        {'b', 90, DSD},
        {'r', 90, DSD},
        {'f', 90, DSD},
        {'c', 90, DSD} 
      };   
     
      for (int i = 0; i < 4; i++){
        servoCmd(robotIniPosArray[i][0], robotIniPosArray[i][1], robotIniPosArray[i][2]);
      }
    }
    
  • 雙搖桿賣家連貨都還沒出,接下來不知道能再做些什麼?倒是在 YouTube 看到可以先用搖桿做出上述的動作,然後把動作「記憶」下來讓機器人不斷重覆,這樣就更彈性,不像上述影片把動作寫死,要更改得要連接電腦重新記錄動作的陣列並上傳。
2022.02.13
  • 雙搖桿還沒到貨,暫時還無法進一步學習,不過稍為把MeArm_Servo_Chk.ino 改寫了一下,轉動速度變慢,變成自動展示:
2022.02.12
2022.02.12
  • 燒掉的舵機好奇它的內部結構,在丟掉前拆解拍照看個清楚:



2022.02.11
  • meArm 機械手臂的初步成果:它的動作是在電腦上下指令操控的,未來會改成使用搖桿來操控:
    1. 組裝過程中弄斷了一片零件,使用快速膠黏上,後續動作就卡卡的,拆裝了不下三次,最後才有影片這樣,卡卡的程度最小(影片中可看到略為抖動的現象)。
    2. 測試過程中出現燒焦味,最後還冒煙,然後燒掉了一個舵機及擴充電源模塊(查價後得知一個台幣 24 元),與蝦皮的賣家聊聊後才了解 Arduino 的 5V2A 供電就足夠了。算是學到一個經驗:

    3. 燒掉的是AMS1117-5V 電源IC:

  • meArm 套件不是很貴,為了後續改用搖桿操控時能夠滑順,再向蝦皮賣家買了一套及 PS2雙搖捍。有了幾次的組裝經驗,meArm 不能像之前那樣迫不及待就組好,最後還得拆掉重來。幾次下來的心得:
      1. 本套教程所有示例程序下载
      2. MeArm机械臂装配说明(太极创客编制版)
      3. MeArm安装调试程序

2022.02.10

沒有留言:

張貼留言

您可以留下意見,但 Luke 可能無法馬上回覆,尚請見諒。