Pages

MindControl

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:

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.

Download

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.