Module TeachMyAgent.environments.envs.bodies.walkers.MillipedeBody

Expand source code
import numpy as np
from Box2D.b2 import edgeShape, circleShape, fixtureDef, polygonShape, revoluteJointDef, contactListener

from TeachMyAgent.environments.envs.bodies.walkers.WalkerAbstractBody import WalkerAbstractBody
from TeachMyAgent.environments.envs.utils.custom_user_data import CustomBodyUserData, CustomMotorUserData

MAIN_BODY_POLYGONS = [
    [(-10, +10), (+10, +10),
     (+10, -10), (-10, -10)]
]
MAIN_BODY_BOTTOM_WIDTH = 20
SPEED_HIP     = 4
SPEED_KNEE    = 6

class MillipedeBody(WalkerAbstractBody):
    '''
        Millipede embodiment with controllable number of bodies.
    '''
    def __init__(self, scale, motors_torque=200, nb_of_bodies=4, nb_steps_under_water=600,
                 reset_on_hull_critical_contact=False):
        '''
            Creates a millipede walker.

            Each of its body has two legs. The head is considered as the rightmost body.

            Args:
                scale: Scale value used in the environment (to adapt the embodiment to its environment)
                motors_torque: Maximum torque the embodiment can use on its motors
                nb_of_bodies: Number of square bodies the agent has
                 nb_steps_under_water: How many consecutive steps the embodiment can survive under water
                reset_on_hull_critical_contact: Whether a contact detected with the head should stop the episode
        '''
        super(MillipedeBody, self).__init__(scale, motors_torque, nb_steps_under_water)
        self.LEG_DOWN = 3 / self.SCALE  # 0 = center of hull
        self.LEG_W, self.LEG_H = 4 / self.SCALE, 10 / self.SCALE
        self.TORQUE_PENALTY = 0.00035
        self.reset_on_hull_critical_contact = reset_on_hull_critical_contact

        self.nb_of_bodies = nb_of_bodies
        self.TORQUE_PENALTY = 0.00035 / self.nb_of_bodies # 1 body = 1 pair of legs

        self.AGENT_WIDTH = MAIN_BODY_BOTTOM_WIDTH / self.SCALE * self.nb_of_bodies
        self.AGENT_HEIGHT = 20 / self.SCALE + \
                            self.LEG_H * 2 - self.LEG_DOWN
        self.AGENT_CENTER_HEIGHT = self.LEG_H * 2 + self.LEG_DOWN

    def draw(self, world, init_x, init_y, force_to_center):
        MAIN_BODY_FIXTURES = [
            fixtureDef(
                shape=polygonShape(vertices=[(x / self.SCALE, y / self.SCALE) for x, y in polygon]),
                density=5.0,
                friction=0.1,
                categoryBits=0x20,
                maskBits=0x000F)
            for polygon in MAIN_BODY_POLYGONS
        ]

        LEG_FD = fixtureDef(
            shape=polygonShape(box=(self.LEG_W / 2, self.LEG_H / 2)),
            density=1.0,
            restitution=0.0,
            categoryBits=0x20,
            maskBits=0x000F)

        LOWER_FD = fixtureDef(
            shape=polygonShape(box=(0.8 * self.LEG_W / 2, self.LEG_H / 2)),
            density=1.0,
            restitution=0.0,
            categoryBits=0x20,
            maskBits=0x000F)

        init_x = init_x - MAIN_BODY_BOTTOM_WIDTH / self.SCALE * self.nb_of_bodies / 2 # initial init_x is the middle of the whole body
        previous_main_body = None
        for j in range(self.nb_of_bodies):
            main_body_x = init_x + j * (MAIN_BODY_BOTTOM_WIDTH / self.SCALE)
            main_body = world.CreateDynamicBody(
                position=(main_body_x, init_y),
                fixtures=MAIN_BODY_FIXTURES
            )
            main_body.color1 = (0.5, 0.4, 0.9)
            main_body.color2 = (0.3, 0.3, 0.5)
            main_body.ApplyForceToCenter((force_to_center, 0), True)

            # self.body_parts.append(BodyPart(main_body, True, True))
            main_body.userData = CustomBodyUserData(True, is_contact_critical=False, name="body")
            self.body_parts.append(main_body)

            for i in [-1, +1]:
                leg = world.CreateDynamicBody(
                    position=(main_body_x, init_y - self.LEG_H / 2 - self.LEG_DOWN),
                    #angle=(i * 0.05),
                    fixtures=LEG_FD
                )
                leg.color1 = (0.6 - i / 10., 0.3 - i / 10., 0.5 - i / 10.)
                leg.color2 = (0.4 - i / 10., 0.2 - i / 10., 0.3 - i / 10.)
                rjd = revoluteJointDef(
                    bodyA=main_body,
                    bodyB=leg,
                    anchor=(main_body_x, init_y - self.LEG_DOWN),
                    enableMotor=True,
                    enableLimit=True,
                    maxMotorTorque=self.MOTORS_TORQUE,
                    motorSpeed=i,
                    lowerAngle=-0.8,
                    upperAngle=1.1,
                )

                leg.userData = CustomBodyUserData(False, name="leg")
                self.body_parts.append(leg)

                joint_motor = world.CreateJoint(rjd)
                joint_motor.userData = CustomMotorUserData(SPEED_HIP, False)
                self.motors.append(joint_motor)

                lower = world.CreateDynamicBody(
                    position=(main_body_x, init_y - self.LEG_H * 3 / 2 - self.LEG_DOWN),
                    #angle=(i * 0.05),
                    fixtures=LOWER_FD
                )
                lower.color1 = (0.6 - i / 10., 0.3 - i / 10., 0.5 - i / 10.)
                lower.color2 = (0.4 - i / 10., 0.2 - i / 10., 0.3 - i / 10.)
                rjd = revoluteJointDef(
                    bodyA=leg,
                    bodyB=lower,
                    anchor=(main_body_x, init_y - self.LEG_DOWN - self.LEG_H),
                    enableMotor=True,
                    enableLimit=True,
                    maxMotorTorque=self.MOTORS_TORQUE,
                    motorSpeed=1,
                    lowerAngle=-1.6,
                    upperAngle=-0.1,
                )

                lower.userData = CustomBodyUserData(True, name="lower")
                self.body_parts.append(lower)

                joint_motor = world.CreateJoint(rjd)
                joint_motor.userData = CustomMotorUserData(
                    SPEED_KNEE,
                    True,
                    contact_body=lower,
                    angle_correction=1.0)
                self.motors.append(joint_motor)

            if previous_main_body is not None:
                rjd = revoluteJointDef(
                    bodyA=previous_main_body,
                    bodyB=main_body,
                    anchor=(main_body_x - MAIN_BODY_BOTTOM_WIDTH / self.SCALE / 2, init_y),
                    enableMotor=False,
                    enableLimit=True,
                    lowerAngle=-0.05 * np.pi,
                    upperAngle=0.05 * np.pi,
                )

                world.CreateJoint(rjd)
            previous_main_body = main_body

        self.reference_head_object = previous_main_body  # set as head the rightmost body
        self.reference_head_object.is_contact_critical = self.reference_head_object

Classes

class MillipedeBody (scale, motors_torque=200, nb_of_bodies=4, nb_steps_under_water=600, reset_on_hull_critical_contact=False)

Millipede embodiment with controllable number of bodies.

Creates a millipede walker.

Each of its body has two legs. The head is considered as the rightmost body.

Args

scale
Scale value used in the environment (to adapt the embodiment to its environment)
motors_torque
Maximum torque the embodiment can use on its motors
nb_of_bodies
Number of square bodies the agent has
nb_steps_under_water: How many consecutive steps the embodiment can survive under water
reset_on_hull_critical_contact
Whether a contact detected with the head should stop the episode
Expand source code
class MillipedeBody(WalkerAbstractBody):
    '''
        Millipede embodiment with controllable number of bodies.
    '''
    def __init__(self, scale, motors_torque=200, nb_of_bodies=4, nb_steps_under_water=600,
                 reset_on_hull_critical_contact=False):
        '''
            Creates a millipede walker.

            Each of its body has two legs. The head is considered as the rightmost body.

            Args:
                scale: Scale value used in the environment (to adapt the embodiment to its environment)
                motors_torque: Maximum torque the embodiment can use on its motors
                nb_of_bodies: Number of square bodies the agent has
                 nb_steps_under_water: How many consecutive steps the embodiment can survive under water
                reset_on_hull_critical_contact: Whether a contact detected with the head should stop the episode
        '''
        super(MillipedeBody, self).__init__(scale, motors_torque, nb_steps_under_water)
        self.LEG_DOWN = 3 / self.SCALE  # 0 = center of hull
        self.LEG_W, self.LEG_H = 4 / self.SCALE, 10 / self.SCALE
        self.TORQUE_PENALTY = 0.00035
        self.reset_on_hull_critical_contact = reset_on_hull_critical_contact

        self.nb_of_bodies = nb_of_bodies
        self.TORQUE_PENALTY = 0.00035 / self.nb_of_bodies # 1 body = 1 pair of legs

        self.AGENT_WIDTH = MAIN_BODY_BOTTOM_WIDTH / self.SCALE * self.nb_of_bodies
        self.AGENT_HEIGHT = 20 / self.SCALE + \
                            self.LEG_H * 2 - self.LEG_DOWN
        self.AGENT_CENTER_HEIGHT = self.LEG_H * 2 + self.LEG_DOWN

    def draw(self, world, init_x, init_y, force_to_center):
        MAIN_BODY_FIXTURES = [
            fixtureDef(
                shape=polygonShape(vertices=[(x / self.SCALE, y / self.SCALE) for x, y in polygon]),
                density=5.0,
                friction=0.1,
                categoryBits=0x20,
                maskBits=0x000F)
            for polygon in MAIN_BODY_POLYGONS
        ]

        LEG_FD = fixtureDef(
            shape=polygonShape(box=(self.LEG_W / 2, self.LEG_H / 2)),
            density=1.0,
            restitution=0.0,
            categoryBits=0x20,
            maskBits=0x000F)

        LOWER_FD = fixtureDef(
            shape=polygonShape(box=(0.8 * self.LEG_W / 2, self.LEG_H / 2)),
            density=1.0,
            restitution=0.0,
            categoryBits=0x20,
            maskBits=0x000F)

        init_x = init_x - MAIN_BODY_BOTTOM_WIDTH / self.SCALE * self.nb_of_bodies / 2 # initial init_x is the middle of the whole body
        previous_main_body = None
        for j in range(self.nb_of_bodies):
            main_body_x = init_x + j * (MAIN_BODY_BOTTOM_WIDTH / self.SCALE)
            main_body = world.CreateDynamicBody(
                position=(main_body_x, init_y),
                fixtures=MAIN_BODY_FIXTURES
            )
            main_body.color1 = (0.5, 0.4, 0.9)
            main_body.color2 = (0.3, 0.3, 0.5)
            main_body.ApplyForceToCenter((force_to_center, 0), True)

            # self.body_parts.append(BodyPart(main_body, True, True))
            main_body.userData = CustomBodyUserData(True, is_contact_critical=False, name="body")
            self.body_parts.append(main_body)

            for i in [-1, +1]:
                leg = world.CreateDynamicBody(
                    position=(main_body_x, init_y - self.LEG_H / 2 - self.LEG_DOWN),
                    #angle=(i * 0.05),
                    fixtures=LEG_FD
                )
                leg.color1 = (0.6 - i / 10., 0.3 - i / 10., 0.5 - i / 10.)
                leg.color2 = (0.4 - i / 10., 0.2 - i / 10., 0.3 - i / 10.)
                rjd = revoluteJointDef(
                    bodyA=main_body,
                    bodyB=leg,
                    anchor=(main_body_x, init_y - self.LEG_DOWN),
                    enableMotor=True,
                    enableLimit=True,
                    maxMotorTorque=self.MOTORS_TORQUE,
                    motorSpeed=i,
                    lowerAngle=-0.8,
                    upperAngle=1.1,
                )

                leg.userData = CustomBodyUserData(False, name="leg")
                self.body_parts.append(leg)

                joint_motor = world.CreateJoint(rjd)
                joint_motor.userData = CustomMotorUserData(SPEED_HIP, False)
                self.motors.append(joint_motor)

                lower = world.CreateDynamicBody(
                    position=(main_body_x, init_y - self.LEG_H * 3 / 2 - self.LEG_DOWN),
                    #angle=(i * 0.05),
                    fixtures=LOWER_FD
                )
                lower.color1 = (0.6 - i / 10., 0.3 - i / 10., 0.5 - i / 10.)
                lower.color2 = (0.4 - i / 10., 0.2 - i / 10., 0.3 - i / 10.)
                rjd = revoluteJointDef(
                    bodyA=leg,
                    bodyB=lower,
                    anchor=(main_body_x, init_y - self.LEG_DOWN - self.LEG_H),
                    enableMotor=True,
                    enableLimit=True,
                    maxMotorTorque=self.MOTORS_TORQUE,
                    motorSpeed=1,
                    lowerAngle=-1.6,
                    upperAngle=-0.1,
                )

                lower.userData = CustomBodyUserData(True, name="lower")
                self.body_parts.append(lower)

                joint_motor = world.CreateJoint(rjd)
                joint_motor.userData = CustomMotorUserData(
                    SPEED_KNEE,
                    True,
                    contact_body=lower,
                    angle_correction=1.0)
                self.motors.append(joint_motor)

            if previous_main_body is not None:
                rjd = revoluteJointDef(
                    bodyA=previous_main_body,
                    bodyB=main_body,
                    anchor=(main_body_x - MAIN_BODY_BOTTOM_WIDTH / self.SCALE / 2, init_y),
                    enableMotor=False,
                    enableLimit=True,
                    lowerAngle=-0.05 * np.pi,
                    upperAngle=0.05 * np.pi,
                )

                world.CreateJoint(rjd)
            previous_main_body = main_body

        self.reference_head_object = previous_main_body  # set as head the rightmost body
        self.reference_head_object.is_contact_critical = self.reference_head_object

Ancestors

Inherited members