1
\$\begingroup\$

I'm making a GUI that can be controlled by a steering wheel. It need only run on Windows.

It works as expected with exception of the fact that the first 90 degrees in either direction seems to be treated like a dead zone. If I write out axis values as I move the steering wheel, I see nothing until 90 degrees, then it reads as normal - starting from near-zero @ +/- 90 degrees and linearly increasing to +/- 1.0 at the stops (so it's not like it's suppressing the first 90 degrees - it seems to not read until +/- 90 degrees).

I've tried two different high-end steering wheels, both behave the same. Neither have dead zones when using them in driving games.

I'm using PyJoystick which uses SDL2.

Is there a way to control the steering wheel dead zone in PyJoystick or SDL2?

Super basic code example:

import pyjoystick
from pyjoystick.sdl2 import Joystick, run_event_loop

def __joystickKeyReceived(key):
    print (f"received: {key}: {key.value}")

j = Joystick(0) # 0 is index to the steering wheel. `j.name` will show if you've got the correct joystick
mngr = pyjoystick.ThreadEventManager(event_loop=run_event_loop, handle_key_event=__joystickKeyReceived)
mngr.start()
input("Press Enter to exit")

Running the above while turning the wheel will print out something like (after moving past +/- 90 degrees, anyway):

revcevied: -Axis 0: -0.01163500419623105
revcevied: -Axis 0: -0.030060273136491913
revcevied: -Axis 0: -0.037575341420614874
revcevied: -Axis 0: -0.03330281528953989
...

of note: there is no noticeable dead zone when using this same code with, say, an XBox controller connected to the PC.

pyjoystick is available through pip - pip install pyjoystick

Digging through pyJoystick, it includes an "sdl2" module which defines the Joystick class. Joystick has a base class in the "interface" module (the base class is also called "Joystick"). The base class sets a variable called "deadband" that defaults to 0.2.

0.2 * 900 = 180. 180 degrees is the dead zone I'm seeing on both of my 900 degree wheels

That base class also has get_deadband and set_deadband methods. Both of which seem to do change the value of a class variable called "deadband", but behavior is not changed.

However, if I change the default value of 0.2 to, say, 0.01, then the dead zone nearly goes away.

So it appears that there's a problem with the implementation of PyJoystick that's preventing the set_deadband method from working as expected (either that, or I'm missing a step).

I'm leaving this question unanswered to see if anyone else has an idea of how to set deadband without modifying default values in the library.

\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

Try this:

import pyjoystick
from pyjoystick.sdl2 import Joystick, run_event_loop

def __joystickKeyReceived(key):
    print (f"received: {key}: {key.value}")

def __add_joystick(joy):
    joy.set_deadband(0.01)

mngr = pyjoystick.ThreadEventManager(event_loop=run_event_loop,add_joystick=__add_joystick, handle_key_event=__joystickKeyReceived)
mngr.start()
input("Press Enter to exit")

Explain:

The ThreadEventManager holds references to all joysticks. When a signal occurs, only the deadband variable in the internal joystick object is used:

def save_key_event(self, key):
    ...
    joy = self.joysticks[key.joystick]
    key.joystick = joy
    ...

    if key.keytype == key.AXIS:
        dead = getattr(key.joystick, 'deadband', 0)
        if dead:
            key.value = deadband(key.value, dead)
        ...
    self._update_key_event(key)

The Joystick constructor will create a new object, which is different from the one in ThreadEventManager. It accesses the underlying sdl api during initialization and gets some data such as the device name, but deadbond is not included, because it is the extended data of the encapsulation layer, not the original data in the sdl2 API:

class Joystick(BaseJoystick):
    def __new__(cls, identifier=None, instance_id=None, *args, **kwargs):
        joy = super().__new__(cls)
        ...
        instance_id = identifier
        joy.joystick = sdl2.SDL_JoystickOpen(identifier)
        ...
        joy.identifier = sdl2.SDL_JoystickID(instance_id).value
        joy.name = sdl2.SDL_JoystickName(joy.joystick).decode('utf-8')
        joy.numaxes = sdl2.SDL_JoystickNumAxes(joy.joystick)
        joy.numbuttons = sdl2.SDL_JoystickNumButtons(joy.joystick)
        joy.numhats = sdl2.SDL_JoystickNumHats(joy.joystick)
        joy.numballs = sdl2.SDL_JoystickNumBalls(joy.joystick)
        joy.init_keys()
        ...
        return joy

So j = Joystick(0) is not the same as the object actually used. j.set_deadband can not work.

\$\endgroup\$
1
  • \$\begingroup\$ Very well explained. Thank you for taking the time to do that! \$\endgroup\$
    – Brian K
    Commented Jun 26 at 17:51

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .