2 import lejos.nxt.Battery;
\r
9 * Abstraction for a NXT motor. Three instances of <code>Motor</code>
\r
10 * are available: <code>Motor.A</code>, <code>Motor.B</code>
\r
11 * and <code>Motor.C</code>. To control each motor use
\r
12 * methods <code>forward, backward, reverseDirection, stop</code>
\r
13 * and <code>flt</code>. To set each motor's speed, use
\r
14 * <code>setSpeed. Speed is in degrees per second. </code>.\
\r
15 * Methods that use the tachometer: regulateSpeed, rotate, rotateTo <br>
\r
16 * Motor has 2 modes : speedRegulation and smoothAcceleration. These are initially enabled. <>
\r
17 * They can be switched off/on by the methods regulateSpeed() and smoothAcceleration().
\r
18 * The actual maximum speed of the motor depends on battery voltage and load..
\r
19 * Speed regulation fails if the target speed exceeds the capability of the motor.
\r
24 * Motor.A.setSpeed(720);// 2 RPM
\r
25 * Motor.C.setSpeed(720);
\r
26 * Motor.A.forward();
\r
27 * Motor.C.forward();
\r
28 * Thread.sleep (1000);
\r
31 * Motor.A.regulateSpeed(true);
\r
32 * Motor.A.rotateTo( 360);
\r
33 * Motor.A.rotate(-720,true);
\r
34 * while(Motor.A.isRotating();
\r
35 * int angle = Motor.A.getTachoCount(); // should be -360
\r
37 * @author Roger Glassey revised 26 March 2007
\r
39 public class Motor extends BasicMotor implements TimerListener
\r
41 private TachoMotorPort _port;
\r
42 private int _speed = 360;
\r
43 private int _speed0 = 360;
\r
44 // used for speed regulation
\r
45 private boolean _keepGoing = true;// for regulator
\r
47 * Initially true; changed only by regulateSpeed(),<br>
\r
48 * used by Regulator, updteState, reset*
\r
50 private boolean _regulate = true;
\r
51 private boolean _wasRegulating = false;
\r
52 public Regulator regulator = new Regulator();
\r
53 private Timer timer = new Timer(100,this);
\r
54 // used for control of angle of rotation
\r
55 private int _direction = 1; // +1 is forward ; used by rotate();
\r
56 private int _limitAngle;
\r
57 private int _stopAngle;
\r
58 private boolean _rotating = false;
\r
59 private boolean _wasRotating = false;
\r
60 private boolean _rampUp = true;
\r
61 private int _lastTacho = 0;
\r
62 private int _actualSpeed;
\r
63 private float _voltage;
\r
66 /** initialized to be false(ramping enabled); changed only by smoothAcceleration
\r
68 private boolean _noRamp = false;
\r
73 public static final Motor A = new Motor (MotorPort.A);
\r
78 public static final Motor B = new Motor (MotorPort.B);
\r
83 public static final Motor C = new Motor (MotorPort.C);
\r
85 public Motor (MotorPort port)
\r
88 regulator.setDaemon(true);
\r
92 public int getStopAngle() { return (int)_stopAngle;}
\r
95 * Causes motor to rotate forward.
\r
97 public void forward()
\r
99 synchronized(regulator)
\r
107 * Causes motor to rotate backwards.
\r
109 public void backward()
\r
111 synchronized(regulator)
\r
119 * Reverses direction of the motor. It only has
\r
120 * effect if the motor is moving.
\r
122 public void reverseDirection()
\r
124 synchronized(regulator)
\r
126 if (_mode == 1 || _mode == 2)
\r
128 _mode = (3 - _mode);
\r
135 * Causes motor to float. The motor will lose all power,
\r
136 * but this is not the same as stopping. Use this
\r
137 * method if you don't want your robot to trip in
\r
142 synchronized(regulator)
\r
150 * Causes motor to stop, pretty much
\r
151 * instantaneously. In other words, the
\r
152 * motor doesn't just stop; it will resist
\r
153 * any further motion.
\r
154 * Cancels any rotate() orders in progress
\r
158 synchronized(regulator)
\r
165 *calls controlMotor, startRegating; updates _direction, _rotating, _wasRotating
\r
169 _rotating = false; //regulator should stop testing for rotation limit ASAP
\r
170 // synchronized(regulator)
\r
175 _regulate = _wasRegulating;
\r
177 _wasRotating = false; // perhaps redundant
\r
179 if(_mode>2) // stop or float
\r
181 _port.controlMotor(0, _mode);
\r
184 _port.controlMotor(_power, _mode);
\r
191 _direction = 3 - 2*_mode;
\r
196 * @return true iff the motor is currently in motion.
\r
198 public final boolean isMoving()
\r
200 return (_mode == 1 || _mode == 2 || _rotating);
\r
205 * causes motor to rotate through angle. <br>
\r
206 * @param angle through which the motor will rotate
\r
208 public void rotate(int angle)
\r
210 rotateTo(getTachoCount()+angle);
\r
214 * causes motor to rotate through angle; <br>
\r
215 * iff immediateReturn is true, method returns immediately and the motor stops by itself <br>
\r
216 * When the angle is reached, the method isRotating() returns false;
\r
217 * @param angle through which the motor will rotate
\r
218 * @param immediateReturn iff true, method returns immediately, thus allowing monitoring of sensors in the calling thread.
\r
220 public void rotate(int angle, boolean immediateReturn)
\r
222 int t = getTachoCount();
\r
223 rotateTo(t+angle,immediateReturn);
\r
227 * causes motor to rotate to limitAngle; <br>
\r
228 * Then getTachoCount should be within +- 2 degrees of the limit angle when the method returns
\r
229 * @param limitAngle to which the motor will rotate
\r
231 public void rotateTo(int limitAngle)
\r
233 rotateTo(limitAngle,false);
\r
237 * causes motor to rotate to limitAngle; <br>
\r
238 * if immediateReturn is true, method returns immediately and the motor stops by itself <br>
\r
239 * Then getTachoCount should be within +- 2 degrees if the limit angle
\r
240 * When the angle is reached, the method isRotating() returns false;
\r
241 * @param limitAngle to which the motor will rotate, and then stop.
\r
242 * @param immediateReturn iff true, method returns immediately, thus allowing monitoring of sensors in the calling thread.
\r
244 public void rotateTo(int limitAngle,boolean immediateReturn)
\r
246 synchronized(regulator)
\r
251 _regulate = _wasRegulating;
\r
252 _wasRotating = false;
\r
254 _stopAngle = limitAngle;
\r
255 if(limitAngle > getTachoCount()) _mode = 1;
\r
257 _port.controlMotor(_power, _mode);
\r
258 _direction = 3 - 2*_mode;
\r
259 if(_regulate) regulator.reset();
\r
262 _stopAngle -= _direction * overshoot();
\r
263 _limitAngle = limitAngle;
\r
265 _rotating = true; // rotating to a limit
\r
266 _rampUp = !_noRamp && Math.abs(_stopAngle-getTachoCount())>40 && _speed>200; //no ramp for small angles
\r
267 if(immediateReturn)return;
\r
269 while(_rotating) Thread.yield();
\r
273 *inner class to regulate speed; also stop motor at desired rotation angle
\r
275 private class Regulator extends Thread
\r
278 *tachoCount when regulating started
\r
282 * set by reset, used to regulate motor speed
\r
284 float basePower = 0;
\r
286 * time regulating started
\r
291 * helper method - used by reset and setSpeed()
\r
293 int calcPower(int speed)
\r
295 // float pwr = 100 - 7.4f*Battery.getVoltage()+0.065f*speed;// no-load motor
\r
296 float pwr = 100 - 7.4f*_voltage+0.065f*speed;
\r
297 if(pwr<0) return 0;
\r
298 if(pwr>100)return 100;
\r
299 else return (int)pwr;
\r
303 * called by forward() backward() and reverseDirection() <br>
\r
304 * resets parameters for speed regulation
\r
306 public void reset()
\r
308 if(!_regulate)return;
\r
309 time0 = (int)System.currentTimeMillis();
\r
310 angle0 = getTachoCount();
\r
311 basePower = calcPower(_speed);
\r
312 setPower((int)basePower);
\r
316 * Monitors time and tachoCount to regulate speed and stop motor rotation at limit angle
\r
323 float accel =5f;// deg/sec/ms was 1.5
\r
325 int ts = 0;//time to reach speed
\r
327 { synchronized(this)
\r
329 if(_regulate && isMoving()) //regulate speed
\r
331 int elapsed = (int)System.currentTimeMillis()-time0;
\r
332 int angle = getTachoCount()-angle0;
\r
333 // basePower = calcPower(_speed);
\r
337 ts = (int)(_speed/accel);
\r
339 if(elapsed<ts)// not at speed yet
\r
342 // target distance = a * t * t/ 2 - maintain constant acceleration
\r
343 error = accel*elapsed * elapsed/2000 - (float)Math.abs(angle);
\r
345 else // adjust elapsed time for acceleration time - don't try to catch up
\r
347 error = ((elapsed - ts/2)* _speed)/1000f - (float)Math.abs(angle);
\r
351 error = (elapsed*_speed/1000f)- (float)Math.abs(angle);
\r
352 power = basePower + 0.75f * error;// -0.1f * e0;// magic numbers from experiment
\r
353 if(power<0) power = 0;
\r
355 float smooth = 0.0025f;// another magic number from experiment
\r
356 basePower = basePower + smooth*(power-basePower);
\r
357 setPower((int)power);
\r
359 // stop at rotation limit angle
\r
360 if(_rotating && _direction*(getTachoCount() - _stopAngle)>0)
\r
362 if(!_wasRotating)_speed0 = _speed;
\r
363 _mode = 3; // stop motor
\r
364 _port.controlMotor (0, 3);
\r
365 int a = angleAtStop();//returns when motor has stopped
\r
366 int remaining = _limitAngle - a;
\r
367 if(_direction * remaining >3 ) // not yet done; don't call nudge for less than 3 deg
\r
369 if(!_wasRotating)// initial call to rotate(); save state variables
\r
371 _wasRegulating = _regulate;
\r
374 setSpeed(300);//was 150
\r
375 _wasRotating = true;
\r
376 // limit = _limitAngle;
\r
378 nudge(remaining,a); //another try
\r
380 else //rotation complete; restore state variables
\r
382 setSpeed(_speed0);//restore speed setting
\r
383 _mode = 3; // stop motor maybe redundant
\r
384 _port.controlMotor (0, _mode);
\r
386 _wasRotating = false;
\r
387 _regulate = _wasRegulating;
\r
395 *helper method for run - stop at limit angle branch
\r
397 private void nudge(int remaining,int tachoCount)
\r
400 if(remaining>0)_mode = 1;
\r
402 _port.controlMotor(_power, _mode);
\r
403 _direction = 3 - 2*_mode;
\r
404 _stopAngle = tachoCount + remaining/3;
\r
405 if(remaining < 3 && remaining > -3) _stopAngle += _direction; //nudge at least 1 deg
\r
411 *helper method for run
\r
415 int a0 = getTachoCount();
\r
416 boolean turning = true;
\r
420 _port.controlMotor(0,3); // looks redundant, but controlMotor(0,3) fails, rarely.
\r
421 try{Thread.sleep(20);}// was 10
\r
422 catch(InterruptedException w){}
\r
423 a = getTachoCount();
\r
424 turning = Math.abs(a - a0)>0;
\r
432 *causes run() to exit
\r
434 public void shutdown(){_keepGoing = false;}
\r
438 * turns speed regulation on/off; <br>
\r
439 * Cumulative speed error is within about 1 degree after initial acceleration.
\r
440 * @param yes is true for speed regulation on
\r
442 public void regulateSpeed(boolean yes)
\r
448 * enables smoother acceleration. Motor speed increases gently, and does not <>
\r
449 * overshoot when regulate Speed is used.
\r
452 public void smoothAcceleration(boolean yes)
\r
456 * Sets motor speed , in degrees per second; Up to 900 is posssible with 8 volts.
\r
457 * @param speed value in degrees/sec
\r
459 public final void setSpeed (int speed)
\r
461 _speed = Math.abs(speed);
\r
462 setPower((int)regulator.calcPower(_speed));
\r
468 *sets motor power. This method is used by the Regulator thread to control motor speed.
\r
469 *Warning: negative power will cause the motor to run in reverse but without updating the _direction
\r
470 *field which is used by the Regulator thread. If the speed regulation is enabled, the rusults are
\r
473 public synchronized void setPower(int power)
\r
476 _port.controlMotor (_power, _mode);
\r
480 * Returns the current motor speed in degrees per second
\r
482 public final int getSpeed()
\r
486 public int getMode() {return _mode;}
\r
487 public int getPower() { return _power;}
\r
489 private int overshoot()
\r
491 return (int)(5+ _speed*0.060f);//60?
\r
495 * Return the angle that a Motor is rotating to.
\r
497 * @return angle in degrees
\r
499 public int getLimitAngle()
\r
501 return _limitAngle;
\r
505 *returns true when motor is rotating towarad a specified angle
\r
507 public boolean isRotating()
\r
511 public boolean isRegulating(){return _regulate;}
\r
513 * requred by TimerListener interface
\r
515 public void timedOut()
\r
517 int angle = getTachoCount();
\r
518 _actualSpeed = 10*(angle - _lastTacho);
\r
519 _lastTacho = angle;
\r
520 _voltage = Battery.getVoltage();
\r
524 *returns actualSpeed degrees per second, calculated every 100 ms; negative value means motor is rotating backward
\r
526 public int getActualSpeed() { return _actualSpeed;}
\r
528 * Returns the tachometer count.
\r
530 * @return tachometer count in degrees
\r
532 public int getTachoCount()
\r
534 return _port.getTachoCount();
\r
538 * Resets the tachometer count to zero.
\r
540 public void resetTachoCount()
\r
542 _port.resetTachoCount();
\r
545 public float getError() {return regulator.error;}
\r
546 public float getBasePower() {return regulator.basePower;}
\r