| 310 | | }}} |
| 311 | | |
| | 310 | #!/usr/bin/env python |
| | 311 | # -*- coding: utf-8 -*- |
| | 312 | |
| | 313 | import math |
| | 314 | |
| | 315 | import visual |
| | 316 | |
| | 317 | from py4bot.api import * |
| | 318 | |
| | 319 | import settings |
| | 320 | }}} |
| | 321 | |
| | 322 | Nothing fancy, here. |
| | 323 | |
| | 324 | {{{ |
| | 325 | #!python |
| | 326 | |
| | 327 | class Hexapod3D(Robot3D): |
| | 328 | def _createBody(self): |
| | 329 | return Body(settings.LEGS_ORIGIN) |
| | 330 | |
| | 331 | def _createLegs(self): |
| | 332 | legs = {} |
| | 333 | legIk = {} |
| | 334 | for legIndex in settings.LEGS_INDEX: |
| | 335 | legs[legIndex] = Leg3Dof(legIndex, {'coxa': Coxa(), 'femur': Femur(), 'tibia': Tibia()}, settings.FEET_NEUTRAL[legIndex]) |
| | 336 | legIk[legIndex] = Leg3DofIk(settings.LEGS_GEOMETRY[legIndex]) |
| | 337 | |
| | 338 | return legs, legIk |
| | 339 | }}} |
| | 340 | |
| | 341 | Same as before, except that we inherits from {{{Robot3D}}}, instead of {{{Robot}}}. |
| | 342 | |
| | 343 | {{{ |
| | 344 | #!python |
| | 345 | |
| | 346 | def _createActuatorPool(self): |
| | 347 | driver = Driver3D() |
| | 348 | pool = ActuatorPool(driver) |
| | 349 | |
| | 350 | num = 0 |
| | 351 | for leg in self._legs.values(): |
| | 352 | |
| | 353 | # Create joints actuators |
| | 354 | coxa3D = Coxa3D(leg.coxa, num, settings.LEGS_GEOMETRY[leg.index]['coxa']) |
| | 355 | pool.add(coxa3D) |
| | 356 | num += 1 |
| | 357 | |
| | 358 | femur3D = Femur3D(leg.femur, num, settings.LEGS_GEOMETRY[leg.index]['femur']) |
| | 359 | pool.add(femur3D) |
| | 360 | num += 1 |
| | 361 | |
| | 362 | tibia3D = Tibia3D(leg.tibia, num, settings.LEGS_GEOMETRY[leg.index]['tibia']) |
| | 363 | pool.add(tibia3D) |
| | 364 | num += 1 |
| | 365 | |
| | 366 | # Init 3D view |
| | 367 | coxa3D.frame = self._body3D |
| | 368 | femur3D.frame = coxa3D |
| | 369 | tibia3D.frame = femur3D |
| | 370 | |
| | 371 | coxa3D.pos = (settings.LEGS_ORIGIN[leg.index]['x'], 0, -settings.LEGS_ORIGIN[leg.index]['y']) |
| | 372 | femur3D.pos = (settings.LEGS_GEOMETRY[leg.index]['coxa'], 0, 0) |
| | 373 | tibia3D.pos = (settings.LEGS_GEOMETRY[leg.index]['femur'], 0, 0) |
| | 374 | |
| | 375 | coxa3D.rotate(angle=math.radians(settings.LEGS_ORIGIN[leg.index]['gamma0']), axis=(0, 1, 0)) |
| | 376 | |
| | 377 | return pool |
| | 378 | }}} |
| | 379 | |
| | 380 | Here, we use specific 3D actuators: {{{Coxa3D}}}, {{{Femur3D}}}, {{{Tibia3D}}}; they all use the [http://vpython.org VPython] library to represent themselves on the screen. |
| | 381 | |
| | 382 | Note that we don't use a custom actuator mapping; nums are automatically incremented. |
| | 383 | |
| | 384 | We also need to configure the hierarchy, and the relative positions of the actuators. |
| | 385 | |
| | 386 | ''Note: this may change in the future; I'm working on a simpler way to do all this''. |
| | 387 | |
| | 388 | {{{ |
| | 389 | #!python |
| | 390 | |
| | 391 | class Gamepad(RemoteControl): |
| | 392 | def _createFrontend(self): |
| | 393 | return Thrustmaster(settings.THRUSTMASTER_PATH) |
| | 394 | |
| | 395 | def _buildComponents(self): |
| | 396 | self._addConfig("walk") |
| | 397 | self._addConfig("body") |
| | 398 | |
| | 399 | self._addComponent(Button, command=self.selectNextConfig, key="button_08", trigger="hold") |
| | 400 | self._addComponent(Button, command=self.selectPreviousConfig, key="button_09", trigger="hold") |
| | 401 | |
| | 402 | self._addComponent(Button, command=self.robot.incBodyPosition, key="button_004", mapper=MapperSetValue(dz=+5)) |
| | 403 | self._addComponent(Button, command=self.robot.incBodyPosition, key="button_005", mapper=MapperSetValue(dz=-5)) |
| | 404 | self._addComponent(Joystick, configs="walk", command=GaitSequencer().walk, keys=("analog_02", "analog_03", "analog_00"), mapper=MapperWalk()) |
| | 405 | |
| | 406 | self._addComponent(Analog, configs="body", command=self.robot.setBodyExtraPosition, key="analog_00", mapper=MapperSetMultiply('yaw', coef=-15)) |
| | 407 | self._addComponent(Analog, configs="body", command=self.robot.setBodyExtraPosition, key="analog_03", mapper=MapperSetMultiply('pitch', coef=-15)) |
| | 408 | self._addComponent(Analog, configs="body", command=self.robot.setBodyExtraPosition, key="analog_02", mapper=MapperSetMultiply('roll', coef=15)) |
| | 409 | }}} |
| | 410 | |
| | 411 | Same as before... |
| | 412 | |
| | 413 | {{{ |
| | 414 | #!python |
| | 415 | |
| | 416 | def main(): |
| | 417 | scene = visual.display(width=settings.SCENE_WIDTH, height=settings.SCENE_HEIGHT) |
| | 418 | 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) |
| | 419 | |
| | 420 | def addGait(gaitClass, gaitName): |
| | 421 | gait = gaitClass(gaitName, settings.GAIT_LEGS_GROUPS[gaitName], settings.GAIT_PARAMS[gaitName]) |
| | 422 | GaitManager().add(gait) |
| | 423 | |
| | 424 | addGait(GaitTripod, "tripod") |
| | 425 | addGait(GaitTetrapod, "tetrapod") |
| | 426 | addGait(GaitRiple, "riple") |
| | 427 | addGait(GaitWave, "metachronal") |
| | 428 | addGait(GaitWave, "wave") |
| | 429 | GaitManager().select("riple") |
| | 430 | |
| | 431 | robot = Hexapod3D() |
| | 432 | |
| | 433 | remote = Gamepad(robot) |
| | 434 | |
| | 435 | GaitSequencer().start() |
| | 436 | remote.start() |
| | 437 | |
| | 438 | robot.setBodyPosition(z=30) |
| | 439 | |
| | 440 | robot.mainLoop() |
| | 441 | |
| | 442 | remote.stop() |
| | 443 | remote.join() |
| | 444 | GaitSequencer().stop() |
| | 445 | GaitSequencer().join() |
| | 446 | |
| | 447 | if __name__ == "__main__": |
| | 448 | main() |
| | 449 | }}} |
| | 450 | |
| | 451 | The only difference, here, is the scene size init, and the ground addition (optional). |