Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

OSError when using MultiProcessing in PyBricks #80

Closed
AntoniLuongPham opened this issue Jul 3, 2020 · 6 comments
Closed

OSError when using MultiProcessing in PyBricks #80

AntoniLuongPham opened this issue Jul 3, 2020 · 6 comments
Labels
platform: EV3 Issues related to LEGO MINDSTORMS EV3 support Request for technical support for a problem that is not a bug or feature request topic: multitasking Issues releated to parallel threads, tasks, coroutines, etc.

Comments

@AntoniLuongPham
Copy link

AntoniLuongPham commented Jul 3, 2020

I'm trying out multiprocessing in pybricks programs. A simple program with a parallel process is pasted below: whenever the Touch Sensor is pressed, the Motor shall run.

When I run this program, however, it fails as soon as I press the Touch Sensor, with the following error:

# OSError: [Errno 5] EIO:
# Unexpected hardware input/output error with a motor or sensor:
# --> Try unplugging the sensor or motor and plug it back in again.
# --> To see which sensor or motor is causing the problem,
#     check the line in your script that matches
#     the line number given in the 'Traceback' above.
# --> Try rebooting the hub/brick if the problem persists.

May I check what may be the problem? Thank you very much!

#!/usr/bin/env pybricks-micropython


from pybricks.ev3devices import TouchSensor, Motor
from pybricks.parameters import Port, Stop

from multiprocessing import Process


TOUCH_SENSOR = TouchSensor(port=Port.S1)
MOTOR = Motor(port=Port.A)


def motor_on_when_touched():
    while True:
        if TOUCH_SENSOR.pressed():
            MOTOR.run_time(
                speed=1000,   # deg/s
                time=1000,   # ms
                then=Stop.COAST,
                wait=True)


Process(target=motor_on_when_touched).start()
# as soon as Touch Sensor is pressed:
# OSError: [Errno 5] EIO:
# Unexpected hardware input/output error with a motor or sensor:
# --> Try unplugging the sensor or motor and plug it back in again.
# --> To see which sensor or motor is causing the problem,
#     check the line in your script that matches
#     the line number given in the 'Traceback' above.
# --> Try rebooting the hub/brick if the problem persists.


while True:
    pass
@AntoniLuongPham AntoniLuongPham added support Request for technical support for a problem that is not a bug or feature request triage Issues that have not been triaged yet labels Jul 3, 2020
@laurensvalk
Copy link
Member

Threading and processing is not officially supported in this release. Some things might work, but it has not been designed or tested for that use case.

However, Pybricks for EV3 has an experimental feature that you may want to try:

        from pybricks.experimental import run_parallel
        from pybricks.tools import wait

        def task1():
            wait(1000)
            return 'OK1'

        def task2():
            wait(500)
            return 'OK2'

        result = run_parallel(task1, task2)
        print('task1:', repr(result[task1]))
        print('task2:', repr(result[task2]))
        # prints:
        # task1: 'OK1'
        # task2: 'OK2'

@laurensvalk laurensvalk removed the triage Issues that have not been triaged yet label Jul 3, 2020
@GianCann
Copy link

GianCann commented Jul 3, 2020

Hi @laurensvalk ,
this type of feature will possible to have it, in the future, also in the PU Hubs?

@laurensvalk
Copy link
Member

Running multiple things at the same time, yes, but not using threads.

That is why the EV3 feature is experimental. We'd much rather have one simple way of doing it across all hubs.

@dlech dlech added the platform: EV3 Issues related to LEGO MINDSTORMS EV3 label Jul 3, 2020
@AntoniLuongPham
Copy link
Author

AntoniLuongPham commented Jul 3, 2020

@laurensvalk thank you for letting me know of the run_parallel feature.

I've tried it out but run_parallel does not seem to work in the program below -- Ev3rstorm's Dancing thread does not seem to do anything, while its Driving thread does work.

I'm reporting this problem with run_parallel here instead of creating a new ticket because it's still experimental.

Thank you.

#!/usr/bin/env pybricks-micropython


from pybricks.hubs import EV3Brick
from pybricks.ev3devices import Motor, TouchSensor, ColorSensor, InfraredSensor
from pybricks.media.ev3dev import ImageFile, SoundFile
from pybricks.robotics import DriveBase
from pybricks.parameters import Button, Color, Direction, Port, Stop
from pybricks.tools import wait

from pybricks.experimental import run_parallel

from random import randint


class Ev3rstorm(EV3Brick):
    WHEEL_DIAMETER = 26   # milimeters
    AXLE_TRACK = 102      # milimeters


    def __init__(
            self,
            left_foot_motor_port: Port = Port.B, right_foot_motor_port: Port = Port.C,
            ir_sensor_port: Port = Port.S4, ir_beacon_channel: int = 1):
        self.drive_base = DriveBase(left_motor=Motor(port=left_foot_motor_port,
                                                     positive_direction=Direction.CLOCKWISE),
                                    right_motor=Motor(port=right_foot_motor_port,
                                                      positive_direction=Direction.CLOCKWISE),
                                    wheel_diameter=self.WHEEL_DIAMETER,
                                    axle_track=self.AXLE_TRACK)

        self.ir_sensor = InfraredSensor(port=ir_sensor_port)
        self.ir_beacon_channel = ir_beacon_channel
    

    def drive_once_by_ir_beacon(
            self,
            speed: float = 1000,    # mm/s
            turn_rate: float = 90   # rotational speed deg/s
        ):
        ir_beacon_button_pressed = set(self.ir_sensor.buttons(channel=self.ir_beacon_channel))

        # forward
        if ir_beacon_button_pressed == {Button.LEFT_UP, Button.RIGHT_UP}:
            self.drive_base.drive(
                speed=speed,
                turn_rate=0)

        # backward
        elif ir_beacon_button_pressed == {Button.LEFT_DOWN, Button.RIGHT_DOWN}:
            self.drive_base.drive(
                speed=-speed,
                turn_rate=0)

        # turn left on the spot
        elif ir_beacon_button_pressed == {Button.LEFT_UP, Button.RIGHT_DOWN}:
            self.drive_base.drive(
                speed=0,
                turn_rate=-turn_rate)

        # turn right on the spot
        elif ir_beacon_button_pressed == {Button.RIGHT_UP, Button.LEFT_DOWN}:
            self.drive_base.drive(
                speed=0,
                turn_rate=turn_rate)

        # turn left forward
        elif ir_beacon_button_pressed == {Button.LEFT_UP}:
            self.drive_base.drive(
                speed=speed,
                turn_rate=-turn_rate)

        # turn right forward
        elif ir_beacon_button_pressed == {Button.RIGHT_UP}:
            self.drive_base.drive(
                speed=speed,
                turn_rate=turn_rate)

        # turn left backward
        elif ir_beacon_button_pressed == {Button.LEFT_DOWN}:
            self.drive_base.drive(
                speed=-speed,
                turn_rate=turn_rate)

        # turn right backward
        elif ir_beacon_button_pressed == {Button.RIGHT_DOWN}:
            self.drive_base.drive(
                speed=-speed,
                turn_rate=-turn_rate)

        # otherwise stop
        else:
            self.drive_base.stop()
    
    def keep_driving_by_ir_beacon(
            self,
            speed: float = 1000,    # mm/s
            turn_rate: float = 90   # rotational speed deg/s
        ):
        while True:
            self.drive_once_by_ir_beacon(
                speed=speed,
                turn_rate=turn_rate)
            wait(1)

    
    def dance_whenever_ir_beacon_pressed(self):
        while True:
            while Button.BEACON in self.ir_sensor.buttons(channel=self.ir_beacon_channel):
                self.drive_base.turn(angle=randint(-360, 360))
                wait(1)


    def main(self,
             driving_speed: float = 1000   # mm/s
            ):
        self.screen.load_image(ImageFile.TARGET)

        run_parallel(
            self.keep_driving_by_ir_beacon,

            # *** the below parallel thread doesn't run ***
            self.dance_whenever_ir_beacon_pressed)
            

if __name__ == '__main__':
    EV3RSTORM = Ev3rstorm()    
    EV3RSTORM.main()

@laurensvalk
Copy link
Member

As in the example and mentioned in the documentation linked to above, the parallel functions must not have arguments. In your code, there is one argument (self).

It would be possible to account for that but it probably isn’t going to be very efficient. Can you try a more basic example first?

@dlech
Copy link
Member

dlech commented Mar 21, 2023

I don't think MicroPython will ever support multiprocessing but anyone just wanting to run tasks in parallel should have a look at #917.

@dlech dlech closed this as completed Mar 21, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
platform: EV3 Issues related to LEGO MINDSTORMS EV3 support Request for technical support for a problem that is not a bug or feature request topic: multitasking Issues releated to parallel threads, tasks, coroutines, etc.
Projects
None yet
Development

No branches or pull requests

4 participants