pybricks / support

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

[Feature] Gamepad controller investigation #1024

Open laurensvalk opened 1 year ago

laurensvalk commented 1 year ago

This is a spot for discussing gamepad connections, split off from https://github.com/pybricks/support/issues/262

BertLindeman commented 7 months ago

"Short" conclusions about controller rumble actions.

To diminish typing I use this variables to indicate the actuators:

# Left handle, right handle, left trigger, right trigger
ACT0 = [100, 0, 0, 0]  # Left handle
ACT1 = [0, 100, 0, 0]  # right handle
ACT2 = [0, 0, 100, 0]  # left trigger
ACT3 = [0, 0, 0, 100]  # right trigger
  1. If there are more than one xbox.rumble(blabla) commands: the actator(s) start turning after the last rumble command.

  2. Rumble commands update the settings if the list of actuator percentages are equal. Example: This will make left trigger actuator turn 3 times for 50ms with delay 50ms

    controller.rumble(ACT2, 10, 100, 5)
    controller.rumble(ACT2, 50, 50, 3)
  1. Rumble commands add settings for more actuators if the list of actuators are different. Example: This will make all 4 actuators rumble. ACT0 a bit more times than the others.
    controller.rumble(ACT0, 80, 100, 10000)
    controller.rumble(ACT1, 90, 100, 1000)
    controller.rumble(ACT2, 90, 100, 100)
    controller.rumble(ACT3, 50, 100, 10)

Did not (yet) look into how the power settings of an actuator work.

xbox_rumble_test program ```python # pylint: disable=R0801 # no test for duplicate lines import usys as sys from pybricks.iodevices import XboxController from pybricks.tools import wait # Left handle, right handle, left trigger, right trigger ACT0 = [100, 0, 0, 0] # Left handle ACT1 = [0, 100, 0, 0] # right handle ACT2 = [0, 0, 100, 0] # left trigger ACT3 = [0, 0, 0, 100] # right trigger # Set up all devices. controller = XboxController() # duration, delay, count print("1 test ALL actuators with waiting for a button pressed") print(" Expect all 4 act vibrate 4 times") print(" Result: ONLY left and right handle vibrate") while True: controller.rumble(75, 100, 50, 4) print(end=":") if controller.buttons.pressed(): # clear button buffer: while controller.buttons.pressed(): wait(5) break wait(1500) wait(1000) print("\n2 Test 1 actuator. Waiting for a button pressed. Which act? 2 or 3 - 3 or 5 counts") print(" Expect left trigger 3 vibs and right trigger 3") while True: controller.rumble(ACT2, 10, 100, 5) controller.rumble(ACT2, 50, 50, 3) print(end="L") wait(1500) controller.rumble(ACT3, 50, 50, 3) controller.rumble(ACT3, 10, 100, 5) print(end="R") if controller.buttons.pressed(): # clear button buffer: while controller.buttons.pressed(): wait(5) break wait(1500) wait(5000) print("\n3 Test 1 actuator with deminishing delay and see where it starts vibrating") print(" expect left handle to rumble during the loop sometimes and after buttonpress 3 vibs") breakout = False while True: for i in range(100, 0, -5): controller.rumble(ACT0, 50, i, 3) print(i, end=" ") if controller.buttons.pressed(): # clear button buffer: while controller.buttons.pressed(): wait(5) breakout = True break if breakout: break wait(5000) print("\n4 Test 1 actuator with small delay and see where it starts vibrating") print(" Expect hardly any vibs in the loop and after that 3 vibs of act0") while True: for i in range(41, 39, -1): controller.rumble(ACT0, 50, i, 3) print(i, end="_") if controller.buttons.pressed(): # clear button buffer: while controller.buttons.pressed(): wait(5) break wait(5000) print("\n5 Test 1 actuator overwrite") while True: for __i in range(5): dur = __i * 100 waitms = __i + 40 count = __i controller.rumble(ACT0, dur, waitms, count) # controller.rumble(ACT0, __i*100, __i*50, __i) # controller.rumble(ACT0, __i*100, __i*50, __i) # controller.rumble(ACT0, __i*100, __i*50, __i) # controller.rumble(ACT0, __i*100, __i*50, __i) # the same actuator list overwrites the previous one # different actuator list are added print(end="=") if controller.buttons.pressed(): # clear button buffer: while controller.buttons.pressed(): wait(5) break # clear button buffer: while controller.buttons.pressed(): wait(5) mycounter = 0 print("\n6 Test 4 actuators sequential") print(" Expect all 4 vibrate but act0 much more counts") while True: mycounter += 1 controller.rumble(ACT0, 80, 100, 10000) controller.rumble(ACT1, 90, 100, 1000) controller.rumble(ACT2, 90, 100, 100) controller.rumble(ACT3, 50, 100, 10) # controller.rumble(ACT0, 1000, 50, 4) # controller.rumble(ACT1, 1000, 50, 4) # controller.rumble(ACT2, 500, 250, 2) # controller.rumble(ACT3, 500, 250, 2) # the same actuator list overwrites the previous one # different actuator list are added print(end=".") if controller.buttons.pressed(): print("\nThis last test did", mycounter, "loops") break print("\nTests ended\nwaiting 60 seconds for potential vibrations") wait(60000) raise SystemExit(0) print("do (ACT1 right handle bla bla)") controller.rumble(ACT1, 500, 250, 3) wait(2000) print("do (ACT2 left trigger bla bla)") controller.rumble(ACT2, 500, 250, 3) wait(2000) print("do (ACT3 right trigger bla bla)") controller.rumble(ACT3, 500, 250, 3) wait(2000) print("do (ACTn, bla bla) ready") print("do (ACT0 left handle bla bla)") controller.rumble(ACT0, 10, 50, 10) print("do (ACT1 right handle bla bla)") controller.rumble(ACT1, 200, 50, 10) print("do (ACT2 left trigger bla bla)") controller.rumble(ACT2, 300, 50, 9) print("do (ACT3 right trigger bla bla)") controller.rumble(ACT3, 500, 50, 5) print("do (ACTn, bla bla) ready") wait(20000) ```
laurensvalk commented 7 months ago

"RuntimeError: Unknown error": I realized that if I stop the program and restart it and turn on the controller then this can happen

Happens here from time to time.

See https://github.com/pybricks/support/issues/1509

If you find a way to consistently reproduce it, that would be very helpful.

laurensvalk commented 7 months ago

The controls do appear to be additive:

>>> controller.rumble(power=[100, 00, 0, 0], duration=250, delay=00, count=100)
>>> wait(2000)
>>> controller.rumble(power=[0, 00, 100,0 ], duration=250, delay=00, count=100)

Now both the left + left trigger are active.

If you give a new command for an actuator that is already active, the latest command "wins". So if you used count=100 and then count=1, it will stop soon.

laurensvalk commented 7 months ago

I propose these defaults:

def rumble(power=100, duration=200, delay=100, count=1)
    ...

So you can just do:

controller.rumble()
controller.rumble(count=3)

And so on.

gyenesvi commented 7 months ago

The controls do appear to be additive:

That sounds like a useful thing, as different parts may be triggered by different events, and adding them up this way can make it easy.

But sometimes we just want sequential execution of multiple rumble commands, so what do you think @laurensvalk about that wait parameter to make that use case easy as well?

I propose these defaults:

It makes sense to have everything defaulted like that, and then it's easy to use and parameterize only what you want. I'll test with that timing later to see how it feels, yesterday I tested with duration=250, delay=150, that was also fine.

laurensvalk commented 7 months ago

Upcoming rumble blocks!

image

gyenesvi commented 7 months ago

Looks good! I guess the optional parameters unfold by the arrow on the right and it is possible to add any combination.