Lecture#

Course Overview#

What You Will Learn

  • Specifically designed for students who want to learn robotics with direct applications.

  • Focus on Python programming and its applications with mobile robots using ROS 2.

  • Students will perform small hands-on exercises in class to gain a deeper understanding of how the application of Python to ROS can be used in real-world challenges.

  • The final project will be to control one or multiple mobile robots in a Gazebo environment using Nav2 and behavior trees.

Tip

Key Questions: Do you need robotics knowledge for this course? Do you need prior programming knowledge to take this course?

Course Structure#

📖 Before Class
  • Get the lecture materials from Canvas and/or Github.

  • Complete pre-work activities.

🏫 During Class
  • Questions and discussions on the previous lecture(s).

  • Quiz (if any).

  • Lecture — attend class with your laptop and required software installed.

  • Participation is very important.

📝 After Class
  • Review the slides.

  • Re-run the code provided to you.

  • Start working on the next assignment (if any).

Grading Structure#

Component

Weight

Individual Assignments (2)

30%

Quizzes (5)

15%

Participation/Engagement

5%

Team Projects (2)

30%

Final Project

20%

Total

100%

Warning

No Final Exam

⏰ Late Policy
  • 10% deduction per calendar day late

  • Maximum 30% reduction (3 days)

  • Work submitted more than 3 days late earns zero

🤖 AI Policy
  • AI tools NOT permitted for individual assignments

  • AI tools permitted for team projects and final project with documentation

Course Schedule#

Wk

Topic

Deliverable

1

Course Introduction

2

Python Fundamentals — Part I

3

Python Fundamentals — Part II

Assignment 1 Handed Out

4

Python Functions

Quiz 1

5

Object-Oriented Programming — Part I

Assignment 1 Due, Assignment 2 Handed Out

6

Object-Oriented Programming — Part II

Quiz 2

7

ROS 2 Foundations

Assignment 2 Due

8

Spring Break

9

ROS 2 Executors and Custom Messages

Quiz 3, Project 1 Handed Out

10

ROS 2 Services and Parameters

11

Transforms, Coordinate Frames, and Sensor Integration

Project 1 Due, Project 2 Handed Out

12

ROS 2 Actions and Lifecycle Nodes

Quiz 4

13

Navigation Stack Foundations (Nav2)

Project 2 Due, Final Project Handed Out

14

Behavior Trees

Quiz 5

15

Final Project Work Session and Status Check

Final Project Due (May 08)

Quiz Policy#

  • Quizzes are taken during class hours between 7:00pm and 7:20pm.

  • If you are not a remote/online student and you cannot come to class:

    • Do not take the quiz.

    • Take the quiz another day with a 10% penalty.

    • No penalty if you are excused (doctor’s note, supervisor’s note, etc).

  • No proctor needed.

  • Closed-notes quizzes only (no slides and no software to test code).

Note

Online/Remote Students: Email the instructor with day and time you plan to take quizzes.

Peer Reviews#

  • After team projects and the final project are submitted, you will give your teammates a score from 1-10 indicating their productive involvement.

    • You cannot rate yourself.

    • You get 1 pt by just emailing your peer reviews for your teammates.

  • 60% of your overall score will be based on your assignment/project grade and 40% will be based on your peer reviews from your group.

📊 Peer Review Example
  • Your group got 30/30 pts on an assignment/project.

  • Your average peer review from your teammates is 4/10 pts.

  • You emailed your peer review for your teammates: Your average peer review becomes 5/10 pts (50%).

Grade breakdown:

  • Assignment portion: 60% of 30 = 18 pts

  • Peer review portion: 40% of 30 = 12 pts × 50% = 6 pts

  • Your final grade: 18 + 6 = 24/30 pts

Final Project#

Autonomous Mobile Robot Navigation

The final project involves developing a ROS 2 package that enables a mobile robot to autonomously navigate through a simulated environment in Gazebo Harmonic.

The project integrates:

  • Sensor processing

  • Coordinate transforms

  • Nav2 navigation

  • Application-level behavior trees using py_trees

⚙️ What Nav2 Handles (You Configure)
  • Path planning and trajectory following

  • Obstacle avoidance via costmaps (subscribes to sensor topics directly)

  • Recovery behaviors (spin, backup, wait)

🌳 What You Build with py_trees
  • Application-level behavior tree that coordinates high-level tasks:

    • Patrol a sequence of waypoints (calls /navigate_to_pose action)

    • React to events (e.g., object detected → investigate)

    • Handle task priorities (high-priority task interrupts patrol)

  • Your behavior tree calls Nav2 action servers — it does not replace Nav2’s internal navigation logic.

Note

Week 15 is reserved as a work session and status check.

Software & Hardware#

Operating System#

Ubuntu 24.04 LTS (Noble Numbat)

Use this course as an opportunity to learn Linux. You should at least know the following shell commands (terminal):

ls, pwd, cd, mv, mkdir, rm, source, touch, man, and grep.

Minimum Hardware#

CPU

Intel i5 or equivalent, 4+ cores

RAM

8 GB minimum, 16 GB recommended

Storage

50 GB free space

Note

Docker containers will be provided to ensure a consistent development environment across all platforms.

Development Tools#

Software

Description

Download/Install

Visual Studio Code

Light, cross-platform IDE

Visual Studio Code

Docker Desktop

Containerized development

Docker Desktop

ROS 2 Jazzy

Robot Operating System

ROS Jazzy

Gazebo Harmonic

Simulation environment

Gazebo Harmonic

Important

To Do: Install Visual Studio Code and Docker Desktop before the in-class exercise.

Environment Setup#

This section walks through configuring Visual Studio Code and Docker for Python development.

1️⃣ Install VS Code

Download and install from Visual Studio Code. Available for Windows, macOS, and Linux.

2️⃣ Install Docker

Download and install Docker Desktop. Verify installation with docker --version.

3️⃣ Install Extensions

Install the required VS Code extensions (see below).

Visual Studio Code#

Download & Install

  • Download from Visual Studio Code

  • Available for Windows, macOS, Linux

  • Lightweight, extensible, free

Required Extensions

Docker Installation#

Installing Docker Desktop

  1. Download and install Docker Desktop.

  2. Verify your installation:

    docker --version
    
  3. Docker allows us to run isolated containers with all dependencies pre-installed.

  4. We will use Dev Containers in VS Code to develop inside Docker containers.

Note

Linux Users: You may need to add your user to the docker group:

sudo usermod -aG docker $USER

Log out and log back in for changes to take effect.

Creating a Workspace#

  1. Create a workspace where you will host all Python code for this course.

    • Example: ~/enpm605/py_ws

  2. Open a terminal and run:

    cd <path to workspace>
    code .
    
  3. By starting VS Code in a folder, that folder becomes your “workspace”.

  4. VS Code stores workspace-specific settings in .vscode/settings.json.

Tip

Any settings.json file in a nested folder takes precedence over other settings.json files.

Running Python in VS Code#

Selecting the Python Interpreter#

In order to run Python code and get Python IntelliSense, you must tell VS Code which interpreter to use.

  1. Open Command Palette with Ctrl+Shift+P

  2. Start typing Python: Select Interpreter

  3. Select a Python 3 interpreter.

Creating a Python File#

Create the file running_demo.py inside the lecture1 folder and add the following code:

1def greet(name):
2    print("Hello", name)
3
4greet('Bob')

Execution Methods#

Click the Run Python File button (top-right triangle icon).

Right-click on the Python file and select Run Python File in Terminal.

Open terminal with Ctrl + j and run:

python3 running_demo.py
  1. Ctrl+Shift+P → Type REPL → Select Python: Start Native Python REPL

  2. Select code and press Shift+Enter to execute in REPL.

Debugging Python Code#

🔧 Setup
  1. Set a breakpoint: Click in the gutter or place your cursor on a line and press F9

  2. Expand top-right triangle and select Debug Python File or press F5

  3. Since this is your first time debugging this file, a configuration menu will open: Select Python Debugger then Python File.

🔍 Inspect
  1. Use the debug toolbar to step through code, inspect variables, and evaluate expressions.

  2. You can also work with variables in the Debug Console using the > field at the bottom.

Python Linters#

What is a Linter?#

Definition

A linter analyzes your code for potential errors, style violations, and code smells (“Refactoring: Improving the Design of Existing Code” — M. Fowler et al.). Linters help enforce coding standards and catch bugs before runtime. The name comes from the original lint tool for C (1978).

Ruff — The Modern Python Linter#

Why Ruff?

  • Blazingly fast — Written in Rust, runs in milliseconds.

  • All-in-one — Replaces Flake8, Black, isort, and many plugins.

  • Auto-fix — Can automatically fix many issues with --fix.

  • Drop-in replacement — Compatible with existing Flake8/Black configurations.

  • Adopted by major projects — FastAPI, pandas, Apache Airflow, pydantic.

Ruff in Action#

 1import os
 2import sys
 3import math
 4
 5def calculate_area(radius):
 6    pi = 3.14159  # Magic number
 7    area=pi*radius**2  # No spaces
 8    return area
 9
10x = 10
11if x == True:  # Compare to True
12    print("yes")
$ ruff check example.py
example.py:1:8: F401 `os` imported but unused
example.py:3:8: F401 `math` imported but unused
example.py:6:10: PLR2004 Magic value used in comparison
example.py:7:9: E225 Missing whitespace around operator
example.py:11:6: E712 Comparison to `True` should be `if x:`
Found 5 errors.
$ ruff check --fix example.py  # Removes unused imports automatically
Found 5 errors (2 fixed, 3 remaining).

Ruff in VS Code#

Install the Ruff extension from the VS Code marketplace and configure in .vscode/settings.json:

{
    "editor.formatOnSave": true,
    "editor.defaultFormatter": "charliermarsh.ruff",
    "editor.codeActionsOnSave": {
        "source.fixAll.ruff": "explicit",
        "source.organizeImports.ruff": "explicit"
    },
    "ruff.lineLength": 100
}

What This Does:

  • Format on save — Automatically formats code when you save.

  • Fix all — Applies safe auto-fixes on save.

  • Organize imports — Sorts and removes unused imports on save.

Environment Verification#

Verify Your Setup#

  1. Open VS Code in your workspace.

  2. Create a file called verify_setup.py.

  3. Add the following code:

 1import sys
 2
 3def main():
 4    print("=" * 40)
 5    print("Environment Verification")
 6    print("=" * 40)
 7    print(f"Python Version: {sys.version}")
 8    print("Setup successful!")
 9    print("=" * 40)
10
11if __name__ == "__main__":
12    main()
  1. Run the file using any of the methods discussed.

  2. Verify you see the Python version and success message.

Expected Output#

$ python3 verify_setup.py

========================================
Environment Verification
========================================
Python Version: 3.14.2 (tags/v3.14.2:df79316, Dec  5 2025, 17:18:21) [MSC v.1944 64 bit (AMD64)]
Setup successful!
========================================

Tip

Troubleshooting: If you encounter errors, verify: (1) Python is installed, (2) VS Code Python extension is installed, (3) Correct interpreter is selected.

Python Execution Pipeline#

Understanding how Python code is executed from source to output.

Python is an Interpreted Language#

Pipeline Stages

  • Source code — Human-readable code written in Python syntax (.py files).

  • Lexer (Tokenizer) — Breaks source code into tokens (keywords, identifiers, operators, literals).

  • Parser — Analyzes token sequence, validates syntax, and builds a parse tree.

  • Abstract Syntax Tree (AST) — Simplified tree representation focusing on semantic meaning.

  • Bytecode Compiler — Converts AST to low-level instructions (.pyc files in __pycache__).

    • Bytecode is generated in memory for executed scripts.

    • For imported modules, bytecode is cached in __pycache__ to speed up future imports.

  • Interpreter Loop — Executes bytecode instructions via a fetch-decode-execute cycle.

Note

Pipeline Flow: Source Code → Lexer → Parser → AST → Bytecode Compiler → Interpreter Loop → Output

Compilation vs. Runtime Phases#

🔨 Compilation Phase
  • Happens once when module is first imported or run.

  • Lexer → Parser → AST → Bytecode

  • Syntax errors caught here (before any code runs).

  • Recompilation only if source file changes.

▶️ Runtime Phase
  • Happens every time the code executes.

  • Interpreter loop reads and executes bytecode.

  • Runtime errors occur here (NameError, TypeError, etc.).

  • Stack-based execution model.

  • Manages memory and garbage collection.

Compiled vs. Interpreted Languages#

⚙️ Compiled Languages

A compiler translates source code directly into native machine code. The CPU executes the binary directly — no intermediary.

  • Advantage: Faster execution; optimizations applied at compile time.

  • Disadvantage: Platform-specific binaries; longer compile times.

  • Examples: C, C++, Rust, Go

Source Compiler Machine Code CPU

🐍 Interpreted Languages

Source code is compiled to intermediate bytecode. An interpreter executes bytecode instruction by instruction.

  • Advantage: Portable; same bytecode runs on any platform with the interpreter.

  • Disadvantage: Slower; interpreter overhead on each instruction.

  • Examples: Python, JavaScript, Ruby

Source Compiler Bytecode Interpreter CPU

Note

Python is sometimes called a “compiled interpreted language”: it compiles to bytecode, then interprets that bytecode. The bytecode is not native machine code.

CPython — The Reference Implementation#

What is CPython?

  • CPython is the reference (default) implementation of Python.

  • The interpreter itself is written in C — hence the name “CPython”.

  • When you run python3 script.py, you are running a C program that:

    • Lexes, parses, and compiles your code to bytecode.

    • Executes bytecode via an evaluation loop (_PyEval_EvalFrameDefault in ceval.c).

  • The “Python Virtual Machine” (PVM) is not a separate program — it’s the interpreter loop inside CPython.

Key Characteristics:

  • Best compatibility with C extensions — libraries like NumPy, pandas, and TensorFlow are partially written in C for performance and only fully support CPython.

  • Has the Global Interpreter Lock (GIL) — limits true multi-threaded parallelism.

  • CPython 3.13 introduced experimental free-threaded builds (no GIL).

  • CPython 3.14 promotes free-threading to officially supported (but still optional).

    • Install with: python3.14t (the “t” suffix indicates free-threaded)

    • Single-threaded overhead reduced from ~40% to ~5-10%

Check your implementation:

python3 --version && python3 -c "import sys; print(sys.implementation)"

Inspecting the Pipeline#

Create a file called internals_demo.py to explore Python’s internals.

1import io
2import tokenize
3
4code = "x = 5 + 3"
5tokens = tokenize.generate_tokens(io.StringIO(code).readline)
6
7for tok in tokens:
8    print(tok)
1import ast
2
3code = "x = 5 + 3"
4tree = ast.parse(code)
5print(ast.dump(tree, indent=2))
1import dis
2
3def example():
4    x = 5 + 3
5    return x
6
7dis.dis(example)

Sample Output:

27    RESUME                0
28    LOAD_SMALL_INT        8
      STORE_FAST            0 (x)
29    LOAD_FAST_BORROW      0 (x)
      RETURN_VALUE

Tip

5 + 3 becomes LOAD_SMALL_INT 8: the compiler optimizes constant expressions!

Alternative Python Implementations#

PyPy

Written in RPython; uses JIT compilation for 4–10× speedup on long-running code.

Jython

Compiles to Java bytecode; runs on the JVM; access to Java libraries; no GIL.

IronPython

Compiles to .NET IL; runs on the CLR; access to .NET libraries; no GIL.

MicroPython

Optimized for microcontrollers (ESP32, Raspberry Pi Pico); minimal RAM (~256KB).

GraalPy

Runs on GraalVM with JIT; polyglot interoperability with other languages.

Cython

Superset of Python that compiles to C, then to native binaries.

Use Case

Best Choice

Why

General development

CPython

Best compatibility, largest ecosystem

Performance-critical long-running apps

PyPy

JIT compilation speeds up hot paths

Integration with Java ecosystem

Jython

Direct Java library access

Integration with .NET ecosystem

IronPython

Direct .NET library access

Embedded systems / IoT

MicroPython

Minimal footprint, hardware access

Data science / ML

CPython

NumPy, pandas, TensorFlow require C extensions

Introduction to Python Variables#

A preview of Python fundamentals — covered in depth next week.

Create a file called variables_demo.py to follow along with the examples below.

What is a Variable?#

Variables in Python

  • A variable is a name that refers to a value stored in memory.

  • Variables are created using the assignment operator (=).

  • No need to declare the type — Python figures it out automatically.

# No type declaration needed
name = "Alice"
age = 25
gpa = 3.85
is_student = True
// Must declare types
std::string name = "Alice";
int age = 25;
double gpa = 3.85;
bool is_student = true;

Note

Python is dynamically typed — variable types are determined at runtime, not compile time.

Assignment Styles#

The assignment operator (=) binds a name to an object.

# Standard assignment
name = "Guido van Rossum"

# Chained assignment (same object, multiple names)
x = y = 10

# Multiple assignment (unpacking)
name, age, role = "Guido van Rossum", 64, "BDFL Emeritus"

# Swapping values (no temp variable needed!)
a, b = 1, 2
a, b = b, a  # a=2, b=1

Everything is an Object#

In Python, everything is an object. Variables are references (names) bound to objects in memory, not direct storage locations.

x = 2
  • x is a name (reference)

  • 2 is an object in memory

  • The name x is bound to the object 2

print(type(10))       # <class 'int'>
print(type(3.14))     # <class 'float'>
print(type("hello"))  # <class 'str'>
print(type(print))    # <class 'builtin_function_or_method'>

Object Identity#

Every object has a unique identity — an integer representing its memory address. Use id() to inspect it.

a = 10
b = 10.5
c = "hello"

print(id(a))  # e.g., 9793376
print(id(b))  # e.g., 140409546886992
print(id(c))  # e.g., 140409536180784

# What about literals?
print(id(10))      # Same as id(a)?
print(id("hello")) # Same as id(c)?

Warning

The actual id() values change each time you run the program, but they remain constant for each object during its lifetime.

Dynamic Typing#

What is Dynamic Typing?

  • The type is associated with the value (object), not the variable name.

  • A variable can be rebound to objects of different types during execution.

  • Types are checked at runtime, not compile time.

Variable Rebinding:

x = "hello"
print(type(x))  # <class 'str'>

x = 10.5
print(type(x))  # <class 'float'>

Warning

While flexible, rebinding to different types can lead to bugs. We’ll discuss type hints in L2 to make code safer.

Basic Data Types#

Category

Type

Examples

Notes

Mutable

Numeric

int

42, -7, 0

Unlimited precision

No

Numeric

float

3.14, -0.001

64-bit (IEEE 754)

No

Numeric

bool

True, False

Subclass of int

No

Text

str

"hello", 'world'

Immutable sequence

No

Null

NoneType

None

Singleton object

No

Sequence

list

[1, 2, 3]

Ordered, changeable

Yes

Sequence

tuple

(1, 2, 3)

Ordered, unchangeable

No

Mapping

dict

{"a": 1}

Key-value pairs

Yes

Set

set

{1, 2, 3}

Unordered, unique

Yes

Note

We’ll cover sequence types (list, tuple), mappings (dict), and sets in detail in L3.

Mutable vs. Immutable Objects#

🔒 Immutable Objects
  • Cannot be changed after creation

  • Modifying creates a new object

  • Examples: int, float, str, tuple

x = 10
print(id(x))  # 9793376

x = x + 1     # New object created!
print(id(x))  # 9793408 (different)
🔓 Mutable Objects
  • Can be changed in place

  • Same object, modified content

  • Examples: list, dict, set

my_list = [1, 2, 3]
print(id(my_list))  # 140234567890

my_list.append(4)   # Same object!
print(id(my_list))  # 140234567890 (same)

Important

Understanding mutability is crucial for avoiding bugs when passing objects to functions or assigning variables.

Variables are References#

Aliasing: Multiple Names, Same Object

When you assign one variable to another, both names reference the same object.

a = [1, 2, 3]
b = a  # b references same list

print(id(a))  # e.g., 140234567890
print(id(b))  # Same address!
print(a is b) # True

b.append(4)
print(a)  # [1, 2, 3, 4] -- both changed!

Warning

This behavior with mutable objects is a common source of bugs! To create an independent copy, use b = a.copy() or b = list(a).

The None Type#

What is None?

  • None represents the absence of a value or a null value.

  • It is the sole instance of the NoneType class: a singleton.

  • Common uses: default function arguments, uninitialized variables, indicating “no result”.

# Declaring a variable with no value yet
result = None

# Function that doesn't explicitly return anything
def greet(name):
    print(f"Hello, {name}")

x = greet("Alice")  # Prints "Hello, Alice"
print(x)            # None
print(type(x))      # <class 'NoneType'>

Important

Always use is (not ==) to compare with None:

if x is None:

if x == None:

Identity vs. Equality#

The is vs. == Operators

  • == compares values (equality)

  • is compares identity (same object in memory)

a = [1, 2, 3]
b = [1, 2, 3]
c = a

print(a == b)  # True  (same values)
print(a is b)  # False (different objects)
print(a is c)  # True  (same object)

Tip

Rule of thumb: Use == for value comparison. Use is only for None checks or when you specifically need identity comparison.

Variable Naming Rules#

✅ Rules (Must Follow)
  • Must start with letter or underscore

  • Can contain letters, digits, underscores

  • Case-sensitive (Namename)

  • Cannot be a reserved keyword

# Valid names
my_variable = 1
_private = 2
camelCase = 3
var123 = 5

# Invalid names
# 2fast = 10      # starts with digit
# my-var = 5      # contains hyphen
# class = "test"  # reserved keyword
📏 Conventions (PEP 8)
  • snake_case for variables/functions

  • UPPER_CASE for constants

  • PascalCase for classes

  • Be descriptive but concise

# Good style (PEP 8)
student_name = "Alice"
MAX_RETRIES = 3
total_count = 0

# Avoid
x = "Alice"  # Too vague
studentName = "Alice"  # camelCase
STUDENTNAME = "Alice"  # ALL CAPS

Tip

Follow PEP 8 — Python’s official style guide. We’ll enforce it in assignments.

Reserved Keywords#

These words have special meaning and cannot be used as variable names.

False     None      True      and       as        assert
async     await     break     class     continue  def
del       elif      else      except    finally   for
from      global    if        import    in        is
lambda    nonlocal  not       or        pass      raise
return    try       while     with      yield
# Check keywords programmatically
import keyword

print(keyword.iskeyword("if"))  # True
print(keyword.iskeyword("hello"))  # False
print(keyword.kwlist)  # List of all Python keywords

Note

If you accidentally use a keyword as a variable name, Python will raise a SyntaxError.

The print() Function#

The print() function outputs to the screen. It’s a variadic function: accepts any number of arguments.

# Print literals
print("Hello")          # Hello
print(3)                # 3
print(2.4)              # 2.4

# Print expressions
print(2 + 3)            # 5
print("*" * 10)         # **********

# Multiple arguments (separated by space by default)
print("Welcome", "to", "ENPM", 605)  # Welcome to ENPM 605

# Custom separator and end
print("a", "b", "c", sep="-")        # a-b-c
print("No newline", end="")          # No newline after

Preview: What’s Next in L2#

📖 L2: Python Basics — Part I
  • Packages and modules

  • Importing (import, from)

  • Operators (arithmetic, relational, logical)

  • Boolean type and truth testing

  • String operations and methods

  • Indexing and slicing

  • Control flow (if/elif/else)

📘 L3: Python Basics — Part II
  • Lists and list methods

  • Tuples and unpacking

  • Dictionaries

  • Sets

  • Loops (for, while)

  • List comprehensions

Note

Today’s introduction gives you enough to start experimenting. L2 will build on these concepts systematically.