FAST-NUCES · Spring 2026 · CS-4090

Object Classification from Robotic Sensor Features

My Machine Learning for Robotics assignment from Spring 2026. K-Nearest Neighbours and Gaussian Naive Bayes classify four object types from five synthetic sensor readings.

500Synthetic rows
4Object classes
99%KNN (k=3) test accuracy
100%GaussianNB test accuracy

Overview

CS-4090 Machine Learning for Robotics, Assignment 4, Spring 2026, FAST-NUCES, with Dr. Maryam Iqbal. The brief asked for a classification system a robot could use to tell objects apart from sensor readings, using KNN and Naive Bayes.

Dataset

500 synthetic sensor rows

125 rows each of metal can, plastic bottle, wooden block and rubber ball. Five features: distance, size, colour intensity, weight and sensor voltage. Each class has its own ranges. Seed 42, no missing values.

Models

KNN and GaussianNB

Features are scaled on the training set only. A 100-row stratified hold-out is used for the numbers below. After a small grid search both tuned models reach 100% on that test set.

Course

CS-4090 · Spring 2026

FAST-NUCES, roll 22I-2327. The assignment brief is in assignment/assignment-04.pdf. The write-up I submitted is in report/report.pdf.

The submitted report recorded 31% for both models. That run had trained on a first version of the table where labels were assigned at random, so the features had no relationship with the class. The scripts and notebook here train on the class-conditional table. The accuracies are high because weight and voltage already separate the four classes fairly cleanly — this is a clean synthetic table, not a claim about real sensors.

Dataset

The table lives in data/synthetic_robotic_dataset.csv and can be rebuilt with the same seed from src/generate_dataset.py.

Feature ranges by class
ClassDistanceSizeColour intensityWeightSensor voltage
metal_can15–908–20120–220250–5003.0–5.0
plastic_bottle15–9018–4060–18030–1500.5–2.0
wooden_block15–9010–3040–130150–3201.5–3.2
rubber_ball15–906–18150–25580–2201.0–2.8

Feature means on the generated table. Distance is almost the same for every class; weight and voltage are the strongest separators. Wooden block and rubber ball overlap more than the others, which is where the one default-KNN mistake happened.

ClassDistanceSizeColourWeightVoltage
metal_can51.5614.20169.66380.924.05
plastic_bottle49.9529.04123.4788.771.25
rubber_ball52.2912.04203.02151.991.86
wooden_block51.9420.5184.52237.702.35

Models and Results

80/20 split, stratified, seed 42: 400 training rows and 100 test rows, 25 per class. StandardScaler is fitted on the training set only. Class names follow LabelEncoder order: metal_can, plastic_bottle, rubber_ball, wooden_block.

ModelTest accuracyNotes
KNN (k=3)99%One wooden block predicted as rubber_ball
GaussianNB100%All 100 test rows correct

KNN (k=3) per class

ClassPrecisionRecallF1
metal_can1.00001.00001.0000
plastic_bottle1.00001.00001.0000
rubber_ball0.96151.00000.9804
wooden_block1.00000.96000.9796

After grid search

ModelBest parameters5-fold CVTest accuracy
KNNmetric=manhattan, k=11, weights=distance99.25%100%
GaussianNBvar_smoothing=1e-0999.75%100%

On this test set the two tuned models tie at 100%. GaussianNB was already perfect with its defaults; the search kept the default smoothing. I would pick GaussianNB if I had to choose one here — it is already at 100% without extra knobs, and it is cheaper to evaluate than an 11-neighbour KNN.

src/predict.py applies the scaler that was fitted at training time. The fitted models, scaler and label encoder are written together to models/object_classifiers.joblib when you run src/train.py. That file is left out of git on purpose — it is rebuilt in a few seconds.

Folder Structure

Folder structure
Object_Classification/
├── assignment/
│   └── assignment-04.pdf
├── report/
│   ├── report.pdf
│   ├── report.docx
│   └── notebook-colab-export.pdf
├── notebooks/
│   └── object_classification.ipynb
├── src/
│   ├── generate_dataset.py
│   ├── train.py
│   └── predict.py
├── data/
│   └── synthetic_robotic_dataset.csv
├── models/                         # created by src/train.py
├── screenshots/
├── assets/
│   ├── css/
│   │   └── style.css
│   └── js/
│       └── main.js
├── index.html
├── README.md
├── requirements.txt
├── LICENSE
├── .gitignore
├── .gitattributes
└── .nojekyll

How to Run

Python 3.10 or later. From the repository root:

Setup and train
python -m venv .venv
# Windows:  .venv\Scripts\activate
# Linux / macOS:  source .venv/bin/activate
pip install -r requirements.txt
python src/generate_dataset.py
python src/train.py
python src/predict.py --distance 40 --size 12 --color 180 --weight 400 --voltage 4.2 --model knn

generate_dataset.py is optional if you just want to reuse the CSV already in data/. train.py will also generate the table if the CSV is missing. The notebook is notebooks/object_classification.ipynb — it looks for src/ and data/ from the repository root or from notebooks/.

To preview this page locally, open index.html or serve the folder with python -m http.server 8080 and visit http://localhost:8080.

License

The code I wrote is under the MIT License. The assignment brief in assignment/ and the report in report/ remain course material and are included as I received or submitted them.