mirror of
https://github.com/wassname/metacar.git
synced 2026-10-30 13:30:43 +08:00
191 lines
5.7 KiB
TypeScript
191 lines
5.7 KiB
TypeScript
import { MotionEngine, actionSpaceDescription } from "./motion_engine";
|
|
import {Level} from "./level";
|
|
|
|
import {
|
|
MAP, ROADSIZE
|
|
} from "./global";
|
|
|
|
import * as U from "./utils";
|
|
|
|
export class ControlMotionEngine extends MotionEngine {
|
|
|
|
private actions: (string|number)[];
|
|
private maxSpeed: number = 2.0;
|
|
|
|
constructor(level: Level) {
|
|
/*
|
|
@level (Level class)
|
|
@options (Object) Option to build the motion engine
|
|
@rotation_step: Between 0 and 2. (rotation_step*Pi)
|
|
@actions: List of possible actions (Left, Right, Up, Down, Wait)
|
|
*/
|
|
super(level);
|
|
|
|
this.level = level;
|
|
this.actions = ["SteeringAngle", "Throttle"]
|
|
}
|
|
|
|
setUp(car: any, lidar: any){
|
|
/*
|
|
Setup the motion engine
|
|
@car: (Vehicle Object)
|
|
@lidar: (Lidar Object)
|
|
*/
|
|
this.car = car;
|
|
this.lidar = lidar;
|
|
// Init the lidar state
|
|
this.state = [];
|
|
for (let y = 0; y < lidar.pts; y++) {
|
|
let line = [];
|
|
for (let x = 0; x < lidar.pts; x++) {
|
|
line.push(MAP.DEFAULT);
|
|
}
|
|
this.state.push(line);
|
|
}
|
|
// Set up keyboard interaction
|
|
this.setUpKeyboard();
|
|
// Setup up velocity to 0
|
|
this.car.v = 0;
|
|
this.car.a = 0; // Acceleration
|
|
this.car.yaw_rate = 0;
|
|
|
|
this.detectInteractions();
|
|
}
|
|
|
|
setUpKeyboard(){
|
|
/*
|
|
Setup possible keyboard interactions
|
|
*/
|
|
let left = U.keyboard(37);
|
|
let up = U.keyboard(38);
|
|
let right = U.keyboard(39);
|
|
let down = U.keyboard(40);
|
|
|
|
left.press = () => {
|
|
this.turn(-0.7);
|
|
};
|
|
left.release = () => {
|
|
this.turn(0);
|
|
};
|
|
|
|
right.press = () => {
|
|
this.turn(0.7);
|
|
};
|
|
right.release = () => {
|
|
this.turn(0);
|
|
};
|
|
|
|
// Move forward
|
|
up.press = () => { this.move(1); };
|
|
up.release = () => {
|
|
this.car.a = 0;
|
|
};
|
|
|
|
// Move backward
|
|
down.press = () => { this.move(-1);};
|
|
down.release = () => {
|
|
this.car.a = 0;
|
|
};
|
|
}
|
|
|
|
turn(value: number){
|
|
/*
|
|
Turn
|
|
@value: yaw_rate value Between -1 and 1
|
|
*/
|
|
this.car.yaw_rate = value;
|
|
}
|
|
|
|
move(throttle: number){
|
|
/*
|
|
Move forward
|
|
@throttle throttle value Between -1 and 1
|
|
*/
|
|
this.car.a = throttle;
|
|
}
|
|
|
|
actionStep(delta: number, actions: number[]){
|
|
/*
|
|
Step into the environement with one action
|
|
@delta (Float) time since the last update
|
|
@action: (Array of Float) The throttle and the steering angle
|
|
*/
|
|
this.car.a = Math.min(Math.max(actions[0], -1), 1);
|
|
this.car.yaw_rate = Math.min(Math.max(actions[1], -1), 1);
|
|
let {agentCollisions, onRoad} = this.step(delta);
|
|
this.car.a = 0;
|
|
this.car.yaw_rate = 0;
|
|
return {agentCollisions, onRoad};
|
|
}
|
|
|
|
/**
|
|
* Return a description of the action space.
|
|
*/
|
|
public actionSpace(): actionSpaceDescription {
|
|
return {
|
|
type: "Continous",
|
|
size: 2,
|
|
range: [[0., 1.], [-1., 1.]]
|
|
}
|
|
}
|
|
|
|
step(delta: number){
|
|
/*
|
|
Step into the environement
|
|
@delta (Float) time since the last update
|
|
*/
|
|
this.car.last_a = this.car.a;
|
|
this.car.last_yaw_rate = this.car.yaw_rate;
|
|
|
|
// The car lose speed over time
|
|
if (this.car.v > 0 && this.car.a == 0)
|
|
this.car.v = Math.max(0, this.car.v - 0.01);
|
|
else if (this.car.v < 0 && this.car.a == 0)
|
|
this.car.v = Math.min(0, this.car.v + 0.01);
|
|
|
|
if (this.car.a > 0 && this.car.v >= 0)
|
|
this.car.v = Math.min(this.maxSpeed, this.car.v + this.car.a*0.005);
|
|
else if (this.car.a > 0 && this.car.v < 0){
|
|
this.car.v = Math.min(this.maxSpeed, this.car.v + this.car.a*0.03);
|
|
}
|
|
if (this.car.a < 0 && this.car.v <= 0)
|
|
this.car.v = Math.max(-this.maxSpeed, this.car.v + this.car.a*0.005);
|
|
else if (this.car.a < 0 && this.car.v > 0){
|
|
this.car.v = Math.max(-this.maxSpeed, this.car.v + this.car.a*0.03);
|
|
}
|
|
if (this.car.yaw_rate == 0){
|
|
this.car.x += this.car.v * Math.cos(this.car.rotation)*delta;
|
|
this.car.y += this.car.v * Math.sin(this.car.rotation)*delta;
|
|
}
|
|
else{
|
|
// Rewrite the yaw rate
|
|
let yr = this.car.yaw_rate*0.01*(this.car.v+0.001)*Math.PI;
|
|
|
|
this.car.x += (this.car.v/yr)*(Math.sin(this.car.rotation+(yr*delta)) - Math.sin(this.car.rotation));
|
|
this.car.y += (this.car.v/yr)*(Math.cos(this.car.rotation) - Math.cos(this.car.rotation+(yr*delta)));
|
|
|
|
this.car.rotation += yr*delta;
|
|
}
|
|
// Update the x and x position according to the map
|
|
this.car.mx = Math.floor(this.car.x / ROADSIZE);
|
|
this.car.my = Math.floor(this.car.y / ROADSIZE);
|
|
|
|
// Set the new road of the car (if changed)
|
|
this.car.checkAndsetNewRoad();
|
|
|
|
// Be sure to keep the lidar position at the same position than the car
|
|
this.lidar.x = this.car.x;
|
|
this.lidar.y = this.car.y;
|
|
this.lidar.rotation = this.car.rotation;
|
|
|
|
// Detection the new collision with the environement
|
|
let {agentCollisions, onRoad} = this.detectInteractions(true, true);
|
|
|
|
if (agentCollisions.length > 0){ // Stop the vehicle if a collision is detected
|
|
this.car.v = 0;
|
|
this.car.vy = 0;
|
|
}
|
|
return {agentCollisions, onRoad};
|
|
}
|
|
}
|