Skip to content

Final Project - Bio-signal Data Collection & Analyzation Tool Kit for Motion Sickness Prediction

In this webpage I present the detail work in my final project - How to build a Bio-signal Data Collection & Analyzation Tool Kit for Motion Sickness Prediction

Final Result

Motion sickness has long been recognized as an important factor affecting a person's car riding experience. In this project, I developed a bio-signal data collection and analysis tool kit that can be further developed for predict the occurrence of motion sickness based on human physiological signals. This device will be used in a car riding environment, anyone who is likely to encounter car-sickness will be a potential user.

Summary Slide:

Video of final system:

Hero shots

The hero shot and video below provide an overview of the system.

The system components before assemble.

Idea Sketch

The figure below has shown the original idea design of this tool set.

This detection tool consists of 5 parts:

  • A set of sensors for collecting human physiological signals includes PPG, SKT etc.
  • A data processor module based on Raspberry Pi with Wifi connection.
  • A trained AI algorithm for data processing which can be deployed on Raspberry Pi.
  • A screen to show motion sickness dose value.
  • A control panel for function selection.

After weeks of learning I have adjusted my final project design and the idea is demonstrated as below. In this new design the Tool kit is divided into two parts: A Raspberry Pi 4B Data Processing Module and A Seeed Studio Xiao ESP32-C3 Biosignal Data Collection Module.

Computer Aided Design

Creating the housing design for the data processing & detection module using Fusion CAD, with a focus on ergonomics and durability.

The 3D design process is document as video below.

You can download my 3D housing design by link below:

Computer Controlled Cutting

Design and cutting the logo sticker with a vinal cutter.

The vinal cutting process is shown as video below.

Find the vinyl cutting design file at link below:

3D Printing

3D print the data processing & detection module housing.

I printed my design using both an FDM and an SLA printer. The SLA printer's curing platform seems to have issues, so the FDM printer produced better results.

Input & Output device testing

Testing the input sensors and output screen.

Here is a video shows me testing different sensors.

Electronic Design & Production

Design the PCB for detection module with Fusion 360.

Manufacture the PCB with Roland and soldering it.

The video below shown the PCB design and manufacture process.

You can download my PCB design by link below:

Embedded Programming for sensors and network communication

Programming and testing the bio-signal detection module, setting up wifi communication between the Raspberry Pi and bio-signal detection module. In the following code the wire was connected as below:

  • MAX30205 (Temperature Sensor): VCC to 3.3V, GND to Ground, SCL to D5, SDA to D4
  • Pulse Sensor: Signal to A1, VCC to 5V, GND to Ground
  • GSR Sensor: Signal to A2, VCC to 5V, GND to Ground
#include <WiFi.h>
#include <Wire.h>
#include <Protocentral_MAX30205.h>
#include <PulseSensorPlayground.h>

// Wi-Fi Server setup
WiFiServer server(1122);

// MAX30205 (Temperature Sensor) and Pulse Sensor setup
MAX30205 tempSensor;
const int PulseWire = A1; // Purple wire connected to GPIO2
const int GSR = A2;       // GSR sensor pin
PulseSensorPlayground pulseSensor;

// Variables for sensor data
float temperature = 0.0;
int gsrValue = 0;
int bpmRaw = 0;

// Wi-Fi initialization
void Wifi_TCP_Init() {
  WiFi.softAP("ESP32-Sensors"); // Access Point name
  server.begin();               // Start server
  Serial.begin(115200);         // Initialize serial communication
  Serial.println("WiFi server started!");
}

// Read sensor data
void readSensorData() {
  // Temperature from MAX30205
  temperature = tempSensor.getTemperature();

  // GSR sensor average value
  long sum = 0;
  for (int i = 0; i < 10; i++) { // Average 10 measurements to remove glitches
    sum += analogRead(GSR);
    delay(10);
  }
  gsrValue = sum / 10;

  // Pulse sensor raw signal
  bpmRaw = analogRead(PulseWire);
}

// Send sensor data to connected clients
void Wifi_TCP_Send() {
  WiFiClient client = server.available(); // Check if a client is connected
  if (client) {
    Serial.println("Client connected!");

    while (client.connected()) { // Keep sending data while the client is connected
      readSensorData(); // Read the latest sensor data

      // Format and send the sensor data
      client.print("temp:");
      client.print(temperature, 2); // Send temperature with 2 decimal places
      client.print(",gsr:");
      client.print(gsrValue);
      client.print(",bpmraw:");
      client.print(bpmRaw);
      client.print("\n"); // Add newline for easy parsing

      // Debug output for the Serial Monitor
      Serial.println("Data sent to client:");
      Serial.print("Temperature: "); Serial.println(temperature, 2);
      Serial.print("GSR Value: "); Serial.println(gsrValue);
      Serial.print("Pulse Sensor Raw: "); Serial.println(bpmRaw);

      delay(10); // Send time
    }

    Serial.println("Client disconnected.");
    client.stop(); // Stop the client when it disconnects
  }
}

void setup() {
  Wire.begin();             // Initialize I2C communication
  tempSensor.begin();       // Initialize the temperature sensor
  pulseSensor.analogInput(PulseWire); // Initialize the pulse sensor
  Wifi_TCP_Init();          // Initialize Wi-Fi and server
}

void loop() {
  Wifi_TCP_Send();          // Continuously send sensor data over Wi-Fi
}

Interface and application programming

Developing the data processing program and deploy it on the Raspberry Pi. Then build a user interface to display analyzed bio-signal data, making results accessible for end users. The interfaces looks like figures below:

import tkinter as tk
import socket
import threading
import time

# Initialize GUI application
root = tk.Tk()
root.title("Motion Sickness Prediction")
root.geometry("800x480")
root.configure(bg="white")

# Add title labels
title_label = tk.Label(
    root,
    text="Have A Happy Journey",
    font=("Arial", 24, "bold"),
    bg="white",
    fg="black"
)
title_label.pack(pady=(20, 5))

subtitle_label = tk.Label(
    root,
    text="Motion Sickness Monitoring",
    font=("Arial", 16),
    bg="white",
    fg="black"
)
subtitle_label.pack(pady=(0, 20))

# Add alert label
alert_label = tk.Label(
    root,
    text="Low motion sickness possibility.",
    font=("Arial", 18, "bold"),
    bg="green",
    fg="white",
    padx=20,
    pady=10
)
alert_label.pack(pady=(50, 20))

# Function to update alert based on conditions
def update_alert(delta_temp, delta_gsr, delta_bpmraw):
    if delta_temp < 0 and delta_gsr > 0 and delta_bpmraw > 0:
        alert_label.config(
            text="High motion sickness possibility, Please take a rest!",
            bg="red",
            fg="white"
        )
    else:
        alert_label.config(
            text="Low motion sickness possibility.",
            bg="green",
            fg="white"
        )

# Function to handle data receiving and processing
def receive_data():
    tcp_client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    tcp_client_socket.connect(("192.168.4.1", 1122))

    buffer = ""
    temp_values = []
    gsr_values = []
    bpmraw_values = []

    first_interval = True
    averages_first = {"temp": 0, "gsr": 0, "bpmraw": 0}
    start_time = time.time()

    try:
        while True:
            recv_data = tcp_client_socket.recv(1024).decode("utf-8")
            buffer += recv_data

            while '\n' in buffer:
                message_end = buffer.index('\n')
                message = buffer[:message_end]
                buffer = buffer[message_end + 1:]

                try:
                    if "temp:" in message and "gsr:" in message and "bpmraw:" in message:
                        temp_start = message.index("temp:") + 5
                        temp_end = message.index(",gsr:")
                        temperature = float(message[temp_start:temp_end].strip())

                        gsr_start = message.index("gsr:") + 4
                        gsr_end = message.index(",bpmraw:")
                        gsr = int(message[gsr_start:gsr_end].strip())

                        bpmraw_start = message.index("bpmraw:") + 7
                        bpmraw = int(message[bpmraw_start:].strip())

                        temp_values.append(temperature)
                        gsr_values.append(gsr)
                        bpmraw_values.append(bpmraw)

                        if time.time() - start_time >= 5:
                            avg_temp = sum(temp_values) / len(temp_values) if temp_values else 0
                            avg_gsr = sum(gsr_values) / len(gsr_values) if gsr_values else 0
                            avg_bpmraw = sum(bpmraw_values) / len(bpmraw_values) if bpmraw_values else 0

                            if first_interval:
                                averages_first["temp"] = avg_temp
                                averages_first["gsr"] = avg_gsr
                                averages_first["bpmraw"] = avg_bpmraw
                                first_interval = False
                            else:
                                delta_temp = avg_temp - averages_first["temp"]
                                delta_gsr = avg_gsr - averages_first["gsr"]
                                delta_bpmraw = avg_bpmraw - averages_first["bpmraw"]

                                update_alert(delta_temp, delta_gsr, delta_bpmraw)

                                first_interval = True

                            temp_values.clear()
                            gsr_values.clear()
                            bpmraw_values.clear()
                            start_time = time.time()
                except Exception as e:
                    print(f"Parsing Error: {e}")
    except KeyboardInterrupt:
        print("Exiting...")
    finally:
        tcp_client_socket.close()

# Start the data receiving thread
threading.Thread(target=receive_data, daemon=True).start()

# Run the Tkinter event loop
root.mainloop()

The motion sickness prediction logic is very simple and it is based on the research: "Koohestani, A., Nahavandi, D., Asadi, H., Kebria, P. M., Khosravi, A., Alizadehsani, R., & Nahavandi, S. (2019). A knowledge discovery in motion sickness: a comprehensive literature review. IEEE access, 7, 85755-85770."

The motion sickness prediction algorithm in this code evaluates changes in three physiological parameters: temperature, GSR, and pulse. If the temperature decreases (delta_temp < 0) while both GSR and pulse increase (delta_gsr > 0 and delta_bpmraw > 0), the system flags a high risk of motion sickness with a red alert: "High motion sickness possibility, Please take a rest!". In all other cases, it displays a green alert: "Low motion sickness possibility."

Generate a standalone executable .exe file

To convert your Python script into a standalone .exe file, follow these steps:

  1. Install PyInstaller First, install PyInstaller using pip. Open your terminal or command prompt and run the following command:
pip install pyinstaller
  1. Prepare the Script Make sure your Python script (e.g., RealtimeMSpredict.py) is working properly.

  2. Create the Executable Navigate to the folder containing your Python script using the terminal or command prompt. Then, run the following command:

pyinstaller --onefile --windowed RealtimeMSpredict.py
  • --onefile: This option bundles everything into a single executable file.
  • --windowed: This prevents a terminal window from appearing when you run the application (useful for GUI apps).
  1. Find & Test the Executable After PyInstaller finishes building the executable, you can find it in the dist/ folder inside your project directory. Navigate to the dist/ folder and run the .exe file.

Note for Distributing the PyInstaller Executable

When need to copy .exe file generated by PyInstaller, consider the following:

  1. dist/ Folder

    • This folder contains the final .exe file.
    • What to copy: Only the .exe file from this folder.
    • Why: This is the standalone executable that can be run on another machine without needing Python installed.
  2. build/ Folder

    • This folder contains intermediate files used during the build process.
    • What to copy: Nothing from this folder is needed for distribution.
    • Why: It is not necessary to distribute this folder to the end user.
  3. <YourScriptName>.spec File

    • This is a configuration file created by PyInstaller.
    • What to copy: This file is not required for distribution.
    • Why: It's used for rebuilding the executable, not needed for running the app.
  4. Additional Files or Folders

    • If your application uses external files (like images, configuration files, etc.), ensure these are included.
    • What to copy: External files such as images, configuration files, etc., if they are used by your app.
    • Why: These files are necessary for the app to function properly, but they are not bundled automatically unless you specify them with the --add-data option.
  5. DLL or Dependency Files (in some cases)

    • PyInstaller bundles most dependencies, but sometimes external DLLs or libraries are needed (especially for non-Python dependencies).
    • What to copy: Any additional DLLs or dependencies that are missing when running the .exe on another machine.
    • Why: These files may be required if PyInstaller missed bundling them automatically.

Assemble & test.

See assemble and test result.

Material BOM List

The purchase list involved in this project is shown as table below.

Questions Answered

  1. What does it do?
    The project is a motion sickness detection toolkit that collects and analyzes human physiological signals (such as motion, PPG, and skin temperature) to help predict the onset of motion sickness. It is intended for use in a car riding environment to assist individuals prone to motion sickness.

  2. Who’s done what beforehand?
    Previous research has focused on understanding motion sickness and detecting it through biosignal analysis. There are some existing tools and devices for general health monitoring, but they are often not specialized for real-time motion sickness prediction, especially in vehicle settings.

  3. What did you design?
    The project involved designing:

  • A sensor system to collect relevant physiological signals.
  • A data processing module based on a Raspberry Pi to manage and process the collected data.
  • A user interface with a screen to display the motion sickness dose value.
  • A housing to securely contain the biosignal detection module, with branding elements like a logo sticker.
  1. What materials and components were used?
    Materials and components included:
  • Sensors: for measuring motion, PPG (photoplethysmography), and skin temperature.
  • Raspberry Pi: for data processing and Wifi communication.
  • Screen: to display motion sickness values.
  • Housing materials: for the 3D-printed casing.
  • Miscellaneous electronics: for sensor connections and PCB production.
  1. Where did they come from?
    These components were sourced from various electronics suppliers, such as online electronic stores (e.g., Adafruit, SparkFun), 3D printing services or local labs, and general hardware suppliers for materials like acrylic sheets for cutting.

  2. How much did they cost?
    The costs is approximately 3000 CNY including materials and fabrication cost.

  3. What parts and systems were made?

  • The sensor system for data collection.
  • The data processing and communication system on the Raspberry Pi.
  • The display interface showing motion sickness values.
  • The housing and logo sticker for branding and protection.
  1. What processes were used?
    Key processes included:
  • Embedded programming for sensor data processing.
  • Electronic design and assembly for integrating sensors and the Raspberry Pi.
  • 3D printing for the housing.
  • Computer-controlled cutting for the logo sticker.
  • Interface programming to display processed data on the screen.
  1. What questions were answered?
    The project answered:
  • Can physiological signals reliably predict motion sickness?
  • Is it feasible to build a compact, portable toolkit for real-time use in a car?
  • What combination of sensors and processing techniques provides the most accurate detection?
  1. What worked? What didn’t?
    Worked: Data collection from sensors, basic data processing, Wifi communication, and initial testing in a controlled environment.
    Didn’t work: The original plan was to use AI models deployed on raspberry-pi to predict the motion sickness, this was not realized since the time is limited.

  2. How was it evaluated?
    The toolkit was evaluated through:

  • Controlled testing of sensor accuracy and data processing.
  • User feedback on the display interface and usability.
  • Performance tests in a car environment to assess real-world reliability.
  1. What are the implications?
    This toolkit could advance real-time motion sickness detection technology in automotive settings, making it useful for both passengers and automotive companies. It opens up possibilities for developing more personalized, adaptive vehicle environments and for future studies on motion sickness mitigation techniques.