Hi! Iām Dr. B ![]()
Here I will add intermediate and advanced exercises that incorporate data science and AI fundamentals. Looking forward to feedback and answering any questions you may have!
Hi! Iām Dr. B ![]()
Here I will add intermediate and advanced exercises that incorporate data science and AI fundamentals. Looking forward to feedback and answering any questions you may have!
CS4AL Exercise 1 - calibrating sensor threshold values
# ================================================================
# AI IN K-12 STEM ā CodeBot Workshop
# SECTION 1 OF 5: What Counts as AI?
#
# ----------------------------------------------------------------
# SETUP ā complete BEFORE clicking RUN
# ----------------------------------------------------------------
# 1. SANDBOX MODE
# Left sidebar ā device icon ā Target Device = "Sim CodeBot"
# Status bar bottom-left should say: "Sim CodeBot Connected"
#
# 2. ENVIRONMENT
# Bottom of sim panel ā environment dropdown ā "Line-follow 101"
# You should see a flat floor with a BLACK LINE on it
# Click RESET to place the bot at start position
#
# 3. CAMERA
# Bottom of sim panel ā camera dropdown ā "Attached"
# Camera follows behind the bot ā you can see the front sensors
#
# 4. RUN ā switch to CONSOLE tab ā follow prompts
# ================================================================
# CSTA: 1A-DA-07, 2-DA-08, 2-DA-09, 3A-AP-13
# ================================================================
from botcore import *
import time
def banner(title):
print("")
print("=" * 58)
print(" " + title)
print("=" * 58)
print("")
def go(label=""):
"""Minimal press-enter prompt."""
if label:
input(f" {label} ā press Enter...")
else:
input(" Press Enter to continue...")
print("")
def gate(question, correct_answer):
print("")
print("~" * 58)
print(" CHECKPOINT")
print("~" * 58)
print(f" {question}")
print("")
print(" Decode the hex string at the BOTTOM of this script.")
print(" Enter the decoded word (lowercase, no spaces).")
print("")
while True:
ans = input(" Your answer: ").strip().lower()
if ans == correct_answer.lower():
print("")
print(" CORRECT! Section 1 complete.")
print(" Open Section 2 to continue.")
print("")
break
else:
print(" Not quite ā decode the hex and try again.")
def get_int(prompt, default, lo, hi):
raw = input(prompt).strip()
if raw == "":
return default
try:
return max(lo, min(hi, int(raw)))
except ValueError:
print(f" Invalid ā using default ({default})")
return default
def show_sensors(threshold, duration):
start = time.time()
print(" LS0 LS1 LS2 LS3 LS4 ")
print(" " + "-" * 50)
while time.time() - start < duration:
raw = [ls.read(i) for i in range(5)]
binary = [1 if v > threshold else 0 for v in raw]
leds.ls(binary)
print(f" {binary[0]} {binary[1]} {binary[2]} "
f"{binary[3]} {binary[4]} "
# f"({raw[0]:4},{raw[1]:4},{raw[2]:4},{raw[3]:4},{raw[4]:4})"
)
time.sleep(0.4)
print(" " + "-" * 50)
leds.ls([0, 0, 0, 0, 0])
print("")
# ================================================================
# LECTURE
# ================================================================
banner("SECTION 1 of 5: What Counts as AI?")
print(" When people say 'AI' they usually mean one of three things:")
print(" 1. Rule-based AI ā a human writes explicit IF/THEN rules")
print(" 2. Machine Learning ā an algorithm learns rules from data")
print(" 3. Statistical Models ā math functions fit to data patterns")
print("")
print(" All three share one idea: INPUT DATA ā DECISION.")
print(" The difference is WHO wrote the decision rules.")
go()
print(" Our CodeBot uses Rule-Based AI:")
print(" - We write the rules ourselves (IF middle sensor ā go straight)")
print(" - No learning from data occurs")
print(" - But it still qualifies as AI ā it takes data and decides")
print("")
print(" Machine Learning goes further:")
print(" - You provide EXAMPLES (labeled data)")
print(" - The algorithm discovers the rules on its own")
print(" - Examples: Gmail spam, Netflix, image recognition, ChatGPT")
go()
print(" AI models don't see the world like humans.")
print(" They see NUMBERS ā called FEATURES.")
print("")
print(" Feature = any measurable property of what you're classifying.")
print("")
print(" Spam filter: word count, # links, sender score")
print(" Medical AI: age, blood pressure, pixel values")
print(" Self-driving: camera pixels, LIDAR distances, speed")
go()
print(" Our CodeBot has 5 LINE SENSORS across its front edge.")
print(" Each reads reflected infrared light: 0 (bright) to 4095 (dark).")
print(" These 5 readings = our model's FEATURE VECTOR.")
print("")
print(" [LS0] [LS1] [LS2] [LS3] [LS4]")
print(" far near MID near far")
print(" left left right right")
print("")
print(" LS2 (middle) is the most critical for line detection.")
go()
print(" We convert each raw reading into a BINARY FEATURE:")
print("")
print(" raw value > threshold ā 1 (line detected)")
print(" raw value <= threshold ā 0 (no line)")
print("")
print(" The THRESHOLD is our DECISION BOUNDARY.")
print(" It separates the two classes: line vs. no line.")
print("")
print(" Because WE set it before the model runs,")
print(" it is called a HYPERPARAMETER.")
go()
print(" HYPERPARAMETER definition:")
print(" A config value set by the engineer BEFORE training.")
print(" It controls how the model decides ā but is NOT")
print(" learned from data. Must be tuned by experimenting.")
print("")
print(" Other ML hyperparameter examples:")
print(" - Learning rate in a neural network")
print(" - k (# of neighbors) in k-nearest neighbor")
print(" - Number of trees in a random forest")
go()
print(" Setting the boundary wrong causes two types of error:")
print("")
print(" FALSE POSITIVE (threshold too LOW)")
print(" Model says 'line!' when there is no line.")
print(" ā LEDs fire on plain floor ā bot steers randomly")
print(" ā Real world: spam filter flags a real email")
print("")
print(" FALSE NEGATIVE (threshold too HIGH)")
print(" Model says 'no line' when the line IS there.")
print(" ā LEDs stay dark over the line ā bot drives off track")
print(" ā Real world: medical AI misses a tumor")
go()
print(" CALIBRATION = finding the threshold that minimizes both errors.")
print("")
print(" In ML this is done automatically using a VALIDATION SET ā")
print(" labeled data held back from training, used only to tune.")
print("")
print(" You are about to do it MANUALLY ā same process,")
print(" performed by hand instead of by an optimizer.")
go()
# ================================================================
# HANDS-ON
# ================================================================
banner("HANDS-ON: Explore the Decision Boundary")
print(" You will set the threshold THREE times:")
print(" Round 1 ā too LOW ā observe false positives")
print(" Round 2 ā too HIGH ā observe false negatives")
print(" Round 3 ā just right ā calibrated boundary")
print("")
print(" Watch the LEDs on the bot's FRONT EDGE in the sim.")
print(" LED lit = sensor reports 1. LED dark = sensor reports 0.")
print("")
print(" Make sure:")
print(" [ā] Environment = Line-follow 101")
print(" [ā] Camera = Attached")
print(" [ā] Bot is near the black line (RESET if needed)")
go()
# ---- Round 1: Too Low ----
print(" ROUND 1 ā TOO LOW (false positives)")
print(" A low threshold fires on everything ā line AND plain floor.")
print(" The model can't tell them apart.")
print("")
low = get_int(" Enter a LOW threshold (try 200-400, default 300): ",
300, 0, 4095)
print("")
print(" Watching for 6 seconds ā notice ALL LEDs light up...")
print("")
show_sensors(low, 6)
print(" ā All 5 LEDs lit even on plain floor?")
print(" ā That's a false positive flood ā the model is useless here.")
go()
# ---- Round 2: Too High ----
print(" ROUND 2 ā TOO HIGH (false negatives)")
print(" A high threshold almost never fires ā")
print(" the model is blind to the line entirely.")
print("")
high = get_int(" Enter a HIGH threshold (try 3500-4000, default 3800): ",
3800, 0, 4095)
print("")
print(" Watching for 6 seconds ā notice LEDs stay dark...")
print("")
show_sensors(high, 6)
print(" ā All LEDs dark even over the black line?")
print(" ā That's a false negative ā the bot would drive off the track.")
go()
# ---- Round 3: Calibrated ----
print(" ROUND 3 ā CALIBRATED (just right)")
print(" Find the value where:")
print(" - LEDs OFF on plain floor (no false positives)")
print(" - LEDs ON over the line (no false negatives)")
print("")
print(" Typical range for this simulator: 1500 ā 2500")
print(" Start at 2000, adjust based on what you observe.")
print("")
THRESHOLD = get_int(" Enter your calibrated threshold (default 2000): ",
2000, 0, 4095)
print("")
print(" Watching for 8 seconds ā look for selective LED firing...")
print("")
show_sensors(THRESHOLD, 8)
print(" ā LEDs firing only over the black line?")
print(" ā Plain floor staying dark?")
print(" ā If not, re-run with an adjusted value.")
print("")
print(f" Calibrated boundary: {THRESHOLD}")
print("")
go()
# ================================================================
# DISCUSSION
# ================================================================
print(" DISCUSSION:")
print("")
print(" Q1: Is rule-based AI still 'AI'?")
print(" Yes ā it takes data and makes decisions.")
print(" But it can't improve itself from experience.")
print("")
print(" Q2: In ML, who sets the decision boundary?")
print(" The optimizer ā it finds the boundary that")
print(" minimizes errors on the training data.")
go()
print(" Q3: What is a hyperparameter?")
print(" A value set BEFORE training ā not learned from data.")
print(" Our threshold is one.")
print("")
print(" Q4: False Positive vs. False Negative?")
print(" FP: model says YES when the answer is NO (too low)")
print(" FN: model says NO when the answer is YES (too high)")
print("")
print(" Q5: How does ML calibrate automatically?")
print(" Uses a VALIDATION SET ā held-out labeled data ā")
print(" to measure and minimize both error types.")
go()
# ================================================================
# CHECKPOINT
# ================================================================
gate(
"A config value set BEFORE training ā not learned from data ā is a ___?",
"threshold"
)
CS4AL Exercise 2 - policy selection
# ================================================================
# AI IN K-12 STEM ā CodeBot Workshop
# SECTION 2 OF 5: From Features to Decisions
#
# NO RESET REQUIRED ā drag robot between rounds if needed
# ================================================================
from botcore import *
import time
# --- Sensor labels ---
LS_FAR_LEFT = 0
LS_LEFT = 1
LS_MIDDLE = 2
LS_RIGHT = 3
LS_FAR_RIGHT = 4
THRESHOLD = 2000
# ================================================================
# HELPERS
# ================================================================
def banner(title):
print("\n" + "="*58)
print(" " + title)
print("="*58 + "\n")
def go(label=""):
input(f" {label if label else 'Press Enter to continue...'}")
print("")
def ask(question, correct):
print("\n QUESTION:")
print(f" {question}\n")
while True:
ans = input(" Your answer: ").strip().lower()
if ans == correct:
print(" ā Correct\n")
break
else:
print(" Try again.\n")
def gate(question, answer):
print("\n" + "~"*58)
print(" CHECKPOINT")
print("~"*58)
print(f" {question}\n")
while True:
ans = input(" Your answer: ").strip().lower()
if ans == answer:
print("\n CORRECT!\n")
break
else:
print(" Try again.")
def read_sensors():
return [ls.read(i) > THRESHOLD for i in range(5)]
def classify(s):
if s[LS_MIDDLE]:
return "ON_LINE"
elif s[LS_LEFT] or s[LS_FAR_LEFT]:
return "LEFT_OF_LINE"
elif s[LS_RIGHT] or s[LS_FAR_RIGHT]:
return "RIGHT_OF_LINE"
else:
return "LOST"
def stop():
motors.run(LEFT, 0)
motors.run(RIGHT, 0)
def reposition():
#print(" Drag the robot back near the black line.")
print(" Do NOT press RESET.\n")
# ================================================================
# LESSON
# ================================================================
banner("SECTION 2 of 5: From Features to Decisions")
print(" In Section 1, we focused on FEATURES.")
print(" The robot measured the world and converted it into 1s and 0s.")
print("")
print(" That told the robot what it could SEE.")
go()
print(" But sensing alone does not create behavior.")
print("")
print(" Imagine this:")
print(" A robot perfectly detects the line...")
print(" but never moves.")
print("")
print(" That robot has perception, but no intelligence in action.")
go()
print(" To act, the robot needs a DECISION step.")
print("")
print(" The full pipeline is now:")
print("")
print(" SENSOR ā FEATURE ā DECISION ā ACTION")
print("")
print(" The DECISION step is where behavior is created.")
go()
print(" A POLICY is the rule that defines that decision.")
print("")
print(" It answers this question:")
print("")
print(" Given what I see, what should I do?")
go()
print(" Example:")
print("")
print(" If middle sensor sees the line ā go straight")
print(" If left sensors see the line ā turn right")
print(" If right sensors see the line ā turn left")
print("")
print(" That entire mapping is the POLICY.")
go()
ask(
"What do we call the rule that maps sensor input to action?",
"policy"
)
print(" Now here is the key idea for this lesson:")
print("")
print(" The sensors will NOT change.")
print("")
print(" Only the POLICY will change.")
go()
print(" And we will observe what happens to behavior.")
print("")
print(" You will see three cases:")
print("")
print(" 1. No decision rule")
print(" 2. A well-designed rule")
print(" 3. A poorly designed rule")
go()
# ================================================================
# ROUND 1 ā NULL
# ================================================================
banner("ROUND 1: NULL POLICY")
print(" In this round, the robot has NO policy.")
print("")
print(" It can see the line...")
print(" but it has no instructions for what to do.")
go()
motors.enable(True)
start = time.time()
while time.time() - start < 3:
s = read_sensors()
leds.ls(s)
stop()
print(f"{s} --> NO ACTION")
time.sleep(0.1)
stop()
motors.enable(False)
print("")
print(" OBSERVE:")
print(" ā Sensors are active")
print(" ā LEDs respond correctly")
print(" ā But the robot does not move")
print("")
print(" The robot has information,")
print(" but no decision rule to use it.")
go()
ask(
"Did the robot have data but no useful behavior? (yes/no)",
"yes"
)
# ================================================================
# ROUND 2 ā BALANCED
# ================================================================
banner("ROUND 2: BALANCED POLICY")
print(" Now we introduce a POLICY.")
print("")
print(" This policy matches the situation to the response.")
go()
print(" If the robot is centered:")
print(" ā go straight")
print("")
print(" If the robot drifts slightly:")
print(" ā make a small correction")
print("")
print(" If the robot drifts more:")
print(" ā make a stronger correction")
go()
print(" The key idea is:")
print("")
print(" The size of the response")
print(" matches the size of the problem.")
go()
reposition()
go("Ready")
motors.enable(True)
start = time.time()
while time.time() - start < 3:
s = read_sensors()
leds.ls(s)
label = classify(s)
if label == "ON_LINE":
motors.run(LEFT, 40)
motors.run(RIGHT, 40)
elif label == "LEFT_OF_LINE":
motors.run(LEFT, 20)
motors.run(RIGHT, 50)
elif label == "RIGHT_OF_LINE":
motors.run(LEFT, 50)
motors.run(RIGHT, 20)
else:
stop()
print(f"{s} --> {label}")
time.sleep(0.1)
stop()
motors.enable(False)
print("")
print(" OBSERVE:")
print(" ā Smooth forward motion")
print(" ā Small, controlled corrections")
print(" ā Stable tracking")
go()
ask(
"Did the robot adjust its movement based on position? (yes/no)",
"yes"
)
# ================================================================
# ROUND 3 ā AGGRESSIVE
# ================================================================
banner("ROUND 3: AGGRESSIVE POLICY")
print(" Now we keep the SAME sensors...")
print(" but change ONLY the policy.")
go()
print(" This time, the robot reacts too strongly.")
print("")
print(" Small errors cause large movements.")
go()
print(" The result:")
print("")
print(" Too much correction")
print(" Overshooting")
print(" Unstable behavior")
go()
reposition()
go("Ready")
motors.enable(True)
start = time.time()
while time.time() - start < 3:
s = read_sensors()
leds.ls(s)
label = classify(s)
if label == "ON_LINE":
motors.run(LEFT, 50)
motors.run(RIGHT, 50)
elif label == "LEFT_OF_LINE":
motors.run(LEFT, -100)
motors.run(RIGHT, 100)
elif label == "RIGHT_OF_LINE":
motors.run(LEFT, 100)
motors.run(RIGHT, -100)
else:
motors.run(LEFT, 100)
motors.run(RIGHT, -100)
print(f"{s} --> {label}")
time.sleep(0.1)
stop()
motors.enable(False)
print("")
print(" OBSERVE:")
print(" ā Sudden, extreme motion")
print(" ā Overcorrection")
print(" ā Instability")
go()
ask(
"Did stronger reactions improve performance? (yes/no)",
"no"
)
# ================================================================
# DISCUSSION
# ================================================================
banner("DISCUSSION")
print(" Across all three rounds:")
print("")
print(" The sensors stayed the same.")
print(" The threshold stayed the same.")
go()
print(" Only the POLICY changed.")
print("")
print(" Yet the behavior changed dramatically.")
go()
ask(
"What caused the change in behavior?",
"policy"
)
print(" This leads to a key idea in AI systems:")
print("")
print(" Good sensing + bad policy ā poor behavior")
print(" Good sensing + good policy ā effective behavior")
go()
# ================================================================
# CHECKPOINT
# ================================================================
gate(
"A rule that maps sensor input to action is called a ___?",
"policy"
)
CS4AL Exercise 3 - training / testing
# ================================================================
# AI IN K-12 STEM ā CodeBot Workshop
# SECTION 3 OF 5: Learning from Data (DRIVE + COLLECT)
# ================================================================
from botcore import *
import time
# ================================================================
# HELPERS
# ================================================================
def banner(title):
print("\n" + "="*58)
print(" " + title)
print("="*58 + "\n")
def go(label=""):
input(f" {label if label else 'Press Enter to continue...'}")
print("")
def gate(question, correct_answer):
print("\n" + "~"*58)
print(" CHECKPOINT")
print("~"*58)
print(f" {question}\n")
while True:
ans = input(" Your answer: ").strip().lower()
if ans == correct_answer:
print("\n CORRECT! Section 3 complete.\n")
break
else:
print(" Try again.")
def stop():
motors.run(LEFT, 0)
motors.run(RIGHT, 0)
# ================================================================
# LECTURE
# ================================================================
banner("SECTION 3 of 5: Let the Data Decide")
print(" So far:")
print(" - YOU set the threshold")
print(" - YOU chose the policy")
print("")
print(" That is RULE-BASED AI.")
go()
print(" MACHINE LEARNING is different.")
print("")
print(" Instead of writing rules,")
print(" we give the system EXAMPLES.")
go()
print(" Each example has:")
print(" - INPUT (sensor values)")
print(" - LABEL (correct answer)")
print("")
print(" Example:")
print(" Sensors ā 'line'")
print(" Sensors ā 'floor'")
go()
print(" The system learns a rule from data.")
print("")
print(" This is called TRAINING.")
go()
# ================================================================
# HANDS-ON (DRIVING + DATA COLLECTION)
# ================================================================
banner("HANDS-ON: Drive and Collect Data")
line_vals = []
floor_vals = []
print(" You will now DRIVE the robot and collect data.")
print("")
print(" Controls:")
print(" w = forward")
print(" a = left")
print(" d = right")
print(" s = stop")
print("")
print(" l = record LINE sample")
print(" f = record FLOOR sample")
print(" q = finish collection")
go()
motors.enable(True)
while True:
cmd = input("Command (w/a/s/d/l/f/q): ").strip().lower()
# ---- driving ----
if cmd == "w":
motors.run(LEFT, 50)
motors.run(RIGHT, 50)
elif cmd == "a":
motors.run(LEFT, 0)
motors.run(RIGHT, 50)
elif cmd == "d":
motors.run(LEFT, 50)
motors.run(RIGHT, 0)
elif cmd == "s":
stop()
# ---- labeling ----
elif cmd == "l":
v = ls.read(2)
line_vals.append(v)
print(f" LINE sample recorded: {v}")
elif cmd == "f":
v = ls.read(2)
floor_vals.append(v)
print(f" FLOOR sample recorded: {v}")
elif cmd == "q":
break
else:
print(" Invalid command")
stop()
motors.enable(False)
go("Data collection complete")
# ================================================================
# TRAIN MODEL
# ================================================================
print(" TRAINING MODEL...")
if len(line_vals) == 0 or len(floor_vals) == 0:
print(" ERROR: Need both LINE and FLOOR samples.")
print(" Restart and collect both types.")
exit()
threshold = (sum(line_vals)/len(line_vals) + sum(floor_vals)/len(floor_vals)) / 2
print(f" Learned threshold: {int(threshold)}")
go()
# ================================================================
# TEST MODEL
# ================================================================
print(" Testing learned model...\n")
motors.enable(True)
start = time.time()
while time.time() - start < 8:
v = ls.read(2)
if v > threshold:
motors.run(LEFT, 50)
motors.run(RIGHT, 50)
else:
stop()
print(f" Sensor: {v}")
stop()
motors.enable(False)
go("Observe behavior")
# ================================================================
# DISCUSSION
# ================================================================
print(" DISCUSSION:")
print("")
print(" Did you manually set the threshold?")
print(" No ā the DATA determined it.")
print("")
print(" That is machine learning.")
go()
print(" Key idea:")
print(" Rules are NOT written.")
print(" They are LEARNED from examples.")
go()
# ================================================================
# CHECKPOINT
# ================================================================
gate(
"Examples used to train a model are called the ___ set?",
"training"
)