auto ip configuration
This commit is contained in:
189
README.md
189
README.md
@ -68,124 +68,133 @@ pip install -r requirements.txt
|
|||||||
|
|
||||||
### Experiment Scripts Overview
|
### Experiment Scripts Overview
|
||||||
|
|
||||||
- **app.py**: Web interface for controlling the experiment and word list
|
- **app.py**: Main server - web interface, experiment control, and automatic tracking recorder
|
||||||
- **server.py**: UDP relay server for communication between VR clients
|
- **server.py**: UDP relay for communication between VR clients (auto-started by app.py)
|
||||||
- **control.py**: Command-line tool for setting VR client modes and server IP
|
- **index.html**: Web UI for configuring and running experiments
|
||||||
- **index.html**: Web UI served by app.py
|
- **static/**: Frontend assets (CSS, JavaScript, player display)
|
||||||
- **word-list.txt**: Default list of charade words
|
- **data/**: Word lists (English and German)
|
||||||
|
|
||||||
### Setup Instructions
|
### Setup Instructions
|
||||||
|
|
||||||
#### 1. Network Setup
|
#### 1. Network Setup
|
||||||
- Connect all VR headsets to the same network as the server
|
- Connect all VR headsets to the same network as your computer
|
||||||
- Note the IP addresses of the VR headsets (you'll need these for configuration)
|
- Note the IP addresses of both VR headsets
|
||||||
- **Important**: Update `experiment-scripts/server.py` lines 19-20 with your actual VR headset IPs
|
- Note your computer's IP address (the server IP)
|
||||||
```python
|
|
||||||
# Replace these example IPs with your actual headset IPs
|
|
||||||
DEVICE1_ADDR = ("YOUR_PLAYER1_IP", 5001) # e.g., ("192.168.1.100", 5001)
|
|
||||||
DEVICE2_ADDR = ("YOUR_PLAYER2_IP", 5001) # e.g., ("192.168.1.101", 5001)
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 2. Start the Relay Server
|
To find your server IP:
|
||||||
```bash
|
```bash
|
||||||
cd experiment-scripts
|
# Linux/Mac
|
||||||
python server.py
|
hostname -I
|
||||||
|
|
||||||
|
# Windows
|
||||||
|
ipconfig
|
||||||
```
|
```
|
||||||
|
|
||||||
#### 3. Start the Web Interface
|
**Note**: The UDP relay automatically detects and forwards data between connected VR headsets - no manual IP configuration needed in server.py!
|
||||||
|
|
||||||
|
#### 2. Start the Server
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd experiment-scripts
|
cd experiment-scripts
|
||||||
python -m fastapi dev app.py
|
fastapi dev app.py
|
||||||
```
|
|
||||||
Then navigate to `http://localhost:8000`
|
|
||||||
|
|
||||||
#### 4. Configure VR Clients
|
|
||||||
|
|
||||||
Tell clients which server to connect to:
|
|
||||||
```bash
|
|
||||||
# Windows PowerShell (replace with your actual VR headset IPs)
|
|
||||||
cd experiment-scripts
|
|
||||||
$env:TARGET_IP="YOUR_PLAYER1_IP,YOUR_PLAYER2_IP" ; python control.py "IP:127.0.0.1"
|
|
||||||
|
|
||||||
# Linux/Mac (replace with your actual VR headset IPs)
|
|
||||||
cd experiment-scripts
|
|
||||||
TARGET_IP="YOUR_PLAYER1_IP,YOUR_PLAYER2_IP" python3 control.py "IP:127.0.0.1"
|
|
||||||
```
|
```
|
||||||
|
|
||||||
#### 5. Set Experiment Condition
|
This single command automatically starts:
|
||||||
|
- Web interface on http://localhost:8000
|
||||||
|
- UDP relay server (server.py)
|
||||||
|
- Tracking data recorder
|
||||||
|
|
||||||
Choose one of these modes:
|
Navigate to `http://localhost:8000` to access the control interface.
|
||||||
```bash
|
|
||||||
# Dynamic Face only
|
|
||||||
$env:TARGET_IP="YOUR_PLAYER1_IP,YOUR_PLAYER2_IP" ; python control.py "MODE:1;1;1;0"
|
|
||||||
|
|
||||||
# Dynamic Hands only
|
|
||||||
$env:TARGET_IP="YOUR_PLAYER1_IP,YOUR_PLAYER2_IP" ; python control.py "MODE:0;0;0;1"
|
|
||||||
|
|
||||||
# Dynamic Hands + Face
|
|
||||||
$env:TARGET_IP="YOUR_PLAYER1_IP,YOUR_PLAYER2_IP" ; python control.py "MODE:1;1;1;1"
|
|
||||||
|
|
||||||
# Static Face only
|
|
||||||
$env:TARGET_IP="YOUR_PLAYER1_IP,YOUR_PLAYER2_IP" ; python control.py "MODE:1;0;0;0"
|
|
||||||
|
|
||||||
# Static Hands only (requires controllers)
|
|
||||||
$env:TARGET_IP="YOUR_PLAYER1_IP,YOUR_PLAYER2_IP" ; python control.py "MODE:0;0;0;1"
|
|
||||||
|
|
||||||
# Static Hands + Face (requires controllers for hands)
|
|
||||||
$env:TARGET_IP="YOUR_PLAYER1_IP,YOUR_PLAYER2_IP" ; python control.py "MODE:1;0;0;1"
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 6. Prepare Word List
|
|
||||||
|
|
||||||
You can shuffle the word list directly in the web interface (see Web Interface Usage section below).
|
|
||||||
|
|
||||||
### Web Interface Usage
|
### Web Interface Usage
|
||||||
|
|
||||||
Once you have the web interface running at `http://localhost:8000`:
|
All experiment configuration and control is done through the web interface at `http://localhost:8000`.
|
||||||
|
|
||||||
#### Setting Up a Charades Session
|
#### 1. Configure VR Headsets
|
||||||
|
|
||||||
1. **Configure Player IPs**
|
In the **VR Headset Configuration** section:
|
||||||
- Enter the IP addresses of Player 1 and Player 2 VR headsets
|
- Enter **Server IP Address**: Your computer's IP address
|
||||||
- These should match the IPs you used in the `control.py` commands
|
- Enter **Player 1 IP Address**: First VR headset IP
|
||||||
|
- Enter **Player 2 IP Address**: Second VR headset IP
|
||||||
|
- Select **Experiment Mode** (see Experiment Conditions below)
|
||||||
|
- Click **"Send Configuration to Headsets"**
|
||||||
|
|
||||||
2. **Prepare Word List**
|
Wait for confirmation that the configuration was sent successfully.
|
||||||
- Copy your word list from `word-list.txt` and paste it into the large text area on the right side
|
|
||||||
- Click the **"Shuffle"** button to randomize the word order
|
|
||||||
- Click the **"Modify"** button to generate interactive word items
|
|
||||||
|
|
||||||
3. **Set Game Parameters**
|
#### 2. Configure Experiment Session
|
||||||
- **Current Player For Acting**: Select which player will be acting out the words
|
|
||||||
- **Time (s)**: Set the time limit for each word (e.g., 30 seconds)
|
|
||||||
- **Last Word Status**: Set to "None" for the first word
|
|
||||||
|
|
||||||
#### Running the Experiment
|
In the **Session Configuration** section:
|
||||||
|
- **Group ID**: Identifier for this session (used in CSV filenames)
|
||||||
|
- **Time per Word**: Duration in seconds for each word (e.g., 30)
|
||||||
|
- **Total Duration**: Total experiment time in minutes (0 = unlimited)
|
||||||
|
|
||||||
**Manual Mode (Individual Words)**
|
In the **Network Configuration** section:
|
||||||
1. Enter a word in the "Word" field
|
- **Active Player**: Select which player will be performing (Player 1 or Player 2)
|
||||||
2. Select the acting player (Player 1 or Player 2)
|
|
||||||
3. Set the time limit
|
|
||||||
4. Click **"Send"** to transmit the word to the VR headsets
|
|
||||||
|
|
||||||
**Automatic Mode (Word List)**
|
#### 3. Prepare Word List
|
||||||
1. After clicking "Modify" with your word list, the interface shows all words with timers
|
|
||||||
2. The system automatically starts with the first word and counts down
|
|
||||||
3. **During the session**:
|
|
||||||
- **Mark words correct**: Hover over a word and check the checkbox if guessed correctly
|
|
||||||
- **Visual indicators**: ▶ = Current word, ✅ = Correct, ❌ = Time expired
|
|
||||||
4. **Stop the session**: Click **"Stop"** to end early
|
|
||||||
|
|
||||||
#### Exporting Results
|
In the **Word List** section:
|
||||||
|
- Copy words from `data/word-list.txt` or enter your own (one word per line)
|
||||||
|
- Click **"Shuffle Words"** to randomize order
|
||||||
|
- Click **"Start Experiment"** when ready
|
||||||
|
|
||||||
1. After completing the word list, click **"Save as CSV"**
|
#### 4. Run the Experiment
|
||||||
2. This downloads a CSV file with word name, correctness, and time remaining
|
|
||||||
|
|
||||||
### Mode Details
|
When you click **"Start Experiment"**:
|
||||||
|
- The system automatically sends words to the active player
|
||||||
|
- Tracking data recording starts automatically
|
||||||
|
- Words advance based on the timer
|
||||||
|
- Check the checkbox next to each word if guessed correctly
|
||||||
|
- Click **"Stop Experiment"** to end early
|
||||||
|
|
||||||
Mode format: `<show_head>;<show_facial_expression>;<show_eye_rotation>;<show_hands>`
|
#### 5. Export Data
|
||||||
|
|
||||||
- **Dynamic modes**: Real-time face/hand tracking
|
After the experiment:
|
||||||
- **Static modes**: Participants use controllers instead of natural movement
|
- **"Save Results (CSV)"**: Downloads word results
|
||||||
|
- Format: `{group_id}_results_{timestamp}.csv`
|
||||||
|
- Contains: word, correct/incorrect, time remaining
|
||||||
|
|
||||||
|
- **"Download Tracking Data (CSV)"**: Downloads tracking data
|
||||||
|
- Format: `{group_id}_tracking_{timestamp}.csv`
|
||||||
|
- Contains: camera and controller positions/rotations at 60Hz
|
||||||
|
- Includes: timestamps, current word, condition, elapsed time
|
||||||
|
|
||||||
|
### Experiment Conditions
|
||||||
|
|
||||||
|
The experiment supports six different conditions that control which body parts are tracked and how:
|
||||||
|
|
||||||
|
| Condition | Description | Settings |
|
||||||
|
|-----------|-------------|----------|
|
||||||
|
| **Dynamic Face** | Real-time face tracking with expressions and eye rotation | 1;1;1;0 |
|
||||||
|
| **Dynamic Hands** | Real-time hand tracking with finger gestures | 0;0;0;1 |
|
||||||
|
| **Dynamic Hands+Face** | Full tracking: face, expressions, eyes, and hands | 1;1;1;1 |
|
||||||
|
| **Static Face** | Head position tracking only (no expressions) | 1;0;0;0 |
|
||||||
|
| **Static Hands** | Controller tracking (no finger tracking) | 0;0;0;1 |
|
||||||
|
| **Static Hands+Face** | Head position + controller tracking | 1;0;0;1 |
|
||||||
|
|
||||||
|
**Mode format**: `<show_head>;<show_facial_expression>;<show_eye_rotation>;<show_hands>`
|
||||||
|
|
||||||
|
**Notes**:
|
||||||
|
- **Dynamic modes**: Use natural face/hand tracking via Quest Pro sensors
|
||||||
|
- **Static modes**: Participants must use controllers for hand input
|
||||||
|
- Select the condition in the web interface before starting the experiment
|
||||||
|
|
||||||
|
### Tracking Data
|
||||||
|
|
||||||
|
The system automatically records tracking data from the **active player** (the one performing charades) at approximately 60Hz:
|
||||||
|
|
||||||
|
**Recorded data**:
|
||||||
|
- Center eye camera position (x, y, z) and rotation (w, x, y, z)
|
||||||
|
- Left hand controller position and rotation
|
||||||
|
- Right hand controller position and rotation
|
||||||
|
- Current word being performed
|
||||||
|
- Timestamps and elapsed time
|
||||||
|
|
||||||
|
**Data recording**:
|
||||||
|
- Starts automatically when experiment starts
|
||||||
|
- Stops automatically when experiment stops
|
||||||
|
- Only records from the active player (not the guesser)
|
||||||
|
- Exports as CSV with group ID and timestamp in filename
|
||||||
|
|
||||||
## Unity Project Details
|
## Unity Project Details
|
||||||
|
|
||||||
|
|||||||
@ -1,23 +1,9 @@
|
|||||||
import socket
|
import socket
|
||||||
import threading
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from time import sleep
|
|
||||||
|
|
||||||
CONTROL_ADDR = ("127.0.0.1", 5001)
|
# Track connected devices dynamically
|
||||||
|
# No manual IP configuration needed!
|
||||||
# TODO: Adjust the following addresses so they match the IP addresses of the
|
connected_devices = {} # {ip: last_seen_timestamp}
|
||||||
# VR headsets.
|
|
||||||
# In our case the IP addresses were:
|
|
||||||
# - for player 1: 10.42.0.38
|
|
||||||
# - for player 2: 10.42.0.72
|
|
||||||
#
|
|
||||||
# The ports are hardcoded to 5001 inside the Unity application, so you
|
|
||||||
# shouldn't change those.
|
|
||||||
#
|
|
||||||
# Note: For this to work the VR headsets must be connected to the same network
|
|
||||||
# as this server.
|
|
||||||
DEVICE1_ADDR = ("10.42.0.38", 5001)
|
|
||||||
DEVICE2_ADDR = ("10.42.0.72", 5001)
|
|
||||||
|
|
||||||
sock_from_A = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
sock_from_A = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||||
sock_from_A.bind(("0.0.0.0", 5000))
|
sock_from_A.bind(("0.0.0.0", 5000))
|
||||||
@ -26,30 +12,36 @@ def forward(source_socket):
|
|||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
data, addr = source_socket.recvfrom(1024 * 16)
|
data, addr = source_socket.recvfrom(1024 * 16)
|
||||||
target_ip = DEVICE1_ADDR[0] if addr[0] == DEVICE2_ADDR[0] else DEVICE2_ADDR[0]
|
source_ip = addr[0]
|
||||||
label = "A→B" if addr == DEVICE1_ADDR else "B→A"
|
timestamp = datetime.now()
|
||||||
if addr != DEVICE1_ADDR and addr != DEVICE2_ADDR:
|
|
||||||
label = f"unknown {addr}"
|
|
||||||
|
|
||||||
# Forward to other player
|
# Register this device
|
||||||
sock_from_A.sendto(data, (target_ip, 5000))
|
connected_devices[source_ip] = timestamp
|
||||||
|
|
||||||
|
# Get list of other active devices (excluding localhost and source)
|
||||||
|
other_devices = [ip for ip in connected_devices.keys()
|
||||||
|
if ip != source_ip and not ip.startswith("127.")]
|
||||||
|
|
||||||
|
# Forward to all other connected devices
|
||||||
|
for target_ip in other_devices:
|
||||||
|
sock_from_A.sendto(data, (target_ip, 5000))
|
||||||
|
|
||||||
# Also forward to app.py tracking listener on port 5002
|
# Also forward to app.py tracking listener on port 5002
|
||||||
# Prepend source IP so app.py can identify which player sent the data
|
# Prepend source IP so app.py can identify which player sent the data
|
||||||
source_ip = addr[0]
|
|
||||||
tagged_data = f"SOURCE_IP:{source_ip}|".encode('utf-8') + data
|
tagged_data = f"SOURCE_IP:{source_ip}|".encode('utf-8') + data
|
||||||
sock_from_A.sendto(tagged_data, ("127.0.0.1", 5002))
|
sock_from_A.sendto(tagged_data, ("127.0.0.1", 5002))
|
||||||
|
|
||||||
timestamp = datetime.now().strftime("%H:%M:%S")
|
|
||||||
|
|
||||||
# Logging
|
# Logging
|
||||||
#if next(counter) % 20 == 0:
|
if len(other_devices) > 0:
|
||||||
# if addr[0] != DEVICE2_ADDR[0]:
|
label = f"{source_ip} → {', '.join(other_devices)}"
|
||||||
# print(f"[{timestamp}] {label}: {data.decode()}")
|
else:
|
||||||
# print('sent to ', (target_ip, 5000))
|
label = f"{source_ip} (no other devices connected)"
|
||||||
|
|
||||||
|
# Uncomment for verbose logging:
|
||||||
|
# print(f"[{timestamp.strftime('%H:%M:%S')}] {label}")
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Fehler {label}: {e}")
|
print(f"Error in relay: {e}")
|
||||||
|
|
||||||
print("UDP Relay läuft. Strg+C zum Beenden.")
|
print("UDP Relay läuft. Strg+C zum Beenden.")
|
||||||
try:
|
try:
|
||||||
|
|||||||
Reference in New Issue
Block a user