Управляемый малиновый PI автомобиль (код для 6-летнего возраста)

Я пытался показать своей 6-летней дочери способ построить и создать простую игрушечную машину. У нас есть 4-моторный комплект шасси, малиновый PI и моторизированный щит, который может управлять этими 4 двигателями. Мы поставили блок питания и отдельную батарею для двигателей:

                                             Â

(Я также перепутал некоторые моторы и штырьки на плате, что привело к тому, что некоторые двигатели приняли «назад» как «вперед»)

Конечно, этот код можно объяснить 6-летнему, так как она все еще ожидает, что принтер напечатает что-то, когда я покажу ей команду Car Python, но как бы вы изменили этот код, чтобы сделать его более понятным для ребенка?

Я также был бы признателен за любые вопросы о организации и качестве кода, поскольку следующим шагом будет управление автомобилем «через Bluetooth».

41 голос | спросил alecxe 6 WedEurope/Moscow2017-12-06T05:22:23+03:00Europe/Moscow12bEurope/MoscowWed, 06 Dec 2017 05:22:23 +0300 2017, 05:22:23

6 ответов


33

Реализация forward ужасна. Единственный правильный способ выразить это - заставить все четыре двигателя продвигаться вперед. Как аналог, как бы вы отреагировали, если ваш производитель клавиатуры сказал вам «о, извините, мы допустили ошибку, вам нужно набрать каждую вторую букву в верхнем регистре, чтобы получить строчное слово»? Это было бы неинтуитивно.

API, который вы предоставляете, должен быть максимально простым и простым. Вы не хотите, чтобы ваша дочь узнала, что «программисты совершают ошибки и ленивы об их фиксации, поэтому мы должны жить с этим».

В какой форме ваша дочь должна изменить текущую программу? Вероятно, вы должны добавить некоторый пример кода для ожидания некоторого времени, чтобы разрешить составленные команды, такие как «forward, wait 1s, right, 1s, backward».

Ваш код, кажется, сбой с исключением при нажатии неожиданного ключа. Это неприемлемо для робота, поскольку это может нанести вред людям или домашним животным при бесконтрольном (см. Законы Азимова).

Использование raw_input выглядит так, как будто пользователь должен нажать клавишу Enter, чтобы подтвердить каждое действие. Это слишком тяжелое бремя. Управление автомобилем должно быть простым и немедленным, чтобы предотвратить несчастные случаи. Как бы вы себя чувствовали, если бы ваш взрослый уличный автомобиль попросил вас подтвердить каждый шаг на газе или тормозе?

ответил Roland Illig 6 WedEurope/Moscow2017-12-06T09:58:59+03:00Europe/Moscow12bEurope/MoscowWed, 06 Dec 2017 09:58:59 +0300 2017, 09:58:59
26
  • По моему опыту, дети понимают DRY без объяснения причин, и на самом деле им очень скучно повторение.

  •   

    , что привело к тому, что некоторые двигатели приняли «назад» как «вперед»

    Это я боюсь тяжелой ошибки. Исправьте его, как только сможете, потому что это может испортить интуицию ребенка. Даже я не могу понять, почему метод forward настолько асимметричен.

  • Не полагайтесь на магические числа и значения аргументов по умолчанию. Значения по умолчанию очень трудно понять.

Все, что сказал, я считаю, что этот вопрос лучше подходит для обмена CSE .

ответил vnp 6 WedEurope/Moscow2017-12-06T09:04:10+03:00Europe/Moscow12bEurope/MoscowWed, 06 Dec 2017 09:04:10 +0300 2017, 09:04:10
5

Я написал программу управления Pi + python, поэтому я быстро посмотрел, что я сделал. Интересно, что я заметил, что у меня, похоже, была такая же проблема с форвардом и назад. Я нашел этот комментарий:

# Forwards and backwards are the same for left and right
# The trick is to reverse the motor connections for one  side :-) 

Таким образом, я решил это, заменив соединения для обоих двигателей с одной стороны. Я мог бы рассказать вам, где код, но он находится на веб-сайте с моим продуктом, и я не хочу рекламировать его. Он использует другой контроллер двигателя, но принцип тот же. Вот основной код:

# Main program

# Get the curses screen
screen = curses.initscr()

..... <omitted code to set up the I/O>

# Tell user what to expect
# in curses print does not work anymore. use addstr
screen.addstr("<prouduct name> example program for Python\n")
screen.addstr("Use numeric keypad keys control\n")
screen.addstr("Left  Both  Right\n")
screen.addstr("     Forward     \n")
screen.addstr("  7     8     9  \n")
screen.addstr("                 \n")
screen.addstr("  4    Stop   6  \n")
screen.addstr("                 \n")
screen.addstr("  1     2     3  \n")
screen.addstr("     Reverse     \n")
screen.addstr("Left  Both  Right\n")
screen.addstr("Don't forget to set numlock on!!!!\n")
screen.addstr("Use Q or q to quit\n")
screen.addstr("\n")


run = 1
while run==1 :
  key = screen.getch() # Key?
  if key==ord('q') :
    run = 0 # stop running

  if key==ord('1') : 
     gb.move_brushed(BOARD,LEFT,BACKW) # Left backwards 

  if key==ord('2') : 
     gb.move_brushed(BOARD,LEFT,BACKW)  # Left backwards 
     gb.move_brushed(BOARD,RIGHT,BACKW) # Right backwards 

  if key==ord('3') : 
     gb.move_brushed(BOARD,RIGHT,BACKW) # Right backwards 

  if key==ord('4') :
     gb.move_brushed(BOARD,LEFT,STOP) # Left stop 

  if key==ord('5') : 
     gb.move_brushed(BOARD,LEFT,STOP)  # Left stop 
     gb.move_brushed(BOARD,RIGHT,STOP) # Right stop 

  if key==ord('6') :
     gb.move_brushed(BOARD,RIGHT,STOP) # Right stop 

  if key==ord('7') :
     gb.move_brushed(BOARD,LEFT,FORWD) # Left forwards 

  if key==ord('8') :
     gb.move_brushed(BOARD,LEFT,FORWD)  # Left forwards 
     gb.move_brushed(BOARD,RIGHT,FORWD) # Right forwards 

  if key==ord('9') :
     gb.move_brushed(BOARD,RIGHT,FORWD) # Right forwards 

# on exist stop everything
gb.emerg_stop()
# Set terminal behaviour normal again
curses.endwin()
ответил Oldfart 7 ThuEurope/Moscow2017-12-07T17:45:38+03:00Europe/Moscow12bEurope/MoscowThu, 07 Dec 2017 17:45:38 +0300 2017, 17:45:38
2

В другой заметке, чтобы не нажимать кнопку ввода после каждой команды, вы могли бы добавить следующую модификацию основной части кода для использования pygame, хотя это сделало бы ее менее понятной, поэтому я бы определенно сохранил обе версии.

if __name__ == '__main__':
    GPIO.setwarnings(False)
    GPIO.cleanup()

    try:
        car = Car()
        import pygame

        while True:
            events = pygame.event.get()
            for event in events:
                if event.type == pygame.KEYDOWN:
                    if event.key == pygame.K_UP or event.key == pygame.K_w:
                        car.forward()
                    elif event.key == pygame.K_LEFT or event.key == pygame.K_a:
                        car.forward_left()
                    elif event.key == pygame.K_RIGHT or event.key == pygame.K_d:
                        car.forward_right()
                    elif event.key == pygame.K_DOWN or event.key == pygame.K_s:
                        car.backward()
                    elif event.key == pygame.K_SPACE:
                        car.stop()
    finally:
        try:
            GPIO.cleanup()
        except:  # ignore cleanup errors
            pass

В тех случаях, когда указатель мыши или клавиши со стрелками контролируют движение, а пространство останавливает его.

Благодаря @Haz на https://stackoverflow.com/questions /16044229 /как к получить-клавиатуру-вход-в-Pygame

ответил 13ros27 15 FriEurope/Moscow2017-12-15T20:54:38+03:00Europe/Moscow12bEurope/MoscowFri, 15 Dec 2017 20:54:38 +0300 2017, 20:54:38
2

Я думаю, что совет из import this поможет вам здесь, особенно «Явный лучше, чем неявный», «Простой лучше, чем сложный», «Плоский лучше, чем вложенный» и «Показатели удобочитаемости «. В частности, я хотел бы отметить, что вы создаете класс Car, который может использоваться только как одноэлементный. Но модули Python уже являются вполне допустимыми одноэлементными классами, поэтому вы можете исключить уровень абстракции и вложенности, реализуя все непосредственно на уровне модуля.

Я также согласен с другими, что вы должны исправить проблемы вперед /назад, и рекомендуется всегда указывать скорость двигателя, а не полагаться на значения по умолчанию. Мне нравится ваш подход к стеку событий, но я бы ссылался на функции напрямую (не по имени), и я бы добавил дополнительный шаг, чтобы было легче увидеть, что происходит.

Это дает более простой код, например:

from RPi import GPIO
from gpiozero import Motor, OutputDevice

# setup GPIO
GPIO.setwarnings(False)
GPIO.cleanup()
OutputDevice(5).on()
OutputDevice(17).on()
OutputDevice(25).on()
OutputDevice(12).on()

# setup motor references (with correct forward/backward settings)
front_right = Motor(forward=24, backward=27)
front_left = Motor(forward=22, backward=6)
rear_right = Motor(forward=18, backward=13)
rear_left = Motor(forward=16, backward=23)

# define available commands
def forward(self):
    front_right.forward(1)
    front_left.forward(1)
    rear_right.forward(1)
    rear_left.forward(1)

def backward(self):
    front_right.backward(1)
    front_left.backward(1)
    rear_right.backward(1)
    rear_left.backward(1)

def backward_right(self):
    front_right.backward(1)
    front_left.backward(1)
    rear_right.backward(1)
    rear_left.backward(0.2)

def backward_left(self):
    front_right.backward(1)
    front_left.backward(1)
    rear_right.backward(0.2)
    rear_left.backward(1)

def forward_left(self):
    front_right.forward(1)
    front_left.forward(0.2)
    rear_right.forward(1)
    rear_left.forward(1)

def forward_right(self):
    front_right.forward(0.2)
    front_left.forward(1)
    rear_right.forward(1)
    rear_left.forward(1)

def stop(self):
    front_right.stop()
    front_left.stop()
    rear_right.stop()
    rear_left.stop()

commands = {
    "f": forward,
    "b": backward,
    "s": stop
}

try:
    while True:
        # better to use another library that can use arrow keys and maybe spacebar for stop
        command = raw_input() 
        func = commands[command]
        func()
finally:
    try:
        GPIO.cleanup()
    except:  # ignore cleanup errors
        pass

Если вы хотите использовать DRYer, вы можете сделать это (за счет большей абстракции):

from RPi import GPIO
from gpiozero import Motor, OutputDevice

# setup GPIO
GPIO.setwarnings(False)
GPIO.cleanup()
OutputDevice(5).on()
OutputDevice(17).on()
OutputDevice(25).on()
OutputDevice(12).on()

# setup motor references (with correct forward/backward settings)
motors = dict(
    front_right=Motor(forward=24, backward=27),
    front_left=Motor(forward=22, backward=6),
    rear_right=Motor(forward=18, backward=13),
    rear_left=Motor(forward=16, backward=23)
)

# define available commands (dicts of speeds for each motor)
commands = {
    "f": dict(front_right=1, front_left=1, rear_right=1, rear_left=1),
    "b": dict(front_right=-1, front_left=-1, rear_right=-1, rear_left=-1),
    "s": dict(front_right=0, front_left=0, rear_right=0, rear_left=0),
}

def set_motor_speed(motor, speed):
    if speed < 0:
        motor.backward(-speed)
    elif speed == 0:
        motor.stop()
    else:
        motor.forward(speed)

def run_command(command):
    settings=commands[command]
    for motor_name, speed in settings.items():
        set_motor_speed(motors[motor_name], speed)

try:
    while True:
        # better to use another library that can use arrow keys and maybe spacebar for stop
        command = raw_input() 
        run_command(command)
finally:
    try:
        GPIO.cleanup()
    except:  # ignore cleanup errors
        pass
ответил Matthias Fripp 15 FriEurope/Moscow2017-12-15T21:40:00+03:00Europe/Moscow12bEurope/MoscowFri, 15 Dec 2017 21:40:00 +0300 2017, 21:40:00
1
class Car:
    def __init__(self):
        self.front_right = Motor(forward=24, backward=27)
        self.front_left = Motor(forward=22, backward=6)

        self.rear_right = Motor(forward=18, backward=13)
        self.rear_left = Motor(forward=16, backward=23)

Эта модификация метода __init__ должна означать, что не имеет значения, что вы путаете контакты, потому что я просто перепутал те, которые запутались. ПРИМЕЧАНИЕ. По-прежнему требуется бит кода для их включения. Кроме того, может быть полезно, если это попытка помочь кому-то узнать кодировку, чтобы добавить комментарии к деталям, что все делает.

ответил 13ros27 15 FriEurope/Moscow2017-12-15T20:34:52+03:00Europe/Moscow12bEurope/MoscowFri, 15 Dec 2017 20:34:52 +0300 2017, 20:34:52

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132