wiki:Tutorial5AdvancedRemoteControlUsage

Version 16 (modified by fma, 8 years ago) ( diff )

--

Table of Contents

Tutorial 5: Advanced remote control usage

As we've seen in tutorial 1, we created a Gamepad class in order to control our robot. This class inherits from RemoteControl, and implements a few virtual methods. As an example, we will use my setup for my big 4 DoF hexapod, Cronos. We just create a class which inherits from RemoteControl:

class Gamepad(RemoteControl):
    def _createFrontend(self):
        return Thrustmaster(THRUSTMASTER_PATH)

The method _createFrontend() must return a valid Frontend (we just saw in previous tutorial how to create such Frontends). This methods takes as first argument the Frontend path in /dev. An additional argument can be given to override the DEFAULT_AXIS_CALIBRATION table.

    def _buildComponents(self):
        self._addConfig("walk")
        self._addConfig("body")

        self._addComponent(Button, command=self.selectNextConfig, key="digital_003", trigger="hold")

        self._addComponent(Button, configs="walk", command=GaitSequencer().walkStop, key="digital_000")
        self._addComponent(Button, configs="walk", command=GaitSequencer().walkStep, key="digital_002")
        self._addComponent(Button, configs="walk", command=GaitSequencer().selectNextGait, key="digital_006", trigger="hold")
        self._addComponent(Button, configs="walk", command=GaitSequencer().selectPrevGait, key="digital_007", trigger="hold")

        self._addComponent(Joystick, configs="walk", command=GaitSequencer().walk, keys=("analog_02", "analog_03", "analog_00"), mapper=MapperWalk())

        # Body inc. translation
        self._addComponent(Axis, command=self.robot.incBodyPosition, key="analog_04", modifier="digital_008", mapper=MapperSetMultiply('dx', coef=5))
        self._addComponent(Axis, command=self.robot.incBodyPosition, key="analog_05", modifier="digital_008", mapper=MapperSetMultiply('dy', coef=5))
        self._addComponent(Button, command=self.robot.incBodyPosition, key="digital_004", modifier="digital_008", mapper=MapperSetValue(dz=+10))
        self._addComponent(Button, command=self.robot.incBodyPosition, key="digital_005", modifier="digital_008", mapper=MapperSetValue(dz=-10))

        # Body inc. rotation
        self._addComponent(Axis, command=self.robot.incBodyPosition, key="analog_04", modifier="digital_009", mapper=MapperSetMultiply('droll', coef=5))
        self._addComponent(Axis, command=self.robot.incBodyPosition, key="analog_05", modifier="digital_009", mapper=MapperSetMultiply('dpitch', coef=-5))
        self._addComponent(Button, command=self.robot.incBodyPosition, key="digital_004", modifier="digital_009", mapper=MapperSetValue(dyaw=+5))
        self._addComponent(Button, command=self.robot.incBodyPosition, key="digital_006", modifier="digital_009", mapper=MapperSetValue(dyaw=-5))

        # Body extra translation
        self._addComponent(Axis, configs="body", command=self.robot.setBodyExtraPosition, key="analog_02", modifier="digital_008", mapper=MapperSetMultiply('x', coef=30))
        self._addComponent(Axis, configs="body", command=self.robot.setBodyExtraPosition, key="analog_03", modifier="digital_008", mapper=MapperSetMultiply('y', coef=30))
        self._addComponent(Axis, configs="body", command=self.robot.setBodyExtraPosition, key="analog_01", modifier="digital_008", mapper=MapperSetMultiply('z', coef=20))

        # Body extra rotation
        self._addComponent(Axis, configs="body", command=self.robot.setBodyExtraPosition, key="analog_00", modifier="digital_009", mapper=MapperSetMultiply('yaw', coef=-10))
        self._addComponent(Axis, configs="body", command=self.robot.setBodyExtraPosition, key="analog_03", modifier="digital_009", mapper=MapperSetMultiply('pitch', coef=-10))
        self._addComponent(Axis, configs="body", command=self.robot.setBodyExtraPosition, key="analog_02", modifier="digital_009", mapper=MapperSetMultiply('roll', coef=10))

        # Inc feet neutral position
        self._addComponent(Button, command=self.robot.incFeetNeutral, key="digital_005", mapper=MapperSetValue(dneutral=-10))
        self._addComponent(Button, command=self.robot.incFeetNeutral, key="digital_007", mapper=MapperSetValue(dneutral=+10))

Interesting things take place in _buildComponents(): this is where Components (Button, Axis...) of our remote control are created.

We first define 2 remote control configs, walk and body, as we don't have enough buttons to do all we want. In the first config, walk, we define some components to make the robot walk, but also to change the body position (by increments). In the second config, body, we define additional components to control the body. If no config is specified in the Component definition, it means it is available in all configs.

Then we add the components, using the _addComponent() method helper. See user guide for complete description.

But let's see how things really work.

RemoteControl is a thread, running a infinite main loop. During an iteration, each Component is checked. If triggered, the Component sends its output to the Mapper, which in turn sends its output to the command callable.

For Button, triggering depends on the optional trigger param. For Switch, triggering occurs when the state toggles. For Axis, triggering occurs when the value changes. For Joystick, triggering occurs when any value changes. If a modifier is specified, it must be pressed for the triggering to occur. It acts as a keyboard shift or control key. This way, the main key can be used in different Components.

For Axis and Joystick, a threshold param avoids false changes near 0: the value will only be triggered if it changed and if it is greater or equal than threshold.

For Axis and Joystick, a Curve can be used to change the response; a linear curve is used as default.

Ok, now let's dig a little bit into the Mappers.

To control the robot walk, we created a Joystick Component with 3 axes, X, Y and RZ. These 3 values will be send as a tuple as soon as one of them changes.

But the GaitSequencer walk() method expects speed, direction, length and angle params. We used the MapperWalk mapper, which computes all these params from X/Y/RZ values, which can be sent to the walk() method.

See py4bot/inputs/mappers/mapper.py to see how mappers are defined.

Of course, we can define our own Mappers; they don't need to be in the mapper.py file, we can put them in our own code.

In the next tutorial, we will dive into gaits.

Note: See TracWiki for help on using the wiki.