# Project export: ThoughtWheels

This document was generated by HackStack to give an AI agent context about a hackathon project. Sections are labeled with their provenance; content marked as truncated was cut to keep this document small.

## Project metadata

- Hackathon: TreeHacks 2024
- Tagline: Now, you can control your wheelchair with your mind!
- Devpost: https://devpost.com/software/thougthwheels
- GitHub: https://github.com/Jasonnyang/TreeHacks
- Video: https://www.youtube.com/embed/gvdP0iAJSX8?enablejsapi=1&hl=en_US&rel=0&start=&version=3&wmode=transparent
- Team: 2 GitHub contributor(s) — Jason Yang (1 commits), kushalk173-sc (1 commits)

## Devpost submission (written by the team)

### Overview

The rise of Artificial Intelligence and technological advancements has significantly boosted productivity for many. However, there's a noticeable gap in applying these innovations to aid the disabled and elderly. Addressing this, we created a transformative solution: a smart wheelchair prototype. This wheelchair is uniquely controlled by EEG signals, enabling mobility for those with paralysis, and offering older individuals the ability to move freely and continue their daily tasks without physical constraints. This project isn't just about mobility; it's about restoring independence and quality of life. Challenges Faced One of the primary technical challenges we faced was developing a reliable method for interpreting EEG signals into precise commands for the wheelchair. Capturing the electrical activity of the brain with the Muse 2 headset and translating it into actionable inputs required sophisticated signal processing algorithms. We had to ensure the system could accurately differentiate between intentional commands and involuntary brain activity. Additionally, integrating this technology into a wheelchair in a way that was both safe and effective presented its own set of engineering hurdles, including optimizing the system to reduce latency as much as possible. Our Mission Our mission extends beyond mobility. We aim to harness EEG signals as a bridge between machines and the human body. With additional funding, we plan to expand our technology to monitor stress levels and other vital metrics, utilizing EEG data to enhance mental health. This innovation will empower individuals to understand and manage their stress, paving the way for a healthier, more connected future. Technology and Innovation To achieve our goals, we utilized the Muse 2 headset to record EEG signals. This innovative approach allowed us to capture the electrical activity of the brain with precision. We then developed a system to interpret these signals, focusing on microaggressions like blinks and slight head movements, as inputs to control the movement of the wheelchair. This method of control is not only intuitive but also enables users with severe mobility restrictions to command the wheelchair effortlessly, showcasing our commitment to enhancing accessibility and independence through technology. Our smart wheelchair prototype is more than a mobility aid; it's a step towards a future where technology bridges the gap between disability and independence, enabling everyone to live their lives to the fullest. We believe in the power of innovation to change lives, and with the right support, we can make this vision a reality.

## README (from the GitHub repository)

TreeHacks

## Detected evidence (automated analysis)

Indexed codebase: 3 recognized source files, 12 KB.
- Python (language) — detected in the code

## Codebase structure (from repository index)

### Files (5 of 5)

```
muse.py
README.md
Waves.ipynb
Wheelchair.ino
wheelchairConnect.py
```

### Dependencies

No dependency index available.

### Recent commits (newest first)

- Create muse.py
- Added new motors
- Added arduino button
- Add files via upload
- Updated Readme
- Arduino Code

## Key source files (fetched from GitHub, selected and truncated for size)

### wheelchairConnect.py

```python
import serial
import time

# Establish a serial connection (Adjust 'COM3' and baud rate as needed)
ser = serial.Serial('COM3', 9600, timeout=1)
time.sleep(2)  # Wait for the connection to establish

def send_command(command):
    ser.write((command + '\n').encode())  # Send the command to the Arduino, add newline as a delimiter
    time.sleep(1)  # Wait for the Arduino to process the command

# Example usage based on user input
while True:
    cmd = input("Enter command (up, down, left, right): ")
    send_command(cmd)
    if cmd == "quit":
        break

ser.close()  # Close the serial connection when done

```

### muse.py

```python
# -*- coding: utf-8 -*-
"""
Estimate Relaxation from Band Powers

This example shows how to buffer, epoch, and transform EEG data from a single
electrode into values for each of the classic frequencies (e.g. alpha, beta, theta)
Furthermore, it shows how ratios of the band powers can be used to estimate
mental state for neurofeedback.

The neurofeedback protocols described here are inspired by
*Neurofeedback: A Comprehensive Review on System Design, Methodology and Clinical Applications* by Marzbani et. al

Adapted from https://github.com/NeuroTechX/bci-workshop
"""

import numpy as np  # Module that simplifies computations on matrices
import matplotlib.pyplot as plt  # Module used for plotting
from pylsl import StreamInlet, resolve_byprop  # Module to receive EEG data

import os
import sys
from tempfile import gettempdir
from subprocess import call

import matplotlib.pyplot as plt
import numpy as np
from sklearn import svm
from scipy.signal import butter, lfilter, lfilter_zi


NOTCH_B, NOTCH_A = butter(4, np.array([55, 65]) / (256 / 2), btype='bandstop')


def epoch(data, samples_epoch, samples_overlap=0):
    """Extract epochs from a time series.

    Given a 2D array of the shape [n_samples, n_channels]
    Creates a 3D array of the shape [wlength_samples, n_channels, n_epochs]

    Args:
        data (numpy.ndarray or list of lists): data [n_samples, n_channels]
        samples_epoch (int): window length in samples
        samples_overlap (int): Overlap between windows in samples

    Returns:
        (numpy.ndarray): epoched data of shape
    """

    if isinstance(data, list):
        data = np.array(data)

    n_samples, n_channels = data.shape

    samples_shift = samples_epoch - samples_overlap

    n_epochs = int(
        np.floor((n_samples - samples_epoch) / float(samples_shift)) + 1)

    # Markers indicate where the epoch starts, and the epoch contains samples_epoch rows
    markers = np.asarray(range(0, n_epochs + 1)) * samples_shift
    markers = markers.astype(int)

    # Divide data in epochs
    epochs = np.zeros((samples_epoch, n_channels, n_epochs))

    for i in range(0, n_epochs):
        epochs[:, :, i] = data[markers[i]:markers[i] + samples_epoch, :]

    return epochs


def compute_band_powers(eegdata, fs):
    """Extract the features (band powers) from the EEG.

    Args:
        eegdata (numpy.ndarray): array of dimension [number of samples,
                number of channels]
        fs (float): sampling frequency of eegdata

    Returns:
        (numpy.ndarray): feature matrix of shape [number of feature points,
            number of different features]
    """
    # 1. Compute the PSD
    winSampleLength, nbCh = eegdata.shape

    # Apply Hamming window
    w = np.hamming(winSampleLength)
    dataWinCentered = eegdata - np.mean(eegdata, axis=0)  # Remove offset
    dataWinCenteredHam = (dataWinCentered.T * w).T

    NFFT = nextpow2(winSampleLength)
    Y = np.fft.fft(dataWinCenteredHam, n=NFFT, axis=0) / winSampleLength
    PSD = 2 * np.abs(Y[0:int(NFFT / 2), :])
    f = fs / 2 * np.linspace(0, 1, int(NFFT / 2))

    # SPECTRAL FEATURES
    # Average of band powers
    # Delta <4
    ind_delta, = np.where(f < 4)
    meanDelta = np.mean(PSD[ind_delta, :], axis=0)
    # Theta 4-8
    ind_theta, = np.where((f >= 4) & (f <= 8))
    meanTheta = np.mean(PSD[ind_theta, :], axis=0)
    # Alpha 8-12
    ind_alpha, = np.where((f >= 8) & (f <= 12))
    meanAlpha = np.mean(PSD[ind_alpha, :], axis=0)
    # Beta 12-30
    ind_beta, = np.where((f >= 12) & (f < 30))
    meanBeta = np.mean(PSD[ind_beta, :], axis=0)

    feature_vector = np.concatenate((meanDelta, meanTheta, meanAlpha,
                                     meanBeta), axis=0)

    feature_vector = np.log10(feature_vector)

    return feature_vector


def nextpow2(i):
    """
    Find the next power of 2 for number i
    """
    n = 1
    while n < i:
        n *= 2
    return n


def compute_feature_matrix(epochs, fs):
    """
    Call compute_feature_vector for each EEG epoch
    """
    n_epochs = epochs.shape[2]

    for i_epoch in range(n_epochs):
        if i_epoch == 0:
            feat = compute_band_powers(epochs[:, :, i_epoch], fs).T
            # Initialize feature_matrix
            feature_matrix = np.zeros((n_epochs, feat.shape[0]))

        feature_matrix[i_epoch, :] = compute_band_powers(
            epochs[:, :, i_epoch], fs).T

    return feature_matrix


def get_feature_names(ch_names):
    """Generate the name of the features.

    Args:
        ch_names (list): electrode names

    Returns:
        (list): feature names
    """
    bands = ['delta', 'theta', 'alpha', 'beta']

    feat_names = []
    for band in bands:
        for ch in range(len(ch_names)):
            feat_names.append(band + '-' + ch_names[ch])

    return feat_names


def update_buffer(data_buffer, new_data, notch=False, filter_state=None):
    """
    Concatenates "new_data" into "data_buffer", and returns an array with
    the same size as "data_buffer"
    """
    if new_data.ndim == 1:
        new_data = new_data.reshape(-1, data_buffer.shape[1])

    if notch:
        if filter_state is None:
            filter_state = np.tile(lfilter_zi(NOTCH_B, NOTCH_A),
                                   (data_buffer.shape[1], 1)).T
        new_data, filter_state = lfilter(NOTCH_B, NOTCH_A, new_data, axis=0,
                                         zi=filter_state)

    new_buffer = np.concatenate((data_buffer, new_data), axis=0)
    new_buffer = new_buffer[new_data.shape[0]:, :]

    return new_buffer, filter_state


def get_last_data(data_buffer, newest_samples):
    """
    Obtains from "buffer_array" the "newest samples" (N rows from the
    bottom of the buffer)
    """
    new_buffer = data_buffer[(data_buffer.shape[0] - newest_samples):, :]

    return new_buffer

# Handy little enum to make code more readable


class Band:
    Delta = 0
    Theta = 1
    Alpha = 2
    Beta = 3


""" EXPERIMENTAL PARAMETERS """
# Modify the
[truncated — 5452 more characters]
```