Published unlisted
Edited
Dec 24, 2020
Insert cell
Insert cell
Insert cell
core = require('https://cdn.jsdelivr.net/npm/@openhps/core@0.2.0-alpha.7/')
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
/**
* We create a custom source node that converts a motor PWM signal
* to linear and angular velocity.
**/
class MotorSource extends core.SourceNode {
constructor(source) {
super(source);
this.leftPWM = 0;
this.rightPWM = 0;

this.maxSpeed = 2.0;
}
/**
* Move the robot by controlling the left and right motor PWM
* @param leftPWM Left motor PWM
* @param rightPWM Right motor PWM
**/
move(leftPWM, rightPWM) {
// Make sure that the PWM is between 0 and 100
this.leftPWM = Math.min(100, Math.max(0, leftPWM));
this.rightPWM = Math.min(100, Math.max(0, rightPWM));

// Update the position and push the frame
return this.updatePosition();
}
updatePosition() {
return new Promise((resolve, reject) => {
// Our max linear velocity with one motor is this.maxSpeed meters per second on 100%
const speedLeft = this.maxSpeed * (this.leftPWM / 100.);
const speedRight = this.maxSpeed * (this.rightPWM / 100.);

// We are going to make an inaccurate simulation
// where we manipulate the angular velocity
// based on the left and right motor PWM.
// In this example we do not take the rotation
// axis into account.
const linearVelocity = new core.LinearVelocity(
speedLeft + speedRight,
0,
0
);
const angularVelocity = new core.AngularVelocity(
0,
0,
2 * Math.PI * 0.5 * (speedRight - speedLeft)
);

// Get the stored position and add the linear and angular velocity
this.model
.findDataService(core.DataObject)
.findByUID(this.source.uid)
.then(storedObj => {
const position = storedObj.getPosition();
position.timestamp = core.TimeService.now();
position.linearVelocity = linearVelocity;
position.angularVelocity = angularVelocity;
const frame = new core.DataFrame(storedObj);
frame.createdTimestamp = core.TimeService.now();
frame.source.setPosition(position);
return this.push(frame);
})
.then(() => {
resolve();
})
.catch(ex => {
reject(ex);
});
});
}
onPull() {
// Pull received, load last stored data and push again without changes
return this.model
.findDataService(core.DataObject)
.findByUID(this.source.uid)
.then(storedObj => {
const frame = new core.DataFrame(storedObj);
frame.createdTimestamp = core.TimeService.now();
return this.push(frame);
});
}
}
Insert cell
Insert cell
motorSource = new MotorSource(new core.DataObject("robot"));
Insert cell
Insert cell
model = await core.ModelBuilder.create()
.addService(timeService)
.from(motorSource)
.via(new core.TimedPullNode(250, core.TimeUnit.MILLI))
.via(new core.VelocityProcessingNode())
.via(
new core.CallbackNode(frame => {
// This node prevents the robot from going off-screen
const position = frame.source.getPosition();
position.x = Math.min(20, Math.max(0, position.x));
position.y = Math.min(20, Math.max(0, position.y));
frame.source.setPosition(position);
})
)
.to(
new core.CallbackSinkNode(frame => {
const position = frame.source.getPosition();
const positionPlot = {
x: position.x,
y: position.y
};

// Check if the last position is different
// if not, we do not want to overlay it
if (
JSON.stringify(data[data.length - 1]) !== JSON.stringify(positionPlot)
) {
// Add the new location to the plot
data.push(positionPlot);
if (data.length > 100) {
data.shift();
}
chart.plot(data);
}
})
)
.build()
Insert cell
Insert cell
{
const robot = new core.DataObject("robot");
const position = new core.Absolute2DPosition(10, 10);
position.timestamp = core.TimeService.now();
robot.setPosition(position);
model.push(new core.DataFrame(robot));
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more