diff --git a/README.md b/README.md index d991a2a..b3380df 100644 --- a/README.md +++ b/README.md @@ -202,6 +202,7 @@ src/ Comprehensive documentation available in the `docs/` folder: +- **[Tutorials](docs/tutorials/README.md)**: Step-by-step guides for new users - **[Quick Reference](docs/quick-reference.md)**: Essential commands and setup - **[Implementation Guide](docs/implementation-guide.md)**: Technical implementation details - **[Project Status](docs/project-status.md)**: Current development state diff --git a/docs/README.md b/docs/README.md index e976cde..81a8dcd 100755 --- a/docs/README.md +++ b/docs/README.md @@ -6,11 +6,28 @@ HRIStudio is a web-based Wizard-of-Oz platform for Human-Robot Interaction resea | Document | Description | |----------|-------------| +| **[Tutorials](tutorials/README.md)** | Step-by-step guides for using HRIStudio | | **[Quick Reference](quick-reference.md)** | Essential commands, setup, troubleshooting | | **[Project Status](project-status.md)** | Current development state (March 2026) | | **[Implementation Guide](implementation-guide.md)** | Full technical implementation | | **[NAO6 Integration](nao6-quick-reference.md)** | Robot setup and commands | +## Tutorials + +New to HRIStudio? Start with our comprehensive tutorials: + +| Tutorial | Description | Time | +|----------|-------------|------| +| [Getting Started](tutorials/01-getting-started.md) | Installation and first login | 10 min | +| [Your First Study](tutorials/02-your-first-study.md) | Creating a research study | 15 min | +| [Designing Experiments](tutorials/03-designing-experiments.md) | Building experiment protocols | 25 min | +| [Running Trials](tutorials/04-running-trials.md) | Executing experiments | 20 min | +| [Wizard Interface](tutorials/05-wizard-interface.md) | Real-time trial control | 15 min | +| [Robot Integration](tutorials/06-robot-integration.md) | Connecting NAO6 robot | 20 min | +| [Forms & Surveys](tutorials/07-forms-and-surveys.md) | Managing consent and data | 15 min | +| [Data & Analysis](tutorials/08-data-and-analysis.md) | Collecting and exporting data | 15 min | +| [Simulation Mode](tutorials/09-simulation-mode.md) | Testing without a robot | 10 min | + ## Getting Started ### 1. Clone & Install @@ -162,9 +179,22 @@ bun db:seed - `docs/quick-reference.md` - Commands & setup - `docs/nao6-quick-reference.md` - NAO6 commands +### Tutorials +- `docs/tutorials/README.md` - Tutorial overview +- `docs/tutorials/01-getting-started.md` - Installation & setup +- `docs/tutorials/02-your-first-study.md` - Creating studies +- `docs/tutorials/03-designing-experiments.md` - Building protocols +- `docs/tutorials/04-running-trials.md` - Executing trials +- `docs/tutorials/05-wizard-interface.md` - Trial control +- `docs/tutorials/06-robot-integration.md` - Robot setup +- `docs/tutorials/07-forms-and-surveys.md` - Forms management +- `docs/tutorials/08-data-and-analysis.md` - Data collection +- `docs/tutorials/09-simulation-mode.md` - Testing without robot + ### Technical Documentation - `docs/implementation-guide.md` - Full technical implementation - `docs/project-status.md` - Development status +- `docs/mock-robot-simulation.md` - Robot simulation ### Archive (Historical) - `docs/_archive/` - Old documentation (outdated but preserved) diff --git a/docs/mock-robot-simulation.md b/docs/mock-robot-simulation.md new file mode 100644 index 0000000..3c96c27 --- /dev/null +++ b/docs/mock-robot-simulation.md @@ -0,0 +1,182 @@ +# HRIStudio Mock Robot Simulation + +This directory contains a mock robot server for simulating NAO6 robot connections without a physical robot. + +## Quick Start + +### Option 1: Standalone Mock Server (Recommended for testing) + +```bash +cd scripts/mock-robot +bun install +bun dev +``` + +This starts the mock robot WebSocket server on `ws://localhost:9090`. + +### Option 2: Docker Compose Mock Mode + +```bash +cd nao6-hristudio-integration +docker compose -f docker-compose.yml -f docker-compose.mock.yml --profile mock up -d +``` + +### Option 3: Client-Side Simulation (No server needed) + +Enable simulation mode in the wizard interface: +- Set `NEXT_PUBLIC_SIMULATION_MODE=true` in your `.env` file +- Or use the simulation toggle in the UI + +## Architecture + +``` +┌─────────────────────────────────────────────────────────────┐ +│ HRIStudio Platform │ +├─────────────────────────────────────────────────────────────┤ +│ Wizard Interface (Browser) │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ wizard-ros-service.ts │ │ +│ │ ├── simulationMode: true → Simulates locally │ │ +│ │ └── simulationMode: false → Connects to server │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ │ +│ ┌────────────┴────────────┐ │ +│ │ │ │ +│ Real Mode Simulation Mode │ +│ │ │ │ +│ ▼ ▼ │ +│ ┌─────────────────┐ ┌─────────────────┐ │ +│ │ Mock Robot │ │ Local JS │ │ +│ │ WebSocket │ │ Simulation │ │ +│ │ Server │ │ (No server) │ │ +│ └─────────────────┘ └─────────────────┘ │ +└─────────────────────────────────────────────────────────────┘ +``` + +## Mock Robot Server Protocol + +The mock server implements the rosbridge WebSocket protocol: + +### Supported Operations + +| Operation | Description | +|-----------|-------------| +| `subscribe` | Subscribe to a topic | +| `unsubscribe` | Unsubscribe from a topic | +| `publish` | Publish a message to a topic | +| `call_service` | Call a ROS service | +| `advertise` | Advertise a topic | +| `unadvertise` | Stop advertising a topic | + +### Simulated Topics + +| Topic | Type | Description | +|-------|------|-------------| +| `/joint_states` | `sensor_msgs/JointState` | Joint positions (26 joints) | +| `/naoqi_driver/battery` | `naoqi_bridge_msgs/Battery` | Battery status (85%) | +| `/bumper` | `naoqi_bridge_msgs/Bumper` | Bumper contact sensors | +| `/hand_touch` | `naoqi_bridge_msgs/HandTouch` | Hand touch sensors | +| `/head_touch` | `naoqi_bridge_msgs/HeadTouch` | Head touch sensors | +| `/sonar/left` | `sensor_msgs/Range` | Left sonar distance | +| `/sonar/right` | `sensor_msgs/Range` | Right sonar distance | + +### Simulated Services + +| Service | Response | +|---------|----------| +| `/naoqi_driver/get_robot_info` | `{ robotName: "MOCK-NAO6", robotVersion: "6.0" }` | +| `/naoqi_driver/get_joint_names` | List of 26 joint names | +| `/naoqi_driver/get_position` | Current position `{ x, y, theta }` | +| `/naoqi_driver/is_waking_up` | `{ is_waking_up: false }` | + +### Supported Actions + +| Action | Parameters | Description | +|--------|------------|-------------| +| `say_text` | `text` | Speak text | +| `walk_forward` | `speed` | Walk forward | +| `walk_backward` | `speed` | Walk backward | +| `turn_left` | `speed` | Turn left | +| `turn_right` | `speed` | Turn right | +| `stop` | - | Stop all movement | +| `move_head` | `yaw`, `pitch`, `speed` | Move head | + +## Configuration + +### Environment Variables + +```bash +# Mock Robot Server (scripts/mock-robot) +MOCK_ROBOT_PORT=9090 # WebSocket port +MOCK_PUBLISH_INTERVAL=100 # Sensor update interval (ms) + +# HRIStudio Client +NEXT_PUBLIC_SIMULATION_MODE=true # Enable client-side simulation +NEXT_PUBLIC_ROS_BRIDGE_URL=ws://localhost:9090 +``` + +## Testing + +### 1. Start Mock Server +```bash +cd scripts/mock-robot +bun dev +``` + +### 2. Start HRIStudio +```bash +cd hristudio +bun dev +``` + +### 3. Test Connection +Visit `http://localhost:3000/nao-test` and click "Connect". You should see: +- Connection status: `connected` +- Battery: ~85% +- Joint states updating +- Log messages showing subscriptions + +### 4. Test Actions +Use the wizard interface to test: +- Speech actions +- Movement actions +- Head control + +## Troubleshooting + +### "Connection timeout" error +- Ensure mock server is running: `curl ws://localhost:9090` +- Check port is correct (default 9090) + +### "Not connected to ROS bridge" error +- Enable simulation mode: `NEXT_PUBLIC_SIMULATION_MODE=true` +- Or connect to mock server first + +### Actions not executing +- Check connection status in wizard interface +- Enable simulation mode if using client-side simulation + +## Files + +| File | Description | +|------|-------------| +| `scripts/mock-robot/src/server.ts` | TypeScript mock server | +| `scripts/mock-robot/server.js` | JavaScript mock server (for Docker) | +| `src/lib/ros/wizard-ros-service.ts` | Client with simulation mode | +| `src/hooks/useWizardRos.ts` | React hook with simulation support | +| `docker-compose.mock.yml` | Docker mock service | +| `robot-plugins/plugins/nao6-mock.json` | Mock NAO6 plugin | + +## Development + +### Adding New Simulated Actions + +1. Edit `scripts/mock-robot/src/server.ts` +2. Add handler in `handlePublish()` or `handleServiceCall()` +3. Update `nao6-mock.json` plugin with new action definition + +### Adding New Simulated Sensors + +1. Edit `scripts/mock-robot/src/server.ts` +2. Add topic publishing in `publishRobotState()` +3. Update subscriber topics in `WizardRosService.subscribeToRobotTopics()` diff --git a/docs/tutorials/01-getting-started.md b/docs/tutorials/01-getting-started.md new file mode 100644 index 0000000..205dd49 --- /dev/null +++ b/docs/tutorials/01-getting-started.md @@ -0,0 +1,151 @@ +# Tutorial 1: Getting Started + +Learn how to set up HRIStudio and log in for the first time. + +## Objectives + +- Install HRIStudio dependencies +- Start the development environment +- Log in and explore the interface + +## Prerequisites + +- [Bun](https://bun.sh) installed +- [Docker](https://docker.com) installed +- [Git](https://git-scm.com) installed + +## Step 1: Clone the Repository + +```bash +git clone https://github.com/soconnor0919/hristudio.git +cd hristudio +``` + +## Step 2: Install Dependencies + +HRIStudio uses Bun as its package manager: + +```bash +bun install +``` + +## Step 3: Start the Database + +HRIStudio requires PostgreSQL. The easiest way is using Docker: + +```bash +# Start PostgreSQL and MinIO (for file storage) +bun run docker:up + +# Push database schema +bun db:push + +# Seed with sample data +bun db:seed +``` + +This creates the database schema and populates it with: +- 4 default user accounts +- Sample study and experiments +- Test participants and trials + +## Step 4: Start the Development Server + +```bash +bun dev +``` + +The application will be available at `http://localhost:3000`. + +## Step 5: Log In + +Use one of the default accounts: + +| Role | Email | Password | +|------|-------|----------| +| Administrator | `sean@soconnor.dev` | `password123` | +| Researcher | `felipe.perrone@bucknell.edu` | `password123` | +| Wizard | `emily.watson@lab.edu` | `password123` | +| Observer | `maria.santos@tech.edu` | `password123` | + +## Exploring the Interface + +After logging in, you'll see the main dashboard: + +``` +┌─────────────────────────────────────────────────────────────┐ +│ HRIStudio [User] [Settings] │ +├─────────────────────────────────────────────────────────────┤ +│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ +│ │ Studies │ │ Trials │ │Plugins │ │ Admin │ │ +│ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │ +│ │ +│ Recent Activity │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ • Study: Comparative WoZ Study - Ready │ │ +│ │ • Trial: P101 - Completed (5 min ago) │ │ +│ └─────────────────────────────────────────────────────┘ │ +└─────────────────────────────────────────────────────────────┘ +``` + +### Navigation + +- **Studies** - View and manage your research studies +- **Trials** - Monitor and manage experiment trials +- **Plugins** - Manage robot integrations +- **Admin** - System administration (admins only) + +## Using Simulation Mode + +If you don't have a physical robot, enable simulation mode: + +1. Create or edit `hristudio/.env.local` +2. Add: `NEXT_PUBLIC_SIMULATION_MODE=true` +3. Restart the dev server + +Simulation mode allows you to test experiments without connecting to a real robot. + +## Troubleshooting + +### Database Connection Failed + +```bash +# Check if Docker is running +docker ps + +# Restart the database +bun run docker:down +bun run docker:up +bun db:push +``` + +### Port Already in Use + +If port 3000 is in use: + +```bash +# Use a different port +PORT=3001 bun dev +``` + +### Seed Script Fails + +```bash +# Reset the database +bun run docker:down -v +bun run docker:up +bun db:push +bun db:seed +``` + +## Next Steps + +Now that you're set up: + +1. **[Your First Study](02-your-first-study.md)** - Create a research study +2. **[Designing Experiments](03-designing-experiments.md)** - Build your first protocol +3. **[Simulation Mode](09-simulation-mode.md)** - Test without a robot + +--- + +**Previous**: [Tutorials Overview](../tutorials/README.md) | **Next**: [Your First Study](02-your-first-study.md) diff --git a/docs/tutorials/02-your-first-study.md b/docs/tutorials/02-your-first-study.md new file mode 100644 index 0000000..7e701b4 --- /dev/null +++ b/docs/tutorials/02-your-first-study.md @@ -0,0 +1,222 @@ +# Tutorial 2: Your First Study + +Learn how to create a research study and configure team access. + +## Objectives + +- Create a new research study +- Configure study settings (IRB, institution) +- Add team members with appropriate roles + +## What is a Study? + +In HRIStudio, a **Study** is the top-level container for your research: + +``` +Study +├── Experiments (multiple protocols) +├── Participants (study participants) +├── Team Members (collaborators) +├── Forms & Surveys (consent, questionnaires) +└── Trials (individual experiment runs) +``` + +## Step 1: Create a New Study + +1. Log in as **Researcher** or **Administrator** +2. Click **Studies** in the sidebar +3. Click **Create Study** + +### Study Settings + +| Field | Description | Required | +|-------|-------------|----------| +| Name | Study title | Yes | +| Description | Brief overview of research goals | Yes | +| Institution | University or organization | No | +| IRB Protocol | Protocol number (e.g., 2024-HRI-001) | No | +| Status | Draft, Active, Completed, Archived | Yes | + +### Example: Creating "Robot Trust Study" + +``` +Name: Robot Trust Study +Description: Investigating how robot appearance affects human trust in collaborative tasks. +Institution: Bucknell University +IRB Protocol: 2024-HRI-TRUST +Status: Draft +``` + +## Step 2: Add Team Members + +Studies can have multiple collaborators with different roles: + +| Role | Permissions | +|------|-------------| +| Owner | Full access, can delete study | +| Researcher | Create/edit experiments, manage participants | +| Wizard | Execute trials, control robot | +| Observer | View-only access, add annotations | + +### Adding a Wizard + +1. Open your study +2. Go to **Team** tab +3. Click **Add Member** +4. Enter the wizard's email +5. Select **Wizard** role +6. Click **Invite** + +The wizard will receive access to: +- View the study and experiments +- Execute trials +- Control the robot during trials +- Add notes to trials + +## Step 3: Install Robot Plugins + +For studies involving robots, you need to install the appropriate plugin: + +1. Go to **Plugins** in the sidebar +2. Select your study from the dropdown +3. Click **Browse Plugins** +4. Find your robot (e.g., "NAO6 Robot (ROS2 Integration)") +5. Click **Install** +6. Configure robot settings (IP address, etc.) + +### Plugin Configuration + +For NAO6 robots: + +``` +Robot IP: 192.168.1.100 +Connection Type: ROS2 Bridge +WebSocket URL: ws://localhost:9090 +``` + +## Step 4: Create Forms + +Before running trials, you need consent forms: + +1. Go to **Forms** tab in your study +2. Click **Create Form** +3. Select form type: + - **Consent** - Informed consent documents + - **Survey** - Post-session questionnaires + - **Questionnaire** - Demographic forms + +### Form Templates + +HRIStudio provides templates to get started: + +| Template | Use Case | +|----------|----------| +| Informed Consent | Required for all participants | +| Post-Session Survey | Collect feedback after trials | +| Demographics | Collect participant information | + +## Step 5: Add Participants + +1. Go to **Participants** tab +2. Click **Add Participant** +3. Enter participant code (e.g., "P001") +4. Fill in optional details + +### Batch Import + +For large studies, import from CSV: + +```csv +participantCode,name,email,notes +P001,John Smith,john@email.com,Condition A +P002,Jane Doe,jane@email.com,Condition B +``` + +## Study Workflow + +``` +Draft → Active → Recruiting → In Progress → Completed + │ │ │ │ │ + │ │ │ │ └── All trials done + │ │ │ └── Trials running + │ │ └── Recruiting participants + │ └── Ready to collect data + └── Setting up study +``` + +## Study Settings Deep Dive + +### IRB Compliance + +Store your IRB information: +- Protocol number +- Approval date +- Expiration date +- Consent form versions + +### Data Management + +Configure data retention: +- Anonymization settings +- Export formats (CSV, JSON) +- Backup frequency + +### Notification Settings + +Configure alerts for: +- Trial completion +- Participant issues +- Robot disconnection + +## Common Tasks + +### Clone a Study + +Create a copy of an existing study: + +1. Open the study +2. Click **Settings** (gear icon) +3. Select **Duplicate Study** +4. Enter new study name + +### Archive a Study + +When a study is complete: + +1. Go to study settings +2. Change status to **Archived** +3. Data is preserved but study is read-only + +### Transfer Ownership + +Change the study owner: + +1. Go to **Team** tab +2. Find the new owner +3. Click **Make Owner** + +## Troubleshooting + +### Can't Add Team Member + +- Check email is correct +- User must have an HRIStudio account +- You must be an owner or admin + +### Plugin Installation Failed + +- Check robot is on the network +- Verify WebSocket URL is correct +- Check Docker services are running + +## Next Steps + +Now that your study is set up: + +1. **[Designing Experiments](03-designing-experiments.md)** - Create your first experiment protocol +2. **[Forms & Surveys](07-forms-and-surveys.md)** - Customize your consent forms +3. **[Running Trials](04-running-trials.md)** - Learn about trial management + +--- + +**Previous**: [Getting Started](01-getting-started.md) | **Next**: [Designing Experiments](03-designing-experiments.md) diff --git a/docs/tutorials/03-designing-experiments.md b/docs/tutorials/03-designing-experiments.md new file mode 100644 index 0000000..4d3fc81 --- /dev/null +++ b/docs/tutorials/03-designing-experiments.md @@ -0,0 +1,341 @@ +# Tutorial 3: Designing Experiments + +Learn how to create experiment protocols using the visual block designer. + +## Objectives + +- Navigate the experiment designer +- Use core blocks (events, wizard actions, control flow) +- Build a branching experiment protocol + +## What is an Experiment? + +An **Experiment** defines the protocol for your study: + +``` +Experiment +├── Steps (ordered sequence) +│ ├── Actions (robot behaviors) +│ ├── Wizard Blocks (human decisions) +│ └── Control Flow (loops, branches) +├── Robot Actions (from plugins) +└── Parameters (configurable values) +``` + +## Step 1: Create an Experiment + +1. Open your study +2. Go to **Experiments** tab +3. Click **New Experiment** + +### Experiment Settings + +| Field | Description | +|-------|-------------| +| Name | Protocol title | +| Description | What the experiment measures | +| Robot | Which robot to use | +| Version | Track protocol versions | + +## Step 2: The Experiment Designer Interface + +The designer has three main areas: + +``` +┌──────────────────────────────────────────────────────────────┐ +│ Experiment: Robot Trust Study v1 [Save] │ +├────────────┬─────────────────────────────────────────────────┤ +│ │ │ +│ Blocks │ Canvas │ +│ Library │ │ +│ │ ┌─────────┐ ┌─────────┐ │ +│ ┌──────┐ │ │ Step 1 │───▶│ Step 2 │ │ +│ │Events│ │ │ Hook │ │ Story │ │ +│ ├──────┤ │ └─────────┘ └────┬────┘ │ +│ │Wizard│ │ │ │ +│ ├──────┤ │ ┌────▼────┐ │ +│ │Control│ │ │ Step 3 │ │ +│ ├──────┤ │ │ Check │ │ +│ │Robot │ │ └────┬────┘ │ +│ └──────┘ │ ┌────┴────┐ │ +│ │ ┌────┴───┐ ┌───┴────┐ │ +│ │ │Step 4a │ │Step 4b │ │ +│ │ │Correct │ │ Wrong │ │ +│ │ └───┬────┘ └───┬────┘ │ +│ │ └─────┬─────┘ │ +│ │ ┌────▼────┐ │ +│ │ │ Step 5 │ │ +│ │ │Conclude │ │ +│ │ └─────────┘ │ +├────────────┴─────────────────────────────────────────────────┤ +│ Properties Panel │ +│ ┌─────────────────────────────────────────────────────────┐ │ +│ │ Step 1: The Hook │ │ +│ │ Duration: 25 seconds │ │ +│ │ Actions: 2 blocks │ │ +│ └─────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────┘ +``` + +## Step 3: Understanding Block Categories + +### Events (Triggers) + +Start your experiment with these blocks: + +| Block | Description | +|-------|-------------| +| **Trial Start** | Triggers when trial begins | +| **Wizard Button** | Waits for wizard to press a button | +| **Timer** | Waits for a specified duration | +| **Participant Response** | Waits for participant input | + +### Wizard Actions + +Blocks the wizard can control: + +| Block | Description | +|-------|-------------| +| **Say Text** | Robot speaks text | +| **Play Animation** | Play a predefined animation | +| **Show Image** | Display image on robot screen | +| **Move Robot** | Move robot to position | + +### Control Flow + +Control experiment progression: + +| Block | Description | +|-------|-------------| +| **Branch** | Split into multiple paths | +| **Loop** | Repeat a sequence | +| **Wait** | Pause for duration | +| **Converge** | Merge multiple paths back | + +### Robot Actions + +Actions from your installed robot plugin: + +| Block | Description | +|-------|-------------| +| **say_text** | Robot speaks | +| **walk_forward** | Robot walks forward | +| **turn_left** | Robot turns | +| **wave** | Robot waves | + +## Step 4: Building "The Interactive Storyteller" + +Let's build a simple storytelling experiment with branching: + +### Step 1: The Hook (Start) + +1. Click **+ Add Step** +2. Name it "The Hook" +3. Set type to **Robot** +4. Drag **Say Text** block: + ``` + text: "Hello! I have a story to tell you. Are you ready?" + ``` +5. Drag **Move Arm** block: + ``` + arm: right + gesture: welcome + ``` + +### Step 2: The Narrative + +1. Add new step "The Narrative" +2. Connect from Step 1 +3. Add **Say Text**: + ``` + text: "Once upon a time, a traveler flew to Mars..." + ``` +4. Add **Turn Head** for gaze behavior: + ``` + yaw: 1.5 + pitch: 0.0 + ``` + +### Step 3: Comprehension Check (Branching) + +1. Add new step "Comprehension Check" +2. Set type to **Conditional** +3. Add **Ask Question**: + ``` + question: "What color was the rock?" + options: + - Correct: "Red" + - Incorrect: "Blue" + ``` +4. This creates two paths automatically + +### Step 4: Branch Paths + +**Branch A (Correct):** +``` +Say: "Yes! It was a glowing red rock." +Emotion: Happy +``` + +**Branch B (Incorrect):** +``` +Say: "Actually, it was red." +Emotion: Sad +``` + +### Step 5: Converge + +1. Add new step "Story Continues" +2. Set type to **Converge** +3. Connect both branches to this step +4. Add concluding speech + +### Step 6: Conclusion + +1. Add final step "Conclusion" +2. Add **Say Text**: "The End. Thank you for listening!" +3. Add **Bow** animation + +## Step 5: Block Properties + +Each block has configurable properties: + +### Say Text Block + +```json +{ + "text": "Hello, how are you?", + "language": "en-US", + "speed": 1.0, + "emotion": "neutral" +} +``` + +### Branch Block + +```json +{ + "variable": "last_response", + "options": [ + { "label": "Yes", "value": "yes", "nextStepId": "step_abc" }, + { "label": "No", "value": "no", "nextStepId": "step_xyz" } + ] +} +``` + +### Loop Block + +```json +{ + "iterations": 3, + "maxDuration": 60, + "children": [...] +} +``` + +## Step 6: Testing Your Experiment + +### Preview Mode + +Test your experiment without running a real trial: + +1. Click **Preview** button +2. Step through each block +3. See timing and flow +4. Test branching decisions + +### Simulation Mode + +Run with a simulated robot: + +1. Enable `NEXT_PUBLIC_SIMULATION_MODE=true` +2. Start a trial +3. Robot actions are logged but not executed +4. Great for protocol testing + +## Advanced: Parallel Execution + +Run multiple actions simultaneously: + +``` +Step: Greeting +├── Parallel Block +│ ├── Say: "Hello!" +│ ├── Move Arm: Wave +│ └── Move Head: Look at participant +``` + +## Experiment Versioning + +Track protocol changes: + +1. **Draft** - Experiment being designed +2. **Testing** - Being tested with participants +3. **Ready** - Approved for data collection +4. **Deprecated** - Superseded by newer version + +## Common Patterns + +### Linear Protocol + +``` +Start → Step 1 → Step 2 → Step 3 → End +``` + +### Branching Protocol + +``` +Start → Step 1 + ├── Condition A → Step 2a + └── Condition B → Step 2b +``` + +### Loop Protocol + +``` +Start → Step 1 → Loop (3x) → Step 2 → End + ↑ + └── (back to Step 1) +``` + +### Parallel Protocol + +``` +Start → Parallel + ├── Action A + ├── Action B + └── Action C + → Continue +``` + +## Troubleshooting + +### Block Not Connecting + +- Check step types are compatible +- Ensure no circular dependencies +- Verify conditions are complete + +### Robot Action Not Available + +- Install the robot plugin +- Check plugin is enabled for study +- Verify robot is connected + +### Timing Issues + +- Adjust duration estimates +- Use explicit wait blocks +- Test with real timing + +## Next Steps + +Now that you've designed your experiment: + +1. **[Running Trials](04-running-trials.md)** - Execute your experiment +2. **[Wizard Interface](05-wizard-interface.md)** - Learn real-time control +3. **[Robot Integration](06-robot-integration.md)** - Connect your robot + +--- + +**Previous**: [Your First Study](02-your-first-study.md) | **Next**: [Running Trials](04-running-trials.md) diff --git a/docs/tutorials/04-running-trials.md b/docs/tutorials/04-running-trials.md new file mode 100644 index 0000000..7d956ae --- /dev/null +++ b/docs/tutorials/04-running-trials.md @@ -0,0 +1,366 @@ +# Tutorial 4: Running Trials + +Learn how to execute experiments and manage participant trials. + +## Objectives + +- Schedule and start trials +- Monitor trial progress +- Handle trial interruptions +- Collect trial data + +## What is a Trial? + +A **Trial** is a single execution of an experiment with one participant: + +``` +Trial +├── Participant (who took part) +├── Experiment (which protocol) +├── Status (scheduled, in_progress, completed) +├── Events (timestamped actions) +└── Data (collected responses) +``` + +## Trial Lifecycle + +``` +Scheduled → In Progress → Completed + │ │ │ + │ ▼ │ + │ Aborted ◄────────┤ + │ │ │ + └────────► Failed ◄───────┘ +``` + +| Status | Description | +|--------|-------------| +| Scheduled | Trial is planned but not started | +| In Progress | Trial is currently running | +| Completed | Trial finished successfully | +| Aborted | Trial stopped early by wizard | +| Failed | Trial failed due to error | + +## Step 1: Schedule a Trial + +### Create Trial for Participant + +1. Go to your **Study** +2. Open **Trials** tab +3. Click **Schedule Trial** +4. Select: + - **Participant**: P001 + - **Experiment**: The Interactive Storyteller + - **Scheduled Time**: Today, 2:00 PM + +### Batch Scheduling + +For multiple participants: + +1. Click **Batch Schedule** +2. Select participants (P001-P020) +3. Select experiment +4. Set time slots + +``` +| Time | Participant | +|------------|-------------| +| 2:00 PM | P001 | +| 2:15 PM | P002 | +| 2:30 PM | P003 | +| ... | ... | +``` + +## Step 2: Prepare for Trial + +Before starting: + +1. **Verify Robot Connection** + - Check robot is powered on + - Verify network connection + - Test WebSocket connection + +2. **Review Experiment** + - Ensure experiment is "Ready" status + - Check step count and timing + - Verify all actions are configured + +3. **Prepare Environment** + - Ensure participant consent is obtained + - Set up recording equipment (if needed) + - Remove distractions + +## Step 3: Start a Trial + +### From Trials List + +1. Find the scheduled trial +2. Click **Start Trial** +3. Confirm participant is ready +4. Click **Begin** + +### From Wizard Interface + +1. Open **Wizard Interface** +2. Select trial from queue +3. Click **Start** + +## Step 4: During the Trial + +### Wizard Interface Overview + +``` +┌──────────────────────────────────────────────────────────────┐ +│ Trial: P001 - Interactive Storyteller [00:05:23]│ +├──────────────────────────────────────────────────────────────┤ +│ ┌──────────────┐ ┌────────────────────┐ ┌─────────────────┐ │ +│ │ Trial │ │ Timeline │ │ Robot Control │ │ +│ │ Controls │ │ │ │ │ │ +│ │ │ │ ●───●───○───○ │ │ ┌─────────────┐ │ │ +│ │ [▶ Play] │ │ Step 1 2 3 4 │ │ │ Connected ✓ │ │ │ +│ │ [⏸ Pause] │ │ ↑ │ │ │ Battery: 85%│ │ │ +│ │ [⏹ Stop] │ │ Current: Step 2 │ │ └─────────────┘ │ │ +│ │ │ │ │ │ │ │ +│ │ [📝 Notes] │ │ Progress: 40% │ │ [Say Text] │ │ +│ │ [⚠ Alert] │ │ │ │ [Move Robot] │ │ +│ └──────────────┘ └────────────────────┘ │ [Custom Action]│ │ +│ └─────────────────┘ │ +└──────────────────────────────────────────────────────────────┘ +``` + +### Trial Controls + +| Button | Action | Keyboard | +|--------|--------|----------| +| Play | Resume trial | Space | +| Pause | Pause trial | Space | +| Stop | End trial early | Escape | +| Notes | Add timestamped note | N | +| Alert | Send alert notification | A | + +### Monitoring Progress + +**Timeline View:** +- Visual step progression +- Current step highlighted +- Completed steps checked +- Estimated time remaining + +**Event Log:** +- Timestamped events +- Action executions +- Wizard interventions +- Robot responses + +## Step 5: Wizard Interventions + +During Wizard-of-Oz studies, wizards can intervene: + +### Add Intervention + +1. Click **+ Intervention** +2. Select type: + - **Pause**: Temporarily stop trial + - **Resume**: Continue after pause + - **Note**: Add observation + - **Alert**: Notify researcher + +### Branch Selection + +When reaching a conditional step: + +1. Observe participant response +2. Select appropriate branch: + - **Correct**: Proceed to positive path + - **Incorrect**: Proceed to correction path +3. Select is logged for analysis + +### Manual Actions + +Execute unplanned actions: + +1. Click **+ Action** +2. Select from robot actions +3. Configure parameters +4. Execute immediately + +## Step 6: Trial Completion + +### Automatic Completion + +When all steps complete: +1. Final step executes +2. Trial status → "Completed" +3. Data is saved automatically +4. Summary shown + +### Manual Completion + +To end early: + +1. Click **Stop Trial** +2. Confirm completion +3. Select reason: + - Participant fatigue + - Technical issue + - Protocol complete +4. Save partial data + +## Step 7: Post-Trial + +### Automatic Prompts + +After trial completion: + +1. **Participant Debrief** + - Thank participant + - Answer questions + - Collect final feedback + +2. **Survey Distribution** + - Send post-session survey + - Collect responses + +3. **Data Export** + - Download trial data + - Export event log + +### Trial Summary + +View trial summary: + +``` +┌─────────────────────────────────────────────────────────────┐ +│ Trial Summary - P001 │ +├─────────────────────────────────────────────────────────────┤ +│ Duration: 5:23 │ +│ Steps Completed: 6/6 (100%) │ +│ Interventions: 2 │ +│ │ +│ Actions: │ +│ ✓ Say Text: "Hello..." (2.3s) │ +│ ✓ Turn Head: yaw=1.5 (1.1s) │ +│ ✓ Say Text: "What color..." (3.2s) │ +│ ⚠ Intervention: Pause (10s) │ +│ ✓ Branch: Correct selected │ +│ ✓ Say Text: "Yes! It was red" (2.8s) │ +│ │ +│ Events: 18 logged │ +└─────────────────────────────────────────────────────────────┘ +``` + +## Managing Multiple Trials + +### Trial Queue + +View upcoming trials: + +``` +┌─────────────────────────────────────────────────────────────┐ +│ Trial Queue [Refresh] │ +├─────────────────────────────────────────────────────────────┤ +│ 2:00 PM │ P001 │ Interactive Storyteller │ Scheduled │ +│ 2:20 PM │ P002 │ Interactive Storyteller │ Scheduled │ +│ 2:40 PM │ P003 │ Interactive Storyteller │ Scheduled │ +│ 3:00 PM │ P004 │ Interactive Storyteller │ Scheduled │ +└─────────────────────────────────────────────────────────────┘ +``` + +### Trial History + +View past trials: + +| Participant | Started | Duration | Status | Interventions | +|-------------|---------|----------|--------|---------------| +| P001 | Today 2:00 PM | 5:23 | Completed | 2 | +| P002 | Today 2:20 PM | 4:58 | Completed | 1 | +| P003 | Today 2:45 PM | - | In Progress | 0 | + +## Data Collection + +### Automatic Data Capture + +HRIStudio automatically logs: + +- Timestamps for all events +- Action executions +- Robot responses +- Wizard interventions +- Participant responses +- Timing data + +### Manual Data + +Wizards can add: + +- Timestamped notes +- Observation categories +- Participant behavior codes +- Custom annotations + +### Export Formats + +Download trial data: + +| Format | Contents | +|--------|----------| +| CSV | Tabular data for spreadsheets | +| JSON | Full event log with metadata | +| Video | Screen recording (if enabled) | + +## Troubleshooting + +### Trial Won't Start + +1. Check robot connection +2. Verify experiment is "Ready" +3. Check participant consent +4. Review error logs + +### Trial Paused Unexpectedly + +- Robot may have disconnected +- Check network connection +- Resume when connection restored + +### Data Not Saved + +- Ensure database connection +- Check disk space +- Export data manually + +## Best Practices + +### Before Trials + +- [ ] Robot connected and tested +- [ ] Experiment verified +- [ ] Participant consent obtained +- [ ] Recording equipment ready +- [ ] Wizard briefed on protocol + +### During Trials + +- [ ] Monitor timeline progress +- [ ] Take timestamped notes +- [ ] Document interventions +- [ ] Watch for issues + +### After Trials + +- [ ] Review trial summary +- [ ] Export data promptly +- [ ] Send follow-up surveys +- [ ] Update participant status + +## Next Steps + +Now that you can run trials: + +1. **[Wizard Interface](05-wizard-interface.md)** - Master real-time control +2. **[Data & Analysis](08-data-and-analysis.md)** - Analyze your results +3. **[Forms & Surveys](07-forms-and-surveys.md)** - Collect post-trial data + +--- + +**Previous**: [Designing Experiments](03-designing-experiments.md) | **Next**: [Wizard Interface](05-wizard-interface.md) diff --git a/docs/tutorials/05-wizard-interface.md b/docs/tutorials/05-wizard-interface.md new file mode 100644 index 0000000..84f518e --- /dev/null +++ b/docs/tutorials/05-wizard-interface.md @@ -0,0 +1,393 @@ +# Tutorial 5: Wizard Interface + +Learn how to use the real-time wizard control interface for Wizard-of-Oz studies. + +## Objectives + +- Navigate the wizard interface +- Control robot actions in real-time +- Make branching decisions +- Handle trial interruptions + +## What is the Wizard Interface? + +The **Wizard Interface** is your control center during trials. It provides: + +- Real-time trial monitoring +- Robot action controls +- Decision-making tools +- Intervention capabilities +- Event logging + +``` +┌──────────────────────────────────────────────────────────────┐ +│ WIZARD INTERFACE │ +├────────────────┬─────────────────────┬──────────────────────┤ +│ │ │ │ +│ Trial │ Timeline │ Robot │ +│ Controls │ Progress │ Status │ +│ │ │ │ +│ ┌──────────┐ │ ┌───────────────┐ │ ┌────────────────┐ │ +│ │ ▶ Play │ │ │ 1 → 2 → 3 → │ │ │ ● Connected │ │ +│ │ ⏸ Pause │ │ │ ↑ │ │ │ Battery: 85% │ │ +│ │ ⏹ Stop │ │ │ Step 2 │ │ │ Position: (0,0)│ │ +│ └──────────┘ │ └───────────────┘ │ └────────────────┘ │ +│ │ │ │ +│ ┌──────────┐ │ Progress: 40% │ ┌────────────────┐ │ +│ │ 📝 Notes │ │ Time: 00:05:23 │ │ Action Panel │ │ +│ │ ⚠ Alert │ │ │ │ │ │ +│ └──────────┘ │ │ │ [Say Text] │ │ +│ │ │ │ [Move Robot] │ │ +│ │ │ │ [Wave] │ │ +│ │ │ │ [Custom...] │ │ +│ │ │ └────────────────┘ │ +└────────────────┴─────────────────────┴──────────────────────┘ +``` + +## Step 1: Accessing the Wizard Interface + +### Method 1: From Trials List + +1. Go to **Trials** in sidebar +2. Find your scheduled trial +3. Click **Open Wizard** + +### Method 2: Direct URL + +``` +/trials/{trialId}/wizard +``` + +### Method 3: Trial Queue + +1. Go to **Wizard Queue** +2. See all pending trials +3. Click **Start** on any trial + +## Step 2: Understanding the Layout + +### Left Panel: Trial Controls + +| Control | Function | +|---------|----------| +| Play/Pause | Start or pause trial | +| Stop | End trial early | +| Notes | Add timestamped observations | +| Alert | Send alert to researchers | + +### Center Panel: Timeline + +- **Visual Progress**: See step progression +- **Current Position**: Highlighted current step +- **Navigation**: Click to jump to step (if allowed) +- **Time Display**: Elapsed and estimated remaining + +### Right Panel: Robot Control + +**Status Section:** +- Connection indicator +- Battery level +- Position tracking +- Sensor readings + +**Action Section:** +- Quick action buttons +- Custom action builder +- Action history + +## Step 3: Controlling the Robot + +### Quick Actions + +Pre-configured robot actions: + +| Action | Description | +|--------|-------------| +| Say Text | Make robot speak | +| Wave | Wave gesture | +| Look at Me | Turn head toward participant | +| Look Away | Turn head elsewhere | +| Nod | Confirmation nod | +| Shake Head | Negation shake | + +### Custom Say Text + +1. Click **Say Text** +2. Enter text in popup: + ``` + "Hello! Nice to meet you." + ``` +3. Select options: + - Speed: Normal / Slow / Fast + - Emotion: Neutral / Happy / Excited +4. Click **Execute** +5. Robot speaks the text + +### Move Robot + +1. Click **Move Robot** +2. Select movement type: + - Walk Forward/Back + - Turn Left/Right + - Move Head + - Move Arm +3. Set parameters +4. Execute + +### Custom Actions + +For advanced control: + +1. Click **Custom...** +2. Select action from plugin +3. Configure parameters +4. Execute + +## Step 4: Making Decisions + +When the experiment reaches a branching point: + +### Decision Popup + +A popup appears with options: + +``` +┌─────────────────────────────────────────────────────────────┐ +│ Branch Decision Required │ +├─────────────────────────────────────────────────────────────┤ +│ │ +│ Step: Comprehension Check │ +│ Question: "What color was the rock?" │ +│ │ +│ Participant's response: They said "blue" (incorrect) │ +│ │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ ○ Correct Response (Red) │ │ +│ │ → Robot celebrates │ │ +│ │ │ │ +│ │ ● Incorrect Response (Other) │ │ +│ │ → Robot gently corrects │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ +│ [Cancel] [Confirm Selection] │ +└─────────────────────────────────────────────────────────────┘ +``` + +### Decision Guidelines + +1. **Observe** participant's actual response +2. **Consider** protocol criteria +3. **Select** appropriate branch +4. **Confirm** selection + +### After Selection + +- Decision is logged with timestamp +- Trial continues on selected path +- Both participant and robot continue + +## Step 5: Handling Interruptions + +### Pause Trial + +When you need to pause: + +1. Click **Pause** button +2. Optionally add reason: + - Participant needs break + - Technical issue + - External interruption +3. Trial pauses, robot holds position + +### Resume Trial + +1. Click **Play** button +2. Trial resumes from pause point +3. Pause duration is logged + +### Stop Trial + +For early termination: + +1. Click **Stop** button +2. Select reason: + - Participant fatigue + - Technical failure + - Protocol deviation + - Participant withdrawal +3. Confirm stop +4. Partial data is saved + +### Add Notes + +Record observations: + +1. Click **Notes** button +2. Enter observation: + ``` + Participant laughed at the robot's gesture. + ``` +3. Note is timestamped automatically +4. Notes appear in event log + +### Send Alert + +Notify researchers: + +1. Click **Alert** button +2. Select alert type: + - Technical issue + - Safety concern + - Protocol question + - Other +3. Add description +4. Send alert + +## Step 6: Monitoring Robot Status + +### Connection Status + +| Status | Icon | Meaning | +|--------|------|---------| +| Connected | ● Green | Robot responding | +| Connecting | ● Yellow | Attempting connection | +| Disconnected | ● Red | No robot connection | +| Error | ⚠ Orange | Connection error | + +### Battery Monitor + +View battery level: +- Green: > 50% +- Yellow: 20-50% +- Red: < 20% + +### Sensor Display + +Real-time sensor readings: +- Joint positions +- Touch sensors +- Sonar distances +- Camera feed (if available) + +### Action Queue + +See pending/executing actions: +``` +Executing: Say Text "Hello!" +Pending: Move Head (queued) +``` + +## Step 7: Keyboard Shortcuts + +Speed up your workflow: + +| Key | Action | +|-----|--------| +| Space | Play/Pause toggle | +| Escape | Stop trial | +| N | Add note | +| A | Send alert | +| 1-9 | Execute quick action | +| ← → | Navigate timeline | +| ↑ ↓ | Select branch option | + +## Step 8: Event Logging + +All actions are logged automatically: + +``` +[14:32:05] Trial started +[14:32:07] Step 1: The Hook +[14:32:08] Action: Say Text "Hello!" +[14:32:11] Action: Move Arm Wave +[14:32:15] Step 2: The Narrative +[14:32:16] Action: Say Text "Once upon a time..." +[14:33:05] Step 3: Comprehension Check +[14:33:06] Action: Say Text "What color was the rock?" +[14:33:28] Wizard Note: "Participant said blue" +[14:33:30] Branch: Incorrect selected +[14:33:31] Step 4b: Correction +[14:33:32] Action: Say Text "Actually, it was red." +[14:34:05] Trial completed +``` + +## Trial Modes + +### Observer Mode + +For observers (read-only): +- View trial progress +- See robot status +- Cannot execute actions +- Can add notes + +### Active Wizard Mode + +Full control: +- Execute actions +- Make decisions +- Pause/resume +- Add notes/alerts + +### Training Mode + +Practice without real data: +- Simulated robot +- No data saved +- Safe to experiment + +## Best Practices + +### Before Trial + +- [ ] Review experiment protocol +- [ ] Test robot connection +- [ ] Familiarize with action panel +- [ ] Know decision criteria + +### During Trial + +- [ ] Stay focused on participant +- [ ] Make decisions based on observation +- [ ] Document notable events +- [ ] Keep action log clean + +### After Trial + +- [ ] Review event log +- [ ] Add final notes +- [ ] Confirm data saved +- [ ] Prepare for next trial + +## Troubleshooting + +### Robot Not Responding + +1. Check connection indicator +2. Verify network +3. Check robot power +4. Restart connection + +### Actions Not Executing + +1. Check action queue +2. Verify parameters +3. Check robot state (not in rest mode) + +### Decision Popup Not Appearing + +1. Check if step has branches +2. Verify step type is "conditional" +3. Contact researcher + +## Next Steps + +Mastered the wizard interface? + +1. **[Robot Integration](06-robot-integration.md)** - Deep dive into robot control +2. **[Data & Analysis](08-data-and-analysis.md)** - Review trial data +3. **[Simulation Mode](09-simulation-mode.md)** - Practice without a robot + +--- + +**Previous**: [Running Trials](04-running-trials.md) | **Next**: [Robot Integration](06-robot-integration.md) diff --git a/docs/tutorials/06-robot-integration.md b/docs/tutorials/06-robot-integration.md new file mode 100644 index 0000000..6e05671 --- /dev/null +++ b/docs/tutorials/06-robot-integration.md @@ -0,0 +1,386 @@ +# Tutorial 6: Robot Integration + +Learn how to connect and configure robots for your HRI studies. + +## Objectives + +- Connect NAO6 robot to HRIStudio +- Configure robot plugins +- Test robot connection +- Troubleshoot common issues + +## Supported Robots + +HRIStudio supports multiple robot platforms: + +| Robot | Protocol | Actions | +|-------|----------|---------| +| **NAO6** | ROS2 | Speech, movement, gestures, sensors | +| **TurtleBot3** | ROS2 | Navigation, sensors | +| **Mock Robot** | WebSocket | All actions (simulation) | + +## Understanding the Architecture + +``` +┌──────────────────────────────────────────────────────────────┐ +│ HRIStudio Platform │ +│ │ +│ ┌──────────────┐ ┌──────────────────────┐ │ +│ │ Wizard │◄────────────►│ Robot Communication │ │ +│ │ Interface │ WebSocket │ Service │ │ +│ └──────────────┘ └──────────┬───────────┘ │ +│ │ │ +│ │ ROS Bridge │ +│ ┌─────▼─────┐ │ +│ │ rosbridge │ │ +│ │ :9090 │ │ +│ └─────┬─────┘ │ +└────────────────────────────────────────────┼─────────────────┘ + │ + ┌─────────────────┼─────────────────┐ + │ │ │ + ┌─────▼─────┐ ┌─────▼─────┐ │ + │ NAO │ │ NAO │ │ + │ Driver │ │ Robot │ │ + │ (ROS2) │◄───►│ (naoqi) │ │ + └───────────┘ └───────────┘ │ + Network Robot │ +``` + +## Step 1: Set Up NAO6 Robot + +### Network Configuration + +1. Connect NAO6 to your network: + ``` + # On the robot, say "Connect to Wi-Fi" + # Or use the Choregraphe interface + ``` + +2. Note the robot's IP address: + ``` + # On the robot, say "What is my IP address?" + # Or check robot's network settings + ``` + +3. Verify network access: + ```bash + ping nao.local + # Or ping the IP directly: + ping 192.168.1.100 + ``` + +### Robot Credentials + +Default credentials: +``` +Username: nao +Password: robolab +``` + +### Wake Up Robot + +Before connecting, wake up the robot: + +```bash +ssh nao@192.168.1.100 +# Enter password when prompted + +# Wake up the robot +python -c "from naoqi import ALProxy; proxy = ALProxy('ALMotion', '192.168.1.100', 9559); proxy.wakeUp()" +``` + +## Step 2: Start Docker Services + +### Using Docker Compose + +```bash +cd ~/nao6-hristudio-integration + +# Set robot IP +export NAO_IP=192.168.1.100 + +# Start services +docker compose up -d +``` + +### Services Overview + +| Service | Port | Purpose | +|---------|------|---------| +| `nao_driver` | - | ROS2 driver for NAO | +| `ros_bridge` | 9090 | WebSocket bridge | +| `ros_api` | - | Topic introspection | + +### Verify Services + +```bash +# Check running containers +docker ps + +# View logs +docker compose logs -f + +# Test WebSocket connection +ws://localhost:9090 +``` + +## Step 3: Configure HRIStudio + +### Install Robot Plugin + +1. Go to **Plugins** in sidebar +2. Select your study +3. Click **Browse Plugins** +4. Find **NAO6 Robot (ROS2 Integration)** +5. Click **Install** + +### Configure Plugin + +Set robot connection: + +``` +┌─────────────────────────────────────────────────────────────┐ +│ NAO6 Robot Configuration │ +├─────────────────────────────────────────────────────────────┤ +│ Robot Name: NAO6-Lab │ +│ Robot IP: 192.168.1.100 │ +│ WebSocket URL: ws://localhost:9090 │ +│ │ +│ Advanced Settings: │ +│ □ Use Simulation Mode │ +│ Connection Timeout: 30 seconds │ +└─────────────────────────────────────────────────────────────┘ +``` + +### Environment Variables + +Create `hristudio/.env.local`: + +```bash +# Robot connection +NAO_ROBOT_IP=192.168.1.100 +NAO_PASSWORD=robolab +NAO_USERNAME=nao + +# WebSocket bridge +NEXT_PUBLIC_ROS_BRIDGE_URL=ws://localhost:9090 +``` + +## Step 4: Test Connection + +### Using the NAO Test Page + +1. Navigate to: `http://localhost:3000/nao-test` +2. Click **Connect** +3. Verify connection status + +### Connection Status Indicators + +| Status | Meaning | +|--------|---------| +| **Connected** | Robot responding normally | +| **Connecting** | Attempting connection | +| **Error** | Connection failed | +| **Timeout** | Robot not responding | + +### Test Actions + +Test basic robot actions: + +| Action | Expected Behavior | +|--------|-------------------| +| Say Text | Robot speaks | +| Wave | Robot waves arm | +| Walk Forward | Robot walks | +| Turn Left | Robot turns | + +## Step 5: Robot Actions Reference + +### Speech Actions + +| Action | Parameters | Description | +|--------|------------|-------------| +| `say_text` | `text` | Speak text | +| `say_with_emotion` | `text`, `emotion` | Emotional speech | +| `set_volume` | `level` | Set speech volume | +| `set_language` | `language` | Set speech language | + +### Movement Actions + +| Action | Parameters | Description | +|--------|------------|-------------| +| `walk_forward` | `speed`, `duration` | Walk forward | +| `walk_backward` | `speed` | Walk backward | +| `turn_left` | `speed` | Turn left | +| `turn_right` | `speed` | Turn right | +| `stop` | - | Stop all movement | + +### Head Actions + +| Action | Parameters | Description | +|--------|------------|-------------| +| `move_head` | `yaw`, `pitch`, `speed` | Move head to position | +| `turn_head` | `yaw`, `pitch` | Turn head (relative) | + +### Arm Actions + +| Action | Parameters | Description | +|--------|------------|-------------| +| `move_arm` | `arm`, joint angles | Move arm to position | +| `wave` | `arm` | Wave gesture | + +### Autonomous Life + +| Action | Parameters | Description | +|--------|------------|-------------| +| `wake_up` | - | Wake robot from rest | +| `rest` | - | Put robot to rest | +| `set_autonomous_life` | `enabled` | Toggle autonomous behavior | + +## Step 6: Troubleshooting + +### Common Issues + +#### Robot Not Found + +``` +Error: Cannot connect to robot at 192.168.1.100 +``` + +**Solutions:** +1. Verify IP address: `ping 192.168.1.100` +2. Check robot is powered on +3. Verify network connectivity +4. Try `nao.local` hostname + +#### WebSocket Connection Failed + +``` +Error: WebSocket connection to ws://localhost:9090 failed +``` + +**Solutions:** +1. Check Docker is running +2. Verify ros_bridge container: `docker ps` +3. Check port 9090 is not blocked +4. Restart services: `docker compose restart` + +#### Robot Not Responding + +Robot connected but actions don't execute. + +**Solutions:** +1. Wake up robot: `ssh nao@IP python -c "from naoqi import ALProxy; p=ALProxy('ALMotion','IP',9559);p.wakeUp()"` +2. Check robot is not in rest mode +3. Verify no blocking software on robot + +#### Action Timeout + +``` +Error: Action timed out after 30 seconds +``` + +**Solutions:** +1. Robot may be busy with previous action +2. Check network latency +3. Increase timeout in settings + +### Diagnostic Commands + +```bash +# Check Docker containers +docker ps + +# View all logs +docker compose logs + +# View specific service +docker compose logs ros_bridge + +# Restart services +docker compose restart + +# Stop and start fresh +docker compose down +docker compose up -d +``` + +### Network Troubleshooting + +```bash +# Check robot IP +ssh nao@IP "ifconfig" + +# Test from robot +ssh nao@IP "curl localhost:9090" + +# Check firewall +sudo iptables -L +``` + +## Step 7: Robot Maintenance + +### Battery Management + +- Check battery before each session +- Aim for >50% battery +- Charge during breaks +- Replace battery if <20% capacity + +### Calibration + +Periodically calibrate: +- Joint positions +- Camera alignment +- Touch sensors +- Sound localization + +### Software Updates + +Keep robot software updated: +- NAOqi version +- ROS2 packages +- HRIStudio plugin + +## Security Considerations + +### Network Security + +- Use encrypted network (WPA2/WPA3) +- Firewall robot from internet +- Use strong passwords + +### SSH Access + +- Change default passwords +- Use SSH keys when possible +- Limit SSH access + +### Data Security + +- Robot camera data may be sensitive +- Store data securely +- Follow IRB guidelines + +## Simulation Mode + +For testing without a robot: + +1. Enable simulation mode in settings +2. Or set `NEXT_PUBLIC_SIMULATION_MODE=true` +3. All actions are simulated locally + +See [Simulation Mode Tutorial](09-simulation-mode.md) for details. + +## Next Steps + +Now that your robot is connected: + +1. **[Running Trials](04-running-trials.md)** - Execute trials with robot +2. **[Wizard Interface](05-wizard-interface.md)** - Control the robot +3. **[Data & Analysis](08-data-and-analysis.md)** - Collect interaction data + +--- + +**Previous**: [Wizard Interface](05-wizard-interface.md) | **Next**: [Forms & Surveys](07-forms-and-surveys.md) diff --git a/docs/tutorials/07-forms-and-surveys.md b/docs/tutorials/07-forms-and-surveys.md new file mode 100644 index 0000000..7be30c2 --- /dev/null +++ b/docs/tutorials/07-forms-and-surveys.md @@ -0,0 +1,505 @@ +# Tutorial 7: Forms & Surveys + +Learn how to create and manage consent forms, surveys, and questionnaires. + +## Objectives + +- Create consent forms for IRB compliance +- Build post-session surveys +- Collect participant responses +- Manage form templates + +## Form Types + +HRIStudio supports three form types: + +| Type | Purpose | When | +|------|---------|------| +| **Consent** | Informed consent for participation | Before trial | +| **Survey** | Collect feedback and observations | After trial | +| **Questionnaire** | Demographic data collection | Any time | + +## Step 1: Access Forms + +1. Go to your **Study** +2. Click **Forms** tab +3. View existing forms and templates + +### Form List View + +``` +┌─────────────────────────────────────────────────────────────┐ +│ Forms [+ Create] │ +├─────────────────────────────────────────────────────────────┤ +│ Name Type Responses Status │ +│ ─────────────────────────────────────────────────────────── │ +│ Informed Consent Consent 12/20 Active │ +│ Post-Session Survey Survey 8/20 Active │ +│ Demographics Questionnaire 15/20 Active │ +│ Template: Standard Consent - Template │ +│ Template: Feedback Survey - Template │ +└─────────────────────────────────────────────────────────────┘ +``` + +## Step 2: Create a Form + +### Using a Template + +1. Click **Create Form** +2. Select **Use Template** +3. Choose template: + - Informed Consent + - Post-Session Survey + - Demographics +4. Customize as needed + +### From Scratch + +1. Click **Create Form** +2. Select **Blank Form** +3. Choose form type +4. Build fields manually + +## Step 3: Form Builder + +The form builder lets you create custom fields: + +``` +┌─────────────────────────────────────────────────────────────┐ +│ Form Builder: Post-Session Survey │ +├─────────────────────────────────────────────────────────────┤ +│ │ +│ Form Settings │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ Title: Post-Session Survey │ │ +│ │ Type: Survey │ │ +│ │ Active: ☑ │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ +│ Fields │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ 1. [Rating] How engaging was the robot? [✕] │ │ +│ │ 2. [Text] What did you enjoy most? [✕] │ │ +│ │ 3. [Multiple Choice] Robot personality? [✕] │ │ +│ │ │ │ +│ │ [+ Add Field] │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ +│ Preview │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ How engaging was the robot? │ │ +│ │ ○ 1 ○ 2 ○ 3 ○ 4 ○ 5 │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ +│ [Cancel] [Save] │ +└─────────────────────────────────────────────────────────────┘ +``` + +## Step 4: Field Types + +### Text Field + +``` +┌─────────────────────────────────────────────────────────────┐ +│ Field Type: Text │ +├─────────────────────────────────────────────────────────────┤ +│ Label: Participant Age │ +│ Required: ☑ │ +│ Placeholder: e.g., 25 │ +│ │ +│ Preview: │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ Participant Age * │ │ +│ │ [e.g., 25 ] │ │ +│ └─────────────────────────────────────────────────────┘ │ +└─────────────────────────────────────────────────────────────┘ +``` + +### Rating Scale + +``` +┌─────────────────────────────────────────────────────────────┐ +│ Field Type: Rating │ +├─────────────────────────────────────────────────────────────┤ +│ Label: How engaging was the robot? │ +│ Required: ☑ │ +│ Scale: 1 to [5] │ +│ Low Label: Not at all engaging │ +│ High Label: Very engaging │ +│ │ +│ Preview: │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ How engaging was the robot? * │ │ +│ │ │ │ +│ │ 1 2 3 4 5 │ │ +│ │ ○ ○ ○ ○ ○ │ │ +│ │ Not at all Very engaging │ │ +│ └─────────────────────────────────────────────────────┘ │ +└─────────────────────────────────────────────────────────────┘ +``` + +### Multiple Choice + +``` +┌─────────────────────────────────────────────────────────────┐ +│ Field Type: Multiple Choice │ +├─────────────────────────────────────────────────────────────┤ +│ Label: Did the robot respond appropriately? │ +│ Required: ☑ │ +│ Options: │ +│ 1. Yes, always │ +│ 2. Yes, most of the time │ +│ 3. Sometimes │ +│ 4. Rarely │ +│ 5. No │ +│ │ +│ Preview: │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ Did the robot respond appropriately? * │ │ +│ │ │ │ +│ │ ○ Yes, always │ │ +│ │ ○ Yes, most of the time │ │ +│ │ ○ Sometimes │ │ +│ │ ○ Rarely │ │ +│ │ ○ No │ │ +│ └─────────────────────────────────────────────────────┘ │ +└─────────────────────────────────────────────────────────────┘ +``` + +### Yes/No + +``` +┌─────────────────────────────────────────────────────────────┐ +│ Field Type: Yes/No │ +├─────────────────────────────────────────────────────────────┤ +│ Label: Would you interact with this robot again? │ +│ Required: ☐ │ +│ │ +│ Preview: │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ Would you interact with this robot again? │ │ +│ │ │ │ +│ │ ○ Yes ○ No │ │ +│ └─────────────────────────────────────────────────────┘ │ +└─────────────────────────────────────────────────────────────┘ +``` + +### Text Area + +``` +┌─────────────────────────────────────────────────────────────┐ +│ Field Type: Text Area │ +├─────────────────────────────────────────────────────────────┤ +│ Label: What did you enjoy most about the interaction? │ +│ Required: ☐ │ +│ Rows: [4] │ +│ │ +│ Preview: │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ What did you enjoy most about the interaction? │ │ +│ │ │ │ +│ │ [ ] │ │ +│ │ [ ] │ │ +│ │ [ ] │ │ +│ │ [ ] │ │ +│ └─────────────────────────────────────────────────────┘ │ +└─────────────────────────────────────────────────────────────┘ +``` + +### Date + +``` +┌─────────────────────────────────────────────────────────────┐ +│ Field Type: Date │ +├─────────────────────────────────────────────────────────────┤ +│ Label: Session Date │ +│ Required: ☑ │ +│ │ +│ Preview: │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ Session Date * │ │ +│ │ [📅 Select date ] │ │ +│ └─────────────────────────────────────────────────────┘ │ +└─────────────────────────────────────────────────────────────┘ +``` + +### Signature + +``` +┌─────────────────────────────────────────────────────────────┐ +│ Field Type: Signature │ +├─────────────────────────────────────────────────────────────┤ +│ Label: Participant Signature │ +│ Required: ☑ │ +│ │ +│ Preview: │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ Participant Signature * │ │ +│ │ │ │ +│ │ ┌───────────────────────────────────────────────┐ │ │ +│ │ │ │ │ │ +│ │ │ [Sign here] │ │ │ +│ │ │ │ │ │ +│ │ └───────────────────────────────────────────────┘ │ │ +│ └─────────────────────────────────────────────────────┘ │ +└─────────────────────────────────────────────────────────────┘ +``` + +## Step 5: Consent Forms + +### Required Elements + +For IRB compliance, consent forms must include: + +- [ ] Study title and purpose +- [ ] Principal investigator +- [ ] Procedures description +- [ ] Risks and benefits +- [ ] Confidentiality statement +- [ ] Voluntary participation note +- [ ] Signature and date fields + +### Consent Form Template + +```json +{ + "title": "Informed Consent", + "type": "consent", + "fields": [ + { "type": "text", "label": "Study Title", "required": true }, + { "type": "text", "label": "Principal Investigator", "required": true }, + { "type": "textarea", "label": "Purpose of the Study", "required": true }, + { "type": "textarea", "label": "Procedures", "required": true }, + { "type": "textarea", "label": "Risks and Benefits", "required": true }, + { "type": "textarea", "label": "Confidentiality", "required": true }, + { "type": "yes_no", "label": "I consent to participate", "required": true }, + { "type": "signature", "label": "Participant Signature", "required": true }, + { "type": "date", "label": "Date", "required": true } + ] +} +``` + +## Step 6: Surveys + +### Post-Session Survey Example + +```json +{ + "title": "Post-Session Questionnaire", + "type": "survey", + "fields": [ + { + "type": "rating", + "label": "How engaging was the robot?", + "settings": { "scale": 5 } + }, + { + "type": "rating", + "label": "How natural did the interaction feel?", + "settings": { "scale": 5 } + }, + { + "type": "multiple_choice", + "label": "Did the robot respond appropriately?", + "options": ["Always", "Usually", "Sometimes", "Rarely", "Never"] + }, + { + "type": "textarea", + "label": "What did you like most?" + }, + { + "type": "textarea", + "label": "What could be improved?" + } + ] +} +``` + +### Questionnaire Example (Demographics) + +```json +{ + "title": "Demographics", + "type": "questionnaire", + "fields": [ + { "type": "text", "label": "Age" }, + { + "type": "multiple_choice", + "label": "Gender", + "options": ["Male", "Female", "Non-binary", "Prefer not to say"] + }, + { + "type": "multiple_choice", + "label": "Experience with robots", + "options": ["None", "A little", "Moderate", "Extensive"] + } + ] +} +``` + +## Step 7: Form Versions + +Forms support versioning for IRB compliance: + +1. Create new version when modifying: + - Question text changes + - New fields added + - Required fields changed + +2. Version history: + ``` + Version 1 (Current) - Active + Version 2 - Draft + Version 3 - Archived + ``` + +3. Track changes: + - Version number + - Change date + - Change description + +## Step 8: Distributing Forms + +### Automatic Distribution + +Configure automatic form sending: + +1. Open form settings +2. Enable **Auto-distribute** +3. Set trigger: + - Before trial (consent) + - After trial (survey) +4. Select participants + +### Manual Distribution + +Send forms manually: + +1. Open form +2. Click **Distribute** +3. Select participants +4. Choose delivery method + +### Participant Link + +Generate shareable link: + +``` +https://hristudio.example.com/forms/{formId}?participant={participantCode} +``` + +## Step 9: Collecting Responses + +### View Responses + +1. Open form +2. Click **Responses** tab +3. View individual submissions + +### Response Dashboard + +``` +┌─────────────────────────────────────────────────────────────┐ +│ Form Responses: Post-Session Survey │ +├─────────────────────────────────────────────────────────────┤ +│ Total Responses: 15/20 (75%) │ +│ │ +│ Question: How engaging was the robot? │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ 5 ████████████████████████████████████ 8 responses │ │ +│ │ 4 ██████████████████ 5 responses │ │ +│ │ 3 ████████ 2 responses │ │ +│ │ 2 ████ 1 response │ │ +│ │ 1 ████ 1 response │ │ +│ │ │ │ +│ │ Average: 4.2 / 5.0 │ │ +│ └─────────────────────────────────────────────────────┘ │ +└─────────────────────────────────────────────────────────────┘ +``` + +### Export Responses + +Download collected data: + +| Format | Contents | +|--------|----------| +| CSV | Tabular data | +| JSON | Full response objects | +| PDF | Printed consent forms | + +## Step 10: Form Templates + +### Creating Templates + +1. Create form with desired fields +2. Click **Save as Template** +3. Enter template name +4. Template is available for reuse + +### Template Library + +| Template | Use Case | +|----------|----------| +| Standard Consent | Generic research consent | +| Child Consent | Studies with minors | +| Extended Consent | Complex procedures | +| Feedback Survey | Post-session feedback | +| NASA-TLX | Workload assessment | +| SUS | System usability | + +## Best Practices + +### Consent Forms + +- [ ] Review with IRB before use +- [ ] Keep language simple +- [ ] Include all required elements +- [ ] Version control for changes +- [ ] Store signed forms securely + +### Surveys + +- [ ] Keep questions concise +- [ ] Use appropriate scales +- [ ] Test with pilot participants +- [ ] Randomize order when appropriate +- [ ] Include open-ended questions + +### Data Management + +- [ ] Export data regularly +- [ ] Backup responses +- [ ] Anonymize data for analysis +- [ ] Follow data retention policy + +## Troubleshooting + +### Form Not Loading + +- Check form is active +- Verify participant access +- Check network connection + +### Response Not Saving + +- Check required fields +- Verify session active +- Try again or refresh + +### Participant Can't Access + +- Verify participant code valid +- Check form is distributed +- Confirm study is active + +## Next Steps + +Now that you've created your forms: + +1. **[Running Trials](04-running-trials.md)** - Connect forms to trials +2. **[Data & Analysis](08-data-and-analysis.md)** - Analyze collected data +3. **[Your First Study](02-your-first-study.md)** - Set up your study + +--- + +**Previous**: [Robot Integration](06-robot-integration.md) | **Next**: [Data & Analysis](08-data-and-analysis.md) diff --git a/docs/tutorials/08-data-and-analysis.md b/docs/tutorials/08-data-and-analysis.md new file mode 100644 index 0000000..fca3508 --- /dev/null +++ b/docs/tutorials/08-data-and-analysis.md @@ -0,0 +1,505 @@ +# Tutorial 8: Data & Analysis + +Learn how to collect, export, and analyze trial data from HRIStudio. + +## Objectives + +- Understand data collection in HRIStudio +- Export trial data in various formats +- Analyze event logs +- Generate reports + +## Data Collection Overview + +HRIStudio automatically captures comprehensive data during trials: + +``` +┌─────────────────────────────────────────────────────────────┐ +│ Data Collection │ +├─────────────────────────────────────────────────────────────┤ +│ │ +│ Trial Metadata │ +│ ├── Start/End times │ +│ ├── Duration │ +│ ├── Participant info │ +│ └── Experiment version │ +│ │ +│ Event Log (Timestamped) │ +│ ├── Step changes │ +│ ├── Action executions │ +│ ├── Robot responses │ +│ └── Wizard interventions │ +│ │ +│ Form Responses │ +│ ├── Consent forms │ +│ ├── Surveys │ +│ └── Questionnaires │ +│ │ +│ Sensor Data │ +│ ├── Joint positions │ +│ ├── Touch events │ +│ └── Audio/video (if enabled) │ +│ │ +└─────────────────────────────────────────────────────────────┘ +``` + +## Step 1: Accessing Trial Data + +### From Trial List + +1. Go to **Trials** tab +2. Find completed trial +3. Click **View Details** + +### From Study Dashboard + +1. Open your study +2. Go to **Data** tab +3. Select trial or view aggregate + +## Step 2: Trial Event Log + +Each trial generates a complete event log: + +```json +{ + "trialId": "trial_abc123", + "participantCode": "P001", + "experimentName": "Interactive Storyteller", + "startedAt": "2024-03-15T14:00:00Z", + "completedAt": "2024-03-15T14:05:23Z", + "duration": 323, + "status": "completed", + "events": [ + { + "timestamp": "2024-03-15T14:00:00.123Z", + "type": "trial_started", + "stepId": null, + "data": {} + }, + { + "timestamp": "2024-03-15T14:00:02.456Z", + "type": "step_changed", + "stepId": "step_1", + "stepName": "The Hook", + "data": {} + }, + { + "timestamp": "2024-03-15T14:00:03.789Z", + "type": "action_executed", + "actionName": "Say Text", + "parameters": { "text": "Hello!" }, + "duration": 2300, + "status": "completed" + }, + { + "timestamp": "2024-03-15T14:00:08.012Z", + "type": "action_executed", + "actionName": "Wave", + "duration": 1500, + "status": "completed" + }, + { + "timestamp": "2024-03-15T14:02:30.123Z", + "type": "intervention", + "interventionType": "note", + "data": { "note": "Participant laughed" } + }, + { + "timestamp": "2024-03-15T14:03:00.456Z", + "type": "wizard_response", + "variable": "last_response", + "selectedValue": "correct", + "data": {} + }, + { + "timestamp": "2024-03-15T14:05:23.789Z", + "type": "trial_completed", + "data": { "stepsCompleted": 6 } + } + ] +} +``` + +### Event Types + +| Event Type | Description | Data Captured | +|------------|-------------|---------------| +| `trial_started` | Trial began | Timestamp | +| `step_changed` | New step began | Step ID, name | +| `action_executed` | Robot action | Action details, duration | +| `action_completed` | Action finished | Duration, result | +| `action_failed` | Action failed | Error details | +| `wizard_response` | Wizard decision | Selected option | +| `intervention` | Wizard intervention | Type, note | +| `trial_paused` | Trial paused | Reason | +| `trial_resumed` | Trial resumed | Pause duration | +| `trial_completed` | Trial finished | Summary | + +## Step 3: Exporting Data + +### Export Single Trial + +1. Open trial details +2. Click **Export** +3. Select format + +### Export Study Data + +1. Open study +2. Go to **Data** tab +3. Click **Export All** +4. Select options: + - Date range + - Trial status + - Include forms + +### Export Formats + +#### CSV Format + +```csv +trial_id,participant,experiment,started_at,duration,status,steps_completed +trial_abc,P001,Interactive Storyteller,2024-03-15T14:00:00Z,323,completed,6 +trial_def,P002,Interactive Storyteller,2024-03-15T14:20:00Z,298,completed,6 +trial_ghi,P003,Interactive Storyteller,2024-03-15T14:40:00Z,0,failed,1 +``` + +#### JSON Format + +```json +{ + "exportDate": "2024-03-15T15:00:00Z", + "studyName": "Robot Trust Study", + "trials": [...], + "forms": [...], + "metadata": { + "totalTrials": 20, + "completedTrials": 18, + "averageDuration": 312 + } +} +``` + +#### Event Log CSV + +```csv +timestamp,event_type,step_name,action_name,parameters,duration,status +2024-03-15T14:00:00.123Z,trial_started,,,,, +2024-03-15T14:00:02.456Z,step_changed,The Hook,,,, +2024-03-15T14:00:03.789Z,action_executed,The Hook,Say Text,"{""text"":""Hello!""}",2300,completed +2024-03-15T14:00:08.012Z,action_executed,The Hook,Wave,,1500,completed +2024-03-15T14:02:30.123Z,intervention,The Narrative,Note,"{""note"":""Participant laughed""}",,, +2024-03-15T14:03:00.456Z,wizard_response,Comprehension Check,Correct,,,, +2024-03-15T14:05:23.789Z,trial_completed,,,,323, +``` + +## Step 4: Data Dashboard + +### Study Dashboard + +View aggregate statistics: + +``` +┌─────────────────────────────────────────────────────────────┐ +│ Study Dashboard: Robot Trust Study │ +├─────────────────────────────────────────────────────────────┤ +│ │ +│ Overview │ +│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ +│ │ 20 │ │ 18 │ │ 5m12s │ │ 2 │ │ +│ │ Trials │ │ Complete│ │ Avg Time│ │ Failed │ │ +│ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │ +│ │ +│ Completion Rate │ +│ ████████████████████████████████████░░░░ 90% │ +│ │ +│ Timeline │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ P001 ████████████████████████████████ 5:23 │ │ +│ │ P002 ██████████████████████████████ 5:02 │ │ +│ │ P003 ██████████████████████████ 4:45 │ │ +│ │ ... │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ +└─────────────────────────────────────────────────────────────┘ +``` + +### Metrics + +| Metric | Description | +|--------|-------------| +| Total Trials | Number of scheduled trials | +| Completed | Successfully completed trials | +| Average Duration | Mean trial time | +| Completion Rate | % of trials completed | +| Failed | Trials that failed | +| Average Steps | Mean steps per trial | + +## Step 5: Analyzing Event Data + +### Timing Analysis + +Calculate action durations: + +```python +import json + +with open('trial_events.json') as f: + data = json.load(f) + +# Calculate action durations +for event in data['events']: + if event['type'] == 'action_executed': + duration = event.get('duration', 0) + print(f"{event['actionName']}: {duration/1000:.1f}s") +``` + +### Intervention Analysis + +Track wizard interventions: + +```python +# Count interventions by type +interventions = [ + e for e in data['events'] + if e['type'] == 'intervention' +] + +by_type = {} +for i in interventions: + itype = i['data'].get('type', 'unknown') + by_type[itype] = by_type.get(itype, 0) + 1 + +print(by_type) +# {'note': 15, 'pause': 3, 'alert': 1} +``` + +### Branch Selection Analysis + +Analyze wizard decisions: + +```python +# Get wizard responses +responses = [ + e for e in data['events'] + if e['type'] == 'wizard_response' +] + +# Count by value +by_value = {} +for r in responses: + value = r.get('selectedValue', 'unknown') + by_value[value] = by_value.get(value, 0) + 1 + +print(by_value) +# {'correct': 12, 'incorrect': 6} +``` + +## Step 6: Form Data Analysis + +### Response Aggregation + +Aggregate survey responses: + +```python +# Calculate average rating +ratings = [ + r['responses']['engagement_rating'] + for r in form_responses +] + +avg_rating = sum(ratings) / len(ratings) +print(f"Average engagement: {avg_rating:.2f}/5") +``` + +### Cross-Tabulation + +Compare responses across conditions: + +``` + | Condition A | Condition B | Total +--------------------|------------|-------------|------- +Robot engaged | 4.2 | 4.5 | 4.35 +Natural interaction | 3.8 | 4.1 | 3.95 +Would use again | 78% | 85% | 81% +``` + +## Step 7: Data Visualization + +### Trial Timeline + +Visualize trial progression: + +``` +P001: ████████████████░░░░░░░░░░░░░░░░░ 5:23 +P002: ███████████████░░░░░░░░░░░░░░░░░░ 4:58 +P003: ██████████████████████████████░░░░ 6:02 +P004: ████████████████░░░░░░░░░░░░░░░░░░ 5:15 +``` + +### Action Distribution + +``` +Action Frequency +──────────────── +Say Text ████████████████████ 45 +Wave ████████████ 25 +Turn Head ████████████ 25 +Move Arm ████ 5 +``` + +### Branch Outcomes + +``` +Branch Selection +──────────────── +Correct Response (A): ██████████████████████████ 67% +Incorrect Response (B): █████████████ 33% +``` + +## Step 8: Generating Reports + +### Trial Summary Report + +Generate PDF summary: + +``` +═══════════════════════════════════════════════════════════ + TRIAL SUMMARY REPORT +═══════════════════════════════════════════════════════════ + +Study: Robot Trust Study +Participant: P001 +Date: March 15, 2024 +Experiment: Interactive Storyteller v1 + +EXECUTIVE SUMMARY +─────────────────────────────────────────────────────────── +Duration: 5 minutes 23 seconds +Status: Completed successfully +Steps Completed: 6/6 +Interventions: 2 + +TIMELINE +─────────────────────────────────────────────────────────── +14:00:00 Trial started +14:00:02 Step 1: The Hook +14:00:08 Step 2: The Narrative +14:02:30 Wizard note: "Participant engaged" +14:03:00 Step 3: Comprehension Check +14:03:28 Branch selected: Correct +14:03:30 Step 4a: Correct Response +14:05:23 Trial completed + +METRICS +─────────────────────────────────────────────────────────── +Actions Executed: 12 +Action Success Rate: 100% +Average Action Duration: 2.1s +Wizard Intervention Rate: 0.37/min + +═══════════════════════════════════════════════════════════ +``` + +### Study Report + +Aggregate across participants: + +``` +═══════════════════════════════════════════════════════════ + STUDY REPORT +═══════════════════════════════════════════════════════════ + +Study: Robot Trust Study +Date Range: March 1-15, 2024 +Participants: 20 + +PARTICIPATION +─────────────────────────────────────────────────────────── +Enrolled: 20 +Completed: 18 (90%) +Withdrew: 1 (5%) +Failed: 1 (5%) + +TIMING +─────────────────────────────────────────────────────────── +Mean Duration: 5m 12s ± 28s +Min Duration: 4m 45s +Max Duration: 6m 02s + +INTERVENTIONS +─────────────────────────────────────────────────────────── +Total Interventions: 34 +Notes: 25 (73%) +Pauses: 7 (21%) +Alerts: 2 (6%) + +BRANCH SELECTION +─────────────────────────────────────────────────────────── +Branch A (Correct): 12 (67%) +Branch B (Incorrect): 6 (33%) + +═══════════════════════════════════════════════════════════ +``` + +## Step 9: Data Privacy + +### Anonymization + +Remove identifying information: + +```python +# Replace participant codes with anonymous IDs +participant_map = { + 'P001': 'S001', + 'P002': 'S002', + 'P003': 'S003', +} +``` + +### Export Settings + +Configure export options: + +| Option | Description | +|--------|-------------| +| Include participant codes | Keep or anonymize | +| Include timestamps | Full or relative | +| Include notes | Include/exclude | +| Include form responses | Include/exclude | + +## Best Practices + +### Data Collection + +- [ ] Enable all event logging +- [ ] Configure sensor data capture +- [ ] Set up automatic backups +- [ ] Test data export before study + +### Data Storage + +- [ ] Export regularly (daily/weekly) +- [ ] Store in secure location +- [ ] Follow IRB data retention +- [ ] Backup critical data + +### Data Analysis + +- [ ] Document analysis methods +- [ ] Track protocol versions +- [ ] Note data quality issues +- [ ] Share data dictionary + +## Next Steps + +Now that you understand data collection: + +1. **[Your First Study](02-your-first-study.md)** - Apply data practices +2. **[Simulation Mode](09-simulation-mode.md)** - Test data collection +3. **[Running Trials](04-running-trials.md)** - Practice with data capture + +--- + +**Previous**: [Forms & Surveys](07-forms-and-surveys.md) | **Next**: [Simulation Mode](09-simulation-mode.md) diff --git a/docs/tutorials/09-simulation-mode.md b/docs/tutorials/09-simulation-mode.md new file mode 100644 index 0000000..ea2ff22 --- /dev/null +++ b/docs/tutorials/09-simulation-mode.md @@ -0,0 +1,389 @@ +# Tutorial 9: Simulation Mode + +Learn how to test HRIStudio experiments without a physical robot. + +## Objectives + +- Enable simulation mode +- Use the mock robot server +- Test experiments end-to-end +- Practice trial execution + +## Why Simulation Mode? + +Simulation mode allows you to: + +- **Test protocols** without a robot +- **Train wizards** before live sessions +- **Debug experiments** in development +- **Run pilots** without robot access +- **Develop** on any computer + +## Understanding Simulation Options + +HRIStudio offers two simulation approaches: + +| Approach | Pros | Cons | +|----------|------|------| +| **Client-side** | No server needed, instant | Limited robot simulation | +| **Mock Server** | Full rosbridge protocol | Requires running server | + +### Client-Side Simulation + +Simulates robot locally in the browser: +- No network required +- Instant startup +- Basic action timing +- Fake sensor data + +### Mock Server + +Full WebSocket server simulating rosbridge: +- Complete protocol support +- Realistic timing +- Sensor data simulation +- Better for integration testing + +## Step 1: Enable Client-Side Simulation + +### Quick Start + +1. Create or edit `hristudio/.env.local` +2. Add: + ```bash + NEXT_PUBLIC_SIMULATION_MODE=true + ``` +3. Restart the dev server: + ```bash + bun dev + ``` + +### Verify Enabled + +Look for the simulation indicator in the UI: + +``` +┌─────────────────────────────────────────────────────────────┐ +│ Wizard Interface [🔵 SIMULATION MODE] │ +├─────────────────────────────────────────────────────────────┤ +``` + +### Features Available + +In simulation mode: + +- ✅ All robot actions execute (simulated timing) +- ✅ Speech actions show estimated duration +- ✅ Movement actions track position +- ✅ Sensor data is simulated +- ✅ Trial execution works normally +- ❌ Real robot not controlled +- ❌ Physical interactions not possible + +## Step 2: Start Mock Robot Server + +For more complete testing, use the mock server: + +### Option 1: Standalone Server + +```bash +cd hristudio/scripts/mock-robot +bun install +bun dev +``` + +Server starts on `ws://localhost:9090` + +### Option 2: Docker + +```bash +cd nao6-hristudio-integration +docker compose -f docker-compose.yml -f docker-compose.mock.yml --profile mock up -d +``` + +### Verify Server Running + +```bash +# Check container +docker ps + +# Should show: +# CONTAINER ID IMAGE STATUS +# abc123def456 hristudio-mock-robot Up 2 minutes +``` + +## Step 3: Connect to Mock Server + +1. Go to the **NAO Test Page**: `/nao-test` +2. Ensure `NEXT_PUBLIC_SIMULATION_MODE` is NOT set (or set to false) +3. Click **Connect** +4. You should see: + ``` + Connected to rosbridge + Subscribed to: /joint_states, /bumper, /sonar/left, ... + ``` + +## Step 4: Test Robot Actions + +### From NAO Test Page + +1. **Speech Test** + - Enter text: "Hello, this is a test" + - Click **Say** + - See simulated speech duration + +2. **Movement Test** + - Set walk speed: 0.1 m/s + - Click **Walk Forward** + - Watch position update + +3. **Head Control** + - Set yaw: 1.0, pitch: 0.0 + - Click **Move Head** + - See joint angles update + +### From Wizard Interface + +1. Start a trial +2. Execute actions as normal +3. Actions are sent to mock server +4. Mock server responds with simulated data + +## Step 5: Simulated Actions Reference + +### Speech Actions + +| Action | Simulation Behavior | +|--------|---------------------| +| `say_text` | Duration = 1.5s + 300ms × word_count | +| `say_with_emotion` | Duration = 1.5s + 300ms × word_count + emotion_overhead | +| `wave_goodbye` | Duration = 3.0s | + +### Movement Actions + +| Action | Simulation Behavior | +|--------|---------------------| +| `walk_forward` | Position updates over 500ms | +| `walk_backward` | Position updates over 500ms | +| `turn_left` | Angle decreases over 500ms | +| `turn_right` | Angle increases over 500ms | +| `stop` | Velocity set to 0 | + +### Sensor Simulation + +| Sensor | Simulated Value | +|--------|-----------------| +| Battery | 85% ± 2% variation | +| Joint States | Random positions ±0.1 rad | +| Bumper | False (no contact) | +| Sonar | 0.5-1.0m (random) | +| Touch | False (no touch) | + +## Step 6: Testing Experiment Protocols + +### Full Protocol Test + +1. Enable simulation mode +2. Create or open experiment +3. Schedule trial +4. Start trial in wizard interface +5. Execute through all steps +6. Verify timing and flow + +### Test Checklist + +- [ ] All steps execute in order +- [ ] Branching decisions work +- [ ] Timing estimates are accurate +- [ ] Event log captures everything +- [ ] No errors or warnings +- [ ] Trial completes successfully + +### Debug Mode + +Enable verbose logging: + +```bash +# In browser console, run: +localStorage.setItem('debug', 'true') + +# Refresh page +# Now see detailed action logs in console +``` + +## Step 7: Training Wizards + +Simulation mode is perfect for training: + +### Training Scenario 1: Basic Operation + +1. Enable simulation mode +2. Load simple experiment +3. Practice: + - Starting/pausing trials + - Executing quick actions + - Adding notes + +### Training Scenario 2: Decision Making + +1. Load branching experiment +2. Practice: + - Observing participant cues + - Selecting appropriate branches + - Documenting decisions + +### Training Scenario 3: Handling Issues + +1. Practice: + - Pausing for breaks + - Responding to alerts + - Stopping trials early + +## Step 8: Development Workflow + +### TDD with Simulation + +``` +┌─────────────────────────────────────────────────────────────┐ +│ Development Cycle │ +├─────────────────────────────────────────────────────────────┤ +│ │ +│ 1. Design experiment in UI │ +│ │ │ +│ ▼ │ +│ 2. Enable simulation mode │ +│ │ │ +│ ▼ │ +│ 3. Run test trial │ +│ │ │ +│ ▼ │ +│ 4. Review event log │ +│ │ │ +│ ▼ │ +│ 5. Fix issues found │ +│ │ │ +│ └────────────┐ │ +│ │ │ +│ └ (repeat) │ +│ │ +└─────────────────────────────────────────────────────────────┘ +``` + +### Testing Checklist + +Before running real trials: + +- [ ] Experiment works in simulation +- [ ] All actions execute correctly +- [ ] Timing is acceptable +- [ ] Branching works as expected +- [ ] Wizard notes function properly +- [ ] Data exports correctly + +## Step 9: Transitioning to Real Robot + +When ready to test with real robot: + +### Step 1: Disable Simulation + +Remove or set to false: +```bash +NEXT_PUBLIC_SIMULATION_MODE=false +``` + +### Step 2: Connect Robot + +1. Start Docker services +2. Verify robot connection +3. Test with NAO Test Page + +### Step 3: Run Comparison Trial + +1. Run same experiment on real robot +2. Compare timing and behavior +3. Adjust parameters as needed + +### Step 4: Document Differences + +Note any protocol adjustments needed: +- Timing differences +- Action parameter changes +- Branch criteria updates + +## Troubleshooting + +### Simulation Actions Not Working + +1. Check `NEXT_PUBLIC_SIMULATION_MODE=true` is set +2. Verify no errors in browser console +3. Try refreshing the page + +### Mock Server Connection Failed + +```bash +# Check if server is running +docker ps | grep mock + +# Check server logs +docker compose logs mock_robot + +# Restart if needed +docker compose restart mock_robot +``` + +### Actions Execute But Nothing Happens + +1. Check WebSocket URL is correct +2. Verify port 9090 is not blocked +3. Try client-side simulation instead + +## Comparison: Simulation vs Real + +| Aspect | Simulation | Real Robot | +|--------|------------|------------| +| Setup time | 1 min | 30+ min | +| Availability | Always | Requires robot | +| Cost | Free | Robot access needed | +| Timing accuracy | Estimated | Actual | +| Physical interaction | ✗ | ✓ | +| Sensor accuracy | Fake | Real | +| Network dependent | No | Yes | + +## Best Practices + +### When to Use Simulation + +- During experiment design +- While robot unavailable +- For wizard training +- For debugging protocols +- For quick iteration + +### When to Use Real Robot + +- Final protocol validation +- Timing accuracy critical +- Physical interaction matters +- Sensor data needed +- Pre-study pilot + +### Transition Checklist + +Before real trials: +- [ ] Protocol tested in simulation +- [ ] Timing verified +- [ ] Actions calibrated +- [ ] Wizard team trained +- [ ] Backup plan ready + +## Next Steps + +Now that you've mastered simulation: + +1. **[Robot Integration](06-robot-integration.md)** - Connect real robot +2. **[Running Trials](04-running-trials.md)** - Execute live trials +3. **[Your First Study](02-your-first-study.md)** - Run complete study + +--- + +**Previous**: [Data & Analysis](08-data-and-analysis.md) | **Back**: [Tutorials Overview](../tutorials/README.md) diff --git a/docs/tutorials/README.md b/docs/tutorials/README.md new file mode 100644 index 0000000..5ad259d --- /dev/null +++ b/docs/tutorials/README.md @@ -0,0 +1,71 @@ +# HRIStudio Tutorials + +Welcome to the HRIStudio tutorials! These guides will help you get up and running with the platform for your HRI research. + +## Tutorial Overview + +| Tutorial | Description | Time | +|----------|-------------|------| +| **[Getting Started](tutorials/01-getting-started.md)** | Installation, setup, and first login | 10 min | +| **[Your First Study](tutorials/02-your-first-study.md)** | Creating a study and adding team members | 15 min | +| **[Designing Experiments](tutorials/03-designing-experiments.md)** | Building experiment protocols with blocks | 25 min | +| **[Running Trials](tutorials/04-running-trials.md)** | Executing trials and managing participants | 20 min | +| **[Wizard Interface](tutorials/05-wizard-interface.md)** | Real-time trial control and monitoring | 15 min | +| **[Robot Integration](tutorials/06-robot-integration.md)** | Connecting NAO6 and other robots | 20 min | +| **[Forms & Surveys](tutorials/07-forms-and-surveys.md)** | Creating consent forms and questionnaires | 15 min | +| **[Data & Analysis](tutorials/08-data-and-analysis.md)** | Collecting and exporting trial data | 15 min | +| **[Simulation Mode](tutorials/09-simulation-mode.md)** | Testing without a physical robot | 10 min | + +## Quick Navigation + +### For Researchers +1. [Getting Started](tutorials/01-getting-started.md) - Set up your environment +2. [Your First Study](tutorials/02-your-first-study.md) - Create your study +3. [Designing Experiments](tutorials/03-designing-experiments.md) - Build your protocol +4. [Running Trials](tutorials/04-running-trials.md) - Execute your study +5. [Data & Analysis](tutorials/08-data-and-analysis.md) - Analyze results + +### For Wizards +1. [Getting Started](tutorials/01-getting-started.md) - Basic setup +2. [Wizard Interface](tutorials/05-wizard-interface.md) - Control trials +3. [Robot Integration](tutorials/06-robot-integration.md) - Connect to robot + +### For Administrators +1. [Getting Started](tutorials/01-getting-started.md) - Full setup +2. [Robot Integration](tutorials/06-robot-integration.md) - Configure robots +3. [Forms & Surveys](tutorials/07-forms-and-surveys.md) - Manage templates + +## Common Workflows + +### Basic HRI Experiment +``` +Create Study → Design Experiment → Add Participants → Run Trials → Collect Data +``` + +### Wizard-of-Oz Study +``` +Create Study → Design Experiment with Wizard Blocks → Configure Robot → +Add Wizards → Run Trials with Live Control → Collect Data +``` + +### Pilot Testing +``` +Create Study → Design Experiment → Enable Simulation Mode → Run Test Trials → +Refine Protocol → Connect Real Robot → Run Study +``` + +## Prerequisites + +- **For local development**: Bun, Docker, PostgreSQL +- **For robot studies**: NAO6 robot or compatible robot +- **For cloud deployment**: Vercel, Cloudflare R2, PostgreSQL database + +## Getting Help + +- Check the [Quick Reference](../quick-reference.md) for common commands +- Review the [Implementation Guide](../implementation-guide.md) for technical details +- Visit the [NAO6 Integration](../nao6-quick-reference.md) for robot-specific help + +--- + +**Next**: [Getting Started](tutorials/01-getting-started.md) diff --git a/robot-plugins b/robot-plugins index d772aec..f83a207 160000 --- a/robot-plugins +++ b/robot-plugins @@ -1 +1 @@ -Subproject commit d772aecc54306b211cd97ff6f720936d38a6b8a9 +Subproject commit f83a207b16d5ceabfb6a25be35b99bfe4fe53ad1 diff --git a/scripts/mock-robot/.env.example b/scripts/mock-robot/.env.example new file mode 100644 index 0000000..7258746 --- /dev/null +++ b/scripts/mock-robot/.env.example @@ -0,0 +1,18 @@ +# Mock Robot Configuration +# Copy this file to .env and adjust as needed + +# Port for mock robot WebSocket server (default: 9090, same as rosbridge) +MOCK_ROBOT_PORT=9090 + +# How often to publish robot state (ms) +MOCK_PUBLISH_INTERVAL=100 + +# Robot configuration +MOCK_ROBOT_NAME=MOCK-NAO6 +MOCK_ROBOT_VERSION=6.0 +MOCK_BATTERY_LEVEL=85 + +# Enable simulation features +MOCK_ENABLE_SPEECH=true +MOCK_ENABLE_MOVEMENT=true +MOCK_ENABLE_SENSORS=true diff --git a/scripts/mock-robot/package.json b/scripts/mock-robot/package.json new file mode 100644 index 0000000..798cc1a --- /dev/null +++ b/scripts/mock-robot/package.json @@ -0,0 +1,21 @@ +{ + "name": "@hristudio/mock-robot", + "version": "1.0.0", + "description": "Mock robot server for simulating NAO6 robot connections", + "type": "module", + "main": "dist/server.js", + "scripts": { + "dev": "tsx watch src/server.ts", + "build": "tsc", + "start": "node dist/server.js" + }, + "dependencies": { + "ws": "^8.16.0" + }, + "devDependencies": { + "@types/node": "^20.11.0", + "@types/ws": "^8.5.10", + "tsx": "^4.7.0", + "typescript": "^5.3.3" + } +} diff --git a/scripts/mock-robot/src/server.ts b/scripts/mock-robot/src/server.ts new file mode 100644 index 0000000..4749981 --- /dev/null +++ b/scripts/mock-robot/src/server.ts @@ -0,0 +1,412 @@ +import { WebSocketServer, WebSocket } from "ws"; + +interface RosMessage { + op: string; + topic?: string; + type?: string; + id?: string; + msg?: Record; + service?: string; + args?: Record; +} + +interface Subscriber { + id: string; + topic: string; + type: string; + ws: WebSocket; +} + +const PORT = parseInt(process.env.MOCK_ROBOT_PORT || "9090", 10); +const PUBLISH_INTERVAL = parseInt(process.env.MOCK_PUBLISH_INTERVAL || "100", 10); + +const subscribers: Map = new Map(); +let subscriberIdCounter = 0; + +const mockRobotState = { + battery: 85, + position: { x: 0, y: 0, theta: 0 }, + joints: [ + "HeadYaw", + "HeadPitch", + "LShoulderPitch", + "LShoulderRoll", + "LElbowYaw", + "LElbowRoll", + "LWristYaw", + "LHand", + "RShoulderPitch", + "RShoulderRoll", + "RElbowYaw", + "RElbowRoll", + "RWristYaw", + "RHand", + "LHipYawPitch", + "LHipRoll", + "LHipPitch", + "LKneePitch", + "LAnklePitch", + "LAnkleRoll", + "RHipYawPitch", + "RHipRoll", + "RHipPitch", + "RKneePitch", + "RAnklePitch", + "RAnkleRoll", + ], + jointPositions: new Array(26).fill(0).map(() => (Math.random() - 0.5) * 0.1), + bumperLeft: false, + bumperRight: false, + handTouchLeft: false, + handTouchRight: false, + headTouchFront: false, + headTouchMiddle: false, + headTouchRear: false, + sonarLeft: 0.5 + Math.random() * 0.5, + sonarRight: 0.5 + Math.random() * 0.5, + lastSpeechText: "", +}; + +function broadcastToSubscribers(topic: string, msg: Record, type: string): void { + const message = JSON.stringify({ + op: "publish", + topic, + type, + msg, + }); + + subscribers.forEach((sub) => { + if (sub.topic === topic && sub.ws.readyState === WebSocket.OPEN) { + try { + sub.ws.send(message); + } catch (e) { + console.error(`Failed to send to subscriber ${sub.id}:`, e); + } + } + }); +} + +function publishRobotState(): void { + broadcastToSubscribers( + "/joint_states", + { + header: { stamp: { sec: Math.floor(Date.now() / 1000), nanosec: 0 }, frame_id: "" }, + name: mockRobotState.joints, + position: mockRobotState.jointPositions, + velocity: new Array(26).fill(0), + effort: new Array(26).fill(0), + }, + "sensor_msgs/JointState" + ); + + broadcastToSubscribers( + "/naoqi_driver/battery", + { header: {}, percentage: mockRobotState.battery, charging: false, plug: false }, + "naoqi_bridge_msgs/Bumper" + ); + + broadcastToSubscribers( + "/bumper", + { left: mockRobotState.bumperLeft, right: mockRobotState.bumperRight }, + "naoqi_bridge_msgs/Bumper" + ); + + broadcastToSubscribers( + "/hand_touch", + { + leftHand: mockRobotState.handTouchLeft, + rightHand: mockRobotState.handTouchRight, + }, + "naoqi_bridge_msgs/HandTouch" + ); + + broadcastToSubscribers( + "/head_touch", + { + front: mockRobotState.headTouchFront, + middle: mockRobotState.headTouchMiddle, + rear: mockRobotState.headTouchRear, + }, + "naoqi_bridge_msgs/HeadTouch" + ); + + broadcastToSubscribers( + "/sonar/left", + { header: {}, radiation_type: 1, field_of_view: 0.5, min_range: 0.1, max_range: 5.0, range: mockRobotState.sonarLeft }, + "sensor_msgs/Range" + ); + + broadcastToSubscribers( + "/sonar/right", + { header: {}, radiation_type: 1, field_of_view: 0.5, min_range: 0.1, max_range: 5.0, range: mockRobotState.sonarRight }, + "sensor_msgs/Range" + ); +} + +function handleMessage(ws: WebSocket, data: string): void { + try { + const message: RosMessage = JSON.parse(data); + console.log(`[MockRobot] Received: ${message.op} ${message.topic || message.service || ""}`); + + switch (message.op) { + case "subscribe": + handleSubscribe(ws, message); + break; + + case "unsubscribe": + handleUnsubscribe(message); + break; + + case "publish": + handlePublish(message); + break; + + case "call_service": + handleServiceCall(ws, message); + break; + + case "advertise": + console.log(`[MockRobot] Client advertising: ${message.topic}`); + break; + + case "unadvertise": + console.log(`[MockRobot] Client unadvertising: ${message.topic}`); + break; + + case "auth": + ws.send(JSON.stringify({ op: "auth_result", result: true })); + break; + + default: + console.log(`[MockRobot] Unknown operation: ${message.op}`); + } + } catch (e) { + console.error("[MockRobot] Failed to parse message:", e); + } +} + +function handleSubscribe(ws: WebSocket, message: RosMessage): void { + if (!message.topic) return; + + const id = `sub_${subscriberIdCounter++}`; + const subscriber: Subscriber = { + id, + topic: message.topic, + type: message.type || "unknown", + ws, + }; + + subscribers.set(id, subscriber); + console.log(`[MockRobot] Subscribed to ${message.topic} (${id})`); + + if (message.id) { + ws.send(JSON.stringify({ op: "subscribe", id: message.id, values: true })); + } +} + +function handleUnsubscribe(message: RosMessage): void { + if (!message.id) return; + + const subscriber = subscribers.get(message.id); + if (subscriber) { + console.log(`[MockRobot] Unsubscribed from ${subscriber.topic}`); + subscribers.delete(message.id); + } +} + +function handlePublish(message: RosMessage): void { + if (!message.topic || !message.msg) return; + + console.log(`[MockRobot] Publish to ${message.topic}:`, JSON.stringify(message.msg).slice(0, 200)); + + if (message.topic === "/cmd_vel") { + handleCmdVel(message.msg); + } else if (message.topic === "/speech") { + handleSpeech(message.msg); + } else if (message.topic === "/joint_angles") { + handleJointAngles(message.msg); + } else if (message.topic === "/autonomous_life/control") { + handleAutonomousLife(message.msg); + } else if (message.topic === "/leds") { + handleLEDs(message.msg); + } +} + +function handleCmdVel(msg: Record): void { + const twist = msg as { linear?: { x?: number; y?: number; z?: number }; angular?: { x?: number; y?: number; z?: number } }; + const linear = twist.linear || {}; + const angular = twist.angular || {}; + + if (angular.z !== undefined && angular.z !== 0) { + mockRobotState.position.theta += angular.z * (PUBLISH_INTERVAL / 1000); + console.log(`[MockRobot] Turning: angular.z=${angular.z}, new theta=${mockRobotState.position.theta.toFixed(2)}`); + } + + if (linear.x !== undefined && linear.x !== 0) { + const dx = linear.x * Math.cos(mockRobotState.position.theta) * (PUBLISH_INTERVAL / 1000); + const dy = linear.x * Math.sin(mockRobotState.position.theta) * (PUBLISH_INTERVAL / 1000); + mockRobotState.position.x += dx; + mockRobotState.position.y += dy; + console.log(`[MockRobot] Walking: linear.x=${linear.x}, pos=(${mockRobotState.position.x.toFixed(2)}, ${mockRobotState.position.y.toFixed(2)})`); + } +} + +function handleSpeech(msg: Record): void { + const text = (msg as { data?: string }).data || ""; + mockRobotState.lastSpeechText = text; + console.log(`[MockRobot] Speaking: "${text}"`); + + setTimeout(() => { + broadcastToSubscribers( + "/speech/status", + { state: "done", text }, + "std_msgs/String" + ); + console.log(`[MockRobot] Speech complete: "${text}"`); + }, Math.max(500, text.split(/\s+/).length * 300 + 1500)); +} + +function handleJointAngles(msg: Record): void { + const data = msg as { + joint_names?: string[]; + joint_angles?: number[]; + speed?: number; + }; + + if (data.joint_names && data.joint_angles && Array.isArray(data.joint_angles)) { + const jointAngles = data.joint_angles; + data.joint_names.forEach((name, i) => { + const idx = mockRobotState.joints.indexOf(name); + const angle = jointAngles[i]; + if (idx >= 0 && angle !== undefined) { + mockRobotState.jointPositions[idx] = angle; + } + }); + console.log(`[MockRobot] Joint angles updated: ${data.joint_names.join(", ")}`); + } +} + +function handleAutonomousLife(msg: Record): void { + const state = (msg as { data?: string }).data || "disabled"; + console.log(`[MockRobot] Autonomous life: ${state}`); +} + +function handleLEDs(msg: Record): void { + const ledName = (msg as { name?: string }).name || "unknown"; + const color = (msg as { color?: string }).color || "unknown"; + console.log(`[MockRobot] LED ${ledName} set to ${color}`); +} + +function handleServiceCall(ws: WebSocket, message: RosMessage): void { + const service = message.service || ""; + const id = message.id || `svc_${Date.now()}`; + const args = message.args || {}; + + console.log(`[MockRobot] Service call: ${service}`, args); + + let response: Record = {}; + + switch (service) { + case "/rosapi/get_param": + response = { value: args.param || "" }; + break; + + case "/rosapi/topics_for_type": + response = { topics: [] }; + break; + + case "/rosapi/get_topic_type": + response = { type: "" }; + break; + + case "/rosapi/get_node_details": + response = { node_api: "", publications: [], subscriptions: [], services: [] }; + break; + + case "/naoqi_driver/get_robot_info": + response = { + robotName: "MOCK-NAO6", + robotVersion: "6.0", + bodyType: "nao", + headTiltAngle: 0, + time: Math.floor(Date.now() / 1000), + }; + break; + + case "/naoqi_driver/get_joint_names": + response = { joint_names: mockRobotState.joints }; + break; + + case "/naoqi_driver/get_position": + response = { + x: mockRobotState.position.x, + y: mockRobotState.position.y, + theta: mockRobotState.position.theta, + }; + break; + + case "/naoqi_driver/is_waking_up": + response = { success: true, is_waking_up: false, is_webots: false }; + break; + + case "/naoqi_driver/robot_supports": + response = { supports_service: true }; + break; + + case "/naoqi_driver/set_autonomous_state": + response = { success: true }; + break; + + case "/naoqi_driver/toggle_autonomous": + response = { success: true }; + break; + + case "/naoqi_driver/call_button_action": + response = { success: true, button_id: (args as { button_id?: string }).button_id }; + break; + + case "/naoqi_driver/robot_batch_request": + response = { success: true }; + break; + + default: + console.log(`[MockRobot] Unknown service: ${service}`); + response = { success: true }; + } + + ws.send(JSON.stringify({ + op: "service_response", + id, + service, + result: true, + values: response, + })); +} + +const wss = new WebSocketServer({ port: PORT }); + +console.log(`[MockRobot] Mock Robot Server starting on ws://localhost:${PORT}`); +console.log(`[MockRobot] Publish interval: ${PUBLISH_INTERVAL}ms`); +console.log("[MockRobot] Simulating NAO6 robot with rosbridge protocol\n"); + +wss.on("connection", (ws: WebSocket) => { + console.log("[MockRobot] Client connected"); + + ws.on("message", (data: Buffer) => { + handleMessage(ws, data.toString()); + }); + + ws.on("close", () => { + console.log("[MockRobot] Client disconnected"); + }); + + ws.on("error", (error) => { + console.error("[MockRobot] WebSocket error:", error); + }); + + ws.send(JSON.stringify({ op: "connected", id: "mock_robot_server" })); +}); + +setInterval(publishRobotState, PUBLISH_INTERVAL); + +console.log(`[MockRobot] Server ready. Connect via WebSocket to ws://localhost:${PORT}`); diff --git a/scripts/mock-robot/tsconfig.json b/scripts/mock-robot/tsconfig.json new file mode 100644 index 0000000..247b39a --- /dev/null +++ b/scripts/mock-robot/tsconfig.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "NodeNext", + "moduleResolution": "NodeNext", + "outDir": "dist", + "rootDir": "src", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true + }, + "include": ["src/**/*"], + "exclude": ["node_modules", "dist"] +} diff --git a/scripts/seed-dev.ts b/scripts/seed-dev.ts index b82de5f..18f23b3 100755 --- a/scripts/seed-dev.ts +++ b/scripts/seed-dev.ts @@ -236,6 +236,7 @@ async function main() { description: "A comprehensive informed consent document template for HRI research studies.", isTemplate: true, templateName: "Informed Consent", + version: 1, fields: [ { id: "1", type: "text", label: "Study Title", required: true }, { id: "2", type: "text", label: "Principal Investigator Name", required: true }, @@ -261,6 +262,7 @@ async function main() { description: "Standard questionnaire to collect participant feedback after HRI sessions.", isTemplate: true, templateName: "Post-Session Survey", + version: 2, fields: [ { id: "1", type: "rating", label: "How engaging was the robot?", required: true, settings: { scale: 5 } }, { id: "2", type: "rating", label: "How understandable was the robot's speech?", required: true, settings: { scale: 5 } }, @@ -283,6 +285,7 @@ async function main() { description: "Basic demographic information collection form.", isTemplate: true, templateName: "Demographics", + version: 3, fields: [ { id: "1", type: "text", label: "Age", required: true }, { id: "2", type: "multiple_choice", label: "Gender", required: true, options: ["Male", "Female", "Non-binary", "Prefer not to say"] }, @@ -303,6 +306,7 @@ async function main() { title: "Interactive Storyteller Consent", description: "Consent form for the Comparative WoZ Study - Interactive Storyteller scenario.", active: true, + version: 4, fields: [ { id: "1", type: "text", label: "Participant Name", required: true }, { id: "2", type: "date", label: "Date", required: true }, diff --git a/src/app/(dashboard)/help/page.tsx b/src/app/(dashboard)/help/page.tsx index 2d5cb1a..7c8fa22 100644 --- a/src/app/(dashboard)/help/page.tsx +++ b/src/app/(dashboard)/help/page.tsx @@ -26,9 +26,9 @@ export default function HelpCenterPage() { description: "Learn the basics of HRIStudio and set up your first study.", icon: BookOpen, items: [ - { label: "Platform Overview", href: "#" }, - { label: "Creating a New Study", href: "#" }, - { label: "Managing Team Members", href: "#" }, + { label: "Tutorials Overview", href: "/help/tutorials" }, + { label: "Getting Started Guide", href: "/help/tutorials/getting-started" }, + { label: "Your First Study", href: "/help/tutorials/your-first-study" }, ], }, { @@ -36,9 +36,9 @@ export default function HelpCenterPage() { description: "Master the visual experiment designer and flow control.", icon: FlaskConical, items: [ - { label: "Using the Visual Designer", href: "#" }, - { label: "Robot Actions & Plugins", href: "#" }, - { label: "Variables & Logic", href: "#" }, + { label: "Visual Designer Guide", href: "/help/tutorials/designing-experiments" }, + { label: "Robot Actions & Plugins", href: "/help/tutorials/robot-integration" }, + { label: "Wizard Interface", href: "/help/tutorials/wizard-interface" }, ], }, { @@ -46,9 +46,9 @@ export default function HelpCenterPage() { description: "Execute experiments and manage Wizard of Oz sessions.", icon: PlayCircle, items: [ - { label: "Wizard Interface Guide", href: "#" }, - { label: "Participant Management", href: "#" }, - { label: "Handling Robot Errors", href: "#" }, + { label: "Running Trials Guide", href: "/help/tutorials/running-trials" }, + { label: "Participant Management", href: "/help/tutorials/your-first-study" }, + { label: "Simulation Mode", href: "/help/tutorials/simulation-mode" }, ], }, { @@ -56,9 +56,9 @@ export default function HelpCenterPage() { description: "Analyze trial results and export research data.", icon: BarChart3, items: [ - { label: "Understanding Analytics", href: "#" }, - { label: "Exporting Data (CSV/JSON)", href: "#" }, - { label: "Video Replay & Annotation", href: "#" }, + { label: "Data & Analysis Guide", href: "/help/tutorials/data-and-analysis" }, + { label: "Forms & Surveys", href: "/help/tutorials/forms-and-surveys" }, + { label: "Exporting Data", href: "/help/tutorials/data-and-analysis" }, ], }, ]; diff --git a/src/app/(dashboard)/help/tutorials/data-and-analysis/page.tsx b/src/app/(dashboard)/help/tutorials/data-and-analysis/page.tsx new file mode 100644 index 0000000..172c73a --- /dev/null +++ b/src/app/(dashboard)/help/tutorials/data-and-analysis/page.tsx @@ -0,0 +1,196 @@ +import { TutorialPage } from "~/components/ui/tutorial-page"; +import { Button } from "~/components/ui/button"; +import Link from "next/link"; + +export default function DataAndAnalysisTutorial() { + return ( + +

Data Collection Overview

+

HRIStudio automatically captures comprehensive data during trials:

+
Trial Data
+├── Trial Metadata
+│   ├── Start/End times
+│   ├── Duration
+│   ├── Participant info
+│   └── Experiment version
+├── Event Log (Timestamped)
+│   ├── Step changes
+│   ├── Action executions
+│   ├── Robot responses
+│   └── Wizard interventions
+├── Form Responses
+│   ├── Consent forms
+│   ├── Surveys
+│   └── Questionnaires
+└── Sensor Data
+    ├── Joint positions
+    ├── Touch events
+    └── Audio/video (if enabled)
+ +

Event Types

+ + + + + + + + + + + + +
Event TypeDescriptionData Captured
trial_startedTrial beganTimestamp
step_changedNew step beganStep ID, name
action_executedRobot actionAction details, duration
wizard_responseWizard decisionSelected option
interventionWizard interventionType, note
trial_completedTrial finishedSummary
+ +

Step 1: Accessing Trial Data

+ +

From Trial List

+
    +
  1. Go to Trials tab
  2. +
  3. Find completed trial
  4. +
  5. Click View Details
  6. +
+ +

From Study Dashboard

+
    +
  1. Open your study
  2. +
  3. Go to Data tab
  4. +
  5. Select trial or view aggregate
  6. +
+ +

Step 2: Exporting Data

+ +

Export Single Trial

+
    +
  1. Open trial details
  2. +
  3. Click Export
  4. +
  5. Select format
  6. +
+ +

Export Study Data

+
    +
  1. Open study
  2. +
  3. Go to Data tab
  4. +
  5. Click Export All
  6. +
  7. Select options: +
      +
    • Date range
    • +
    • Trial status
    • +
    • Include forms
    • +
    +
  8. +
+ +

Export Formats

+ + + + + + + + + +
FormatContents
CSVTabular data for spreadsheets
JSONFull event log with metadata
VideoScreen recording (if enabled)
+ +

Step 3: Analytics Dashboard

+

View aggregate statistics:

+
    +
  • Total Trials - Number of scheduled trials
  • +
  • Completed - Successfully completed trials
  • +
  • Average Duration - Mean trial time
  • +
  • Completion Rate - % of trials completed
  • +
  • Failed - Trials that failed
  • +
+ +

Step 4: Analyzing Event Data

+ +

Timing Analysis

+

Calculate action durations from event log:

+
{`for event in events:
+    if event.type == 'action_executed':
+        duration = event.get('duration', 0)
+        print(f"{event.actionName}: {duration/1000:.1f}s")`}
+ +

Intervention Analysis

+

Track wizard interventions:

+
{`interventions = [e for e in events if e.type == 'intervention']
+
+by_type = {}
+for i in interventions:
+    itype = i.data.get('type', 'unknown')
+    by_type[itype] = by_type.get(itype, 0) + 1`}
+ +

Step 5: Generating Reports

+ +

Trial Summary Report

+

Generate PDF summary with:

+
    +
  • Executive summary
  • +
  • Timeline of events
  • +
  • Metrics and statistics
  • +
  • Intervention summary
  • +
+ +

Study Report

+

Aggregate across participants:

+
    +
  • Participation rates
  • +
  • Timing statistics
  • +
  • Intervention totals
  • +
  • Branch selection distribution
  • +
+ +

Data Privacy

+ +

Anonymization

+

Remove identifying information:

+
{`participant_map = {
+    'P001': 'S001',
+    'P002': 'S002',
+    'P003': 'S003',
+}`}
+ +

Best Practices

+
    +
  • Export data regularly (daily/weekly)
  • +
  • Store in secure location
  • +
  • Follow IRB data retention
  • +
  • Backup critical data
  • +
+ +
+ + +
+
+ ); +} diff --git a/src/app/(dashboard)/help/tutorials/designing-experiments/page.tsx b/src/app/(dashboard)/help/tutorials/designing-experiments/page.tsx new file mode 100644 index 0000000..80651c5 --- /dev/null +++ b/src/app/(dashboard)/help/tutorials/designing-experiments/page.tsx @@ -0,0 +1,181 @@ +import { TutorialPage } from "~/components/ui/tutorial-page"; +import { Button } from "~/components/ui/button"; +import Link from "next/link"; + +export default function DesigningExperimentsTutorial() { + return ( + +

What is an Experiment?

+

An Experiment defines the protocol for your study:

+
Experiment
+├── Steps (ordered sequence)
+│   ├── Actions (robot behaviors)
+│   ├── Wizard Blocks (human decisions)
+│   └── Control Flow (loops, branches)
+├── Robot Actions (from plugins)
+└── Parameters (configurable values)
+ +

Step 1: Create an Experiment

+
    +
  1. Open your study
  2. +
  3. Go to Experiments tab
  4. +
  5. Click New Experiment
  6. +
+ +

Step 2: The Visual Designer

+

The designer has three main areas:

+
    +
  • Block Library (left) - Drag blocks from here
  • +
  • Canvas (center) - Design your protocol visually
  • +
  • Properties Panel (right) - Configure selected elements
  • +
+ +

Step 3: Block Categories

+ +

Events (Triggers)

+

Start your experiment with these blocks:

+ + + + + + + + + + +
BlockDescription
Trial StartTriggers when trial begins
Wizard ButtonWaits for wizard to press a button
TimerWaits for a specified duration
Participant ResponseWaits for participant input
+ +

Wizard Actions

+

Blocks the wizard can control:

+ + + + + + + + + + +
BlockDescription
Say TextRobot speaks text
Play AnimationPlay a predefined animation
Show ImageDisplay image on robot screen
Move RobotMove robot to position
+ +

Control Flow

+

Control experiment progression:

+ + + + + + + + + + +
BlockDescription
BranchSplit into multiple paths
LoopRepeat a sequence
WaitPause for duration
ConvergeMerge multiple paths back
+ +

Step 4: Building a Branching Protocol

+

Let's build "The Interactive Storyteller" - a simple storytelling experiment:

+ +

Step 1: The Hook (Start)

+
    +
  1. Click + Add Step
  2. +
  3. Name it "The Hook"
  4. +
  5. Set type to Robot
  6. +
  7. Drag Say Text block
  8. +
  9. Configure: {`{ text: "Hello! I have a story to tell you." }`}
  10. +
+ +

Step 2: Comprehension Check (Branching)

+
    +
  1. Add new step "Comprehension Check"
  2. +
  3. Set type to Conditional
  4. +
  5. Add Ask Question block
  6. +
  7. Configure options: +
    {`{
    +  question: "What color was the rock?",
    +  options: [
    +    { label: "Correct", value: "red" },
    +    { label: "Incorrect", value: "other" }
    +  ]
    +}`}
    +
  8. +
  9. This creates two paths automatically
  10. +
+ +

Step 3: Converge Paths

+
    +
  1. Add new step "Story Continues"
  2. +
  3. Set type to Converge
  4. +
  5. Connect both branches to this step
  6. +
+ +

Step 5: Testing Your Experiment

+ +

Preview Mode

+

Test your experiment without running a real trial:

+
    +
  1. Click Preview button
  2. +
  3. Step through each block
  4. +
  5. See timing and flow
  6. +
  7. Test branching decisions
  8. +
+ +

Simulation Mode

+

Run with a simulated robot:

+
    +
  1. Enable NEXT_PUBLIC_SIMULATION_MODE=true
  2. +
  3. Start a trial
  4. +
  5. Robot actions are logged but not executed
  6. +
+ +

Common Patterns

+ +

Linear Protocol

+
Start → Step 1 → Step 2 → Step 3 → End
+ +

Branching Protocol

+
Start → Step 1
+          ├── Condition A → Step 2a
+          └── Condition B → Step 2b
+ +

Loop Protocol

+
Start → Step 1 → Loop (3x) → Step 2 → End
+             ↑
+             └── (back to Step 1)
+ +
+ + +
+
+ ); +} diff --git a/src/app/(dashboard)/help/tutorials/forms-and-surveys/page.tsx b/src/app/(dashboard)/help/tutorials/forms-and-surveys/page.tsx new file mode 100644 index 0000000..0ee508e --- /dev/null +++ b/src/app/(dashboard)/help/tutorials/forms-and-surveys/page.tsx @@ -0,0 +1,172 @@ +import { TutorialPage } from "~/components/ui/tutorial-page"; +import { Button } from "~/components/ui/button"; +import Link from "next/link"; + +export default function FormsAndSurveysTutorial() { + return ( + +

Form Types

+

HRIStudio supports three form types:

+ + + + + + + + + +
TypePurposeWhen
ConsentInformed consent for participationBefore trial
SurveyCollect feedback and observationsAfter trial
QuestionnaireDemographic data collectionAny time
+ +

Step 1: Access Forms

+
    +
  1. Go to your Study
  2. +
  3. Click Forms tab
  4. +
  5. View existing forms and templates
  6. +
+ +

Step 2: Create a Form

+ +

Using a Template

+
    +
  1. Click Create Form
  2. +
  3. Select Use Template
  4. +
  5. Choose template: +
      +
    • Informed Consent
    • +
    • Post-Session Survey
    • +
    • Demographics
    • +
    +
  6. +
  7. Customize as needed
  8. +
+ +

From Scratch

+
    +
  1. Click Create Form
  2. +
  3. Select Blank Form
  4. +
  5. Choose form type
  6. +
  7. Build fields manually
  8. +
+ +

Step 3: Form Field Types

+ + + + + + + + + + + + + + +
Field TypeDescriptionExample
TextSingle line text inputParticipant name
Text AreaMulti-line textOpen-ended feedback
RatingScale ratingRate 1-5
Multiple ChoiceSelect one optionGender selection
Yes/NoBinary choiceConsent checkbox
DateDate pickerSession date
SignatureDigital signatureConsent signature
+ +

Step 4: Consent Forms

+

For IRB compliance, consent forms must include:

+
    +
  • Study title and purpose
  • +
  • Principal investigator
  • +
  • Procedures description
  • +
  • Risks and benefits
  • +
  • Confidentiality statement
  • +
  • Voluntary participation note
  • +
  • Signature and date fields
  • +
+ +

Step 5: Distributing Forms

+ +

Automatic Distribution

+
    +
  1. Open form settings
  2. +
  3. Enable Auto-distribute
  4. +
  5. Set trigger: +
      +
    • Before trial (consent)
    • +
    • After trial (survey)
    • +
    +
  6. +
  7. Select participants
  8. +
+ +

Manual Distribution

+
    +
  1. Open form
  2. +
  3. Click Distribute
  4. +
  5. Select participants
  6. +
+ +

Step 6: Collecting Responses

+ +

View Responses

+
    +
  1. Open form
  2. +
  3. Click Responses tab
  4. +
  5. View individual submissions
  6. +
+ +

Export Responses

+

Download collected data:

+ + + + + + + + + +
FormatContents
CSVTabular data
JSONFull response objects
PDFPrinted consent forms
+ +

Form Templates

+

Pre-built templates available:

+ + + + + + + + + +
TemplateUse Case
Standard ConsentGeneric research consent
Post-Session SurveyPost-session feedback
DemographicsParticipant information
+ +
+ + +
+
+ ); +} diff --git a/src/app/(dashboard)/help/tutorials/getting-started/page.tsx b/src/app/(dashboard)/help/tutorials/getting-started/page.tsx new file mode 100644 index 0000000..6d6d0c3 --- /dev/null +++ b/src/app/(dashboard)/help/tutorials/getting-started/page.tsx @@ -0,0 +1,143 @@ +import { TutorialPage } from "~/components/ui/tutorial-page"; +import { Button } from "~/components/ui/button"; +import Link from "next/link"; + +export default function GettingStartedTutorial() { + return ( + +

Prerequisites

+

Before you begin, make sure you have the following installed:

+
    +
  • Bun - The package manager for HRIStudio
  • +
  • Docker - For running PostgreSQL and MinIO
  • +
  • Git - For version control
  • +
+ +

Step 1: Clone the Repository

+

Start by cloning the HRIStudio repository:

+
git clone https://github.com/soconnor0919/hristudio.git
+cd hristudio
+ +

Step 2: Install Dependencies

+

HRIStudio uses Bun as its package manager:

+
bun install
+ +

Step 3: Start the Database

+

HRIStudio requires PostgreSQL. The easiest way is using Docker:

+
# Start PostgreSQL and MinIO (for file storage)
+bun run docker:up
+
+# Push database schema
+bun db:push
+
+# Seed with sample data
+bun db:seed
+

+ Note: This creates the database schema and populates it with + sample users, studies, and experiments so you can explore the platform immediately. +

+ +

Step 4: Start the Development Server

+
bun dev
+

The application will be available at http://localhost:3000.

+ +

Step 5: Log In

+

Use one of the default accounts:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
RoleEmailPassword
Administratorsean@soconnor.devpassword123
Researcherfelipe.perrone@bucknell.edupassword123
Wizardemily.watson@lab.edupassword123
Observermaria.santos@tech.edupassword123
+ +

Exploring the Interface

+

After logging in, you'll see the main dashboard with navigation to:

+
    +
  • Studies - View and manage your research studies
  • +
  • Trials - Monitor and manage experiment trials
  • +
  • Plugins - Manage robot integrations
  • +
  • Admin - System administration (admins only)
  • +
+ +

Using Simulation Mode

+

If you don't have a physical robot, enable simulation mode:

+
    +
  1. Create or edit hristudio/.env.local
  2. +
  3. Add: NEXT_PUBLIC_SIMULATION_MODE=true
  4. +
  5. Restart the dev server
  6. +
+

Simulation mode allows you to test experiments without connecting to a real robot.

+ +

Troubleshooting

+ +

Database Connection Failed

+
# Check if Docker is running
+docker ps
+
+# Restart the database
+bun run docker:down
+bun run docker:up
+bun db:push
+ +

Port Already in Use

+

If port 3000 is in use:

+
PORT=3001 bun dev
+ +

Seed Script Fails

+
# Reset the database
+bun run docker:down -v
+bun run docker:up
+bun db:push
+bun db:seed
+ +
+ +
+
+ ); +} diff --git a/src/app/(dashboard)/help/tutorials/page.tsx b/src/app/(dashboard)/help/tutorials/page.tsx new file mode 100644 index 0000000..7c6a66f --- /dev/null +++ b/src/app/(dashboard)/help/tutorials/page.tsx @@ -0,0 +1,241 @@ +import { + BookOpen, + FlaskConical, + PlayCircle, + BarChart3, + Bot, + FileText, + ClipboardList, + Layers, + ArrowRight, +} from "lucide-react"; +import { + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, +} from "~/components/ui/card"; +import { Button } from "~/components/ui/button"; +import { PageLayout } from "~/components/ui/page-layout"; +import Link from "next/link"; + +const tutorials = [ + { + slug: "getting-started", + title: "Getting Started", + description: "Set up HRIStudio and learn the basics", + icon: BookOpen, + duration: "10 min", + level: "Beginner", + href: "/help/tutorials/getting-started", + }, + { + slug: "your-first-study", + title: "Your First Study", + description: "Create a research study and manage team members", + icon: Layers, + duration: "15 min", + level: "Beginner", + href: "/help/tutorials/your-first-study", + }, + { + slug: "designing-experiments", + title: "Designing Experiments", + description: "Build experiment protocols with the visual designer", + icon: FlaskConical, + duration: "25 min", + level: "Intermediate", + href: "/help/tutorials/designing-experiments", + }, + { + slug: "running-trials", + title: "Running Trials", + description: "Execute experiments and manage participants", + icon: PlayCircle, + duration: "20 min", + level: "Intermediate", + href: "/help/tutorials/running-trials", + }, + { + slug: "wizard-interface", + title: "Wizard Interface", + description: "Real-time trial control and monitoring", + icon: Bot, + duration: "15 min", + level: "Intermediate", + href: "/help/tutorials/wizard-interface", + }, + { + slug: "robot-integration", + title: "Robot Integration", + description: "Connect NAO6 and configure robot plugins", + icon: ClipboardList, + duration: "20 min", + level: "Advanced", + href: "/help/tutorials/robot-integration", + }, + { + slug: "forms-and-surveys", + title: "Forms & Surveys", + description: "Create consent forms and questionnaires", + icon: FileText, + duration: "15 min", + level: "Intermediate", + href: "/help/tutorials/forms-and-surveys", + }, + { + slug: "data-and-analysis", + title: "Data & Analysis", + description: "Collect and export trial data", + icon: BarChart3, + duration: "15 min", + level: "Intermediate", + href: "/help/tutorials/data-and-analysis", + }, +]; + +const levelColors: Record = { + Beginner: "bg-green-100 text-green-700 dark:bg-green-900 dark:text-green-300", + Intermediate: "bg-yellow-100 text-yellow-700 dark:bg-yellow-900 dark:text-yellow-300", + Advanced: "bg-red-100 text-red-700 dark:bg-red-900 dark:text-red-300", +}; + +export default function TutorialsPage() { + return ( + +
+

Quick Start Path

+

+ Follow this sequence to go from setup to running your first trial. +

+
+ {tutorials.slice(0, 5).map((tutorial, index) => ( +
+ + + + {index < 4 && } +
+ ))} +
+
+ +
+ {tutorials.map((tutorial) => ( + + + +
+
+ +
+ + {tutorial.level} + +
+ {tutorial.title} + {tutorial.description} +
+ +
+ + {tutorial.duration} + + +
+
+
+ + ))} +
+ +
+

By Role

+
+ + + Researchers + + + + Getting Started + + + Your First Study + + + Designing Experiments + + + Data & Analysis + + + + + + Wizards + + + + Getting Started + + + Wizard Interface + + + Robot Integration + + + + + + Administrators + + + + Getting Started + + + Robot Integration + + + Forms & Surveys + + + + + + Pilot Testing + + + + Getting Started + + + Designing Experiments + + + Running Trials + + + +
+
+
+ ); +} diff --git a/src/app/(dashboard)/help/tutorials/robot-integration/page.tsx b/src/app/(dashboard)/help/tutorials/robot-integration/page.tsx new file mode 100644 index 0000000..207f6d2 --- /dev/null +++ b/src/app/(dashboard)/help/tutorials/robot-integration/page.tsx @@ -0,0 +1,182 @@ +import { TutorialPage } from "~/components/ui/tutorial-page"; +import { Button } from "~/components/ui/button"; +import Link from "next/link"; + +export default function RobotIntegrationTutorial() { + return ( + +

Supported Robots

+

HRIStudio supports multiple robot platforms:

+ + + + + + + + + +
RobotProtocolCapabilities
NAO6ROS2Speech, movement, gestures, sensors
TurtleBot3ROS2Navigation, sensors
Mock RobotWebSocketAll actions (simulation)
+ +

Step 1: Set Up NAO6 Robot

+ +

Network Configuration

+
    +
  1. Connect NAO6 to your network
  2. +
  3. Note the robot's IP address: +
    # On the robot, say "What is my IP address?"
    +# Or check robot's network settings
    +
  4. +
  5. Verify network access: +
    ping nao.local
    +# Or ping the IP directly:
    +ping 192.168.1.100
    +
  6. +
+ +

Wake Up Robot

+

Before connecting, wake up the robot:

+
ssh nao@192.168.1.100
+# Enter password when prompted
+
+# Wake up the robot
+python -c "from naoqi import ALProxy; proxy = ALProxy('ALMotion', '192.168.1.100', 9559); proxy.wakeUp()"
+ +

Step 2: Start Docker Services

+ +
cd ~/nao6-hristudio-integration
+
+# Set robot IP
+export NAO_IP=192.168.1.100
+
+# Start services
+docker compose up -d
+ +

Services Overview

+ + + + + + + + + +
ServicePortPurpose
nao_driver-ROS2 driver for NAO
ros_bridge9090WebSocket bridge
ros_api-Topic introspection
+ +

Step 3: Configure HRIStudio

+ +

Install Robot Plugin

+
    +
  1. Go to Plugins in sidebar
  2. +
  3. Select your study
  4. +
  5. Click Browse Plugins
  6. +
  7. Find NAO6 Robot (ROS2 Integration)
  8. +
  9. Click Install
  10. +
+ +

Configure Plugin

+
Robot Name: NAO6-Lab
+Robot IP: 192.168.1.100
+WebSocket URL: ws://localhost:9090
+ +

Environment Variables

+

Create hristudio/.env.local:

+
# Robot connection
+NAO_ROBOT_IP=192.168.1.100
+NAO_PASSWORD=robolab
+NAO_USERNAME=nao
+
+# WebSocket bridge
+NEXT_PUBLIC_ROS_BRIDGE_URL=ws://localhost:9090
+ +

Step 4: Test Connection

+
    +
  1. Navigate to: http://localhost:3000/nao-test
  2. +
  3. Click Connect
  4. +
  5. Verify connection status shows "Connected"
  6. +
  7. Test basic actions (Say, Wave, Move)
  8. +
+ +

Robot Actions Reference

+ +

Speech Actions

+ + + + + + + + + +
ActionParametersDescription
say_texttextSpeak text
say_with_emotiontext, emotionEmotional speech
set_volumelevelSet speech volume
+ +

Movement Actions

+ + + + + + + + + + +
ActionParametersDescription
walk_forwardspeed, durationWalk forward
walk_backwardspeedWalk backward
turn_leftspeedTurn left
turn_rightspeedTurn right
+ +

Troubleshooting

+ +

Robot Not Found

+
Error: Cannot connect to robot at 192.168.1.100
+
+Solutions:
+1. Verify IP address: ping 192.168.1.100
+2. Check robot is powered on
+3. Verify network connectivity
+4. Try nao.local hostname
+ +

WebSocket Connection Failed

+
Error: WebSocket connection to ws://localhost:9090 failed
+
+Solutions:
+1. Check Docker is running: docker ps
+2. Verify ros_bridge container
+3. Check port 9090 is not blocked
+4. Restart services: docker compose restart
+ +
+ + +
+
+ ); +} diff --git a/src/app/(dashboard)/help/tutorials/running-trials/page.tsx b/src/app/(dashboard)/help/tutorials/running-trials/page.tsx new file mode 100644 index 0000000..78e76f8 --- /dev/null +++ b/src/app/(dashboard)/help/tutorials/running-trials/page.tsx @@ -0,0 +1,180 @@ +import { TutorialPage } from "~/components/ui/tutorial-page"; +import { Button } from "~/components/ui/button"; +import Link from "next/link"; + +export default function RunningTrialsTutorial() { + return ( + +

What is a Trial?

+

A Trial is a single execution of an experiment with one participant:

+
Trial
+├── Participant (who took part)
+├── Experiment (which protocol)
+├── Status (scheduled, in_progress, completed)
+├── Events (timestamped actions)
+└── Data (collected responses)
+ +

Trial Lifecycle

+
Scheduled → In Progress → Completed
+    │            │            │
+    │            ▼            │
+    │        Aborted ◄────────┤
+    │            │            │
+    └────────► Failed ◄───────┘
+ +

Step 1: Schedule a Trial

+
    +
  1. Go to your Study
  2. +
  3. Open Trials tab
  4. +
  5. Click Schedule Trial
  6. +
  7. Select: +
      +
    • Participant: P001
    • +
    • Experiment: The Interactive Storyteller
    • +
    • Scheduled Time: Today, 2:00 PM
    • +
    +
  8. +
+ +

Step 2: Prepare for Trial

+

Before starting:

+
    +
  1. Verify Robot Connection +
      +
    • Check robot is powered on
    • +
    • Verify network connection
    • +
    • Test WebSocket connection
    • +
    +
  2. +
  3. Review Experiment +
      +
    • Ensure experiment is "Ready" status
    • +
    • Check step count and timing
    • +
    • Verify all actions are configured
    • +
    +
  4. +
  5. Prepare Environment +
      +
    • Ensure participant consent is obtained
    • +
    • Set up recording equipment (if needed)
    • +
    • Remove distractions
    • +
    +
  6. +
+ +

Step 3: Start a Trial

+

From Trials List:

+
    +
  1. Find the scheduled trial
  2. +
  3. Click Start Trial
  4. +
  5. Confirm participant is ready
  6. +
  7. Click Begin
  8. +
+ +

Step 4: During the Trial

+

The wizard interface provides:

+
    +
  • Timeline View - Visual step progression
  • +
  • Current Step - Highlighted current step
  • +
  • Progress - Estimated time remaining
  • +
  • Event Log - Timestamped events
  • +
+ +

Step 5: Wizard Interventions

+

During Wizard-of-Oz studies, wizards can intervene:

+ +

Add Intervention

+
    +
  1. Click + Intervention
  2. +
  3. Select type: +
      +
    • Pause: Temporarily stop trial
    • +
    • Resume: Continue after pause
    • +
    • Note: Add observation
    • +
    • Alert: Send alert notification
    • +
    +
  4. +
+ +

Branch Selection

+

When reaching a conditional step:

+
    +
  1. Observe participant response
  2. +
  3. Select appropriate branch
  4. +
  5. Selection is logged for analysis
  6. +
+ +

Step 6: Trial Completion

+ +

Automatic Completion

+

When all steps complete:

+
    +
  1. Final step executes
  2. +
  3. Trial status → "Completed"
  4. +
  5. Data is saved automatically
  6. +
  7. Summary shown
  8. +
+ +

Manual Completion

+

To end early:

+
    +
  1. Click Stop Trial
  2. +
  3. Confirm completion
  4. +
  5. Select reason
  6. +
  7. Save partial data
  8. +
+ +

Best Practices

+ +

Before Trials

+
    +
  • Robot connected and tested
  • +
  • Experiment verified
  • +
  • Participant consent obtained
  • +
  • Recording equipment ready
  • +
  • Wizard briefed on protocol
  • +
+ +

During Trials

+
    +
  • Monitor timeline progress
  • +
  • Take timestamped notes
  • +
  • Document interventions
  • +
  • Watch for issues
  • +
+ +
+ + +
+
+ ); +} diff --git a/src/app/(dashboard)/help/tutorials/simulation-mode/page.tsx b/src/app/(dashboard)/help/tutorials/simulation-mode/page.tsx new file mode 100644 index 0000000..d885b3c --- /dev/null +++ b/src/app/(dashboard)/help/tutorials/simulation-mode/page.tsx @@ -0,0 +1,203 @@ +import { TutorialPage } from "~/components/ui/tutorial-page"; +import { Button } from "~/components/ui/button"; +import Link from "next/link"; + +export default function SimulationModeTutorial() { + return ( + +

Why Simulation Mode?

+

Simulation mode allows you to:

+
    +
  • Test protocols without a robot
  • +
  • Train wizards before live sessions
  • +
  • Debug experiments in development
  • +
  • Run pilots without robot access
  • +
  • Develop on any computer
  • +
+ +

Simulation Options

+

HRIStudio offers two simulation approaches:

+ + + + + + + + + + + + + + + + +
ApproachProsCons
Client-sideNo server needed, instantLimited robot simulation
Mock ServerFull rosbridge protocolRequires running server
+ +

Step 1: Enable Client-Side Simulation

+ +

Quick Start

+
    +
  1. Create or edit hristudio/.env.local
  2. +
  3. Add: NEXT_PUBLIC_SIMULATION_MODE=true
  4. +
  5. Restart the dev server: +
    bun dev
    +
  6. +
+ +

Verify Enabled

+

Look for the simulation indicator in the UI:

+
Wizard Interface [🔵 SIMULATION MODE]
+ +

Step 2: Start Mock Server (Optional)

+

For more complete testing, use the mock server:

+ +

Standalone Server

+
cd hristudio/scripts/mock-robot
+bun install
+bun dev
+ +

Docker

+
cd nao6-hristudio-integration
+docker compose -f docker-compose.yml -f docker-compose.mock.yml --profile mock up -d
+ +

Step 3: Test Robot Actions

+ +

From NAO Test Page

+
    +
  1. Navigate to: /nao-test
  2. +
  3. Click Connect
  4. +
  5. Test actions: +
      +
    • Speech - Enter text, click Say
    • +
    • Movement - Set speed, click Walk
    • +
    • Head - Set angles, click Move
    • +
    +
  6. +
+ +

Simulated Actions

+ + + + + + + + + +
ActionSimulation Behavior
say_textDuration = 1.5s + 300ms × word_count
walk_forwardPosition updates over 500ms
turn_left/rightAngle changes over 500ms
+ +

Step 4: Run Test Trials

+
    +
  1. Enable simulation mode
  2. +
  3. Create or open experiment
  4. +
  5. Schedule trial
  6. +
  7. Start trial in wizard interface
  8. +
  9. Execute through all steps
  10. +
  11. Verify timing and flow
  12. +
+ +

Test Checklist

+
    +
  • All steps execute in order
  • +
  • Branching decisions work
  • +
  • Timing estimates are accurate
  • +
  • Event log captures everything
  • +
  • No errors or warnings
  • +
  • Trial completes successfully
  • +
+ +

Step 5: Training Wizards

+

Simulation mode is perfect for training:

+ +

Training Scenarios

+
    +
  1. Basic Operation - Start/pause trials, execute actions
  2. +
  3. Decision Making - Select appropriate branches
  4. +
  5. Handling Issues - Pause, respond to alerts, stop early
  6. +
+ +

Transitioning to Real Robot

+
    +
  1. Disable Simulation +
    NEXT_PUBLIC_SIMULATION_MODE=false
    +
  2. +
  3. Connect Robot +
      +
    • Start Docker services
    • +
    • Verify robot connection
    • +
    • Test with NAO Test Page
    • +
    +
  4. +
  5. Run Comparison Trial +
      +
    • Run same experiment on real robot
    • +
    • Compare timing and behavior
    • +
    • Adjust parameters as needed
    • +
    +
  6. +
+ +

Comparison: Simulation vs Real

+ + + + + + + + + + + + +
AspectSimulationReal Robot
Setup time1 min30+ min
AvailabilityAlwaysRequires robot
CostFreeRobot access needed
Timing accuracyEstimatedActual
Physical interaction
Sensor accuracyFakeReal
+ +

Best Practices

+ +

When to Use Simulation

+
    +
  • During experiment design
  • +
  • While robot unavailable
  • +
  • For wizard training
  • +
  • For debugging protocols
  • +
  • For quick iteration
  • +
+ +

When to Use Real Robot

+
    +
  • Final protocol validation
  • +
  • Timing accuracy critical
  • +
  • Physical interaction matters
  • +
  • Sensor data needed
  • +
  • Pre-study pilot
  • +
+ +
+ +
+
+ ); +} diff --git a/src/app/(dashboard)/help/tutorials/wizard-interface/page.tsx b/src/app/(dashboard)/help/tutorials/wizard-interface/page.tsx new file mode 100644 index 0000000..13eb2f0 --- /dev/null +++ b/src/app/(dashboard)/help/tutorials/wizard-interface/page.tsx @@ -0,0 +1,179 @@ +import { TutorialPage } from "~/components/ui/tutorial-page"; +import { Button } from "~/components/ui/button"; +import Link from "next/link"; + +export default function WizardInterfaceTutorial() { + return ( + +

What is the Wizard Interface?

+

The Wizard Interface is your control center during trials. It provides:

+
    +
  • Real-time trial monitoring
  • +
  • Robot action controls
  • +
  • Decision-making tools
  • +
  • Intervention capabilities
  • +
  • Event logging
  • +
+ +

Step 1: Accessing the Interface

+ +

Method 1: From Trials List

+
    +
  1. Go to Trials in sidebar
  2. +
  3. Find your scheduled trial
  4. +
  5. Click Open Wizard
  6. +
+ +

Method 2: Direct URL

+
{`/trials/{trialId}/wizard`}
+ +

Method 3: Trial Queue

+
    +
  1. Go to Wizard Queue
  2. +
  3. See all pending trials
  4. +
  5. Click Start on any trial
  6. +
+ +

Step 2: Understanding the Layout

+ +

Left Panel: Trial Controls

+ + + + + + + + + + +
ControlFunction
Play/PauseStart or pause trial
StopEnd trial early
NotesAdd timestamped observations
AlertSend alert to researchers
+ +

Center Panel: Timeline

+
    +
  • Visual Progress - See step progression
  • +
  • Current Position - Highlighted current step
  • +
  • Time Display - Elapsed and estimated remaining
  • +
+ +

Right Panel: Robot Control

+
    +
  • Status Section - Connection, battery, position
  • +
  • Action Section - Quick action buttons
  • +
+ +

Step 3: Controlling the Robot

+ +

Quick Actions

+

Pre-configured robot actions:

+ + + + + + + + + + +
ActionDescription
Say TextMake robot speak
WaveWave gesture
Look at MeTurn head toward participant
NodConfirmation nod
+ +

Custom Say Text

+
    +
  1. Click Say Text
  2. +
  3. Enter text in popup
  4. +
  5. Select options (speed, emotion)
  6. +
  7. Click Execute
  8. +
+ +

Step 4: Making Decisions

+

When the experiment reaches a branching point:

+
    +
  1. Observe participant's actual response
  2. +
  3. Consider protocol criteria
  4. +
  5. Select appropriate branch
  6. +
  7. Confirm selection
  8. +
+

Decision is logged with timestamp and trial continues.

+ +

Step 5: Handling Interruptions

+ +

Pause Trial

+
    +
  1. Click Pause button
  2. +
  3. Add reason (optional)
  4. +
  5. Trial pauses, robot holds position
  6. +
+ +

Resume Trial

+
    +
  1. Click Play button
  2. +
  3. Trial resumes from pause point
  4. +
  5. Pause duration is logged
  6. +
+ +

Stop Trial

+
    +
  1. Click Stop button
  2. +
  3. Select reason
  4. +
  5. Confirm stop
  6. +
  7. Partial data is saved
  8. +
+ +

Keyboard Shortcuts

+ + + + + + + + + + +
KeyAction
SpacePlay/Pause toggle
EscapeStop trial
NAdd note
ASend alert
+ +

Event Logging

+

All actions are logged automatically:

+
[14:32:05] Trial started
+[14:32:08] Step 1: The Hook
+[14:32:10] Action: Say Text "Hello!"
+[14:33:28] Wizard Note: "Participant engaged"
+[14:33:30] Branch: Correct selected
+[14:34:05] Trial completed
+ +
+ + +
+
+ ); +} diff --git a/src/app/(dashboard)/help/tutorials/your-first-study/page.tsx b/src/app/(dashboard)/help/tutorials/your-first-study/page.tsx new file mode 100644 index 0000000..01c8e2e --- /dev/null +++ b/src/app/(dashboard)/help/tutorials/your-first-study/page.tsx @@ -0,0 +1,181 @@ +import { TutorialPage } from "~/components/ui/tutorial-page"; +import { Button } from "~/components/ui/button"; +import Link from "next/link"; + +export default function YourFirstStudyTutorial() { + return ( + +

What is a Study?

+

In HRIStudio, a Study is the top-level container for your research:

+
Study
+├── Experiments (multiple protocols)
+├── Participants (study participants)
+├── Team Members (collaborators)
+├── Forms & Surveys (consent, questionnaires)
+└── Trials (individual experiment runs)
+ +

Step 1: Create a New Study

+
    +
  1. Log in as Researcher or Administrator
  2. +
  3. Click Studies in the sidebar
  4. +
  5. Click Create Study
  6. +
+ +

Study Settings

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FieldDescription
NameStudy title
DescriptionBrief overview of research goals
InstitutionUniversity or organization
IRB ProtocolProtocol number (e.g., 2024-HRI-001)
StatusDraft, Active, Completed, Archived
+ +

Step 2: Add Team Members

+

Studies can have multiple collaborators with different roles:

+ + + + + + + + + + + + + + + + + + + + + + + + + +
RolePermissions
OwnerFull access, can delete study
ResearcherCreate/edit experiments, manage participants
WizardExecute trials, control robot during trials
ObserverView-only access, add annotations
+ +

Adding a Wizard

+
    +
  1. Open your study
  2. +
  3. Go to Team tab
  4. +
  5. Click Add Member
  6. +
  7. Enter the wizard's email
  8. +
  9. Select Wizard role
  10. +
  11. Click Invite
  12. +
+ +

Step 3: Install Robot Plugins

+

For studies involving robots, you need to install the appropriate plugin:

+
    +
  1. Go to Plugins in the sidebar
  2. +
  3. Select your study from the dropdown
  4. +
  5. Click Browse Plugins
  6. +
  7. Find your robot (e.g., "NAO6 Robot")
  8. +
  9. Click Install
  10. +
  11. Configure robot settings (IP address, etc.)
  12. +
+ +

Step 4: Add Participants

+
    +
  1. Go to Participants tab
  2. +
  3. Click Add Participant
  4. +
  5. Enter participant code (e.g., "P001")
  6. +
  7. Fill in optional details
  8. +
+ +

Batch Import

+

For large studies, import from CSV:

+
participantCode,name,email,notes
+P001,John Smith,john@email.com,Condition A
+P002,Jane Doe,jane@email.com,Condition B
+ +

Study Workflow

+
Draft → Active → Recruiting → In Progress → Completed
+  │        │          │            │           │
+  │        │          │            │           └── All trials done
+  │        │          │            └── Trials running
+  │        │          └── Recruiting participants
+  │        └── Ready to collect data
+  └── Setting up study
+ +

Common Tasks

+ +

Clone a Study

+
    +
  1. Open the study
  2. +
  3. Click Settings (gear icon)
  4. +
  5. Select Duplicate Study
  6. +
  7. Enter new study name
  8. +
+ +

Archive a Study

+

When a study is complete:

+
    +
  1. Go to study settings
  2. +
  3. Change status to Archived
  4. +
  5. Data is preserved but study is read-only
  6. +
+ +
+ + +
+
+ ); +} diff --git a/src/components/dashboard/app-sidebar.tsx b/src/components/dashboard/app-sidebar.tsx index 41b2f8d..88f6ba5 100755 --- a/src/components/dashboard/app-sidebar.tsx +++ b/src/components/dashboard/app-sidebar.tsx @@ -11,6 +11,7 @@ import { Building, ChevronDown, FlaskConical, + GraduationCap, Home, LogOut, MoreHorizontal, @@ -59,7 +60,6 @@ import { Logo } from "~/components/ui/logo"; import { useStudyManagement } from "~/hooks/useStudyManagement"; import { handleAuthError, isAuthError } from "~/lib/auth-error-handler"; -import { api } from "~/trpc/react"; // Global items - always available const globalItems = [ @@ -129,10 +129,9 @@ const helpItems = [ icon: BookOpen, }, { - title: "Interactive Tour", - url: "#tour", + title: "Tutorials", + url: "/help/tutorials", icon: PlayCircle, - action: "tour", }, ]; @@ -183,12 +182,6 @@ export function AppSidebar({ } }, [isLoadingUserStudies, selectedStudyId, userStudies, selectStudy]); - // Debug API call - const { data: debugData } = api.dashboard.debug.useQuery(undefined, { - enabled: process.env.NODE_ENV === "development", - staleTime: 1000 * 30, // 30 seconds - }); - type Study = { id: string; name: string; @@ -285,9 +278,6 @@ export function AppSidebar({ return () => clearInterval(interval); }, [refreshStudyData]); - // Show debug info in development - const showDebug = process.env.NODE_ENV === "development"; - const [mounted, setMounted] = React.useState(false); React.useEffect(() => { @@ -600,23 +590,14 @@ export function AppSidebar({ {helpItems.map((item) => { const isActive = pathname.startsWith(item.url); - const menuButton = - item.action === "tour" ? ( - startTour("full_platform")} - isActive={false} - > + const menuButton = ( + + {item.title} - - ) : ( - - - - {item.title} - - - ); + + + ); return ( @@ -639,99 +620,8 @@ export function AppSidebar({ - {/* Debug info moved to footer tooltip button */} - - {showDebug && ( - - {isCollapsed ? ( - - - - - - -
Session: {session?.user?.email ?? "No session"}
-
Role: {userRole ?? "No role"}
-
Studies: {userStudies.length}
-
Selected: {selectedStudy?.name ?? "None"}
-
Auth: {session ? "✓" : "✗"}
- {debugData && ( - <> -
DB User: {debugData.user?.email ?? "None"}
-
- System Roles:{" "} - {debugData.systemRoles.join(", ") || "None"} -
-
- Memberships: {debugData.studyMemberships.length} -
-
All Studies: {debugData.allStudies.length}
-
- Session ID: {debugData.session.userId.slice(0, 8)} - ... -
- - )} -
-
-
- ) : ( - - - - - Debug - - - - - - Debug Info - - -
-
Session: {session?.user?.email ?? "No session"}
-
Role: {userRole ?? "No role"}
-
Studies: {userStudies.length}
-
Selected: {selectedStudy?.name ?? "None"}
-
Auth: {session ? "✓" : "✗"}
- {debugData && ( - <> -
DB User: {debugData.user?.email ?? "None"}
-
- System Roles:{" "} - {debugData.systemRoles.join(", ") || "None"} -
-
- Memberships: {debugData.studyMemberships.length} -
-
All Studies: {debugData.allStudies.length}
-
- Session ID: {debugData.session.userId.slice(0, 8)} - ... -
- - )} -
-
-
- )} -
- )} {isCollapsed ? ( diff --git a/src/components/ui/tutorial-page.tsx b/src/components/ui/tutorial-page.tsx new file mode 100644 index 0000000..ba8fbbf --- /dev/null +++ b/src/components/ui/tutorial-page.tsx @@ -0,0 +1,126 @@ +import { type ReactNode } from "react"; +import Link from "next/link"; +import { Button } from "~/components/ui/button"; +import { + ChevronLeft, + ChevronRight, + CheckCircle2, +} from "lucide-react"; +import { PageLayout } from "~/components/ui/page-layout"; + +interface TutorialStep { + title: string; + description: string; +} + +interface TutorialPageProps { + children: ReactNode; + title: string; + description: string; + duration: string; + level: string; + steps: TutorialStep[]; + prevTutorial?: { + title: string; + href: string; + }; + nextTutorial?: { + title: string; + href: string; + }; +} + +export function TutorialPage({ + children, + title, + description, + duration, + level, + steps, + prevTutorial, + nextTutorial, +}: TutorialPageProps) { + const levelColors: Record = { + Beginner: "bg-green-100 text-green-700 dark:bg-green-900 dark:text-green-300", + Intermediate: "bg-yellow-100 text-yellow-700 dark:bg-yellow-900 dark:text-yellow-300", + Advanced: "bg-red-100 text-red-700 dark:bg-red-900 dark:text-red-300", + }; + + return ( + +
+
+ {children} +
+ + +
+
+ ); +} + +function Card({ children, className }: { children: ReactNode; className?: string }) { + return ( +
+ {children} +
+ ); +} diff --git a/src/hooks/useWizardRos.ts b/src/hooks/useWizardRos.ts index cfc452c..9a5a5e8 100644 --- a/src/hooks/useWizardRos.ts +++ b/src/hooks/useWizardRos.ts @@ -10,6 +10,7 @@ import { export interface UseWizardRosOptions { autoConnect?: boolean; + simulationMode?: boolean; onConnected?: () => void; onDisconnected?: () => void; onError?: (error: unknown) => void; @@ -24,6 +25,7 @@ export interface UseWizardRosOptions { export interface UseWizardRosReturn { isConnected: boolean; isConnecting: boolean; + isSimulationMode: boolean; connectionError: string | null; robotStatus: RobotStatus; activeActions: RobotActionExecution[]; @@ -48,6 +50,7 @@ export interface UseWizardRosReturn { args?: Record, ) => Promise; setAutonomousLife: (enabled: boolean) => Promise; + setSimulationMode: (enabled: boolean) => void; } export function useWizardRos( @@ -55,6 +58,7 @@ export function useWizardRos( ): UseWizardRosReturn { const { autoConnect = true, + simulationMode = false, onConnected, onDisconnected, onError, @@ -101,14 +105,17 @@ export function useWizardRos( // Initialize service (only once) useEffect(() => { if (!isInitializedRef.current) { - serviceRef.current = getWizardRosService(); + serviceRef.current = getWizardRosService(simulationMode); + if (simulationMode) { + serviceRef.current.setSimulationMode(true); + } isInitializedRef.current = true; } return () => { mountedRef.current = false; }; - }, []); + }, [simulationMode]); // Set up event listeners with stable callbacks useEffect(() => { @@ -381,9 +388,19 @@ export function useWizardRos( [isConnected], ); + const setSimulationMode = useCallback((enabled: boolean) => { + const service = serviceRef.current; + if (service) { + service.setSimulationMode(enabled); + } + }, []); + + const isSimulationMode = serviceRef.current?.isSimulationMode() ?? simulationMode; + return { isConnected, isConnecting, + isSimulationMode, connectionError, robotStatus, activeActions, @@ -392,5 +409,6 @@ export function useWizardRos( executeRobotAction, callService, setAutonomousLife, + setSimulationMode, }; } diff --git a/src/lib/ros/wizard-ros-service.ts b/src/lib/ros/wizard-ros-service.ts index 347d6c0..cdbb647 100644 --- a/src/lib/ros/wizard-ros-service.ts +++ b/src/lib/ros/wizard-ros-service.ts @@ -60,6 +60,10 @@ export class WizardRosService extends EventEmitter { private maxReconnectAttempts = 5; private isConnecting = false; + // Simulation mode + private simulationMode: boolean; + private simulationInterval: NodeJS.Timeout | null = null; + // Robot state private robotStatus: RobotStatus = { connected: false, @@ -73,15 +77,40 @@ export class WizardRosService extends EventEmitter { // Active action tracking private activeActions: Map = new Map(); - constructor(url: string = "ws://localhost:9090") { + constructor(url: string = "ws://localhost:9090", simulationMode: boolean = false) { super(); this.url = url; + this.simulationMode = simulationMode || + (typeof window !== "undefined" && process.env.NEXT_PUBLIC_SIMULATION_MODE === "true"); + } + + /** + * Check if running in simulation mode + */ + isSimulationMode(): boolean { + return this.simulationMode; + } + + /** + * Enable or disable simulation mode + */ + setSimulationMode(enabled: boolean): void { + this.simulationMode = enabled; + if (!enabled && this.simulationInterval) { + clearInterval(this.simulationInterval); + this.simulationInterval = null; + } } /** * Connect to ROS bridge WebSocket */ async connect(): Promise { + // Simulation mode - fake connection + if (this.simulationMode) { + return this.connectSimulation(); + } + return new Promise((resolve, reject) => { if ( this.isConnected || @@ -167,6 +196,11 @@ export class WizardRosService extends EventEmitter { disconnect(): void { this.clearReconnectTimer(); + if (this.simulationMode) { + this.disconnectSimulation(); + return; + } + if (this.ws) { this.ws.close(1000, "Manual disconnect"); this.ws = null; @@ -178,10 +212,173 @@ export class WizardRosService extends EventEmitter { this.emit("disconnected"); } + /** + * Simulation mode connection - simulates robot responses + */ + private async connectSimulation(): Promise { + console.log(`[WizardROS] SIMULATION MODE - Connecting to mock robot`); + this.isConnected = true; + this.isConnecting = false; + this.connectionAttempts = 0; + + // Initialize mock robot state + const mockStates = this.getMockJointStates(); + this.robotStatus = { + connected: true, + battery: 85, + position: { x: 0, y: 0, theta: 0 }, + joints: mockStates.names.reduce((acc, name, i) => { + acc[name] = mockStates.positions[i] ?? 0; + return acc; + }, {} as Record), + sensors: {}, + lastUpdate: new Date(), + }; + + // Start publishing simulated sensor data + this.simulationInterval = setInterval(() => { + this.publishSimulationData(); + }, 100); + + this.emit("connected"); + console.log(`[WizardROS] SIMULATION MODE - Connected to mock robot`); + } + + /** + * Simulation mode disconnection + */ + private disconnectSimulation(): void { + console.log(`[WizardROS] SIMULATION MODE - Disconnecting`); + if (this.simulationInterval) { + clearInterval(this.simulationInterval); + this.simulationInterval = null; + } + this.isConnected = false; + this.robotStatus.connected = false; + this.emit("disconnected"); + } + + /** + * Publish simulated sensor data + */ + private publishSimulationData(): void { + if (!this.simulationMode || !this.isConnected) return; + + const mockData = this.getMockJointStates(); + this.updateJointStates(mockData); + this.robotStatus.battery = 85 + Math.random() * 2 - 1; // Slight variation + this.robotStatus.sensors = { + "/bumper": { left: false, right: false }, + "/hand_touch": { leftHand: false, rightHand: false }, + "/head_touch": { front: false, middle: false, rear: false }, + "/sonar/left": { range: 0.5 + Math.random() * 0.5 }, + "/sonar/right": { range: 0.5 + Math.random() * 0.5 }, + }; + this.robotStatus.lastUpdate = new Date(); + this.emit("robot_status_updated", this.robotStatus); + } + + /** + * Get mock joint states for simulation + */ + private getMockJointStates(): { names: string[]; positions: number[] } { + const names = [ + "HeadYaw", "HeadPitch", + "LShoulderPitch", "LShoulderRoll", "LElbowYaw", "LElbowRoll", "LWristYaw", "LHand", + "RShoulderPitch", "RShoulderRoll", "RElbowYaw", "RElbowRoll", "RWristYaw", "RHand", + "LHipYawPitch", "LHipRoll", "LHipPitch", "LKneePitch", "LAnklePitch", "LAnkleRoll", + "RHipYawPitch", "RHipRoll", "RHipPitch", "RKneePitch", "RAnklePitch", "RAnkleRoll", + ]; + const positions = names.map(() => (Math.random() - 0.5) * 0.1); + return { names, positions }; + } + + /** + * Execute action in simulation mode + */ + private async executeSimulationAction( + pluginName: string, + actionId: string, + parameters: Record, + actionConfig?: { + topic: string; + messageType: string; + payloadMapping: { + type: string; + payload?: Record; + transformFn?: string; + }; + }, + ): Promise { + const executionId = `${pluginName}_${actionId}_${Date.now()}`; + const execution: RobotActionExecution = { + id: executionId, + actionId, + pluginName, + parameters, + status: "pending", + startTime: new Date(), + }; + + this.activeActions.set(executionId, execution); + this.emit("action_started", execution); + + try { + execution.status = "executing"; + this.activeActions.set(executionId, execution); + + console.log(`[WizardROS] SIMULATION MODE - Executing ${actionId}:`, parameters); + + // Simulate action execution based on action type + let duration = 500; + + if (actionId === "say_text" || actionId === "say_with_emotion" || actionConfig?.topic === "/speech") { + const text = String(parameters.text || parameters.data || "Hello"); + const wordCount = text.split(/\s+/).filter(Boolean).length; + duration = 1500 + Math.max(1000, wordCount * 300); + } else if (actionId.includes("walk") || actionId.includes("turn") || actionConfig?.topic === "/cmd_vel") { + duration = 500; + // Simulate position change + const speed = Number(parameters.speed) || 0.1; + if (actionId === "walk_forward") { + this.robotStatus.position.x += speed * 0.5; + } else if (actionId === "walk_backward") { + this.robotStatus.position.x -= speed * 0.5; + } else if (actionId === "turn_left") { + this.robotStatus.position.theta -= 0.5; + } else if (actionId === "turn_right") { + this.robotStatus.position.theta += 0.5; + } + } else if (actionId.includes("head") || actionId.includes("move") || actionConfig?.topic === "/joint_angles") { + duration = 1000; + } + + // Simulate async execution + await new Promise((resolve) => setTimeout(resolve, duration)); + + execution.status = "completed"; + execution.endTime = new Date(); + this.emit("action_completed", execution); + } catch (error) { + execution.status = "failed"; + execution.error = error instanceof Error ? error.message : String(error); + execution.endTime = new Date(); + this.emit("action_failed", execution); + } + + this.activeActions.set(executionId, execution); + return execution; + } + + + /** * Check if connected to ROS bridge */ getConnectionStatus(): boolean { + if (this.simulationMode) { + return this.isConnected; + } return this.isConnected && this.ws?.readyState === WebSocket.OPEN; } @@ -213,6 +410,11 @@ export class WizardRosService extends EventEmitter { throw new Error("Not connected to ROS bridge"); } + // Simulation mode - simulate action execution + if (this.simulationMode) { + return this.executeSimulationAction(pluginName, actionId, parameters, actionConfig); + } + const executionId = `${pluginName}_${actionId}_${Date.now()}`; const execution: RobotActionExecution = { id: executionId, @@ -587,6 +789,42 @@ export class WizardRosService extends EventEmitter { throw new Error("Not connected to ROS bridge"); } + // Simulation mode - return mock responses + if (this.simulationMode) { + console.log(`[WizardROS] SIMULATION MODE - Service call: ${service}`, args); + + const mockResponses: Record = { + "/naoqi_driver/get_robot_info": { + result: true, + values: { + robotName: "MOCK-NAO6", + robotVersion: "6.0", + bodyType: "nao", + }, + }, + "/naoqi_driver/get_joint_names": { + result: true, + values: { + joint_names: [ + "HeadYaw", "HeadPitch", "LShoulderPitch", "LShoulderRoll", + "LElbowYaw", "LElbowRoll", "LWristYaw", "LHand", + "RShoulderPitch", "RShoulderRoll", "RElbowYaw", "RElbowRoll", + "RWristYaw", "RHand", "LHipYawPitch", "LHipRoll", + "LHipPitch", "LKneePitch", "LAnklePitch", "LAnkleRoll", + "RHipYawPitch", "RHipRoll", "RHipPitch", "RKneePitch", + "RAnklePitch", "RAnkleRoll", + ], + }, + }, + "/naoqi_driver/get_position": { + result: true, + values: this.robotStatus.position, + }, + }; + + return mockResponses[service] || { result: true }; + } + const id = `call_${this.messageId++}`; return new Promise((resolve, reject) => { @@ -892,7 +1130,7 @@ let isCreatingInstance = false; /** * Get or create the global wizard ROS service (true singleton) */ -export function getWizardRosService(): WizardRosService { +export function getWizardRosService(simulationMode?: boolean): WizardRosService { // Prevent multiple instances during creation if (isCreatingInstance && !wizardRosService) { throw new Error("WizardRosService is being initialized, please wait"); @@ -901,7 +1139,10 @@ export function getWizardRosService(): WizardRosService { if (!wizardRosService) { isCreatingInstance = true; try { - wizardRosService = new WizardRosService(); + const url = typeof window !== "undefined" + ? (process.env.NEXT_PUBLIC_ROS_BRIDGE_URL || "ws://localhost:9090") + : "ws://localhost:9090"; + wizardRosService = new WizardRosService(url, simulationMode); } finally { isCreatingInstance = false; } @@ -912,8 +1153,12 @@ export function getWizardRosService(): WizardRosService { /** * Initialize wizard ROS service with connection */ -export async function initWizardRosService(): Promise { - const service = getWizardRosService(); +export async function initWizardRosService(simulationMode?: boolean): Promise { + const service = getWizardRosService(simulationMode); + + if (simulationMode !== undefined) { + service.setSimulationMode(simulationMode); + } if (!service.getConnectionStatus()) { await service.connect(); @@ -921,3 +1166,13 @@ export async function initWizardRosService(): Promise { return service; } + +/** + * Reset the global wizard ROS service (useful for testing or reinitializing) + */ +export function resetWizardRosService(): void { + if (wizardRosService) { + wizardRosService.disconnect(); + wizardRosService = null; + } +}