Skip to content

Commit

Permalink
update built docs
Browse files Browse the repository at this point in the history
  • Loading branch information
stenczelt committed Jul 8, 2024
1 parent c240d87 commit 2aee012
Show file tree
Hide file tree
Showing 42 changed files with 871 additions and 1,120 deletions.
2 changes: 1 addition & 1 deletion docs/.buildinfo
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Sphinx build info version 1
# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done.
config: ef9e2a8e97348ddd0f290a1da02d5450
config: 43fe11dab36c7389fa8da6f14306d505
tags: 645f666f9bcd5a90fca523b33c5a78b7
123 changes: 51 additions & 72 deletions docs/_sources/ev3-motors.rst.txt
Original file line number Diff line number Diff line change
@@ -1,114 +1,93 @@
EV3 Matlab Toolkit - Motors Introduction
EV3 Motors Introduction
===========================================

The EV3 Matlab toolkit provides a collection of functions to read sensors and control motors. Here, we will go through an introduction to motor control.
The EV3 python interface provides a collection of functions to read sensors and control motors. Here, we will go through an introduction to motor control.



Prerequisites
-------------

This part of the tutorial assumes that you connected Matlab to the EV3 via USB. Complete the :doc:`toolkit-setup` section before proceeding.
This part of the tutorial assumes that you connected to the EV3 in interactive mode, via SSH. Complete the :doc:`ev3-setup` section before proceeding.

Also attach one of the motors to Port A on the brick.
Attach one of the big motors to Port A on the brick, the other one to Port B, and the third small motor to Port C. The motors always need to be attached to the ports marked with letters.

Don't forget that when you want to start a new connection, you have to end the previous one first with ``delete(b)``.
.. image:: resources/monster.jpg


Initialise
----------

Example program
---------------

Open Matlab, and open the ``Example.m`` file from your ``<EV3>`` directory. (You can also download it from here: :download:`Example.m <https://github.com/gabor1/pkp-lego/raw/master/resources/Example.m>`.)

This example program demonstrates some of the functionality of the EV3 Matlab toolkit. The program will give you some basic interactive control over a motor connected to Port A.

Start the program, and play around with it using the instructions printed after start. Start the motors, and check the readings from the tachometer.

*Task:* What does the tachometer measure?

Let's inspect this program now.

Initialisation
~~~~~~~~~~~~~~

Once connected to the brick, launch the built-in python interpreter
::
brickrun -r -- pybricks-micropython

% init the connection
disp('Connecting ... ')
% brick usb init
b = Brick('ioType','usb');
% beep to indicate connection
b.beep();
Then import some python functions
::
from pybricks.hubs import EV3Brick
from pybricks.ev3devices import Motor
from pybricks.parameters import Port

As you know already, ``disp`` outputs a string to the Matlab console. We then initiate a connection via USB to the brick. Note that if you want to run this example through wireless, you could follow the instructions in :doc:`toolkit-setup`, and change this block of code to create a wireless connection. Finally, the successful connection is signalled by a beep.

**Important: **if you are trying to use a motor, the motor must also be initialised. This can be done using the command ``b.outputStart(0,Device.MotorA)``. Don't forget to do this - otherwise, the motor will do nothing!
Create a brick object:
::
ev3 = EV3Brick()

User input
~~~~~~~~~~
And then a motor object. Notice how you need to specify the port it is connected to.
::
m = Motor(Port.A)
If you specify a port with no motor connected, you get an error. Try it!

Now we can issue commands
::
m.run(200)

% user input
userIn = '';
...
while(~strcmp(userIn,'f'))
% get input
userIn = input('> Input(u,d,t,s,b,o,c,v,f): ','s');
...
end
This gets the motor going at a speed of 200 degrees (of angle) per second. We get the command prompt back, and can continue to do things, while the motor keeps running. In order to stop it, we can issue another command
::
m.stop()

Your input is monitored in a while loop. The condition for entering the while loop is that the user input (stored in the ``userIn`` variable) be not equal to the letter *f*. So next time you input *f*, the while loop won't be entered, and the program will finish by closing the connection to the brick.
Try all your motors. Run them simultaneously.

Speed control
~~~~~~~~~~~~~

::
--------------

% increase speed
if (userIn == 'u')
motorPower = motorPower+10;
if motorPower >= 100
motorPower = 100;
end
b.outputPower(0,Device.MotorA,motorPower)
disp(['> Motor Power: ' num2str(motorPower)]);
end
Play around with different motor speeds. At very low speed, the motor will stutter, because of friction. Find the smallest speed at which your motor turns reliably. If you want something to turn very slowly but steadily, you will need to build a **gearbox**, i.e. run the motor faster, but have it connect to a small gear then a large gear, so the latter turns more slowly.

When you input the letter *u*, the value of the variable ``motorPower`` is increased by 10. This is communicated to the brick by calling the ``outputPower`` function, specifying the motor port with ``Device.MotorA``, and supplying the power value in ``motorPower``. Note that the motor power is a percentage value in the range ``-100..100``, which is why it is capped at 100 in the code.
Many ways to stop
------------------

Note how the motor power is outputted to the console. First, the number is converted to a string by the ``num2str`` function. This is then combined into a vector of strings using the ``[string1 string2]`` notation. Finally, this vector of strings is passed to the ``disp`` function.

For those interested: the first argument of the ``outputPower`` function is the *usb chain level*. This only matters when you have several bricks chained together via USB cables. In that case you could send commands to other bricks by specifying the appropriate level. Since we will always work with a single brick, the usb chain level should always be 0.
There are several ways to stop a motor. You can just stop applying power to it entirely, or you can also break by counteractive the voltage generated as the motor turns a bit due to its angular momentum. This latter functionality is available via the function
::
m.brake()

*Task:* Implement an input method where you can specify the amount by which the motor power should change.
There is an even more controlled way, which actively holds the motor position at the place where it was when you issue the command. The motor remains under power, so e.g. it can hold a weight that is being lifted. You can attach an arm to the motor and weight the end with antoher piece to see the difference. Make sure that there is space for the arm to fully turn, otherwise you risk breaking pieces.
::
m.hold()

Hint: You might want to search the internet for a Matlab function that turns strings into integers.
When you use the `hold` function, you can see that the motor cannot be turned by hand. To release, use a `stop` command.

Tachometer
~~~~~~~~~~
-----------

As you can now tell, motor function is a bit more complicated than just "go" and "stop". In order to help us control the robot, it often helps to have an accurate picture of the state it is in, for example, just how far did the motors **actually** turn. We can access this information using the following function called
::
m.angle()

% output the tachometer
if (userIn == 'o')
tacho = b.outputGetCount(0,Device.MotorA);
disp(['> Tachometer: ' num2str(tacho)]);
end
which reports the angle (in degrees) through which the motor has turned. We can reset the value of this internal angle sensor at any time using
::
m.reset_angle()

When you input the letter *o*, the tachometer value of the motor is read an displayed. This value is the angle relative to the last time the tacho was cleared **in degrees**.
Writing programs
-----------------

*Task:* Compute the average of all tacho measurements triggered by the input *o*. Output the result when the program is finishing.
Now instead of using the brick interactively, write a simple program into the "main.py" file of a new project that moves a motor by a small amount. Download this program onto the brick and run it. In order to run downloaded programs, you will need to browse for them using the keys on the brick, and navigate to "main.py".

Hint: You might want to store each read value in a vector. Search the internet for how to add values to a vector in Matlab.
Further information
-------------------

Sensor reading
~~~~~~~~~~~~~~
The motors can do many more things than what is given on this page. VSCode has a link to the User Guide, which you can access from the Mindstorm <o> tab by selecting "Open user guide". Motors and sensors are discussed under the "ev3devices" section of the PYBRICKS MODULES heading.

This example program doesn't read from any sensors. For an intro to sensors, check out the :doc:`toolkit-sensors` page.



Motor Control
---------------
186 changes: 49 additions & 137 deletions docs/_sources/ev3-sensors.rst.txt
Original file line number Diff line number Diff line change
@@ -1,168 +1,80 @@
EV3 Matlab Toolkit - Sensors Introduction and Low-Pass Filters
EV3 Sensors Introduction and Low-Pass Filters
============================================================================

The EV3 Matlab toolkit provides a collection of functions to read sensors and control motors. Here, we will go through an example program to analyse and manipulate sensor data.

The EV3 python interface provides a collection of functions to read sensors and control motors. Here, we will go through an introduction to sensors.


Prerequisites
-------------

This part of the tutorial assumes that you connected Matlab to the EV3 via USB or wireless. Complete the :doc:`toolkit-setup` section before proceeding.

Also attach the touch sensor to Port 1 on your brick.

Don't forget that when you want to start a new connection, you have to end the previous one first with ``delete(b)``.



Example program
---------------

Download the :download:`plotSensor.m <https://github.com/gabor1/pkp-lego/raw/master/resources/plotSensor.m>` file and open it in Matlab.

This example program demonstrates how to read sensor values and display them on a graph.

The program is by default configured to show whether the touch sensor (attached to Port 1) is pressed or not. It also assumes that you connected your brick to the laptop via USB: change the brick initialisation line accordingly if you're using wireless.

Start the program, and start pressing the touch sensor.

*Task:* Is there any *latency* in the change appearing on the graph after you press the button?

Let's inspect this program now.



Configuration
~~~~~~~~~~~~~

The first few lines allow you to choose how to connect to the brick, which sensor type to monitor, and on which port::

brick = Brick('ioType','usb') % potentially change this to WiFi connection
layer = 0 % the usb chain layer (always 0 in this course)
no = Device.Port1 % the port number the sensor is attached to
mode = Device.Pushed % the sensor mode from types.html

You can use any of the 4 available ports as the sensor port.

You can use the following sensor modes:

* Touch sensor
This part of the tutorial assumes that you connected to the EV3 in interactive mode, via SSH. Complete the :doc:`ev3-setup` section before proceeding.

* ``Device.Pushed`` -- Returns 1 if the touch sensor is pushed otherwise 0.
* ``Device.Bumps`` -- Returns the number of touch sensor bumps since the last reset.
Attach a touch sensor to Port 1, the light sensor to Port 2, and the ultrasonic sensor to Port 3. The sensors always need to be attached to the ports marked with numbers.

* Color sensor

* ``Device.ColReflect`` -- Measures the reflected light intensity from 0% to 100%.
* ``Device.ColAmbient`` -- Measures the ambient light intensity from 0% to 100%.
* ``Device.ColColor`` -- Returns the detected color where '0' is no color, '1' is black, '2' is blue, '3' is green, '4' is yellow, '5' is red, '6' is white and '7' is brown.

* Ultrasonic sensor

* ``Device.USDistCM`` -- Measures the ultrasonic distance in centimeters (max 255 cm).
* ``Device.USDistIN`` -- Measures the ultrasonic distance in inches (max 100 inches).
* ``Device.USListen`` -- Returns 1 if an ultrasonic signal is detected otherwise 0.

* Gyro sensor

* ``Device.GyroAng`` -- Measures the rotation angle in degrees since the last reset.
* ``Device.GyroRate`` -- Measures the rotation rate in degrees per second.



Initialisation
~~~~~~~~~~~~~~
Initialise
----------

Once connected to the brick, launch the built-in python interpreter
::
brickrun -r -- pybricks-micropython

% start timing
tic;
% create figure
hfig = figure('name', 'EV3 Sensor');
% init the the data
t = 0;
x = 0;
hplot = plot(t,x);
% one read to set the mode
reading = brick.inputReadSI(layer, no, mode);
% set the title
name = brick.inputDeviceGetName(layer, no);
title(['Device name: ' name]);
% set the y label
name = brick.inputDeviceSymbol(layer, no);
ylabel(['Sensor value (' name(1 : end - 1) ')']);
% set the x label
xlabel('Time (s)');
% set the x axis
xlim([0 10]);

As you know already, ``tic`` starts a timer, the current value of which we can read by ``toc``. We then create a Matlab figure and set its labels and axes.



Main loop
~~~~~~~~~~

Then import some python functions
::

% wait until the figure is closed
while(findobj('name','EV3 Sensor') == 1)
...
end

The readings are collected in a while loop. The loop is exited when the object named ``'EV3 Sensor'`` is not available anymore: that is, when the figure has been closed.
from pybricks.hubs import EV3Brick
from pybricks.ev3devices import (
ColorSensor,
TouchSensor,
UltrasonicSensor,
)
from pybricks.tools import wait



Readings
~~~~~~~~~~~~~

Create a brick object
::
ev3 = EV3Brick()

% get the reading
reading = brick.inputReadSI(layer, no, mode);
t = [t toc];
x = [x reading];
set(hplot, 'Xdata', t)
set(hplot, 'Ydata', x)
drawnow
% reset after 10 seconds
if (toc > 10)
% reset
t = 0;
x = x(end);
tic
end

You get your reading by calling ``inputReadSI`` on the brick. The reading is then collected into a vector called ``x``. The figure is updated with the current time and the latest reading, and then redrawn.
And then a sensor object
::
s = TouchSensor(Port.S1)

Finally, the figure is reset every 10 seconds by resetting the ``t`` and ``x`` vectors and restarting the timer.
Measuring sensor values
------------------------

*Task:* Currently the figure is reset every 10 seconds. Make this a configurable parameter at the top of the program.
In order to test the sensor, we don't just want to read it once, we need to read it continuously in a loop, so that when something happens, the change in state is picked up. However, issuing many read commands in rapid succession would overwhelm the brick, so we need to wait a little time between each successive attempt to read the sensor, like so
::
while True:
print(s.pressed())
wait(200)

The `wait` command as above will stop the execution of the program for 200 milliseconds before allowing it to go around the loop again. Run the above program, and then press the touch sensor. To break out of the loop, you can press Ctrl-C. Be careful with such "infinite loops", once you ar not in interactive mode, it is hard to get out of them!

Now try the other sensors. The light sensor is initialised using
::
s.ColorSensor(Port.S2)

Other sensor values
--------------------
It is multifunctional, it can detect the ambient level of light, the brightness of a reflecting surface, and the colour of a surface. These are accessed by the following challenges
::
s.ambient()
s.reflection()
s.color()

You can easily change the program to plot colour, gyro, and ultrasonic sensor readings too.
Experiment by interacting with the sensor and note the values you see in each case. What are the minimum and maximum values returned?

*Task:* Attach the colour sensor and try the ``Device.ColReflect`` and ``Device.ColAmbient`` modes. When would you use which?
The ultrasonic sensor measures distances. It is initialised using
::
s.UltrasonicSensor(Port.S3)

*Task:* Attach the gyro sensor and try the ``Device.GyroAng`` and ``Device.GyroRate`` modes. What does each measure?
and the distance to an object is accessed using
::
s.distance()

*Task:* Attach the ultrasonic sensor and try the ``Device.USDistCM`` mode. How far does it seem to measure well? How precise does it seem to be?
which returns the value in mm. Experiment with measuring distances. There is a minimum and maximum distance between which the sensor is active. At large distances, the size and shape of the object in front of the sensor also matters.

A Sensory Motor Exercise
------------------------

Try to write a program that turns a motor while a button is pressed and stops the motor when the button is released.

Low-pass filters
------------------
Expand All @@ -182,6 +94,6 @@ In other words, the first value is take as it is, but all following values are w

*Task:* What does it mean if the smoothing factor is 0 or 1?

*Task:* Adapt your plotSensor code to send the signal through a low-pass filter. Make the smoothing factor easily customisable. Choose a sensor and observe the effects with high and low smoothing factors.
*Task:* Adapt your code to send the sensor signal through a low-pass filter. Make the smoothing factor easily customisable. Choose a sensor and observe the effects with high and low smoothing factors.

For more details on low-pass filters, check out the `Low-pass filter Wikipedia article <http://en.wikipedia.org/wiki/Low-pass_filter>`_ and the `Exponentially-Weighted Moving Average Wikipedia article <http://en.wikipedia.org/wiki/Exponential_smoothing>`_.
Loading

0 comments on commit 2aee012

Please sign in to comment.