200 likes | 410 Views
Event Driven Programming Minus the GUI Plus a Robot. By Charlie Vick. Why Event-Driven Robotics?. This robot consists of sensors and motors. Event driven programming decouples sensor monitoring and motor controlling. Different classes specialize, compartmentalizing the hardware.
E N D
Event Driven ProgrammingMinus the GUIPlus a Robot By Charlie Vick
Why Event-Driven Robotics? • This robot consists of sensors and motors. • Event driven programming decouples sensor monitoring and motor controlling. Different classes specialize, compartmentalizing the hardware. • Can more easily switch out sensors or motors, as long as everyone can use the same event system and events.
Robot Gear • Hardware: Lego Mindstorm NXT - servos, sensors and an ARM processor • Software: open-source 3rd party firmware called leJOS, which runs compiled Java bytecode
Architecture (part 1) • Lejos library has a class, ‘SensorPort’ • SensorPort.addSensorPortListener( SensorPortListener aListener) is a public method • SensorPortListener: Interface, has one method: void stateChanged(SensorPort source, int oldValue, int newValue)
This seemed a real coup • leJOS looked to be designed for event-driven programming • Wire up SensorPortListeners to a few sensors, have each steer in different ways when triggered
Problem • API Documentation out of date. Forum search revealed SensorPortListener was deprecated for most sensors. • Only works on one sensor I have, touch. • Doesn’t actually work.
Program Architecture (part 2) • Slightly more complex. • One class, SensorReader, iterates through a couple of sensors. If certain values are hit, fire a certain event. • Another class, EventHandler, receives these events and changes the motors accordingly.
How do these classes communicate? • Each runs on its own thread. • They pass messages over a shared, synchronized queue. • This means events are handled approximately in the order created, barring synchronization issues (Bonus: What is a better / more granular / more responsive data structure than a regular queue, given events with different priorities?)
Code sample from Architecture 1 publicstaticvoid main (String[] aArg) { Robot robot = new Robot(); SensorPort.S1. addSensorPortListener(robot); robot.moveForward(); } In Robot: publicvoid moveForward() { Motor.A.setSpeed(20); Motor.B.setSpeed(20); while (!ts.isPressed()) { //ts instanceof TouchSensor Motor.A.forward(); Motor.B.forward(); } } //bonus - what part is NOT very event-driven here?
Code sample from Architecture 1 publicvoid stateChanged(SensorPort port, int oldValue, int newValue) { //replace with diff values checking in oldValue, newValue//otherwise not very event-drivenif (oldValue < 500 && newValue < 500) { Motor.A.stop(); Motor.B.stop(); Motor.A.setSpeed(20); Motor.B.setSpeed(20); Motor.A.backward(); Motor.B.backward(); Thread.sleep(100); Motor.A.stop(); Motor.B.stop(); Motor.A.forward(); Motor.B.backward(); Thread.sleep(250); Motor.A.stop(); Motor.B.stop(); moveForward(); }
Architecture 2 Code public class SynchronizedQueue<E> { private final ArrayList<E> queue; public void push(E o) { synchronized(queue) { queue.add(o); queue.notify(); } } public E pop() { synchronized(queue) { while (queue.isEmpty()) { queue.wait(); } return queue.remove(0); } } }
Architecture 2 code: SensorReader(note: ‘ts’ is a TouchSensor instance) publicvoid run() { while (true) { if(ts.isPressed()) { queue.push(new RobotEvent( RobotEventInt.BACKUP)); } else { queue.push(new RobotEvent( RobotEventInt.FORWARD)); } try { Thread.sleep(200); } catch (InterruptedException e) { System.out.println("Sensor interrupted"); } } }
Architecture 2 code: EventResponder’s run method. Both implement Runnable, an interface allowing them to run in separate Threads.Bonus: What is missing? publicvoid run() { while(true) { RobotEventInt curr = queue.pop(); if (curr.getName().equals( RobotEventInt.FORWARD)) { leftMotor.forward(); rightMotor.forward(); } elseif (curr.getName().equals( RobotEventInt.BACKUP)) { if (!leftMotor.isStopped() && !rightMotor.isStopped()) { leftMotor.stop(); rightMotor.stop(); } leftMotor.backward(); rightMotor.backward(); Thread.sleep(40); } } }
Interface Runnable • One method: public void run() {} Add a Runnable implementor to a Thread, run Thread.start(), and the code in run() executes asynchronously.
RobotEvent • Implements RobotEventInt, a collection of static String values (RobotEventInt.BACKUP) and one method, getName(), which should always be one of RobotEventInt’s static String values.
Architecture 2 Working Yet? • Still working at it. Robot throws an error that indicates not all classes loaded, which print statements don’t seem to verify. Runs fine until queue.pop() in EventResponder.
Lessons Learned • If you learn about deprecated classes in a library from an online forum and NOT from the library’s API, try to switch libraries. • (Also, if you shutdown and accidentally suspend an Ubuntu laptop and then power-cycle it, some perfect storm wipes out /sys and some of /sbin) • (On a related note, Dropbox is great for backing up your work)