برنامهنویسی ربات با پایتون: قدمبهقدم کد ربات جاروبرقی
هر کاری که یک ربات انجام میدهد در یک جمله خلاصه میشود: سنسورها را بخوان، تصمیم بگیر، چرخها را تنظیم کن — و دوباره از اول. این آموزش شما را از یک فایل خالی تا یک الگوریتم رقابتی برای ربات جاروبرقی لیگ خانه هوشمند میبرد، همراه با کد کامل پایتون که میتوانید در برنامهی لیگ اجرا کنید.
هیچ پیشزمینهای در رباتیک لازم نیست. اگر بتوانید در پایتون یک دستور if و یک حلقهی
while بنویسید، همین الان به اندازهی کافی بلدید که این صفحه را تا آخر جلو ببرید. بقیهی
چیزها — سنسور، جهتیابی، استراتژی پوشش — سر جای خودش توضیح داده میشود. اگر هنوز خود مسابقه را
نمیشناسید، اول لیگ ربات جاروبرقی چیست را بخوانید و بعد به این صفحه برگردید.
قبل از خط اول: آمادهسازی
سه چیز لازم دارید و همهشان در بخش شروع سریع صفحهی اصلی جمع شدهاند:
- برنامهی لیگ — نرمافزاری که کل مسابقه داخل آن اجرا میشود. (نصبکنندهی ویندوز · فایل مک)
- ویندوز — SmartHomeLeague-Windows.exe، دوبار کلیک کنید.
- مک — SmartHomeLeague-Mac.zip، بازش کنید و روی فایل داخلش راستکلیک و Open را بزنید.
داخل ZIP، چیزهایی که به شما مربوط میشود اینها هستند:
| مسیر | چیست |
|---|---|
| دکمهی کد پایه | برنامهی شروعِ ردهای که انتخاب کردهاید — نقطهی شروع شما، خط به خط توضیح داده شده |
examples/robot_code_u14.py | نمونهکد ردهی U14 — نقطهی شروع شما |
examples/robot_code_fs.py | نمونهکد ردهی First Step |
examples/robot_code_u19.py | نمونهکد ردهی U19 — شامل GPS و باتری |
controllers/ و protos/ و plugins/ | موتور پشت صحنهی دنیا. معمولاً کاری با اینها ندارید |
برای اینکه ربات راه بیفتد: برنامه را باز کنید ← ردهی خود را انتخاب کنید ← فایل
ردهی خود را انتخاب کنید ← حالت مسابقه ← دکمهی کد پایه را بزنید ← یکی از کدهای
پوشهی examples/ را به ربات بدهید ← دکمهی را فشار دهید. ربات شروع به حرکت میکند.
از این لحظه میتوانید رفتارش را عوض کنید.
مدل ذهنی: کد شما یک حلقه است
این تنها ایدهای است که با فهمیدنش همهی کدهای ربات جا میافتند، و دقیقاً همان چیزی است که مبتدیها بیشترین مقاومت را در برابرش دارند. برنامهی شما یک داستان نیست که یک بار از بالا تا پایین اجرا شود. یک قطعهکد کوتاه است که شبیهساز آن را بارها و بارها، دهها بار در ثانیه، اجرا میکند. هر دور از این حلقه یک «لحظه» برای ربات است: نگاه میکند، یک آن فکر میکند و چرخها را کمی میچرخاند.
یک لحظه، باز شده، این شکلی است:
- بخوان — سنسورهای فاصله چه میگویند؟ رو به کدام طرف هستم؟
- تصمیم بگیر — چیزی سر راهم هست؟ مستقیم بروم، منحرف شوم یا بچرخم؟
- عمل کن — یک سرعت به چرخ چپ بده و یک سرعت به چرخ راست.
کل کار همین است. هر الگوریتم تمیزکاری هوشمندی که در این صفحه میبینید، فقط یک پاسخ متفاوت به مرحلهی دوم است. این کوچکترین کنترلری است که اجرا میشود — اسکلتی که همهی مثالهای بعدی روی آن ساخته میشوند:
حلقهی کنترل خالی
# This whole file IS one moment for the robot. # The app runs it again ten times every second. wheelleft = 0 # speed of the left wheel (-25 .. 25) wheelright = 0 # speed of the right wheel (-25 .. 25)
برنامهی لیگ ضربان قلب کار است: فایل شما را ده بار در ثانیه اجرا میکند. یعنی «دنیا را ۳۲
میلیثانیه جلو ببر و بعد کنترل را به من برگردان». وقتی شبیهسازی تمام شود مقدار -1
برمیگرداند و به همین دلیل شرط حلقه اینطور نوشته شده است.
زمان شبیهسازی فقط وقتی جلو میرود که شما این تابع را صدا بزنید.
مهمترین نتیجهی این ماجرا
ربات شما نمیتواند منتظر بماند. نه time.sleep(1) وجود دارد و نه دستوری به معنای
«یک ثانیه به چپ بچرخ». اگر بخوابید، دنیا هم با شما میخوابد. اگر حلقهی while تودرتویی
بنویسید که تمام نمیشود، ربات قفل میکند و چرخها با همان
سرعت قبلی میمانند.
راهحل یک شمارنده است که بین دورها باقی میماند. برای اینکه ۰٫۶۴ ثانیه بچرخید،
turning = 20 میگذارید (چون ۲۰ ضربدر ۳۲ میلیثانیه تقریباً ۰٫۶۴ ثانیه میشود) و بعد هر
دور یکی از آن کم میکنید تا به صفر برسد. این الگو تقریباً در همهی مثالهای پایین تکرار میشود —
نسخهی رباتیکیِ «یادم باشد داشتم چه کار میکردم».
چرخاندن چرخها
ربات دو چرخ محرک دارد، یکی چپ و یکی راست. شما هیچوقت نمیگویید «برو جلو»؛ میگویید «چرخ چپ با این سرعت، چرخ راست با آن سرعت»، و همین اختلاف بین دو چرخ است که فرمان میدهد:
| چرخ چپ | چرخ راست | نتیجه |
|---|---|---|
| حداکثر، جلو | حداکثر، جلو | حرکت مستقیم رو به جلو با بیشترین سرعت |
| حداکثر، عقب | حداکثر، عقب | حرکت مستقیم به عقب |
| حداکثر، جلو | حداکثر، عقب | چرخش در جا، ساعتگرد |
| حداکثر، عقب | حداکثر، جلو | چرخش در جا، پادساعتگرد |
| حداکثر، جلو | نصف، جلو | یک قوس ملایم به سمت راست |
گرفتن موتورها و حرکت به جلو
# Drive straight ahead, for ever. MAX_SPEED = 25 # the fastest a wheel can turn wheelleft = MAX_SPEED wheelright = MAX_SPEED
تنظیم دو عدد چرخ سادهترین کار ممکن است، و روش استاندارد برنامه برای گفتن این
جمله به موتور است: «کاری به رسیدن به یک زاویهی مشخص نداشته باش، فقط با سرعتی که میدهم بچرخ».
فراموش کردن همین یک خط، شمارهیک دلیلی است که ربات یک مبتدی تکان نمیخورد.
نام دقیق دستگاهها و بیشینهی سرعت را از نمونهکد داخل پوشهی examples/ بردارید — آن فایل
برای همین رباتی نوشته شده که شما دارید میرانید.
خواندن ۸ سنسور فاصله و قطبنمای IMU
در ردههای First Step و U14 ربات هشت سنسور فاصله دارد که دور تا دور بدنهاش چیده شدهاند، بهاضافهی یک واحد اینرسی (IMU) که نقش قطبنما را بازی میکند. ردهی U19 علاوه بر اینها یک GPS و یک باتری هم دارد. کل دید ربات از دنیا همین است — نه دوربینی هست، نه نقشهای، و نه فهرستی از اینکه مبلمان کجاست. هر چیزی که الگوریتم شما بداند، باید از همین چند عدد بیرون بکشد.
هشت سنسور را مثل یک حلقه تصور کنید که هرکدام به بیرون خیره شدهاند:
حلقهی سنسورها از نمای بالا
ds0 ds7 <- front ds1 ds6 ^ ds2 (robot) ds5 ds3 ds4 <- back
اما این نقشه را همینطوری باور نکنید. خودتان تأییدش کنید، چون ترتیب شمارهها دقیقاً از آن جزئیاتی است که بیسروصدا یک بعدازظهر را نابود میکند: ربات را آرام به سمت دیوار ببرید و ببینید کدام عددها تکان میخورند. دو چیز دیگر هم باید قبل از نوشتن هر منطقی کشف کنید:
- عدد به کدام سمت میرود؟ بعضی سنسورهای فاصله یک شدت خام برمیگردانند که با نزدیکشدن مانع بالا میرود؛ بعضی دیگر خودِ فاصله را میدهند که پایین میآید. هر دو طبیعی است. مقدارها را چاپ کنید، ربات را تا کنار دیوار ببرید و نگاه کنید.
- «نزدیک» یعنی چه عددی؟ هیچ آستانهی جهانشمولی وجود ندارد. عدد را وقتی جلوی ربات باز است یادداشت کنید، بعد عدد را درست قبل از برخورد یادداشت کنید و چیزی بین این دو انتخاب کنید. همین یک ثابت، پرتنظیمترین خط کنترلر شما خواهد بود.
IMU به سؤال دیگری جواب میدهد: رو به کدام طرف هستم؟ زاویهی yaw آن بر حسب رادیان بین منفی پی و مثبت پی برمیگردد. هر وقت خواستید آن را بخوانید به درجه تبدیل کنید، ولی داخل محاسبات همان رادیان را نگه دارید.
ابزار اندازهگیری: همهچیز را فعال کن و چاپ کن
# Every sensor is simply a variable. Nothing to enable, nothing to read out. # # front distance straight ahead, in centimetres (smaller = closer) # frontleft the same, ahead and to the left # frontright the same, ahead and to the right # bumperfront ۱ وقتی نیمهی جلوی حلقهی سپر برخورد کرده # bumperback ۱ وقتی نیمهی عقب برخورد کرده # bumper ۱ وقتی هرکدام از آن دو برخورد کرده باشد # color the colour of the floor just ahead # heading which way the robot faces, 0..359 degrees (U14 and U19) if front < 40: turnleft(1) else: forward(1) if step % 10 == 0: # ~3 lines a second, not 30 print(values, "heading", heading)
این کد را اجرا کنید و در همان حال ربات را با ماوس در صحنه بکشید و ببرید. در دو دقیقه چیدمان سنسورها، عددهای نزدیک و دور، و اینکه هر جهت قطبنما به کدام دیوار میخورد را میفهمید. هر ساعتی که اینجا بگذارید، سه ساعت رفع اشکال گیجکننده را بعداً از شما میگیرد.
دو تلهی سنسورها
حدس زدن آستانه. «نزدیک» یک حس نیست، یک عدد است. پنل سنسورها را باز کنید و ببینید در همان لحظهای که میخواهید ربات بپیچد، سنسور دقیقاً چه عددی میگوید. از همان عدد استفاده کنید. یک دورهی
نمونهبرداری فعالش کنید. خواندن یک سنسور خواب یک عدد ثابت میدهد که هیچوقت عوض نمیشود و شما یک ساعت
دستورهای if بیگناه خودتان را متهم میکنید.
فراموش کردن واحدها. سنسورهای فاصله سانتیمتر میگویند و عدد کوچکتر یعنی نزدیکتر — برعکس چیزی که بیشتر تازهکارها بار اول پر
میشود. هیچوقت getValue() را بالای حلقه صدا نزنید.
اولین کنترلر واقعی: حرکت و چرخش کنار دیوار
این یک کنترلر کامل و کارکننده است. مستقیم جلو میرود و هر وقت سنسورهای جلو دیواری دیدند کمی عقب
میکشد و به اندازهی تعداد مشخصی دور میچرخد و بعد راهش را ادامه میدهد. همهی اجزایش را قبلاً دیدهاید:
حلقه، شمارندهای که بین دورها باقی میماند، و زنجیرهای از elif که اولین شرط درست برنده است.
کنترلر شماره ۱ — مستقیم برو تا چیزی سد راه شود
# Controller #1 - drive, and turn away from whatever blocks the way. # Remember: this file runs ten times a second, so 10 = one second. NEAR = 40 # centimetres. "A wall is close." Find YOUR number TURN_STEPS = 7 # how many passes the spin lasts (7 = 0.7 s) if timer > 0: # 1) still finishing the previous turn timer -= 1 wheelleft, wheelright = 25, -25 elif front < NEAR: # 2) wall dead ahead -> spin away timer = TURN_STEPS wheelleft, wheelright = -10, -10 elif frontleft < NEAR: # 3) something on the left -> veer right wheelleft, wheelright = 25, 8 elif frontright < NEAR: # 4) something on the right -> veer left wheelleft, wheelright = 8, 25 else: # 5) clear road -> full speed wheelleft, wheelright = 25, 25
سه نکته ارزش مکث دارند، چون در هر کنترلری که در عمرتان بنویسید تکرار میشوند:
- شرط چرخش اول از همه میآید. اگر شاخهی «آیا هنوز مشغولم؟» پایین بود، دیواری که درست جلوی ربات است در هر دور چرخش را از نو شروع میکرد و ربات تا آخر مسابقه به دیوار میسایید. کار نیمهتمام همیشه قبل از کار جدید بررسی میشود.
- استفاده از
max()روی دو سنسور. گرفتن بزرگترِ دو سنسور جلو باعث میشود دیواری که با زاویه نزدیک میشود هم شاخه را فعال کند. اگر یکی از آنها را نادیده بگیرید، یک نقطهکور به اندازهی پایهی صندلی میسازید. - همیشه به یک سمت بچرخید. کد بالا کنار دیوار همیشه به یک جهت میچرخد. اینکه بر اساس اینکه کدام سنسور فعال شده گاهی چپ و گاهی راست بچرخید در نگاه اول باهوشانه است، ولی رباتی میسازد که در گوشهها میلرزد و گیر میکند. ثبات از زیرکی بهتر است.
دکمهی شروع را بزنید و تماشا کنید. حالا رباتی دارید که تمیز میکند — بد، ولی واقعی. از اینجا به بعد همهچیز دربارهی این است که در همان زمان، کف بیشتری تمیز شود.
سه راه برای اینکه واقعاً تمیز کند
کنترلر شماره ۱ پرسه میزند. در یک اتاق کوچک بالاخره بیشتر جاها را لمس میکند، ولی مدام از روی مسیر خودش رد میشود و گوشههای کاملی را دستنخورده جا میگذارد. رباتهای جاروبرقی واقعی چند رفتار ساده را با هم ترکیب میکنند و ربات شما هم باید همین کار را بکند.
۱. برخورد تصادفی — یک خط بهتر از هیچ
ارزانترین ارتقا: وقتی به چیزی خوردید، به اندازهی تصادفی و در جهت تصادفی بچرخید. زاویهی چرخش ثابت میتواند ربات را در چرخهای گرفتار کند که تا آخر مسابقه همان مثلث را دوباره و دوباره طی کند؛ تصادفیبودن این تقارن را میشکند. این هنوز یک استراتژی نیست، ولی بهعنوان رفتار پشتیبان برای وقتی که رفتار باهوشتر گیج میشود، عالی است.
وصلهای برای کنترلر شماره ۱ — چرخش فرار تصادفی
import random turning = 0 direction = 1 # +1 = spin one way, -1 = the other # ... inside the loop, replace the two turn branches with: if turning > 0: turning -= 1 ls, rs = MAX_SPEED * direction, -MAX_SPEED * direction elif front > NEAR: turning = random.randint(10, 35) # 0.32 s .. 1.12 s of spinning direction = random.choice((-1, 1)) # a coin flip ls, rs = -MAX_SPEED * 0.4, -MAX_SPEED * 0.4
۲. دیوارگرد — اولین رفتاری که واقعاً زمین را پوشش میدهد
دیوارگردی یعنی نگه داشتن دیوار در یک فاصلهی ثابت از یک طرف ربات و حرکت در امتداد آن. یک بار دور اتاق بگردید و کل محیط اتاق، از جمله گوشههایی که برخورد تصادفی هیچوقت به آنها نمیرسد، تمیز شده است. ضمناً راه مطمئنی برای رفتن از اتاقی به اتاق دیگر است: اگر به اندازهی کافی دیوار را دنبال کنید، بالاخره از هر دری رد میشوید.
ترفند این است که بهجای فکر کردن با if، با خطا فکر کنید: سنسور کناری چقدر از جایی که
میخواهم فاصله دارد؟ به تناسب همان خطا فرمان بدهید. خیلی نزدیک است، کمی دور شوید؛ خیلی دور است، کمی
نزدیک شوید. اسم این کار کنترلر تناسبی است، سادهترین و پرکاربردترین ایدهی کل مهندسی کنترل، و در
پایتون چهار خط است.
کنترلر شماره ۲ — دنبال کردن دیوار سمت راست
# Wall following - hug the wall on the right at a steady distance. TARGET = 30 # centimetres you want to keep on the right GAIN = 0.8 # how hard the robot reacts to being off target CRUISE = 20 if front < 35: # inside corner -> spin left, hard wheelleft, wheelright = -15, 15 else: error = (TARGET - frontright) * GAIN # positive = too close to the wall wheelleft = max(-25, min(25, CRUISE - error)) wheelright = max(-25, min(25, CRUISE + error))
تنظیم GAIN تمام بازی است. خیلی کوچک باشد، ربات تنبلانه از دیوار دور میشود و گمش
میکند. خیلی بزرگ باشد، بهشدت زیگزاگ میرود و هر بار از هدف رد میشود — همان نوسان کلاسیکی که روی
صفحه فوراً میبینید. از همین عدد شروع کنید، بعد آن را دو برابر یا نصف کنید و تغییر را تماشا کنید.
عوض کردن یک ثابت در هر مرحله و دیدن نتیجه، خودِ مهندسی رباتیک است؛ میانبری وجود ندارد.
تنها کاری که دیوارگرد بلد نیست، تمیز کردن وسط اتاق است. اگر به حال خودش رها شود، کل مسابقه دور محیط میچرخد و امتیاز کمی میگیرد. به یک شریک نیاز دارد.
۳. مارپیچ — برای وسط بازِ اتاق
مارپیچ یعنی دایرهای که شعاعش در هر دور کمی بزرگتر میشود. آن را در یک فضای باز شروع کنید تا در حلقههای تنگ به بیرون باز شود و یک ناحیه را تقریباً بینقص و بدون رفتوبرگشت اضافی پوشش دهد. این دقیقاً مکمل دیوارگرد است: یکی لبهها را میگیرد، دیگری وسط را.
کنترلر شماره ۳ — مارپیچ بازشونده
# The spiral - open circles that widen until something gets in the way. GROWTH = 0.01 # how fast the circle opens up if timer > 0: # bouncing away after a bump timer -= 1 wheelleft, wheelright = -10, -10 elif front < 35: # the spiral ran into something inner = 0.15 # reset it, then bounce and re-spiral timer = 8 else: inner = min(1.0, inner + GROWTH) # every pass the circle opens a little wheelleft = 25 wheelright = 25 * inner
کنار هم گذاشتنشان: یک کلید حالت
هیچکدام از این سه رفتار بهتنهایی کافی نیست. یک کنترلر رقابتی هر سه را نگه میدارد و بینشان جابهجا میشود — و منطق این جابهجایی هم فقط یک شمارندهی دیگر است:
اسکلت استراتژی
# One robot, three behaviours, switched on a clock. # Remember: ten passes make one second. SPIRAL, WALL, BOUNCE = 0, 1, 2 mode_steps += 1 if mode == SPIRAL: do_spiral() if mode_steps > 130: # ~13 s of spiralling is plenty mode, mode_steps = WALL, 0 elif mode == WALL: do_wall_follow() if mode_steps > 290: # ~29 s along the wall, then shake it up mode, mode_steps = BOUNCE, 0 else: do_random_bounce() if mode_steps > 100: mode, mode_steps = SPIRAL, 0
این عددها حدسی هستند و باید هم باشند. عوضشان کنید، مسابقه را اجرا کنید، به امتیاز نگاه کنید و دوباره عوضشان کنید. تیمهایی خوب کار میکنند که پنجاه بار مسابقه را اجرا کردهاند، نه آنهایی که باهوشترین پیشنویس اول را نوشتهاند.
فقط U19: مسیریابی با GPS
ربات ردهی U19 یک GPS هم دارد که موقعیت دقیقش را در خانه گزارش میکند. این ماجرا را عوض میکند: بهجای اینکه فقط به چیزی که جلوی رویتان است واکنش نشان دهید، میتوانید تصمیم بگیرید که به یک نقطهی مشخص بروید — گوشهی دور، اتاقی که هنوز نرفتهاید، یا یک پد شارژ.
رفتن به یک نقطه دو عدد لازم دارد: جهت از اینجا تا هدف، و جهتی که الان رو به آن هستید. اختلاف این دو، خطای فرمان شماست. تنها تکهی ریاضی واقعی این صفحه، همین است که چطور دو زاویه را بیخطر از هم کم کنیم.
چرا زاویهها را نمیشود همینطوری کم کرد
فرض کنید رو به ۳۵۰ درجه ایستادهاید و هدف در ۱۰ درجه است. تفریق ساده ۳۴۰ درجه میدهد، پس ربات
سادهلوح تقریباً یک دور کامل میچرخد — در حالی که جواب درست یک چرخش ۲۰ درجهای به راست است. راهحل یک
خط کلاسیک است: اختلاف را به سینوس و کسینوس تبدیل کنید و بگذارید atan2 دوباره آن را به
زاویه برگرداند. نتیجه همیشه کوتاهترین مسیر چرخش است، بین منفی پی و مثبت پی، و در نقطهی
بازگشت از ۳۵۹ به صفر هم نمیشکند.
کنترلر شماره ۴ — رفتن به یک مختصات (U19)
# U19 only: the robot knows where it is (x, y) and which way it faces. # The app can drive there for you - or you can steer it yourself. # --- the easy way: hand the job to the app --------------------- goto(gx, gy) # start driving towards that point if atgoal == 1: # becomes 1 once the robot has arrived stopgoto() # --- the hand-written way, if you want to own the steering ----- # distto(gx, gy) distance to the point, in centimetres # angleto(gx, gy) the turn you still need, -180..180 degrees # (positive = the goal is to your left) turn = angleto(gx, gy) if distto(gx, gy) < 15: # arrived wheelleft, wheelright = 0, 0 elif turn > 12: # goal is to the left -> spin left wheelleft, wheelright = -12, 12 elif turn < -12: # goal is to the right -> spin right wheelleft, wheelright = 12, -12 else: # lined up -> drive at it wheelleft, wheelright = 25, 25
هشداری که برای تیمها گران تمام میشود: تابع drive_to فقط خط مستقیم را دنبال میکند و
بس. نمیداند که مبلی سر راه است. همیشه بررسی مانع را بالای آن نگه دارید — اگر سنسورهای جلو
روشن شدند، سفر را رها کنید، فرار کنید و در دور بعد دوباره صادرش کنید. مسیریابی و دور زدن مانع دو کار
متفاوتاند و هر دو باید سرِ پست باشند.
قبل از اینکه به عددهای GPS اعتماد کنید، بررسی کنید که واقعاً به همان ترتیب و واحدی که فکر میکنید
برمیگردند؛ مقدار gps.getValues() را چاپ کنید و ربات را به یک گوشهی مشخص ببرید. در ردهی
U19 نقشهی خانه ثابت است و فقط جای اشیا بین راندها عوض میشود، پس داشتن مجموعهای از نقاط مسیر از پیش
تعیینشده اشکالی ندارد — ولی کنترلری که فقط این نقاط را دنبال کند و سنسورهایش را نادیده بگیرد،
کل مسابقه را صرف هل دادن یک صندلی میکند.
فقط U19: باتری و پدهای شارژ
ربات ردهی U19 با باتری کار میکند و ناحیههای سبز روی کف، پدهای شارژ بیسیم هستند. این موضوع مسابقه را به یک مسئلهی مدیریت منابع تبدیل میکند: هر ثانیهای که صرف شارژ میشود، ثانیهای است که تمیزکاری نمیشود، اما باتری خالی مسابقهی شما را زودتر تمام میکند و همهی امتیازهای بعد از آن لحظه را میگیرد.
پاییدن باتری و رفتن به نزدیکترین پد
# U19 only: the battery drains as you drive and refills on the pad. # battery is a percentage, 0..100. dockx / docky are the pad's coordinates. LOW = 25 # go and charge below 25% if battery < LOW: goto(dockx, docky) # charging beats cleaning right now elif atgoal == 1 and battery > 90: stopgoto() # topped up - back to work clean() else: clean() # your normal cleaning behaviour
دو عادت، استراتژی انرژی خوب را از بد جدا میکند. اول، زود بروید — خود سفر تا پد هم انرژی میخورد و تمام کردن باتری وسط راه بدترین اتفاق ممکن است. دوم، از روی عادت تا آخر شارژ نکنید. اگر مسابقه دارد تمام میشود، آن شارژ هیچوقت خرج نخواهد شد؛ بیرون بمانید و تمیز کنید. خواندن زمان باقیماندهی مسابقه و مقایسهاش با هزینهی سفر، دقیقاً همان قضاوتی است که یک راند نزدیک را میبرد.
امتیازدهی واقعاً چه چیزی را پاداش میدهد؟
این بخش را قبل از بهینهسازی هر چیزی بخوانید، چون تعیین میکند «بهتر» اصلاً یعنی چه. امتیاز بر اساس میزان تمیزکاری است: پوشش کاشیهای کف و سطح پاکیزگی بهدستآمده در زمان مسابقه. چند نتیجه مستقیماً از همین جمله بیرون میآید:
- مسافت طیشده هیچ ارزشی ندارد. رباتی که با سرعت یک راهرو را بالا و پایین میکند، همانقدر امتیاز میگیرد که رباتی که سر جایش ایستاده. فقط کفِ جدید شمرده میشود.
- پوشش یک مسئلهی جستوجو است، نه مسئلهی سرعت. سؤال این نیست که «چقدر تند میتوانم برانم»، سؤال این است که «چطور تضمین کنم یک بار به همهجای خانه سر میزنم». منظم بودن از تند بودن مهمتر است.
- نقشه در هر راند عوض میشود. مسیری که برای یک خانه هاردکد شده باشد در خانهی بعدی بیارزش است. (در U19 خانه ثابت است و اشیا جابهجا میشوند — همان درس، با دُز کمتر.) قبل از اینکه به امتیازتان اعتماد کنید، کنترلر را روی بیش از یک دنیا امتحان کنید.
- گیر کردن فاجعه است. رباتی که نود ثانیهی آخر زیر یک صندلی گیر کرده باشد، خیلی بیشتر از یک ربات کمی دستوپاچلفتی که مدام حرکت میکند امتیاز از دست میدهد. یک مکانیزم فرار بسازید: اگر موقعیت یا مقدار سنسورها چند ثانیه تقریباً ثابت ماند، بدون چون و چرا عقب بروید و تند بچرخید.
- زمان یک منبع است. اول کف باز را که ارزان است جارو کنید و گوشههای دردسرساز را برای بعد بگذارید. یک نقشهی ساده که تمام میشود، از یک نقشهی بینقص که وقت کم میآورد بهتر است.
یک هدف ذهنی مفید، الگوی چمنزنی است: رفتوبرگشتهای موازی و بلند در عرض اتاق که هر بار به اندازهی پهنای ربات جابهجا میشوند. این همان کاری است که آدمها موقع جاروبرقی کشیدن بهطور غریزی میکنند و به پوشش بهینه خیلی نزدیک است. با همان IMU هم میشود تقریبی از آن ساخت: روی یک جهت ثابت برو تا دیوار، ۹۰ درجه بچرخ، یک پرش کوتاه برو، دوباره ۹۰ درجه به همان سمت بچرخ، و تکرار کن.
در تمام این مسیر جوانمردانه بازی کنید. قوانین، کد اصیل میخواهند — سرقت ادبی ممنوع است — و تصمیم داوران و برگزارکنندگان نهایی است. برداشتن یک ایده از این صفحه دقیقاً کاری است که این صفحه برایش نوشته شده؛ فرستادن کنترلر شخص دیگری نه.
رفع اشکال: چاپ کن، نگاه کن، هر بار یک چیز
نمیشود یک ربات را با دیباگر خطبهخط جلو برد، چون دنیا مؤدبانه منتظر فکر کردن شما نمیماند. ابزاری که جایش را میگیرد خط چاپ است — یک خط وضعیت کوتاه که چند بار در ثانیه چاپ میشود و میگوید ربات همین حالا چه باوری دارد:
تنها خط چاپی که ارزش نگه داشتن دارد
if step % 10 == 0:
print("F", round(front), " L", round(left_side), " R", round(right_side),
" mode", mode, " turning", turning)
و بعد اینطور کار کنید:
- به عددها نگاه کنید، نه به ربات. وقتی چیزی خراب میشود، معمولاً کنسول از قبل جواب را نوشته
است. اگر
frontعدد ۱۲۰۰ بوده و ربات با خیال راحت رفته توی دیوار، ثابتNEARشما اشتباه است — نه منطقتان. - کم چاپ کنید. سی خط در ثانیه نویزی است که خوانده نمیشود؛
step % 10حدود سه خط میدهد که چشم انسان واقعاً میتواند دنبالش کند. - هر بار فقط یک ثابت را عوض کنید. دو تا را عوض کنید و دیگر نمیدانید کدام کمک کرد. قبل از تغییر، مقدار قبلی را در یک کامنت بنویسید.
- سرعت مسابقه را کم کنید. برنامه با سرعت ۱، ۲، ۴ و ۸ برابر اجرا میشود — روی ۱ بگذارید و تماشا کنید. رفتاری که با سرعت کامل مثل لرزش تصادفی به نظر میرسد، با یکچهارم سرعت اغلب کاملاً واضح است.
- اول بازتولید، بعد رفع. اگر ربات در یک گوشهی خاص گیر میکند، دوباره همانجا بگذاریدش و عددهای همان نقطه را ببینید، بهجای حدس زدن از روی حافظه.
اشتباههایی که همه مرتکب میشوند
| نشانه | علت معمول |
|---|---|
| ربات اصلاً حرکت نمیکند | wheelleft و wheelright را تنظیم نکردهاید، یا خطایی فایل را پیش از رسیدن به آنها متوقف کرده — خط قرمز خطا زیر کادر کد را ببینید |
| یک شرط هیچوقت اجرا نمیشود | آستانهی شما اشتباه است. پنل سنسورها را باز کنید و ببینید سنسور در آن لحظه واقعاً چه میگوید |
| ربات قفل میکند | یک حلقهی while یا sleep() داخل فایل شما. فایل باید تا آخر اجرا شود تا برنامه بتواند دوباره صدایش بزند |
| ربات کنار دیوار میلرزد | شاخهی «آیا دارم میچرخم؟» اول نیست، پس چرخش در هر دور از نو شروع میشود |
| چرخش تقریباً درست است — گاهی | دارید زمان میشمارید بهجای خواندن IMU. چرخش زمانی با کوچکترین تغییر سرعت یا اصطکاک منحرف میشود |
| در راهرو مدام چپ و راست میرود | مقدار GAIN دیوارگرد زیاد است — نصفش کنید |
| روی یک نقشه خوب کار میکند، روی نقشهی بعدی نه | ثابتها برای یک خانه تنظیم شدهاند، یا مسیر هاردکد شده. نقشه هر راند عوض میشود |
| تغییرهای فایل هیچ اثری ندارند | فایل ویرایششده را پیش از زدن شروع دوباره بارگذاری نکردهاید |
| در تمرین امتیاز عالی، در مسابقه ضعیف | همیشه فقط از یک نقطه و یک زاویهی شروع تست کردهاید |
تمرین همین ایدهها بدون هیچ نصبی
اگر میخواهید پیش از دانلود هر چیزی حس حلقهی کنترل را بچشید — یا معلم هستید و روی کامپیوترهای مدرسه اجازهی نصب نرمافزار ندارید — همین مسابقه در مرورگر هم باز میشود. کاملاً داخل صفحهی وب اجرا میشود: دو ربات، سنسورهای فاصلهی واقعی، یک سنسور رنگ که میگوید کف جلوی رو قبلاً تمیز شده یا نه، و همان حلقهی «سنسور را بخوان، چرخ را تنظیم کن» که با پایتون نوشته میشود.
هر چیزی که بالا یاد گرفتید مستقیماً منتقل میشود. بلوک if timer > 0: در بازی همان الگوی
شمارندهی turning در کنترلر شماره ۱ است؛ دیوارگرد، مارپیچ و برخورد تصادفی هم آنجا کار
میکنند — همان خانه، همان ربات و همان پایتون است، پس سریعترین راه ممکن برای ساختن شهود است.
اجرای مسابقه در مرورگر خانهی مسابقه، رده به رده
ساختن نقشهی خودتان در مپساز، راهی دستکمگرفتهشده برای تست کنترلر است. خانهای طراحی کنید با یک راهروی باریک بدقلق، یک دسته پایهی صندلی و یک اتاق بنبست؛ ضعفهای الگوریتمتان را در چند دقیقه پیدا میکنید، بهجای اینکه روز مسابقه کشفشان کنید.
سوالات پرتکرار
ربات جاروبرقی را با چه زبانی برنامهنویسی کنیم؟
پایتون. نمونهکدهای داخل ZIP پروژه — examples/robot_code_u14.py و robot_code_fs.py و robot_code_u19.py — فایلهای سادهی پایتون هستند و کل مسابقه را میشود فقط با متغیر، شرط، حلقه و تابع انجام داد. در ردهی U19 استفاده از زبانهای پیشرفتهتر هم مجاز است.
چقدر باید پایتون بلد باشم تا شروع کنم؟
متغیر، if/elif/else، حلقهی while، لیست و تابع برای نوشتن یک کنترلر رقابتی کافی است. نه کلاس لازم است، نه دکوریتور، نه کتابخانهی ریاضی خارجی. تقریباً همهی کنترلرهای مبتدی کمتر از هشتاد خط هستند.
چرا ربات نمیتواند با time.sleep منتظر بماند؟
فایل شما یک بار اجرا میشود و تمام میشود؛ برنامه ده بار در ثانیه دوباره صدایش میزند. خوابیدن یا حلقه زدن داخل آن، ربات را قفل میکند و چرخها را با سرعت قبلی رها میکند. بهجایش یک شمارنده مثل timer = 20 بگذارید و هر دور یکی از آن کم کنید.
چطور دقیقاً به اندازهی چند درجه بچرخم؟
زمان نشمارید — IMU را بخوانید. زاویهی yaw را قبل از چرخش ثبت کنید و تا وقتی کوتاهترین اختلاف زاویهای تا هدف کوچک نشده بچرخید. این اختلاف را با atan2(sin(target - actual), cos(target - actual)) حساب کنید تا در نقطهی بازگشت زاویه هم درست بماند.
امتیاز چطور حساب میشود؟
میزان تمیزکاری: پوشش کاشیهای کف و سطح پاکیزگی بهدستآمده در زمان مسابقه. مسافت طیشده بهتنهایی هیچ امتیازی ندارد، پس الگوریتمی که مدام از روی کف تمیزشده رد میشود امتیاز کمی میگیرد. تصویر کامل را در معرفی لیگ ببینید و برای اینکه امتیازهای واقعی چه شکلی بودند به جوایز و نتایج مسابقات سر بزنید.
میشود بدون نصب هیچ نرمافزاری همین مفاهیم را تمرین کرد؟
بله. نسخهی مرورگری در همین سایت کاملاً داخل صفحهی وب اجرا میشود و دقیقاً همان مدل ذهنی را دارد: سنسور بخوان، سرعت چرخ بده، یک دور در هر لحظه. این بازی یک رقابت جانبی است و لیگ اصلی نیست، اما سریعترین راه برای حس کردن رفتار حلقهی کنترل است.
نمونهکدها و نقشهها را از کجا بگیرم؟
از مخزن رسمی: github.com/smart-home-league/simulation یا مستقیم از بخش شروع سریع.
وقتی گیر کردم کجا بپرسم؟
کانال تلگرام لیگ t.me/firasmarthome و سرور دیسکورد. سایت رسمی لیگ smarthomerobot.ir هم یک پایگاه دانش دارد.
قدم بعدی
حالا تصویر کامل را دارید: حلقه، سنسورها، چهار کنترلر کارکننده و یک راه فکر کردن دربارهی امتیاز. قدم بعدی خواندن بیشتر نیست — باز کردن برنامه است، عمداً خراب کردن کنترلر شماره ۱، و فهمیدن اینکه هر عدد چه کار میکند.
- هنوز نصب نکردهاید؟ راهنمای نصب دانلود برنامه، فایل پروژه و اولین اجرا را قدمبهقدم توضیح میدهد.
- ببینید دقیقاً چه چیزی امتیاز دارد. قوانین کامل مسابقه و معرفی لیگ میگویند داوران چه چیزی را اندازه میگیرند.
- معلم، مربی یا والد هستید؟ راهنمای مدارس و مربیان توضیح میدهد چطور این لیگ را در کلاس یا باشگاه رباتیک اجرا کنید.
جوایز و نتایج مسابقات لیگ منتشر میشوند. از آنها بهعنوان معیار استفاده کنید: کنترلر خودتان را روی همان دنیاها اجرا کنید و ببینید کجا میایستادید.
دانلود ابزارها و شروع لیگ ربات جاروبرقی چیست تمرین در مرورگر English version
