ربات مورچهای ۶ پا با دوربین زنده و ESP32
رباتهایی که از طبیعت الهام میگیرن همیشه جذابیت خاص خودشون رو دارن و این بار تو آیسیمدار قراره بریم سراغ یه پروژه خیلی هیجانانگیز. میخوایم یه ربات مورچهای بسازیم که با شش تا پای مکانیکی حرکت میکنه و به لطف دوربینی که روی سرش داره، کل مسیر رو به صورت زنده روی صفحه گوشیت پخش میکنه. این مورچه هوشمند هم میتونه از راه دور کنترل بشه و هم قابلیت حرکت خودکار و فرار از موانع رو داره. قلب تپنده این پروژه، برد قدرتمند ESP32-S3 هست که هم کدهای حرکتی رو پردازش میکنه و هم ارتباط وایفای رو برای دریافت تصویر و ارسال فرمانها میسازه. پاهای این ربات طوری طراحی شدن که فقط چهار تا پای جلو و عقب حرکت اصلی رو انجام میدن و پاهای وسطی فقط برای حفظ تعادل و انتقال اصطکاک به زمین در نظر گرفته شدن؛ این کار باعث میشه تو تعداد موتورها و مصرف باتری کلی صرفهجویی بشه. برای ساخت بدنه این ربات به پرینتر سهبعدی نیاز داری و کنارش چند تا قطعه الکترونیکی ساده مثل سروو موتور و ماژول کاهنده ولتاژ کارمون رو راه میاندازه. اگه پایهای تا این حشره مکانیکی و بامزه رو روی میز کارت زنده کنی، ابزارها رو دم دستت بذار تا بریم سراغ ساختش.
فایلهای پرینت سهبعدی بدنه ربات
برای ساخت بدنه به این قطعات که باید پرینت سهبعدی بشن نیاز داری:
- صفحه اصلی بدنه (1 عدد)
- پاهای جلو و عقب (4 عدد)
- صفحههای بزرگ (4 عدد)
- صفحههای کوچک (4 عدد)
- پایه نگهدارنده سروو موتور جلو و عقب (4 عدد)
- پای میانی چپ (1 عدد)
- پای میانی راست (1 عدد)
- پایه میانی چپ (1 عدد)
- پایه میانی راست (1 عدد)
- پایه نگهدارنده سروو میانی (2 عدد)
- شاخک سنسور سر چپ (1 عدد)
- شاخک سنسور سر راست (1 عدد)
تنظیم زاویه و کالیبره کردن سروو موتورها
اول از همه باید همه سروو موتورها رو روی زاویه 90 درجه تنظیم کنی تا دقیقا تو مرکز بازه حرکتی 0 تا 180 درجهشون قرار بگیرن. برای این کار میتونی خیلی راحت از یه ماژول تستر سروو استفاده کنی. البته با یه برد آردوینو و چند خط کد ساده هم میتونی موتورها رو تو این موقعیت قفل کنی و کارتو راه بندازی.
جا زدن سروو موتورهای وسط بدنه
حالا دو تا سروو موتور میانی رو داخل صفحه اصلی بدنه جا بزن. برای محکم کردنشون میتونی از یه تیکه سیم ضخیم 2 میلیمتری به عنوان پین استفاده کنی و با چسب قطرهای، موتور و پین رو سر جاشون ثابت نگه داری.
تو قدم بعدی، قطعات مربوط به پاهای میانی و پایههاشون رو به هم وصل کن و با چسب حسابی محکمشون کن. بازوی پلاستیکی سروو موتور رو روش قرار بده، پایه رو وصل کن و در نهایت با پیچی که همراه خود موتور هست، همهچیز رو سفت کن. برای اطمینان بیشتر، میتونی یه سوراخ خیلی ریز روی پایه و بازوی موتور دریل کنی و یه پیچ کوچیک هم اونجا ببندی تا موقع راه رفتن ربات از هم جدا نشن.
نصب سروو موتورهای پاهای جلو و عقب
حالا بریم سراغ موتورهای اصلی حرکت. دو تا سروو موتور برای جلو و دو تا سروو موتور برای عقب رو دقیقا مثل عکسهایی که میبینی، داخل صفحه اصلی بدنه جا بزن. این بار برای محکم کردنشون از پیچهای خود سرووها استفاده کن. بعد از اینکه موتورها سفت شدن، بازوی پلاستیکی هر کدوم رو با زاویه 90 درجه نسبت به بدنه، روی شفت موتور قرار بده.
سوار کردن پاهای اصلی مورچه
یکی از صفحههای بزرگ رو به بازوی سروو وصل کن و با پیچ محکم ببند. حالا پایههای نگهدارنده سروو رو به قسمت پایینی موتورها بچسبون. وقتشه که پاهای پرینتشده رو روی صفحههای بزرگ و کوچیک سوار کنی؛ دقیقا همونطوری که تو عکسها میبینی.
برای اینکه قطعات موقع راه رفتن ربات از هم نپاشن، صفحهها و پاها رو با چسب قطرهای حسابی به هم بچسبون. فقط خیلی دقت کن که صفحه کوچیک و پایه نگهدارنده به هیچ وجه نباید به هم بچسبن تا پاها بتونن راحت مفصل حرکتی داشته باشن.
سیمکشی و رسوندن برق به قطعات
برای راه انداختن این ربات به یه باتری لیتیومی نیاز داریم که ولتاژی در حدود 7.7 ولت بهمون میده. اما چون سروو موتورها و برد ESP32 با ولتاژ 5 ولت کار میکنن، باید از یه ماژول کاهنده ولتاژ برای تبدیل این انرژی استفاده کنی. ما برای مرتب شدن کار، از یه تیکه برد سوراخدار و چند تا پینهدر برای پخش کردن برق بین قطعات استفاده کردیم.
برای شروع سیمکشی، سیم دیتای موتورها (که معمولا رنگش نارنجیه) رو از سیمهای برق جدا کن. این سیمها بعدا قراره به برد اصلی که داخل سر مورچه قرار میگیره وصل بشن و معمولا طولشون برای رسیدن به اونجا کاملا کافیه. حالا پینهای تغذیه موتورها رو به همراه خروجی باتری، روی همون برد کوچیکی که ساختی لحیم کن تا بخش انتهایی بدنه حسابی مرتب بشه.
حتما یه کلید قطع و وصل هم سر راه باتری قرار بده تا هر بار مجبور نباشی کانکتور باتری رو با دردسر جدا کنی. جایگاه نصب این کلید رو تو قدم بعدی میبینی.
بستن دم مورچه و جادادن باتری
قطعه پرینتشده مربوط به دم مورچه رو بردار و با یه پیچ به قسمت میانی بدنه وصلش کن. این دم توخالی طراحی شده و فضای خیلی خوبی برای جا دادن باتری، مدار توزیع برق و سیمهای اضافه داره. اگه تا الان اتصالات تغذیه موتورها و باتری رو کامل نکردی، الان بهترین فرصته که مدارت رو ببندی و کلید روشن و خاموش رو هم تو یه جای مناسب روی بدنه جاگذاری کنی.
نصب سر مورچه و سنسورهای تشخیص مانع
سر ربات رو هم با یه پیچ سر جاش محکم کن. داخل این قسمت قراره مغز متفکر پروژه یعنی برد ESP32-S3 قرار بگیره. اگه میخوای رباتت فقط از راه دور کنترل بشه، میتونی قید سنسورها رو بزنی و مستقیم بری سراغ قدم بعدی.
اما برای اینکه ربات بتونه خودکار حرکت کنه، همونطوری که تو تصویر میبینی، چند تیکه سیم به پایههای میکرو سوییچها لحیم کن. میتونی از یه سیم مشترک برای پین GND هر دو سوییچ استفاده کنی. حالا سوییچها رو داخل بخش بالایی سر قرار بده و با کمی چسب حرارتی سر جاشون ثابت کن. وقتی قاب سر رو بستی، شاخکهای سنسور رو به اهرم میکرو سوییچها وصل کن تا وقتی به موانع برخورد میکنن، سوییچ فشرده بشه.
آماده کردن آردوینو برای پروگرام کردن ESP32
اگه موقع کامپایل با ارور مواجه شدی، این کارها رو انجام بده: اول اینکه تو تنظیمات آردوینو و بخش مدیریت بردها، علاوه بر لینک اصلی پکیج ESP32، حتما باید لینک نسخه توسعهدهنده (Dev) رو هم وارد کنی تا فایلهای کاملتری دانلود بشه.
نکته دوم و خیلی مهم اینه که حتما از نسخه 2 پکیج بردهای ESP32 استفاده کنی. نسخههای جدیدتر مثل نسخه 3 معمولا با کتابخونههای استاندارد سروو موتور تداخل دارن و خطای کامپایل میدن. پس اگه نرمافزار پیشنهاد داد که برد رو آپدیت کنی، اصلا قبول نکن تا مشکلی تو عملکرد کتابخونه سروو پیش نیاد.
آپلود کردن کدهای حرکت ربات
همونطوری که قبلا گفتیم، پاهای جلو و عقب عامل اصلی حرکت ربات هستن و پاهای وسط فقط برای حفظ تعادل و انتقال نیرو در نظر گرفته شدن. این طراحی شاید دقیقا شبیه حشرات واقعی نباشه، اما باعث میشه ربات بتونه با موتورهای کمتر و مصرف انرژی پایینتر راه بره.
برنامهای که برای این مورچه نوشته شده، از دو بخش اصلی تشکیل شده: یکی برای استریم و ارسال زنده ویدیو، و اون یکی برای دریافت فرمانهای حرکتی ریموت کنترل. کدهای مربوط به راه رفتن موتورها به صورت توابع جداگانه نوشته شدن تا با زدن دکمه، همون تابع اجرا بشه. اول فایل کد رو تو محیط آردوینو باز کن.
برای آپلود برنامه روی برد، دکمه کوچیک Boot روی ESP32 رو نگه دار و همزمان کابل USB رو به کامپیوتر وصل کن. بعد روی دکمه آپلود کلیک کن و وقتی فرآیند نوشتن شروع شد، میتونی دکمه Boot رو رها کنی.
/*
* ESP32S3 XIAO * ESP32S3 XIAO Red Ant Remote Control - Home Web
* make sure that PSRAM is enabled
*
* Markus Opitz 2024 instructables.com
*/
// servos * * * * * * * * * * * * * * * * * * * *
#include <s3servo.h>
s3servo servo1;
s3servo servo2;
s3servo servo3;
s3servo servo4;
s3servo servo5;
s3servo servo6;
int center1 = 75; //best center position of each leg (individual)
//int center2 = 105;
int center2 = 125;
int center3 = 90;
int center4 = 90;
int center5 = 115;
int center6 = 75;
int pos = 90;
int pos2;
int Min = 50; //move betwenn this two values
int Max = 100;
int speed1 = 200; //pause between forward and backward movement
int steplength = 30; //big step
int smallstep = 5; //small step
int flag = 0;
// button * * * * * * * * * * * * * * * * * * * *
const int buttonLeft = 3;
const int buttonRight = 4;
int leftState = 1;
int rightState = 1;
// camera * * * * * * * * * * * * * * * * * * * *
#include "esp_camera.h"
#include <WiFi.h>
#include "esp_timer.h"
#include "img_converters.h"
#include "Arduino.h"
#include "fb_gfx.h"
#include "soc/soc.h" // disable brownout problems
#include "soc/rtc_cntl_reg.h" // disable brownout problems
#include "esp_http_server.h"
#include "esp_http_server.h"
#include "esp32-hal-ledc.h"
#include "sdkconfig.h"
#define CAMERA_MODEL_XIAO_ESP32S3 // Has PSRAM
#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG)
#include "esp32-hal-log.h"
#endif
// Replace with your network data * * * * * * * * * * * * * * * * * * * * *
const char* ssid = "********";
const char* password = "********";
#define PART_BOUNDARY "123456789000000000000987654321"
#define CAMERA_MODEL_AI_THINKER
//#define CAMERA_MODEL_WROVER_KIT
#if defined(CAMERA_MODEL_XIAO_ESP32S3)
#define PWDN_GPIO_NUM -1
#define RESET_GPIO_NUM -1
#define XCLK_GPIO_NUM 10
#define SIOD_GPIO_NUM 40
#define SIOC_GPIO_NUM 39
#define Y9_GPIO_NUM 48
#define Y8_GPIO_NUM 11
#define Y7_GPIO_NUM 12
#define Y6_GPIO_NUM 14
#define Y5_GPIO_NUM 16
#define Y4_GPIO_NUM 18
#define Y3_GPIO_NUM 17
#define Y2_GPIO_NUM 15
#define VSYNC_GPIO_NUM 38
#define HREF_GPIO_NUM 47
#define PCLK_GPIO_NUM 13
#define LED_GPIO_NUM 21
#else
#error "Camera model not selected"
#endif
// web page for remote control and screen * * * * * * * * * * * * *
static const char* _STREAM_CONTENT_TYPE = "multipart/x-mixed-replace;boundary=" PART_BOUNDARY;
static const char* _STREAM_BOUNDARY = "\r\n--" PART_BOUNDARY "\r\n";
static const char* _STREAM_PART = "Content-Type: image/jpeg\r\nContent-Length: %u\r\n\r\n";
httpd_handle_t camera_httpd = NULL;
httpd_handle_t stream_httpd = NULL;
static const char PROGMEM INDEX_HTML[] = R"rawliteral(
<html>
<head>
<title>Red Ant</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
body { font-family: Arial; text-align: center; margin:0px auto; padding-top: 30px; background-color: #010101; color: orange;}
table { margin-left: auto; margin-right: auto; }
td { padding: 8 px; }
.button1 {
background-color: #f93e16;
border: 1;
color: white;
padding: 10px 20px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 32px;
margin: 6px 3px;
cursor: pointer;
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
-webkit-tap-highlight-color: rgba(0,0,0,0);
}
.button2 {
background-color: #4EB232;
border: 1;
color: white;
padding: 10px 20px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 18px;
margin: 6px 3px;
cursor: pointer;
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
-webkit-tap-highlight-color: rgba(0,0,0,0);
}
img { width: auto ;
max-width: 100% ;
height: auto ;
}
</style>
</head>
<body>
<h1>Red Ant</h1>
<img src="" id="photo">
<table>
<tr>
<td align="center"><button1 class="button1" onmousedown="toggleCheckbox('left');" ontouchstart="toggleCheckbox('left');" onmouseup="toggleCheckbox('stop');" ontouchend="toggleCheckbox('stop');">↖</button1></td>
<td align="center"><button1 class="button1" onmousedown="toggleCheckbox('forward');" ontouchstart="toggleCheckbox('forward');" onmouseup="toggleCheckbox('stop');" ontouchend="toggleCheckbox('stop');"> ↑ ↑ </button1></td>
<td align="center"><button1 class="button1" onmousedown="toggleCheckbox('right');" ontouchstart="toggleCheckbox('right');" onmouseup="toggleCheckbox('stop');" ontouchend="toggleCheckbox('stop');">↗</button1></td>
</tr>
<tr><td align="center"><button1 class="button1" onmousedown="toggleCheckbox('turnleft');" ontouchstart="toggleCheckbox('turnleft');" onmouseup="toggleCheckbox('stop');" ontouchend="toggleCheckbox('stop');">↶</button1></td>
<td align="center"><button1 class="button1" onmousedown="toggleCheckbox('rise');" ontouchstart="toggleCheckbox('rise');" onmouseup="toggleCheckbox('stop');" ontouchend="toggleCheckbox('stop');">⟰</button1></td>/
<td align="center"><button1 class="button1" onmousedown="toggleCheckbox('turnright');" ontouchstart="toggleCheckbox('turnright');" onmouseup="toggleCheckbox('stop');" ontouchend="toggleCheckbox('stop');">↷</button1></td></tr>
<tr><td colspan="3" align="center"><button1 class="button1" onmousedown="toggleCheckbox('backward');" ontouchstart="toggleCheckbox('backward');" onmouseup="toggleCheckbox('stop');" ontouchend="toggleCheckbox('stop');">↓</button1></td></tr>
<tr><td colspan="3" align="center"><button2 class="button2" onmousedown="toggleCheckbox('automode');" ontouchstart="toggleCheckbox('automode');" onmouseup="toggleCheckbox('stop');" ontouchend="toggleCheckbox('stop');">Auto Mode</button1></td></tr>
</table>
<script>
function toggleCheckbox(x) {
var xhr = new XMLHttpRequest();
xhr.open("GET", "/action?go=" + x, true);
xhr.send();
}
window.onload = document.getElementById("photo").src = window.location.href.slice(0, -1) + ":81/stream";
</script>
</body>
</html>
)rawliteral";
static esp_err_t index_handler(httpd_req_t *req){
httpd_resp_set_type(req, "text/html");
return httpd_resp_send(req, (const char *)INDEX_HTML, strlen(INDEX_HTML));
}
static esp_err_t stream_handler(httpd_req_t *req){
camera_fb_t * fb = NULL;
esp_err_t res = ESP_OK;
size_t _jpg_buf_len = 0;
uint8_t * _jpg_buf = NULL;
char * part_buf[64];
res = httpd_resp_set_type(req, _STREAM_CONTENT_TYPE);
if(res != ESP_OK){
return res;
}
while(true){
fb = esp_camera_fb_get();
if (!fb) {
Serial.println("Camera capture failed");
res = ESP_FAIL;
} else {
if(fb->width > 400){
if(fb->format != PIXFORMAT_JPEG){
bool jpeg_converted = frame2jpg(fb, 80, &_jpg_buf, &_jpg_buf_len);
esp_camera_fb_return(fb);
fb = NULL;
if(!jpeg_converted){
Serial.println("JPEG compression failed");
res = ESP_FAIL;
}
} else {
_jpg_buf_len = fb->len;
_jpg_buf = fb->buf;
}
}
}
if(res == ESP_OK){
size_t hlen = snprintf((char *)part_buf, 64, _STREAM_PART, _jpg_buf_len);
res = httpd_resp_send_chunk(req, (const char *)part_buf, hlen);
}
if(res == ESP_OK){
res = httpd_resp_send_chunk(req, (const char *)_jpg_buf, _jpg_buf_len);
}
if(res == ESP_OK){
res = httpd_resp_send_chunk(req, _STREAM_BOUNDARY, strlen(_STREAM_BOUNDARY));
}
if(fb){
esp_camera_fb_return(fb);
fb = NULL;
_jpg_buf = NULL;
} else if(_jpg_buf){
free(_jpg_buf);
_jpg_buf = NULL;
}
if(res != ESP_OK){
break;
}
//Serial.printf("MJPG: %uB\n",(uint32_t)(_jpg_buf_len));
}
return res;
}
static esp_err_t cmd_handler(httpd_req_t *req){
char* buf;
size_t buf_len;
char variable[32] = {0,};
buf_len = httpd_req_get_url_query_len(req) + 1;
if (buf_len > 1) {
buf = (char*)malloc(buf_len);
if(!buf){
httpd_resp_send_500(req);
return ESP_FAIL;
}
if (httpd_req_get_url_query_str(req, buf, buf_len) == ESP_OK) {
if (httpd_query_key_value(buf, "go", variable, sizeof(variable)) == ESP_OK) {
} else {
free(buf);
httpd_resp_send_404(req);
return ESP_FAIL;
}
} else {
free(buf);
httpd_resp_send_404(req);
return ESP_FAIL;
}
free(buf);
} else {
httpd_resp_send_404(req);
return ESP_FAIL;
}
sensor_t * s = esp_camera_sensor_get();
int res = 0;
if(!strcmp(variable, "forward")) {
Serial.println("Forward");
forward();
}
else if(!strcmp(variable, "left")) {
Serial.println("Left");
left();
}
else if(!strcmp(variable, "right")) {
Serial.println("Right");
right();
}
else if(!strcmp(variable, "backward")) {
Serial.println("Backward");
backward();
}
else if(!strcmp(variable, "turnleft")) {
Serial.println("turn left");
}
else if(!strcmp(variable, "turnright")) {
Serial.println("turn right");
}
else if(!strcmp(variable, "rise")) {
Serial.println("rise");
rise();
}
else if(!strcmp(variable, "stop")) {
Serial.println("stop");
stop();
}
else if(!strcmp(variable, "automode")) {
Serial.println("auto modus");
if (flag = 0) flag =1;
if (flag = 1) flag =0;
//automode
}
else { //stop
servo1.write(center1); //al servos in start position
servo2.write(center2);
servo3.write(center3);
servo4.write(center4);
servo5.write(center5);
servo6.write(center6);
delay(2000);
//res = -1;
}
if (flag =1){
automode();
}
if(res){
return httpd_resp_send_500(req);
}
httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
return httpd_resp_send(req, NULL, 0);
}
void startCameraServer(){
httpd_config_t config = HTTPD_DEFAULT_CONFIG();
config.server_port = 80;
httpd_uri_t index_uri = {
.uri = "/",
.method = HTTP_GET,
.handler = index_handler,
.user_ctx = NULL
};
httpd_uri_t cmd_uri = {
.uri = "/action",
.method = HTTP_GET,
.handler = cmd_handler,
.user_ctx = NULL
};
httpd_uri_t stream_uri = {
.uri = "/stream",
.method = HTTP_GET,
.handler = stream_handler,
.user_ctx = NULL
};
if (httpd_start(&camera_httpd, &config) == ESP_OK) {
httpd_register_uri_handler(camera_httpd, &index_uri);
httpd_register_uri_handler(camera_httpd, &cmd_uri);
}
config.server_port += 1;
config.ctrl_port += 1;
if (httpd_start(&stream_httpd, &config) == ESP_OK) {
httpd_register_uri_handler(stream_httpd, &stream_uri);
}
}
void setup() {
WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); //disable brownout detector
Serial.begin(115200);
Serial.setDebugOutput(false);
// button * * * * * * * * * * * * * * * * * *
pinMode(buttonLeft, INPUT_PULLUP);
pinMode(buttonRight, INPUT_PULLUP);
// servos * * * * * * * * * * * * * * * * * *
servo1.attach(6); //please check the correct wiring
servo2.attach(5);
servo3.attach(44);
servo4.attach(7);
servo5.attach(8);
servo6.attach(9);
delay(20);
servo1.write(center1); //all servos in start position
servo2.write(center2);
servo3.write(center3);
servo4.write(center4);
servo5.write(center5);
servo6.write(center6);
delay(2000); //wait
// camera * * * * * * * * * * * * * * * * * *
camera_config_t config;
config.ledc_channel = LEDC_CHANNEL_0;
config.ledc_timer = LEDC_TIMER_0;
config.pin_d0 = Y2_GPIO_NUM;
config.pin_d1 = Y3_GPIO_NUM;
config.pin_d2 = Y4_GPIO_NUM;
config.pin_d3 = Y5_GPIO_NUM;
config.pin_d4 = Y6_GPIO_NUM;
config.pin_d5 = Y7_GPIO_NUM;
config.pin_d6 = Y8_GPIO_NUM;
config.pin_d7 = Y9_GPIO_NUM;
config.pin_xclk = XCLK_GPIO_NUM;
config.pin_pclk = PCLK_GPIO_NUM;
config.pin_vsync = VSYNC_GPIO_NUM;
config.pin_href = HREF_GPIO_NUM;
config.pin_sscb_sda = SIOD_GPIO_NUM;
config.pin_sscb_scl = SIOC_GPIO_NUM;
config.pin_pwdn = PWDN_GPIO_NUM;
config.pin_reset = RESET_GPIO_NUM;
config.xclk_freq_hz = 20000000;
config.frame_size = FRAMESIZE_VGA; //choose the prefered size
/*UXGA(1600x1200)
SXGA(1280x1024)
XGA(1024x768)
SVGA(800x600)
VGA(640x480)
CIF(400x296)
QVGA(320x240)
HQVGA(240x176)
QQVGA(160x120)
*/
config.pixel_format = PIXFORMAT_JPEG; // for streaming
config.grab_mode = CAMERA_GRAB_WHEN_EMPTY;
config.fb_location = CAMERA_FB_IN_PSRAM;
//config.jpeg_quality = 12; //10-63 lower number means higher quality
config.jpeg_quality = 18;
config.fb_count = 1;
// if PSRAM IC present, init with UXGA resolution and higher JPEG quality
// for larger pre-allocated frame buffer.
if(config.pixel_format == PIXFORMAT_JPEG){
if(psramFound()){
config.jpeg_quality = 10;
config.fb_count = 2;
config.grab_mode = CAMERA_GRAB_LATEST;
} else {
// Limit the frame size when PSRAM is not available
config.frame_size = FRAMESIZE_CIF;
config.fb_location = CAMERA_FB_IN_DRAM;
}
} else {
// Best option for face detection/recognition
config.frame_size = FRAMESIZE_240X240;
#if CONFIG_IDF_TARGET_ESP32S3
config.fb_count = 2;
#endif
}
// Camera init
esp_err_t err = esp_camera_init(&config);
if (err != ESP_OK) {
Serial.printf("Camera init failed with error 0x%x", err);
return;
}
// Wi-Fi connection
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi connected");
Serial.print("Camera Stream Ready! Go to: http://");
Serial.println(WiFi.localIP());
// Start streaming web server
startCameraServer();
}
void loop() {
}
// walking modes * * * * * * * * * * * * * * * * * *
void forward() {
servo3.write(center3 - 10); //left mid down
servo4.write(center4 - 10); //right mid up
delay(20);
servo1.write(center1 - steplength); //left side forward
delay(30);
servo5.write(center5 - steplength);
servo2.write(center2 - steplength); //right side backwards/give push
delay(30);
servo6.write(center6 - steplength);
delay(speed1);
servo3.write(center3 + 10); //left mid up
servo4.write(center4 + 10); //right mid down
delay(20);
servo1.write(center1 + steplength); //left side backwards/give push
delay(30);
servo5.write(center5 + steplength);
servo2.write(center2 + steplength); //right side forward
delay(30);
servo6.write(center6 + steplength);
delay(speed1);
}
void left() {
servo3.write(center3 - 10); //left mid down
servo4.write(center4 - 10); //right mid up
delay(20);
servo1.write(center1 - smallstep); //left side forward
servo5.write(center5 - smallstep);
servo2.write(center2 - steplength); //right side backwards/give push
servo6.write(center6 - steplength);
delay(speed1);
servo3.write(center3 + 10); //left mid up
servo4.write(center4 + 10); //right mid down
delay(20);
servo1.write(center1 + smallstep); //left side backwards/give push
servo5.write(center5 + smallstep);
servo2.write(center2 + steplength); //right side forward
servo6.write(center6 + steplength);
delay(speed1);
}
void right() {
servo3.write(center3 + 10); //left mid up
servo4.write(center4 + 10); //right mid down
delay(20);
servo1.write(center1 + steplength); //left side backward/give push
servo5.write(center5 + steplength);
servo2.write(center2 + smallstep); //right side forward
servo6.write(center6 + smallstep);
delay(speed1);
servo3.write(center3 - 10); //left mid down
servo4.write(center4 - 10); //right mid up
delay(20);
servo1.write(center1 - steplength); //left side forward
servo5.write(center5 - steplength);
servo2.write(center2 - smallstep); //right side backwards/give push
servo6.write(center6 - smallstep);
delay(speed1);
}
void backward() {
servo3.write(center3 + 10); //left mid up
servo4.write(center4 + 10); //right mid down
delay(20);
servo1.write(center1 - steplength); //left side forward/give push
delay(30);
servo5.write(center5 - steplength);
servo2.write(center2 - steplength); //right side backwards
delay(30);
servo6.write(center6 - steplength);
delay(speed1 + 100);
servo3.write(center3 - 10); //left mid down
servo4.write(center4 - 10); //right mid up
delay(20);
servo1.write(center1 + steplength); //left side backwards
delay(30);
servo5.write(center5 + steplength);
servo2.write(center2 + steplength); //right side forward/give push
delay(30);
servo6.write(center6 + steplength);
delay(speed1 + 100);
}
void rise() {
servo1.write(center1 - 30); //front legs up
servo2.write(center2 + 30);
servo3.write(center3 - 20); //left mid down
servo4.write(center4 + 20); //right mid down
servo5.write(center5 - 25); //rear legs centered
servo6.write(center6 + 25);
delay(100);
}
void stop() {
servo1.write(center1);
servo2.write(center2);
servo3.write(center3);
servo4.write(center4);
servo5.write(center5);
servo6.write(center6);
delay(50);
}
void automode(){
leftState = digitalRead(buttonLeft);
rightState = digitalRead(buttonRight);
delay(50);
if ((leftState == LOW) && (leftState == HIGH)) {
Serial.println("stop and go right");
for (int i = 0; i<=3; i++){
backward();
}
for (int i = 0; i<=3; i++){
right();
}
}
if ((leftState == HIGH) && (leftState == LOW)) {
Serial.println("stop and go left");
for (int i = 0; i<=3; i++){
backward();
}
for (int i = 0; i<=3; i++){
left();
}
}
if ((leftState == LOW) && (leftState == LOW)) {
Serial.println("stop, go back and try again");
for (int i = 0; i<=6; i++){
backward();
}
for (int i = 0; i<=5; i++){
left();
}
}
else {
forward();
}
if (flag = 0) {
return;
}
delay(200);
}
تو فایلهای پروژه، یه نسخه بهینهشده از کدها هم قرار داده شده که مشکلات هماهنگی با کتابخونه سروو توش کاملا برطرف شده. پیشنهاد میکنم از همون نسخه بهینهشده استفاده کنی تا رباتت روانتر کار کنه. این کدها نتیجه توسعه گروهی هست و عملکرد خیلی پایداری به رباتت میده.
چطور ربات رو با وایفای کنترل کنیم؟
این ربات جذاب رو میتونی هم به مودم خونه وصل کنی و هم خودش رو تبدیل به یه نقطه اتصال (AP) کنی تا مستقیم با گوشیت بهش وصل بشی. برای استفاده از حالت دوم اینطوری عمل کن:
اول کلید پاور مورچه رو روشن کن و چند ثانیه منتظر بمون. حالا با گوشی یا لپتاپت شبکههای وایفای رو سرچ کن و به شبکهای با اسم Red_Ant_Access وصل شو (این شبکه اصلا نیازی به رمز عبور نداره).
مرورگر گوشیت رو باز کن و آدرس آیپی 192.168.4.1 رو وارد کن. به همین راحتی یه پنل کنترل روی صفحه گوشیت ظاهر میشه که تو پسزمینهاش، تصویر زنده دوربین ربات در حال پخشه.
حالا با نگه داشتن انگشتت روی دکمههای رابط کاربری، میتونی ربات رو به جلو، عقب، چپ و راست هدایت کنی یا حتی بهش فرمان چرخش و بلند شدن بدی.
/*
* ESP32S3 XIAO Red Ant Remote Control - Access Point
* make sure that PSRAM is enabled
*/
// servos * * * * * * * * * * * * * * * * * * * *
#include <s3servo.h>
s3servo servo1;
s3servo servo2;
s3servo servo3;
s3servo servo4;
s3servo servo5;
s3servo servo6;
int center1 = 75; //best center position of each leg (individual)
int center2 = 105;
int center3 = 90;
int center4 = 90;
int center5 = 115;
int center6 = 75;
int pos = 90;
int pos2;
int Min = 50; //move betwenn this two values
int Max = 100;
int speed1 = 350; //pause between forward and backward movement
int steplength = 30; //big step
int smallstep = 5; //small step
int flag = 0;
// button * * * * * * * * * * * * * * * * * * * *
const int buttonLeft = 5;
const int buttonRight = 6;
int leftState =0;
int rightState =0;
// camera * * * * * * * * * * * * * * * * * * * *
#include "esp_camera.h"
#include <WiFi.h>
#include "esp_timer.h"
#include "img_converters.h"
#include "Arduino.h"
#include "fb_gfx.h"
#include "soc/soc.h" // disable brownout problems
#include "soc/rtc_cntl_reg.h" // disable brownout problems
#include "esp_http_server.h"
#include "esp_http_server.h"
#include "esp32-hal-ledc.h"
#include "sdkconfig.h"
#define CAMERA_MODEL_XIAO_ESP32S3 // Has PSRAM
#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG)
#include "esp32-hal-log.h"
#endif
// Replace with your network data
const char* ssid = "Red_Ant_Access"; //or set another name
//const char* password = "xxx"; //set you own password
#define PART_BOUNDARY "123456789000000000000987654321"
#define CAMERA_MODEL_AI_THINKER
//#define CAMERA_MODEL_WROVER_KIT
#if defined(CAMERA_MODEL_XIAO_ESP32S3)
#define PWDN_GPIO_NUM -1
#define RESET_GPIO_NUM -1
#define XCLK_GPIO_NUM 10
#define SIOD_GPIO_NUM 40
#define SIOC_GPIO_NUM 39
#define Y9_GPIO_NUM 48
#define Y8_GPIO_NUM 11
#define Y7_GPIO_NUM 12
#define Y6_GPIO_NUM 14
#define Y5_GPIO_NUM 16
#define Y4_GPIO_NUM 18
#define Y3_GPIO_NUM 17
#define Y2_GPIO_NUM 15
#define VSYNC_GPIO_NUM 38
#define HREF_GPIO_NUM 47
#define PCLK_GPIO_NUM 13
#define LED_GPIO_NUM 21
#else
#error "Camera model not selected"
#endif
// web page for remote control and screen * * * * * * * * * * * * *
static const char* _STREAM_CONTENT_TYPE = "multipart/x-mixed-replace;boundary=" PART_BOUNDARY;
static const char* _STREAM_BOUNDARY = "\r\n--" PART_BOUNDARY "\r\n";
static const char* _STREAM_PART = "Content-Type: image/jpeg\r\nContent-Length: %u\r\n\r\n";
httpd_handle_t camera_httpd = NULL;
httpd_handle_t stream_httpd = NULL;
static const char PROGMEM INDEX_HTML[] = R"rawliteral(
<html>
<head>
<title>Red Ant</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
body { font-family: Arial; text-align: center; margin:0px auto; padding-top: 30px; background-color: #010101; color: orange;}
table { margin-left: auto; margin-right: auto; }
td { padding: 8 px; }
.button1 {
background-color: #f93e16;
border: 1;
color: white;
padding: 10px 20px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 32px;
margin: 6px 3px;
cursor: pointer;
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
-webkit-tap-highlight-color: rgba(0,0,0,0);
}
.button2 {
background-color: #4EB232;
border: 1;
color: white;
padding: 10px 20px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 18px;
margin: 6px 3px;
cursor: pointer;
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
-webkit-tap-highlight-color: rgba(0,0,0,0);
}
img { width: auto ;
max-width: 100% ;
height: auto ;
}
</style>
</head>
<body>
<h1>Red Ant</h1>
<img src="" id="photo">
<table>
<tr>
<td align="center"><button1 class="button1" onmousedown="toggleCheckbox('left');" ontouchstart="toggleCheckbox('left');" onmouseup="toggleCheckbox('stop');" ontouchend="toggleCheckbox('stop');">↖</button1></td>
<td align="center"><button1 class="button1" onmousedown="toggleCheckbox('forward');" ontouchstart="toggleCheckbox('forward');" onmouseup="toggleCheckbox('stop');" ontouchend="toggleCheckbox('stop');"> ↑ ↑ </button1></td>
<td align="center"><button1 class="button1" onmousedown="toggleCheckbox('right');" ontouchstart="toggleCheckbox('right');" onmouseup="toggleCheckbox('stop');" ontouchend="toggleCheckbox('stop');">↗</button1></td>
</tr>
<tr><td align="center"><button1 class="button1" onmousedown="toggleCheckbox('turnleft');" ontouchstart="toggleCheckbox('turnleft');" onmouseup="toggleCheckbox('stop');" ontouchend="toggleCheckbox('stop');">↶</button1></td>
<td align="center"><button1 class="button1" onmousedown="toggleCheckbox('rise');" ontouchstart="toggleCheckbox('rise');" onmouseup="toggleCheckbox('stop');" ontouchend="toggleCheckbox('stop');">⟰</button1></td>/
<td align="center"><button1 class="button1" onmousedown="toggleCheckbox('turnright');" ontouchstart="toggleCheckbox('turnright');" onmouseup="toggleCheckbox('stop');" ontouchend="toggleCheckbox('stop');">↷</button1></td></tr>
<tr><td colspan="3" align="center"><button1 class="button1" onmousedown="toggleCheckbox('backward');" ontouchstart="toggleCheckbox('backward');" onmouseup="toggleCheckbox('stop');" ontouchend="toggleCheckbox('stop');">↓</button1></td></tr>
<tr><td colspan="3" align="center"><button2 class="button2" onmousedown="toggleCheckbox('automode');" ontouchstart="toggleCheckbox('automode');" onmouseup="toggleCheckbox('stop');" ontouchend="toggleCheckbox('stop');">Auto Mode</button1></td></tr>
</table>
<script>
function toggleCheckbox(x) {
var xhr = new XMLHttpRequest();
xhr.open("GET", "/action?go=" + x, true);
xhr.send();
}
window.onload = document.getElementById("photo").src = window.location.href.slice(0, -1) + ":81/stream";
</script>
</body>
</html>
)rawliteral";
static esp_err_t index_handler(httpd_req_t *req){
httpd_resp_set_type(req, "text/html");
return httpd_resp_send(req, (const char *)INDEX_HTML, strlen(INDEX_HTML));
}
static esp_err_t stream_handler(httpd_req_t *req){
camera_fb_t * fb = NULL;
esp_err_t res = ESP_OK;
size_t _jpg_buf_len = 0;
uint8_t * _jpg_buf = NULL;
char * part_buf[64];
res = httpd_resp_set_type(req, _STREAM_CONTENT_TYPE);
if(res != ESP_OK){
return res;
}
while(true){
fb = esp_camera_fb_get();
if (!fb) {
Serial.println("Camera capture failed");
res = ESP_FAIL;
} else {
if(fb->width > 400){
if(fb->format != PIXFORMAT_JPEG){
bool jpeg_converted = frame2jpg(fb, 80, &_jpg_buf, &_jpg_buf_len);
esp_camera_fb_return(fb);
fb = NULL;
if(!jpeg_converted){
Serial.println("JPEG compression failed");
res = ESP_FAIL;
}
} else {
_jpg_buf_len = fb->len;
_jpg_buf = fb->buf;
}
}
}
if(res == ESP_OK){
size_t hlen = snprintf((char *)part_buf, 64, _STREAM_PART, _jpg_buf_len);
res = httpd_resp_send_chunk(req, (const char *)part_buf, hlen);
}
if(res == ESP_OK){
res = httpd_resp_send_chunk(req, (const char *)_jpg_buf, _jpg_buf_len);
}
if(res == ESP_OK){
res = httpd_resp_send_chunk(req, _STREAM_BOUNDARY, strlen(_STREAM_BOUNDARY));
}
if(fb){
esp_camera_fb_return(fb);
fb = NULL;
_jpg_buf = NULL;
} else if(_jpg_buf){
free(_jpg_buf);
_jpg_buf = NULL;
}
if(res != ESP_OK){
break;
}
//Serial.printf("MJPG: %uB\n",(uint32_t)(_jpg_buf_len));
}
return res;
}
static esp_err_t cmd_handler(httpd_req_t *req){
char* buf;
size_t buf_len;
char variable[32] = {0,};
buf_len = httpd_req_get_url_query_len(req) + 1;
if (buf_len > 1) {
buf = (char*)malloc(buf_len);
if(!buf){
httpd_resp_send_500(req);
return ESP_FAIL;
}
if (httpd_req_get_url_query_str(req, buf, buf_len) == ESP_OK) {
if (httpd_query_key_value(buf, "go", variable, sizeof(variable)) == ESP_OK) {
} else {
free(buf);
httpd_resp_send_404(req);
return ESP_FAIL;
}
} else {
free(buf);
httpd_resp_send_404(req);
return ESP_FAIL;
}
free(buf);
} else {
httpd_resp_send_404(req);
return ESP_FAIL;
}
sensor_t * s = esp_camera_sensor_get();
int res = 0;
if(!strcmp(variable, "forward")) {
Serial.println("Forward");
forward();
}
else if(!strcmp(variable, "left")) {
Serial.println("Left");
left();
}
else if(!strcmp(variable, "right")) {
Serial.println("Right");
right();
}
else if(!strcmp(variable, "backward")) {
Serial.println("Backward");
backward();
}
else if(!strcmp(variable, "turnleft")) {
Serial.println("turn left");
}
else if(!strcmp(variable, "turnright")) {
Serial.println("turn right");
}
else if(!strcmp(variable, "rise")) {
Serial.println("rise");
rise();
}
else if(!strcmp(variable, "automode")) {
Serial.println("auto modus");
if (flag = 0) flag =1;
if (flag = 1) flag =0;
//automode
}
else { //stop
servo1.write(center1); //al servos in start position
servo2.write(center2);
servo3.write(center3);
servo4.write(center4);
servo5.write(center5);
servo6.write(center6);
delay(2000);
//res = -1;
}
if (flag =1){
automode();
}
if(res){
return httpd_resp_send_500(req);
}
httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
return httpd_resp_send(req, NULL, 0);
}
void startCameraServer(){
httpd_config_t config = HTTPD_DEFAULT_CONFIG();
config.server_port = 80;
httpd_uri_t index_uri = {
.uri = "/",
.method = HTTP_GET,
.handler = index_handler,
.user_ctx = NULL
};
httpd_uri_t cmd_uri = {
.uri = "/action",
.method = HTTP_GET,
.handler = cmd_handler,
.user_ctx = NULL
};
httpd_uri_t stream_uri = {
.uri = "/stream",
.method = HTTP_GET,
.handler = stream_handler,
.user_ctx = NULL
};
if (httpd_start(&camera_httpd, &config) == ESP_OK) {
httpd_register_uri_handler(camera_httpd, &index_uri);
httpd_register_uri_handler(camera_httpd, &cmd_uri);
}
config.server_port += 1;
config.ctrl_port += 1;
if (httpd_start(&stream_httpd, &config) == ESP_OK) {
httpd_register_uri_handler(stream_httpd, &stream_uri);
}
}
void setup() {
WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); //disable brownout detector
Serial.begin(115200);
Serial.setDebugOutput(false);
// button * * * * * * * * * * * * * * * * * *
pinMode(buttonLeft, INPUT_PULLUP);
pinMode(buttonRight, INPUT_PULLUP);
// servos * * * * * * * * * * * * * * * * * *
servo1.attach(6);
servo2.attach(5);
servo3.attach(44);
servo4.attach(7);
servo5.attach(8);
servo6.attach(9);
delay(20);
servo1.write(center1); //al servos in start position
servo2.write(center2);
servo3.write(center3);
servo4.write(center4);
servo5.write(center5);
servo6.write(center6);
delay(2000); //wait
// camera * * * * * * * * * * * * * * * * * *
camera_config_t config;
config.ledc_channel = LEDC_CHANNEL_0;
config.ledc_timer = LEDC_TIMER_0;
config.pin_d0 = Y2_GPIO_NUM;
config.pin_d1 = Y3_GPIO_NUM;
config.pin_d2 = Y4_GPIO_NUM;
config.pin_d3 = Y5_GPIO_NUM;
config.pin_d4 = Y6_GPIO_NUM;
config.pin_d5 = Y7_GPIO_NUM;
config.pin_d6 = Y8_GPIO_NUM;
config.pin_d7 = Y9_GPIO_NUM;
config.pin_xclk = XCLK_GPIO_NUM;
config.pin_pclk = PCLK_GPIO_NUM;
config.pin_vsync = VSYNC_GPIO_NUM;
config.pin_href = HREF_GPIO_NUM;
config.pin_sscb_sda = SIOD_GPIO_NUM;
config.pin_sscb_scl = SIOC_GPIO_NUM;
config.pin_pwdn = PWDN_GPIO_NUM;
config.pin_reset = RESET_GPIO_NUM;
config.xclk_freq_hz = 20000000;
config.frame_size = FRAMESIZE_VGA; //choose the prefered size
/*UXGA(1600x1200)
SXGA(1280x1024)
XGA(1024x768)
SVGA(800x600)
VGA(640x480)
CIF(400x296)
QVGA(320x240)
HQVGA(240x176)
QQVGA(160x120)
*/
config.pixel_format = PIXFORMAT_JPEG; // for streaming
config.grab_mode = CAMERA_GRAB_WHEN_EMPTY;
config.fb_location = CAMERA_FB_IN_PSRAM;
config.jpeg_quality = 12;
config.fb_count = 1;
// if PSRAM IC present, init with UXGA resolution and higher JPEG quality
// for larger pre-allocated frame buffer.
if(config.pixel_format == PIXFORMAT_JPEG){
if(psramFound()){
config.jpeg_quality = 10;
config.fb_count = 2;
config.grab_mode = CAMERA_GRAB_LATEST;
} else {
// Limit the frame size when PSRAM is not available
config.frame_size = FRAMESIZE_CIF;
config.fb_location = CAMERA_FB_IN_DRAM;
}
} else {
// Best option for face detection/recognition
config.frame_size = FRAMESIZE_240X240;
#if CONFIG_IDF_TARGET_ESP32S3
config.fb_count = 2;
#endif
}
// Camera init
esp_err_t err = esp_camera_init(&config);
if (err != ESP_OK) {
Serial.printf("Camera init failed with error 0x%x", err);
return;
}
// Wi-Fi connection (Soft Access Point)
WiFi.softAP(ssid);
/*
// Wi-Fi connection for your network
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
*/
Serial.println("");
Serial.println("WiFi connected");
Serial.print("Camera Stream Ready! Go to: http://");
Serial.println(WiFi.localIP());
// Start streaming web server
startCameraServer();
}
void loop() {
}
// walking modes * * * * * * * * * * * * * * * * * *
void forward() {
servo3.write(center3 - 10); //left mid down
servo4.write(center4 - 10); //right mid up
delay(20);
servo1.write(center1 - steplength); //left side forward
delay(30);
servo5.write(center5 - steplength);
servo2.write(center2 - steplength); //right side backwards/give push
delay(30);
servo6.write(center6 - steplength);
delay(speed1);
servo3.write(center3 + 10); //left mid up
servo4.write(center4 + 10); //right mid down
delay(20);
servo1.write(center1 + steplength); //left side backwards/give push
delay(30);
servo5.write(center5 + steplength);
servo2.write(center2 + steplength); //right side forward
delay(30);
servo6.write(center6 + steplength);
delay(speed1);
}
void left() {
servo3.write(center3 - 10); //left mid down
servo4.write(center4 - 10); //right mid up
delay(20);
servo1.write(center1 - smallstep); //left side forward
servo5.write(center5 - smallstep);
servo2.write(center2 - steplength); //right side backwards/give push
servo6.write(center6 - steplength);
delay(speed1);
servo3.write(center3 + 10); //left mid up
servo4.write(center4 + 10); //right mid down
delay(20);
servo1.write(center1 + smallstep); //left side backwards/give push
servo5.write(center5 + smallstep);
servo2.write(center2 + steplength); //right side forward
servo6.write(center6 + steplength);
delay(speed1);
}
void right() {
servo3.write(center3 + 10); //left mid up
servo4.write(center4 + 10); //right mid down
delay(20);
servo1.write(center1 + steplength); //left side backward/give push
servo5.write(center5 + steplength);
servo2.write(center2 + smallstep); //right side forward
servo6.write(center6 + smallstep);
delay(speed1);
servo3.write(center3 - 10); //left mid down
servo4.write(center4 - 10); //right mid up
delay(20);
servo1.write(center1 - steplength); //left side forward
servo5.write(center5 - steplength);
servo2.write(center2 - smallstep); //right side backwards/give push
servo6.write(center6 - smallstep);
delay(speed1);
}
void backward() {
servo3.write(center3 + 10); //left mid up
servo4.write(center4 + 10); //right mid down
delay(20);
servo1.write(center1 - steplength); //left side forward/give push
delay(30);
servo5.write(center5 - steplength);
servo2.write(center2 - steplength); //right side backwards
delay(30);
servo6.write(center6 - steplength);
delay(speed1 + 100);
servo3.write(center3 - 10); //left mid down
servo4.write(center4 - 10); //right mid up
delay(20);
servo1.write(center1 + steplength); //left side backwards
delay(30);
servo5.write(center5 + steplength);
servo2.write(center2 + steplength); //right side forward/give push
delay(30);
servo6.write(center6 + steplength);
delay(speed1 + 100);
}
void rise() {
servo1.write(center1 - 30); //front legs up
servo2.write(center2 + 30);
servo3.write(center3 - 20); //left mid down
servo4.write(center4 + 20); //right mid down
servo5.write(center5 - 25); //rear legs centered
servo6.write(center6 + 25);
delay(100);
}
void automode(){
leftState = digitalRead(buttonLeft);
rightState = digitalRead(buttonRight);
delay(50);
if ((leftState == LOW) && (leftState == HIGH)) {
Serial.println("stop and go right");
for (int i = 0; i<=3; i++){
backward();
}
for (int i = 0; i<=3; i++){
right();
}
}
if ((leftState == HIGH) && (leftState == LOW)) {
Serial.println("stop and go left");
for (int i = 0; i<=3; i++){
backward();
}
for (int i = 0; i<=3; i++){
left();
}
}
if ((leftState == LOW) && (leftState == LOW)) {
Serial.println("stop, go back and try again");
for (int i = 0; i<=6; i++){
backward();
}
for (int i = 0; i<=5; i++){
left();
}
}
else {
forward();
}
if (flag = 0) {
return;
}
delay(200);
}
اگه کدها رو با دقت روی برد ریخته باشی، تو این مرحله همهچیز بدون تأخیر و کاملا روان اجرا میشه. از کنترل کردن مورچهات حسابی لذت ببر!
حرکت خودکار مورچه و جمعبندی کار!
اگه دوست داری رباتت به حال خودش باشه و تو محیط گشت بزنه، کافیه تو پنل کنترل روی دکمه سبز رنگ Auto Mode کلیک کنی. تو این حالت، مورچه شروع به حرکت میکنه و به محض اینکه شاخکهای روی سرش به مانعی برخورد کنه، از طریق میکرو سوییچها متوجه میشه و مسیرش رو به سمت چپ یا راست تغییر میده تا یه وقت گیر نیفته.
به همین سادگی تو موفق شدی یه ربات هوشمند بسازی که هم تصویر زنده به گوشیت میفرسته و هم محیط اطرافش رو درک میکنه. ساختن پروژههای رباتیک همیشه چالشهای شیرین خودش رو داره. اگه موقع چاپ سهبعدی قطعات یا راهاندازی کدهای این مورچه بامزه به مشکلی خوردی یا ایده جالبی برای ارتقاش به ذهنت رسید، حتما تو بخش نظرات برامون بنویس تا با هم دربارهاش حسابی گپ بزنیم.
فایلهای پیوست
تو میتونی اولین سازنده باشی!
اگه پروژه رو ساختی به اشتراک بزار و اعتبار هدیه بگیر
این آموزش با استانداردهای اختصاصی آیسیمدار بازنویسی و بهینهسازی شده و تمامی حقوق انتشار آن متعلق به این مجموعه است و در صورت کپی یا بازنشر پیگرد قانونی خواهد داشت.








































گفتگوهای کاربران
هنوز نظری ثبت نشده؛ تو شروع کن!
هنوز نظری ثبت نشده؛ تو شروع کن!