Changes between Version 76 and Version 77 of Tutorials


Ignore:
Timestamp:
Nov 22, 2017, 8:48:12 AM (8 years ago)
Author:
fma
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • Tutorials

    v76 v77  
    1717 * IK: Inverse Kinematic
    1818
    19 == Tutorial 1: First robot ==
    20 
    21 ''Files for this tutorial are available in [https://framagit.org/fma38/Py4bot/tree/master/py4bot/examples/tutorial_1 py4bot/examples/tutorial_1/]. But don't just copy them here: write them from scratch, following the tutorial.''
    22 
    23 First, we create a new empty dir in our home dir:
    24 
    25 {{{
    26 $ mkdir tutorial_1
    27 $ cd tutorial_1
    28 }}}
    29 
    30 All usefull classes are available from the '''{{{api.py}}}''' module. So, we just have to import things from here. Let's create a '''{{{hexapod.py}}}''' file to put our code, and import what we are going to use in this tutorial:
    31 
    32 {{{
    33 #!python
    34 
    35 # -*- coding: utf-8 -*-
    36 
    37 from py4bot.api import *
    38 }}}
    39 
    40 Now, let's create the robot! A multi-legs robot is mainly build from a body, and several legs.
    41 
    42 We also need to create an ''actuator pool'', which contains all legs joints actuators, in order to move them synchronized. In our example, actuators are standard servos, and we use the '''Veyron''' board, from '''DFRobot''', to drive them:
    43 
    44 {{{
    45 #!python
    46 
    47 import settings
    48 
    49 
    50 class Hexapod(Robot):
    51     def _createBody(self):
    52         return Body(settings.LEGS_ORIGIN)
    53 
    54     def _createLegs(self):
    55         legs = {}
    56         legIk = {}
    57         for legIndex in settings.LEGS_INDEX:
    58             legs[legIndex] = Leg3Dof(legIndex, {'coxa': Coxa(), 'femur': Femur(), 'tibia': Tibia()}, settings.FEET_NEUTRAL[legIndex])
    59             legIk[legIndex] = Leg3DofIk(settings.LEGS_GEOMETRY[legIndex])
    60 
    61         return legs, legIk
    62 
    63     def _createActuatorPool(self):
    64         driver = Veyron()
    65         pool = ServoPool(driver)
    66 
    67         for leg in self._legs.values():
    68 
    69             # Create joints actuators
    70             num = settings.LEGS_SERVOS_MAPPING[leg.index]['coxa']
    71             servo = Servo(leg.coxa, num, **settings.SERVOS_CALIBRATION[num])
    72             pool.add(servo)
    73 
    74             num = settings.LEGS_SERVOS_MAPPING[leg.index]['femur']
    75             servo = Servo(leg.femur, num, **settings.SERVOS_CALIBRATION[num])
    76             pool.add(servo)
    77 
    78             num = settings.LEGS_SERVOS_MAPPING[leg.index]['tibia']
    79             servo = Servo(leg.tibia, num, **settings.SERVOS_CALIBRATION[num])
    80             pool.add(servo)
    81 
    82         return pool
    83 }}}
    84 
    85 As you can see, we just implemented 3 virtual methods, {{{_createBody()}}}, {{{_createLegs()}}} and {{{_createActuatorPool()}}}.
    86 
    87 Also note that we used some values from the '''{{{settings.py}}}''' module. This module is just a simple way to centralise the configuration of our hexapod. We will describe this module later.
    88 
    89 To drive a robot, we need a remote control. In this tutorial, we are going to use an old gamepad, a [http://www.thrustmaster.com/en_IN/products/firestorm-dual-analog-3 Thrustmaster Firestorm Dual Analog 3]. As I own such gamepad, I already added support in Py4bot:
    90 
    91 {{{
    92 #!python
    93 
    94 class Gamepad(RemoteControl):
    95     def _createFrontend(self):
    96         return Thrustmaster(settings.THRUSTMASTER_PATH)
    97 
    98     def _buildComponents(self):
    99         self._addConfig("walk")
    100         self._addConfig("body")
    101 
    102         self._addComponent(Button, command=self.selectNextConfig, key="button_008", trigger="hold")
    103         self._addComponent(Button, command=self.selectPreviousConfig, key="button_009", trigger="hold")
    104 
    105         self._addComponent(Button, command=self.robot.incBodyPosition, key="button_004", mapper=MapperSetValue(dz=+5))
    106         self._addComponent(Button, command=self.robot.incBodyPosition, key="button_005", mapper=MapperSetValue(dz=-5))
    107         self._addComponent(Joystick, configs="walk", command=GaitSequencer().walk, keys=("analog_02", "analog_03", "analog_00"), mapper=MapperWalk())
    108 
    109         self._addComponent(Analog, configs="body", command=self.robot.setBodyExtraPosition, key="analog_00", mapper=MapperSetMultiply('yaw', coef=-15))
    110         self._addComponent(Analog, configs="body", command=self.robot.setBodyExtraPosition, key="analog_03", mapper=MapperSetMultiply('pitch', coef=-15))
    111         self._addComponent(Analog, configs="body", command=self.robot.setBodyExtraPosition, key="analog_02", mapper=MapperSetMultiply('roll', coef=15))
    112 }}}
    113 
    114 We won't deep into details, here; refer to the [[UserGuideGit#Controllers|documentation]] for more informations about remote controls. This is an important part of the framework, and you  can do very smart things, like having several configurations, using several remotes for the same robot...
    115 
    116 Now, let's create our robot:
    117 
    118 {{{
    119 #!python
    120 
    121 def main():
    122     def addGait(gaitClass, gaitName):
    123         group = settings.GAIT_LEGS_GROUPS[gaitName]
    124         params = settings.GAIT_PARAMS['default']
    125         for key1, value1 in settings.GAIT_PARAMS[gaitName].items():
    126             for key2, value2 in value1.items():
    127                 params[key1][key2] = value2
    128         gait = gaitClass(gaitName, group, params)
    129         GaitManager().add(gait)
    130 
    131     addGait(GaitTripod, "tripod")
    132     addGait(GaitTetrapod, "tetrapod")
    133     addGait(GaitRiple, "riple")
    134     addGait(GaitWave, "metachronal")
    135     addGait(GaitWave, "wave")
    136     GaitManager().select("riple")
    137 
    138     robot = Hexapod()
    139 
    140     remote = Gamepad(robot)
    141 
    142     GaitSequencer().start()
    143     remote.start()
    144 
    145     robot.setBodyPosition(z=30)
    146 
    147     robot.mainLoop()
    148 
    149     remote.stop()
    150     remote.join()
    151     GaitSequencer().stop()
    152     GaitSequencer().join()
    153 
    154 
    155 if __name__ == "__main__":
    156     main()
    157 }}}
    158 
    159 First, we add a few pre-defined gaits, in order to make it walk. Then, we create the robot itself, the remote control, and start all this little world!
    160 
    161 === Settings ===
    162 
    163 Let's discuss about settings used in the previous part. See the [[UserGuideGit#Geometrydefinition|user guide]] for the body/legs geometry conventions used.
    164 
    165 {{{
    166 #!python
    167 
    168 LEGS_INDEX = ('RF', 'RM', 'RR', 'LR', 'LM', 'LF')
    169 }}}
    170 
    171 {{{LEGS_INDEX}}} contains the names used to define legs; they can be freely chosen, but these values must be used as keys for other params.
    172 
    173 {{{
    174 #!python
    175 
    176 LEGS_GEOMETRY = {
    177     'RM': {'coxa': 25, 'femur': 45, 'tibia': 65},
    178     'RF': {'coxa': 25, 'femur': 45, 'tibia': 65},
    179     'LF': {'coxa': 25, 'femur': 45, 'tibia': 65},
    180     'LM': {'coxa': 25, 'femur': 45, 'tibia': 65},
    181     'LR': {'coxa': 25, 'femur': 45, 'tibia': 65},
    182     'RR': {'coxa': 25, 'femur': 45, 'tibia': 65}
    183 }
    184 }}}
    185 
    186 {{{LEGS_GEOMETRY}}} dict contains the lengths of the different parts of the legs, in mm.
    187 
    188 {{{
    189 #!python
    190 
    191 LEGS_ORIGIN = {
    192     'RM': {'x':  50., 'y':   0., 'gamma0' :   0.},
    193     'RF': {'x':  35., 'y':  80., 'gamma0' :  30.},
    194     'LF': {'x': -35., 'y':  80., 'gamma0' : 150.},
    195     'LM': {'x': -50., 'y':   0., 'gamma0' : 180.},
    196     'LR': {'x': -35., 'y': -80., 'gamma0' : 210.},
    197     'RR': {'x':  35., 'y': -80., 'gamma0' : 330.},
    198 }
    199 }}}
    200 
    201 {{{LEGS_ORIGIN}}} dict contains the positions and orientation of the origin of the legs: {{{(x, y)}}} defines the center of rotation of '''coxa''' joint, and {{{gamma0}}} is the angle of the legs at neutral position.
    202 
    203 {{{
    204 #!python
    205 
    206 FEET_NEUTRAL = {
    207     'RM': LEGS_GEOMETRY['RM']['coxa'] + LEGS_GEOMETRY['RM']['femur'],
    208     'RF': LEGS_GEOMETRY['RF']['coxa'] + LEGS_GEOMETRY['RF']['femur'],
    209     'LF': LEGS_GEOMETRY['LF']['coxa'] + LEGS_GEOMETRY['LF']['femur'],
    210     'LM': LEGS_GEOMETRY['LM']['coxa'] + LEGS_GEOMETRY['LM']['femur'],
    211     'LR': LEGS_GEOMETRY['LR']['coxa'] + LEGS_GEOMETRY['LR']['femur'],
    212     'RR': LEGS_GEOMETRY['RR']['coxa'] + LEGS_GEOMETRY['RR']['femur'],
    213 }
    214 }}}
    215 
    216 {{{FEET_NEUTRAL}}} dict contains the feet neutral positions af all legs. This is just the distance from the legs origins, in the ground plane, along leg X axis.
    217 
    218 {{{
    219 #!python
    220 
    221 LEGS_SERVOS_MAPPING = {
    222     'RF': {'coxa':  0, 'femur':  1, 'tibia':  2},
    223     'RM': {'coxa':  4, 'femur':  5, 'tibia':  6},
    224     'RR': {'coxa':  8, 'femur':  9, 'tibia': 10},
    225     'LR': {'coxa': 15, 'femur': 14, 'tibia': 13},
    226     'LM': {'coxa': 19, 'femur': 18, 'tibia': 17},
    227     'LF': {'coxa': 23, 'femur': 22, 'tibia': 21}
    228 }
    229 }}}
    230 
    231 {{{LEGS_SERVOS_MAPPING}}} is a table to map all joints to actuators nums.
    232 
    233 {{{
    234 #!python
    235 
    236 SERVOS_CALIBRATION = {
    237      0: {'offset':   0, 'neutral': 1500, 'ratio':  0.090},  # coxa  leg RF
    238      1: {'offset': 180, 'neutral': 1500, 'ratio':  0.090},  # femur leg RF
    239      2: {'offset':  90, 'neutral': 1500, 'ratio':  0.090},  # tibia leg RF
    240 
    241      4: {'offset':   0, 'neutral': 1500, 'ratio':  0.090},  # coxa  leg RM
    242      5: {'offset': 180, 'neutral': 1500, 'ratio':  0.090},  # femur leg RM
    243      6: {'offset':  90, 'neutral': 1500, 'ratio':  0.090},  # tibia leg RM
    244 
    245      8: {'offset':   0, 'neutral': 1500, 'ratio':  0.090},  # coxa  leg RR
    246      9: {'offset': 180, 'neutral': 1500, 'ratio':  0.090},  # femur leg RR
    247     10: {'offset':  90, 'neutral': 1500, 'ratio':  0.090},  # tibia leg RR
    248 
    249     15: {'offset':   0, 'neutral': 1500, 'ratio':  0.090},  # coxa  leg LR
    250     14: {'offset': 180, 'neutral': 1500, 'ratio': -0.090},  # femur leg LR
    251     13: {'offset':  90, 'neutral': 1500, 'ratio': -0.090},  # tibia leg LR
    252 
    253     19: {'offset':   0, 'neutral': 1500, 'ratio':  0.090},  # coxa  leg LM
    254     18: {'offset': 180, 'neutral': 1500, 'ratio': -0.090},  # femur leg LM
    255     17: {'offset':  90, 'neutral': 1500, 'ratio': -0.090},  # tibia leg LM
    256 
    257     23: {'offset':   0, 'neutral': 1500, 'ratio':  0.090},  # coxa  leg LF
    258     22: {'offset': 180, 'neutral': 1500, 'ratio': -0.090},  # femur leg LF
    259     21: {'offset':  90, 'neutral': 1500, 'ratio': -0.090},  # tibia leg LF
    260 }
    261 
    262 }}}
    263 
    264 {{{SERVOS_CALIBRATION}}} contains some calibration:
    265 
    266 * {{{offset}}} is the angle between the servo reference and the real angle. See the [[FAQ#Howisgeometrydefined|FAQ]] for the real angles definition;
    267 * {{{neutral}}} is the pulse value for the neutral position of the servo;
    268 * {{{ratio}}} is angle per pulse width.
    269 
    270 Offsets may vary, depending how you mount the servos. Same, ratio sign may have to be inverted on one side, if you have a symmetrical design; all angle are always computed using trigonometric direction (CCW).
    271 
    272 See the [[UserGuideGit#Servoscalibration|user guide]] how to fine tune servos calibration.
    273 
    274 Finally:
    275 
    276 {{{
    277 #!python
    278 
    279 GAIT_LEGS_GROUPS = {
    280     'tripod':      (('RM', 'LF', 'LR'), ('RF', 'LM', 'RR')),
    281     'tetrapod':    (('RR', 'LM'), ('RF', 'LR'), ('RM', 'LF')),
    282     'riple':       (('RR',), ('LM',), ('RF',), ('LR',), ('RM',), ('LF',)),
    283     'metachronal': (('RR',), ('LM',), ('RF',), ('LR',), ('RM',), ('LF',)),
    284     'wave':        (('RR',), ('RM',), ('RF',), ('LR',), ('LM',), ('LF',))
    285 }
    286 
    287 GAIT_PARAMS = {
    288     'default': {
    289         'length': {
    290             'min': 5.,
    291             'max': 30.
    292         },
    293         'angle': {
    294             'min': 0.5,
    295             'max': 5.
    296         },
    297         'height': {
    298             'min': 20.,
    299             'max': 40.
    300         },
    301         'speed': {
    302             'min': 25.,
    303             'max': 250.
    304         }
    305     },
    306     'tripod': {
    307         'length': {
    308             'max': 40.
    309         },
    310         'angle': {
    311             'max': 10.
    312         }
    313     },
    314     'tetrapod': {},
    315     'riple': {},
    316     'metachronal': {},
    317     'wave': {}
    318 }
    319 }}}
    320 
    321 {{{GAIT_LEGS_GROUPS}}} contains the legs grouped together and controlled at the same time during the gait usage.
    322 
    323 Note that we need to define sequences, so don't forget the comma to define a tuple with a unique element.
    324 
    325 {{{GAIT_PARAMS}}} contains some additional gaits params:
    326 
    327 * {{{length}}} is the distance (range) each leg will move for an entire cycle when the robot translate at full speed;
    328 * {{{angle}}} is the angle (range) each leg will turn for an entire cycle when the robot rotate at full speed;
    329 * {{{height}}} is the height (range) the leg will lift up;
    330 * {{{speed}}} is the speed (range).
    331 
    332 These values must be adjusted for each robot, to avoid legs collisions.
    333 
    334 {{{
    335 #!python
    336 
    337 THRUSTMASTER_PATH = "/dev/input/by-id/usb-Mega_World_Thrustmaster_dual_analog_3.2-event-joystick"
    338 }}}
    339 
    340 No need for further explanations.
     19== Tutorials ==
     20
     21 * [[Tutorial1FirstRobot|Tutorial 1: First robot]]
     22 * [[Tutorial2UsingThe3DSimulator|Tutorial 1: Using the 3D simulator]]
     23 * [[Tutorial3ServosCalibration|Tutorial 3: Servos calibration]]
     24 * [[Tutorial4AddGamepadSupport|Tutorial 4: Add gamepad support]]
    34125
    34226== Tutorial 2: Using the 3D simulator ==