Пошук найближчого об’єкта

Інший спосіб використання ультразвукового далекоміра – це пошук об’єктів, що знаходяться поблизу.

Спробуйте

Який найефективніший спосіб знайти предмет? Спробуйте!

Повернути і виявити

Щоб спочатку виявити об’єкт, робот може повільно обертатися по колу, постійно опитуючи дальномір. Потім, коли об’єкт виявлено, він може припинити обертання і рухатися до об’єкта.

Як ми можемо визначити, коли об’єкт виявлено? Уявіть, що робот знаходиться в центрі порожньої кімнати, а десь поблизу нього розміщено випадковий об’єкт. Далекомір буде показувати великі значення відстані, поки не досягне об’єкта, після чого значення відстані зменшиться. Саме зміна значень відстані вказує на те, що об’єкт виявлено.

Як ми можемо знайти зміну показань відстані за кожну ітерацію циклу? Ми можемо зберегти попереднє показання відстані у змінній і порівняти його з поточним показанням відстані. Якщо ця зміна перевищує певний поріг, то ми можемо припустити, що об’єкт був виявлений.

Щоб запрограмувати це, ми можемо почати з налаштування швидкості приводних двигунів на обертання в протилежних напрямках, щоб почати обертання робота на місці. Потім, коли зміна показань відстані перевищить поріг, робот може припинити обертання і рухатися до об’єкта.

У наведеному нижче прикладі коду ми використовуємо поріг зміни 30 см.

changeThreshold = 30 # distance change in cm needed to trigger detection

# store initial value for current distance
currentDistance = rangefinder.distance()

# start spinning in place until an object is detected
drivetrain.set_speed(5, -5)

while True: # doesn't actually repeat forever. loop will be broken if an object is detected

    # update previous and current distance
    previousDistance = currentDistance
    currentDistance = rangefinder.distance()

    # if sudden decrease in distance, then an object has been detected
    if previousDistance - currentDistance > changeThreshold:
        break # break out of the while loop

    time.sleep(0.1)

# stop spinning drive motors
drivetrain.stop()
../../_images/detection.png

Покращення точності

Чи бачите ви якісь проблеми в цьому рішенні?

Коли відстань дальноміра опускається нижче порогу, це не означає, що робот знайшов об’єкт, а скоріше, що робот знайшов його край. Отже, робот буде націлюватися на край об’єкта, а не на його центр.

Натомість робот повинен запам’ятати напрямок, в якому він знаходиться, коли виявляє обидва краї. Потім робот може націлитися на центр між цими краями, а отже, і на центр об’єкта. Ми можемо зберегти кожен кут краю у змінних, назвавши їх firstAngle і secondAngle.

Ми вже добре знайомі з поворотом до виявлення краю. Тепер нам потрібно виявити обидва краї. Однак просто скопіювати код виявлення краю було б досить незручно і схильно до помилок, тому давайте створимо функцію для узагальнення цього. Зверніть увагу, що існуючий код виявляє раптове зменшення відстані, але ми хочемо обробляти і раптові збільшення відстані.

Як ми можемо підтримати обидві поведінки в одній функції? Ми можемо передати параметр, щоб вказати, чи хочемо ми виявити збільшення або зменшення відстані! Ми можемо назвати цей параметр isIncrease і передати в нього булеве значення (true або false).

Якщо increase дорівнює True, то ми хочемо виявити збільшення відстані, яке відбувається, коли currentDistance - previousDistance > changeThreshold.

Якщо increase дорівнює False, то ми хочемо виявити зменшення відстані, яке відбувається, коли previousDistance - currentDistance > changeThreshold.

Для більшої гнучкості додамо параметр для порогу зміни.

Ось визначення функції:

def turnUntilEdge(isIncrease, changeThreshold):

    # store initial value for current distance
    currentDistance = rangefinder.distance()

    # start spinning in place until an object is detected
    drivetrain.set_speed(5, -5)

    while True: # doesn't actually repeat forever. loop will be broken if an object is detected

        # update previous and current distance
        previousDistance = currentDistance
        currentDistance = rangefinder.distance()

        if isIncrease and currentDistance - previousDistance > changeThreshold:
            # if sudden increase in distance, then an object has been detected
            break
        elif not isIncrease and previousDistance - currentDistance > changeThreshold:
            # if sudden decrease in distance, then an object has been detected
            break

        time.sleep(0.1)

    # stop spinning drive motors
    drivetrain.stop()
../../_images/detectiondefinition.png

Ось еквівалентний виклик функції до коду повороту та виявлення в попередньому розділі:

turnUntilEdge(False, 30)
../../_images/detectioncall.png

Тепер настав час написати повну програму для виявлення обох країв і повороту до центру.

Впровадження подвійного виявлення країв

Давайте пройдемося по кожному кроку процесу в коді.

Спочатку робот повинен обертатися на місці, поки не виявить перший край, а потім зупинитися. Це просто виклик функції, який ми бачили раніше.

turnUntilEdge(False, 30)
../../_images/detectioncall.png

Далі ми хочемо записати напрямок руху робота до цього першого краю і зберегти його в firstAngle.

firstAngle = imu.get_yaw()
../../_images/firstangle.png

Потім робот повинен знову обертатися на місці, поки не виявить другий край, коли відбудеться раптове збільшення відстані.

turnUntilEdge(True, 30)
../../_images/turntoedgetrue.png

Як тільки робот виявить другий край, він повинен записати його напрямок і зберегти його в secondAngle. Тепер потрібно з’ясувати, на скільки робот повинен відступити, щоб націлитися на центр об’єкта. Ми можемо це зробити, знайшовши різницю між двома кутами і розділивши її на два. Це половина кута між двома краями, і якщо робот відступить на цю відстань, він буде спрямований до центру об’єкта. Давайте збережемо це в змінній під назвою angleToTurn.

secondAngle = imu.get_yaw()
angleToTurn = (firstAngle - secondAngle) / 2
../../_images/angletoturn.png

Нарешті, робот може повернутись настільки, щоб опинитися обличчям до центру об’єкта, і рухатись у його напрямку.

Ось повний код. Зверніть увагу, що для кращої наочності дій робота додано півсекундні паузи:

# turn to first edge
turnUntilEdge(False, 30)

# store angle at first edge
firstAngle = imu.get_yaw()

time.sleep(0.5)

# turn to second edge
turnUntilEdge(True, 30)

# store angle at second edge and calculate angle to turn
secondAngle = imu.get_yaw()
angleToTurn = (firstAngle - secondAngle) / 2

time.sleep(0.5)

# turn to center of object
drivetrain.turn(angleToTurn)
../../_images/fulldualedge.png