This tutorial builds on the concepts in the tutorial on basic motor control to help you add precise motor control to your robots. Examples would include pointing the ultrasound sensor in a specific direction or moving the robot a specific distance.
This tutorial will discuss the pbLua Output API and how to use it to get more advanced control of the motors.
By the end of this tutorial, you’ll be able to tell the motors to turn a specific number of degrees, use the 3 built-in tachometer variables, and determine when a motor is finished a specific action.
Precision Motor Control
In the previous tutorial, you learned how to set the motor speed, and how to use regulation to control that speed. Now let’s try and figure out if we can get it to turn a specific number of degrees. In the previous section we glossed over the second parameter in the nxt.OutputSetSpeed() function, which is the runstate.
The runstate tells the driver what kind of motion we want it to provide. It can do a number of things including:
- Idle the motor, which turns it off
- Ramp the motor up to a certain speed
- Run the motor at a fixed speed
- Ramp the motor speed down to a certain speed
- Hold the motor in a fixed position
When the standard NXT firmware does a motor move block, it actually breaks the motion into three parts. The first is a ramp up phase, then a run phase, and finally a ramp down phase.
We don’t really need to do that with pbLua. The driver used by pbLua is a slightly modified version of the original LEGO firmware that automatically handles stopping (and holding) the motor at a particular location.
Note that the positioning operation works best when the batteries in the NXT are fresh. Lower battery voltage will make the motor driver operation
much slower when approaching a target tachometer count.
Using Tachometer Variables
The NXT motors are superior to the old RCX motors in strength and features, and one of the most important new features is the tachometer feedback.
The NXT motor connector features two signals that are used by the firmware to update three counters that can be read by the user. The current accumulated tachometer reading of the motor has 360 counts in a full revolution. The tacho value is signed, so as the motor turns forwards the count increases, and as the motor turns backwards, the count decreases, eventually passing through 0 and becoming negative.
All three of the tacho variables are updated as the motor turns, all three are available as return values from the nxt.OutputGetStatus(), but they are used slightly differently by the NXT firmware as follows:
|tacho||Determines the target value for the current motor control operation|
|blocktacho||Reset before every motor control block in the standard firmware, and is resettable any time by pbLua|
|rotcount||An auxiliary tachometer value in the standard firmware, and is resettable any time by pbLua|
To reset any (or all) of the tachometer values to 0, use the nxt.OutputResetTacho() function call. Set the parameter of the corresponding tacho to a non-zero value to force that tacho to be cleared. The default value is 0 which leaves the tachometer alone.
The following program shows how the tachometer settings are affected by the nxt.OutputResetTacho(), according to this table:
|Left Arrow||Clear the tacho counter|
|Grey Square||Clear the blocktacho counter|
|Right Arrow||Clear the rotcount counter|
When you run the program, make sure you have a motor plugged into the port. Turn the motor by hand to see the tacho values changing, and press the grey buttons on the NXT to clear the desired tachometer count. Press the orange button to stop the program.
-- Read tachos until the orange button is pressed function TachoRead(port) repeat if 4 == nxt.ButtonRead() then nxt.OutputResetTacho(port,1,0,0) elseif 2 == nxt.ButtonRead() then nxt.OutputResetTacho(port,0,0,1) elseif 1 == nxt.ButtonRead() then nxt.OutputResetTacho(port,0,1,0) end print( nxt.TimerRead(), nxt.OutputGetStatus(port) ) t = nxt.TimerRead() while t+100 > nxt.TimerRead() do -- nothing end until( 8 == nxt.ButtonRead() ) end -- And using the function with a motor plugged into port A... TachoRead(1)
In most cases, you’ll end up using only the main tacho value, and you’ll never touch the other two.
Turning To A Specific Position
All that’s really left to do now is show you how to set up a motor to turn a particular number of tacho counts, which is easy if you keep one thing in mind:
Incremental tacho counts are always positive numbers, the firmware automatically takes care of whether you’re going forwards or backwards based on the value of the speed parameter!
When you are trying to set the motor to a specific postion you must use single motor regulation in brake mode. This makes it much easier to take advantage of the built-in motor control firmware. You only have to set the regulation mode once, then call the nxt.OutputSetSpeed()
function with the desired speed and incremental target position, like this:
-- Turn Motor 1 exactly 180 degrees at half speed port = 1 nxt.OutputSetRegulation(port,1,1) nxt.OutputSetSpeed(port,0x20,50,180)
This code is fine, but sometimes you want to wait until the motor has moved to the desired position. An example of this is a “radar” type sweep of the ultrasonic sensor mounted to a motor. You might want it to scan to the left and to the right continuously.
Obviously, if you tell the motor to scan to the right before it’s finished scanning to the left you’ll end up missing some useful data. Here’s an example that waits for the motor to get within 4 counts of the target position before continuing.
-- Turn Motor 1 exactly 180 degrees - and wait until done port = 1 function move(degrees) nxt.OutputSetRegulation(port,1,1) _,tacho = nxt.OutputGetStatus(port) nxt.OutputSetSpeed(port,0x20,nxt.sign(degrees)*50,nxt.abs(degrees)) repeat _,curtacho = nxt.OutputGetStatus(port) until 4 > nxt.abs( curtacho - (tacho + degrees) ) end
Once neat little trick in the previous code is how the degrees parameter is manipulated to make sure that the degrees are always specified as a positive number. The nxt.sign() function makes sure that the motor speed has the dame sign as degrees, while nxt.abs() makes sure that degrees is positive for the call to nxt.OutputSetSpeed().
When you apply this code to a real robot, you may want to consider additional factors and error cases including:
- Does the load affect the tolerance of the motion?
- What happens if the motor never gets to the target position?
- Do you need a timeout to detect if the motor is still in motion?
By playing around with the motor code and experimenting on your own robots and observing their behaviour, you’ll soon figure out how to make the best use of the pbLua Output API.