ClementRomac's picture
ClementRomac HF Staff
Added interactive demo with some policies
09a6f7f
// Head
HULL_POLYGON = [
[-20, +12], [+6, +12],
[+15, +4], [+15, -4],
[+6, -12], [-20, -12]
];
BODY_P1 = [
[-8, +9], [+8, +12],
[+8, -12], [-8, -9]
];
BODY_P2 = [
[-8, +4], [+8, +9],
[+8, -9], [-8, -4]
];
// Tail
BODY_P3 = [
[-4, +2], [+4, +4],
[+4, -4], [-4, -2]
];
FIN = [
[-1, -10], [-1, +10],
[+1, +10], [+1, -10]
];
HULL_BOTTOM_WIDTH = 35;
const SPEED = 6;
/**
* @classdesc Fish morphology.
*/
class FishBody extends SwimmerAbstractBody {
/**
* @constructor
* @param scale {number} - Scale of the environment
* @param motors_torque {number}
* @param density {number} - Density of the agent's body.
* @param nb_steps_outside_water {number}
*/
constructor(scale, motors_torque=80, density, nb_steps_outside_water=600) {
super(scale, motors_torque, density, nb_steps_outside_water);
this.TORQUE_PENALTY = 0.00035;
this.AGENT_WIDTH = HULL_BOTTOM_WIDTH / this.SCALE;
this.AGENT_HEIGHT = 18 / this.SCALE;
this.AGENT_CENTER_HEIGHT = 9 / this.SCALE;
this.remove_reward_on_head_angle = true;
this.fins = [];
this.tail = null;
}
draw(world, init_x, init_y){
let vertices;
let rjd;
let joint_motor;
// HULL
let hull_fd = new b2.FixtureDef();
hull_fd.shape = new b2.PolygonShape();
vertices = [];
for(let vertex of HULL_POLYGON){
vertices.push(new b2.Vec2(vertex[0] / this.SCALE, vertex[1] / this.SCALE));
}
hull_fd.shape.Set(vertices, HULL_POLYGON.length);
hull_fd.density = this.DENSITY;
hull_fd.friction = 0.1;
hull_fd.filter.categoryBits = 0x20;
hull_fd.filter.maskBits = 0x000F; // 0.99 bouncy
let hull_bd = new b2.BodyDef();
hull_bd.type = b2.Body.b2_dynamicBody;
hull_bd.position.Set(init_x, init_y);
let hull = world.CreateBody(hull_bd);
hull.CreateFixture(hull_fd);
hull.color1 = "#806682"; // [0.5, 0.4, 0.9]
hull.color2 = "#4D4D80";
hull.SetUserData(new CustomBodyUserData(true, false, "head"));
this.body_parts.push(hull);
this.reference_head_object = hull;
// BODY_P1
let body_p1_x = init_x - 35 / 2 / this.SCALE - 16 / 2 / this.SCALE;
let body_p1_fd = new b2.FixtureDef();
body_p1_fd.shape = new b2.PolygonShape();
vertices = [];
for(let vertex of BODY_P1){
vertices.push(new b2.Vec2(vertex[0] / this.SCALE, vertex[1] / this.SCALE));
}
body_p1_fd.shape.Set(vertices, BODY_P1.length);
body_p1_fd.density = this.DENSITY;
body_p1_fd.restitution = 0.0;
body_p1_fd.filter.categoryBits = 0x20;
body_p1_fd.filter.maskBits = 0x000F; // 0.99 bouncy
let body_p1_bd = new b2.BodyDef();
body_p1_bd.type = b2.Body.b2_dynamicBody;
body_p1_bd.position.Set(body_p1_x, init_y);
let body_p1 = world.CreateBody(body_p1_bd);
body_p1.CreateFixture(body_p1_fd);
body_p1.color1 = "#806682"; // [0.5, 0.4, 0.9]
body_p1.color2 = "#4D4D80";
body_p1.SetUserData(new CustomBodyUserData(true, false, "body"));
this.body_parts.push(body_p1);
// Revolute joint between HULL and BODY_P1
rjd = new b2.RevoluteJointDef();
rjd.Initialize(hull, body_p1, new b2.Vec2(init_x - 35 / 2 / this.SCALE, init_y));
rjd.enableMotor = true;
rjd.enableLimit = true;
rjd.maxMotorTorque = this.MOTORS_TORQUE;
rjd.motorSpeed = 1;
rjd.lowerAngle = -0.1 * Math.PI;
rjd.upperAngle = 0.2 * Math.PI;
joint_motor = world.CreateJoint(rjd);
joint_motor.SetUserData(new CustomMotorUserData("neck", SPEED, true, 0.0, body_p1));
this.motors.push(joint_motor);
// BODY_P2
let body_p2_x = body_p1_x - 16 / 2 / this.SCALE - 16 / 2 / this.SCALE;
let body_p2_fd = new b2.FixtureDef();
body_p2_fd.shape = new b2.PolygonShape();
vertices = [];
for(let vertex of BODY_P2){
vertices.push(new b2.Vec2(vertex[0] / this.SCALE, vertex[1] / this.SCALE));
}
body_p2_fd.shape.Set(vertices, BODY_P2.length);
body_p2_fd.density = this.DENSITY;
body_p2_fd.restitution = 0.0;
body_p2_fd.filter.categoryBits = 0x20;
body_p2_fd.filter.maskBits = 0x000F;
let body_p2_bd = new b2.BodyDef();
body_p2_bd.type = b2.Body.b2_dynamicBody;
body_p2_bd.position.Set(body_p2_x, init_y);
let body_p2 = world.CreateBody(body_p2_bd);
body_p2.CreateFixture(body_p2_fd);
body_p2.color1 = "#806682"; // [0.5, 0.4, 0.9]
body_p2.color2 = "#4D4D80";
body_p2.SetUserData(new CustomBodyUserData(true, false, "body"));
this.body_parts.push(body_p2);
// Revolute joint between BODY_P1 and BODY_P2
rjd = new b2.RevoluteJointDef();
rjd.Initialize(body_p1, body_p2, new b2.Vec2(body_p1_x - 16 / 2 / this.SCALE, init_y));
rjd.enableMotor = true;
rjd.enableLimit = true;
rjd.maxMotorTorque = this.MOTORS_TORQUE;
rjd.motorSpeed = 1;
rjd.lowerAngle = -0.15 * Math.PI;
rjd.upperAngle = 0.15 * Math.PI;
joint_motor = world.CreateJoint(rjd);
joint_motor.SetUserData(new CustomMotorUserData("hip", SPEED, true, 0.0, body_p2));
this.motors.push(joint_motor);
// BODY_P3 - TAIL
let body_p3_x = body_p2_x - 16 / 2 / this.SCALE - 8 / 2 / this.SCALE;
let body_p3_fd = new b2.FixtureDef();
body_p3_fd.shape = new b2.PolygonShape();
vertices = [];
for(let vertex of BODY_P3){
vertices.push(new b2.Vec2(vertex[0] / this.SCALE, vertex[1] / this.SCALE));
}
body_p3_fd.shape.Set(vertices, BODY_P3.length);
body_p3_fd.density = this.DENSITY;
body_p3_fd.restitution = 0.0;
body_p3_fd.filter.categoryBits = 0x20;
body_p3_fd.filter.maskBits = 0x000F;
let body_p3_bd = new b2.BodyDef();
body_p3_bd.type = b2.Body.b2_dynamicBody;
body_p3_bd.position.Set(body_p3_x, init_y);
let body_p3 = world.CreateBody(body_p3_bd);
body_p3.CreateFixture(body_p3_fd);
body_p3.color1 = "#806682"; // [0.5, 0.4, 0.9]
body_p3.color2 = "#4D4D80";
body_p3.SetUserData(new CustomBodyUserData(true, false, "body"));
this.body_parts.push(body_p3);
this.tail = body_p3;
// Revolute joint between BODY_P2 and BODY_P3
rjd = new b2.RevoluteJointDef();
rjd.Initialize(body_p2, body_p3, new b2.Vec2(body_p2_x - 16 / 2 / this.SCALE, init_y));
rjd.enableMotor = true;
rjd.enableLimit = true;
rjd.maxMotorTorque = this.MOTORS_TORQUE;
rjd.motorSpeed = 1;
rjd.lowerAngle = -0.3 * Math.PI;
rjd.upperAngle = 0.3 * Math.PI;
joint_motor = world.CreateJoint(rjd);
joint_motor.SetUserData(new CustomMotorUserData("knee", SPEED, true, 0.0, body_p3));
this.motors.push(joint_motor);
// FINS
let fin_fd = new b2.FixtureDef();
fin_fd.shape = new b2.PolygonShape();
vertices = [];
for(let vertex of FIN){
vertices.push(new b2.Vec2(vertex[0] / this.SCALE, vertex[1] / this.SCALE));
}
fin_fd.shape.Set(vertices, FIN.length);
fin_fd.density = this.DENSITY;
fin_fd.restitution = 0.0;
fin_fd.filter.categoryBits = 0x20;
fin_fd.filter.maskBits = 0x000F;
let fin_positions = [
[init_x, init_y - 22 / 2 / this.SCALE + 0.2],
];
let fin_angle = -0.2 * Math.PI;
let middle_fin_x_distance = Math.sin(fin_angle) * 20 / 2 / this.SCALE;
let middle_fin_y_distance = Math.cos(fin_angle) * 20 / 2 / this.SCALE;
for(let fin_pos of fin_positions){
let current_fin_x = fin_pos[0] + middle_fin_x_distance;
let current_fin_y = fin_pos[1] - middle_fin_y_distance;
let fin_bd = new b2.BodyDef();
fin_bd.type = b2.Body.b2_dynamicBody;
fin_bd.position.Set(current_fin_x, current_fin_y);
let fin = world.CreateBody(fin_bd);
fin.CreateFixture(fin_fd);
fin.color1 = "#806682"; // [0.5, 0.4, 0.9]
fin.color2 = "#4D4D80";
fin.SetUserData(new CustomBodyUserData(true, false, "fin"));
this.body_parts.push(fin);
this.fins.push(fin);
// Revolute joint between HULL and FIN
rjd = new b2.RevoluteJointDef();
rjd.Initialize(hull, fin, new b2.Vec2(fin_pos[0], fin_pos[1]));
rjd.enableMotor = true;
rjd.enableLimit = true;
rjd.maxMotorTorque = this.MOTORS_TORQUE;
rjd.motorSpeed = 1;
rjd.lowerAngle = -0.3 * Math.PI;
rjd.upperAngle = 0.2 * Math.PI;
joint_motor = world.CreateJoint(rjd);
joint_motor.SetUserData(new CustomMotorUserData("shoulder", SPEED, true, 0.0, fin));
this.motors.push(joint_motor);
}
}
}