2 Creating your own robot programs

Having started to learn how to control the movement of the simulated robot in general in the previous notebook, let’s see if you can get it to perform some more specific actions, such as moving around a square course.

Work through the instructions carefully and slowly. It is important that you complete each step successfully before moving on to the next.

2.1 Writing your own programs – getting into the ‘mind’ of the robot

When trying to understand, or write your own, robot programs, it can be helpful to ‘play robot’. This means that you put yourself in the place of the robot and act out (or at least imagine) what you or the robot would do when executing each statement of the program. If you have the help of a friend, get them to read out the program statements to you one at a time.

2.2 Creating and editing your own programs

As we’re going to be writing RoboLab programs, we need to set up the lab and load the nbev3devsim simulator in the normal way:

from nbev3devsim.load_nbev3devwidget import roboSim, eds

%load_ext nbev3devsim

If you download and run the following program in the simulator, then you will find that it drives the robot forward a short way and then turns on the spot for more than a right angle (ninety degrees, or a quarter of one complete rotation).

The -x, -y and -a switches in the magic used to download the program to the simulator specify the original location and orientation of the robot in the simulator.

The -p switch enables pen down mode, to leave a trace of where the robot has moved.

The -C switch clears any previously made traces.

%%sim_magic_preloaded -x 200 -y 700 -a 0 -p -C

# Set the left and right motors
# in a forward direction
# and run for 1 rotation
tank_drive.on_for_rotations(SpeedPercent(50),
                            SpeedPercent(50), 1)


# Set the robot to turn on the spot
# and run for 2 rotations *of the wheels*
tank_turn.on_for_rotations(100,
                           SpeedPercent(75), 2)

If you want to be able to refer to a code cell from earlier in the notebook, you may find it useful to pop the cell out into a floating widget by clicking on the cell to select it and then clicking the >_ toolbar button.

2.2.1 Activity – Copying and modifying a program

We can edit a code cell directly to add additional lines to it, or we may want to retain our original code (for reference) and create a new program based on an earlier one.

We can grab a copy of a code cell by:

  • clicking on it to select it

  • clicking the copy selected cells button in the toolbar

  • clicking the paste cells below toolbar button to place a copy of the cell in a new cell

  • select the new cell and use the arrow keys to move it to a location of your choosing.

Select the code cell above by clicking on it, use the toolbar button to copy it, single-click on this Markdown cell to provide the current location, then click on the toolbar paste button to copy the code cell below this one.

Modify the program in your newly created cell so that the robot turns through a right angle (or as close as you can get it without spending too much time!).

To what extent does the turn speed setting affect the controllability of the robot? If you change the speed, do you need to modify the turn time as well?

Test your code by running the code cell to download the code to the simulator, and then run it in the simulator.

You may find that using the pen trace helps you see how far the robot has turned.

%%sim_magic_preloaded -x 200 -y 700 -a 0 -p -C

# Set the left and right motors
# in a forward direction
# and run for 1 rotation
tank_drive.on_for_rotations(SpeedPercent(50),
                            SpeedPercent(50), 1)


# Set the robot to turn on the spot
# and run for 1 rotation *of the wheels*
tank_turn.on_for_rotations(-100,
                           SpeedPercent(75), 2)

Record your own observations here about the extent to which you could accurately control the robot’s turning behaviour, for example, at different turn speeds.

Example solution

Click the arrow in the sidebar or run this cell to reveal the settings I used.

%%sim_magic_preloaded -x 200 -y 800 -a 0 -p -C

# Set the left and right motors in a forward direction
# and run for 1 rotation
tank_drive.on_for_rotations(SpeedPercent(50),
                            SpeedPercent(50), 1)


# Set the robot to turn on the spot
# and run for 1 rotation *of the wheels*
tank_turn.on_for_rotations(-100,
                           SpeedPercent(15), 1.6)

With these settings, on my computer, I found I could turn the robot through about ninety degrees by setting the number of wheel rotations on the turn step to between 1.5 and 1.7 for a speed of 75%.

The slower the robot moved, the more controllable it appeared to be. The angle of turn required was independent of the turn speed.

It was virtually impossible to get it to turn exactly 90 degrees: the simulator does not appear to work at such a fine level of detail.

2.2.2 Copying code using notebook keyboard shortcut commands

You can also use the Esc-C keyboard shortcut to copy a selected cell and Esc-V to paste a copied cell immediately below the currently selected cell.

Alternatively, you can highlight and select code within a code cell and then use the keyboard copy and paste commands to copy the code from the original cell, create a new code cell, then paste the copied code into the new code cell.

2.2.3 Deleting cells

Sometimes, you may find you want to delete a code or Markdown cell.

To delete a cell, click on it to select it and then click the scissors (cut selected cells) toolbar button.

2.2.4 Deleting and moving lines of code in a code cell

To delete one or more particular lines of code in a code cell, highlight just that line or lines of code then use the keyboard delete key to delete them.

To move one or more lines of code in a code cell, first highlight them to select them. Next, cut them using your normal keyboard cut command (for example Ctrl-X or Cmd-X), place the cursor where you want them to go, then paste them using your normal keyboard paste command (for example Ctrl-V or Cmd-V).

2.3 Working with comments

In a computer program, a comment refers to a line of explanatory text that is not executed when the program runs. As such, it does not have to conform to the syntax of the programming language once it has been identified as a comment.

In the following activities, you may notice that the code cells are not prefixed using simulator magic commands.

Instead, the Python code contained in the cells is executed in the full Python environment associated with the notebook kernel when the code cell is run.

In a Python program, a comment is identified by prefixing a line of text or a line of code with a # (a ‘hash’ or, in US English, a ‘pound’) character.

# A Python comment line is prefixed by a # (hash) character

If you run the following code cell, the contents of which should look like this:

# This is a comment

print('Hello')

#print("Commented out code lines are not executed.") <!-- JD: this had been marked up for uncommenting in the .ipynb file, but I think that would be incorrect. -->

you will see that the first commented-out line is ignored, the second line prints out the word Hello, and the third line is also ignored.

# This is a comment

print('Hello')

#print("Commented out code lines are not executed.")

Uncommenting a line refers to removing the initial character that identifies the line as a comment line.

2.3.1 Activity – Uncommenting a line of code

In the following code cell, uncomment the second print statement in the code cell above by deleting the # character in front of it, and then run the code cell. You should see that both messages are displayed.

# This is a comment

print('Hello')

#print("Commented out code lines are not executed.")

If a comment line would work in uncommented form as a line of code, rather than being a more general line of human-readable text, then it will often be referred to as being ‘commented out’.

If you hear the phrase ‘comment out that line’, it thus refers to leaving the line in the program, but prefixing it with the comment character so that the line is not executed when the program is run.

2.3.2 Activity – Commenting out a line of code

Comment out the first print statement in the code cell below by prefixing it with the # comment character. Run the cell. This time you should only see the second print message being displayed.

# This is a comment

print('Hello')

#print("Commented out code lines are not executed.")

2.3.3 Activity – Uncommenting an invalid line of code

Uncomment the first (text comment) line in the code cell below and run the cell. This time, an error will be thrown because the first line is not a valid line of Python code.

# This is a comment

print('Hello')

#print("Commented out code lines are not executed.")

When developing your own programs, or reading other people’s code, you may find broken lines of code have been left in a program, albeit commented out, often with additional commments alongside, as a note the programmer has left to themselves.

Using print statements to display variable values as a program executes, and then commenting them out when you want to use the program in anger without debugging statements on display, provides a simple tried-and-tested technique for quickly debugging programs.

Professional developers use a wide range of tools to help them debug their programs as they create them. These include variable inspectors and breakpoint tools that allow the program execution to be stopped and inspected at various points. Using such tools is beyond the scope of this module.

2.4 Using comments

Comments are often used to document particular lines of code to clarify or explain their role in a program. It is good practice to keep comments as meaningful as possible.

2.4.1 Block comments

The phrase block comment, or the plural comments, is taken to refer to continuous explanatory comment text that extends over several lines.

# Multiple consecutive lines of comments are
# often referred to as 'block comments'.
#
# The syntax of some programming languages 
# allows block comments to be defined simply by
# using special character sequences at the start
# and end of the comment block.
#
# In Python, one widely used convention is 
# to delimit each line as its own comment line.

'''
Multi-line text strings can also be used as a way
of 'escaping' lines of text so that it is not executed. 

Such strings are identified by using three single
(or double) quotes at the start of the text string,
and three matching single (or double) quotes at
the end of the string.

If a text string appears as the last item in a
Jupyter notebook code cell, it will be returned 
and displayed as the cell output if the cell is run.

Run this code cell to see what I mean...
'''

2.4.2 Using comments in other ways

As well as being used to annotate a program with helpful information, comments can also be used to provide visual clues to the division of lines or chunks of code. For example, you might use a comment to identify separate sections of a program from each other. This technique can be used to make your programs easier to read.

2.5 Using notebook Markdown cells to narrate program code and program development

As well as commenting code within a code cell, you can also make notes in a Jupyter notebook’s Markdown cells. For example, you might use Markdown cells:

  • to describe what you aim to achieve with the code in a following code cell

  • to describe the output produced by running a previous code cell

  • to reflect on the behaviour of code executed in a previous code cell; for example, if it does not work as expected.

Remember that the notebooks are yours, so use them for making your own notes as you work through them.

You may notice that in some activities, cells are distinguished by a yellow background. These cells are prompts for you to make your own notes.

For example, you may wish to make some notes in the following cell about why it might be useful to make notes in the notebooks…

Double-click this Markdown cell to edit it. ‘Run’ the cell to render the Markdown as styled HTML text.

Add some of your own thoughts here about why it might be useful to add your own notes to the Jupyter notebooks.

If you create your own cells in the notebook, either code cells or Markdown cells, you may want to highlight them with the yellow background so that you can easily find them again. You can do this as follows. Create a new cell by clicking on the + button in the notebook toolbar. To change it from the default code cell to a Markdown cell, press Esc-M while the cell is selected or use the cell type drop-down list in the notebook toolbar to change the cell type to Markdown. Add some content to it and then, with the cell selected, click on the person (Toggle cell student) button in the toolbar to toggle the yellow background on and off.

2.6 Saving a RoboLab program

When writing notebooks it is good practice to save them periodically.

Notebooks are typically saved with the .ipynb filetype suffix. The name of the file is taken from the name set in the notebook header at the very top of the page. (You can change the notebook name by clicking on the notebook name, which will pop up a Rename Notebook dialogue box:

When you make changes to a notebook, either by editing a code or Markdown cell, or by running one or more code cells, you may notice that an (unsaved changes) message appears at the top of the notebook:

The notebook will be automatically saved every minute or two. When it is autosaved, the status will change:

You can also do a ‘hard save’ of your notebook by clicking on the Save and Checkpoint notebook toolbar button or, from the File menu by selecting Save and Checkpoint. You can also save the notebook to a new file name from the File > Save as… menu option. The File > Make a Copy option will create a copy of the current notebook.

Checkpoints are useful because they not only autosave the notebook to the current file, but they also save a hidden copy of the notebook in a checkpoints directory. The File > Revert to Checkpoint menu option allows you to reload the previously checkpointed notebook. This is handy if you have made some changes to your notebook and those changes have been autosaved, but you find you want to revert to the previous version of the notebook where you made a conscious decision to save and checkpoint it.

It’s not uncommon to see that you have been working on a notebook for quite some time since consciously saving it!

2.7 Creating your own program from scratch

In the first part of this notebook, you learned how to create a simple program to turn the robot through ninety degrees, before exploring in a little more detail how to use some of the notebook’s editing features to create new programs based on previous ones. You also saw how code comments could be used to help structure a program and make it more readable.

In this section, you will have an opportunity to try to put all those pieces together to create your own program, from scratch.

2.7.1 Activity – Drawing a square

Make a copy of the earlier code cell in which you programmed the robot to draw a straight line then turn through a right angle and paste it below:


In the new code cell, copy the lines of code to drive the robot forward and turn through ninety degrees, and paste the copied lines of code below the original lines in the same code cell.

What do you think your program will do now?

Run the program in the simulator to find out.

Note that copying and pasting code is a tried-and-trusted technique. Do not be averse to reusing any code your have developed previously and that behaves as you expect.

My observations

Click on the arrow in the sidebar or run this cell to reveal my observations.

When I ran my version of the program (see below), the robot drew two sides of a square as an L shape and had made the turn ready to start a third edge.

%%sim_magic_preloaded -x 500 -y 500 -a 0 -p -C

# Draw a square


# First edge and turn

# Set the left and right motors in a forward direction
# and run for 1 rotation
tank_drive.on_for_rotations(SpeedPercent(50), SpeedPercent(50), 1)

# Set the robot to turn on the spot
# and run for 1 rotation *of the wheels*
tank_turn.on_for_rotations(-100, SpeedPercent(75), 1.68)



# Second edge and turn

# Set the left and right motors in a forward direction
# and run for 1 rotation
tank_drive.on_for_rotations(SpeedPercent(50), SpeedPercent(50), 1)


# Set the robot to turn on the spot
# and run for 1 rotation *of the wheels*
tank_turn.on_for_rotations(-100, SpeedPercent(75), 1.68)

2.8 Building on a previously developed computer program

Trying to build a complex program in one go is a risky strategy. There are many factors that can prevent a program from working correctly, including:

  • syntax errors: characters in the wrong place, or incorrectly typed package or variable names

  • logical errors: for example, if your program uses logical tests, these may not be tested as intended, or the wider logic of the program may be incorrect.

For this reason, it often makes sense to start small and then test increasingly elaborate versions of your program by incrementally adding a line or two of code at a time, checking that things are still working as you’d expect as you add additional steps to your program.

2.8.1 Activity – Extending your program further

Building on the program you developed in the previous activity, extend your program further so that the robot draws all four sides of a square.

(Either modify your program in an earlier code cell, or make a copy of the earlier code cell, paste it here, and work in the newly copied cell: your notebook, your rules…!)

You may find it convenient to make some notes first about how the new goal (to draw four sides of the square) relates to the previous goal, and what you need to change about your original program so that it performs the new task.

Make your notes here.

When you have created your extended/modified program, download it to the simulator and run it to test that it works.

Run your program several times, with the pen down and without clearing the trace between runs. Note how any errors compound as you run the program multiple times.

You may notice that it is easier to ‘tune’ the value of the rotation count required to turn the robot through ninety degrees when the robot turns multiple times, because it is easier to see where, and by how much, the turn undershoots or overshoots the right angle.

Increasing the length of the side of the square can also help reveal when the angle is slightly off the right angle.

Also note that you may not be able to get the robot to create a perfect square. (Don’t spend too long trying to achieve this!) You will learn in a later notebook how to get the robot to turn through a right angle in a more accurate and controllable way.

Reflection: Having written your program, what do you notice about it? If you want to change the size of the square’s side length, for example, or modify the angle turned by the robot, how easy is it to modify your program?

Record your thoughts and observations about how easy your program is to extend and maintain here.

My observations

Click the arrow in the sidebar or run this cell to reveal my observations.

Here is my program. On multiple runs, it seems to overshoot the turn slightly.

%%sim_magic_preloaded

# Draw a square


# First edge and turn

# Set the left and right motors in a forward direction
# and run for 1 rotation
tank_drive.on_for_rotations(SpeedPercent(50), SpeedPercent(50), 1)

# Set the robot to turn on the spot
# and run for 1 rotation *of the wheels*
tank_turn.on_for_rotations(-100, SpeedPercent(75), 0.846)



# Second edge and turn

# Set the left and right motors in a forward direction
# and run for 1 rotation
tank_drive.on_for_rotations(SpeedPercent(50), SpeedPercent(50), 1)


# Set the robot to turn on the spot
# and run for 1 rotation *of the wheels*
tank_turn.on_for_rotations(-100, SpeedPercent(75), 0.846)


# Third edge and turn

# Set the left and right motors in a forward direction
# and run for 1 rotation
tank_drive.on_for_rotations(SpeedPercent(50), SpeedPercent(50), 1)

# Set the robot to turn on the spot
# and run for 1 rotation *of the wheels*
tank_turn.on_for_rotations(-100, SpeedPercent(75), 0.846)



# Fourth edge and turn

# Set the left and right motors in a forward direction
# and run for 1 rotation
tank_drive.on_for_rotations(SpeedPercent(50), SpeedPercent(50), 1)


# Set the robot to turn on the spot
# and run for 1 rotation *of the wheels*
tank_turn.on_for_rotations(-100, SpeedPercent(75), 0.846)

In terms of the program’s design, I used comments to split up the code for each side, but there does seem to be a lot of repetition. If I want to change the side length or the angle turned, I need to make changes in lots of different places. If I had to draw a regular shape with twenty or thirty sides, things would start to get really unwieldy!

2.9 Summary

In this notebook, you have learned how to make use of notebooks to create your own simulated robot control programs and run them in the robot simulator.

You have seen how sequential programs can be used to instruct a robot to perform a series of tasks, one after the other.

For simple, rote tasks, that may already be quite useful. But in the approach we have taken so far, there does appear to be a lot of repetition in the code.

Surely there’s a better way…?