MURVV Robot with JavaScript

From Phidgets Support
MURVV: Phidget Mobile Robot

Introduction

This project demonstrates how the Phidget JavaScript libraries can be used to control a robot.

MURVV is a four-wheeled vehicle with independent motors controlling mecanum wheels. These wheels allow MURVV to move in almost any direction.





Parts

Murvv parts.jpg
Murvv motors.jpg

This project utilizes the following hardware:


A custom chassis was made for this project, including mecanum wheels, however, the components listed above can be used with any chassis.











Software

For this project, we will create a simple webpage that is hosted by the Phidget SBC4. Using the Phidget Network Server and the Phidget22 JavaScript libraries, we will send commands to the DC Motor Phidgets and remotely control the robot.

Murvv webpage.jpg


The webpage that is hosted by the Phidget SBC4. It includes a live video feed from the webcam, as well as two simple joysticks that are used to control MURVV

Configuration

We will be using the Phidget Network Server to communicate with the DC Motor Phidgets connected to the Phidget SBC4. The Phidget Network Server can be enabled by navigating to Phidgets > phidget22 Network Server.

Murvv networkserver.png



Webpage Code

We will create an HTML file at /var/www/ titled murvvRC.html.

HTML Layout

<body>
	<div id="controlsContainer">
		<canvas id="controls" width="960" height="330" id="canvas"></canvas>
		<iframe id="iframe" width="320" height="240" src="http://phidgetsbc.local:81/?action=stream"></iframe>
	</div>
	<div class="info">
		Notice:<label id="notice"></label><br>
	</div>
</body>

JavaScript - Phidgets Init

This section of the code creates four DC Motor objects. Each object is mapped to a specific motor controller (and therefore, a specific motor) using setHubPort.

function onAttach(w) {
	console.log('Motor controller attached on hub port ', w.hubPort);
    //Make MURVV more responsive
	w.setAcceleration(50.0);
}

function onDetach (w) {
	console.log('Motor controller detached on hub port ', w.hubPort);
}

var conn = new phidget22.NetworkConnection({
	hostname: "phidgetsbc.local",
	port: 8080,
	name: "Phidget Server Connection",
	onError: function(code, msg) { console.error("Connection Error:", msg); },
	onConnect: function() { console.log("Connected"); },
	onDisconnect: function() { console.log("Disconnected"); }
});

conn.connect().catch(function(err) {
	console.error("Error during connect:", err);
});

//Create
var w_fr = new phidget22.DCMotor();
var w_fl = new phidget22.DCMotor();
var w_br = new phidget22.DCMotor();
var w_bl = new phidget22.DCMotor();

//Address
w_bl.setHubPort(0);
w_fl.setHubPort(1);
w_br.setHubPort(2);
w_fr.setHubPort(3);

//Handlers
w_bl.onAttach = onAttach;
w_bl.onDetach = onDetach;
w_fl.onAttach = onAttach;
w_fl.onDetach = onDetach;
w_br.onAttach = onAttach;
w_br.onDetach = onDetach;
w_fr.onAttach = onAttach;
w_fr.onDetach = onDetach;

//Open
w_fr.open();
w_fl.open();
w_br.open();
w_bl.open();

JavaScript - GUI Creation

This section of the code draws two red circles that will be used as virtual joysticks.

var canvas = document.getElementsByTagName('canvas')[0];
const ctx = canvas.getContext("2d");

var leftControl = {x: 0, y: 0, r: 0};
var rightControl = {x: 0, y: 0, r: 0};


leftControl.x = canvas.width / 4 - 80;
leftControl.y = canvas.height / 2 + 20;
leftControl.r = 140;
rightControl.x = canvas.width / 4 + canvas.width / 2 + 80;
rightControl.y = canvas.height / 2 + 20;
rightControl.r = 140;

//draws the control circles
ctx.lineWidth = 2;
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.beginPath();

ctx.strokeStyle = '#FF0000';
ctx.arc(leftControl.x, leftControl.y, leftControl.r, 0, Math.PI * 2);
ctx.stroke();
ctx.beginPath();
ctx.arc(rightControl.x, rightControl.y, rightControl.r, 0, Math.PI * 2);
ctx.stroke();

JavaScript - Manage GUI

This section of the code handles touch events (this project is meant for use on a smartphone). If the intended use is a computer, you can modify this to mouse events. After a touch has been registered and the coordinates mapped, commands are sent to the DC Motor Phidgets using setTargetVelocity.

anvas.addEventListener('touchstart', handleStart, false);
canvas.addEventListener('touchend', handleEnd, false);
canvas.addEventListener('touchcancel', handleCancel, false);
canvas.addEventListener('touchmove', handleMove, false);

var x = 0;
var y = 0;

function handleStart(event) {
	x = event.touches[0].clientX;
	y = event.touches[0].clientY;
	mecanumDrive(inputCalculation());
}
function handleEnd(e) {
	x = leftControl.x;
	y = rightControl.y;
	mecanumDrive({x: 0,y: 0});
}

function handleCancel(e) {
	x = leftControl.x;
	y = rightControl.y;
	mecanumDrive({x: 0,y: 0});
}

function handleMove(e) {
	x = event.touches[0].clientX;
	y = event.touches[0].clientY;
	mecanumDrive(inputCalculation());
}


/*
* Determines which control the touch coordinates are for.
*/
function inputCalculation() {
  var cx = leftControl.x;
  var cy = leftControl.y;
  var side = 0;
  
  var c = { x: 0, y: 0 };
  var mag = Math.sqrt((x - cx) * (x - cx) + (y - cy) * (y - cy));
  
  if (mag < leftControl.r) {
    c.x = x - cx;
    c.y = y - cy;
	side = -1;
  }

  cx = rightControl.x;
  cy = rightControl.y;
  mag = Math.sqrt((x - cx) * (x - cx) + (y - cy) * (y - cy));
  if (mag < rightControl.r) {
    c.x = x - cx;
    c.y = y - cy;
	side = 1;
  }

  return [c, side];
}

function mecanumDrive(touchIn) {
	var c = touchIn[0];
	var side = touchIn[1];
	var vel = 0;
	
	var maxOutput = 0.4;	/* speed limiting coeff. */
		if (side == -1) {
			
			vel = maxOutput*c.x/140;
			$('#notice').text("turning, x: "+String(c.x)+", y: "+String(c.y));
			
			w_fr.setTargetVelocity(-vel);
			w_fl.setTargetVelocity(-vel);
			w_br.setTargetVelocity(-vel);
			w_bl.setTargetVelocity(-vel);
			
		}
		else if (side == 1) {
			if (Math.abs(c.y) >= Math.abs(c.x)) {
				vel = maxOutput*c.y/140;
				$('#notice').text("Forward / backwards, x: "+String(c.x)+", y: "+String(c.y));
				w_fr.setTargetVelocity(-vel);
				w_fl.setTargetVelocity(vel);
				w_br.setTargetVelocity(vel);
				w_bl.setTargetVelocity(-vel);
			}
			else {
				vel = maxOutput*c.x/140;
				$('#notice').text("strafing, x: "+String(c.x)+", y: "+String(c.y));
				w_fr.setTargetVelocity(vel);
				w_fl.setTargetVelocity(-vel);
				w_br.setTargetVelocity(vel);
				w_bl.setTargetVelocity(-vel);
			}
		}
		else {
				$('#notice').text("No input detected");
				w_fr.setTargetVelocity(0);
				w_fl.setTargetVelocity(0);
				w_br.setTargetVelocity(0);
				w_bl.setTargetVelocity(0);
		}
}

Result

Now MURVV can be controlled anywhere within our WiFi network! Note that the performance of this WiFi-based control is highly dependent on the signal strength of your local network.