= Tutorial 5: Advanced remote control usage = As we've seen in [[Tutorial1FirstRobot|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''': {{{ #!python class Gamepad(RemoteControl): def _createFrontend(self): return Thrustmaster(THRUSTMASTER_PATH) def _buildComponents(self): self._addConfig("walk") self._addConfig("body") self._addComponent(Button, command=self.selectNextConfig, key="button_003", trigger="hold") self._addComponent(Button, configs="walk", command=GaitSequencer().walkStop, key="button_000") self._addComponent(Button, configs="walk", command=GaitSequencer().walkStep, key="button_002") self._addComponent(Button, configs="walk", command=GaitSequencer().selectNextGait, key="button_006", trigger="hold") self._addComponent(Button, configs="walk", command=GaitSequencer().selectPrevGait, key="button_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(Analog, command=self.robot.incBodyPosition, key="analog_04", modifier="button_008", mapper=MapperSetMultiply('dx', coef=5)) self._addComponent(Analog, command=self.robot.incBodyPosition, key="analog_05", modifier="button_008", mapper=MapperSetMultiply('dy', coef=5)) self._addComponent(Button, command=self.robot.incBodyPosition, key="button_004", modifier="button_008", mapper=MapperSetValue(dz=+10)) self._addComponent(Button, command=self.robot.incBodyPosition, key="button_005", modifier="button_008", mapper=MapperSetValue(dz=-10)) # Body inc. rotation self._addComponent(Analog, command=self.robot.incBodyPosition, key="analog_04", modifier="button_009", mapper=MapperSetMultiply('droll', coef=5)) self._addComponent(Analog, command=self.robot.incBodyPosition, key="analog_05", modifier="button_009", mapper=MapperSetMultiply('dpitch', coef=-5)) self._addComponent(Button, command=self.robot.incBodyPosition, key="button_004", modifier="button_009", mapper=MapperSetValue(dyaw=+5)) self._addComponent(Button, command=self.robot.incBodyPosition, key="button_006", modifier="button_009", mapper=MapperSetValue(dyaw=-5)) # Body extra translation self._addComponent(Analog, configs="body", command=self.robot.setBodyExtraPosition, key="analog_02", modifier="button_008", mapper=MapperSetMultiply('x', coef=30)) self._addComponent(Analog, configs="body", command=self.robot.setBodyExtraPosition, key="analog_03", modifier="button_008", mapper=MapperSetMultiply('y', coef=30)) self._addComponent(Analog, configs="body", command=self.robot.setBodyExtraPosition, key="analog_01", modifier="button_008", mapper=MapperSetMultiply('z', coef=20)) # Body extra rotation self._addComponent(Analog, configs="body", command=self.robot.setBodyExtraPosition, key="analog_00", modifier="button_009", mapper=MapperSetMultiply('yaw', coef=-10)) self._addComponent(Analog, configs="body", command=self.robot.setBodyExtraPosition, key="analog_03", modifier="button_009", mapper=MapperSetMultiply('pitch', coef=-10)) self._addComponent(Analog, configs="body", command=self.robot.setBodyExtraPosition, key="analog_02", modifier="button_009", mapper=MapperSetMultiply('roll', coef=10)) # Inc feet neutral position self._addComponent(Button, command=self.robot.incFeetNeutral, key="button_005", mapper=MapperSetValue(dneutral=-10)) self._addComponent(Button, command=self.robot.incFeetNeutral, key="button_007", mapper=MapperSetValue(dneutral=+10)) }}} The first method, {{{_createFrontend()}}} must return a valid frontend (we just saw in [[Tutorial4AddInputFrontendSupport|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. In the second method, {{{_buildComponents()}}}, interesting things take place: this is where {{{Component}}}s ({{{Button}}}, {{{Analog}}}...) 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). Note that when the config is not specified in the component definition, it means it is available in all configs. In the second config, ''body'', we define additional components to control the body. Then we add the components, using the {{{_addComponent()}}} method helper. This method takes the component class, the command to execute, the frontend key, a optional modifier, and a mapper. '''Mappers''' are simple objects helping to adapt params from '''!RemoteControl''' '''Components''' output to '''Robot'''/'''!GaitSequencer''' methods input. For example, to control the robot walk, we can create a '''Joystick''' component with 3 axes: X, Y, RZ. But the '''!GaitSequencer''' {{{walk()}}} method expects '''speed''', '''direction''', '''length''' and '''angle''' params. Using the '''!MapperWalk''' mapper, you can compute all these params from X/Y/RZ axes, and send them to the '''walk()''' method. That's what we did in the [[Tutorials|tutorials]]. See [https://framagit.org/fma38/Py4bot/blob/master/py4bot/inputs/mappers/mapper.py py4bot/inputs/mappers/mapper.py] to see how this mapper is defined. Of course, you can define your own '''Mappers'''.