package josx.robotics; import josx.platform.rcx.*; import josx.util.*; // !! For rotation, it should straighten out when stopped to try to keep // the angle somewhat accurate. // !! 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 RotationNavigator class contains methods for performing basic navigational * movements. This class uses two rotation sensors to monitor the wheels of the * differential drive. For this class to work properly, the rotation sensors * should record positive (+) values when the wheels move forward, and negative (-) * values when the wheels move backward. This class also assumes the Motor.forward() * command will cause the drive wheels to move in a forward direction.
* 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 0.1 19-August-2001 */ public class RotationNavigator implements Navigator, SensorConstants { // orientation and co-ordinate data private float angle; private float x; private float y; // Motors for differential steering: private Motor left; private Motor right; // Rotation sensors: private Sensor rotLeft; private Sensor rotRight; // Movement values: private float COUNTS_PER_CM; private float COUNTS_PER_DEGREE; // Internal states private boolean moving; private byte command; // command constants: private byte STOP = 0; private byte FORWARD = 1; private byte BACKWARD = 2; private byte LEFT_ROTATE = 3; // Unused private byte RIGHT_ROTATE = 4; // Unused /** * Allocates a RotationNavigator object and initializes if with the proper motors and sensors. * 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 or in circles when you tell it to go forwards, try * rotating the wires to the motor ports by 90 or 180 degrees. * @param wheelDiameter The diameter of the wheel, usually printed right on the * wheel, in centimeters (e.g. 49.6 mm = 4.96 cm) * @param driveLength The distance from the center of the left tire to the center * of the right tire, in centimeters. * @param ratio The ratio of sensor rotations to wheel rotations.
* e.g. 3 complete rotations of the sensor for every one turn of the wheel = 3f
* 1 rotation of the sensor for every 2 turns of the wheel = 0.5f * @param rightMotor The motor used to drive the right wheel e.g. Motor.C. * @param leftMotor The motor used to drive the left wheel e.g. Motor.A. * @param rightRot Sensor used to read rotations from the right wheel. e.g. Sensor.S3 * @param leftRot Sensor used to read rotations from the left wheel. e.g. Sensor.S1 */ public RotationNavigator(float wheelDiameter, float driveLength, float ratio, Motor leftMotor, Motor rightMotor, Sensor leftRot, Sensor rightRot) { this.right = rightMotor; this.left = leftMotor; this.rotLeft = leftRot; this.rotLeft.setTypeAndMode(SENSOR_TYPE_ROT, SENSOR_MODE_ANGLE); this.rotLeft.setPreviousValue(0); this.rotLeft.activate(); this.rotRight = rightRot; this.rotRight.setTypeAndMode(SENSOR_TYPE_ROT, SENSOR_MODE_ANGLE); this.rotRight.setPreviousValue(0); this.rotRight.activate(); // Set coordinates and starting angle: angle = 0.0f; x = 0.0f; y = 0.0f; moving = false; command = STOP; // Calculate the counts per centimeter float wheelCircumference = wheelDiameter * (float)Math.PI; COUNTS_PER_CM = (16 * ratio)/wheelCircumference; // Calculate counts per degree float fullRotation = (driveLength * (float)Math.PI); COUNTS_PER_DEGREE = ((fullRotation/wheelCircumference)*(16*ratio))/360; // Thread is for keeping the RCX straight when driving by // monitoring the rotation sensors while moving. SteerThread steering = new SteerThread(); steering.start(); } /** * Overloaded RotationNavigator constructor that assumes the following:
* Left motor = Motor.A Right motor = Motor.C
* Left rotation sensor = Sensor.S1 Right rotation sensor = Sensor.S3 * @param wheelDiameter The diameter of the wheel, usually printed right on the * wheel, in centimeters (e.g. 49.6 mm = 4.96 cm) * @param axleLength The distance from the center of the left tire to the center * of the right tire, in centimeters. * @param ratio The ratio of sensor rotations to wheel rotations.
* e.g. 3 complete rotations of the sensor for every one turn of the wheel = 3f
* 1 rotation of the sensor for every 2 turns of the wheel = 0.5f */ public RotationNavigator(float wheelDiameter, float driveLength, float ratio) { this(wheelDiameter, driveLength, ratio, Motor.A, Motor.C, Sensor.S1, Sensor.S3); } /** * 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 the following with modulo (%) ??? while(this.angle < 0) this.angle += 360; // Must be > 0 // Calculate the number of intervals of rotation sensor to count int count = (int)(COUNTS_PER_DEGREE * angle); rotLeft.setPreviousValue(0); rotRight.setPreviousValue(0); if (angle > 0) { right.forward(); left.backward(); while(rotLeft.readValue() > (count*-1) || rotRight.readValue() < count) {} } else if (angle < 0) { right.backward(); left.forward(); while(rotLeft.readValue() < (count*-1) || rotRight.readValue() > count) {} } 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 dist The positive or negative distance to move the robot (in centimeters). */ public void travel(int dist) { // !! The command != STOP lines need to be tested!!! // !! Should stop and exit travel() when travel() interrupted by stop() int counts = (int)(dist * COUNTS_PER_CM); if(dist > 0) { forward(); while(command != STOP && (rotLeft.readValue() < counts || rotRight.readValue() < counts)) { Thread.yield(); } } else if(dist < 0) { backward(); while(command != STOP && rotLeft.readValue() > counts || rotRight.readValue() > counts) { Thread.yield(); } } stop(); } /** * Moves the RCX robot forward until stop() is called. * * @see Navigator#stop(). */ public void forward() { rotLeft.setPreviousValue(0); rotRight.setPreviousValue(0); command = FORWARD; } /** * Inner class that monitors the rotation sensors and keeps the * navigator steering straight. */ private class SteerThread extends Thread { public void run() { while(true) { while(command == FORWARD) { left.forward(); right.forward(); moving = true; if(rotLeft.readValue() > rotRight.readValue()) { left.flt(); while(rotLeft.readValue() > rotRight.readValue()) {} left.forward(); } if(rotRight.readValue() > rotLeft.readValue()) { right.flt(); while(rotRight.readValue() > rotLeft.readValue()) {} right.forward(); } Thread.yield(); } while(command == BACKWARD) { left.backward(); right.backward(); moving = true; if(rotLeft.readValue() < rotRight.readValue()) { left.flt(); while(rotLeft.readValue() < rotRight.readValue()) {} left.backward(); } if(rotRight.readValue() < rotLeft.readValue()) { right.flt(); while(rotRight.readValue() < rotLeft.readValue()) {} right.backward(); } Thread.yield(); } moving = false; Thread.yield(); } } } /** * Moves the RCX robot backward until stop() is called. * * @see Navigator#stop(). */ public void backward() { rotLeft.setPreviousValue(0); rotRight.setPreviousValue(0); command = BACKWARD; } /** * Halts the RCX robot and calculates new x, y coordinates. * * @see Navigator#forward(). */ public void stop() { if(moving) { command = STOP; while(moving) { Thread.yield(); } left.stop(); right.stop(); // Recalculate x-y coordinates based on rotation sensors int rotAvg = (rotLeft.readValue() + rotRight.readValue()) / 2; float centimeters = rotAvg / COUNTS_PER_CM; // update x, y coordinates x = x + (float)(Math.cos(Math.toRadians(angle)) * centimeters); y = y + (float)(Math.sin(Math.toRadians(angle)) * centimeters); } } }