Learn EV3 Python‎ > ‎

Using Motors

Be sure to be using version 17 or later of ev3dev because significant changes were made in versions 10 and 17. You can check the kernel version by selecting "About" in Brickman and scrolling down to the "kernel version". If you don't have a compatible version, upgrade the kernel before continuing. 

The most important change in version 10 is that speed regulation is now always on (except for the run_direct command) which means motors are now always controlled by speed_sp parameter (except for the run_direct command). Basically then, you should avoid using run_direct and thus never use duty_cycle_sp and always use speed_sp. When using run_forever in place of run_direct you need to be aware that changing speed_sp while run_forever is running does NOT have immediate effect whereas changing duty_cycle_sp while run_direct was running DID have immediate effect.

In version 17 a bug was fixed which had caused the motors to behave unreasonably with large values of speed_sp.

For the EV3 motors, speed_sp is in degrees per second so if you set speed_sp to 360 the motor will try to rotate 360° per second or one rotation per second. For the standard EV3 large motors, a speed_sp value of 1000 (degrees per second) is roughly equivalent to a duty_cycle_sp value of 100 and to a power value of 100 in the standard Lego EV3 software (EV3-G). Therefore with the large motors you should always use speed_sp values in the range -1000 to +1000. (Prior to February 2017 I was recommending the use of values in the range -900 to +900 because a bug was causing unpredictable behaviour with values outside this range but that bug was solved with version 17 of ev3dev so as long as you have version 17 or later you can safely use values -1000 to +1000.) 

With the standard EV3 medium motor it should be safe to use values of speed_sp up to 1400 but it is very convenient to assume that the maximum advisable value is 1000 so that the medium motor can be considered to behave just like the large motor.

Note that speed_sp represents the TARGET speed in degrees per second but motors are of course subject to the laws of physics so the real speed may sometimes not correspond to the requested speed. For example, if you run this code mB.run_timed(time_sp=600, speed_sp=600) then you might expect the motor to turn 0.6s*600°/s=360°=1 rotation but in reality it will turn significantly less (maybe 15% less) because the inertia of the motor stops the motor from reaching its target speed instantly and therefore for a short period at the beginning of the motion the motor turns less fast than requested. Also, be aware that most EV3 large motors are not quite capable of reaching a speed of 1000 degrees per second so for values of speed_sp above 900 you might get an actual speed that is a few percent less than that requested.

When low values of speed_sp are used the movements can again differ from what was requested but in this case the motor tends to move faster than requested, for reasons that are not yet well understood. For example, when using speed_sp=100 the motor may turn about 23% faster than requested.

For the official motor documentation click HERE.

You may want to make one or more motors turn at a given speed
  • through a given angle or number of rotations
  • for a given time
  • 'forever' (until the motor is stopped by a stop() command later during program execution)

Turn motor through a given angle
Use run_to_rel_pos(position_sp=<angle in degrees>, speed_sp=<value>). If you want to motor to run backwards then use a negative value for position_sp rather than for speed_sp. Using a negative value for speed_sp will not work because the sign of speed_sp is ignored by this command (but not by others). Use speed_sp values between 0 and 1000.

Example
To make a large motor on port B turn through 360° at speed 900 and optionally apply a 'hold' (like a strong brake - see later):

#!/usr/bin/env python3
# so that script can be run from Brickman

from ev3dev.ev3 import *
from time import sleep

m = LargeMotor('outB')

m.run_to_rel_pos(position_sp=360, speed_sp=900, stop_action="hold")

sleep(5)   # Give the motor time to move

Turn motor for a given time
Use  run_timed(time_sp=<time in milliseconds>, speed_sp=<value>)
Note that any negative sign for time_sp is ignored.

Example 
This example runs a large motor attached to port B backwards for 3 seconds with a 'speed setpoint' set to -750 (equivalent to a power setting of -75 in the standard EV3 software). It will work even if a second large motor is also plugged in to another motor port, since the port letter is specified. Without the last line, the program would end as soon as the motor starts to turn, so you probably wouldn't see it move at all. Waiting for 5 seconds (1+4) ensures that the motor can turn for 3 seconds before the program ends.

#!/usr/bin/env python3
# So program can be run from Brickman

from ev3dev.ev3 import *
from time import sleep
m = LargeMotor('outB')
m.run_timed(time_sp=3000, speed_sp=-750)
print("set speed (speed_sp) = " + str(m.speed_sp))
sleep(1)  # it takes a moment for the motor to start moving
print("actual speed = " + str(m.speed))
sleep(4)

The above example prints the value of speed_sp to the terminal window, or console. I've used a '+' operator to concatenate (join) the text string on the left with the speed_sp value on the right, but the speed_sp value is an integer so I had to convert it to a text string with the str() function before the concatenation could take place. I think it is reasonable for newbies to use the '+' operator in this way, but there is another method which is often preferred by more advanced Python programmers - see this discussion and this page. Note as you run the above program that the print statement is run as soon as the motor STARTS to move - the program does not wait until the motor has finished moving before running the next command.

The example also gives a value (m.speed) for the actual speed of the motor, one second after the motor was told to start turning. The one second delay is needed because it takes a moment for the motor to get going - without the delay the actual speed would probably be given as zero because the motor would not yet have begun turning.

Interestingly, the above program also works if the third line is changed to
m = Motor('outB')
but does not work (with a large motor still attached to port B) if this line is used:
m = MediumMotor('outB')

Turn motor 'forever'
Use      run_forever(speed_sp=<value>)   and stop the motor with stop() or stop(stop_action=<value>) where <value> can be "coast", "brake" or "hold".

Example
#!/usr/bin/env python3
# So program can be run from Brickman

from ev3dev.ev3 import *
from time import sleep
m = LargeMotor('outB')

m.run_forever(speed_sp=900)
sleep(5)
m.stop(stop_action="hold")
sleep(5)

I show this code because it is brief - in reality you would never use this code because the motor will run for a given time so it would be better to use run_timed. Real use of run_forever could be, for example, to start a robot moving then interrupt the motion when an obstacle is detected.

In my example the  stop_action  is set to  'hold'  which makes an active, forceful effort to stop the motor turning. You can alternatively use  stop_action="brake"  which passively and less forcefully tries to stop the motor turning, or you can use  stop_action="coast" which lets the motor continue to turn until stopped by friction. If you want, you can also specify a value for stop_action when running other motor commands like  run_timed()  or  run_to_rel_pos()In the standard Lego EV3 icon-based software the only options are 'coast' and 'brake'.

The value of stop_action will be remembered so in the following code, for example, the second stop() command will use stop_action="coast" . Also, the second run_forever command will use the remembered value for speed_sp since none was specified.

#!/usr/bin/env python3
# So program can be run from Brickman

from ev3dev.ev3 import *
from time   import sleep
m = LargeMotor('outB')

m.run_forever(speed_sp=200)   # equivalent to power=20 in EV3-G
sleep(5)
m.stop(stop_action="coast")
sleep(4)

m.run_forever()
sleep(5)
m.stop()
sleep(4)

Wait for completion
When a command causes a motor to begin a movement program execution does not pause for the movement to complete before continuing. Often we DO want program execution to pause. It is usually possible to use a sleep() command for this but this is not ideal - the script below shows a better way to make the program pause until the movement completes.

#!/usr/bin/env python3
from ev3dev.ev3 import *

motor = LargeMotor('outC')
motor.run_timed(time_sp=3000, speed_sp=-750, stop_action='brake')
motor.wait_while('running')

Sound.beep()

Note that the above method works with all three possible stop_action values, 'coast', 'brake' and 'hold'. The beep() command is included so that you can hear that program execution was indeed paused until the motor's movement was complete before playing the beep. The wait_while() command only works in EV3 Python v0.8.0 or later, so be sure to have the latest version. Version 0.8.0 was released in October 2016.

You may sometimes see the following code fragment:
while any(motor.state):
    sleep(0.1)
this code is also designed to make program execution pause until the motor completes its movement but it is less satisfactory, largely because it does not work with stop_action='hold'. This code fragment above should therefore be considered obsolete.

Stopping the Motors
You already know that motors can be stopped with the stop() command and that stopping can be done in three ways: coast, brake or hold (for example, to stop a motor called mB: mB.stop(stop_action='brake') . I think the stop_action='coast' is probably rarely used. I also think that it would quite rare to change to stop_action mode during a script e.g. from brake to hold. Therefore I think that in programs where the stop_action mode will not change the stop_action mode should be set just once and as early in the script as possible. However, so not set the stop_action mode in the same line that assigns the motor to a motor class since that will not work as expected. In other words, do not do this (I've used strikethrough to emphasise that this is wrong). mB=LargeMotor('outB', stop_action='brake').

Sometimes the motors will continue to run after a (poorly-written?) script terminates. How would you stop the motors in such a case? You could start up the Python3 interpreter and issue a few commands or you could do what I do: keep a special script 'stop-motors.py' to hand for just such cases. My script only works for two large motors called mB and mC plugged into ports B and C, but for me that is often the case. Modify the script if necessary to match your situation.

#!/usr/bin/env python3
from ev3dev.ev3 import *

mB = LargeMotor('outB')
mC = LargeMotor('outC')

mB.stop()
mC.stop()

# to make extra sure the motors have stopped:
mB.run_forever(speed_sp=0)
mC.run_forever(speed_sp=0)

Speed Regulation
EV3 Python usually uses a feature called 'speed regulation' when using motors. When speed regulation is on, the motor controller will vary the power supplied to the motor to try to maintain the speed specified in speed_sp which is in degrees per second. It's comparable to the way your car's cruise control will try to maintain a constant speed even if your car goes up or down hills. If speed regulation is off, which since kernel version 10 and later is only the case for run_direct(), the controller will use the power specified in duty_cycle_sp. Don't forget that for the EV3 large motors a power value of 100 in the standard Lego EV3 software (EV3-G) is roughly equivalent to a speed_sp value of 1000.

Other Notes
Note that if you have only one large motor plugged in to the EV3 then you do not need to specify which motor port it is plugged into. The same is true for the medium motor, and even for each type of sensor. 

This page has introduced some but not all of the motor commands. For example, it introduced run_to_rel_pos() but not run_to_abs_pos(). To learn about other commands, see the official documentation.
Comments