diff --git a/README.md b/README.md index 1aa4d7b..69507c4 100644 --- a/README.md +++ b/README.md @@ -20,19 +20,22 @@ TODO: Write usage instructions ## TODO -- make walker into an env, with reset, done etc -- display save loss curves... everything from info -- make diff env's, copy humanoidwalker, humanoid, learning2run etc -- display - - display/save time for each agent - - globals.step_counter - - x progress - - smile frown? - - save checkpoints +- [x] make walker into an env, with reset, done etc +- [x] display save loss curves... everything from info +- [x] make diff env's, copy humanoidwalker, humanoid, learning2run etc +- [ ] display + - [x] display/save time for each agent + - [x] globals.step_counter + - [x] x progress + - [ ] smile frown? + - [ ] fix my frameskip + - [x] save checkpoints - [ ] obstacles later on -- [ ] load pretrained -- [ ] reward hacking - - [ ] head high, feet on ground, legs crossing, moving forward, angles near 0 + - [x] balls + - [ ] Allow user to chuck bouncing balls. Some can remain as obstacles +- [x] load pretrained +- [x] reward hacking + - [x] head high, feet on ground, legs crossing, moving forward, angles near 0 # Credits @@ -41,6 +44,10 @@ TODO: Write usage instructions # Notes -- Without node: Training: 52732.152ms -- With node: Training: 17211.739ms -- With cuda LoopTime: 12583.449ms +- Training times: + - Without node: Training: 52732.152ms + - With node: Training: 17211.739ms + - With cuda LoopTime: 12583.449ms +- tfjs-node-gpu needs python2 to compile + +- is is slipping, what about with more friction? diff --git a/src/js/renderer.js b/src/js/renderer.js index 2ec46ee..6fd52fb 100644 --- a/src/js/renderer.js +++ b/src/js/renderer.js @@ -25,9 +25,11 @@ class Renderer { } drawFrame() { + // clear this.ctx.clearRect(0, 0, this.main_screen.width, this.main_screen.height); this.ctx.save(); + // camera var minmax = this.getMinMaxDistance(); this.target_zoom = Math.min(this.config.max_zoom_factor, this.getZoom(minmax.min_x, minmax.max_x + 4, minmax.min_y + 2, minmax.max_y + 2.5)); this.zoom += 0.1 * (this.target_zoom - this.zoom); @@ -36,13 +38,36 @@ class Renderer { this.ctx.translate(this.translate_x * this.zoom, this.translate_y); this.ctx.scale(this.zoom, -this.zoom); + // bodies this.drawFloor(); + for (var k = this.config.population_size - 1; k >= 0; k--) { this.drawWalker(this.walkers[k]); } + + for (let i = 0; i < this.walkers[0].balls.length; i++) { + const ball = this.walkers[0].balls[i]; + this.drawCircle(ball) + + } + + this.ctx.restore(); } + drawCircle(body) { + // set strokestyle and fillstyle before call + this.ctx.beginPath(); + var fixture = body.GetFixtureList(); + var shape = fixture.GetShape(); + var p0 = body.GetWorldPoint(shape.m_p); + let radius = shape.m_radius + // this.ctx.moveTo(p0.x, p0.y); + this.ctx.arc(p0.x, p0.y, radius, 0, Math.PI*2) + this.ctx.fill(); + this.ctx.stroke(); + } + drawFloor() { this.ctx.strokeStyle = "#444"; this.ctx.lineWidth = 1 / this.zoom; @@ -62,10 +87,7 @@ class Renderer { this.ctx.lineWidth = 1 / this.zoom; // left legs and arms first - this.drawRect(walker.left_leg.lower_leg); - this.drawRect(walker.left_leg.upper_leg); - this.drawRect(walker.left_arm.upper_arm); - this.drawRect(walker.left_arm.lower_arm); + this.ctx.lineWidth = walker.left_leg.frictionJoint.maxForce ? 4 / this.zoom : 1 / this.zoom; this.drawRect(walker.left_leg.foot); @@ -75,6 +97,12 @@ class Renderer { this.drawRect(walker.left_arm.hand); this.ctx.lineWidth = 1 / this.zoom; + this.drawRect(walker.left_leg.lower_leg); + this.drawRect(walker.left_leg.upper_leg); + this.drawRect(walker.left_arm.lower_arm); + this.drawRect(walker.left_arm.upper_arm); + + // head this.drawRect(walker.head.neck); this.drawRect(walker.head.head); diff --git a/src/js/walker.js b/src/js/walker.js index 52dc14b..8eca356 100644 --- a/src/js/walker.js +++ b/src/js/walker.js @@ -95,13 +95,14 @@ class Walker { this.fd = new b2.FixtureDef(); this.fd.density = this.density; this.fd.restitution = 0.1; // bounciness - this.fd.friction = 100; // only on contact (grip) + this.fd.friction = 10000; // only on contact (grip) this.fd.shape = new b2.PolygonShape(); this.fd.filter.groupIndex = -1; this.joints = []; this.otherJoints = [] this.otherBodies = [] + this.balls = [] this.grips = [false, false, false, false] // this.frictionJoints = [] @@ -170,10 +171,16 @@ class Walker { destroy() { this.joints.map(joint => this.world.DestroyJoint(joint)) this.otherJoints.map(joint => this.world.DestroyJoint(joint)) + this.bodies.map(body => body.DestroyFixture(body.GetFixtureList())) this.bodies.map(body => this.world.DestroyBody(body)) + + this.balls.map(body => body.DestroyFixture(body.GetFixtureList())) + this.balls.map(body => this.world.DestroyBody(body)) + this.bodies = [] this.joints = [] + this.balls = [] this.otherJoints = [] } @@ -473,6 +480,33 @@ class Walker { this.joints.push(j); } + addBall(x, y, r) { + var bodyDef = new b2.BodyDef() + bodyDef.type = b2.Body.b2_dynamicBody + bodyDef.position.x = x + bodyDef.position.y = y + var ball = this.world.CreateBody(bodyDef); + + // Create a circle shape and set its radius to 6 + var circle = new b2.CircleShape(); + // circle.setRadius(6) + circle.m_radius=r + + + // Create a fixture definition to apply our shape to + var fixtureDef = new b2.FixtureDef(); + fixtureDef.shape = circle; + fixtureDef.density = 0.5; + fixtureDef.friction = 0.4; + fixtureDef.restitution = 0.6; // Make it bounce a little bit + + // Create our fixture and attach it to the body + var fixture = ball.CreateFixture(fixtureDef); + // circle.dispose(); + + this.balls.push(ball) + } + getBodies() { return [ @@ -491,7 +525,7 @@ class Walker { this.left_leg.foot, this.right_leg.upper_leg, this.right_leg.lower_leg, - this.right_leg.foot + this.right_leg.foot, ]; } @@ -554,7 +588,12 @@ class Walker { @action: (Integer) The action to take (can be null if no action) */ + if (Math.random() < 0.05) { + this.addBallOnWalker() + } + // repeat actions + // FIXME I need to delay between drawing each frame. Right now action repeat makes it look like frames are skipping for (let i = 0; i < this.config.action_repeat; i++) { // TODO add sitcky actions once it's working this.world.ClearForces(); @@ -612,7 +651,8 @@ class Walker { leg_switch_reward, head_height: (this.head.head.GetPosition().y - mean_foot_height), center_x: this.torso.upper_torso.GetPosition().x, - center_y: this.torso.upper_torso.GetPosition().y + center_y: this.torso.upper_torso.GetPosition().y, + mean_foot_height } this.reward = Object.values(this.rewards).reduce((tot, v) => tot + v, 0) / 3 @@ -642,16 +682,28 @@ class Walker { this.steping = false; } } + + addBallOnWalker() { + let p = this.torso.upper_torso.GetPosition() + this.addBall(p.x, randf(1, 4), randf(0.03,0.3)) + } + + reset() { /** Reset position to initial or random position TODO */ // console.log('reset not implemented') if (this.bodies) this.destroy(); this.build(); this.initGrip() + this.episodeSteps = 0 this.randomise(0.5) + this.addBallOnWalker() + this.addBallOnWalker() + this.addBallOnWalker() + // run for a few warm up steps (to let it fall over... we don't want to reward it just for restarting the episode upright) for (let i = 0; i < 200; i++) { this.world.Step(1 / this.config.time_step, this.config.velocity_iterations, this.config.position_iterations); @@ -659,7 +711,7 @@ class Walker { this.world.Step(1 / this.config.time_step, this.config.velocity_iterations, this.config.position_iterations); if (typeof WEB !== "undefined") this.renderer.drawFrame() } - console.debug('warm up finished') + // console.debug('warm up finished') } shuffle() { /** Reset position to initial or random position TODO */