let wheelPosVectors = [];
let steeringCoefficients = [];
let steeringNormalVectors = [];
let wheelPositions = Array(4).fill(0);
const staticFrictionCoef = 0.2;
const slidingFrictionCoef = 0.1 * staticFrictionCoef;
createCanvas(windowWidth, windowHeight);
rotated = createVector(0,0)
createVector(1,-1).normalize(),
createVector(-1,-1).normalize(),
createVector(-1,-1).normalize(),
createVector(1,-1).normalize()
const wheelX = robotWidth/2 + 3;
const wheelY = robotHeight/2 - 3;
createVector(-wheelX,-wheelY + 5),
createVector(wheelX,-wheelY - 5),
createVector(-wheelX,wheelY + 5),
createVector(wheelX,wheelY - 5),
for (let i in wheelPosVectors) {
steeringNormalVectors[i] = p5.Vector.normalize(wheelPosVectors[i]).rotate(HALF_PI);
for (let i in steeringNormalVectors) {
steeringCoefficients[i] = p5.Vector.dot(steeringNormalVectors[i], driveVectors[i]) / wheelPosVectors[i].mag();
position = createVector(width/2, height/2);
driveDirection = createVector(0,0);
velocity = createVector(0,0);
background(200,230,255, 100);
driveDirection.x = 3 * (-keyDown("A") + keyDown("D"));
driveDirection.y = 3 * (-keyDown("W") + keyDown("S"));
driveDirection.rotate(-angle)
turnRate = 0.09 * (-keyDown("Q") + keyDown("E"));
let wheelRotationTorques = calculateRotationRates();
let oldRotationTorques = calculateInertialTorques(velocity, angularVel);
let {speed, rotation} = calculateRobotMovements(wheelRotationTorques, oldRotationTorques);
translate(position.x, position.y);
line(0,0, driveDirection.x * 10, driveDirection.y * 10);
line(0,0, rotated.x * 10, rotated.y * 10);
for(let i = 0; i < oldRotationTorques.length; i++) {
10, 50*oldRotationTorques[i]
for(let i = 0; i < wheelRotationTorques.length; i++) {
10, 50*wheelRotationTorques[i]
let compassPos = Math.max(robotWidth, robotHeight);
translate(compassPos +20, compassPos +20);
circle(0,0, compassPos + 50);
circle(0,0, compassPos + 40);
triangle(0,-46, -5, -40, 5,-40);
let isOverX = position.x < 0 || position.x > width;
let isOverY = position.y < 0 || position.y > height;
translate(clamp(position.x, 10, width-10), clamp(position.y, 10, height-10));
scale(Math.sign(position.x), Math.sign(position.y));
triangle(3.5,3.5, -3,1,1,-3);
function clamp(x,a,b) {return Math.max(Math.min(x,b), a)}
for(let i in wheelPosVectors) {
rect (wheelPosVectors[i].x - 8*Math.sign(wheelPosVectors[i].x), wheelPosVectors[i].y, 10, 5);
rect (wheelPosVectors[i].x - 10*Math.sign(wheelPosVectors[i].x), wheelPosVectors[i].y, 8, 6);
translate(wheelPosVectors[i].x, wheelPosVectors[i].y);
scale(-Math.sign(wheelPosVectors[i].x), Math.sign(wheelPosVectors[i].y))
for(let ln =0; ln < numLines; ln++) {
let pos = map(ln, 0, numLines-1, 0,10);
pos += wheelPositions[i] * -Math.sign(wheelPosVectors[i].y);
pos = ((pos % 10.1) + 10.1) % 10.1;
let angle = map (pos, -5,5, 0,PI);
line(-1.9,1.9*sin(angle)+pos,1.9,-1.9*sin(angle)+pos);
rect(0, 0, robotWidth, 7)
rect(-robotWidth*0.5 + 4, 0, 5, robotHeight)
rect(robotWidth*0.5 - 4, 0, 5, robotHeight);
function calculateRotationRates () {
let wheelRotationTorques = new Array(4).fill(0);
for(let i in wheelRotationTorques) {
let rotatedDriveVector = p5.Vector.rotate(driveVectors[i], angle);
wheelRotationTorques[i] = (p5.Vector.dot(driveDirection, driveVectors[i]) + turnRate / steeringCoefficients[i]) / wheelRadius;
let maxTorque = Math.abs(wheelRotationTorques[0]);
for(let i = 1; i < wheelRotationTorques.length; i++) {
if(Math.abs(wheelRotationTorques[i]) > maxTorque) {maxTorque = Math.abs(wheelRotationTorques[i]);}
if(maxTorque < 3) {return wheelRotationTorques;}
for(let i in wheelRotationTorques) {
wheelRotationTorques[i] /= maxTorque;
wheelRotationTorques[i] *= 3;
return wheelRotationTorques;
function calculateInertialTorques (velocity, angularVel) {
let oldRotationTorques = [];
let rotatedVel = velocity.copy();
rotatedVel.rotate(-angle)
for (let i in driveVectors) {
let rotatedDriveVector = p5.Vector.rotate(driveVectors[i], -angle);
oldRotationTorques[i] = (p5.Vector.dot(rotatedVel, driveVectors[i]) + angularVel / steeringCoefficients[i]) / wheelRadius;
return oldRotationTorques
function calculateRobotMovements (wheelRotationTorques, oldRotationTorques) {
let speed = createVector(0,0);
let driveForceVectors = [];
for (let i in driveVectors) {
wheelPositions[i] += wheelRotationTorques[i];
let torqueDifference = (wheelRotationTorques[i] - oldRotationTorques[i]);
let frictionTorque = calculateFriction(torqueDifference);
let torqueApplied = oldRotationTorques[i]+frictionTorque;
driveForceVectors[i] = driveVectors[i].copy()
driveForceVectors[i].mult(torqueApplied * wheelRadius);
speed.add(driveForceVectors[i]);
rotation += torqueApplied * wheelRadius * steeringCoefficients[i];
speed.mult(0.5).rotate(angle);
return {speed, rotation};
function calculateFriction(torqueDifference) {
let effectiveWeight = robotWeight + getNormalDistRandom(0.3);
if(abs(torqueDifference) < effectiveWeight * staticFrictionCoef) {
let direction = Math.sign(torqueDifference);
if(direction == 0) {direction = 1}
return (direction)*effectiveWeight*slidingFrictionCoef;
function getNormalDistRandom(stdev) {
const u1 = Math.random();
const u2 = Math.random();
return stdev * Math.sqrt(-2*Math.log(u1)) * Math.cos(2*Math.PI*u2);
document.addEventListener("keydown", e => {
if(!keysDown.includes(e.keyCode)) {
keysDown.push(e.keyCode);
document.addEventListener("keyup", e => {
keysDown.splice(keysDown.indexOf(e.keyCode), 1);
return keysDown.includes(key.charCodeAt(0));