[[PageOutline(2-5, Table of Contents, floated)]] = Tutorial 2: Using the 3D simulator = ''Files for this tutorial are available in [https://framagit.org/fma38/Py4bot/tree/master/py4bot/examples/tutorial_2 py4bot/examples/tutorial_2/]'' Here, we will see how to use the 3D simulation stuff. As before, we create a new empty dir in our home dir: {{{ $ mkdir tutorial_2 $ cd tutorial_2 }}} Let's create another '''{{{hexapod.py}}}''' file: {{{ #!python #!/usr/bin/env python # -*- coding: utf-8 -*- import math import visual from py4bot.api import * import settings }}} Nothing fancy, here. {{{ #!python class Hexapod3D(Robot3D): def _createBody(self): return Body(settings.LEGS_ORIGIN) def _createLegs(self): legs = {} legIk = {} for legIndex in settings.LEGS_INDEX: legs[legIndex] = Leg3Dof(legIndex, {'coxa': Coxa(), 'femur': Femur(), 'tibia': Tibia()}, settings.FEET_NEUTRAL[legIndex]) legIk[legIndex] = Leg3DofIk(settings.LEGS_GEOMETRY[legIndex]) return legs, legIk }}} Same as before, except that we inherits from {{{Robot3D}}}, instead of {{{Robot}}}. {{{ #!python def _createActuatorPool(self): driver = Driver3D() pool = ActuatorPool(driver) num = 0 for leg in self._legs.values(): # Create joints actuators coxa3D = Coxa3D(leg.coxa, num, settings.LEGS_GEOMETRY[leg.index]['coxa']) pool.add(coxa3D) num += 1 femur3D = Femur3D(leg.femur, num, settings.LEGS_GEOMETRY[leg.index]['femur']) pool.add(femur3D) num += 1 tibia3D = Tibia3D(leg.tibia, num, settings.LEGS_GEOMETRY[leg.index]['tibia']) pool.add(tibia3D) num += 1 # Init 3D view coxa3D.frame = self._body3D femur3D.frame = coxa3D tibia3D.frame = femur3D coxa3D.pos = (settings.LEGS_ORIGIN[leg.index]['x'], 0, -settings.LEGS_ORIGIN[leg.index]['y']) femur3D.pos = (settings.LEGS_GEOMETRY[leg.index]['coxa'], 0, 0) tibia3D.pos = (settings.LEGS_GEOMETRY[leg.index]['femur'], 0, 0) coxa3D.rotate(angle=math.radians(settings.LEGS_ORIGIN[leg.index]['gamma0']), axis=(0, 1, 0)) return pool }}} Here, we use specific 3D actuators: {{{Coxa3D}}}, {{{Femur3D}}}, {{{Tibia3D}}}; they all use the [http://vpython.org VPython] library to represent themselves on the screen. Note that we don't use a custom actuator mapping; nums are automatically incremented. We also need to configure the hierarchy, and the relative positions of the actuators. ''Note: this may change in the future; I'm working on a simpler way to do all this''. {{{ #!python class Gamepad(RemoteControl): def _createFrontend(self): return Thrustmaster(settings.THRUSTMASTER_PATH) def _buildComponents(self): self._addConfig("walk") self._addConfig("body") self._addComponent(Button, command=self.selectNextConfig, key="digital_008", trigger="hold") self._addComponent(Button, command=self.selectPreviousConfig, key="digital_009", trigger="hold") self._addComponent(Button, command=self.robot.incBodyPosition, key="digital_004", mapper=MapperSetValue(dz=+5)) self._addComponent(Button, command=self.robot.incBodyPosition, key="digital_005", mapper=MapperSetValue(dz=-5)) self._addComponent(Joystick, configs="walk", command=GaitSequencer().walk, keys=("analog_02", "analog_03", "analog_00"), mapper=MapperWalk()) self._addComponent(Axis, configs="body", command=self.robot.setBodyExtraPosition, key="analog_00", mapper=MapperSetMultiply('yaw', coef=-15)) self._addComponent(Axis, configs="body", command=self.robot.setBodyExtraPosition, key="analog_03", mapper=MapperSetMultiply('pitch', coef=-15)) self._addComponent(Axis, configs="body", command=self.robot.setBodyExtraPosition, key="analog_02", mapper=MapperSetMultiply('roll', coef=15)) }}} Same as before... {{{ #!python def main(): def addGait(gaitClass, gaitName): group = settings.GAIT_LEGS_GROUPS[gaitName] params = settings.GAIT_PARAMS['default'] for key1, value1 in settings.GAIT_PARAMS[gaitName].items(): for key2, value2 in value1.items(): params[key1][key2] = value2 gait = gaitClass(gaitName, group, params) GaitManager().add(gait) scene = visual.display(width=settings.SCENE_WIDTH, height=settings.SCENE_HEIGHT) ground = visual.box(axis=(0, 1, 0), pos=(0, 0, 0), length=0.1, height=500, width=500, color=visual.color.gray(0.75), opacity=0.5) addGait(GaitTripod, "tripod") addGait(GaitTetrapod, "tetrapod") addGait(GaitRiple, "riple") addGait(GaitWave, "metachronal") addGait(GaitWave, "wave") GaitManager().select("riple") robot = Hexapod3D() remote = Gamepad(robot) GaitSequencer().start() remote.start() robot.setBodyPosition(z=30) robot.mainLoop() remote.stop() remote.join() GaitSequencer().stop() GaitSequencer().join() if __name__ == "__main__": main() }}} The only difference, here, is the scene size init, and the ground addition (optional). == Settings == {{{ #!python # -*- coding: utf-8 -*- from py4bot.common import config # Legs index LEGS_INDEX = ('RF', 'RM', 'RR', 'LR', 'LM', 'LF') # Legs geometry LEGS_GEOMETRY = { 'RM': {'coxa': 25, 'femur': 45, 'tibia': 65}, 'RF': {'coxa': 25, 'femur': 45, 'tibia': 65}, 'LF': {'coxa': 25, 'femur': 45, 'tibia': 65}, 'LM': {'coxa': 25, 'femur': 45, 'tibia': 65}, 'LR': {'coxa': 25, 'femur': 45, 'tibia': 65}, 'RR': {'coxa': 25, 'femur': 45, 'tibia': 65} } # Legs origin LEGS_ORIGIN = { 'RM': {'x': 35., 'y': 0., 'gamma0' : 0.}, 'RF': {'x': 35., 'y': 65., 'gamma0' : 30.}, 'LF': {'x': -35., 'y': 65., 'gamma0' : 150.}, 'LM': {'x': -35., 'y': 0., 'gamma0' : 180.}, 'LR': {'x': -35., 'y': -65., 'gamma0' : 210.}, 'RR': {'x': 35., 'y': -65., 'gamma0' : 330.}, } # Legs feet neutral position FEET_NEUTRAL = { 'RM': LEGS_GEOMETRY['RM']['coxa'] + LEGS_GEOMETRY['RM']['femur'], 'RF': LEGS_GEOMETRY['RF']['coxa'] + LEGS_GEOMETRY['RF']['femur'], 'LF': LEGS_GEOMETRY['LF']['coxa'] + LEGS_GEOMETRY['LF']['femur'], 'LM': LEGS_GEOMETRY['LM']['coxa'] + LEGS_GEOMETRY['LM']['femur'], 'LR': LEGS_GEOMETRY['LR']['coxa'] + LEGS_GEOMETRY['LR']['femur'], 'RR': LEGS_GEOMETRY['RR']['coxa'] + LEGS_GEOMETRY['RR']['femur'], } # Gaits GAIT_LEGS_GROUPS = { 'tripod': (('RM', 'LF', 'LR'), ('RF', 'LM', 'RR')), 'tetrapod': (('RR', 'LM'), ('RF', 'LR'), ('RM', 'LF')), 'riple': (('RR',), ('LM',), ('RF',), ('LR',), ('RM',), ('LF',)), 'metachronal': (('RR',), ('LM',), ('RF',), ('LR',), ('RM',), ('LF',)), 'wave': (('RR',), ('RM',), ('RF',), ('LR',), ('LM',), ('LF',)) } # Gaits parameters GAIT_PARAMS = { 'default': { 'length': { 'min':30., 'angle': 2.5, 'height': { 'min': 20., 'max': 40. }, 'speed': { 'min': 25., 'max': 250. } }, 'tripod': {}, 'tetrapod': {}, 'riple': {}, 'metachronal': {}, 'wave': {} } # Gamepad path THRUSTMASTER_PATH = "/dev/input/by-id/usb-Mega_World_Thrustmaster_dual_analog_3.2-event-joystick" # VPython scene default size SCENE_WIDTH = 640 SCENE_HEIGHT = 480 }}} We removed the servos mapping/calibration, as we don't need them anymore, and we just added the scene default size. In the [wiki:Tutorial3ServosCalibration next tutorial], we will see how to fine tune {{{SERVOS_CALIBRATION}}} table.