pybricks / support

Pybricks support and general discussion
MIT License
109 stars 7 forks source link

[Feature] block for motor.run_time and adding expand/collapse for motor.run() #1770

Open afarago opened 2 months ago

afarago commented 2 months ago

Is your feature request related to a problem? Please describe. It would be awesome to get a block for motor.run_time function as with competition this is the first recommended and most used approach for handling mission models.

Describe the solution you'd like I would like to have a motor - run_at - for - then block.

Describe alternatives you've considered I can still use image Many teams use motor.run_until_stalled - but that not always an option.

Additional context image

Additionally I would suggest to change the motor code block to use a expand/collapse instead of the mode parameter option "forever" that is quite misleading / hard to understand. Suggested modes

If multiple expand/collapse is possible I would also suggest to add a second collapse/expand mechanism for the then part and keep the default Stop.COAST when collapsed.

laurensvalk commented 2 months ago

Thanks for opening this issue.

It would be awesome to get a block for motor.run_time function as with competition this is the first recommended and most used approach for handling mission models.

Can you elaborate on the use cases? Why is it the first recommended and most used approach?

Additionally I would suggest to change the motor code block to use a expand/collapse instead of the mode parameter option "forever" that is quite misleading / hard to understand. (...) If multiple expand/collapse is possible I would also suggest to add a second collapse/expand mechanism for the then part and keep the default Stop.COAST when collapsed.

While everything is technically possible, I would be cautious about hiding options behind multiple different expansions or mutators on a single block. Thus far, we have gone with either a dropdown (for different modes) or an expansion (for optional settings), but not both at the same time. When you see a block visually, it has to be unambiguous how you'd get it from the block palette.

afarago commented 2 months ago

Can you elaborate on the use cases? Why is it the first recommended and most used approach? FLL/WRO robots typically try to avoid moving to degrees as it can easily lead to a stuck robot.

Sample code for Motor and DriveBase.

def motor_run_safe(motor, time=2000, speed=200)
    motor.run_time(speed, time)
    while not (motor.done() or motor.stalled()):
        wait(10)
    motor.stop()

It is always a good option to use Motor.run_until_stalled(), yet beginner teams, also use a similar construct for DriveBase.

def robot_align_mission(time=2000, speed=200):
    robot.drive(speed, 0)
    sw = StopWatch()
    while not (robot.done() or robot.stalled() or sw.time()>time):
        wait(10)
    robot.stop()

While everything is technically possible, I would be cautious about hiding options behind multiple different expansions or mutators on a single block. Thus far, we have gone with either a dropdown (for different modes) or an expansion (for optional settings), but not both at the same time. When you see a block visually, it has to be unambiguous how you'd get it from the block palette.

Ok, understood, it is definitely a tradeoff; just noting that Motor run forever seems a bit hard to comprehend.

laurensvalk commented 2 months ago

Thank you.

attachment (e.g arm) cannot rotate to target degrees (e.g. trying to lift or push beyond its power limit) --> robot stops mid-mission, not executing the next steps

It seems that run_until_stalled would be appropriate for this. Using the multitask block, it is also quite easy to make a timeout with regular rotations:

image

This also has the benefit that you don't have to wait two full seconds if you did reach the target. Or including the stall condition:

image

driving wheel cannot rotate to target degrees

I seem to recall we have an issue/discussion about this since the following point came up there too:

(...) also use a similar construct for DriveBase. (...) robot_align_mission()

And we sort of came to the conclusion at the time that the goal usually isn't to drive for time but rather to achieve robot_align_mission or similar. Maybe we can come up with a useful drivebase mission that does exactly that.

Ok, understood, it is definitely a tradeoff; just noting that Motor run forever seems a bit hard to comprehend.

I agree. Please do share suggestions to make it easier. We've previously had just run, or on (to complement off), but those weren't especially clear either, so we chose the rather verbose:

rotate forever (until you use another motor block or the program ends)

The latter is to address the frequent question that the motor does not appear to turn because the program ends right away if that is your last block.

Another option is to make a dedicated block for rotating forever, since it is certainly a bit different from the finite rotations. Ideally, we have a single verb per block. One block does that one thing. The options specify how. But maybe there are other words we can use so they can still be separate without creating confusion.

image

afarago commented 2 months ago

It seems that run_until_stalled would be appropriate for this. Using the multitask block, it is also quite easy to make a timeout with regular rotations:

Beginner teams, kids 8-12y will not get and use these concepts imo. At the moment we teach them to use run till seconds for safety - starting with run by degrees step. Anything beyond this concept will not be digestible in my experience on a larger scale for this group.

And we sort of came to the conclusion at the time that the goal usually isn't to drive for time but rather to achieve robot_align_mission or similar. Maybe we can come up with a useful drivebase mission that does exactly that.

Motivation: I am working on a competition tutorial and were considering to add examples for typical FLL/WRO competition approaches using pybricks python - SPIKE wordblocks - pybricks blocks side-by-side for comparison.

Additional detail: many times we want to stop approximately at an endposition. Stall detection would require also tuning different stall sensitivity (per model) - again too complex. Many times (e.g.) drivebase/motor_pair stall is random - if the mat/wheels are clean they stall, if they are somewhat dirty, they rotate freely though not moving anymore. Similarly with motors - sometimes arms can break, gears can slip/skip and there is no clear stall. All in all - for beginners apart from navigation using time can be the most important factor. (Quite painful that pybricks uses ms instead of the seconds that is both closer to kids' minds and also is SI unit - but most kids can easily jump the ms=sec*1000 equation)