Controlling NXT motors

Being able to control the NXT motors is crucial for most robots. Only very few models work without moving parts. So read on to learn all you need about the class NXTMotor.

Contents

Getting ready

If you want to execute the examples from this tutorial directly in the command window, I recommend this little sequence of commands to establish a connection to your NXT:

COM_CloseNXT all
h = COM_OpenNXT('bluetooth.ini');
COM_SetDefaultNXT(h);

Now all following examples should work right away by copy-pasting...

The basics

To operate the motors, we need to create motor objects, i.e. instances of the class NXTMotor. Those objects are capable of sending various commands to one or two motors, or can be used to retrieve data from them. To create such an object, the simplest way is just

mA = NXTMotor('A')

Now the object mA provides properties and methods to control the NXT motor on port A. First, we probably just want to see if the motor works at all. Ok then, let's go:

mA.Power = 50;  % let's start with half the maximum power
mA.SendToNXT(); % this is actually the moment we start the motor

Motor A keeps on running... To stop it, we use

mA.Stop('off');

This will turn off the power, causing the motor to come to a soft stop (so called "coasting"). If we wanted to brake all of a sudden at once, we'd call

mA.Stop('brake');

In this case, the NXT's active brake will be enabled, causing the motor to stop hard at the instance it receives the command. The brake alsp keeps the motor actively at its position. It's surprisingly strong, however it also consumes a lot of power, so we better turn it off again. The motor is now completely turned off just as in the beginning of our tutorial session:

mA.Stop('off');

Constructing objects and setting properties

You've already seen the basic concept of motor control which consits of these 3 basic steps:

  1. Create an NXTMotor object
  2. Set up the parameters as you need them
  3. Send the command to the NXT using SendToMotor()

You've already seen how to specify the motor settings by setting properties.

mB1       = NXTMotor();  % create a blank motor object
mB1.Port  = MOTOR_B;     % this is the same as using  mB.Port = 'B';
mB1.Power = -100;        % full power backwards

The example above shows two things:

  1. You can use the old-fashioned motor constants (i.e. MOTOR_C) to specify port names, or do it by giving strings (like 'C').
  2. To reverse the direction the motor should turn, set negative power values (as in electronics you'd swap the sign of the current)

A shorter way to create exactly the same object as mB1 is to use NXTMotor's constructor. The first argument always has to be the port you want to use, followed by combinations of 'Property', value as you might know from other MATLAB commands:

mB2 = NXTMotor('B', 'Power', -100, 'SpeedRegulation', false);

As said before: mB1 and mB2 do exactly the same, so use whichever way you prefer. To inspect motor objects on the command line, just type their name:

mB1
mB2

Precise moves

Knowing how we control the motor's speed by setting a certain value to Power is not enough in most cases. One could try to move the motor a certain distance by using something like this:

mA = NXTMotor('A', 'Power', 50);
mA.SendToNXT();
pause(3);   % wait exactly 3 seconds
mA.Stop('brake');

Using this technique we get the motor running 3 seconds, but due to several influences (for example Bluetooth lag or subtle differences in NXT motors) this won't lead to the same distance travelled by the motor. Also we can't manage different loads on the motors. This is why we have to specify the distance the motor should turn in degrees. The according property is called TachoLimit.

mA.TachoLimit = 360;
mA.SendToNXT();

Now the motor will make exactly 1 whole turn and then stop at about 360 degrees (accurate to +/- 1 degree in most cases). It is very important to understand that the motor does this movement "on its own", while MATLAB can execute the next command right away. If we say:

mB = NXTMotor('B', 'Power', 50, 'TachoLimit', 1000);
mB.SendToNXT();
NXT_PlayTone(440, 500);

then the NXT will start turning motor B and beep right away! If we want to beep only when the motor has reached its position, we have be patient and wait for the motor:

mB.SendToNXT();
mB.WaitFor();            % this command will hold MATLAB a while
NXT_PlayTone(440, 500);  % now the motor has stopped!

This brings us to the next important point: In order for a motor to accept a command with SendToNXT(), it must be idle, i.e. at rest (and not carrying out a movement already). We can achieve this by either:

If we don't follow this rule, the NXT will ignore the second motor command and tell you it has just dropped a command by a beeping signal. Example:

% imagine we want our motor to make 2 turns of 360 degrees
mB = NXTMotor('B', 'Power', 50, 'TachoLimit', 360);
mB.Stop('off');  % Just to make sure motor is ready!
mB.SendToNXT();  % Motor is busy now
mB.SendToNXT();  % THIS DOES NOT WORK!!! MOTOR IS BUSY
% This example is wrong.
% We have to use a .WaitFor between the two .SendToNXT()

To switch back to unlimited movement, just set TachoLimit back to 0.

Different braking modes

So setting a value to TachoLimit specifies the angle the motor should spin to, and by the sign of Power we can decide the turning direction. We still have some more options to control how the motor should brake. The property is called ActionAtTachoLimit. Quoting possible parameters from the documentation of NXTMotor:

Speed regulation and smooth start

There are two more options left we'll shortly discuss here. For further details you should consider the documentation of NXTMotor.

Using two motors

Until now we've always talked about motor objects controlling one single motor. You could create two of these objects to start a driving robot (where the wheels are attached to ports B and C):

mB = NXTMotor('B', 'Power', 50);
mC = NXTMotor('C', 'Power', 50);
mB.SendToNXT();
mC.SendToNXT();

There are two problems with this simple approach:

mBC = NXTMotor('BC', 'Power', 50);
mBC.SendToNXT();

Easy, huh? We can also use the commands WaitFor() or stop as we're used to. And the property Port can of course be accessed as usual. Another example:

myMotors             = NXTMotor();
myMotors.Port        = [MOTOR_B; MOTOR_C];  % same as  myMotors.Port = 'BC';
myMotors.Power       = 50;
myMotors.TachoLimit  = 1000;
myMotors.SmoothStart = true;
myMotors.SpeedRegulation = false;  % don't forget this!
% If we tell the constructor that we use two ports, speed regulation will
% automatically turned off, i.e.:
% m = NXTMotor('BC');  % <-- m.SpeedRegulation = false is set already!

myMotors.Stop('off');  % make sure motors are off before we start.
myMotors.SendToNXT();

There is one certain behavior you should get familiar with. Let's assume you use the following code:

mBC = NXTMotor('BC', 'Power', 50, 'TachoLimit', 1000);
mBC.ActionAtTachoLimit = 'Coast';
mBC.SendToNXT();
mBC.WaitFor();
pause(3);

During the final pause of 3 seconds, we can notice a certain high-pitched noise coming from the motors. Also they seem to be powered up and slightly moving or vibrating. This is the motor synchronization in action. If you try to move one wheel, the other will be moved automatically, too. This ensures the wheels are (roughly) in the same position, as if they were connected through an axle. We can manually turn off this synchronization with Stop():

mBC.Stop('off');

Driving bots the easy way

Using the knowledge we now have about NXTMotor, it's time to get used working with multiple motor objects. In the following example, we set up a basic set of objects. To

% Set some parameters:
leftWheel   = MOTOR_B;
rightWheel  = MOTOR_C;
bothWheels  = [leftWheel; rightWheel];
drivingPower = 60;
turningPower = 40;
drivingDist  = 1000; % in degrees
turningDist  = 220;  % in degrees

% now create the objects for straigt driving:
mForward = NXTMotor(bothWheels, 'Power', drivingPower, 'TachoLimit', drivingDist);
mReverse = mForward; % clone object
mReverse.Power = -mForward.Power; % just swap the power sign

Now we can already drive our bot a certain distance forward or reverse. We just have to send the objects to the NXT and wait until they complete:

% let the bot drive forward
mForward.SendToNXT();
mForward.WaitFor();
% and now return to the origin
mReverse.SendToNXT();
mReverse.WaitFor();
% done!
NXT_PlayTone(440, 500);

In order to make nice turns (by 90 degrees for example), it's best to use a 2-way motion: First let one wheel spin one direction (now the bot has made half of its turn and faces 45 degrees), then turn the other wheel into the opposite direction. Now the 90-degree-turn should be complete.

% for turning the bot, we have two objects each:
mTurnLeft1 = NXTMotor(leftWheel, 'Power', -turningPower, 'TachoLimit', turningDist);
mTurnLeft1.SpeedRegulation = false; % don't need this for turning

% for the 2nd part of turning, use first part's settings and modify:
mTurnLeft2 = mTurnLeft1;                 % copy object
mTurnLeft2.Port     = rightWheel;        % but use other wheel
mTurnLeft2.Power    = -mTurnLeft1.Power; % swap power again

% the right-turn objects are the same, but mirrored:
mTurnRight1 = mTurnLeft1;               % first copy...
mTurnRight2 = mTurnLeft2;
mTurnRight1.Power = -mTurnRight1.Power; % now mirror powers
mTurnRight2.Power = -mTurnRight2.Power;
% Instead of mirroring the powers, we could've also changed
% the ports (swapped left and right wheels).

You can now use these objects like this:

% make a left-turn
mTurnLeft1.SendToNXT();
mTurnLeft1.WaitFor();
mTurnLeft2.SendToNXT();
mTurnLeft2.WaitFor();

% wait here a moment
pause(1);

% turn back to the origin
mTurnRight1.SendToNXT();
mTurnRight1.WaitFor();
mTurnRight2.SendToNXT();
mTurnRight2.WaitFor();

Reading motor positions

Apart from sending commands to the motors, we can also retrieve information from them, the most important being the current position in degrees. For more documentation on this, see the help texts for ReadFromNXT and ResetPosition. These are the commands we'll use in this section.

So, when calling ReadFromNXT() on a motor object, a MATLAB struct is returned, containing some useful fields. We'll just focus on Position, i.e. where the motor has currently turned to. This rotation counter keeps on tracking every movement, wether you turn the motor by hand or by a commend, it always tells you the exact position of the axle at the time the NXT receives your request. The change of Position corresponds to the turning direction: If the motor turns the way as it would if operated with a positive Power, Position increases. Otherwise (spinning reverse), Position decreases (and can become negative). Enough of the theory, a simple example:

% We need a motor object to get access to the methods
% also we do a little driving...
m = NXTMotor('A', Power, '50');
% clear position counter:
m.ResetPosition();

m.SendToNXT();
pause(1);
m.Stop('off');
% motor is still coasting at the moment...

% now retrieve motor info
data = m.ReadFromNXT();
% show it to the user:
disp(sprintf('Motor A is currently at position %d', data.Position));

% wait until the motor doesn't move anymore
pause(2);
% and display the position again:
data = m.ReadFromNXT();
disp(sprintf('Motor A is finally at position %d', data.Position));

Please note that all other properties of the motor object except Port do not matter to use the method the methods ReadFromNXT() and ResetPosition. Also the returned data-structure from ReadFromNXT() only reflects the current state of the NXT motor, and does not necessarily have something to do with the motor object's properties you used to retrieve the data (e.g. TachoLimit and Power of the retrieved data and of the object can be different).

Full example

Now let's imagine we want to control a robotic arm. For the sake of this example, we want it to move up and down, alternatingly for 10 times. Let the arm just have one joint, so we only control one motor. A little problem is gravity: The arm has a certain weight. So if we aren't careful, moving it downwards will sometimes result in a little lower position than we expect, and moving upwards won't move the motor as far as we hope all the time. After all, the motor control is only accurate to a couple of degrees (+/- 1 most of the time, but not necessarily equally distributed). So without reading the motor's position and accounting for those inaccuracies, the errors would accumulate over time and cause a significant displacement of the arm. By simply retrieving the arm's position and making sure that it moves a bit more upwards if necessary, or a bit less downwards, we can avoid the accumulating errors and have precise movement even after a long period of operation. Here is how its done:

% Example which should move a motor (or robotic arm) 10 times back and
% forth (up and down), as precisely as possible without blindly trusting
% the motor commands. Very simple error compensation (i.e. trying absolute
% movements instead of always relative).

% Prepare MATLAB
COM_CloseNXT all
close all
clear all

% Connect to NXT, via USB or BT
h = COM_OpenNXT('bluetooth.ini');
COM_SetDefaultNXT(h);

% set params
power = 30;
port  = MOTOR_A;
dist  = 200;    % distance to move in degrees

% create motor objects
% we use holdbrake, make sense for robotic arms
mUp    = NXTMotor(port, 'Power',  power, 'ActionAtTachoLimit', 'HoldBrake');
mDown  = NXTMotor(port, 'Power', -power, 'ActionAtTachoLimit', 'HoldBrake');

% prepare motor
mUp.Stop('off');
mUp.ResetPosition();

% repeat 10 times
for j=1:10

    % where are we?
    data = mUp.ReadFromNXT();
    pos  = data.Position;

    % where do we want to go?
    % account for errors, i.e. if pos is not 0
    mDown.TachoLimit = dist + pos;

    % move
    mDown.SendToNXT();
    mDown.WaitFor();

    % now we are at the bottom, repeat the game:
    % where are we?
    data = mUp.ReadFromNXT(); % doesn't matter which object we use to read!
    pos  = data.Position;

    % pos SHOULD be = dist in an ideal world
    % but calculate new "real" distance to move
    % based on current error...
    mUp.TachoLimit = pos;
    % this looks very simple now, but it comes from
    %   TachoLimit = dist + (pos - dist);
    % i.e. real distance + error correction
    % Imagine it this way: We are currently at pos,
    % and want to go back to 0, so this is exactly the distance
    % to go!

    mUp.SendToNXT();
    mUp.WaitFor();

end%for

% mode was HOLDBRAKE, so don't forget this:
mUp.Stop('off')
% clean up
COM_CloseNXT(h);

Limitations of NXTMotor

As nice and easy as working with NXTMotor is, the class has its limitations (you can read more about it in the documentation of NXTMotor). One minor problem is that you can't change the speed (i.e. setting a different Power and then call SendToNXT()) of an already running motor. You have to use WaitFor() or Stop() before updating the power, which might not do what you want (changing the speed smoothly during runtime).

Well, there is one exception: If a motor is running infinitely with TachoLimit = 0 set, you actually can send a motor command with a new power, and it will work.

A sometimes bigger problem is latency: Motor commands are not necessarily sent to the NXT at once. Sometimes SendToNXT() takes roughlpy up to 20ms for the actual motor instruction to be executed. Same goes for WaitFor() or Stop(). This is the price you have to pay for the great accuracy that NXTMotor gives you. The reason has to do with the embedded NXC program MotorControl, which is running on the NXT and handles high precision motor movement for you.

What to do if you need fast motor control instead of high precision (i.e. when building a Segway)? Well, you can trade accuracy for performance. The answer is: Use DirectMotorCommand. The last chapter explains how.

For experts: DirectMotorCommand

DirectMotorCommand is a remainder from previous toolbox versions 1.x and 2.x (it was called SendMotorSettings before). It works without a program running the brick by sending "direct commands" (a term from the LEGO NXT Mindstorms communication protocol) only. First of all:

Warning: Do not use DirectMotorCommand and NXTMotor.SendToNXT() for the same motor at one time together (or only if you know exactly what you're doing). It can mess up your motor control and lead to unexpected results! You can still use NXTMotor.ReadFromNXT() if you like.

Direct motor command gives access to some advanced features:

There are however more problems:

The big advantage is execution speed: Nothing else is faster than DirectMotorCommand or NXT_SetOutputState. Latency via USB connections is usually about 3ms, and via Bluetooth it can be between 5ms and 30ms, or even more for synchronized driving.

Make sure to read the documentation page for DirectMotorCommand.

I might help to also read the documents "Executable File and Bytecode Reference" (LEGO MINDSTORMS NXT Executable File Specification) and "Bluetooth Developer Kit (BDK)" from http://mindstorms.lego.com/Overview/nxtreme.aspx ; it may help you getting familiar with the communication protocol. Other useful commands are NXT_GetOutputState and NXT_SetOutputState.


Proceed to next chapter.