package josx.robotics; import josx.platform.rcx.*; import josx.util.*; // !! Should all methods call stop() first in case it was roaming? // OR methods account for RCX currently in moving mode // !! All methods that change x, y must be synchronized. /** * The Navigator class contains methods for performing basic navigational * movements. Normally the Navigator class is instantiated as an object and * methods are called on that object. It can also be extended by your robot * code, but the constructor method will need to be overwritten if you do this. * * Note: This class will only work for robots using two motors to steer differentially * that can rotate within its footprint (i.e. turn on one spot). * * @author Brian Bagnall * @version 1.4 17-Feb-2002 */ public class TimingNavigator implements Navigator { // Time it takes to rotate 1 degrees (in milliseconds): private float ROTATION; // Calculated in constructor // Time it takes to go one centimeter (in milliseconds) private float CENTIMETER; // Calculated in constructor // orientation and co-ordinate data private float angle; private float x; private float y; // oldTime is used for timing purposes in stop(): private int oldTime; // Motors for differential steering: private Motor left; private Motor right; private static Thread sleepThread; // Thread used for delays private boolean travel = false; // Used in stop() to know when to interrupt /** * A variable that adds extra time to each rotation. This gives the * robot more time to overcome momentum when starting a rotation. Proper * use of this variable increases accuracy dramatically. */ private short momentum_delay = 0; /** * Allocates a Navigator object and initializes if with the left and right wheels. * The x and y values will each equal 0 (cm's) on initialization, and the starting * angle is 0 degrees, so if the first move is forward() the robot will run along * the x axis. *
Note: If you find your robot is going backwards when you tell it to go forwards, try * rotating the wires to the motor ports by 90 degrees. * @param right The motor used to drive the right wheel e.g. Motor.C. * @param left The motor used to drive the left wheel e.g. Motor.A. * @param timeOneMeter The number of seconds it takes your robot to drive one meter (e.g. 2.0 seconds is normal for a fast robot, 5 seconds for a slower robot). * @param timeRotate The number of seconds it takes your robot to rotate 360 degrees. (e.g. 0.646 is normal for a small axle length, 2.2 for a larger axle length) */ public TimingNavigator(Motor right, Motor left, float timeOneMeter, float timeRotate) { this.right = right; this.left = left; // Convert seconds into milliseconds per degree // and milliseconds per centimeter ROTATION = (timeRotate/360)*1000; CENTIMETER = (timeOneMeter/100)*1000; // Set coordinates and starting angle: angle = 0.0f; x = 0.0f; y = 0.0f; } /** * A variable that adds extra time to each rotation. This gives the * robot more time to overcome momentum when starting a rotation. Proper * use of this variable increases accuracy dramatically. *
Note: The use of this method also causes the timeRotate variable (supplied in * the constructor) to be recalculated. This method assumes the time to rotate * 360 degrees also includes the delay in overcoming momentum, so this method * subtracts delay from timeRotate. */ public void setMomentumDelay(short delay) { momentum_delay = delay; // ** It should also recalculate the timeRotate variable } /** * Returns the current x coordinate of the RCX. * Note: At present it will only give an updated reading when the RCX is stopped. * @return float Present x coordinate. */ public float getX() { // !! In future, if RCX is on the move it should return the present calculation of x return x; } /** * Returns the current y coordinate of the RCX. * Note: At present it will only give an updated reading when the RCX is stopped. * @return float Present y coordinate. */ public float getY() { return y; } /** * Returns the current angle the RCX robot is facing. * Note: At present it will only give an updated reading when the RCX is stopped. * @return float Angle in degrees. */ public float getAngle() { return angle; } /** * Rotates the RCX robot a specific number of degrees in a direction (+ or -).This * method will return once the rotation is complete. * * @param angle Angle to rotate in degrees. A positive value rotates left, a negative value right. */ public void rotate(float angle) { // keep track of angle this.angle = this.angle + angle; this.angle = (int)this.angle % 360; // Must be < 360 degrees // Is it possible to do this with modulo (%) ??? if(this.angle < 0) this.angle += 360; // Must be > 0 oldTime = (int)System.currentTimeMillis(); int delay = ((int)(ROTATION * Math.abs(angle))) + momentum_delay; if (angle > 0) { right.forward(); left.backward(); } else if (angle < 0) { left.forward(); right.backward(); angle = angle * -1; } try { Thread.sleep(delay); } catch(InterruptedException ie) { } right.stop(); left.stop(); } /** * Rotates the RCX robot to point in a certain direction. It will take the shortest * path necessary to point to the desired angle. Method returns once rotation is complete. * * @param angle The angle to rotate to, in degrees. */ public void gotoAngle(float angle) { // in future, use modulo instead of while loop??? float difference = angle - this.angle; while(difference > 180) difference = difference - 360; // shortest path to goal angle while(difference < -180) difference = difference + 360; // shortest path to goal angle rotate(difference); } /** * Rotates the RCX robot towards the target point and moves the required distance. * * @param x The x coordinate to move to. * @param y The y coordinate to move to. */ public void gotoPoint(float x, float y) { // Determine relative points float x1 = x - this.x; float y1 = y - this.y; // Calculate angle to go to: float angle = (float)Math.atan2(y1,x1); // Calculate distance to travel: float distance; if(y1 != 0) distance = y1/(float)Math.sin(angle); else distance = x1/(float)Math.cos(angle); // Convert angle from rads to degrees: angle = (float)Math.toDegrees(angle); // Now convert theory into action: gotoAngle(angle); travel(Math.round(distance)); } /** * Moves the RCX robot a specific distance. A positive value moves it forwards and * a negative value moves it backwards. Method returns when movement is done. * * @param centimeters The positive or negative distance to move the robot (in centimeters). */ public void travel(int distance) { if(distance > 0) { forward(); } else { backward(); } travel = true; int delay = (int)(CENTIMETER * Math.abs(distance)); sleepThread = Thread.currentThread(); try { sleepThread.sleep(delay); travel = false; stop(); // Will not be called if Interrupted } catch (InterruptedException ie) { travel = false; } } /** * Moves the RCX robot forward until stop() is called. * * @see Navigator#stop(). */ public void forward() { // Start timer oldTime = (int)System.currentTimeMillis(); right.forward(); left.forward(); } /** * Moves the RCX robot backward until stop() is called. * * @see josx.robotics.Navigator#stop(). */ public void backward() { // Start timer oldTime = (int)System.currentTimeMillis(); right.backward(); left.backward(); } /** * Halts the RCX robot and calculates new x, y coordinates. * * @see Navigator#forward(). */ public void stop() { // Only call interrupt if travelling if(travel == true) sleepThread.interrupt(); left.stop(); right.stop(); // Recalculate x-y coordinates based on Timer results // Note: Changes oldTime to 0, then if someone // calls stop() twice it won't screw up x-y coordinates if (oldTime != 0) { int totalTime = (int)System.currentTimeMillis() - oldTime; float centimeters = totalTime / CENTIMETER; // update x, y coordinates x = x + (float)(Math.cos(Math.toRadians(angle)) * centimeters); y = y + (float)(Math.sin(Math.toRadians(angle)) * centimeters); oldTime = 0; } } }