MindControl
is a Python library for a direct control over two types of popular
LEGO Mindstorms devices, the EV3 and the NXT. You can use it to
perform accurate motor movements, as well as perform some sensor
measurements via Bluetooth, using your computer or any other
compatible Bluetooth-enabled device you can run Python on.
It runs on
Python 3.x, requires a quick and simple installation, and is free to
download. There are some other similar libraries available across the
Internet, but my aim was to make it work as simply as possible
without omitting anything crucial or going against the Python grain.
Namely, you can use it to:
- spin any motor by a given angle, under any power
- perform simultaneous controlled turns on the EV3, as well as "free", unsynchronized spinning
- read sensor measurement values from the EV3
- play tones on the EV3
Obviously,
what is remaining to do at the time of writing is to implement the
sensor measurements on the NXT, not just the EV3. Admittedly, I did
not venture into EV3 screen control either, assuming that the users
will be looking at the computer screen rather than the EV3 display to
see what is going on.
Installation
Installation
of MindControl is rather simple, especially if you are already
familiar with Python and its environment. Of course, a prerequisite
for using it is at least one EV3 or NXT brick with its peripherals,
although you can use both, or more of each if you prefer ― at least
in theory, Bluetooth should allow seven simultaneous devices, and
there are workarounds (unrelated to MindControl) that should allow
for even more.
The
installation differs slightly depending on the type of device you are
using, so pay close attention to that while setting things up:
- Install Python. Obviously, our starting assumption is that you will use Python, so if you don't have it yet, download and install it according to the instructions from www.python.org. It is free. Most likely you have it already installed if you read this far.
- Install PySerial module. To run MindControl, you will need to install this additional module that takes care of serial port connections. You can download it at pypi.python.org or, just as simple, head to c:\python\scripts and execute pip install pyserial. Again, even if you are a sporadic Python user, this should sound very familiar.
- Download the MindControl Python module here. Simply place it in a folder where you plan to write your own Python scripts (.py) using this module.
- Turn on your Mindstorms bricks, and enable Bluetooth both on them and your computer.
- Pair each of your devices with the computer if not paired already. Make sure new serial ports have been created for each of your Mindstorms devices. Click on the Bluetooth icon in your tray, and choose Open Settings. Then click on the COM Ports tab. At least one COM port should be assigned to each device ― note its number. If more than one port is assigned for a device, note its Outgoing COM port number. In the screenshot, one has to note the COM4 for the NXT, and the COM8 for the EV3. If you are not using Windows, please find a relevant similar operation that applies to your OS.
- If you are using any NXT devices, download a MindControl executable here and upload it on each of your devices, either by using the official LEGO software, or some alternative solution such as NEXTTool. This step is not required for the EV3 devices.
- Start the MindCtrl program on the NXT. This step is required always before you start the scripts unless you use a start function, which will be described later.
You are
now ready to start coding, i.e. creating and running your own Python
scripts using MindControl.
Usage
All the
commands for the Mindstorms devices are based around the two main
classes, EV3 and NXT. You need to create an object for each of your
devices separately, and can subsequently use it to pass commands to
them. Apart from the explicite exceptions, all the commands for both
types of devices are synchronized, i.e. the script is suspended while
the command is being executed and the flow is resumed only when it
has been completed and confirmed as such by the device. In other
words, should you pass two motor instructions one after the another,
you can rely on the system to wait for the first one to finish
completely before the second one is called.
Creating the instances
To create
an object corresponding to a physical device, you just need to supply
the aforementioned serial port number (COM number) it is connected
to. For that particular example, you would need to use the following:
import
mindctrl
ev3device=mindctrl.EV3('COM8')
nxtdevice=mindctrl.NXT('COM4')
That
should create two objects, ev3device
and nxtdevice,
to which you can subsequently pass commands. You need to create this
sort of object for each Mindstorms device you intend to use.
Absolute motor rotation
The
most frequent job for MindControl will be to rotate the motors. You
need to use the rotate
function for that purpose:
rotate(motor1,
motor2, motor3, motor4, speed)
You can
use it for both NXT and the EV3, e.g.:
ev3device.rotate(90,
None, -360, speed=75)
The
motors correspond to their parameter position. This command rotates
the first motor 90° forward and the third motor one full turn
reverse (one full turn is 360°), both with a speed of 75. The speed
is simply percentage of full speed, which is default. If you do not
want to rotate a motor, pass a zero, or the Python object None.
Missing motors are filled up automatically. Note that, for the NXT
device, you can specify only the three motors, as it only has three
output ports.
If
you specify more than one motor with a single call of rotate,
it will rotate these motors sequentially, i.e. one after another by
default. However, if you are using the EV3, you have the option of
rotating them all at once, proportionally, by setting the simult
parameter as True.
For example:
ev3device.rotate(180,
-360, 720, 90, speed=80, simult=True)
This
command rotates all four motors simultaneously ― the first motor
makes half a turn forward while the second makes a full turn in
reverse, while the third makes full two turns, etc. The speed, if
specified, in this case always applies to the motor making the
largest rotation (greatest absolute angle), while the other speeds
are calculated proportionally for them all to finish at the exact
same moment, or at least, as closely to the same moment as possible.
Note that the simult
parameter is available only for the EV3.
Relative motor rotation
Apart
from the absolute rotations specified by rotate,
you can command relative rotations as well, in a similar fashion. The
basic idea is that all the motors begin in the origin, i.e. zero
position, and the relative rotation commands rotate the motors
relative to the origin and their starting position. This is done by
using the rotateto function:
rotateto(motor1,
motor2, motor3, motor4, speed)
It is
somewhat clearer with an example:
ev3device.rotateto(50,
-100)
ev3device.rotateto(-20,
-110)
ev3device.rotateto(0,
0)
So, the
first command rotates the first motor 50° forward and the second
100° in reverse, just like the absolute rotation command. However,
the second one rotates the first motor 70° reverse to "reach"
-20, and the second motor 10° backwards, to the -110° from the
origin.
The third
call rotates the first motor 20° forward and the second 110°
forward, in order to return the motor positions to those from the
start.
The
origin is always 0 for all the motors upon the start of the script.
Again, only three motors are available for the NXT, and on EV3 you
can additionally specify simult
parameter to be True
if you want all the movements done simultaneously.
Unsynchronized motor spin (EV3 only)
Occasionally
you will need the motors to just start spinning and proceed so until
some other instruction comes up. The spin
command does just that:
spin(speed1,
speed2, speed3, speed4)
It
lets you specify the speed (which may be negative as well for the
reverse direction) for each motor. Passing zero as a parameter stops
the motor, and None
does not change anything about it. For example:
ev3device.spin(-50,
100, None, 20)
This
command spins the first motor with half speed reverse, the second
with full speed forward, and the fourth with 20% speed forward. The
motors keep turning and the control is passed back to the Python
script, i.e. as said in the title, this function is not synchronized.
Typical
usage for unsynchronized spin is i.e. letting a car drive and then
checking some sensor value until it needs to stop. This is much more
convenient than turning the motor a little, checking the sensor,
turning some more, etc. which would cause a rather shaky movement.
You
can change the speeds of these motors anytime by reinvoking spin
with new parameters. To stop them all, you can use the stop
function:
ev3device.stop()
This
stops all the motors and is actually equivalent to ev3device.spin(0,
0, 0, 0).
Reading sensor value (EV3 only)
If
you are using EV3 sensors, you can use the sensor
function to read their measured values. Its syntax requires only one
parameter:
sensor(port)
This
parameter, ranging from 1 to 4, specifies the EV3 hardware input port
the sensor in question is connected to. It returns a number which is
the value measured by the sensor. With the standard sensors, it works
as follows (though it should work similarly for any other sensor as
well):
- Touch sensor returns either 0 if not pressed, and a nonzero value if pressed.
- Infrared sensor and ultrasonic sensor return the estimated distance to the target in front of them.
- Light sensor returns the percentage amount (0-100) of the reflected light, though more about it later.
Apart from
the EV3 sensors, you can use the NXT sensors connected to the EV3
device in the same way. An example:
ev3device.sensor(1)
returns 60 if the target is 60 cm ahead of the infrared sensor
connected to the EV3 in the port 1.
Reading light sensor value (EV3 only)
Since
the light sensor can work in several modes, it has the honour of
having its own function sensor_light.
Apart from the port number, it also requires an additional parameter
that specifies the mode it should be running in:
ev3device.sensor_light(2,
'COLOURS')
This
commands the light sensor connected to the EV3 port number 2 to
switch to the colour scanning mode and return the measured value.
There are
three modes that can be specified, either by a string as shown in the
above example, or with an integer:
- 0 or 'REFLECT': measure the reflected light. Returns the percentage of reflected light (0-100).
- 1 or 'AMBIENT': measure the ambient light level, and return it as a percentage (0-100).
- 2 or 'COLOURS': measure the colour the sensor points to and return its code along with its name. In this mode, the sensor always returns a tuple, i.e. a pair of a colour number and the name of the colour. The colour codes are: 0 - not available (nothing measured), 1 - black, 2 - blue, 3 - green, 4 - yellow, 5 - red, 6 - white and 7 - brown. I.e. if the sensor points at a blue surface, calling the function returns (2, 'BLUE').
Note that
the sensor needs to be rather close to the measured object in the
colour detection mode.
Playing tones (EV3 only)
You can
use the EV3 to play a tone with a specified frequency, duration and
volume:
tone(frequency,
volume, duration)
Frequency
is specified in Hertz (Hz), the volume in percentage (1-100), and the
duration in milliseconds, i.e. 1000 corresponds to a duration of one
second. An example:
ev3device.tone(440,
75, 500)
This
command plays an A note (440 Hz) at 75% loudness for 500 milliseconds
(half a second).
Starting a MindCtrl program (NXT only)
In
order to perform the functions correctly, the MindControl executable
needs to keep running on the NXT device. You can either do it
manually by choosing it in the appropriate menu on the device, or
with a script, using a start
function. It has no parameters:
nxtdevice.start()
This is
equivalent to you starting it manually. If you are going to use this
function, make sure it is started before any movements are sent to
that particular NXT device.
Disconnecting a device
Although
a very pragmatic programmer may argue that it may not be required, a
proper purits will always insist that the connection to the device be
terminated once it is not required anymore. You need to terminate it
by using the disconnect
function without parameters, for example:
ev3device.disconnect()
After
this command you cannot call (at least not successfully) any
functions on ev3device
unless you reinstance it under the same name. Keep in mind that you
need to disconnect every device independently!
Logging
By
default, all the instructions and the communication happening between
the computer and the Mindstorms devices is logged both to the console
and the log file mindctrl.log.
These are actually controlled by the module-level variables logtofile
and logtoconsole.
Therefore, if you want to disable logging to console, use:
mindctrl.logtoconsole=False
Likewise,
for the file:
mindctrl.logtofile=False
You
can, of course, set them back to True
anytime to resume logging to the console or the file.
Delays after movements
Sometimes
you may prefer to have brief delays after each motor movement, either
for communication or mechanical reasons. You can set this by using a
module-level variable betweendelay.
It specified the number of seconds to wait after each movement has
been done. It can be a floating-point number as well as an integer.
For example, should you desire to have a quarter of a second pause
after each moments, use:
mindctrl.betweendelay=250
This can
be readjusted anytime in your Python script.
Quick reference
If
you are an experienced Pythonist or just want a handy reference of
the stuff we just went through, this should help. Note that the bold
items apply only to EV3 devices and are not available for NXT.
Classes:
mindctrl.EV3(conn='COM8')
and mindctrl.NXT(conn='COM4')
Their
functions:
rotate(motor1,
motor2, motor3, motor4,
speed=100, simult=False)
- rotate given motor angles with specified speed, simultaneously if
simult is
True
rotateto(motor1,
motor2, motor3, motor4,
speed=100,
simult=False)
- rotate motors to their given relative position, with specified
speed and simultaneously if simult
is True
spin(speed1,
speed2, speed3, speed4)
- rotate each motor continuously (not synchronized) with a given
speed
stop()
- stop all motors
sensor(port)
- read the measured value from the sensor connected to the given port
sensor_light(port,
mode) - read the
measured value from the light sensor connected to the given port, in
mode 0 (reflection), 1 (ambient) or 2 (colour detector)
tone(frequency,
volume, duration) -
play a tone of a given frequency, with a given volume in percentage,
for a given duration in seconds
start()
- start the MindControl client program (NXT only)
disconnect()
- disconnect a device
MindControl
module variables:
mindctrl.logtofile
- a boolean value specifying whether actions are logged to a file
mindctrl.log
mindctrl.logtoconsole
- a boolean value specifying whether actions are logged to the
console
mindctrl.betweendelay
- floating number of seconds of a pause to occur after each motor
movement
Example programs
Finally, a
couple of sample scripts should clarify everything. You may want to
copy and paste them into your own projects after you have finished
the installation.
Rotating
first two motors on both devices 90° forward, sequentially:
import
mindctrl
ev3=mindctrl.EV3('COM8')
nxt=mindctrl.NXT('COM4')
ev3.rotate(90,
90)
nxt.rotate(90,
90)
ev3.disconnect()
nxt.disconnect()
Continuously
print the color under the light sensor connected to the EV3 port 1,
until the touch sensor at port 2 is pressed:
import
mindctrl
mindctrl.logtoconsole=False
ev3=mindctrl.EV3('COM8')
while
not ev3.sensor(2):
print(ev3.sensor_light(1,
2)[1])
ev3.disconnect()
Turn all
four motors with full speed until the infrared sensor connected to
input port 4 detects proximity closer than 30. Then stop all the
motors and sound a tone:
import
mindctrl
ev3=mindctrl.EV3('COM8')
ev3.spin(100,
100, 100, 100)
while
not ev3.sensor(4)<30: pass
ev3.stop()
ev3.tone(250,30,700)
ev3.disconnect()
Download
Click
on the links to download the:
Some additional notes
- If you are unsure with what Python 3.x version to start with, or it does not matter for you, go for the version 3.4 (MindControl was written in it). I can perform only very basic tests on platforms other than Windows, so ― apologies for any incompatibilites if some come up.
- As far as I was able to test, this module does not interfere with other communications that may be simultaneously happening on other serial (COM) ports.
- Depending on the Bluetooth device being used, sometimes the connection between it and the Mindstorms device is terminated if no instructions are passed for a while. However, the connection is reestablished automatically if new instructions are sent, so this does not cause any inconvenience but a very brief delay while reconnecting.
- NXT and EV3 are somewhat different in the way they handle rotation at given angles. NXT tends to rotate the desired angle, overdo a bit, then return, correct again, etc. until the output wheel or axle comes to rest at the desired position. The EV3 is much more controlled, without this shaky movement at its end position. Keep this in mind if using both devices while designing your machine. Use the EV3 where more accurate and homogenous movements are important.
- Try not to base your scripts around the instructions taking a consistent amount of time. Due to many layers and processes each of them starts, and their dependency on external factors, their duration may be unpredictable. If you need accurately timed processes (e.g. building a clock), I'd suggest you keep a master timer in your Python script and use it as a sole reference. Standard module time can help you a lot here.
- As long as they do not interfere with each other's motors or sensors, calling functions in a multithreaded program should work fine. However, each thread should use the same instance of the EV3 or NXT object, instead of each thread opening its own.
- Daisy-chaining the EV3 devices is not implemented as such ― you should connect to each EV3 device via Bluetooth independently instead.
- Letting motors perform very small movements (only a couple of degrees) sometimes do not produce any real results. Only after a few small movements are aggregated to something more (say, 10°), will the motor indeed rotate. If you need very small rotations, I'd rather suggest gearing the motor down.
Obligatory legal stuff
You can
freely use MindControl whatever way you wish, and distribute it as
much as you like, either standalone or as a component of your other
projects. However, you may not sell it as such, and please keep
Legoism.info credited. MindControl is provided as-is, I hold no
responsibility to any kind of damage you may have done to anything or
anyone by using MindControl or any of its derivatives. For any
unclarities, Apache License 2.0 applies.
No comments:
Post a Comment