Bu robot xona boʻylab oʻzi yuradi va yoʻlidagi har qanday narsadan burilib ketadi. Old tomonga oʻrnatilgan ultratovush datchigi robot oldidagi masofani oʻlchaydi. Biror narsa 25 sm dan yaqin boʻlsa, robot orqaga yuradi, oʻngga buriladi va yana oldinga yuradi. HC-SR04 masofani qanday oʻlchashini, holatlar mashinasi (state machine) robot ishini qanday tartibga solishini va millis() robotga yurib turib oʻlchash imkonini qanday berishini oʻrganasiz. Bu oson loyiha, u taxminan 60 daqiqa vaqt oladi.
Nima kerak
Arduino Uno R3Simulyatorda bor× 1Narxni doʻkonda koʻring
L298N motor drayveri moduliSimulyatorda bor× 1Narxni doʻkonda koʻring
TT reduktorli motor 1:48, 65 mm gʻildirak bilanSimulyatorda bor× 2Narxni doʻkonda koʻring
4 × AA batareya qutisiSimulyatorda bor× 1Narxni doʻkonda koʻringSharli tayanch 20 mmSimulyatorda bor× 1Narxni doʻkonda koʻring
HC-SR04 ultratovush datchigiSimulyatorda bor× 1Narxni doʻkonda koʻring
Ulash simlari toʻplami× 1Narxni doʻkonda koʻring
Bu Birinchi yurish loyihasidagi mashina, faqat old tomoniga HC-SR04 ultratovush datchigi qoʻshilgan. Mashinangiz allaqachon boʻlsa, Marketdagi «Toʻsiqlarni aylanib oʻtish» qoʻshimchasini koʻring.
Qanday ishlaydi
HC-SR04 masofani qanday oʻlchaydi
HC-SR04 ning old tomonida ikkita dumaloq qism bor. Biri ultratovush yuboradi: bu 40 kHz chastotali tovush, u odam eshita olmaydigan darajada yuqori. Ikkinchisi aks-sadoni tinglaydi. Bitta oʻlchash quyidagicha boʻladi:
- Uno TRIG pinini 10 mks davomida HIGH qiladi, datchik esa qisqa ultratovush signalini yuboradi.
- Datchik ECHO pinini HIGH qiladi va tinglaydi.
- Aks-sado qaytib kelganda datchik ECHO ni yana LOW qiladi.
Demak, tovush narsagacha borib-qaytishi uchun qancha vaqt kerak boʻlsa, ECHO shuncha vaqt HIGH boʻlib turadi. 20 °C da tovush taxminan 343 m/s tezlikda tarqaladi. Masofaning har bir santimetri uchun tovush borib-qaytib 2 sm yoʻl bosadi va bunga taxminan 58 mks ketadi. Shuning uchun santimetrdagi masofa mikrosekunddagi ECHO vaqtini 58 ga boʻlganga teng. Masalan, 1450 mks boʻlsa, 1450 / 58 = 25 sm.
Datchikning cheklovlari bor:
| Cheklov | Robot uchun nimani bildiradi |
|---|---|
| Oʻlchash oraligʻi taxminan 2 sm dan 4 m gacha | bundan yaqin yoki uzoq narsalar notoʻgʻri qiymat beradi yoki umuman qiymat bermaydi |
| Tovush dastasining kengligi taxminan 15 gradus | dastadan chetdagi narsalar koʻrinmaydi |
| Yumshoq yoki qiya sirtlar | tovush yutiladi yoki burchak ostida chetga ketadi, shuning uchun aks-sado qaytmaydi |
| Signallar orasida taxminan 60 ms | eski aks-sadolar soʻnishi uchun vaqt kerak |
Holatlar mashinasi
Har bir lahzada robot uchta ishdan aynan bittasini bajaradi. Ularning har biri holat deyiladi:
| Holat | Motorlar | Holat qachon tugaydi | Keyingi holat |
|---|---|---|---|
| Drive (yurish) | drive(190, 190): oldinga |
biror narsa 25 sm dan yaqin boʻlganda | Back |
| Back (orqaga) | drive(-160, -160): orqaga |
300 ms oʻtganda | Turn |
| Turn (burilish) | drive(170, -170): joyida oʻngga aylanadi |
380 ms oʻtganda | Drive |
Holatlar mashinasi shunday tuzilgan dastur: unda holatlar roʻyxati, har bir holatda robot nima qilishi va keyingi holatga oʻtish qoidalari bor. Har bir holat alohida, shuning uchun robotning xatti-harakatini oʻqish ham, oʻzgartirish ham oson.
Kutmasdan oʻlchash
loop() hech qachon delay() ni chaqirmaydi. U soniyasiga minglab marta bajariladi va har safar ikkita ishni qiladi:
- Vaqti kelgan boʻlsa, oʻlchaydi. Oxirgi oʻlchovdan beri 60 ms oʻtgan boʻlsa, masofani oʻlchaydi va natijani chiqaradi.
- Joriy holatni bajaradi. Motorlarni sozlaydi va holatni tugatish kerakmi, yoʻqmi, tekshiradi.
Qancha vaqt oʻtganini bilish uchun sketch millis() dan foydalanadi: bu Uno ishga tushganidan beri oʻtgan millisekundlar soni. Faqat oʻlchashning oʻzi biroz kutadi, chunki pulseIn() aks-sadoni kutadi. 25 sm narida turgan toʻsiq uchun bu taxminan 1,5 ms davom etadi. Oʻlchash oraligʻida hech narsa boʻlmasa, u 25 ms dan keyin kutishni toʻxtatadi. Bu qisqa kutish paytida motorlar ishlashda davom etadi.
Nega Back holati uchun shunchaki delay(300) yozib qoʻyilmaydi? Chunki shu kutish davomida robot koʻr boʻlib qoladi. Holatlar mashinasi bilan esa u har qanday holatda har 60 ms da oʻlchaydi. Bu sketch Back va Turn holatlarida oʻlchovlardan hali foydalanmaydi, lekin siz foydalanishingiz mumkin: birinchi topshiriqda robot yoʻl ochilishi bilanoq burilishni toʻxtatadi.
Yigʻish
1. Simulyatorda robotni tanlang
Oʻyinda boshidanoq bor boʻlgan Obstacle avoider mashinasining yuqori plastinasi (qavati) old qismida HC-SR04 allaqachon oʻrnatilgan. Uning simlari bu sahifadagidek ulanmagan, shuning uchun 2-qadamda ularni qaytadan ulaysiz.
Haqiqiy yigʻishda: «Birinchi yurish» loyihasidagi mashinangizdan boshlang. HC-SR04 ni old tomonga tekis, ikkala dumaloq qismi toʻgʻri oldinga qaraydigan qilib oʻrnating. U pastga qarasa, polni koʻradi. Yuqoriga qarasa, past toʻsiqlarni koʻrmay qoladi.
2. Datchikni ulang
| Qayerdan | Qayerga | Nima uchun |
|---|---|---|
| Batareya + | L298N +12V | Motorlar quvvati: toʻrtta AA batareyadan taxminan 6 V |
| Batareya - | L298N GND | Batareya minusi |
| L298N +5V | Uno 5V | Drayver stabilizatori Unoni quvvatlaydi: taxminan 4,4 V |
| L298N GND | Uno GND | Umumiy yer: signallarga bir xil 0 V kerak |
| Uno D5 | L298N ENA | Chap motor tezligi (PWM); ENA jumperi olib qoʻyilgan |
| Uno D4 | L298N IN1 | Chap motor yoʻnalishi |
| Uno D7 | L298N IN2 | Chap motor yoʻnalishi |
| Uno D8 | L298N IN3 | Oʻng motor yoʻnalishi |
| Uno D12 | L298N IN4 | Oʻng motor yoʻnalishi |
| Uno D6 | L298N ENB | Oʻng motor tezligi (PWM); ENB jumperi olib qoʻyilgan |
| Chap motor M+ | L298N OUT1 | |
| Chap motor M- | L298N OUT2 | |
| Oʻng motor M+ | L298N OUT4 | Ataylab almashtirilgan: bu motor koʻzgudagidek oʻrnatilgan |
| Oʻng motor M- | L298N OUT3 | Ataylab almashtirilgan: bu motor koʻzgudagidek oʻrnatilgan |
| HC-SR04 VCC | L298N +5V | Datchik uchun 5 V |
| HC-SR04 GND | Uno GND | Yer (GND) |
| Uno D9 | HC-SR04 TRIG | Oʻlchashni boshlaydi: 10 mks impuls |
| Uno D10 | HC-SR04 ECHO | Aks-sadoning borib-qaytish vaqtiga teng impuls |
Mashinaning ulanishi «Birinchi yurish» loyihasidagidek qoladi. Datchik toʻrtta sim bilan ulanadi: VCC L298N ning +5V klemmasiga, GND Unoning GND pinlaridan biriga, TRIG D9 ga, ECHO esa D10 ga. L298N ning +5V klemmasida Unoni taʼminlaydigan oʻsha 5 V bor, chunki u Unoning 5V piniga sim bilan ulangan.
Haqiqiy yigʻishda: +5V klemmasida Unoga boradigan sim allaqachon bor. Datchikning VCC simini oʻsha klemmaga, uning yoniga kiriting, keyin vintni burab mahkamlang. TRIG va ECHO ni ikki marta tekshiring, chunki ularni adashtirib qoʻyish oson.
3. Yuklang va sinab koʻring
«Kod» boʻlimini oching, shu sahifadagi ObstacleAvoider sketchini qoʻying, «Tekshirish» tugmasini, keyin «Yuklash» tugmasini bosing.
Haqiqiy yigʻishda: mashinani gʻildiraklari havoda turadigan qilib quti ustiga qoʻying, USB kabelni ulang va sketchni yuklang. Serial Monitor (port monitori) oynasini 9600 bod tezlikda oching. Qoʻlingizni datchik oldida harakatlantiring: sonlar qoʻlingiz harakatiga qarab oʻzgaradi. Qoʻlingiz 25 sm dan yaqinroq boʻlganda gʻildiraklar avval orqaga, keyin joyida oʻngga burilishdagidek aylanadi.
4. Robotni yurgizing
Toʻsiqlari bor arenada robotni ishga tushiring. Robot biror narsa 25 sm dan yaqin kelguncha yuradi, keyin orqaga yuradi, buriladi va yana oldinga yuradi. Serial Monitor oynasini oching: robot har 60 ms da santimetrdagi bitta masofani chiqaradi. 400 qiymati aks-sado qaytmaganini, yaʼni taxminan 4 m ichida hech narsa yoʻqligini bildiradi. Masofa jonli koʻrsatkichda ham koʻrinadi.
Haqiqiy yigʻishda: USB kabelni uzing va mashinani polga, bir nechta quti yoki kitob orasiga qoʻying. Qattiq, tekis narsalar eng yaxshi aks-sado beradi.
5. Sonlarni sozlang
| Sozlama | Nimani belgilaydi | Sinab koʻring |
|---|---|---|
STOP_CM = 25 |
toʻsiq qanchalik yaqin kela olishini | robot narsalarga urilsa, 35 |
CRUISE = 190 |
oldinga yurish tezligini | xotirjamroq robot uchun 150 |
BACK_MS = 300 |
qancha vaqt orqaga yurishini | tor joylarda kattaroq qiymat |
TURN_MS = 380 |
qancha vaqt aylanishini | kattaroq burilish uchun 600 |
Tezroq robot uchun STOP_CM kattaroq boʻlishi kerak, chunki u toʻxtashi uchun koʻproq masofa talab qiladi.
Kod
// Obstacle avoider: drive ahead; when something is close, back off and turn.
// Written without delay() in loop(): a state machine and millis() timers,
// so the robot keeps measuring while it moves.
// L298N: ENA -> D5, IN1 -> D4, IN2 -> D7 (left), ENB -> D6, IN3 -> D8, IN4 -> D12 (right)
// HC-SR04: TRIG -> D9, ECHO -> D10
const byte ENA = 5, IN1 = 4, IN2 = 7;
const byte ENB = 6, IN3 = 8, IN4 = 12;
const byte TRIG = 9, ECHO = 10;
const int CRUISE = 190; // forward speed, 0..255
const int STOP_CM = 25; // closer than this: avoid it
const unsigned long PING_MS = 60; // the HC-SR04 needs a pause between pings
const unsigned long BACK_MS = 300;
const unsigned long TURN_MS = 380;
enum class State : byte { Drive, Back, Turn };
State state = State::Drive;
unsigned long stateStart = 0;
unsigned long lastPing = 0;
int distanceCm = 400;
void setMotor(byte enablePin, byte inA, byte inB, int speed) {
speed = constrain(speed, -255, 255);
digitalWrite(inA, speed > 0);
digitalWrite(inB, speed < 0);
analogWrite(enablePin, abs(speed));
}
void drive(int left, int right) {
setMotor(ENA, IN1, IN2, left);
setMotor(ENB, IN3, IN4, right);
}
// Distance in centimetres; 400 when no echo came back (nothing within about 4 m).
int readDistanceCm() {
digitalWrite(TRIG, LOW);
delayMicroseconds(2);
digitalWrite(TRIG, HIGH); // a 10 microsecond pulse starts a measurement
delayMicroseconds(10);
digitalWrite(TRIG, LOW);
unsigned long us = pulseIn(ECHO, HIGH, 25000UL); // wait at most 25 ms
if (us == 0) return 400;
return us / 58; // sound goes there and back: 58 us per cm
}
void enter(State next) {
state = next;
stateStart = millis();
if (next == State::Drive) lastPing = 0; // measure again before driving on
}
void setup() {
const byte pins[] = {ENA, IN1, IN2, ENB, IN3, IN4, TRIG};
for (byte pin : pins) pinMode(pin, OUTPUT);
pinMode(ECHO, INPUT);
Serial.begin(9600);
}
void loop() {
unsigned long now = millis();
if (now - lastPing >= PING_MS) {
lastPing = now;
distanceCm = readDistanceCm();
Serial.println(distanceCm);
}
switch (state) {
case State::Drive:
drive(CRUISE, CRUISE);
if (distanceCm < STOP_CM) enter(State::Back);
break;
case State::Back:
drive(-160, -160);
if (now - stateStart >= BACK_MS) enter(State::Turn);
break;
case State::Turn:
drive(170, -170); // spin right on the spot
if (now - stateStart >= TURN_MS) enter(State::Drive);
break;
}
}Holat va taymerlar
enum class State : byte { Drive, Back, Turn };
State state = State::Drive;
unsigned long stateStart = 0;
unsigned long lastPing = 0;
int distanceCm = 400;enum class State uchta holatga nom beradi, : byte esa holatni bitta baytga joylaydi. state joriy holatni saqlaydi, robot Drive holatidan boshlaydi. stateStart joriy holat qachon boshlanganini, lastPing esa oxirgi oʻlchov qachon olinganini eslab qoladi. Ikkalasi ham unsigned long turida, millis() ham aynan shu turdagi qiymat qaytaradi. distanceCm 400 dan boshlanadi, bu «hech narsa koʻrinmadi» degani.
readDistanceCm(): bitta oʻlchov
digitalWrite(TRIG, HIGH); // a 10 microsecond pulse starts a measurement
delayMicroseconds(10);
digitalWrite(TRIG, LOW);
unsigned long us = pulseIn(ECHO, HIGH, 25000UL); // wait at most 25 ms
if (us == 0) return 400;
return us / 58; // sound goes there and back: 58 us per cmBu qatorlardan oldin TRIG 2 mks davomida LOW holatda ushlab turiladi, shunda ishga tushirish impulsi toza boshlanadi. pulseIn() ECHO qancha vaqt HIGH boʻlib turganini mikrosekundlarda oʻlchaydi. Agar 25 ms ichida aks-sado qaytmasa, u 0 qaytaradi, funksiya esa 400 qaytaradi. Aks holda funksiya santimetrlarni olish uchun natijani 58 ga boʻladi.
enter(): holatni almashtirish
void enter(State next) {
state = next;
stateStart = millis();
if (next == State::Drive) lastPing = 0; // measure again before driving on
}Holat har safar enter() orqali almashadi. U yangi holatni va u boshlangan vaqtni saqlaydi. Robot Drive holatiga qaytganda lastPing = 0 qatori tufayli loop() ning keyingi aylanishida albatta yangi oʻlchov olinadi. Shunday qilib, robot burilishdan keyin oʻlchangan yangi masofaga qarab qaror qabul qiladi.
loop(): avval oʻlchash, keyin harakat
unsigned long now = millis();
if (now - lastPing >= PING_MS) {
lastPing = now;
distanceCm = readDistanceCm();
Serial.println(distanceCm);
}now - lastPing >= PING_MS «60 ms oʻtdimi?» deb soʻraydi. Ayirish shaklida yozilgani uchun bu shart taxminan 49,7 kundan keyin millis() yana 0 dan boshlanganda ham toʻgʻri ishlayveradi.
Keyin har bir holat uchun bittadan case boʻlgan switch keladi. Drive ikkala motorni CRUISE tezligiga qoʻyadi va distanceCm STOP_CM dan kichik boʻlsa, Back holatiga oʻtadi. Back 160 tezlik bilan orqaga yuradi va BACK_MS dan keyin Turn holatiga oʻtadi. Turn robotni joyida oʻngga aylantiradi, bunda chap gʻildirak oldinga, oʻng gʻildirak orqaga aylanadi, TURN_MS dan keyin esa Drive holatiga oʻtadi. setMotor() va drive() «Birinchi yurish» loyihasidagi bilan bir xil.
Agar ishlamasa
- Serial Monitor doim 400 koʻrsatyapti. Oʻlchash oraligʻida hech narsa yoʻq yoki datchik notoʻgʻri ulangan. VCC va GND ni tekshiring, TRIG D9 ga, ECHO esa D10 ga ulanganini ham tekshiring.
- Serial Monitor gʻalati belgilar koʻrsatyapti (haqiqiy yigʻish). Unda 9600 bod tanlang, bu
Serial.begin(9600)dagi tezlikning aynan oʻzi. - Robot burchak ostida duch kelgan devorlarga borib urilyapti. Qiya devor tovushni datchikdan chetga qaytaradi. Pardalar kabi yumshoq narsalar esa tovushni yutadi. STOP_CM ni oshiring yoki CRUISE ni kamaytiring.
- Oldida hech narsa yoʻq, lekin robot orqaga yuryapti. Datchik polni koʻrayotgan boʻlishi mumkin, shuning uchun uni tekis oʻrnating. Datchik oldida osilib turgan sim ham aks-sado qaytarishi mumkin.
- Robot stul oyoqlariga yoki quti burchagiga urilyapti. Ingichka narsalar kam tovush qaytaradi, tor dastadan chetdagi narsalar esa koʻrinmaydi. Bu datchikning cheklovi.
- Gʻildirak notoʻgʻri tomonga aylanyapti. Birinchi yurish loyihasidagidek oʻsha motorning ikkita simini almashtiring.
- Robot orqaga yura boshlaganda qayta ishga tushib qolyapti. Oldinga yurishdan birdaniga orqaga oʻtish katta tok talab qiladi va batareya kuchlanishi pasayib ketadi. Yangi batareyalar va qisqa quvvat simlaridan foydalaning. Drayver yonida +12V va GND orasiga 100–470 mkF li kondensator ulang, uning - oyogʻi GND ga ulansin. Robotlar nega qayta ishga tushib qolishi haqidagi maqolani oʻqing.
Keyingi qadamlar
Bu robot faqat «ha yoki yoʻq» deb javob beradi: 25 sm dan yaqinmi yoki yoʻqmi. Keyingi loyiha tezlikni tanlash uchun masofaning oʻzidan foydalanadi: bu masofa saqlovchi robot.
Taymerlar va holatlar haqida millis() va holatlar darsida koʻproq bilib oling. Datchik haqida HC-SR04 darsini va ultratovush datchiklari qanday koʻrishi haqidagi maqolani oʻqing.
Yanada yaxshilang
- Robot Turn holatidan belgilangan vaqt oʻtgach emas, yoʻl ochilishi bilanoq chiqsin, masalan, masofa 50 sm dan oshganda.
- Robot tasodifiy ravishda chapga yoki oʻngga burilsin: random(2) 0 yoki 1 beradi.
- Toʻsiq 25 dan 50 sm gacha masofada boʻlsa, robot sekinroq yursin.
