🐍 Python Basics for Robotics
**Essential Python programming for robotics development**
*Learn the Python fundamentals you'll need for ROS 2, robot control, and simulation*
🎯 Overview
This guide covers essential Python concepts for robotics programming. You'll learn the basics needed to write ROS 2 nodes, control robots, process sensor data, and work with mathematical operations.
💻 Prerequisites
Before starting, ensure you have: - ✅ Ubuntu with Python 3.8+ installed - ✅ Basic terminal knowledge - ✅ Text editor (VS Code, gedit, or nano)
🚀 Getting Started
Check Python Installation
# Check Python version
python3 --version
# Check pip version
pip3 --version
# Start Python interpreter
python3
Install Essential Packages
# Install common robotics packages
pip3 install numpy matplotlib scipy
# Install ROS 2 Python client
sudo apt install python3-rclpy
📚 Core Python Concepts
1. Variables and Data Types
# Numbers
x = 10 # integer
y = 3.14 # float
z = 2 + 3j # complex
# Strings
name = "Robot"
message = 'Hello, World!'
# Booleans
is_robot = True
is_human = False
# Lists (mutable)
joint_angles = [0.0, 1.57, 0.0, 0.0, 0.0, 0.0]
joint_names = ["shoulder", "elbow", "wrist"]
# Tuples (immutable)
position = (1.0, 2.0, 3.0)
# Dictionaries
robot_config = {
"name": "UR3e",
"dof": 6,
"max_payload": 3.0
}
2. Control Flow
# If statements
if joint_angle > 1.57:
print("Joint limit exceeded!")
elif joint_angle < -1.57:
print("Joint limit exceeded!")
else:
print("Joint angle is safe")
# For loops
for i in range(6):
print(f"Joint {i}: {joint_angles[i]}")
# While loops
count = 0
while count < 5:
print(f"Count: {count}")
count += 1
3. Functions
def calculate_distance(point1, point2):
"""Calculate Euclidean distance between two points."""
import math
dx = point2[0] - point1[0]
dy = point2[1] - point1[1]
dz = point2[2] - point1[2]
return math.sqrt(dx*dx + dy*dy + dz*dz)
# Function with default parameters
def move_robot(x=0.0, y=0.0, z=0.0, speed=1.0):
"""Move robot to specified position."""
print(f"Moving to ({x}, {y}, {z}) at speed {speed}")
return True
# Call functions
distance = calculate_distance((0, 0, 0), (1, 1, 1))
move_robot(1.0, 2.0, 3.0)
🔢 Mathematics and NumPy
NumPy Basics
import numpy as np
# Create arrays
joint_angles = np.array([0.0, 1.57, 0.0, 0.0, 0.0, 0.0])
position = np.array([1.0, 2.0, 3.0])
# Array operations
angles_deg = np.degrees(joint_angles)
angles_rad = np.radians(angles_deg)
# Matrix operations
rotation_matrix = np.array([
[1, 0, 0],
[0, 1, 0],
[0, 0, 1]
])
# Matrix multiplication
new_position = rotation_matrix @ position
Common Mathematical Operations
import numpy as np
import math
# Trigonometric functions
angle = np.pi / 4
sin_val = np.sin(angle)
cos_val = np.cos(angle)
tan_val = np.tan(angle)
# Square root and power
distance = np.sqrt(16)
squared = np.power(4, 2)
# Random numbers
random_angle = np.random.uniform(-np.pi, np.pi)
random_position = np.random.randn(3) # 3D normal distribution
📊 Data Visualization with Matplotlib
Basic Plotting
import matplotlib.pyplot as plt
import numpy as np
# Create data
time = np.linspace(0, 10, 100)
joint_angle = np.sin(time)
# Create plot
plt.figure(figsize=(10, 6))
plt.plot(time, joint_angle, 'b-', linewidth=2, label='Joint Angle')
plt.xlabel('Time (s)')
plt.ylabel('Angle (rad)')
plt.title('Joint Angle vs Time')
plt.grid(True)
plt.legend()
plt.show()
Multiple Plots
# Create subplots
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(10, 8))
# First subplot
ax1.plot(time, np.sin(time), 'r-', label='Sine')
ax1.set_ylabel('Amplitude')
ax1.legend()
ax1.grid(True)
# Second subplot
ax2.plot(time, np.cos(time), 'g-', label='Cosine')
ax2.set_xlabel('Time (s)')
ax2.set_ylabel('Amplitude')
ax2.legend()
ax2.grid(True)
plt.tight_layout()
plt.show()
🤖 ROS 2 Integration
Basic ROS 2 Node
#!/usr/bin/env python3
import rclpy
from rclpy.node import Node
from std_msgs.msg import String, Float64MultiArray
from geometry_msgs.msg import Pose
class RobotController(Node):
def __init__(self):
super().__init__('robot_controller')
# Create publisher
self.joint_pub = self.create_publisher(
Float64MultiArray,
'/joint_commands',
10
)
# Create subscriber
self.pose_sub = self.create_subscription(
Pose,
'/robot_pose',
self.pose_callback,
10
)
# Create timer
self.timer = self.create_timer(0.1, self.timer_callback)
self.get_logger().info('Robot controller node started')
def pose_callback(self, msg):
"""Callback for robot pose updates."""
x, y, z = msg.position.x, msg.position.y, msg.position.z
self.get_logger().info(f'Robot at: ({x:.2f}, {y:.2f}, {z:.2f})')
def timer_callback(self):
"""Timer callback for periodic tasks."""
# Send joint commands
joint_msg = Float64MultiArray()
joint_msg.data = [0.0, 1.57, 0.0, 0.0, 0.0, 0.0]
self.joint_pub.publish(joint_msg)
def main(args=None):
rclpy.init(args=args)
node = RobotController()
rclpy.spin(node)
node.destroy_node()
rclpy.shutdown()
if __name__ == '__main__':
main()
🔧 File I/O and Data Handling
Reading and Writing Files
# Write data to file
joint_data = [0.0, 1.57, 0.0, 0.0, 0.0, 0.0]
with open('joint_angles.txt', 'w') as f:
for angle in joint_data:
f.write(f"{angle}\n")
# Read data from file
angles = []
with open('joint_angles.txt', 'r') as f:
for line in f:
angles.append(float(line.strip()))
print(f"Read angles: {angles}")
CSV Files
import csv
# Write CSV
with open('robot_data.csv', 'w', newline='') as f:
writer = csv.writer(f)
writer.writerow(['Time', 'Joint1', 'Joint2', 'Joint3'])
writer.writerow([0.0, 0.0, 1.57, 0.0])
writer.writerow([0.1, 0.1, 1.67, 0.1])
# Read CSV
with open('robot_data.csv', 'r') as f:
reader = csv.reader(f)
header = next(reader) # Skip header
for row in reader:
time, j1, j2, j3 = float(row[0]), float(row[1]), float(row[2]), float(row[3])
print(f"Time: {time}, Joints: [{j1}, {j2}, {j3}]")
🧪 Testing and Debugging
Basic Testing
def test_calculate_distance():
"""Test the calculate_distance function."""
# Test case 1: Distance from origin
result = calculate_distance((0, 0, 0), (1, 1, 1))
expected = np.sqrt(3)
assert abs(result - expected) < 1e-6, f"Expected {expected}, got {result}"
# Test case 2: Same point
result = calculate_distance((1, 2, 3), (1, 2, 3))
expected = 0.0
assert abs(result - expected) < 1e-6, f"Expected {expected}, got {result}"
print("All tests passed!")
# Run tests
test_calculate_distance()
Debugging Tips
# Use print statements
print(f"Debug: joint_angles = {joint_angles}")
# Use pdb debugger
import pdb
pdb.set_trace() # Code stops here for debugging
# Use logging
import logging
logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger(__name__)
logger.debug(f"Joint angles: {joint_angles}")
📱 Common Robotics Patterns
State Machine
class RobotState:
IDLE = "IDLE"
MOVING = "MOVING"
ERROR = "ERROR"
class RobotController:
def __init__(self):
self.state = RobotState.IDLE
self.target_position = None
def update(self):
if self.state == RobotState.IDLE:
if self.target_position:
self.state = RobotState.MOVING
print("Starting movement...")
elif self.state == RobotState.MOVING:
if self.reached_target():
self.state = RobotState.IDLE
print("Movement completed")
elif self.state == RobotState.ERROR:
print("Robot in error state")
def reached_target(self):
# Simulate reaching target
return True
PID Controller
class PIDController:
def __init__(self, kp, ki, kd):
self.kp = kp
self.ki = ki
self.kd = kd
self.prev_error = 0
self.integral = 0
def compute(self, setpoint, measurement, dt):
error = setpoint - measurement
# Proportional term
p_term = self.kp * error
# Integral term
self.integral += error * dt
i_term = self.ki * self.integral
# Derivative term
derivative = (error - self.prev_error) / dt
d_term = self.kd * derivative
# Update previous error
self.prev_error = error
# Compute output
output = p_term + i_term + d_term
return output
# Usage example
pid = PIDController(kp=1.0, ki=0.1, kd=0.01)
control_output = pid.compute(setpoint=1.0, measurement=0.5, dt=0.01)
✅ Practice Exercises
Exercise 1: Joint Limit Checker
def check_joint_limits(joint_angles, limits):
"""
Check if joint angles are within limits.
Args:
joint_angles: List of joint angles
limits: List of (min, max) tuples for each joint
Returns:
List of booleans indicating if each joint is within limits
"""
# Your code here
pass
# Test data
joints = [0.0, 1.57, 0.0, 0.0, 0.0, 0.0]
limits = [(-3.14, 3.14)] * 6 # All joints have same limits
# Test your function
results = check_joint_limits(joints, limits)
print(f"Joint limits check: {results}")
Exercise 2: Trajectory Generator
def generate_trajectory(start_pos, end_pos, num_points):
"""
Generate a linear trajectory between two positions.
Args:
start_pos: Starting position (x, y, z)
end_pos: Ending position (x, y, z)
num_points: Number of points in trajectory
Returns:
List of positions along the trajectory
"""
# Your code here
pass
# Test your function
start = (0, 0, 0)
end = (1, 1, 1)
trajectory = generate_trajectory(start, end, 10)
print(f"Trajectory: {trajectory}")
🆘 Getting Help
Python Resources
- Official Docs: python.org
- NumPy Docs: numpy.org
- Matplotlib Docs: matplotlib.org
Course Support
- Piazza: Ask questions on course forum
- Office Hours: Get help from TA or instructor
- Lab Sessions: Hands-on help during labs
🚀 Next Steps
After mastering Python basics:
- Set up ROS 2: See ROS Setup Guide
- Learn Gazebo: See Gazebo Setup
- Start Week 3 lab: See Week 3 Lab
- Practice coding: Work on exercises and small projects
**Ready to control robots? Let's set up ROS 2 next! 🤖**
[🤖 ROS Setup](ros-setup.md){ .md-button .md-button--primary }
[🎮 Gazebo Setup](gazebo-setup.md){ .md-button }
[📚 Back to Resources](resources.md){ .md-button }
Last updated: Fall 2025 • Back to Resources