Làm robot tự học lệnh đơn giản.

Mô tả dự án: 

Robot arm, robot nhện, robot múa.. hay các robot mini có sử dụng động cơ servo đều là những sản phẩm gây ấn tượng với những chuyển động đẹp mắt. Đúng như tiêu chí của ARDUINO, mình sẽ làm một dự án ROBOT tự học lệnh cực kì COOL.  

Về robot tự học lệnh

Đây sẽ là một robot sử dụng động cơ servo như một phần chi tiết của nó.

Ta sẽ sử dụng chiết áp như một cảm biến góc.

Điều khiển servo ở hai chế độ:

  • Bằng tay: Sử dụng cảm biến để điều khiển trực tiếp.
  • Tự động: Dạy cho servo rồi để nó tự thực hiện lại động tác mà nó đã học.

Khởi động nào

Chuẩn bị

Mạch

Code test

//Tác giả: Thái Sơn
// CODE ROBOT SERVO HỌC LỆNH, ĐƯỢC DĂNG TẢI LẦN ĐẦU TẠI TRANG WEB: arduino.vn
// Ngày 1-1-2017
//tạo 1 đối tượng servo từ dưới lên trên
#define servo_max 1
// số step lớn nhất , nó sẽ tiêu tốn step_max*servo_max (byte) RAM
// ví dụ cần 200*1=200 byte RAM
#define step_max 200
// nút ấn
#define start_pause_pin 12
#define record_pin 7
#define ENABLE_EEPROM_PIN 2
// đèn báo
#define led_pin 13
// tốc độ nhanh chậm
#define delay_toc_do 5
//cài pin vào analog
byte pin_analog[servo_max] = { A0 };
// cài đặt pin ra cho servo
byte pin_servo[servo_max] = { 3 };
// có 5 servo
unsigned int A0_value;

byte get_goc(byte servo_i)
{
    // tính toán lấy giá trị góc của biến trở i
    switch (servo_i) {
    case 0:
        A0_value = constrain(analogRead(A0), 200, 823);
        return map(A0_value, 200, 823, 0, 180);
        break;
    default:
        break;
    }
}
// kích thước eeprom (số byte) trên arduino của bạn
// trong ví dụ này mình dùng arduino uno r3 có 1024 bytes EEPROM
const unsigned int SIZE_MEMORY_EEPROM = 1024;

///////////////////////////////////////////////////////////////////////////////////
/////////////////////////MAIN CODE- BẠN KHÔNG CẦN CHỈNH SỬA PHẦN NÀY////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////
#include <EEPROM.h>
#include <Servo.h>

Servo servo[servo_max];
//pin button
int step_move = 0;
/*  mảng lưu góc cho 5 servo, 90 là giá trị góc khởi tạo cho toàn bộ phần tử*/

byte goc_servo[servo_max][step_max];
// mảng 2 chiều quản lý (servo_max) servo
byte goc_tam_thoi[servo_max] = { 90 };
// lưu góc tạm thời tại thời điểm cần tính

void setup()
{

    pinMode(led_pin, OUTPUT);
    //2 pin button
    //Serial.begin(9600);
    pinMode(start_pause_pin, INPUT_PULLUP);
    pinMode(ENABLE_EEPROM_PIN, INPUT_PULLUP);
    pinMode(record_pin, INPUT_PULLUP);
    //  cài chế độ 5 pin analog
    for (byte i = 0; i < servo_max; i++) {
        pinMode(pin_analog[i], INPUT);
        servo[i].attach(pin_servo[i]);
    }

    ADCSRA = ((ADCSRA & (B11111000)) | B00000100); // Cài tần số quét analog 1mhz

    learn_and_move(); // chạy hàm
}

void move_servo(byte i, byte goc_i)
{
    servo[i].write(goc_i);
}
void nhap_nhay(unsigned int time_delay, byte count)
{
    for (byte i = 0; i < count; i++) {

        digitalWrite(led_pin, HIGH);
        delay(time_delay);
        digitalWrite(led_pin, LOW);
        delay(time_delay * 2);
    }
}
void record(unsigned int step_x)
{
    for (byte i = 0; i < servo_max; i++) {
        goc_servo[i][step_x] = get_goc(i);
    }
    nhap_nhay(100, 2);
    Serial.println(goc_servo[0][step_x]);
}
void move_all()
{

    for (byte i = 0; i < servo_max; i++) {
        move_servo(i, goc_tam_thoi[i]);
    } // di chuyển
}
void control_servo()
{
    byte hieu;
    for (byte i = 0; i < servo_max; i++) {
        hieu = abs(goc_tam_thoi[i] - get_goc(i));
        if ((hieu >= 1) && (hieu < 170)) { // chống nhiễu
            // bạn cũng không được di chuyển arm điều khiển quá nhanh

            goc_tam_thoi[i] = get_goc(i);
        }
    } // lấy góc
    move_all();
}

void nap_eeprom_sang_ram()
{

    // nạp eeprom sang RAM

    step_move = EEPROM.read(SIZE_MEMORY_EEPROM - 1); // lấy lại step_move từ rom
    for (byte i = 0; i < servo_max; i++) {

        for (int step_j = 0; step_j < step_max; step_j++) {
            goc_servo[i][step_j] = EEPROM.read(step_j + i * step_max);
            //nạp dữ liệu từ rom sang ram
        }
    }
}

void luu_vao_eeprom()
{
    // bước 1: lưu dữ liệu vào rom

    EEPROM.write(SIZE_MEMORY_EEPROM - 1, step_move); // ghi step_move vào rom
    delay(15); // đợi 15ms để hoàn thành ghi 1 ô nhớ

    nhap_nhay(500, 3);
    digitalWrite(led_pin, HIGH); // giữ nguyên đèn
    for (byte i = 0; i < servo_max; i++) {
        //Serial.print("luu");
        //Serial.println(i);
        for (int step_j = 0; step_j < step_move; step_j++) {
            EEPROM.write(step_j + i * step_max, goc_servo[i][step_j]);
            //nạp dữ liệu từ ram vào rom

            delay(15); // đợi 15ms để hoàn thành ghi 1 ô nhớ
        }
    }

    nhap_nhay(500, 3);

    digitalWrite(led_pin, LOW); // tắt đèn
}

void pause()
{
    // chỉ được lưu dữ liệu vào eeprom khi đã có dữ liệu
    // nhấn pause trước, sau đó mới nhấn nút ENABLE_EEPROM_PIN để bắt đầu ghi vào eeprom
    //
    if (digitalRead(start_pause_pin) == 0) {

        while (true) {
            digitalWrite(led_pin, 1);
            delay(300); // chống nhiễu
            Serial.println("PAUSE");

            if (digitalRead(ENABLE_EEPROM_PIN) == 0) {
                // nhấn lưu eeprom
                delay(300);
                luu_vao_eeprom();
            }

            if (digitalRead(start_pause_pin) == 0) {
                Serial.println("START");
                digitalWrite(led_pin, 0);
                delay(300); // chống nhiễu
                goto out_pause;
            }

        } //while
    } //if
out_pause:;
    // thoát lặp
}

void auto_move()
{

    Serial.println(step_move);
    nhap_nhay(50, 3);
    float hieu_f[servo_max];
    unsigned int step = 0, step_next;

    byte time;
    byte thay_doi;
    while (true) {
        //lấy hiệu góc hiện tại và góc sau

        if (step < step_move) {
            step_next = step + 1; // không viết : step++
        }
        else {
            /*step = step_mov :thì step tiếp theo của step cuối cùng là step đầu tiên*/
            step_next = 0;
        }

        for (byte i = 0; i < servo_max; i++) {

            hieu_f[i] = (float(goc_servo[i][step]) - float(goc_servo[i][step_next]));
        }

        Serial.println(step);
        Serial.println(goc_servo[0][step]);
        Serial.println(goc_servo[0][step_next]);
        Serial.println(hieu_f[0]);
        //int denta;
        /*vận tốc sẽ tăng dần khi khởi đầu, đạt max, vận tốc giảm dần khi ở cuối quá trình,

*/
        for (float loading = 1.0; loading <= 100.0; loading++) {

            //denta=30+((sq(loading-150))/1000);
            // loading là phần trăm %, đánh giá kết thúc 1 động tác là 100%
            for (byte i = 0; i < servo_max; i++) {

                goc_tam_thoi[i] = byte(float(goc_servo[i][step]) - (((hieu_f[i]) * loading) / 100.0));
            }

            Serial.print(loading);
            Serial.print("‚");
            Serial.print(hieu_f[0]);
            Serial.print("‚");
            Serial.println(goc_tam_thoi[0]);
            // gia tốc từ chậm->nhanh->chậm

            if ((loading >= 0.0) && (loading < 20.0)) {
                time = delay_toc_do * 2 + 5;
            }
            else if (loading < 30.0) {
                time = delay_toc_do / 2 + 5;
            }
            else if (loading < 90.0) {
                time = delay_toc_do + 5;
            }
            else {
                time = delay_toc_do * 2 + 5;
            }

            // delay(delay_toc_do);
            while ((millis() % delay_toc_do) != 0) { // làm trễ
                /* cứ sau denta_ms, vòng lặp mới được thoát, */

                pause();
            } //while
 /*
            Đoạn code < delay(time) >sẽ tương đương với < while((millis()%time)!=0){;} >
- Lý do mình mình không chọn delay :về bản chất Delay sẽ vô hiệu hóa hoàn toàn chương tình arduino ,
arduino sẽ không thể làm gì cho đến khi hết Delay. 
Trong khi đó chúng ta muốn ấn nút PAUSE(tạm dừng robot) ngay lập tức Robot sẽ không dừng ngay vì bị Delay vô hiệu hóa.
Đấy là lí do mình đã cho hàm pause() lồng vào bên trong khối while() như bạn đã thấy. 
            */
            move_all();
           

        } // for_loading

        if (step < step_move) {
            step++; // tăng cho lần kế tiếp
        }
        else {
            /* step = step_move*/
            step = 0; // lại từ đầu
        }

        //kết thúc 1 động tác
        delay(100);
    } //while
}

void learn_and_move()
{
    step_move = 0;

    while (digitalRead(start_pause_pin) != 0) {
        //b1: điều khiển servo bằng biến trở
        while (digitalRead(record_pin) != 0) {
            control_servo();

            if ((digitalRead(start_pause_pin) == 0) && (step_move == 0)) {

                // nếu chưa có cài đặt nào mà vô thẳng phần chạy thì hiểu : lấy cài đặt từ eeprom
                nap_eeprom_sang_ram();
            }

            if ((digitalRead(start_pause_pin) == 0) && (step_move != 0)) {
                goto buoc_3;
            }
            //delay(1);// bỏ delay luôn
        }
        // nút record đươcj nhấn, thoát lặp
        // b2: lưu vào mảng
        record(step_move);
        if (step_move < step_max) {
            step_move++; // tăng step cho bước sau
        }
    }
// nút start_pause được nhấn, thoát lặp
buoc_3:

    step_move--; //không có bước sau, giảm step_move xuống 1 đơn vị
    //b4:auto move
    auto_move();
}

void loop()
{

} // loop

GIẢI THÍCH CODE VÀ CÁCH DÙNG

Code được chia ra làm 2 phần rõ ràng:

  • Thiết lập người dùng: Cài đặt pin kết nối, số bước  để học.
  • Main code: bạn không cần chỉnh sửa phần này (tối ưu chưa nào ^^).

Trong phần tính toán lấy giá trị của biến trở để quy đổi về góc:

map(A0_value, 200,823, 0, 180);

Như đã biết, giá trị trả về của hàm AnalogRead trong khoảng từ 0-1023 (tương ứng mức điện áp 0->5v). Tuy nhiên, để thực hiện ý tưởng điều khiển tuyến tính servo với góc từ 0->180 thì ta phải lấy khoảng giá trị sao cho góc lệch của servo phải tương ứng với  góc lệch của biến trở.

SIZE_MEMORY_EEPROM:  Kích thước eeprom trên arduino của bạn thui.

 

Main code

Video hướng dẫn dùng code

Các bạn chú ý

Việc sử dụng EEPROM luôn được cân nhắc với tuổi thọ của bộ nhớ EEPROM.

Do đó, nếu yên tâm với bộ nguồn ổn định (USB hoặc PIN) thì không nhất thiết phải lưu dữ liệu vào EEPROM, Về mặt bản chất, việc dùng EEPROM chỉ là sao chép dữ liệu trên bộ nhớ tạm thời (RAM).

Vì vậy, đối với các dự án cần thay thế nguồn (tạm nghỉ robot, robot đồ chơi, robot công nghiệp..) thì chỉ khi thấy ưng ý với code chạy thử,  bạn mới cần đến lưu dữ liệu .

Mình cũng đã sử dụng thành công IC eeprom làm bộ nhớ ngoài cho dự án này.

Vì ic sử dụng giao tiếp I2C nên  2 chân analog A4 và A5 sẽ bị sử dụng để giao tiếp. Bạn không được dùng nó để đặt thêm biến trở.

ROBOT CÁNH TAY 5 BẬC

Chuẩn bị

Cũng như ví dụ trên, nhưng thêm 4 servo và phần khung robot. Thế là chúng ta có đồ chơi rồi.

Sơ đồ mạch

 

Code

//Tác giả: Thái Sơn
// CODE ROBOT SERVO HỌC LỆNH, ĐƯỢC DĂNG TẢI LẦN ĐẦU TẠI CỘNG ĐỒNG ARDUINO VIỆT NAM: arduino.vn
// Ngày 1-1-2017
//tạo 5 đối tượng servo từ dưới lên trên
#define servo_max 5
// số step lớn nhất , nó sẽ tiêu tốn step_max*servo_max (byte) RAM
// ví dụ cần 200*5=1000 byte RAM
#define step_max 200
// nút ấn
#define start_pause_pin 12
#define record_pin 7
#define ENABLE_EEPROM_PIN 2
// đèn báo
#define led_pin 13
// tốc độ nhanh chậm
#define delay_toc_do 20
//cài pin vào analog
byte pin_analog[servo_max] = { A0, A1, A2, A3, A4 };
// cài đặt pin ra cho servo
byte pin_servo[servo_max] = { 3, 5, 6, 9, 10 };
// có 5 servo
unsigned int A0_value, A1_value, A2_value, A3_value, A4_value;

byte get_goc(byte servo_i)
{
    // tính toán lấy giá trị góc của biến trở i
    switch (servo_i) {
    case 0:
        A0_value = constrain(analogRead(A0), 200, 823);
        return map(A0_value, 200, 823, 4, 175);
        break;
    case 1:
        A1_value = constrain(analogRead(A1), 200, 823);
        return map(A1_value, 200, 823, 4, 175);
        break;
    case 2:
        A2_value = constrain(analogRead(A2), 200, 823);
        return map(A2_value, 200, 823, 175, 4);
        break;
    case 3:
        A3_value = constrain(analogRead(A3), 200, 823);
        return map(A3_value, 200, 823, 175, 4);
        break;

    case 4:
        A4_value = constrain(analogRead(A4), 200, 823);
        return map(A4_value, 200, 823, 100, 175); // tay gắp, góc quay 100°->180°
        break;
    default:
        break;
    }
}
// kích thước eeprom (số byte) trên arduino của bạn
// trong ví dụ này mình dùng arduino uno r3 có 1024 bytes EEPROM
const unsigned int SIZE_MEMORY_EEPROM = 1024;

///////////////////////////////////////////////////////////////////////////////////
/////////////////////////MAIN CODE- BẠN KHÔNG CẦN CHỈNH SỬA PHẦN NÀY////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////
#include <EEPROM.h>
#include <Servo.h>

Servo servo[servo_max];
//pin button
int step_move = 0;
/*  mảng lưu góc cho 5 servo, 90 là giá trị góc khởi tạo cho toàn bộ phần tử*/

byte goc_servo[servo_max][step_max];
// mảng 2 chiều quản lý (servo_max) servo
byte goc_tam_thoi[servo_max] = { 90 };
// lưu góc tạm thời tại thời điểm cần tính

void setup()
{

    pinMode(led_pin, OUTPUT);
    //2 pin button
    //Serial.begin(9600);
    pinMode(start_pause_pin, INPUT_PULLUP);
    pinMode(ENABLE_EEPROM_PIN, INPUT_PULLUP);
    pinMode(record_pin, INPUT_PULLUP);
    //  cài chế độ 5 pin analog
    for (byte i = 0; i < servo_max; i++) {
        pinMode(pin_analog[i], INPUT);
        servo[i].attach(pin_servo[i]);
    }

    ADCSRA = ((ADCSRA & (B11111000)) | B00000100); // Cài tần số quét analog 1mhz

    learn_and_move(); // chạy hàm
}

void move_servo(byte i, byte goc_i)
{
    servo[i].write(goc_i);
}
void nhap_nhay(unsigned int time_delay, byte count)
{
    for (byte i = 0; i < count; i++) {

        digitalWrite(led_pin, HIGH);
        delay(time_delay);
        digitalWrite(led_pin, LOW);
        delay(time_delay * 2);
    }
}
void record(unsigned int step_x)
{
    for (byte i = 0; i < servo_max; i++) {
        goc_servo[i][step_x] = get_goc(i);
    }
    nhap_nhay(100, 2);
    Serial.println(goc_servo[0][step_x]);
}
void move_all()
{

    for (byte i = 0; i < servo_max; i++) {
        move_servo(i, goc_tam_thoi[i]);
    } // di chuyển
}
void control_servo()
{
    byte hieu;
    for (byte i = 0; i < servo_max; i++) {
        hieu = abs(goc_tam_thoi[i] - get_goc(i));
        if ((hieu >= 1) && (hieu < 170)) { // chống nhiễu
            // bạn cũng không được di chuyển arm điều khiển quá nhanh

            goc_tam_thoi[i] = get_goc(i);
        }
    } // lấy góc
    move_all();
}

void nap_eeprom_sang_ram()
{

    // nạp eeprom sang RAM

    step_move = EEPROM.read(SIZE_MEMORY_EEPROM - 1); // lấy lại step_move từ rom
    for (byte i = 0; i < servo_max; i++) {

        for (int step_j = 0; step_j < step_max; step_j++) {
            goc_servo[i][step_j] = EEPROM.read(step_j + i * step_max);
            //nạp dữ liệu từ rom sang ram
        }
    }
}

void luu_vao_eeprom()
{
    // bước 1: lưu dữ liệu vào rom

    EEPROM.write(SIZE_MEMORY_EEPROM - 1, step_move); // ghi step_move vào rom
    delay(15); // đợi 15ms để hoàn thành ghi 1 ô nhớ

    nhap_nhay(500, 3);
    digitalWrite(led_pin, HIGH); // giữ nguyên đèn
    for (byte i = 0; i < servo_max; i++) {
        //Serial.print("luu");
        //Serial.println(i);
        for (int step_j = 0; step_j < step_move; step_j++) {
            EEPROM.write(step_j + i * step_max, goc_servo[i][step_j]);
            //nạp dữ liệu từ ram vào rom
            delay(15); // đợi 15ms để hoàn thành ghi 1 ô nhớ
        }
    }

    nhap_nhay(500, 3);

    digitalWrite(led_pin, LOW); // tắt đèn
}

void pause()
{
    // chỉ được lưu dữ liệu vào eeprom khi đã có dữ liệu
    // nhấn pause trước, sau đó mới nhấn nút ENABLE_EEPROM_PIN để bắt đầu ghi vào eeprom
    //
    if (digitalRead(start_pause_pin) == 0) {

        while (true) {
            digitalWrite(led_pin, 1);
            delay(300); // chống nhiễu
            Serial.println("PAUSE");

            if (digitalRead(ENABLE_EEPROM_PIN) == 0) {
                // nhấn lưu eeprom
                delay(300);
                luu_vao_eeprom();
            }

            if (digitalRead(start_pause_pin) == 0) {
                Serial.println("START");
                digitalWrite(led_pin, 0);
                delay(300); // chống nhiễu
                goto out_pause;
            }

        } //while
    } //if
out_pause:;
    // thoát lặp
}

void auto_move()
{

    Serial.println(step_move);
    nhap_nhay(50, 3);
    float hieu_f[servo_max];
    unsigned int step = 0, step_next;

    byte time;
    byte thay_doi;
    while (true) {
        //lấy hiệu góc hiện tại và góc sau

        if (step < step_move) {
            step_next = step + 1; // không viết : step++
        }
        else {
            /*step = step_mov :thì step tiếp theo của step cuối cùng là step đầu tiên*/
            step_next = 0;
        }

        for (byte i = 0; i < servo_max; i++) {

            hieu_f[i] = (float(goc_servo[i][step]) - float(goc_servo[i][step_next]));
        }

        Serial.println(step);
        Serial.println(goc_servo[0][step]);
        Serial.println(goc_servo[0][step_next]);
        Serial.println(hieu_f[0]);
        //int denta;
        /*vận tốc sẽ tăng dần khi khởi đầu, đạt max , vận tốc giảm dần khi ở cuối quá trình, 
       
         */
        for (float loading = 1.0; loading <= 100.0; loading++) {

            //denta=30+((sq(loading-150))/1000);
            // loading là phần trăm %, đánh giá kết thúc 1 động tác là 100%
            for (byte i = 0; i < servo_max; i++) {

                goc_tam_thoi[i] = byte(float(goc_servo[i][step]) - (((hieu_f[i]) * loading) / 100.0));
            }

            Serial.print(loading);
            Serial.print("‚");
            Serial.print(hieu_f[0]);
            Serial.print("‚");
            Serial.println(goc_tam_thoi[0]);
            // gia tốc từ chậm->nhanh->chậm

            if ((loading >= 0.0) && (loading < 20.0)) {
                time = delay_toc_do * 2 + 5;
            }
            else if (loading < 30.0) {
                time = delay_toc_do / 2 + 5;
            }
            else if (loading < 90.0) {
                time = delay_toc_do + 5;
            }
            else {
                time = delay_toc_do * 2 + 5;
            }

            // delay(delay_toc_do);
            while ((millis() % delay_toc_do) != 0) { // làm trễ
                /* cứ sau denta_ms, vòng lặp mới được thoát, */

                pause();
            } //while
            
            /*
            Đoạn code < delay(time) >sẽ tương đương với < while((millis()%time)!=0){;} >
- Lý do mình mình không chọn delay :về bản chất Delay sẽ vô hiệu hóa hoàn toàn chương tình arduino ,
arduino sẽ không thể làm gì cho đến khi hết Delay. 
Trong khi đó chúng ta muốn ấn nút PAUSE(tạm dừng robot) ngay lập tức Robot sẽ không dừng ngay vì bị Delay vô hiệu hóa.
Đấy là lí do mình đã cho hàm pause() lồng vào bên trong khối while() như bạn đã thấy. 
            */
            move_all();
           

        } // for_loading

        if (step < step_move) {
            step++; // tăng cho lần kế tiếp
        }
        else {
            /* step = step_move*/
            step = 0; // lại từ đầu
        }

        //kết thúc 1 động tác
        delay(100);
    } //while
}

void learn_and_move()
{
    step_move = 0;

    while (digitalRead(start_pause_pin) != 0) {
        //b1: điều khiển servo bằng biến trở
        while (digitalRead(record_pin) != 0) {
            control_servo();

            if ((digitalRead(start_pause_pin) == 0) && (step_move == 0)) {

                // nếu chưa có cài đặt náo mà vô thẳng phần chạy thì hiểu : lấy cài đặt từ eeprom
                nap_eeprom_sang_ram();
            }

            if ((digitalRead(start_pause_pin) == 0) && (step_move != 0)) {
                goto buoc_3;
            }
            //delay(1);// bỏ delay luôn
        }
        // nút record đươcj nhấn, thoát lặp
        // b2: lưu vào mảng
        record(step_move);
        if (step_move < step_max) {
            step_move++; // tăng step cho bước sau
        }
    }
// nút start_pause được nhấn, thoát lặp
buoc_3:

    step_move--; //không có bước sau, giảm step_move xuống 1 đơn vị
    //b4:auto move
    auto_move();
}

void loop()
{
    
} // loop
  • Hàm quy đổi MAP được mình cài đặt phù hợp với con robot của mình, các bạn có thể cài lại cho phù hợp với dự án.
  • Bạn chỉ cần thêm vài Code để thêm servo , kể từ phần MAIN CODE trở xuống bạn không cần làm gì thêm ( nếu chú ý sẽ thấy phần Main Code ở cả 2 ví dụ trên là giống nhau).
  • Việc thêm số lượng servo cũng tương tự nhé.
  • Cấu trúc phân vùng nhớ:

TEST CODE

Ý tưởng có được

Trước kia, mình có xem trên youtube một video giới thiệu về một Robot arm (arduino+servo) có khả năng tự học lệnh rồi sau đó thực hiện lại các động tác mà nó đã học.

Link: http://letsmakerobots.com/robot/project/micro-servo-robot

Mình đã COPY-PASTE code của tác giả rồi chạy thử->Kết quả : Tuyệt cú mèo.

Nhưng rồi sau đó, rắc rối mới bắt đầu xuất hiện khi có vài chỗ mình không hiểu nổi tác giả đã viết gì, sau này ngẫm ra mới thấy, đoạn code đó đã được tinh chỉnh để phù hợp với Robot của anh ấy. Tức là mình phải sửa lại toàn bộ 1 trong 2 thứ: Code của anh ta hoặc Con robot của mình.

“Vậy là đủ rồi..!”

Thế là mình đi đến quyết định: Viết lại Code.

  • Tham khảo cấu trúc code của anh ấy.
  • Code có tính tối ưu hóa cao hơn.(Bạn không cần chỉnh sửa ở phần MAIN CODE ^^)
  • Robot có thể nhớ dữ liệu đã học kể cả khi mất điện(eeprom)
  • Chuyển động mượt hơn với biến gia tốc.
  • Sử dụng nút bấm 3 nút bấm linh hoạt thay vì dùng công tắc và nhấn đúp như Code cũ.
  • Biến trở Code cũ không phù hợp .(0->1023)
  • Tần số bộ counter analog 1mhz ( code cũ 125khz)
  • Viết xong ,để một mình mình chơi không vui bằng đem chia sẻ với cộng đồng.

Kết

Hi vọng việc chế tạo một robot cho riêng mình sẽ không còn quá khó nữa.

 

ROBOT WALKER

 

Robot Nhện

Robot Tosy của Việt Nam

 

Đèn nghệ thuật thì sao nhỉ ?!!

 

Robot vẽ nè..

 

FA Spam facebook

 

Mình rất tâm đắc với dự án này, tuy chưa được hài lòng với bộ khung Robot Arm cho lắm ( vật liệu cây nhà lá vườn, việc đầu tư cho servo cũng đã ngốn một khoản kha khá). Mong rằng mình có khả năng tân trang ẻm nó với bộ khung xịn hơn trong thời gian tới. 

 

Năm mới, chúc mọi người sức khỏe và gặt hái nhiều thành công hơn nữa. HAPPY NEW YEAR !

Chúc các bạn sớm có những ứng dụng thiết thực từ bài viết này^^enlightenedcoolheartyeslaugh

Tác giả:  Thái Sơn.

lên
10 thành viên đã đánh giá bài viết này hữu ích.
Các dự án được truyền cảm hứng

Select any filter and click on Apply to see results

Các bài viết cùng tác giả

NOKIA5110 | Hướng dẫn sử dụng và chia sẻ thư viện HOÀNG SA

Xin chào các bạn, bộ thư viện ST7565 homephone có nhiều tính năng nổi bật, để sánh ngang tầm ưu việt, giờ đây lcd 84x48 NOKIA5110 đã có một phiên bản giống hệt như vậy. Cùng khám phá nào !!  

lên
14 thành viên đã đánh giá bài viết này hữu ích.

ST7565 | Hướng dẫn sử dụng glcd ST7565 homephone và chia sẻ thư viện

LCD chip st7565 có kích thước màn hình 128x64 pixel, là loại lcd graphic đa dụng cho nhiều dự án. Ở nước ta việc mua mới lcd này khá khó do không được ưa chuộng, sở dĩ có chuyện này có lẽ là do thiếu các tài liệu và hướng dẫn liên quan. Hiện lcd loại này chỉ có thể vọc từ các điện thoại homephone hoặc mua cũ. Nếu bạn đang có trong tay lcd này, thì bài viết dưới sẽ giúp bạn sử dụng nó với arduino.

lên
15 thành viên đã đánh giá bài viết này hữu ích.