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#
Get the lecture materials from Canvas and/or Github.
Complete pre-work activities.
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.
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
10% deduction per calendar day late
Maximum 30% reduction (3 days)
Work submitted more than 3 days late earns zero
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
Path planning and trajectory following
Obstacle avoidance via costmaps (subscribes to sensor topics directly)
Recovery behaviors (spin, backup, wait)
Application-level behavior tree that coordinates high-level tasks:
Patrol a sequence of waypoints (calls
/navigate_to_poseaction)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 |
|
Docker Desktop |
Containerized development |
|
ROS 2 Jazzy |
Robot Operating System |
|
Gazebo Harmonic |
Simulation environment |
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.
Download and install from Visual Studio Code. Available for Windows, macOS, and Linux.
Download and install Docker Desktop. Verify installation with docker --version.
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
Python (Microsoft) — Python language support
Dev Containers (Microsoft) — Development in containers
Container Tools (Microsoft) — Build, manage, and deploy containers
Docker Installation#
Installing Docker Desktop
Download and install Docker Desktop.
Verify your installation:
docker --versionDocker allows us to run isolated containers with all dependencies pre-installed.
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#
Create a workspace where you will host all Python code for this course.
Example:
~/enpm605/py_ws
Open a terminal and run:
cd <path to workspace> code .
By starting VS Code in a folder, that folder becomes your “workspace”.
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.
Open Command Palette with
Ctrl+Shift+PStart typing Python: Select Interpreter
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
Ctrl+Shift+P→ Type REPL → Select Python: Start Native Python REPLSelect code and press
Shift+Enterto execute in REPL.
Debugging Python Code#
Set a breakpoint: Click in the gutter or place your cursor on a line and press
F9Expand top-right triangle and select Debug Python File or press
F5Since this is your first time debugging this file, a configuration menu will open: Select Python Debugger then Python File.
Use the debug toolbar to step through code, inspect variables, and evaluate expressions.
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).
Popular Python Linters#
Tool |
Type |
Speed |
Notes |
|---|---|---|---|
Ruff |
Linter + Formatter |
Extremely Fast |
Written in Rust, replaces multiple tools |
Flake8 |
Linter |
Fast |
Lightweight, plugin ecosystem |
Pylint |
Linter |
Slow |
Deep analysis, very thorough |
Black |
Formatter |
Fast |
Opinionated, no configuration |
isort |
Import Sorter |
Fast |
Sorts imports alphabetically |
Tip
Recommendation: Use Ruff — it combines linting, formatting, and import sorting in one tool, and is 10–100× faster than alternatives.
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#
Open VS Code in your workspace.
Create a file called
verify_setup.py.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()
Run the file using any of the methods discussed.
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 (
.pyfiles).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 (
.pycfiles 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#
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.
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#
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
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_EvalFrameDefaultinceval.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
xis a name (reference)2is an object in memoryThe name
xis bound to the object2
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 |
|
|
Unlimited precision |
No |
Numeric |
|
|
64-bit (IEEE 754) |
No |
Numeric |
|
|
Subclass of |
No |
Text |
|
|
Immutable sequence |
No |
Null |
|
|
Singleton object |
No |
Sequence |
|
|
Ordered, changeable |
Yes |
Sequence |
|
|
Ordered, unchangeable |
No |
Mapping |
|
|
Key-value pairs |
Yes |
Set |
|
|
Unordered, unique |
Yes |
Note
We’ll cover sequence types (list, tuple), mappings (dict), and sets in detail in L3.
Mutable vs. 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)
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?
Nonerepresents the absence of a value or a null value.It is the sole instance of the
NoneTypeclass: 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)iscompares 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#
Must start with letter or underscore
Can contain letters, digits, underscores
Case-sensitive (
Name≠name)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
snake_casefor variables/functionsUPPER_CASEfor constantsPascalCasefor classesBe 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#
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)
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.