Cách đây một thời gian (hơn 8 tháng) tôi đã suy nghĩ về những gì tôi có thể xây dựng. Tôi muốn tạo ra một robot / thiết bị thú vị sẽ là thử thách cho tôi và sẽ khuyến khích tôi học những điều mới. Tôi đã nghĩ về rất nhiều robot nhưng rất nhiều trong số chúng đã được đăng trên internet. Và tôi đã nghĩ về việc chế tạo một máy bay không người lái một mình hoàn toàn, bao gồm bộ điều khiển máy bay, frame, chương trình và thiết kế khung. Đây là bản dựng dài nhất tôi đã thực hiện, phải mất rất nhiều thời gian và công sức để hoàn thành nhưng cuối cùng sau hơn 8 tháng nó đã sẵn sàng và tôi ở đây để chia sẻ nó với bạn như một dự án nguồn mở hoàn toàn.
Thông tin cho người mới bắt đầu:
Dự án này không phải là đơn giản. Bạn cần một số kiến thức cơ bản về lập trình arduino, PCB và thiết bị điện tử để thực hiện.
Bước 1: Hình ảnh của máy bay
Bước 2: Tại sao Chương trình, Khung, PCB là của riêng tôi?
Bạn có thể hỏi tại sao bạn viết chương trình của riêng bạn cho nó? Tại sao bạn thực hiện khung in 3D tùy chỉnh (xem các bước tiếp theo để xem tại sao tôi thất bại với nó)? Và bổ sung frame tùy chỉnh và PCB. Về cơ bản là vì tôi có thể 🙂 Tôi thích tự làm mọi thứ và học cách chúng hoạt động. Nhờ kiểu suy nghĩ này mà tôi đã học được rất nhiều về quadcopters và cách chúng hoạt động.
Code
Những vấn đề lớn nhất tôi gặp phải là với code để ổn định nó, thật khó để viết nó nhưng rất khó điều chỉnh và loại bỏ một số lỗi. Nó vẫn chưa hoàn hảo nhưng gần hơn với sự hoàn hảo và nó hoạt động thực sự tốt cho đến nay. Khi code của tôi đã sẵn sàng, tôi bắt đầu điều chỉnh các bộ điều chỉnh PID và đây là một điều tồi tệ, tôi gần như phá hủy phòng của tôi vì một số lỗi rất nhỏ trong code của tôi (vì một số lý do code của tôi bị giảm int vô hạn và khi đạt đến giá trị tối thiểu, nó sẽ chuyển sang giá trị tối đa của int và máy bay không người lái của tôi đã bật động cơ 100% và đập vào tường và cửa trong phòng tôi). Trong một thời gian dài bộ điều chỉnh PID của tôi đã được điều chỉnh rất tệ nhưng mỗi ngày tôi lại gần để làm cho nó hoàn hảo. Trong khi điều chỉnh, tôi đã phá vỡ khung in 3D của mình 4 lần 🙁
Đôi điều về khung in 3D
Tôi đã nghĩ rằng việc tạo khung in 3d có thể rất hữu ích. Có khả năng là tôi sẽ phá vỡ nó ít nhất vài lần (tôi đã đúng :)) nhưng sau 7 phần bị hỏng tôi quyết định mua một cái, chủ yếu vì nó mạnh và cứng hơn nhiều. Tôi đã phá vỡ khung này trong máy bay đầu tiên với nó 🙁
Frame
Lý do chính là tiền. Nếu bạn muốn mua một frame cho máy bay không người lái, bạn cần phải chi ít nhất 50 đô la, đó là khá nhiều. Vì vậy, tôi xây dựng cho riêng tôi với giá 20 đô la. Và điều hay ở đây là tôi có thể tạo nhiều kênh như tôi muốn 🙂
PCB
Vì code riêng của tôi và frame riêng của tôi, tôi phải tạo ra PCB. Tôi đã làm 3 cái trong số chúng. Đầu tiên là mạch phân phối điện cho động cơ, thứ hai là frame và người thứ ba dĩ nhiên là điều khiển máy bay. Tất cả chúng sử dụng thông qua các board lỗ giúp nó dễ dàng hơn để hàn. Trong phiên bản cuối cùng của tôi, tôi không sử dụng bảng phân phối điện vì khung của tôi đã có.
Bước 4: Bộ phận
Đối với dự án này, chúng tôi cần rất nhiều phần, bởi vì nó thực sự hơi phức tạp. Tổng chi phí của chúng là khoảng 200 đô la không quá nhiều cho một máy bay không người lái, nhưng đó là vì chúng tôi tạo ra một board điều khiển máy bay và frame bởi chính mình. Dưới đây là danh sách đầy đủ với các liên kết đến các bộ phận mà tôi đã mua từ các nhà cung cấp khác nhau:
- Động cơ
- ESC – tùy thuộc vào động cơ mà bạn chọn dòng tối đa của ESC nên lớn hơn một chút so với dòng tối đa của động cơ. Nó phải có BEC vì bộ điều khiển máy bay được cung cấp bởi nó.
- Pin – Li-Po 3S (11.1V) nên có dung lượng khoảng 3000mAh để có thời gian bay 15 phút. Hãy chắc chắn rằng nó có thể cung cấp đủ dòng điện.
- Khung – lúc đầu được in 3D và trong tính năng này, nhưng hiện tại tôi đã sử dụng khung F450.
- Bộ sạc LiPo – bạn phải sạc pin lipo bằng bộ sạc đặc biệt.
- Cánh quạt – có thể khác nhau nhưng cái này hoạt động tốt nhất. Tôi khuyên bạn nên mua thêm một số cái, chúng rất dễ bị hỏng. Họ cần phải có một lỗ không chủ đề. Bạn chọn một số màu khác nếu bạn muốn.
- Bảo vệ / màn hình pin – không bao giờ kết nối pin với bất cứ thứ gì mà không có nó, tôi đã phá hủy 2 Lipo vì tôi quên nó.
Và đây là các bộ phận cho bộ điều khiển máy bay:
- Atmega328
- NRF24L01 – modul radio, chúng tôi cần nó để tạo liên lạc vô tuyến, hãy nhớ mua phiên bản có antena bên ngoài vì nó có phạm vi lớn hơn nhiều
- MPU6050 – con quay hồi chuyển và gia tốc kế trong một mô-đun
- Một số phần nhỏ hơn:
- tụ 22pF (x2)
- tụ 10uF
- điện trở 4,7kOhm
- Mạch ổn áp 3,3V
- một số dây đực-cái
Điều cuối cùng là frame:
- NRF24L01 – modul radio, chúng tôi cần nó để tạo liên lạc vô tuyến, hãy nhớ mua phiên bản có antena bên ngoài vì nó có phạm vi lớn hơn nhiều
- Atmega328
- Remote điều khiển
- Một số phần nhỏ hơn:
- tụ 22pF (x2)
- tụ 10uF (x2)
- điện trở 4,7kOhm
- Mạch ổn áp 3,3V
- LED
Bước 5: Tạo mẫu
Điều đầu tiên cần làm là khung ngoài, để kiểm tra cách mọi thứ hoạt động và nếu các mạch được kết nối tốt. Tôi đã gắn arduino UNO vào khung drone và gắn mpu6050 trong bảng mạch đã được cố định vào khung bằng băng dính hai mặt. “Frame” tương đương với chiết áp trong bảng mạch có dây cáp dài 5m với máy bay không người lái của tôi. Tại thời điểm này, tôi đã tạo ra máy bay không người lái đầu tiên:) Sau khi thử nghiệm, trong khung gỗ của tôi, các thử nghiệm ngoài trời và một số thay đổi trong thiết kế kết nối tôi đã tạo ra sơ đồ mạch và sau đó là PCB. Ở trên bạn có thể thấy một số hình ảnh về máy bay không người lái của tôi với rất nhiều dây cáp trên đó.
Trên một trong những bức ảnh đó, bạn có thể thấy cáp màu đỏ với băng đen trên đó, hãy đoán xem đây là gì 🙂 đó là một loại công tắc điện cho board arduino.
Khung này để thử nghiệm một máy bay không người lái được làm từ gỗ chỉ để giữ nó đúng vị trí và để nó xoay trên một trục, đó là tất cả.
Bước 6: Giải thích code
Điều khiển máy bay
Code này không khó như bạn nghĩ. Nó chỉ sử dụng 3 thuật toán PID để ổn định nó theo 3 trục (x, y, z), code để lấy góc từ DMP của mpu6050 (xử lý chuyển động kỹ thuật số), và cũng xử lý nhận radio và một số phép toán cơ bản để tính toán dữ liệu nhận được qua radio. Nó tự ổn định ở tốc độ 100 lần một giây (100Hz), điều đó gây ra từ máy bay không người lái này.
Các thuật toán PID:
Có rất nhiều khám phá tuyệt vời về cách thức hoạt động của PID, và vì tôi không phải là một người giỏi làm việc nên tôi không cố gắng giải thích cách thức hoạt động của nó. Tôi sử dụng các giá trị đó cho bc vì sau khi thử nghiệm, tôi thấy chúng là giá trị tốt nhất, nhưng chúng vẫn có thể tốt hơn, với Kd rất lớn. Tôi không phải là chuyên gia về PID và tôi chưa bao giờ thực hiện nó trên bất kỳ máy bay không người lái nào khác. Đối với các động cơ, cánh quạt khác nhau, khung giá trị này có thể khác nhau.
MPU6050:
Code này chỉ được sao chép từ thư viện MPU6050. Tôi muốn viết thư viện của riêng mình, nhanh hơn cho nó nhưng tôi không thể tìm thấy bất kỳ bảng dữ liệu tốt nào về nó và tôi đã lười biếng 🙂 Nếu có thư viện sẵn sàng hoạt động tốt (nhưng có thể nhanh hơn) tại sao không sử dụng nó?
NRF24L01
Đây chỉ là một vài dòng để nhận được các mảng gồm 8 bit từ vị trí hiện tại và chuyển đổi nó thành góc và xoay trên trục x, y và z. Bạn cũng có thể thêm nhiều giá trị hơn và sử dụng chúng bật đèn trên drone hoặc bất cứ thứ gì bạn thích.
Vị trí
Code cho vị trí là thẳng về phía trước. Chỉ cần đọc các giá trị cần điều khiển và gửi chúng qua NRF24L01, không có gì phức tạp. Tốc độ vô tuyến được giảm để tăng phạm vi.
Cả hai code bạn có thể tìm thấy dưới đây.
<p>#include "I2Cdev.h"<br>#include <servo.h> #include <spi.h> #include "RF24.h" #include "MPU6050_6Axis_MotionApps20.h" #if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE #include "Wire.h" #endif //we need this to see nrf24l01 configuration in serial #include "printf.h"</spi.h></servo.h></p><p>//end of libraries ###############################################</p><p>MPU6050 mpu;</p><p>float x_rotation, y_rotation, z_rotation; Servo motor1; Servo motor2; Servo motor3; Servo motor4; bool first_loop = true; //radio RF24 radio(9,10); uint8_t data[6]; const uint64_t pipe = 0xE8E8F0F0E1LL; long last_received; int controll_number = 159;</p><p>//values = 5.2, 0.02, 1500 //variables for movement and positions ########################################### //for my quadcopter this are the best settings for pid float x_kp = 5, x_ki = 0.02, x_kd = 1100; //values for PID X axis int max_pid = 300; float x_p_out, x_i_out, x_d_out, x_output; //outputs for PID float x_now, x_lasttime = 0, x_timechange; float x_input, x_lastinput = 0, x_setpoint = 0; float x_error, x_errorsum = 0, x_d_error, x_lasterror;</p><p>//values = 5.2, 0.02, 1500 float y_kp = 5, y_ki = 0.02, y_kd = 1100; //values for PID Y axis float y_p_out, y_i_out, y_d_out, y_output; //outputs for PID float y_now, y_lasttime = 0, y_timechange; float y_input, y_lastinput = 0, y_setpoint = 0; float y_error, y_errorsum = 0, y_d_error, y_lasterror;</p><p>float z_kp = 2, z_ki = 0, z_kd = 0; //values for PID Z axis float z_p_out, z_i_out, z_d_out, z_output; //outputs for PID float z_now, z_lasttime = 0, z_timechange; float z_input, z_lastinput = 0, z_setpoint = 0; float z_error, z_errorsum = 0, z_d_error, z_lasterror;</p><p>//set it to 0 and see on serial port what is the value for x and y rotation, use only if your flightcontroller board is not perfevtly leveled. If your board is perfectly leveled set it to 0 float x_level_error = 0; float y_level_error = 0;</p><p>/* * * * JUNE 2016 - APRIL 2017 * C by Nikodem Bartnik * nikodem.bartnik@gmail.com * nikodembartnik.pl * * * */</p><p>#define INTERRUPT_PIN 2 // use pin 2 on Arduino Uno & most boards int motor1_power; int motor2_power; int motor3_power; int motor4_power;</p><p>float allmotors_power = 600;</p><p>// MPU control/status vars bool dmpReady = false; // set true if DMP init was successful uint8_t mpuIntStatus; // holds actual interrupt status byte from MPU uint8_t devStatus; // return status after each device operation (0 = success, !0 = error) uint16_t packetSize; // expected DMP packet size (default is 42 bytes) uint16_t fifoCount; // count of all bytes currently in FIFO uint8_t fifoBuffer[64]; // FIFO storage buffer</p><p>// orientation/motion vars Quaternion q; // [w, x, y, z] quaternion container</p><p>VectorFloat gravity; // [x, y, z] gravity vector</p><p>float rotation[3]; // [yaw, pitch, roll] yaw/pitch/roll container and gravity vector int safe_lock = 1;</p><p>volatile bool mpuInterrupt = false; // indicates whether MPU interrupt pin has gone high void dmpDataReady() { mpuInterrupt = true; }</p><p>void setup() { // join I2C bus (I2Cdev library doesn't do this automatically) #if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE Wire.begin(); Wire.setClock(400000); // 400kHz I2C clock. Comment this line if having compilation difficulties #elif I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE Fastwire::setup(400, true); #endif</p><p>printf_begin();</p><p> Serial.begin(115200);</p><p> Serial.println("Initializing I2C devices..."); mpu.initialize(); pinMode(INTERRUPT_PIN, INPUT);</p><p> // verify connection Serial.println("Testing device connections..."); Serial.println(mpu.testConnection() ? "MPU6050 connection successful" : "MPU6050 connection failed"); // bmp.begin(); radio.begin(); delay(1000); radio.setDataRate(RF24_250KBPS); radio.setPALevel(RF24_PA_MAX);</p><p> radio.openReadingPipe(1,pipe); radio.startListening(); </p><p> // load and configure the DMP Serial.println("Initializing DMP..."); devStatus = mpu.dmpInitialize();</p><p> // gyro offsets here mpu.setXGyroOffset(87); mpu.setYGyroOffset(77); mpu.setZGyroOffset(110); mpu.setZAccelOffset(2287); mpu.setYAccelOffset(-1283); mpu.setXAccelOffset(-3083);</p><p> // make sure it worked (returns 0 if so) if (devStatus == 0) {</p><p> Serial.println("Enabling DMP..."); mpu.setDMPEnabled(true);</p><p> attachInterrupt(digitalPinToInterrupt(INTERRUPT_PIN), dmpDataReady, RISING); mpuIntStatus = mpu.getIntStatus();</p><p> dmpReady = true;</p><p> packetSize = mpu.dmpGetFIFOPacketSize(); } else { // ERROR! // 1 = initial memory load failed // 2 = DMP configuration updates failed // (if it's going to break, usually the code will be 1) Serial.print("DMP Initialization failed (code "); Serial.print(devStatus); Serial.println(")"); }</p><p> motor1.attach(5); motor2.attach(8); motor3.attach(7); motor4.attach(4); pinMode(A0, INPUT); pinMode(A1, INPUT); digitalWrite(A0, LOW); motor1.write(0); motor2.write(0); motor3.write(0); motor4.write(0);</p><p>radio.printDetails(); }</p><p>void loop() {</p><p> if (radio.available()) { bool done = false; while (!done){ done = radio.read(data, sizeof(data));</p><p> // Serial.print("Controll number: "); //Serial.println(data[0]); if((millis()-last_received) < 3000){ if(data[0] == controll_number){ Serial.print("DATA1: "); Serial.println(data[1]); allmotors_power = map(data[1], 0, 255, 800, 1500); if(allmotors_power < 0){ allmotors_power = 0; }</p><p> //allmotors_power = map(data[1], 0, 255, 800, 1600); x_setpoint = data[3] - 20; y_setpoint = data[2] - 20; Serial.print("PID OUT X: "); Serial.print(x_rotation); Serial.print(" PID OUT Y: "); Serial.print(y_rotation); Serial.print("Z NOW: "); Serial.println(z_rotation); Serial.print(" TIME: "); Serial.println(millis());</p><p>Serial.print("MOTORS POWER: "); Serial.println(allmotors_power); } } // Serial.println(data[1]); if(done == true){ last_received = millis(); } } }</p><p> if((millis()-last_received) > 3000 && allmotors_power > 0){ safe_lock = 0; } // if programming failed, don't try to do anything // if (!dmpReady) return;</p><p> while (!mpuInterrupt && fifoCount < packetSize) { }</p><p> // reset interrupt flag and get INT_STATUS byte mpuInterrupt = false; mpuIntStatus = mpu.getIntStatus();</p><p> // get current FIFO count fifoCount = mpu.getFIFOCount();</p><p> // check for overflow (this should never happen unless our code is too inefficient) if ((mpuIntStatus & 0x10) || fifoCount == 1024) { </p><p> // otherwise, check for DMP data ready interrupt (this should happen frequently) } else if (mpuIntStatus & 0x02) { // wait for correct available data length, should be a VERY short wait while (fifoCount < packetSize) fifoCount = mpu.getFIFOCount();</p><p> // read a packet from FIFO mpu.getFIFOBytes(fifoBuffer, packetSize); // track FIFO count here in case there is > 1 packet available // (this lets us immediately read more without waiting for an interrupt) fifoCount -= packetSize;</p><p> if(safe_lock == 1){ mpu.dmpGetQuaternion(&q, fifoBuffer); mpu.dmpGetGravity(&gravity, &q); mpu.dmpGetYawPitchRoll(rotation, &q, &gravity);</p><p> x_rotation = rotation[1] * 180/M_PI - x_level_error; y_rotation = rotation[2] * 180/M_PI - y_level_error; z_rotation = rotation[0] * 180/M_PI;</p><p>/* if(pressure_loop_number == 10){ // Serial.print("Preasure: "); //Serial.println(bmp.readAltitude()); pressure_loop_number = 0; allmotors_power = 1000; } pressure_loop_number++; */</p><p> if(first_loop == true){ z_setpoint = z_rotation; // current_altitude = bmp.readAltitude(); //set_altitude = current_altitude; first_loop = false; } motor1_power = allmotors_power; motor2_power = allmotors_power; motor3_power = allmotors_power; motor4_power = allmotors_power; if(allmotors_power > 1500){ allmotors_power = 1500; }</p><p> x_output = calculatePID(0, x_rotation); y_output = calculatePID(1, y_rotation); z_output = calculatePID(2, z_rotation);</p><p> motor1_power += x_output/2; motor1_power += z_output; motor4_power -= x_output/2; motor4_power += z_output;</p><p> motor2_power -= y_output/2; motor2_power -= z_output; motor3_power += y_output/2; motor3_power -= z_output; motor1.writeMicroseconds(motor1_power); motor4.writeMicroseconds(motor4_power); motor2.writeMicroseconds(motor2_power); motor3.writeMicroseconds(motor3_power); mpu.resetFIFO(); }else{ motor1.write(0); motor2.write(0); motor3.write(0); motor4.write(0); } } }</p><p> float calculatePID(int _axis, float _angel){</p><p> // X AXIS if(_axis == 0){ x_now = millis(); x_timechange = x_now - x_lasttime; x_error = x_setpoint - _angel; x_p_out = (x_kp * x_error); x_errorsum = (x_errorsum + x_error); if(x_errorsum > 1023){ x_errorsum = 1023; } if(x_errorsum < -1023){ x_errorsum = -1023; } x_i_out = x_ki * x_errorsum; x_d_error = (x_error - x_lasterror) / x_timechange; x_d_out = x_kd * x_d_error; x_lasterror = x_error; x_output = x_p_out + x_i_out + x_d_out; if(x_output > max_pid){ x_output = max_pid; }else if(x_output < -(max_pid)){ x_output = -(max_pid); } x_lasttime = millis(); return x_output; }</p><p> // Y AXIS else if(_axis == 1){ y_now = millis(); y_timechange = y_now - y_lasttime; y_error = y_setpoint - _angel; y_p_out = (y_kp * y_error);</p><p> y_errorsum = (y_errorsum + y_error) * y_timechange; if(y_errorsum > 1023){ y_errorsum = 1023; } if(y_errorsum < -1023){ y_errorsum = -1023; } y_i_out = y_ki * y_errorsum; y_d_error = (y_error - y_lasterror) / y_timechange; y_d_out = y_kd * y_d_error; y_lasterror = y_error; y_output = y_p_out + y_i_out + y_d_out; if(y_output > max_pid){ y_output = max_pid; }else if(y_output < -(max_pid)){ y_output = -(max_pid); } y_lasttime = millis(); return y_output;</p><p> // ALTITUDE // } else if(_axis == 2){ // return (set_altitude - current_altitude) * 20; }else if(_axis == 2){ z_now = millis(); z_timechange = z_now - z_lasttime; z_error = z_setpoint - _angel; z_p_out = (z_kp * z_error);</p><p> z_errorsum = (z_errorsum + z_error) * z_timechange; if(z_errorsum > 1023){ z_errorsum = 1023; } if(z_errorsum < -1023){ z_errorsum = -1023; } z_i_out = z_ki * z_errorsum; z_d_error = (z_error - z_lasterror) / z_timechange; z_d_out = z_kd * y_d_error; z_lasterror = y_error; z_output = z_p_out + z_i_out + z_d_out; if(z_output > max_pid){ z_output = max_pid; }else if(z_output < -(max_pid)){ z_output = -(max_pid); } z_lasttime = millis(); return z_output;</p><p> // ALTITUDE // } else if(_axis == 2){ // return (set_altitude - current_altitude) * 20; }else{ return 0; }</p><p> }</p>
<p>/*</p><p> * * * JUNE 2016 - APRIL 2017 * C by Nikodem Bartnik * nikodem.bartnik@gmail.com * nikodembartnik.pl * * * */</p><p>#include <spi.h><br>#include "RF24.h"</spi.h></p><p>#define MAX_TILT 20</p><p>RF24 radio(9,10);</p><p>int a = 0; uint8_t data[6]; int controll_number = 159; int safe_lock = 0; int x_offset, y_offset;</p><p>const uint64_t pipe = 0xE8E8F0F0E1LL;</p><p>void setup(void){ Serial.begin(57600);</p><p> pinMode(4, OUTPUT); pinMode(3, INPUT); pinMode(A0, INPUT); pinMode(A1, INPUT); pinMode(A2, INPUT); pinMode(A3, INPUT); digitalWrite(3, HIGH); digitalWrite(4, HIGH);</p><p> radio.begin();</p><p>radio.setDataRate(RF24_250KBPS); radio.setPALevel(RF24_PA_MAX);</p><p> radio.openWritingPipe(pipe); radio.printDetails();</p><p>}</p><p>void loop(void) { if(!digitalRead(3)){ Serial.print("LOW 1"); delay(1000); if(!digitalRead(3)){ Serial.print("LOW 2"); delay(1000); if(!digitalRead(3)){ Serial.print("LOW 3"); if(safe_lock == 0){ safe_lock = 1; }else{ safe_lock = 0; } } } }</p><p>int power = map(analogRead(A2), 0, 1023, 0, 255); int x = map(analogRead(A1), 0, 1023, 0, 255); int y = map(analogRead(A0), 0, 1023, 0, 255); int rotation = map(analogRead(A3), 0, 1023, 0, 255);</p><p>if(x > 150){ x = map(x, 150, 255, 0, MAX_TILT); }else if(x < 105){ x = map(x, 105, 0, 0, -MAX_TILT); }else{ x = 0; }</p><p>if(y > 150){ y = map(y, 150, 255, 0, MAX_TILT); }else if(y < 105){ y = map(y, 105, 0, 0, -MAX_TILT); }else{ y = 0; }</p><p>if(rotation > 150){ rotation = map(rotation, 150, 255, 0, MAX_TILT); }else if(rotation < 105){ rotation = map(rotation, 105, 0, 0, -MAX_TILT); }else{ rotation = 0; }</p><p> data[0] = controll_number; data[1] = power;</p><p> // + 10 because you can't send negative number data[2] = x + MAX_TILT; data[3] = y + MAX_TILT; data[4] = rotation + MAX_TILT; data[5] = safe_lock;</p><p>radio.write( data, sizeof(data) );</p><p> delay(8); }</p>
ludwik_drone_flight_controller.zip
Bước 7: Bộ điều khiển bay PCB (và chia điện)
Ngay tại đó bạn có thể tìm thấy tất cả các tệp bao gồm .brd, .sch và .pdf để in. Và một số hình ảnh về cách tôi thực hiện chúng. Đường đi dây rất nhỏ nên bảng không dễ thực hiện. Không có nhiều thứ để viết nên chỉ cần thưởng thức ảnh và tập tin 🙂
Cách làm PCB
Tôi cũng đã tạo một bảng điện và nó được sử dụng trên phiên bản in 3D của máy bay không người lái của tôi, nhưng hiện tại tôi không sử dụng nó vì khung mà tôi đã mua có bảng phân phối điện tích hợp vào khung.
Tài liệu đính kèm
Bước 8: Điều khiển
Câu chuyện nhanh về frame. Phiên bản đầu tiên của nó là chiết áp trên một chiếc breadboard như tôi đã đề cập trước đó. Nó được kết nối với máy bay không người lái bằng dây cáp dài 5 mét. Sau đó, tôi xây dựng mạch không dây thực sự trên Breadboard với arduino pro mini, hai remote điều khiển và pin. Công việc tuyệt vời nhưng sự lộn xộn với dây cáp này rất tệ. Vì vậy, trong khi chờ đợi khung hình của mình, tôi quyết định thiết kế và tạo PCB cho nó. Và đó là phiên bản cuối cùng, kích thước tối đa của PCB trong phiên bản ổn để làm cho nó tiện dụng hơn. Trông khá tốt, hoạt động hoàn hảo. Tôi đã thêm ngay vào đó 2 công tắc để chuyển đổi đèn cuối cùng hoặc thậm chí có thể là một loại công cụ ném 🙂 Sơ đồ, PCB và chương trình cho nó mà bạn có thể tìm thấy trong các bước trước.
Tài liệu đính kèm
Bước 9: Khung in 3D không thành công
Đây là khung in 3D cho máy bay không người lái của tôi, thật tệ, đừng in nó. Tôi chỉ đặt nó ở ngay đó để cho bạn thấy tôi đã tạo ra nó như thế nào, nó trông như thế nào và bạn không nên thiết kế một khung cho máy bay không người lái. Thời gian in tất cả các bộ phận cho khung này là khoảng 13 giờ. Tôi đã phá vỡ 7 phần của nó và sau đó tôi quyết định từ bỏ tại thời điểm này với khung này và mua một khung hoàn thiện. Tôi thích tập trung vào chương trình của mình và sau đó khi tôi hoàn thành nó, tôi sẽ thiết kế khung mới hoàn hảo, nó sẽ mạnh hơn và nhỏ hơn (cái này thực sự hơi to một chút).
Để thiết kế tất cả những phần đó tôi đã sử dụng fusion360 theo ý kiến của tôi phần mềm thiết kế 3D tốt nhất. Bạn có thể kiểm tra nó ở đây.
Tôi cũng đã làm hòng 2 pin Li-Po 🙁
Tài liệu đính kèm
Bước 10: Một số bản in 3D hữu ích
Bởi vì một số phần được in 3D rất hữu ích, tôi đặt các tệp .stl ở đây nếu bạn muốn in chúng ra. Chỉ có giá đỡ antena cho bộ điều khiển máy bay (bạn cần khoan lỗ mới trong PCB) và giá đỡ PCB cho bộ điều khiển máy bay. Tôi đặt một miếng xốp nhỏ giống như bên dưới giá đỡ này để hạn chế rung và siết nó xuống bằng vít M3.
Tài liệu đính kèm
Bước 11: Lắp ráp
Có một số hình ảnh và giải thích nhanh về cách lắp ráp tất cả các bộ phận của drone. Trước hết bạn phải lắp ráp khung, có 4 cánh tay và 2 tấm chính rất đơn giản. Sau đó, bạn có thể gắn động cơ không chổi than của bạn trên khung. Sử dụng 4 ốc vít ngắn để gắn chúng trên cánh tay (ốc vít không thể quá dài vì chúng có thể làm hỏng colis của động cơ). Sau đó, bạn có thể hàn ESC cho động cơ. Hãy nhớ rằng động cơ phía trước bên trái và phía sau bên phải phải được hàn theo cùng một cách. Phải trước và sau trái phải được kết nối theo cùng một cách nhưng bạn phải hoán đổi 2 chiều của động cơ. Tôi gắn ESC vào khung bằng dây kéo. Hãy nhớ buộc chặt cánh quạt, sẽ rất hữu ích khi sử dụng tuốc nơ vít nhỏ cho nó.
Bước 12: Kiểm tra và nâng cấp
Tôi đã dành hầu hết thời gian để cải tiến nó. Đó là thay đổi code vô tận, thử nghiệm, thử nghiệm thay đổi code, v.v. Ở trên bạn có thể thấy một số hình ảnh của nó. Một lần khi tôi kiểm tra độ ổn định và tôi đã thêm đoạn code để giảm tốc liên tục để làm cho nó hạ cánh nhẹ nhàng sau khi ngắt kết nối radio (ngay bây giờ tôi biết rằng không thể không có GPS). Tôi đã quên thêm vào code một số cấp độ để không để số lượng hoàn toàn giảm xuống. Và điều gì xảy ra khi số lượng giảm dần? Tại một số thời điểm để đi từ điểm trừ tối đa đến điểm cộng tối đa và sau đó máy bay không người lái của tôi đã biến động cơ của chúng không ổn, nó đi lên với khung này đập vào cửa và tường của tôi và rồi rơi xuống. Tôi đã ở phía đối lập. Nó rất gần để va vào tôi. Và đó là lý do tôi đội mũ bảo hiểm trượt tuyết trong các bài kiểm tra còn lại 🙂
Tài liệu đính kèm
Bước 13: Máy bay thực sự thành công đầu tiên
Có rất nhiều vấn đề trong quá trình xây dựng này, một số trong số chúng là do lỗi của tôi. Một lần tôi quên thêm dấu trừ vào trục y và máy bay không người lái của tôi thay vì tự ổn định, lại làm điều ngược lại. Nhưng sau rất nhiều rắc rối, tôi đã đạt được máy bay thành công đầu tiên, 10 máy bay hoàn hảo 🙂 chỉ cần lên xuống nhưng nó ổn định và đó là điều tôi muốn có. Ở trên, bạn có thể xem video từ một trong những máy bay đầu tiên (có khung in 3D) và đây là lần đầu tiên tôi nghĩ rằng có thể nó sẽ hoàn thành công việc. Và rồi mùa đông đến, tuyết rơi, trời lạnh và ẩm ướt. Tôi dành phần lớn thời gian ở nhà để làm những việc khác ngoài drone. Tôi đã trượt tuyết và drone đang chờ thời tiết tốt hơn. Vào tháng 2 năm 2017 tôi bắt đầu làm việc với nó một lần nữa, tôi đã thay đổi một chương trình một chút và làm thêm bài kiểm tra. Nó tốt hơn và tốt hơn mỗi bài kiểm tra. Và sau một thời gian tôi đã đạt được điều này những gì bạn có thể thấy ngay bây giờ. Nó không hoàn hảo nhưng nó rất tốt và điều quan trọng nhất là nó là của tôi! Vẫn còn nhiều việc phải làm để làm cho nó tốt hơn, điều quan trọng nhất là hiệu chỉnh PID cho góc xoay và cao độ nhưng không có cách nào để tôi làm điều đó ngay bây giờ, tôi không có máy tính xách tay, nhưng tôi cần một cái để ra ngoài với nó và với drone và hiệu chỉnh nó bằng cách thay đổi các hằng số PID một chút, tải lên chương trình, kiểm tra và lặp lại miễn là nó sẽ di chuột hoàn hảo. Bởi vì tôi không có máy tính xách tay nên tôi không thể làm điều này 🙁
Bước 14: Phiên bản cuối cùng (0.1)
Vì vậy, như tôi đã nói đây là phiên bản cuối cùng. Bây giờ tôi sẽ hoàn thành bản dựng này (Điều cuối cùng cần làm là hiệu chỉnh PID).