Compare commits

6 Commits

Author SHA1 Message Date
5be4ff0372 refactor: simplify wizard UI by removing trial monitoring and robot control tabs, and streamlining monitoring panel props. 2025-11-20 14:52:08 -05:00
1108f4d25d fix: prevent auto-connect from getting stuck in connecting state
Added comment to clarify that connection state updates happen via
event handlers. Auto-connect now properly handles failures without
retrying automatically, allowing users to manually connect if needed.
2025-11-19 22:56:24 -05:00
5631c69a76 fix: remove invalid battery subscription causing ROS Bridge error
The naoqi_bridge_msgs/BatteryState message type doesn't exist in the
NAO6 ROS2 package, causing subscription errors. Removed the battery
topic subscription for now. Battery info can be obtained through
diagnostics or other means if needed in the future.
2025-11-19 22:52:21 -05:00
18fa6bff5f fix: migrate wizard from polling to WebSocket and fix duplicate ROS connections
- Removed non-functional trial WebSocket (no server exists)
- Kept ROS WebSocket for robot control via useWizardRos
- Fixed duplicate ROS connections by passing connection as props
- WizardMonitoringPanel now receives ROS connection from parent
- Trial status uses reliable tRPC polling (5-15s intervals)
- Updated connection badges to show 'ROS Connected/Offline'
- Added loading overlay with fade-in to designer
- Fixed hash computation to include parameter values
- Fixed incremental hash caching for parameter changes

Fixes:
- WebSocket connection errors eliminated
- Connect button now works properly
- No more conflicting duplicate connections
- Accurate connection status display
2025-11-19 22:51:38 -05:00
b21ed8e805 feat: Relocate experiment designer routes under studies, update ROS2 topic paths, and enhance designer hashing and performance. 2025-11-19 18:05:19 -05:00
86b5ed80c4 nao6 ros2 integration updated 2025-11-13 10:58:45 -05:00
278 changed files with 6457 additions and 2671 deletions

0
.env.example Normal file → Executable file
View File

0
.eslintrc.autofix.js Normal file → Executable file
View File

0
.gitignore vendored Normal file → Executable file
View File

0
.rules Normal file → Executable file
View File

84
DOCUMENTATION.md Normal file
View File

@@ -0,0 +1,84 @@
# HRIStudio Documentation Overview
Clean, organized documentation for the HRIStudio platform.
## Quick Links
### Getting Started
- **[README.md](README.md)** - Main project overview and setup
- **[Quick Reference](docs/quick-reference.md)** - 5-minute setup guide
### HRIStudio Platform
- **[Project Overview](docs/project-overview.md)** - Features and architecture
- **[Database Schema](docs/database-schema.md)** - Complete database reference
- **[API Routes](docs/api-routes.md)** - tRPC API documentation
- **[Implementation Guide](docs/implementation-guide.md)** - Technical implementation
### NAO6 Robot Integration
- **[NAO6 Quick Reference](docs/nao6-quick-reference.md)** - Essential commands
- **[Integration Repository](../nao6-hristudio-integration/)** - Complete integration package
- Installation guide
- Usage instructions
- Troubleshooting
- Plugin definitions
### Experiment Design
- **[Core Blocks System](docs/core-blocks-system.md)** - Experiment building blocks
- **[Plugin System](docs/plugin-system-implementation-guide.md)** - Robot plugins
### Deployment
- **[Deployment & Operations](docs/deployment-operations.md)** - Production deployment
### Research
- **[Research Paper](docs/paper.md)** - Academic documentation
## Repository Structure
```
hristudio/ # Main web application
├── README.md # Start here
├── DOCUMENTATION.md # This file
├── src/ # Next.js application
├── docs/ # Platform documentation
└── ...
nao6-hristudio-integration/ # NAO6 integration
├── README.md # Integration overview
├── docs/ # NAO6 documentation
├── launch/ # ROS2 launch files
├── scripts/ # Utility scripts
├── plugins/ # Plugin definitions
└── examples/ # Usage examples
```
## Documentation Philosophy
- **One source of truth** - No duplicate docs
- **Clear hierarchy** - Easy to find what you need
- **Practical focus** - Real commands, not theory
- **Examples** - Working code samples
## For Researchers
Start here:
1. [README.md](README.md) - Setup HRIStudio
2. [NAO6 Quick Reference](docs/nao6-quick-reference.md) - Connect NAO robot
3. [Project Overview](docs/project-overview.md) - Understand the system
## For Developers
Start here:
1. [Implementation Guide](docs/implementation-guide.md) - Technical architecture
2. [Database Schema](docs/database-schema.md) - Data model
3. [API Routes](docs/api-routes.md) - Backend APIs
## Support
- Check documentation first
- Use NAO6 integration repo for robot-specific issues
- Main HRIStudio repo for platform issues
---
**Last Updated:** December 2024
**Version:** 1.0 (Simplified)

344
HANDOFF-NAO6-INTEGRATION.md Normal file
View File

@@ -0,0 +1,344 @@
# NAO6 Integration Handoff Document
**Date**: 2024-11-12
**Status**: ✅ Production Ready - Action Execution Pending
**Session Duration**: ~3 hours
---
## 🎯 What's Ready
### ✅ Completed
1. **Live Robot Connection** - NAO6 @ nao.local fully connected via ROS2
2. **Plugin System** - NAO6 ROS2 Integration plugin (v2.1.0) with 10 actions
3. **Database Integration** - Plugin installed, experiments seeded with NAO6 actions
4. **Web Test Interface** - `/nao-test` page working with live robot control
5. **Documentation** - 1,877 lines of comprehensive technical docs
6. **Repository Cleanup** - Consolidated into `robot-plugins` git repo (pushed to GitHub)
### 🚧 Next Step: Action Execution
**Current Gap**: Experiment designer → WebSocket → ROS2 → NAO flow not implemented
The plugin is loaded, actions are in the database, but clicking "Execute" in the wizard interface doesn't send commands to the robot yet.
---
## 🚀 Quick Start (For Next Agent)
### Terminal 1: Start NAO6 Integration
```bash
cd ~/Documents/Projects/nao6-hristudio-integration
./start-nao6.sh
```
**Expect**: Color-coded logs showing NAO Driver, ROS Bridge, ROS API running
### Terminal 2: Start HRIStudio
```bash
cd ~/Documents/Projects/hristudio
bun dev
```
**Access**: http://localhost:3000
### Verify Setup
1. **Test Page**: http://localhost:3000/nao-test
- Click "Connect" → Should turn green
- Click "Speak" → NAO should talk
- Movement buttons → NAO should move
2. **Experiment Designer**: http://localhost:3000/experiments/[id]/designer
- Check "Basic Interaction Protocol 1"
- Should see NAO6 actions in action library
- Drag actions to experiment canvas
3. **Database Check**:
```bash
bun db:seed # Should complete without errors
```
---
## 📁 Key File Locations
### NAO6 Integration Repository
```
~/Documents/Projects/nao6-hristudio-integration/
├── start-nao6.sh # START HERE - runs everything
├── nao6-plugin.json # Plugin definition (10 actions)
├── SESSION-SUMMARY.md # Complete session details
└── docs/ # Technical references
├── NAO6-ROS2-TOPICS.md (26 topics documented)
├── HRISTUDIO-ACTION-MAPPING.md (Action specs + TypeScript types)
└── INTEGRATION-SUMMARY.md (Quick reference)
```
### HRIStudio Project
```
~/Documents/Projects/hristudio/
├── robot-plugins/ # Git submodule @ github.com/soconnor0919/robot-plugins
│ └── plugins/
│ └── nao6-ros2.json # MAIN PLUGIN FILE (v2.1.0)
├── src/app/(dashboard)/nao-test/
│ └── page.tsx # Working test interface
├── src/components/experiments/designer/
│ └── ActionRegistry.ts # Loads plugin actions
└── scripts/seed-dev.ts # Seeds NAO6 plugin into DB
```
---
## 🔧 Current System State
### Database
- **2 repositories**: Core + Robot Plugins
- **4 plugins**: Core System, TurtleBot3 Burger, TurtleBot3 Waffle, **NAO6 ROS2 Integration**
- **NAO6 installed in**: "Basic Interaction Protocol 1" study
- **Experiment actions**: Step 1 has "NAO Speak Text", Step 3 has "NAO Move Head"
### ROS2 System
- **26 topics** available when `start-nao6.sh` is running
- **Key topics**: `/speech`, `/cmd_vel`, `/joint_angles`, `/joint_states`, `/bumper`, etc.
- **WebSocket**: ws://localhost:9090 (rosbridge_websocket)
### Robot
- **IP**: nao.local (134.82.159.168)
- **Credentials**: nao / robolab
- **Status**: Awake and responsive (test with ping)
---
## 🎯 Implementation Needed
### 1. Action Execution Flow
**Where to implement**:
- `src/components/trials/WizardInterface.tsx` or similar
- Connect "Execute Action" button → WebSocket → ROS2
**What it should do**:
```typescript
// When wizard clicks "Execute Action" on a NAO6 action
function executeNAO6Action(action: Action) {
// 1. Get action parameters from database
const { type, parameters } = action;
// 2. Connect to WebSocket (if not connected)
const ws = new WebSocket('ws://localhost:9090');
// 3. Map action type to ROS2 topic
const topicMapping = {
'nao6_speak': '/speech',
'nao6_move_forward': '/cmd_vel',
'nao6_move_head': '/joint_angles',
// ... etc
};
// 4. Create ROS message
const rosMessage = {
op: 'publish',
topic: topicMapping[type],
msg: formatMessageForROS(type, parameters)
};
// 5. Send to robot
ws.send(JSON.stringify(rosMessage));
// 6. Log to trial_events
logTrialEvent({
trial_id: currentTrialId,
event_type: 'action_executed',
event_data: { action, timestamp: Date.now() }
});
}
```
### 2. Message Formatting
**Reference**: See `nao6-hristudio-integration/docs/HRISTUDIO-ACTION-MAPPING.md`
**Examples**:
```typescript
function formatMessageForROS(actionType: string, params: any) {
switch(actionType) {
case 'nao6_speak':
return { data: params.text };
case 'nao6_move_forward':
return {
linear: { x: params.speed, y: 0, z: 0 },
angular: { x: 0, y: 0, z: 0 }
};
case 'nao6_move_head':
return {
joint_names: ['HeadYaw', 'HeadPitch'],
joint_angles: [params.yaw, params.pitch],
speed: params.speed,
relative: 0
};
}
}
```
### 3. WebSocket Connection Management
**Suggested approach**:
- Create `useROSBridge()` hook in `src/hooks/`
- Manage connection state, auto-reconnect
- Provide `publish()`, `subscribe()`, `callService()` methods
---
## 🧪 Testing Checklist
Before marking as complete:
- [ ] Can execute "Speak Text" action from wizard interface → NAO speaks
- [ ] Can execute "Move Forward" action → NAO walks
- [ ] Can execute "Move Head" action → NAO moves head
- [ ] Actions are logged to `trial_events` table
- [ ] Connection errors are handled gracefully
- [ ] Emergency stop works from wizard interface
- [ ] Multiple actions in sequence work
- [ ] Sensor monitoring displays in wizard interface
---
## 📚 Reference Documentation
### Primary Sources
1. **Working Example**: `src/app/(dashboard)/nao-test/page.tsx`
- Lines 67-100: WebSocket connection setup
- Lines 200-350: Action execution examples
- This is WORKING code - use it as template!
2. **Action Specifications**: `nao6-hristudio-integration/docs/HRISTUDIO-ACTION-MAPPING.md`
- Lines 1-100: Each action with parameters
- TypeScript types already defined
- WebSocket message formats included
3. **ROS2 Topics**: `nao6-hristudio-integration/docs/NAO6-ROS2-TOPICS.md`
- Complete message type definitions
- Examples for each topic
### TypeScript Types
Already defined in action mapping doc:
```typescript
interface SpeakTextAction {
action: 'nao6_speak';
parameters: {
text: string;
volume?: number;
};
}
interface MoveForwardAction {
action: 'nao6_move_forward';
parameters: {
speed: number;
duration: number;
};
}
```
---
## 🔍 Where to Look
### To understand plugin loading:
- `src/components/experiments/designer/ActionRegistry.ts`
- `src/components/experiments/designer/panels/ActionLibraryPanel.tsx`
### To see working WebSocket code:
- `src/app/(dashboard)/nao-test/page.tsx` (fully functional!)
### To find action execution trigger:
- Search for: `executeAction`, `onActionExecute`, `runAction`
- Likely in: `src/components/trials/` or `src/components/experiments/`
---
## 🚨 Important Notes
### DO NOT
- ❌ Modify `robot-plugins/` without committing/pushing (it's a git repo)
- ❌ Change plugin structure without updating seed script
- ❌ Remove `start-nao6.sh` - it's the main entry point
- ❌ Hard-code WebSocket URLs - use config/env vars
### DO
- ✅ Use existing `/nao-test` page code as reference
- ✅ Test with live robot frequently
- ✅ Log all actions to `trial_events` table
- ✅ Handle connection errors gracefully
- ✅ Add loading states for action execution
### Known Working
- ✅ WebSocket connection (`/nao-test` proves it works)
- ✅ ROS2 topics (26 topics verified)
- ✅ Plugin loading (shows in action library)
- ✅ Database integration (seed script works)
### Known NOT Working
- ❌ Action execution from experiment designer/wizard interface
- ❌ Sensor data display in wizard interface (topics exist, just not displayed)
- ❌ Camera streaming to browser
---
## 🤝 Session Handoff Summary
### What We Did
1. Connected to live NAO6 robot at nao.local
2. Documented all 26 ROS2 topics with complete specifications
3. Created clean plugin with 10 actions
4. Integrated plugin into HRIStudio database
5. Built working test interface proving WebSocket → ROS2 → NAO works
6. Cleaned up repositories (removed duplicates, committed to git)
7. Updated experiments to use NAO6 actions
8. Fixed APT repository issues
9. Created comprehensive documentation (1,877 lines)
### What's Left
**ONE THING**: Connect the experiment designer's "Execute Action" button to the WebSocket/ROS2 system.
The hard part is done. The `/nao-test` page is a fully working example of exactly what you need to do - just integrate that pattern into the wizard interface.
---
## 🎓 Key Insights
1. **We're NOT writing a WebSocket server** - using ROS2's official `rosbridge_websocket`
2. **The test page works perfectly** - copy its pattern
3. **All topics are documented** - no guesswork needed
4. **Plugin is in database** - just needs execution hookup
5. **Robot is live and responsive** - test frequently!
---
## ⚡ Quick Commands
```bash
# Start everything
cd ~/Documents/Projects/nao6-hristudio-integration && ./start-nao6.sh
cd ~/Documents/Projects/hristudio && bun dev
# Test robot
curl -X POST http://localhost:9090 -d '{"op":"publish","topic":"/speech","msg":{"data":"Test"}}'
# Reset robot
sshpass -p robolab ssh nao@nao.local \
"python2 -c 'import sys; sys.path.append(\"/opt/aldebaran/lib/python2.7/site-packages\"); import naoqi; p=naoqi.ALProxy(\"ALRobotPosture\",\"127.0.0.1\",9559); p.goToPosture(\"StandInit\", 0.5)'"
# Reseed database
cd ~/Documents/Projects/hristudio && bun db:seed
```
---
**Next Agent**: Start by reviewing `/nao-test/page.tsx` - it's the Rosetta Stone for this integration. Everything you need is already working there!
**Estimated Time**: 2-4 hours to implement action execution
**Difficulty**: Medium (pattern exists, just needs integration)
**Priority**: High (this is the final piece)
---
**Status**: 🟢 Ready for implementation
**Blocker**: None - all prerequisites met
**Dependencies**: Robot must be running (`start-nao6.sh`)

View File

@@ -230,7 +230,37 @@ Full paper available at: [docs/paper.md](docs/paper.md)
- **4 User Roles**: Complete role-based access control - **4 User Roles**: Complete role-based access control
- **Plugin System**: Extensible robot integration architecture - **Plugin System**: Extensible robot integration architecture
- **Trial System**: Unified design with real-time execution capabilities - **Trial System**: Unified design with real-time execution capabilities
- **Mock Robot Integration**: Complete simulation for development and testing
## NAO6 Robot Integration
Complete NAO6 robot integration is available in the separate **[nao6-hristudio-integration](../nao6-hristudio-integration/)** repository.
### Features
- Complete ROS2 driver integration for NAO V6.0
- WebSocket communication via rosbridge
- 9 robot actions: speech, movement, gestures, sensors, LEDs
- Real-time control from wizard interface
- Production-ready with NAOqi 2.8.7.4
### Quick Start
```bash
# Start NAO integration
cd ~/naoqi_ros2_ws
source install/setup.bash
ros2 launch nao_launch nao6_hristudio.launch.py nao_ip:=nao.local
# Start HRIStudio
cd ~/Documents/Projects/hristudio
bun dev
# Test at: http://localhost:3000/nao-test
```
### Documentation
- **[Integration README](../nao6-hristudio-integration/README.md)** - Complete setup guide
- **[NAO6 Quick Reference](docs/nao6-quick-reference.md)** - Essential commands
- **[Installation Guide](../nao6-hristudio-integration/docs/INSTALLATION.md)** - Detailed setup
- **[Troubleshooting](../nao6-hristudio-integration/docs/TROUBLESHOOTING.md)** - Common issues
## Deployment ## Deployment

0
THESIS_PROJECT_BACKLOG.md Normal file → Executable file
View File

0
TRIAL_START_DEBUG.md Normal file → Executable file
View File

0
WIZARD_INTERFACE_README.md Normal file → Executable file
View File

View File

@@ -43,7 +43,7 @@
"date-fns": "^4.1.0", "date-fns": "^4.1.0",
"drizzle-orm": "^0.41.0", "drizzle-orm": "^0.41.0",
"lucide-react": "^0.536.0", "lucide-react": "^0.536.0",
"next": "^15.5.4", "next": "^16.0.3",
"next-auth": "^5.0.0-beta.29", "next-auth": "^5.0.0-beta.29",
"postgres": "^3.4.4", "postgres": "^3.4.4",
"react": "^19.0.0", "react": "^19.0.0",
@@ -82,6 +82,8 @@
}, },
"trustedDependencies": [ "trustedDependencies": [
"@tailwindcss/oxide", "@tailwindcss/oxide",
"esbuild",
"sharp",
"unrs-resolver", "unrs-resolver",
], ],
"packages": { "packages": {
@@ -185,7 +187,7 @@
"@emnapi/core": ["@emnapi/core@1.4.5", "", { "dependencies": { "@emnapi/wasi-threads": "1.0.4", "tslib": "^2.4.0" } }, "sha512-XsLw1dEOpkSX/WucdqUhPWP7hDxSvZiY+fsUC14h+FtQ2Ifni4znbBt8punRX+Uj2JG/uDb8nEHVKvrVlvdZ5Q=="], "@emnapi/core": ["@emnapi/core@1.4.5", "", { "dependencies": { "@emnapi/wasi-threads": "1.0.4", "tslib": "^2.4.0" } }, "sha512-XsLw1dEOpkSX/WucdqUhPWP7hDxSvZiY+fsUC14h+FtQ2Ifni4znbBt8punRX+Uj2JG/uDb8nEHVKvrVlvdZ5Q=="],
"@emnapi/runtime": ["@emnapi/runtime@1.4.5", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-++LApOtY0pEEz1zrd9vy1/zXVaVJJ/EbAF3u0fXIzPJEDtnITsBGbbK0EkM72amhl/R5b+5xx0Y/QhcVOpuulg=="], "@emnapi/runtime": ["@emnapi/runtime@1.7.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA=="],
"@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.0.4", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-PJR+bOmMOPH8AtcTGAyYNiuJ3/Fcoj2XN/gBEWzDIKh254XO+mM9XoXHk5GNEhodxeMznbg7BlRojVbKN+gC6g=="], "@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.0.4", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-PJR+bOmMOPH8AtcTGAyYNiuJ3/Fcoj2XN/gBEWzDIKh254XO+mM9XoXHk5GNEhodxeMznbg7BlRojVbKN+gC6g=="],
@@ -275,49 +277,55 @@
"@humanwhocodes/retry": ["@humanwhocodes/retry@0.4.3", "", {}, "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ=="], "@humanwhocodes/retry": ["@humanwhocodes/retry@0.4.3", "", {}, "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ=="],
"@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.34.3", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.2.0" }, "os": "darwin", "cpu": "arm64" }, "sha512-ryFMfvxxpQRsgZJqBd4wsttYQbCxsJksrv9Lw/v798JcQ8+w84mBWuXwl+TT0WJ/WrYOLaYpwQXi3sA9nTIaIg=="], "@img/colour": ["@img/colour@1.0.0", "", {}, "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw=="],
"@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.34.3", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.2.0" }, "os": "darwin", "cpu": "x64" }, "sha512-yHpJYynROAj12TA6qil58hmPmAwxKKC7reUqtGLzsOHfP7/rniNGTL8tjWX6L3CTV4+5P4ypcS7Pp+7OB+8ihA=="], "@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.2.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w=="],
"@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.2.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-sBZmpwmxqwlqG9ueWFXtockhsxefaV6O84BMOrhtg/YqbTaRdqDE7hxraVE3y6gVM4eExmfzW4a8el9ArLeEiQ=="], "@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.2.4" }, "os": "darwin", "cpu": "x64" }, "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw=="],
"@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.2.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-M64XVuL94OgiNHa5/m2YvEQI5q2cl9d/wk0qFTDVXcYzi43lxuiFTftMR1tOnFQovVXNZJ5TURSDK2pNe9Yzqg=="], "@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.2.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g=="],
"@img/sharp-libvips-linux-arm": ["@img/sharp-libvips-linux-arm@1.2.0", "", { "os": "linux", "cpu": "arm" }, "sha512-mWd2uWvDtL/nvIzThLq3fr2nnGfyr/XMXlq8ZJ9WMR6PXijHlC3ksp0IpuhK6bougvQrchUAfzRLnbsen0Cqvw=="], "@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.2.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg=="],
"@img/sharp-libvips-linux-arm64": ["@img/sharp-libvips-linux-arm64@1.2.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-RXwd0CgG+uPRX5YYrkzKyalt2OJYRiJQ8ED/fi1tq9WQW2jsQIn0tqrlR5l5dr/rjqq6AHAxURhj2DVjyQWSOA=="], "@img/sharp-libvips-linux-arm": ["@img/sharp-libvips-linux-arm@1.2.4", "", { "os": "linux", "cpu": "arm" }, "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A=="],
"@img/sharp-libvips-linux-ppc64": ["@img/sharp-libvips-linux-ppc64@1.2.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-Xod/7KaDDHkYu2phxxfeEPXfVXFKx70EAFZ0qyUdOjCcxbjqyJOEUpDe6RIyaunGxT34Anf9ue/wuWOqBW2WcQ=="], "@img/sharp-libvips-linux-arm64": ["@img/sharp-libvips-linux-arm64@1.2.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw=="],
"@img/sharp-libvips-linux-s390x": ["@img/sharp-libvips-linux-s390x@1.2.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-eMKfzDxLGT8mnmPJTNMcjfO33fLiTDsrMlUVcp6b96ETbnJmd4uvZxVJSKPQfS+odwfVaGifhsB07J1LynFehw=="], "@img/sharp-libvips-linux-ppc64": ["@img/sharp-libvips-linux-ppc64@1.2.4", "", { "os": "linux", "cpu": "ppc64" }, "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA=="],
"@img/sharp-libvips-linux-x64": ["@img/sharp-libvips-linux-x64@1.2.0", "", { "os": "linux", "cpu": "x64" }, "sha512-ZW3FPWIc7K1sH9E3nxIGB3y3dZkpJlMnkk7z5tu1nSkBoCgw2nSRTFHI5pB/3CQaJM0pdzMF3paf9ckKMSE9Tg=="], "@img/sharp-libvips-linux-riscv64": ["@img/sharp-libvips-linux-riscv64@1.2.4", "", { "os": "linux", "cpu": "none" }, "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA=="],
"@img/sharp-libvips-linuxmusl-arm64": ["@img/sharp-libvips-linuxmusl-arm64@1.2.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-UG+LqQJbf5VJ8NWJ5Z3tdIe/HXjuIdo4JeVNADXBFuG7z9zjoegpzzGIyV5zQKi4zaJjnAd2+g2nna8TZvuW9Q=="], "@img/sharp-libvips-linux-s390x": ["@img/sharp-libvips-linux-s390x@1.2.4", "", { "os": "linux", "cpu": "s390x" }, "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ=="],
"@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.2.0", "", { "os": "linux", "cpu": "x64" }, "sha512-SRYOLR7CXPgNze8akZwjoGBoN1ThNZoqpOgfnOxmWsklTGVfJiGJoC/Lod7aNMGA1jSsKWM1+HRX43OP6p9+6Q=="], "@img/sharp-libvips-linux-x64": ["@img/sharp-libvips-linux-x64@1.2.4", "", { "os": "linux", "cpu": "x64" }, "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw=="],
"@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.34.3", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.2.0" }, "os": "linux", "cpu": "arm" }, "sha512-oBK9l+h6KBN0i3dC8rYntLiVfW8D8wH+NPNT3O/WBHeW0OQWCjfWksLUaPidsrDKpJgXp3G3/hkmhptAW0I3+A=="], "@img/sharp-libvips-linuxmusl-arm64": ["@img/sharp-libvips-linuxmusl-arm64@1.2.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw=="],
"@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.34.3", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.2.0" }, "os": "linux", "cpu": "arm64" }, "sha512-QdrKe3EvQrqwkDrtuTIjI0bu6YEJHTgEeqdzI3uWJOH6G1O8Nl1iEeVYRGdj1h5I21CqxSvQp1Yv7xeU3ZewbA=="], "@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.2.4", "", { "os": "linux", "cpu": "x64" }, "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg=="],
"@img/sharp-linux-ppc64": ["@img/sharp-linux-ppc64@0.34.3", "", { "optionalDependencies": { "@img/sharp-libvips-linux-ppc64": "1.2.0" }, "os": "linux", "cpu": "ppc64" }, "sha512-GLtbLQMCNC5nxuImPR2+RgrviwKwVql28FWZIW1zWruy6zLgA5/x2ZXk3mxj58X/tszVF69KK0Is83V8YgWhLA=="], "@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.2.4" }, "os": "linux", "cpu": "arm" }, "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw=="],
"@img/sharp-linux-s390x": ["@img/sharp-linux-s390x@0.34.3", "", { "optionalDependencies": { "@img/sharp-libvips-linux-s390x": "1.2.0" }, "os": "linux", "cpu": "s390x" }, "sha512-3gahT+A6c4cdc2edhsLHmIOXMb17ltffJlxR0aC2VPZfwKoTGZec6u5GrFgdR7ciJSsHT27BD3TIuGcuRT0KmQ=="], "@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.2.4" }, "os": "linux", "cpu": "arm64" }, "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg=="],
"@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.34.3", "", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.2.0" }, "os": "linux", "cpu": "x64" }, "sha512-8kYso8d806ypnSq3/Ly0QEw90V5ZoHh10yH0HnrzOCr6DKAPI6QVHvwleqMkVQ0m+fc7EH8ah0BB0QPuWY6zJQ=="], "@img/sharp-linux-ppc64": ["@img/sharp-linux-ppc64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-ppc64": "1.2.4" }, "os": "linux", "cpu": "ppc64" }, "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA=="],
"@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.34.3", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.2.0" }, "os": "linux", "cpu": "arm64" }, "sha512-vAjbHDlr4izEiXM1OTggpCcPg9tn4YriK5vAjowJsHwdBIdx0fYRsURkxLG2RLm9gyBq66gwtWI8Gx0/ov+JKQ=="], "@img/sharp-linux-riscv64": ["@img/sharp-linux-riscv64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-riscv64": "1.2.4" }, "os": "linux", "cpu": "none" }, "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw=="],
"@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.34.3", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.2.0" }, "os": "linux", "cpu": "x64" }, "sha512-gCWUn9547K5bwvOn9l5XGAEjVTTRji4aPTqLzGXHvIr6bIDZKNTA34seMPgM0WmSf+RYBH411VavCejp3PkOeQ=="], "@img/sharp-linux-s390x": ["@img/sharp-linux-s390x@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-s390x": "1.2.4" }, "os": "linux", "cpu": "s390x" }, "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg=="],
"@img/sharp-wasm32": ["@img/sharp-wasm32@0.34.3", "", { "dependencies": { "@emnapi/runtime": "^1.4.4" }, "cpu": "none" }, "sha512-+CyRcpagHMGteySaWos8IbnXcHgfDn7pO2fiC2slJxvNq9gDipYBN42/RagzctVRKgxATmfqOSulgZv5e1RdMg=="], "@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.2.4" }, "os": "linux", "cpu": "x64" }, "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ=="],
"@img/sharp-win32-arm64": ["@img/sharp-win32-arm64@0.34.3", "", { "os": "win32", "cpu": "arm64" }, "sha512-MjnHPnbqMXNC2UgeLJtX4XqoVHHlZNd+nPt1kRPmj63wURegwBhZlApELdtxM2OIZDRv/DFtLcNhVbd1z8GYXQ=="], "@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" }, "os": "linux", "cpu": "arm64" }, "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg=="],
"@img/sharp-win32-ia32": ["@img/sharp-win32-ia32@0.34.3", "", { "os": "win32", "cpu": "ia32" }, "sha512-xuCdhH44WxuXgOM714hn4amodJMZl3OEvf0GVTm0BEyMeA2to+8HEdRPShH0SLYptJY1uBw+SCFP9WVQi1Q/cw=="], "@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.2.4" }, "os": "linux", "cpu": "x64" }, "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q=="],
"@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.34.3", "", { "os": "win32", "cpu": "x64" }, "sha512-OWwz05d++TxzLEv4VnsTz5CmZ6mI6S05sfQGEMrNrQcOEERbX46332IvE7pO/EUiw7jUrrS40z/M7kPyjfl04g=="], "@img/sharp-wasm32": ["@img/sharp-wasm32@0.34.5", "", { "dependencies": { "@emnapi/runtime": "^1.7.0" }, "cpu": "none" }, "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw=="],
"@img/sharp-win32-arm64": ["@img/sharp-win32-arm64@0.34.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g=="],
"@img/sharp-win32-ia32": ["@img/sharp-win32-ia32@0.34.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg=="],
"@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.34.5", "", { "os": "win32", "cpu": "x64" }, "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw=="],
"@isaacs/fs-minipass": ["@isaacs/fs-minipass@4.0.1", "", { "dependencies": { "minipass": "^7.0.4" } }, "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w=="], "@isaacs/fs-minipass": ["@isaacs/fs-minipass@4.0.1", "", { "dependencies": { "minipass": "^7.0.4" } }, "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w=="],
@@ -331,25 +339,25 @@
"@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@0.2.12", "", { "dependencies": { "@emnapi/core": "^1.4.3", "@emnapi/runtime": "^1.4.3", "@tybys/wasm-util": "^0.10.0" } }, "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ=="], "@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@0.2.12", "", { "dependencies": { "@emnapi/core": "^1.4.3", "@emnapi/runtime": "^1.4.3", "@tybys/wasm-util": "^0.10.0" } }, "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ=="],
"@next/env": ["@next/env@15.5.4", "", {}, "sha512-27SQhYp5QryzIT5uO8hq99C69eLQ7qkzkDPsk3N+GuS2XgOgoYEeOav7Pf8Tn4drECOVDsDg8oj+/DVy8qQL2A=="], "@next/env": ["@next/env@16.0.3", "", {}, "sha512-IqgtY5Vwsm14mm/nmQaRMmywCU+yyMIYfk3/MHZ2ZTJvwVbBn3usZnjMi1GacrMVzVcAxJShTCpZlPs26EdEjQ=="],
"@next/eslint-plugin-next": ["@next/eslint-plugin-next@15.4.5", "", { "dependencies": { "fast-glob": "3.3.1" } }, "sha512-YhbrlbEt0m4jJnXHMY/cCUDBAWgd5SaTa5mJjzOt82QwflAFfW/h3+COp2TfVSzhmscIZ5sg2WXt3MLziqCSCw=="], "@next/eslint-plugin-next": ["@next/eslint-plugin-next@15.4.5", "", { "dependencies": { "fast-glob": "3.3.1" } }, "sha512-YhbrlbEt0m4jJnXHMY/cCUDBAWgd5SaTa5mJjzOt82QwflAFfW/h3+COp2TfVSzhmscIZ5sg2WXt3MLziqCSCw=="],
"@next/swc-darwin-arm64": ["@next/swc-darwin-arm64@15.5.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-nopqz+Ov6uvorej8ndRX6HlxCYWCO3AHLfKK2TYvxoSB2scETOcfm/HSS3piPqc3A+MUgyHoqE6je4wnkjfrOA=="], "@next/swc-darwin-arm64": ["@next/swc-darwin-arm64@16.0.3", "", { "os": "darwin", "cpu": "arm64" }, "sha512-MOnbd92+OByu0p6QBAzq1ahVWzF6nyfiH07dQDez4/Nku7G249NjxDVyEfVhz8WkLiOEU+KFVnqtgcsfP2nLXg=="],
"@next/swc-darwin-x64": ["@next/swc-darwin-x64@15.5.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-QOTCFq8b09ghfjRJKfb68kU9k2K+2wsC4A67psOiMn849K9ZXgCSRQr0oVHfmKnoqCbEmQWG1f2h1T2vtJJ9mA=="], "@next/swc-darwin-x64": ["@next/swc-darwin-x64@16.0.3", "", { "os": "darwin", "cpu": "x64" }, "sha512-i70C4O1VmbTivYdRlk+5lj9xRc2BlK3oUikt3yJeHT1unL4LsNtN7UiOhVanFdc7vDAgZn1tV/9mQwMkWOJvHg=="],
"@next/swc-linux-arm64-gnu": ["@next/swc-linux-arm64-gnu@15.5.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-eRD5zkts6jS3VfE/J0Kt1VxdFqTnMc3QgO5lFE5GKN3KDI/uUpSyK3CjQHmfEkYR4wCOl0R0XrsjpxfWEA++XA=="], "@next/swc-linux-arm64-gnu": ["@next/swc-linux-arm64-gnu@16.0.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-O88gCZ95sScwD00mn/AtalyCoykhhlokxH/wi1huFK+rmiP5LAYVs/i2ruk7xST6SuXN4NI5y4Xf5vepb2jf6A=="],
"@next/swc-linux-arm64-musl": ["@next/swc-linux-arm64-musl@15.5.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-TOK7iTxmXFc45UrtKqWdZ1shfxuL4tnVAOuuJK4S88rX3oyVV4ZkLjtMT85wQkfBrOOvU55aLty+MV8xmcJR8A=="], "@next/swc-linux-arm64-musl": ["@next/swc-linux-arm64-musl@16.0.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-CEErFt78S/zYXzFIiv18iQCbRbLgBluS8z1TNDQoyPi8/Jr5qhR3e8XHAIxVxPBjDbEMITprqELVc5KTfFj0gg=="],
"@next/swc-linux-x64-gnu": ["@next/swc-linux-x64-gnu@15.5.4", "", { "os": "linux", "cpu": "x64" }, "sha512-7HKolaj+481FSW/5lL0BcTkA4Ueam9SPYWyN/ib/WGAFZf0DGAN8frNpNZYFHtM4ZstrHZS3LY3vrwlIQfsiMA=="], "@next/swc-linux-x64-gnu": ["@next/swc-linux-x64-gnu@16.0.3", "", { "os": "linux", "cpu": "x64" }, "sha512-Tc3i+nwt6mQ+Dwzcri/WNDj56iWdycGVh5YwwklleClzPzz7UpfaMw1ci7bLl6GRYMXhWDBfe707EXNjKtiswQ=="],
"@next/swc-linux-x64-musl": ["@next/swc-linux-x64-musl@15.5.4", "", { "os": "linux", "cpu": "x64" }, "sha512-nlQQ6nfgN0nCO/KuyEUwwOdwQIGjOs4WNMjEUtpIQJPR2NUfmGpW2wkJln1d4nJ7oUzd1g4GivH5GoEPBgfsdw=="], "@next/swc-linux-x64-musl": ["@next/swc-linux-x64-musl@16.0.3", "", { "os": "linux", "cpu": "x64" }, "sha512-zTh03Z/5PBBPdTurgEtr6nY0vI9KR9Ifp/jZCcHlODzwVOEKcKRBtQIGrkc7izFgOMuXDEJBmirwpGqdM/ZixA=="],
"@next/swc-win32-arm64-msvc": ["@next/swc-win32-arm64-msvc@15.5.4", "", { "os": "win32", "cpu": "arm64" }, "sha512-PcR2bN7FlM32XM6eumklmyWLLbu2vs+D7nJX8OAIoWy69Kef8mfiN4e8TUv2KohprwifdpFKPzIP1njuCjD0YA=="], "@next/swc-win32-arm64-msvc": ["@next/swc-win32-arm64-msvc@16.0.3", "", { "os": "win32", "cpu": "arm64" }, "sha512-Jc1EHxtZovcJcg5zU43X3tuqzl/sS+CmLgjRP28ZT4vk869Ncm2NoF8qSTaL99gh6uOzgM99Shct06pSO6kA6g=="],
"@next/swc-win32-x64-msvc": ["@next/swc-win32-x64-msvc@15.5.4", "", { "os": "win32", "cpu": "x64" }, "sha512-1ur2tSHZj8Px/KMAthmuI9FMp/YFusMMGoRNJaRZMOlSkgvLjzosSdQI0cJAKogdHl3qXUQKL9MGaYvKwA7DXg=="], "@next/swc-win32-x64-msvc": ["@next/swc-win32-x64-msvc@16.0.3", "", { "os": "win32", "cpu": "x64" }, "sha512-N7EJ6zbxgIYpI/sWNzpVKRMbfEGgsWuOIvzkML7wxAAZhPk1Msxuo/JDu1PKjWGrAoOLaZcIX5s+/pF5LIbBBg=="],
"@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="], "@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="],
@@ -777,14 +785,10 @@
"cmdk": ["cmdk@1.1.1", "", { "dependencies": { "@radix-ui/react-compose-refs": "^1.1.1", "@radix-ui/react-dialog": "^1.1.6", "@radix-ui/react-id": "^1.1.0", "@radix-ui/react-primitive": "^2.0.2" }, "peerDependencies": { "react": "^18 || ^19 || ^19.0.0-rc", "react-dom": "^18 || ^19 || ^19.0.0-rc" } }, "sha512-Vsv7kFaXm+ptHDMZ7izaRsP70GgrW9NBNGswt9OZaVBLlE0SNpDq8eu/VGXyF9r7M0azK3Wy7OlYXsuyYLFzHg=="], "cmdk": ["cmdk@1.1.1", "", { "dependencies": { "@radix-ui/react-compose-refs": "^1.1.1", "@radix-ui/react-dialog": "^1.1.6", "@radix-ui/react-id": "^1.1.0", "@radix-ui/react-primitive": "^2.0.2" }, "peerDependencies": { "react": "^18 || ^19 || ^19.0.0-rc", "react-dom": "^18 || ^19 || ^19.0.0-rc" } }, "sha512-Vsv7kFaXm+ptHDMZ7izaRsP70GgrW9NBNGswt9OZaVBLlE0SNpDq8eu/VGXyF9r7M0azK3Wy7OlYXsuyYLFzHg=="],
"color": ["color@4.2.3", "", { "dependencies": { "color-convert": "^2.0.1", "color-string": "^1.9.0" } }, "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A=="],
"color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], "color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="],
"color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="], "color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="],
"color-string": ["color-string@1.9.1", "", { "dependencies": { "color-name": "^1.0.0", "simple-swizzle": "^0.2.2" } }, "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg=="],
"commander": ["commander@10.0.1", "", {}, "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug=="], "commander": ["commander@10.0.1", "", {}, "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug=="],
"concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="], "concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="],
@@ -987,8 +991,6 @@
"is-array-buffer": ["is-array-buffer@3.0.5", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "get-intrinsic": "^1.2.6" } }, "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A=="], "is-array-buffer": ["is-array-buffer@3.0.5", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "get-intrinsic": "^1.2.6" } }, "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A=="],
"is-arrayish": ["is-arrayish@0.3.2", "", {}, "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ=="],
"is-async-function": ["is-async-function@2.1.1", "", { "dependencies": { "async-function": "^1.0.0", "call-bound": "^1.0.3", "get-proto": "^1.0.1", "has-tostringtag": "^1.0.2", "safe-regex-test": "^1.1.0" } }, "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ=="], "is-async-function": ["is-async-function@2.1.1", "", { "dependencies": { "async-function": "^1.0.0", "call-bound": "^1.0.3", "get-proto": "^1.0.1", "has-tostringtag": "^1.0.2", "safe-regex-test": "^1.1.0" } }, "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ=="],
"is-bigint": ["is-bigint@1.1.0", "", { "dependencies": { "has-bigints": "^1.0.2" } }, "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ=="], "is-bigint": ["is-bigint@1.1.0", "", { "dependencies": { "has-bigints": "^1.0.2" } }, "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ=="],
@@ -1145,7 +1147,7 @@
"natural-compare": ["natural-compare@1.4.0", "", {}, "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="], "natural-compare": ["natural-compare@1.4.0", "", {}, "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="],
"next": ["next@15.5.4", "", { "dependencies": { "@next/env": "15.5.4", "@swc/helpers": "0.5.15", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", "styled-jsx": "5.1.6" }, "optionalDependencies": { "@next/swc-darwin-arm64": "15.5.4", "@next/swc-darwin-x64": "15.5.4", "@next/swc-linux-arm64-gnu": "15.5.4", "@next/swc-linux-arm64-musl": "15.5.4", "@next/swc-linux-x64-gnu": "15.5.4", "@next/swc-linux-x64-musl": "15.5.4", "@next/swc-win32-arm64-msvc": "15.5.4", "@next/swc-win32-x64-msvc": "15.5.4", "sharp": "^0.34.3" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", "@playwright/test": "^1.51.1", "babel-plugin-react-compiler": "*", "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "sass": "^1.3.0" }, "optionalPeers": ["@opentelemetry/api", "@playwright/test", "babel-plugin-react-compiler", "sass"], "bin": { "next": "dist/bin/next" } }, "sha512-xH4Yjhb82sFYQfY3vbkJfgSDgXvBB6a8xPs9i35k6oZJRoQRihZH+4s9Yo2qsWpzBmZ3lPXaJ2KPXLfkvW4LnA=="], "next": ["next@16.0.3", "", { "dependencies": { "@next/env": "16.0.3", "@swc/helpers": "0.5.15", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", "styled-jsx": "5.1.6" }, "optionalDependencies": { "@next/swc-darwin-arm64": "16.0.3", "@next/swc-darwin-x64": "16.0.3", "@next/swc-linux-arm64-gnu": "16.0.3", "@next/swc-linux-arm64-musl": "16.0.3", "@next/swc-linux-x64-gnu": "16.0.3", "@next/swc-linux-x64-musl": "16.0.3", "@next/swc-win32-arm64-msvc": "16.0.3", "@next/swc-win32-x64-msvc": "16.0.3", "sharp": "^0.34.4" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", "@playwright/test": "^1.51.1", "babel-plugin-react-compiler": "*", "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "sass": "^1.3.0" }, "optionalPeers": ["@opentelemetry/api", "@playwright/test", "babel-plugin-react-compiler", "sass"], "bin": { "next": "dist/bin/next" } }, "sha512-Ka0/iNBblPFcIubTA1Jjh6gvwqfjrGq1Y2MTI5lbjeLIAfmC+p5bQmojpRZqgHHVu5cG4+qdIiwXiBSm/8lZ3w=="],
"next-auth": ["next-auth@5.0.0-beta.29", "", { "dependencies": { "@auth/core": "0.40.0" }, "peerDependencies": { "@simplewebauthn/browser": "^9.0.1", "@simplewebauthn/server": "^9.0.2", "next": "^14.0.0-0 || ^15.0.0-0", "nodemailer": "^6.6.5", "react": "^18.2.0 || ^19.0.0-0" }, "optionalPeers": ["@simplewebauthn/browser", "@simplewebauthn/server", "nodemailer"] }, "sha512-Ukpnuk3NMc/LiOl32njZPySk7pABEzbjhMUFd5/n10I0ZNC7NCuVv8IY2JgbDek2t/PUOifQEoUiOOTLy4os5A=="], "next-auth": ["next-auth@5.0.0-beta.29", "", { "dependencies": { "@auth/core": "0.40.0" }, "peerDependencies": { "@simplewebauthn/browser": "^9.0.1", "@simplewebauthn/server": "^9.0.2", "next": "^14.0.0-0 || ^15.0.0-0", "nodemailer": "^6.6.5", "react": "^18.2.0 || ^19.0.0-0" }, "optionalPeers": ["@simplewebauthn/browser", "@simplewebauthn/server", "nodemailer"] }, "sha512-Ukpnuk3NMc/LiOl32njZPySk7pABEzbjhMUFd5/n10I0ZNC7NCuVv8IY2JgbDek2t/PUOifQEoUiOOTLy4os5A=="],
@@ -1275,7 +1277,7 @@
"set-proto": ["set-proto@1.0.0", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0" } }, "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw=="], "set-proto": ["set-proto@1.0.0", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0" } }, "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw=="],
"sharp": ["sharp@0.34.3", "", { "dependencies": { "color": "^4.2.3", "detect-libc": "^2.0.4", "semver": "^7.7.2" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.34.3", "@img/sharp-darwin-x64": "0.34.3", "@img/sharp-libvips-darwin-arm64": "1.2.0", "@img/sharp-libvips-darwin-x64": "1.2.0", "@img/sharp-libvips-linux-arm": "1.2.0", "@img/sharp-libvips-linux-arm64": "1.2.0", "@img/sharp-libvips-linux-ppc64": "1.2.0", "@img/sharp-libvips-linux-s390x": "1.2.0", "@img/sharp-libvips-linux-x64": "1.2.0", "@img/sharp-libvips-linuxmusl-arm64": "1.2.0", "@img/sharp-libvips-linuxmusl-x64": "1.2.0", "@img/sharp-linux-arm": "0.34.3", "@img/sharp-linux-arm64": "0.34.3", "@img/sharp-linux-ppc64": "0.34.3", "@img/sharp-linux-s390x": "0.34.3", "@img/sharp-linux-x64": "0.34.3", "@img/sharp-linuxmusl-arm64": "0.34.3", "@img/sharp-linuxmusl-x64": "0.34.3", "@img/sharp-wasm32": "0.34.3", "@img/sharp-win32-arm64": "0.34.3", "@img/sharp-win32-ia32": "0.34.3", "@img/sharp-win32-x64": "0.34.3" } }, "sha512-eX2IQ6nFohW4DbvHIOLRB3MHFpYqaqvXd3Tp5e/T/dSH83fxaNJQRvDMhASmkNTsNTVF2/OOopzRCt7xokgPfg=="], "sharp": ["sharp@0.34.5", "", { "dependencies": { "@img/colour": "^1.0.0", "detect-libc": "^2.1.2", "semver": "^7.7.3" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.34.5", "@img/sharp-darwin-x64": "0.34.5", "@img/sharp-libvips-darwin-arm64": "1.2.4", "@img/sharp-libvips-darwin-x64": "1.2.4", "@img/sharp-libvips-linux-arm": "1.2.4", "@img/sharp-libvips-linux-arm64": "1.2.4", "@img/sharp-libvips-linux-ppc64": "1.2.4", "@img/sharp-libvips-linux-riscv64": "1.2.4", "@img/sharp-libvips-linux-s390x": "1.2.4", "@img/sharp-libvips-linux-x64": "1.2.4", "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", "@img/sharp-libvips-linuxmusl-x64": "1.2.4", "@img/sharp-linux-arm": "0.34.5", "@img/sharp-linux-arm64": "0.34.5", "@img/sharp-linux-ppc64": "0.34.5", "@img/sharp-linux-riscv64": "0.34.5", "@img/sharp-linux-s390x": "0.34.5", "@img/sharp-linux-x64": "0.34.5", "@img/sharp-linuxmusl-arm64": "0.34.5", "@img/sharp-linuxmusl-x64": "0.34.5", "@img/sharp-wasm32": "0.34.5", "@img/sharp-win32-arm64": "0.34.5", "@img/sharp-win32-ia32": "0.34.5", "@img/sharp-win32-x64": "0.34.5" } }, "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg=="],
"shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="], "shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="],
@@ -1293,8 +1295,6 @@
"signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="], "signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="],
"simple-swizzle": ["simple-swizzle@0.2.2", "", { "dependencies": { "is-arrayish": "^0.3.1" } }, "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg=="],
"sisteransi": ["sisteransi@1.0.5", "", {}, "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg=="], "sisteransi": ["sisteransi@1.0.5", "", {}, "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg=="],
"sonner": ["sonner@2.0.7", "", { "peerDependencies": { "react": "^18.0.0 || ^19.0.0 || ^19.0.0-rc", "react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-rc" } }, "sha512-W6ZN4p58k8aDKA4XPcx2hpIQXBRAgyiWVkYhT7CvK6D3iAu7xjvVyhQHg2/iaKJZ1XVJ4r7XuwGL+WGEK37i9w=="], "sonner": ["sonner@2.0.7", "", { "peerDependencies": { "react": "^18.0.0 || ^19.0.0 || ^19.0.0-rc", "react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-rc" } }, "sha512-W6ZN4p58k8aDKA4XPcx2hpIQXBRAgyiWVkYhT7CvK6D3iAu7xjvVyhQHg2/iaKJZ1XVJ4r7XuwGL+WGEK37i9w=="],
@@ -1437,6 +1437,8 @@
"@humanfs/node/@humanwhocodes/retry": ["@humanwhocodes/retry@0.3.1", "", {}, "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA=="], "@humanfs/node/@humanwhocodes/retry": ["@humanwhocodes/retry@0.3.1", "", {}, "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA=="],
"@napi-rs/wasm-runtime/@emnapi/runtime": ["@emnapi/runtime@1.4.5", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-++LApOtY0pEEz1zrd9vy1/zXVaVJJ/EbAF3u0fXIzPJEDtnITsBGbbK0EkM72amhl/R5b+5xx0Y/QhcVOpuulg=="],
"@radix-ui/react-menu/@radix-ui/react-roving-focus": ["@radix-ui/react-roving-focus@1.1.10", "", { "dependencies": { "@radix-ui/primitive": "1.1.2", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-dT9aOXUen9JSsxnMPv/0VqySQf5eDQ6LCk5Sw28kamz8wSOW2bJdlX2Bg5VUIIcV+6XlHpWTIuTPCf/UNIyq8Q=="], "@radix-ui/react-menu/@radix-ui/react-roving-focus": ["@radix-ui/react-roving-focus@1.1.10", "", { "dependencies": { "@radix-ui/primitive": "1.1.2", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-dT9aOXUen9JSsxnMPv/0VqySQf5eDQ6LCk5Sw28kamz8wSOW2bJdlX2Bg5VUIIcV+6XlHpWTIuTPCf/UNIyq8Q=="],
"@radix-ui/react-roving-focus/@radix-ui/primitive": ["@radix-ui/primitive@1.1.3", "", {}, "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg=="], "@radix-ui/react-roving-focus/@radix-ui/primitive": ["@radix-ui/primitive@1.1.3", "", {}, "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg=="],
@@ -1495,6 +1497,10 @@
"restore-cursor/onetime": ["onetime@5.1.2", "", { "dependencies": { "mimic-fn": "^2.1.0" } }, "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg=="], "restore-cursor/onetime": ["onetime@5.1.2", "", { "dependencies": { "mimic-fn": "^2.1.0" } }, "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg=="],
"sharp/detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="],
"sharp/semver": ["semver@7.7.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="],
"@aws-crypto/sha1-browser/@smithy/util-utf8/@smithy/util-buffer-from": ["@smithy/util-buffer-from@2.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA=="], "@aws-crypto/sha1-browser/@smithy/util-utf8/@smithy/util-buffer-from": ["@smithy/util-buffer-from@2.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA=="],
"@aws-crypto/sha256-browser/@smithy/util-utf8/@smithy/util-buffer-from": ["@smithy/util-buffer-from@2.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA=="], "@aws-crypto/sha256-browser/@smithy/util-utf8/@smithy/util-buffer-from": ["@smithy/util-buffer-from@2.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA=="],

0
components.json Normal file → Executable file
View File

0
docker-compose.yml Normal file → Executable file
View File

0
docs/README.md Normal file → Executable file
View File

0
docs/api-routes.md Normal file → Executable file
View File

0
docs/block-designer-implementation.md Normal file → Executable file
View File

0
docs/block-designer.md Normal file → Executable file
View File

0
docs/cleanup-summary.md Normal file → Executable file
View File

0
docs/core-blocks-system.md Normal file → Executable file
View File

0
docs/database-schema.md Normal file → Executable file
View File

0
docs/deployment-operations.md Normal file → Executable file
View File

0
docs/experiment-designer-redesign.md Normal file → Executable file
View File

0
docs/experiment-designer-step-integration.md Normal file → Executable file
View File

0
docs/feature-requirements.md Normal file → Executable file
View File

0
docs/flow-designer-connections.md Normal file → Executable file
View File

0
docs/implementation-details.md Normal file → Executable file
View File

0
docs/implementation-guide.md Normal file → Executable file
View File

View File

@@ -1,630 +0,0 @@
# NAO6 HRIStudio Integration: Complete Setup and Troubleshooting Guide
This comprehensive guide documents the complete process of integrating a NAO6 robot with HRIStudio, including all troubleshooting steps and solutions discovered during implementation.
## Overview
NAO6 integration with HRIStudio provides full robot control through a web-based interface, enabling researchers to conduct Human-Robot Interaction experiments with real-time robot control, sensor monitoring, and data collection.
**Integration Architecture:**
```
HRIStudio Web Interface → WebSocket → ROS Bridge → NAOqi Driver → NAO6 Robot
```
## Prerequisites
### Hardware Requirements
- NAO6 robot (NAOqi OS 2.8.7+)
- Ubuntu 22.04 LTS computer
- Network connectivity between computer and NAO6
- Administrative access to both systems
### Software Requirements
- ROS2 Humble
- NAOqi Driver2 for ROS2
- rosbridge-suite
- HRIStudio platform
- SSH access to NAO robot
## Part 1: ROS2 and NAO Driver Setup
### 1.1 Install ROS2 Humble
```bash
# Update system
sudo apt update && sudo apt upgrade -y
# Install ROS2 Humble
sudo apt install software-properties-common
sudo add-apt-repository universe
sudo apt update && sudo apt install curl -y
sudo curl -sSL https://raw.githubusercontent.com/ros/rosdistro/master/ros.asc | sudo apt-key add -
sudo sh -c 'echo "deb http://packages.ros.org/ros2/ubuntu $(lsb_release -cs) main" > /etc/apt/sources.list.d/ros2-latest.list'
sudo apt update
sudo apt install ros-humble-desktop
sudo apt install ros-dev-tools
# Source ROS2
echo "source /opt/ros/humble/setup.bash" >> ~/.bashrc
source ~/.bashrc
```
### 1.2 Install Required ROS2 Packages
```bash
# Install rosbridge for HRIStudio communication
sudo apt install ros-humble-rosbridge-suite
# Install additional useful packages
sudo apt install ros-humble-rqt
sudo apt install ros-humble-rqt-common-plugins
```
### 1.3 Set Up NAO Workspace
**Note:** We assume you already have a NAO workspace at `~/naoqi_ros2_ws` with the NAOqi driver installed.
```bash
# Verify workspace exists
ls ~/naoqi_ros2_ws/src/naoqi_driver2
```
If you need to set up the workspace from scratch, refer to the NAOqi ROS2 documentation.
### 1.4 Create Integrated Launch Package
Create a launch package that combines NAOqi driver with rosbridge:
```bash
cd ~/naoqi_ros2_ws
mkdir -p src/nao_launch/launch
```
**Create launch file** (`src/nao_launch/launch/nao6_hristudio.launch.py`):
```python
from launch import LaunchDescription
from launch.actions import DeclareLaunchArgument
from launch.substitutions import LaunchConfiguration
from launch_ros.actions import Node
def generate_launch_description():
return LaunchDescription([
# NAO IP configuration
DeclareLaunchArgument("nao_ip", default_value="nao.local"),
DeclareLaunchArgument("nao_port", default_value="9559"),
DeclareLaunchArgument("username", default_value="nao"),
DeclareLaunchArgument("password", default_value="nao"),
DeclareLaunchArgument("network_interface", default_value="eth0"),
DeclareLaunchArgument("qi_listen_url", default_value="tcp://0.0.0.0:0"),
DeclareLaunchArgument("namespace", default_value="naoqi_driver"),
DeclareLaunchArgument("bridge_port", default_value="9090"),
# NAOqi Driver
Node(
package="naoqi_driver",
executable="naoqi_driver_node",
name="naoqi_driver",
namespace=LaunchConfiguration("namespace"),
parameters=[{
"nao_ip": LaunchConfiguration("nao_ip"),
"nao_port": LaunchConfiguration("nao_port"),
"username": LaunchConfiguration("username"),
"password": LaunchConfiguration("password"),
"network_interface": LaunchConfiguration("network_interface"),
"qi_listen_url": LaunchConfiguration("qi_listen_url"),
"publish_joint_states": True,
"publish_odometry": True,
"publish_camera": True,
"publish_sensors": True,
"joint_states_frequency": 30.0,
"odom_frequency": 30.0,
"camera_frequency": 15.0,
"sensor_frequency": 10.0,
}],
output="screen",
),
# Rosbridge WebSocket Server for HRIStudio
Node(
package="rosbridge_server",
executable="rosbridge_websocket",
name="rosbridge_websocket",
parameters=[{
"port": LaunchConfiguration("bridge_port"),
"address": "0.0.0.0",
"authenticate": False,
"fragment_timeout": 600,
"delay_between_messages": 0,
"max_message_size": 10000000,
}],
output="screen",
),
# ROS API Server (required for rosbridge functionality)
Node(
package="rosapi",
executable="rosapi_node",
name="rosapi",
output="screen",
),
])
```
**Create package.xml**:
```xml
<?xml version="1.0"?>
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypeid="pf3"?>
<package format="3">
<name>nao_launch</name>
<version>1.0.0</version>
<description>Launch files for NAO6 HRIStudio integration</description>
<maintainer email="your@email.com">Your Name</maintainer>
<license>MIT</license>
<buildtool_depend>ament_cmake</buildtool_depend>
<exec_depend>launch</exec_depend>
<exec_depend>launch_ros</exec_depend>
<exec_depend>naoqi_driver</exec_depend>
<exec_depend>rosbridge_server</exec_depend>
<exec_depend>rosapi</exec_depend>
</package>
```
**Create CMakeLists.txt**:
```cmake
cmake_minimum_required(VERSION 3.8)
project(nao_launch)
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
add_compile_options(-Wall -Wextra -Wpedantic)
endif()
find_package(ament_cmake REQUIRED)
install(DIRECTORY launch/
DESTINATION share/${PROJECT_NAME}/launch/
)
if(BUILD_TESTING)
find_package(ament_lint_auto REQUIRED)
ament_lint_auto_find_test_dependencies()
endif()
ament_package()
```
### 1.5 Build the Workspace
```bash
cd ~/naoqi_ros2_ws
I_AGREE_TO_NAO_MESHES_LICENSE=1 I_AGREE_TO_PEPPER_MESHES_LICENSE=1 colcon build --symlink-install
source install/setup.bash
```
## Part 2: NAO Network Configuration and Connection
### 2.1 Verify NAO Network Connectivity
```bash
# Test basic connectivity
ping -c 4 nao.local
# Test NAOqi service port
timeout 5 bash -c 'echo "test" | nc nao.local 9559' && echo "NAOqi port is open!" || echo "NAOqi port might be closed"
# Alternative test
telnet nao.local 9559
# Press Ctrl+C to exit if connection succeeds
```
### 2.2 Find NAO Credentials
The default NAO credentials are typically:
- Username: `nao`
- Password: Usually `nao`, but can be custom
**Common passwords to try:**
- `nao` (default)
- Institution name (e.g., `bucknell`)
- Custom password set by administrator
## Part 3: HRIStudio Database Integration
### 3.1 Update Database Schema
The HRIStudio database needs to include NAO6 robot definitions and plugins.
**Update robots in seed script** (`scripts/seed-dev.ts`):
```typescript
const robots = [
{
name: "TurtleBot3 Burger",
manufacturer: "ROBOTIS",
model: "TurtleBot3 Burger",
description: "A compact, affordable, programmable, ROS2-based mobile robot for education and research",
capabilities: ["differential_drive", "lidar", "imu", "odometry"],
communicationProtocol: "ros2" as const,
},
{
name: "NAO Humanoid Robot",
manufacturer: "SoftBank Robotics",
model: "NAO V6",
description: "Humanoid robot designed for education, research, and social interaction with ROS2 integration",
capabilities: [
"speech",
"vision",
"walking",
"gestures",
"joint_control",
"touch_sensors",
"sonar_sensors",
"camera_feed",
"imu",
"odometry",
],
communicationProtocol: "ros2" as const,
},
];
```
### 3.2 Create NAO6 Plugin Repository
Create local plugin repository at `public/nao6-plugins/`:
**Repository metadata** (`public/nao6-plugins/repository.json`):
```json
{
"name": "NAO6 ROS2 Integration Repository",
"description": "Official NAO6 robot plugins for ROS2-based Human-Robot Interaction experiments",
"version": "1.0.0",
"author": {
"name": "HRIStudio Team",
"email": "support@hristudio.com"
},
"trust": "official",
"license": "MIT",
"robots": [
{
"name": "NAO6",
"manufacturer": "SoftBank Robotics",
"model": "NAO V6",
"communicationProtocol": "ros2"
}
],
"ros2": {
"distro": "humble",
"packages": ["naoqi_driver2", "naoqi_bridge_msgs", "rosbridge_suite"],
"bridge": {
"protocol": "websocket",
"defaultPort": 9090
}
}
}
```
### 3.3 Seed Database
```bash
# Start database
sudo docker compose up -d
# Push schema changes
bun db:push
# Seed with NAO6 data
bun db:seed
```
## Part 4: Web Interface Integration
### 4.1 Create NAO Test Page
Create `src/app/(dashboard)/nao-test/page.tsx` with the robot control interface.
**Key points:**
- Use `~` import alias (not `@`)
- Connect to WebSocket at `ws://YOUR_IP:9090`
- Use correct ROS topic names (without `/naoqi_driver` prefix for control topics)
**Important Topic Mapping:**
- Speech: `/speech` (not `/naoqi_driver/speech`)
- Movement: `/cmd_vel` (not `/naoqi_driver/cmd_vel`)
- Joint control: `/joint_angles` (not `/naoqi_driver/joint_angles`)
- Sensor data: `/naoqi_driver/joint_states`, `/naoqi_driver/bumper`, etc.
## Part 5: Critical Troubleshooting
### 5.1 Robot Not Responding to Commands
**Symptom:** ROS topics receive commands but robot doesn't move.
**Root Cause:** NAO robots start in "safe mode" with loose joints and need to be "awakened."
**Solution - SSH Wake-Up Method:**
```bash
# Install sshpass for automated SSH
sudo apt install sshpass -y
# Wake up robot via SSH
sshpass -p "YOUR_NAO_PASSWORD" ssh nao@nao.local "python2 -c \"
import sys
sys.path.append('/opt/aldebaran/lib/python2.7/site-packages')
import naoqi
try:
motion = naoqi.ALProxy('ALMotion', '127.0.0.1', 9559)
print 'Connected to ALMotion'
print 'Current stiffness:', motion.getStiffnesses('Body')[0] if motion.getStiffnesses('Body') else 'No stiffness data'
print 'Waking up robot...'
motion.wakeUp()
print 'Robot should now be awake!'
except Exception as e:
print 'Error:', str(e)
\""
```
**Alternative Physical Method:**
1. Press and hold the chest button for 3 seconds
2. Wait for the robot to stiffen and stand up
3. Robot should now respond to movement commands
### 5.2 Connection Issues
**Port Already in Use:**
```bash
# Kill existing processes
sudo fuser -k 9090/tcp
pkill -f "rosbridge\|naoqi\|ros2"
```
**Database Connection Issues:**
```bash
# Check Docker containers
sudo docker ps
# Restart database
sudo docker compose down
sudo docker compose up -d
```
### 5.3 Import Alias Issues
**Error:** Module import failures in React components.
**Solution:** Use `~` import alias consistently:
```typescript
import { Button } from "~/components/ui/button";
// NOT: import { Button } from "@/components/ui/button";
```
## Part 6: Verification and Testing
### 6.1 System Verification Script
Create verification script to test all components:
```bash
#!/bin/bash
echo "=== NAO6 HRIStudio Integration Verification ==="
# Test 1: ROS2 Setup
echo "✓ ROS2 Humble: $ROS_DISTRO"
# Test 2: NAO Connectivity
ping -c 1 nao.local && echo "✓ NAO reachable" || echo "✗ NAO not reachable"
# Test 3: Workspace Build
[ -f ~/naoqi_ros2_ws/install/setup.bash ] && echo "✓ Workspace built" || echo "✗ Workspace not built"
# Test 4: Database Running
sudo docker ps | grep -q postgres && echo "✓ Database running" || echo "✗ Database not running"
echo "=== Verification Complete ==="
```
### 6.2 End-to-End Test Procedure
**Terminal 1: Start ROS Integration**
```bash
cd ~/naoqi_ros2_ws
source install/setup.bash
ros2 launch install/nao_launch/share/nao_launch/launch/nao6_hristudio.launch.py nao_ip:=nao.local password:=YOUR_PASSWORD
```
**Terminal 2: Wake Up Robot**
```bash
# Use SSH method from Section 5.1
sshpass -p "YOUR_PASSWORD" ssh nao@nao.local "python2 -c \"...\""
```
**Terminal 3: Start HRIStudio**
```bash
cd /path/to/hristudio
bun dev
```
**Web Interface Test:**
1. Go to `http://localhost:3000/nao-test`
2. Click "Connect" - should show "Connected"
3. Test speech: Enter text and click "Say Text"
4. Test movement: Use arrow buttons to make robot walk
5. Test head control: Move sliders to control head position
6. Monitor sensor data in tabs
### 6.3 Command-Line Testing
**Test Speech:**
```bash
ros2 topic pub --once /speech std_msgs/String "data: 'Hello from ROS2'"
```
**Test Movement:**
```bash
ros2 topic pub --times 3 /cmd_vel geometry_msgs/msg/Twist '{linear: {x: 0.05, y: 0.0, z: 0.0}, angular: {x: 0.0, y: 0.0, z: 0.0}}'
```
**Test Head Movement:**
```bash
ros2 topic pub --once /joint_angles naoqi_bridge_msgs/msg/JointAnglesWithSpeed '{joint_names: ["HeadYaw"], joint_angles: [0.5], speed: 0.3}'
```
## Part 7: Production Deployment
### 7.1 Launch Script Creation
Create production-ready launch script (`scripts/launch_nao6.sh`):
```bash
#!/bin/bash
# NAO6 HRIStudio Integration Launch Script
set -e
# Configuration
NAO_IP="${NAO_IP:-nao.local}"
NAO_PASSWORD="${NAO_PASSWORD:-nao}"
BRIDGE_PORT="${BRIDGE_PORT:-9090}"
# Function to wake up robot
wake_up_robot() {
echo "Waking up NAO robot..."
sshpass -p "$NAO_PASSWORD" ssh nao@$NAO_IP "python2 -c \"
import sys
sys.path.append('/opt/aldebaran/lib/python2.7/site-packages')
import naoqi
motion = naoqi.ALProxy('ALMotion', '127.0.0.1', 9559)
motion.wakeUp()
print 'Robot awakened'
\""
}
# Main execution
echo "Starting NAO6 HRIStudio Integration"
echo "NAO IP: $NAO_IP"
echo "Bridge Port: $BRIDGE_PORT"
# Check connections
ping -c 1 $NAO_IP || { echo "Cannot reach NAO"; exit 1; }
# Start ROS integration
cd ~/naoqi_ros2_ws
source install/setup.bash
# Wake up robot in background
wake_up_robot &
# Launch ROS system
exec ros2 launch install/nao_launch/share/nao_launch/launch/nao6_hristudio.launch.py \
nao_ip:="$NAO_IP" \
password:="$NAO_PASSWORD" \
bridge_port:="$BRIDGE_PORT"
```
### 7.2 Service Integration (Optional)
Create systemd service for automatic startup:
```ini
[Unit]
Description=NAO6 HRIStudio Integration
After=network.target
[Service]
Type=simple
User=your_user
Environment=NAO_IP=nao.local
Environment=NAO_PASSWORD=your_password
ExecStart=/path/to/launch_nao6.sh
Restart=always
[Install]
WantedBy=multi-user.target
```
## Part 8: Safety and Best Practices
### 8.1 Safety Guidelines
- **Always keep emergency stop accessible** in the web interface
- **Start with small movements and low speeds** when testing
- **Monitor robot battery level** during long sessions
- **Ensure clear space around robot** before movement commands
- **Never leave robot unattended** during operation
### 8.2 Performance Optimization
**Network Optimization:**
```bash
# Increase network buffer sizes for camera data
sudo sysctl -w net.core.rmem_max=26214400
sudo sysctl -w net.core.rmem_default=26214400
```
**ROS2 Optimization:**
```bash
# Use optimized RMW implementation
export RMW_IMPLEMENTATION=rmw_cyclonedx_cpp
```
### 8.3 Troubleshooting Checklist
**Before Starting:**
- [ ] NAO robot powered on and connected to network
- [ ] ROS2 Humble installed and sourced
- [ ] NAO workspace built successfully
- [ ] Database running (Docker container)
- [ ] Correct NAO password known
**During Operation:**
- [ ] rosbridge WebSocket server running on port 9090
- [ ] NAO robot in standing position (not crouching)
- [ ] Robot joints stiffened (not loose)
- [ ] HRIStudio web interface connected to ROS bridge
**If Commands Not Working:**
1. Check robot is awake and standing
2. Verify topic names in web interface match ROS topics
3. Test commands from command line first
4. Check rosbridge logs for errors
## Part 9: Future Enhancements
### 9.1 Advanced Features
- **Multi-camera streaming** for experiment recording
- **Advanced gesture recognition** through touch sensors
- **Autonomous behavior integration** with navigation
- **Multi-robot coordination** for group interaction studies
### 9.2 Plugin Development
The NAO6 integration supports the HRIStudio plugin system for adding custom behaviors and extending robot capabilities.
## Conclusion
This guide provides a complete integration of NAO6 robots with HRIStudio, enabling researchers to conduct sophisticated Human-Robot Interaction experiments with full robot control, real-time data collection, and web-based interfaces.
The key insight discovered during implementation is that NAO robots require explicit "wake-up" commands to enable motor control, which must be performed before any movement commands will be executed.
**Support Resources:**
- NAO Documentation: https://developer.softbankrobotics.com/nao6
- naoqi_driver2: https://github.com/ros-naoqi/naoqi_driver2
- ROS2 Humble: https://docs.ros.org/en/humble/
- HRIStudio Documentation: See `docs/` folder
---
**Integration Status: Production Ready ✅**
*Last Updated: January 2025*
*Tested With: NAO V6.0 / NAOqi 2.8.7.4 / ROS2 Humble / HRIStudio v1.0*

View File

@@ -1,233 +0,0 @@
# NAO6 ROS2 Integration Summary for HRIStudio
## Overview
This document summarizes the complete NAO6 ROS2 integration that has been implemented for HRIStudio, providing researchers with full access to NAO6 capabilities through the visual experiment designer and real-time wizard interface.
## What's Been Implemented
### 1. NAO6 ROS2 Plugin (`nao6-ros2.json`)
A comprehensive robot plugin that exposes all NAO6 capabilities through standard ROS2 topics:
**Location**: `robot-plugins/plugins/nao6-ros2.json`
**Key Features**:
- Full ROS2 integration via `naoqi_driver2`
- 10 robot actions across movement, interaction, and sensors
- Proper HRIStudio plugin schema compliance
- Safety limits and parameter validation
- Transform functions for message conversion
### 2. Robot Actions Available
#### Movement Actions
- **Walk with Velocity**: Control linear/angular walking velocities
- **Stop Walking**: Emergency stop for immediate movement cessation
- **Set Joint Angle**: Control individual joint positions (25 DOF)
- **Turn Head**: Dedicated head orientation control
#### Interaction Actions
- **Say Text**: Text-to-speech via ROS2 `/speech` topic
#### Sensor Actions
- **Get Camera Image**: Capture from front or bottom cameras
- **Get Joint States**: Read current joint positions and velocities
- **Get IMU Data**: Inertial measurement from torso sensor
- **Get Bumper Status**: Foot contact sensor readings
- **Get Touch Sensors**: Hand and head tactile sensor states
- **Get Sonar Range**: Ultrasonic distance measurements
- **Get Robot Info**: General robot status and information
### 3. ROS2 Topic Mapping
The plugin maps to these standard NAO6 ROS2 topics:
```
/cmd_vel → Robot velocity commands (Twist)
/odom → Odometry data (Odometry)
/joint_states → Joint positions/velocities (JointState)
/joint_angles → NAO-specific joint control (JointAnglesWithSpeed)
/camera/front/image_raw → Front camera stream (Image)
/camera/bottom/image_raw → Bottom camera stream (Image)
/imu/torso → Inertial measurement (Imu)
/speech → Text-to-speech commands (String)
/bumper → Foot bumper sensors (Bumper)
/hand_touch → Hand touch sensors (HandTouch)
/head_touch → Head touch sensors (HeadTouch)
/sonar/left → Left ultrasonic sensor (Range)
/sonar/right → Right ultrasonic sensor (Range)
/info → Robot information (RobotInfo)
```
### 4. Transform Functions (`nao6-transforms.ts`)
**Location**: `src/lib/nao6-transforms.ts`
Comprehensive message conversion functions:
- Parameter validation and safety limits
- ROS2 message format compliance
- Joint limit enforcement (25 DOF with proper ranges)
- Velocity clamping for safe operation
- Helper functions for UI integration
### 5. Setup Documentation (`nao6-ros2-setup.md`)
**Location**: `docs/nao6-ros2-setup.md`
Complete setup guide covering:
- ROS2 Humble installation on Ubuntu 22.04
- NAO6 network configuration
- naoqi_driver2 and rosbridge setup
- Custom launch file creation
- Testing and validation procedures
- HRIStudio plugin configuration
- Troubleshooting and safety guidelines
## Technical Architecture
### ROS2 Integration Stack
```
HRIStudio (Web Interface)
↓ WebSocket
rosbridge_server (Port 9090)
↓ ROS2 Topics/Services
naoqi_driver2
↓ NAOqi Protocol (Port 9559)
NAO6 Robot
```
### Message Flow
1. **Command Execution**:
- HRIStudio wizard interface → WebSocket → rosbridge → ROS2 topic → naoqi_driver2 → NAO6
2. **Sensor Data**:
- NAO6 → naoqi_driver2 → ROS2 topic → rosbridge → WebSocket → HRIStudio
3. **Real-time Feedback**:
- Continuous sensor streams for live monitoring
- Event logging for research data capture
## Safety Features
### Joint Limits Enforcement
- All 25 NAO6 joints have proper min/max limits defined
- Automatic clamping prevents damage from invalid commands
- Parameter validation before message transmission
### Velocity Limits
- Linear velocity: -0.55 to 0.55 m/s
- Angular velocity: -2.0 to 2.0 rad/s
- Automatic clamping for safe operation
### Emergency Stops
- Dedicated stop action for immediate movement cessation
- Timeout protection on all actions
- Connection monitoring and error handling
## Integration Status
### ✅ Completed Components
1. **Plugin Definition**: Full NAO6 plugin with proper schema
2. **Action Library**: 10 comprehensive robot actions
3. **Transform Functions**: Complete message conversion system
4. **Documentation**: Setup guide and integration instructions
5. **Safety Systems**: Joint limits, velocity clamping, emergency stops
6. **Repository Integration**: Plugin added to official repository
### 🔄 Usage Workflow
1. **Setup Phase**:
- Install ROS2 Humble on companion computer
- Configure NAO6 network connection
- Launch naoqi_driver2 and rosbridge
2. **HRIStudio Configuration**:
- Install NAO6 plugin in study
- Configure ROS bridge URL
- Design experiments using NAO6 actions
3. **Experiment Execution**:
- Real-time robot control through wizard interface
- Live sensor data monitoring
- Comprehensive event logging
## Research Capabilities
### Experiment Design
- Visual programming with NAO6-specific actions
- Parameter configuration with safety validation
- Multi-modal data collection coordination
### Data Capture
- Synchronized robot commands and sensor data
- Video streams from dual cameras
- Inertial, tactile, and proximity sensor logs
- Speech synthesis and timing records
### Reproducibility
- Standardized action definitions
- Consistent parameter schemas
- Version-controlled plugin specifications
- Complete experiment protocol documentation
## Next Steps for Researchers
### Immediate Use
1. Follow setup guide in `docs/nao6-ros2-setup.md`
2. Install NAO6 plugin in HRIStudio study
3. Create experiments using available actions
4. Run trials with real-time robot control
### Advanced Integration
1. **Custom Actions**: Extend plugin with study-specific behaviors
2. **Multi-Robot**: Scale to multiple NAO6 robots
3. **Navigation**: Add SLAM and path planning capabilities
4. **Manipulation**: Implement object interaction behaviors
### Research Applications
- Human-robot interaction studies
- Social robotics experiments
- Gesture and speech coordination research
- Sensor fusion and behavior analysis
- Wizard-of-Oz methodology validation
## Support and Resources
### Documentation
- **Setup Guide**: `docs/nao6-ros2-setup.md`
- **Plugin Schema**: `robot-plugins/docs/schema.md`
- **ROS2 Integration**: `docs/ros2-integration.md`
- **Transform Functions**: `src/lib/nao6-transforms.ts`
### External Resources
- **NAO6 Documentation**: https://developer.softbankrobotics.com/nao6
- **naoqi_driver2**: https://github.com/ros-naoqi/naoqi_driver2
- **ROS2 Humble**: https://docs.ros.org/en/humble/
- **rosbridge**: http://wiki.ros.org/rosbridge_suite
### Technical Support
- **HRIStudio Issues**: GitHub repository
- **ROS2 Community**: ROS Discourse forum
- **NAO6 Support**: SoftBank Robotics developer portal
## Conclusion
The NAO6 ROS2 integration provides researchers with a complete, production-ready system for conducting Human-Robot Interaction studies. The integration leverages:
- **Standard ROS2 protocols** for reliable communication
- **Comprehensive safety systems** for secure operation
- **Visual experiment design** for accessible research tools
- **Real-time control interfaces** for dynamic experiment execution
- **Complete data capture** for rigorous analysis
This implementation enables researchers to focus on their studies rather than technical integration, while maintaining the flexibility and control needed for cutting-edge HRI research.
---
**Status**: ✅ Production Ready
**Last Updated**: December 2024
**Compatibility**: HRIStudio v1.0+, ROS2 Humble, NAO6 with NAOqi 2.8.7+

157
docs/nao6-quick-reference.md Normal file → Executable file
View File

@@ -1,155 +1,114 @@
# NAO6 HRIStudio Quick Reference # NAO6 Quick Reference
**Essential commands for NAO6 robot integration with HRIStudio** Essential commands for using NAO6 robots with HRIStudio.
## 🚀 Quick Start (5 Steps) ## Quick Start
### 1. Start ROS Integration ### 1. Start NAO Integration
```bash ```bash
cd ~/naoqi_ros2_ws cd ~/naoqi_ros2_ws
source install/setup.bash source install/setup.bash
ros2 launch install/nao_launch/share/nao_launch/launch/nao6_hristudio.launch.py nao_ip:=nao.local password:=robolab ros2 launch nao_launch nao6_hristudio.launch.py nao_ip:=nao.local password:=robolab
``` ```
### 2. Wake Up Robot (CRITICAL!) ### 2. Wake Robot
Press chest button for 3 seconds, or use:
```bash ```bash
sshpass -p "robolab" ssh nao@nao.local "python2 -c \" # Via SSH (institution-specific password)
import sys ssh nao@nao.local
sys.path.append('/opt/aldebaran/lib/python2.7/site-packages') # Then run wake-up command (see integration repo docs)
import naoqi
motion = naoqi.ALProxy('ALMotion', '127.0.0.1', 9559)
motion.wakeUp()
print 'Robot awakened'
\""
``` ```
### 3. Start HRIStudio ### 3. Start HRIStudio
```bash ```bash
cd /home/robolab/Documents/Projects/hristudio cd ~/Documents/Projects/hristudio
bun dev bun dev
``` ```
### 4. Access Test Interface ### 4. Test Connection
- URL: `http://localhost:3000/nao-test` - Open: `http://localhost:3000/nao-test`
- Login: `sean@soconnor.dev` / `password123` - Click "Connect"
- Test robot commands
### 5. Test Robot ## Essential Commands
- Click "Connect" to WebSocket
- Try speech: "Hello from HRIStudio!"
- Use movement buttons to control robot
## 🛠️ Essential Commands ### Test Connectivity
### Connection Testing
```bash ```bash
# Test NAO connectivity ping nao.local # Test network
ping nao.local ros2 topic list | grep naoqi # Check ROS topics
# Test NAOqi service
telnet nao.local 9559
# Check ROS topics
ros2 topic list | grep naoqi
``` ```
### Manual Robot Control ### Manual Control
```bash ```bash
# Speech test # Speech
ros2 topic pub --once /speech std_msgs/String "data: 'Hello world'" ros2 topic pub --once /speech std_msgs/String "data: 'Hello world'"
# Movement test (robot must be awake!) # Movement (robot must be awake!)
ros2 topic pub --times 3 /cmd_vel geometry_msgs/msg/Twist '{linear: {x: 0.05, y: 0.0, z: 0.0}, angular: {x: 0.0, y: 0.0, z: 0.0}}' ros2 topic pub --once /cmd_vel geometry_msgs/msg/Twist '{linear: {x: 0.1}}'
# Head movement test # Stop
ros2 topic pub --once /joint_angles naoqi_bridge_msgs/msg/JointAnglesWithSpeed '{joint_names: ["HeadYaw"], joint_angles: [0.5], speed: 0.3}' ros2 topic pub --once /cmd_vel geometry_msgs/msg/Twist '{linear: {x: 0.0}}'
# Stop all movement
ros2 topic pub --once /cmd_vel geometry_msgs/msg/Twist '{linear: {x: 0.0, y: 0.0, z: 0.0}, angular: {x: 0.0, y: 0.0, z: 0.0}}'
``` ```
### Status Checks ### Monitor Status
```bash ```bash
# Check robot info ros2 topic echo /naoqi_driver/battery # Battery level
ros2 service call /naoqi_driver/get_robot_config naoqi_bridge_msgs/srv/GetRobotInfo ros2 topic echo /naoqi_driver/joint_states # Joint positions
```
# Monitor joint states ## Troubleshooting
ros2 topic echo /naoqi_driver/joint_states --once
# Check ROS nodes **Robot not moving:** Press chest button for 3 seconds to wake up
ros2 node list
# Check WebSocket connection **WebSocket fails:** Check rosbridge is running on port 9090
```bash
ss -an | grep 9090 ss -an | grep 9090
``` ```
## 🔧 Troubleshooting **Connection lost:** Restart rosbridge
### Robot Not Moving
**Problem:** Commands sent but robot doesn't move
**Solution:** Robot needs to be awakened first
```bash ```bash
# Wake up via SSH (see step 2 above) pkill -f rosbridge
# OR press chest button for 3 seconds ros2 run rosbridge_server rosbridge_websocket
``` ```
### Connection Issues ## ROS Topics
```bash
# Kill existing processes
sudo fuser -k 9090/tcp
pkill -f "rosbridge\|naoqi\|ros2"
# Restart database **Commands (Input):**
sudo docker compose down && sudo docker compose up -d
```
### Import Errors in Web Interface
**Problem:** React component import failures
**Solution:** Use `~` import alias consistently:
```typescript
import { Button } from "~/components/ui/button";
// NOT: import { Button } from "@/components/ui/button";
```
## 📊 Key Topics
### Input Topics (Robot Control)
- `/speech` - Text-to-speech - `/speech` - Text-to-speech
- `/cmd_vel` - Movement commands - `/cmd_vel` - Movement
- `/joint_angles` - Joint position control - `/joint_angles` - Joint control
### Output Topics (Sensor Data) **Sensors (Output):**
- `/naoqi_driver/joint_states` - Joint positions/velocities - `/naoqi_driver/joint_states` - Joint data
- `/naoqi_driver/battery` - Battery level
- `/naoqi_driver/bumper` - Foot sensors - `/naoqi_driver/bumper` - Foot sensors
- `/naoqi_driver/hand_touch` - Hand touch sensors - `/naoqi_driver/sonar/*` - Distance sensors
- `/naoqi_driver/head_touch` - Head touch sensors - `/naoqi_driver/camera/*` - Camera feeds
- `/naoqi_driver/sonar/left` - Left ultrasonic sensor
- `/naoqi_driver/sonar/right` - Right ultrasonic sensor
- `/naoqi_driver/camera/front/image_raw` - Front camera
- `/naoqi_driver/camera/bottom/image_raw` - Bottom camera
## 🔗 WebSocket Integration ## WebSocket
**ROS Bridge URL:** `ws://134.82.159.25:9090` **URL:** `ws://localhost:9090`
**Message Format:** **Example message:**
```javascript ```javascript
// Publish command
{ {
"op": "publish", "op": "publish",
"topic": "/speech", "topic": "/speech",
"type": "std_msgs/String", "type": "std_msgs/String",
"msg": {"data": "Hello world"} "msg": {"data": "Hello world"}
} }
// Subscribe to topic
{
"op": "subscribe",
"topic": "/naoqi_driver/joint_states",
"type": "sensor_msgs/JointState"
}
``` ```
## 🎯 Common Use Cases ## More Information
See **[nao6-hristudio-integration](../../nao6-hristudio-integration/)** repository for:
- Complete installation guide
- Detailed usage instructions
- Full troubleshooting guide
- Plugin definitions
- Launch file configurations
## Common Use Cases
### Make Robot Speak ### Make Robot Speak
```bash ```bash

View File

@@ -1,376 +0,0 @@
# NAO6 ROS2 Setup Guide for HRIStudio
This guide walks you through setting up your NAO6 robot with ROS2 integration for use with HRIStudio's experiment platform.
> **📋 For Complete Integration Guide:** See [NAO6 Complete Integration Guide](./nao6-integration-complete-guide.md) for comprehensive setup, troubleshooting, and production deployment instructions.
**⚠️ Critical Note:** NAO robots must be "awakened" (motors stiffened and standing) before movement commands will work. See the troubleshooting section below.
## Prerequisites
- NAO6 robot with NAOqi OS 2.8.7+
- Ubuntu 22.04.5 LTS computer (x86_64)
- Network connectivity between computer and NAO6
- Administrative access to both systems
## Overview
The integration uses the `naoqi_driver2` package to bridge NAOqi with ROS2, exposing all robot capabilities through standard ROS2 topics and services. HRIStudio connects via WebSocket using `rosbridge_server`.
## Step 1: NAO6 Network Configuration
1. **Power on your NAO6** and wait for boot completion
2. **Connect NAO6 to your network**:
- Press chest button to get IP address
- Or use Choregraphe to configure WiFi
3. **Verify connectivity**:
```bash
ping nao.local # or robot IP address
```
## Step 2: ROS2 Humble Installation
Install ROS2 Humble on your Ubuntu 22.04 system:
```bash
# Update system
sudo apt update && sudo apt upgrade -y
# Install ROS2 Humble
sudo apt install software-properties-common
sudo add-apt-repository universe
sudo apt update && sudo apt install curl -y
sudo curl -sSL https://raw.githubusercontent.com/ros/rosdistro/master/ros.asc | sudo apt-key add -
sudo sh -c 'echo "deb http://packages.ros.org/ros2/ubuntu $(lsb_release -cs) main" > /etc/apt/sources.list.d/ros2-latest.list'
sudo apt update
sudo apt install ros-humble-desktop
sudo apt install ros-dev-tools
# Source ROS2
echo "source /opt/ros/humble/setup.bash" >> ~/.bashrc
source ~/.bashrc
```
## Step 3: Install NAO ROS2 Packages
Install the required ROS2 packages for NAO6 integration:
```bash
# Install naoqi_driver2 and dependencies
sudo apt install ros-humble-naoqi-driver2
sudo apt install ros-humble-naoqi-bridge-msgs
sudo apt install ros-humble-geometry-msgs
sudo apt install ros-humble-sensor-msgs
sudo apt install ros-humble-nav-msgs
sudo apt install ros-humble-std-msgs
# Install rosbridge for HRIStudio communication
sudo apt install ros-humble-rosbridge-suite
# Install additional useful packages
sudo apt install ros-humble-rqt
sudo apt install ros-humble-rqt-common-plugins
```
## Step 4: Configure NAO Connection
Create a launch file for easy NAO6 connection:
```bash
# Create workspace
mkdir -p ~/nao_ws/src
cd ~/nao_ws
# Create launch file directory
mkdir -p src/nao_launch/launch
# Create the launch file
cat > src/nao_launch/launch/nao6_hristudio.launch.py << 'EOF'
from launch import LaunchDescription
from launch.actions import DeclareLaunchArgument, IncludeLaunchDescription
from launch.substitutions import LaunchConfiguration
from launch_ros.actions import Node
from launch.launch_description_sources import PythonLaunchDescriptionSource
from ament_index_python.packages import get_package_share_directory
import os
def generate_launch_description():
return LaunchDescription([
# NAO IP configuration
DeclareLaunchArgument('nao_ip', default_value='nao.local'),
DeclareLaunchArgument('nao_port', default_value='9559'),
DeclareLaunchArgument('bridge_port', default_value='9090'),
# NAOqi Driver
Node(
package='naoqi_driver2',
executable='naoqi_driver',
name='naoqi_driver',
parameters=[{
'nao_ip': LaunchConfiguration('nao_ip'),
'nao_port': LaunchConfiguration('nao_port'),
'publish_joint_states': True,
'publish_odometry': True,
'publish_camera': True,
'publish_sensors': True,
'joint_states_frequency': 30.0,
'odom_frequency': 30.0,
'camera_frequency': 15.0,
'sensor_frequency': 10.0
}],
output='screen'
),
# Rosbridge WebSocket Server
Node(
package='rosbridge_server',
executable='rosbridge_websocket',
name='rosbridge_websocket',
parameters=[{
'port': LaunchConfiguration('bridge_port'),
'address': '0.0.0.0',
'authenticate': False,
'fragment_timeout': 600,
'delay_between_messages': 0,
'max_message_size': 10000000
}],
output='screen'
)
])
EOF
# Create package.xml
cat > src/nao_launch/package.xml << 'EOF'
<?xml version="1.0"?>
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypeid="pf3"?>
<package format="3">
<name>nao_launch</name>
<version>1.0.0</version>
<description>Launch files for NAO6 HRIStudio integration</description>
<maintainer email="you@example.com">Your Name</maintainer>
<license>MIT</license>
<buildtool_depend>ament_cmake</buildtool_depend>
<exec_depend>launch</exec_depend>
<exec_depend>launch_ros</exec_depend>
<exec_depend>naoqi_driver2</exec_depend>
<exec_depend>rosbridge_server</exec_depend>
</package>
EOF
# Create CMakeLists.txt
cat > src/nao_launch/CMakeLists.txt << 'EOF'
cmake_minimum_required(VERSION 3.8)
project(nao_launch)
find_package(ament_cmake REQUIRED)
install(DIRECTORY launch/
DESTINATION share/${PROJECT_NAME}/launch/
)
ament_package()
EOF
# Build the workspace
colcon build
source install/setup.bash
```
## Step 5: Test NAO Connection
Start the NAO6 ROS2 integration:
```bash
cd ~/nao_ws
source install/setup.bash
# Launch with your NAO's IP address
ros2 launch nao_launch nao6_hristudio.launch.py nao_ip:=YOUR_NAO_IP
```
Replace `YOUR_NAO_IP` with your NAO's actual IP address (e.g., `192.168.1.100`).
## Step 6: Verify ROS2 Topics
In a new terminal, verify that NAO topics are publishing:
```bash
source /opt/ros/humble/setup.bash
# List all topics
ros2 topic list
# You should see these NAO6 topics:
# /cmd_vel - Robot velocity commands
# /odom - Odometry data
# /joint_states - Joint positions and velocities
# /joint_angles - NAO-specific joint control
# /camera/front/image_raw - Front camera
# /camera/bottom/image_raw - Bottom camera
# /imu/torso - Inertial measurement unit
# /bumper - Foot bumper sensors
# /hand_touch - Hand tactile sensors
# /head_touch - Head tactile sensors
# /sonar/left - Left ultrasonic sensor
# /sonar/right - Right ultrasonic sensor
# /info - Robot information
# Test specific topics
ros2 topic echo /joint_states --once
ros2 topic echo /odom --once
ros2 topic echo /info --once
```
## Step 7: Test Robot Control
Test basic robot control:
```bash
# Make NAO say something
ros2 topic pub /speech std_msgs/msg/String "data: 'Hello from ROS2!'" --once
# Move head (be careful with joint limits)
ros2 topic pub /joint_angles naoqi_bridge_msgs/msg/JointAnglesWithSpeed \
"joint_names: ['HeadYaw']
joint_angles: [0.5]
speed: 0.3" --once
# Basic walking command (very small movement)
ros2 topic pub /cmd_vel geometry_msgs/msg/Twist \
"linear: {x: 0.1, y: 0.0, z: 0.0}
angular: {x: 0.0, y: 0.0, z: 0.0}" --once
# Stop movement
ros2 topic pub /cmd_vel geometry_msgs/msg/Twist \
"linear: {x: 0.0, y: 0.0, z: 0.0}
angular: {x: 0.0, y: 0.0, z: 0.0}" --once
```
## Step 8: Configure HRIStudio
1. **Start HRIStudio** with your development setup
2. **Add NAO6 Plugin Repository**:
- Go to Admin → Plugin Repositories
- Add the HRIStudio official repository if not already present
- Sync to get the latest plugins including `nao6-ros2`
3. **Install NAO6 Plugin**:
- In your study, go to Plugins
- Install the "NAO6 Robot (ROS2 Integration)" plugin
- Configure the ROS bridge URL: `ws://YOUR_COMPUTER_IP:9090`
4. **Create Experiment**:
- Use the experiment designer
- Add NAO6 actions from the robot blocks section
- Configure parameters for each action
5. **Run Trial**:
- Ensure your NAO6 ROS2 system is running
- Start a trial in HRIStudio
- Control the robot through the wizard interface
## Available Robot Actions
Your NAO6 plugin provides these actions for experiments:
### Movement Actions
- **Walk with Velocity**: Control linear/angular velocity
- **Stop Walking**: Emergency stop
- **Set Joint Angle**: Control individual joints
- **Turn Head**: Head orientation control
### Interaction Actions
- **Say Text**: Text-to-speech via ROS2
### Sensor Actions
- **Get Camera Image**: Capture from front/bottom cameras
- **Get Joint States**: Read all joint positions
- **Get IMU Data**: Inertial measurement data
- **Get Bumper Status**: Foot contact sensors
- **Get Touch Sensors**: Hand/head touch detection
- **Get Sonar Range**: Ultrasonic distance sensors
- **Get Robot Info**: General robot status
## Troubleshooting
### NAO Connection Issues
```bash
# Check NAO network connectivity
ping nao.local
# Check NAOqi service
telnet nao.local 9559
# Restart NAOqi on NAO
# (Use robot's web interface or Choregraphe)
```
### ROS2 Issues
```bash
# Check if naoqi_driver2 is running
ros2 node list | grep naoqi
# Check topic publication rates
ros2 topic hz /joint_states
# Restart the launch file
ros2 launch nao_launch nao6_hristudio.launch.py nao_ip:=YOUR_NAO_IP
```
### HRIStudio Connection Issues
```bash
# Verify rosbridge is running
netstat -an | grep 9090
# Check WebSocket connection
curl -i -N -H "Connection: Upgrade" \
-H "Upgrade: websocket" \
-H "Sec-WebSocket-Key: test" \
-H "Sec-WebSocket-Version: 13" \
http://localhost:9090
```
### Robot Safety
- Always keep emergency stop accessible
- Start with small movements and low speeds
- Monitor robot battery level
- Ensure clear space around robot
- Never leave robot unattended during operation
## Performance Optimization
### Network Optimization
```bash
# Increase network buffer sizes for camera data
sudo sysctl -w net.core.rmem_max=26214400
sudo sysctl -w net.core.rmem_default=26214400
```
### ROS2 Optimization
```bash
# Adjust QoS settings for better performance
export RMW_IMPLEMENTATION=rmw_cyclonedx_cpp
export CYCLONEDX_URI=file:///path/to/cyclonedx.xml
```
## Next Steps
1. **Experiment Design**: Create experiments using NAO6 actions
2. **Data Collection**: Use sensor actions for research data
3. **Custom Actions**: Extend the plugin with custom behaviors
4. **Multi-Robot**: Scale to multiple NAO6 robots
5. **Advanced Features**: Implement navigation, manipulation, etc.
## Support Resources
- **NAO Documentation**: https://developer.softbankrobotics.com/nao6
- **naoqi_driver2**: https://github.com/ros-naoqi/naoqi_driver2
- **ROS2 Humble**: https://docs.ros.org/en/humble/
- **HRIStudio Docs**: See `docs/` folder
- **Community**: HRIStudio Discord/Forum
---
**Success!** Your NAO6 is now ready for use with HRIStudio experiments. The robot's capabilities are fully accessible through the visual experiment designer and real-time wizard interface.

0
docs/paper.md Normal file → Executable file
View File

0
docs/plugin-system-implementation-guide.md Normal file → Executable file
View File

0
docs/project-overview.md Normal file → Executable file
View File

0
docs/project-status.md Normal file → Executable file
View File

0
docs/proposal.tex Normal file → Executable file
View File

0
docs/quick-reference.md Normal file → Executable file
View File

0
docs/roman-2025-talk.md Normal file → Executable file
View File

0
docs/ros2-integration.md Normal file → Executable file
View File

0
docs/ros2_naoqi.md Normal file → Executable file
View File

0
docs/route-consolidation-summary.md Normal file → Executable file
View File

0
docs/thesis-project-priorities.md Normal file → Executable file
View File

0
docs/trial-system-overhaul.md Normal file → Executable file
View File

0
docs/wizard-interface-final.md Normal file → Executable file
View File

0
docs/wizard-interface-guide.md Normal file → Executable file
View File

0
docs/wizard-interface-redesign.md Normal file → Executable file
View File

0
docs/wizard-interface-summary.md Normal file → Executable file
View File

0
docs/work_in_progress.md Normal file → Executable file
View File

0
drizzle.config.ts Normal file → Executable file
View File

0
eslint.config.js Normal file → Executable file
View File

0
middleware.ts Normal file → Executable file
View File

368
nao6_integration_README.md Normal file
View File

@@ -0,0 +1,368 @@
# NAO6 HRIStudio Integration
**Complete integration package for NAO6 humanoid robots with the HRIStudio research platform**
## 🎯 Overview
This repository contains all components needed to integrate NAO6 robots with HRIStudio for Human-Robot Interaction research. It provides production-ready ROS2 packages, web interface plugins, control scripts, and comprehensive documentation for seamless robot operation through the HRIStudio platform.
## 📦 Repository Structure
```
nao6-hristudio-integration/
├── README.md # This file
├── launch/ # ROS2 launch configurations
│ ├── nao6_production.launch.py # Production-optimized launch
│ └── nao6_hristudio_enhanced.launch.py # Enhanced with monitoring
├── scripts/ # Utilities and automation
│ ├── test_nao_topics.py # ROS topics simulator
│ ├── test_websocket.py # WebSocket bridge tester
│ ├── verify_nao6_bridge.sh # Integration verification
│ └── seed-nao6-plugin.ts # Database seeding for HRIStudio
├── plugins/ # HRIStudio plugin definitions
│ ├── repository.json # Plugin repository metadata
│ ├── nao6-ros2-enhanced.json # Complete NAO6 plugin
│ └── README.md # Plugin documentation
├── examples/ # Usage examples and tools
│ ├── nao_control.py # Command-line robot control
│ └── start_nao6_hristudio.sh # Automated startup script
└── docs/ # Documentation
├── NAO6_INTEGRATION_COMPLETE.md # Complete integration guide
├── INSTALLATION.md # Installation instructions
├── USAGE.md # Usage examples
└── TROUBLESHOOTING.md # Common issues and solutions
```
## 🚀 Quick Start
### Prerequisites
- **NAO6 Robot** with NAOqi 2.8.7.4+
- **Ubuntu 22.04** with ROS2 Humble
- **HRIStudio Platform** (web interface)
- **Network connectivity** between computer and robot
### 1. Clone Repository
```bash
git clone <repository-url> ~/nao6-hristudio-integration
cd ~/nao6-hristudio-integration
```
### 2. Install Dependencies
```bash
# Install ROS2 packages
sudo apt update
sudo apt install ros-humble-rosbridge-suite ros-humble-naoqi-driver
# Install Python dependencies
pip install websocket-client
```
### 3. Setup NAOqi ROS2 Workspace
```bash
# Build the enhanced nao_launch package
cd ~/naoqi_ros2_ws
colcon build --packages-select nao_launch
source install/setup.bash
```
### 4. Start Integration
```bash
# Option A: Use automated startup script
./examples/start_nao6_hristudio.sh --nao-ip nao.local --password robolab
# Option B: Manual launch
ros2 launch nao_launch nao6_production.launch.py nao_ip:=nao.local password:=robolab
```
### 5. Configure HRIStudio
```bash
# Seed NAO6 plugin into HRIStudio database
cd ~/Documents/Projects/hristudio
bun run ../nao6-hristudio-integration/scripts/seed-nao6-plugin.ts
# Start HRIStudio web interface
bun dev
```
### 6. Test Integration
- Open: `http://localhost:3000/nao-test`
- Login: `sean@soconnor.dev` / `password123`
- Click "Connect" to establish WebSocket connection
- Try robot commands and verify responses
## 🎮 Available Robot Actions
The NAO6 plugin provides 9 comprehensive actions for HRIStudio experiments:
### 🗣️ Communication
- **Speak Text** - Text-to-speech with volume/speed control
- **LED Control** - Visual feedback with colors and patterns
### 🚶 Movement & Posture
- **Move Robot** - Walking, turning with safety limits
- **Set Posture** - Stand, sit, crouch positions
- **Move Head** - Gaze control and attention direction
- **Perform Gesture** - Wave, point, applause, custom animations
### 📡 Sensors & Monitoring
- **Monitor Sensors** - Touch, bumper, sonar detection
- **Check Robot Status** - Battery, joints, system health
### 🛡️ Safety & System
- **Emergency Stop** - Immediate motion termination
- **Wake Up / Rest** - Power management
## 🔧 Command-Line Tools
### Robot Control
```bash
# Direct robot control
python3 examples/nao_control.py --ip nao.local wake
python3 examples/nao_control.py --ip nao.local speak "Hello world"
python3 examples/nao_control.py --ip nao.local move 0.1 0 0
python3 examples/nao_control.py --ip nao.local pose Stand
python3 examples/nao_control.py --ip nao.local emergency
```
### Integration Testing
```bash
# Verify all components
./scripts/verify_nao6_bridge.sh
# Test WebSocket connectivity
python3 scripts/test_websocket.py
# Simulate robot topics (without hardware)
python3 scripts/test_nao_topics.py
```
## 🌐 WebSocket Communication
### Connection Details
- **URL**: `ws://localhost:9090`
- **Protocol**: rosbridge v2.0
- **Format**: JSON messages
### Sample Messages
```javascript
// Speech command
{
"op": "publish",
"topic": "/speech",
"type": "std_msgs/String",
"msg": {"data": "Hello from HRIStudio!"}
}
// Movement command
{
"op": "publish",
"topic": "/cmd_vel",
"type": "geometry_msgs/Twist",
"msg": {
"linear": {"x": 0.1, "y": 0.0, "z": 0.0},
"angular": {"x": 0.0, "y": 0.0, "z": 0.0}
}
}
// Subscribe to sensors
{
"op": "subscribe",
"topic": "/naoqi_driver/joint_states",
"type": "sensor_msgs/JointState"
}
```
## 📋 Key Topics
### Input Topics (Robot Control)
- `/speech` - Text-to-speech commands
- `/cmd_vel` - Movement control
- `/joint_angles` - Joint positioning
- `/led_control` - Visual feedback
### Output Topics (Sensor Data)
- `/naoqi_driver/joint_states` - Joint positions/velocities
- `/naoqi_driver/bumper` - Foot sensors
- `/naoqi_driver/hand_touch` - Hand touch sensors
- `/naoqi_driver/head_touch` - Head touch sensors
- `/naoqi_driver/sonar/left` - Left ultrasonic sensor
- `/naoqi_driver/sonar/right` - Right ultrasonic sensor
- `/naoqi_driver/battery` - Battery level
## 🛡️ Safety Features
### Automated Safety
- **Velocity Limits** - Maximum speed constraints (0.2 m/s linear, 0.8 rad/s angular)
- **Emergency Stop** - Immediate motion termination
- **Battery Monitoring** - Low battery warnings
- **Fall Detection** - Automatic safety responses
- **Wake-up Management** - Proper robot state handling
### Manual Safety Controls
```bash
# Emergency stop via CLI
ros2 topic pub --once /cmd_vel geometry_msgs/msg/Twist '{linear: {x: 0.0, y: 0.0, z: 0.0}, angular: {x: 0.0, y: 0.0, z: 0.0}}'
# Emergency stop via script
python3 examples/nao_control.py --ip nao.local emergency
# Or use HRIStudio emergency stop action
```
## 🔍 Troubleshooting
### Common Issues
**Robot not responding to commands**
```bash
# Check robot is awake
python3 examples/nao_control.py --ip nao.local status
# Wake up robot
python3 examples/nao_control.py --ip nao.local wake
# OR press chest button for 3 seconds
```
**WebSocket connection failed**
```bash
# Check rosbridge is running
ros2 node list | grep rosbridge
# Restart integration
pkill -f rosbridge && pkill -f rosapi
ros2 launch nao_launch nao6_production.launch.py nao_ip:=nao.local
```
**Network connectivity issues**
```bash
# Test basic connectivity
ping nao.local
telnet nao.local 9559
# Check robot credentials
ssh nao@nao.local # Password: robolab (institution-specific)
```
For detailed troubleshooting, see [docs/TROUBLESHOOTING.md](docs/TROUBLESHOOTING.md)
## 📖 Documentation
### Complete Guides
- **[Installation Guide](docs/INSTALLATION.md)** - Detailed setup instructions
- **[Usage Guide](docs/USAGE.md)** - Examples and best practices
- **[Integration Complete](docs/NAO6_INTEGRATION_COMPLETE.md)** - Comprehensive overview
- **[Troubleshooting](docs/TROUBLESHOOTING.md)** - Problem resolution
### Quick References
- **Launch Files** - See `launch/` directory
- **Plugin Definitions** - See `plugins/` directory
- **Example Scripts** - See `examples/` directory
## 🎯 Research Applications
### Experiment Types
- **Social Interaction** - Gestures, speech, gaze studies
- **Human-Robot Collaboration** - Shared task experiments
- **Behavior Analysis** - Touch, proximity, response studies
- **Navigation Studies** - Movement and spatial interaction
- **Multimodal Interaction** - Combined speech, gesture, movement
### Data Capture
- **Synchronized Timestamps** - All robot actions and sensor events
- **Sensor Fusion** - Touch, vision, audio, movement data
- **Real-time Logging** - Comprehensive event capture
- **Export Capabilities** - Data analysis and visualization
## 🏆 Features & Benefits
### ✅ Production Ready
- **Tested Integration** - Verified with NAO V6.0 / NAOqi 2.8.7.4
- **Safety First** - Comprehensive safety monitoring
- **Performance Optimized** - Tuned for stable experiments
- **Error Handling** - Robust failure management
### ✅ Researcher Friendly
- **Web Interface** - No programming required for experiments
- **Visual Designer** - Drag-and-drop experiment creation
- **Real-time Control** - Live robot operation during trials
- **Multiple Roles** - Researcher, wizard, observer access
### ✅ Developer Friendly
- **Open Source** - MIT licensed components
- **Modular Design** - Extensible architecture
- **Comprehensive APIs** - ROS2 and WebSocket interfaces
- **Documentation** - Complete setup and usage guides
## 🚀 Getting Started Examples
### Basic Experiment Workflow
1. **Design** - Create experiment in HRIStudio visual designer
2. **Configure** - Set robot parameters and safety limits
3. **Execute** - Run trial with real-time robot control
4. **Analyze** - Review captured data and events
5. **Iterate** - Refine experiment based on results
### Sample Experiment: Greeting Interaction
```javascript
// HRIStudio experiment sequence
[
{"action": "nao_wake_rest", "parameters": {"action": "wake"}},
{"action": "nao_pose", "parameters": {"posture": "Stand"}},
{"action": "nao_speak", "parameters": {"text": "Hello! Welcome to our study."}},
{"action": "nao_gesture", "parameters": {"gesture": "wave"}},
{"action": "nao_sensor_monitor", "parameters": {"sensorType": "touch", "duration": 30}}
]
```
## 🤝 Contributing
### Development Setup
1. Fork this repository
2. Create feature branch: `git checkout -b feature-name`
3. Test with real NAO6 hardware
4. Submit pull request with documentation updates
### Guidelines
- Follow ROS2 conventions for launch files
- Test all changes with physical robot
- Update documentation for new features
- Ensure backward compatibility
## 📞 Support
### Resources
- **GitHub Issues** - Report bugs and request features
- **Documentation** - Complete guides in `docs/` folder
- **HRIStudio Platform** - Web interface documentation
### Requirements
- **NAO6 Robot** - NAO V6.0 with NAOqi 2.8.7.4+
- **ROS2 Humble** - Ubuntu 22.04 recommended
- **Network Setup** - Robot and computer on same network
- **HRIStudio** - Web platform for experiment design
## 📄 License
MIT License - See LICENSE file for details
## 🏅 Citation
If you use this integration in your research, please cite:
```bibtex
@software{nao6_hristudio_integration,
title={NAO6 HRIStudio Integration},
author={HRIStudio RoboLab Team},
year={2024},
url={https://github.com/hristudio/nao6-integration},
version={2.0.0}
}
```
---
**Status**: Production Ready ✅
**Tested With**: NAO V6.0 / NAOqi 2.8.7.4 / ROS2 Humble / HRIStudio v1.0
**Last Updated**: December 2024
*Advancing Human-Robot Interaction research through standardized, accessible, and reliable tools.*

0
next.config.js Normal file → Executable file
View File

4
package.json Normal file → Executable file
View File

@@ -62,7 +62,7 @@
"date-fns": "^4.1.0", "date-fns": "^4.1.0",
"drizzle-orm": "^0.41.0", "drizzle-orm": "^0.41.0",
"lucide-react": "^0.536.0", "lucide-react": "^0.536.0",
"next": "^15.5.4", "next": "^16.0.3",
"next-auth": "^5.0.0-beta.29", "next-auth": "^5.0.0-beta.29",
"postgres": "^3.4.4", "postgres": "^3.4.4",
"react": "^19.0.0", "react": "^19.0.0",
@@ -102,6 +102,8 @@
}, },
"trustedDependencies": [ "trustedDependencies": [
"@tailwindcss/oxide", "@tailwindcss/oxide",
"esbuild",
"sharp",
"unrs-resolver" "unrs-resolver"
] ]
} }

0
postcss.config.js Normal file → Executable file
View File

0
prettier.config.js Normal file → Executable file
View File

0
public/favicon.ico Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 168 KiB

After

Width:  |  Height:  |  Size: 168 KiB

0
public/hristudio-core/plugins/control-flow.json Normal file → Executable file
View File

0
public/hristudio-core/plugins/events.json Normal file → Executable file
View File

0
public/hristudio-core/plugins/index.json Normal file → Executable file
View File

0
public/hristudio-core/plugins/observation.json Normal file → Executable file
View File

0
public/hristudio-core/plugins/wizard-actions.json Normal file → Executable file
View File

0
public/hristudio-core/repository.json Normal file → Executable file
View File

View File

@@ -0,0 +1,289 @@
# NAO6 HRIStudio Plugin Repository
**Official NAO6 robot integration plugins for the HRIStudio platform**
## Overview
This repository contains production-ready plugins for integrating NAO6 robots with HRIStudio experiments. The plugins provide comprehensive robot control capabilities including movement, speech synthesis, sensor monitoring, and safety features optimized for human-robot interaction research.
## Available Plugins
### 🤖 NAO6 Enhanced ROS2 Integration (`nao6-ros2-enhanced.json`)
**Complete NAO6 robot control for HRIStudio experiments**
**Features:**
-**Speech Synthesis** - Text-to-speech with volume and speed control
-**Movement Control** - Walking, turning, and precise positioning
-**Posture Management** - Stand, sit, crouch, and custom poses
-**Head Movement** - Gaze control and attention direction
-**Gesture Library** - Wave, point, applause, and custom animations
-**LED Control** - Visual feedback with colors and patterns
-**Sensor Monitoring** - Touch, bumper, sonar, and camera sensors
-**Safety Features** - Emergency stop and velocity limits
-**System Control** - Wake/rest and status monitoring
**Requirements:**
- NAO6 robot with NAOqi 2.8.7.4+
- ROS2 Humble or compatible
- Network connectivity to robot
- `nao_launch` package for ROS integration
**Installation:**
1. Install in HRIStudio study via Plugin Management
2. Configure robot IP and WebSocket URL
3. Launch ROS integration: `ros2 launch nao_launch nao6_production.launch.py`
4. Test connection in HRIStudio experiment designer
## Plugin Actions Reference
### Speech & Communication
| Action | Description | Parameters |
|--------|-------------|------------|
| **Speak Text** | Text-to-speech synthesis | text, volume, speed, wait |
| **LED Control** | Visual feedback with colors | ledGroup, color, intensity, pattern |
### Movement & Posture
| Action | Description | Parameters |
|--------|-------------|------------|
| **Move Robot** | Linear and angular movement | direction, distance, speed, duration |
| **Set Posture** | Predefined poses | posture, speed, waitForCompletion |
| **Move Head** | Gaze and attention control | yaw, pitch, speed, presetDirection |
| **Perform Gesture** | Animations and gestures | gesture, intensity, speed, repeatCount |
### Sensors & Monitoring
| Action | Description | Parameters |
|--------|-------------|------------|
| **Monitor Sensors** | Touch, bumper, sonar detection | sensorType, duration, sensitivity |
| **Check Robot Status** | Battery, joints, system health | statusType, logToExperiment |
### Safety & System
| Action | Description | Parameters |
|--------|-------------|------------|
| **Emergency Stop** | Immediate motion termination | stopType, safePosture |
| **Wake Up / Rest** | Power management | action, waitForCompletion |
## Quick Start Examples
### 1. Basic Greeting
```json
{
"sequence": [
{"action": "nao_wake_rest", "parameters": {"action": "wake"}},
{"action": "nao_speak", "parameters": {"text": "Hello! Welcome to our experiment."}},
{"action": "nao_gesture", "parameters": {"gesture": "wave"}}
]
}
```
### 2. Interactive Task
```json
{
"sequence": [
{"action": "nao_speak", "parameters": {"text": "Please touch my head when ready."}},
{"action": "nao_sensor_monitor", "parameters": {"sensorType": "touch", "duration": 30}},
{"action": "nao_speak", "parameters": {"text": "Thank you! Let's begin."}}
]
}
```
### 3. Attention Direction
```json
{
"sequence": [
{"action": "nao_head_movement", "parameters": {"presetDirection": "left"}},
{"action": "nao_speak", "parameters": {"text": "Look over there please."}},
{"action": "nao_gesture", "parameters": {"gesture": "point_left"}}
]
}
```
## Installation & Setup
### Prerequisites
- **HRIStudio Platform** - Web-based WoZ research platform
- **NAO6 Robot** - With NAOqi 2.8.7.4 or compatible
- **ROS2 Humble** - Robot Operating System 2
- **Network Setup** - Robot and computer on same network
### Step 1: Install NAO ROS2 Packages
```bash
# Clone and build NAO ROS2 workspace
cd ~/naoqi_ros2_ws
colcon build --packages-select nao_launch
source install/setup.bash
```
### Step 2: Start Robot Integration
```bash
# Launch comprehensive NAO integration
ros2 launch nao_launch nao6_production.launch.py \
nao_ip:=nao.local \
password:=robolab \
bridge_port:=9090
```
### Step 3: Install Plugin in HRIStudio
1. **Access HRIStudio** - Open your study in HRIStudio
2. **Plugin Management** - Go to Study → Plugins
3. **Browse Store** - Find "NAO6 Robot (Enhanced ROS2 Integration)"
4. **Install Plugin** - Click install and configure settings
5. **Configure WebSocket** - Set URL to `ws://localhost:9090`
### Step 4: Test Integration
1. **Open Experiment Designer** - Create or edit an experiment
2. **Add Robot Action** - Drag NAO6 action from plugin section
3. **Configure Parameters** - Set speech text, movement, etc.
4. **Test Connection** - Use "Check Robot Status" action
5. **Run Trial** - Execute experiment and verify robot responds
## Configuration Options
### Robot Connection
- **Robot IP** - IP address or hostname (default: `nao.local`)
- **Password** - Robot authentication password
- **WebSocket URL** - ROS bridge connection (default: `ws://localhost:9090`)
### Safety Settings
- **Max Linear Velocity** - Maximum movement speed (default: 0.2 m/s)
- **Max Angular Velocity** - Maximum rotation speed (default: 0.8 rad/s)
- **Safety Monitoring** - Enable automatic safety checks
- **Auto Wake-up** - Automatically wake robot when experiment starts
### Performance Tuning
- **Speech Volume** - Default volume level (default: 0.7)
- **Movement Speed** - Default movement speed factor (default: 0.5)
- **Battery Monitoring** - Track battery level during experiments
## Troubleshooting
### ❌ Robot Not Responding
**Problem:** Commands sent but robot doesn't react
**Solution:**
- Check robot is awake: Press chest button for 3 seconds
- Verify network connectivity: `ping nao.local`
- Use "Wake Up / Rest Robot" action in experiment
### ❌ WebSocket Connection Failed
**Problem:** HRIStudio cannot connect to robot
**Solution:**
- Verify rosbridge is running: `ros2 node list | grep rosbridge`
- Check port availability: `ss -an | grep 9090`
- Restart integration: Kill processes and relaunch
### ❌ Movements Too Fast/Unsafe
**Problem:** Robot moves too quickly or unpredictably
**Solution:**
- Reduce max velocities in plugin configuration
- Lower movement speed parameters in actions
- Use "Emergency Stop" action if needed
### ❌ Speech Not Working
**Problem:** Robot doesn't speak or audio issues
**Solution:**
- Check robot volume settings
- Verify text-to-speech service: `ros2 topic echo /speech`
- Ensure speakers are functioning
## Safety Guidelines
### ⚠️ Important Safety Notes
- **Clear Space** - Ensure 2m clearance around robot during movement
- **Emergency Stop** - Keep emergency stop action easily accessible
- **Supervision** - Never leave robot unattended during experiments
- **Battery Monitoring** - Check battery level for long sessions
- **Stable Surface** - Keep robot on level, stable flooring
### Emergency Procedures
```bash
# Immediate stop via CLI
ros2 topic pub --once /cmd_vel geometry_msgs/msg/Twist '{linear: {x: 0.0, y: 0.0, z: 0.0}, angular: {x: 0.0, y: 0.0, z: 0.0}}'
# Or use HRIStudio emergency stop action
# Add "Emergency Stop" action to experiment for quick access
```
## Technical Details
### ROS2 Topics Used
- **Input Topics** (Robot Control):
- `/speech` - Text-to-speech commands
- `/cmd_vel` - Movement commands
- `/joint_angles` - Joint position control
- `/led_control` - LED color control
- **Output Topics** (Sensor Data):
- `/naoqi_driver/joint_states` - Joint positions
- `/naoqi_driver/bumper` - Foot sensors
- `/naoqi_driver/hand_touch` - Hand sensors
- `/naoqi_driver/head_touch` - Head sensors
- `/naoqi_driver/sonar/*` - Ultrasonic sensors
### WebSocket Communication
- **Protocol** - rosbridge v2.0 WebSocket
- **Default Port** - 9090
- **Message Format** - JSON-based ROS message serialization
- **Authentication** - None (local network)
## Development & Contributing
### Plugin Development
1. **Follow Schema** - Use provided JSON schema for action definitions
2. **Test Thoroughly** - Verify with real NAO6 hardware
3. **Document Actions** - Provide clear parameter descriptions
4. **Safety First** - Include appropriate safety measures
### Testing Checklist
- [ ] Robot connectivity and wake-up
- [ ] All movement actions with safety limits
- [ ] Speech synthesis with various texts
- [ ] Sensor monitoring and event detection
- [ ] Emergency stop functionality
- [ ] WebSocket communication stability
## Support & Resources
### Documentation
- **HRIStudio Docs** - [Platform documentation](../../docs/)
- **NAO6 Integration Guide** - [Complete setup guide](../../docs/nao6-integration-complete-guide.md)
- **Quick Reference** - [Essential commands](../../docs/nao6-quick-reference.md)
### Community & Support
- **GitHub Repository** - [hristudio/nao6-ros2-plugins](https://github.com/hristudio/nao6-ros2-plugins)
- **Issue Tracker** - Report bugs and request features
- **Email Support** - robolab@hristudio.com
### Version Information
- **Plugin Version** - 2.0.0 (Enhanced Integration)
- **HRIStudio Compatibility** - v1.0+
- **ROS2 Distro** - Humble (recommended)
- **NAO6 Compatibility** - NAOqi 2.8.7.4+
- **Last Updated** - December 2024
---
## License
**MIT License** - See [LICENSE](LICENSE) file for details
## Citation
If you use these plugins in your research, please cite:
```bibtex
@software{nao6_hristudio_plugins,
title={NAO6 HRIStudio Integration Plugins},
author={HRIStudio RoboLab Team},
year={2024},
url={https://github.com/hristudio/nao6-ros2-plugins},
version={2.0.0}
}
```
---
**Maintained by:** HRIStudio RoboLab Team
**Contact:** robolab@hristudio.com
**Repository:** [hristudio/nao6-ros2-plugins](https://github.com/hristudio/nao6-ros2-plugins)
*Part of the HRIStudio platform for advancing Human-Robot Interaction research*

View File

@@ -0,0 +1,769 @@
{
"id": "nao6-ros2-enhanced",
"name": "NAO6 Robot (Enhanced ROS2 Integration)",
"version": "2.0.0",
"description": "Comprehensive NAO6 robot integration for HRIStudio experiments via ROS2. Provides full robot control including movement, speech synthesis, posture control, sensor monitoring, and safety features. Optimized for human-robot interaction research with production-ready reliability.",
"author": {
"name": "HRIStudio RoboLab Team",
"email": "robolab@hristudio.com",
"organization": "HRIStudio Research Platform"
},
"license": "MIT",
"repositoryUrl": "https://github.com/hristudio/nao6-ros2-plugins",
"documentationUrl": "https://docs.hristudio.com/robots/nao6",
"trustLevel": "official",
"status": "active",
"tags": ["nao6", "ros2", "speech", "movement", "sensors", "hri", "production"],
"robotId": "nao6-softbank",
"communicationProtocol": "ros2_websocket",
"metadata": {
"robotModel": "NAO V6.0",
"manufacturer": "SoftBank Robotics",
"naoqiVersion": "2.8.7.4",
"ros2Distro": "humble",
"websocketUrl": "ws://localhost:9090",
"launchPackage": "nao_launch",
"requiredPackages": [
"naoqi_driver2",
"naoqi_bridge_msgs",
"rosbridge_server",
"rosapi"
],
"safetyFeatures": {
"emergencyStop": true,
"velocityLimits": true,
"fallDetection": true,
"batteryMonitoring": true,
"automaticWakeup": true
},
"capabilities": [
"bipedal_walking",
"speech_synthesis",
"head_movement",
"arm_gestures",
"touch_sensors",
"visual_sensors",
"audio_sensors",
"posture_control",
"balance_control"
]
},
"configurationSchema": {
"type": "object",
"properties": {
"robotIp": {
"type": "string",
"default": "nao.local",
"title": "Robot IP Address",
"description": "IP address or hostname of the NAO6 robot"
},
"robotPassword": {
"type": "string",
"default": "robolab",
"title": "Robot Password",
"description": "Password for robot authentication",
"format": "password"
},
"websocketUrl": {
"type": "string",
"default": "ws://localhost:9090",
"title": "WebSocket URL",
"description": "ROS bridge WebSocket URL for robot communication"
},
"maxLinearVelocity": {
"type": "number",
"default": 0.2,
"minimum": 0.01,
"maximum": 0.5,
"title": "Max Linear Velocity (m/s)",
"description": "Maximum allowed linear movement speed for safety"
},
"maxAngularVelocity": {
"type": "number",
"default": 0.8,
"minimum": 0.1,
"maximum": 2.0,
"title": "Max Angular Velocity (rad/s)",
"description": "Maximum allowed rotational speed for safety"
},
"defaultMovementSpeed": {
"type": "number",
"default": 0.5,
"minimum": 0.1,
"maximum": 1.0,
"title": "Default Movement Speed",
"description": "Speed factor for posture and gesture movements (0.1-1.0)"
},
"speechVolume": {
"type": "number",
"default": 0.7,
"minimum": 0.1,
"maximum": 1.0,
"title": "Speech Volume",
"description": "Default volume for speech synthesis (0.1-1.0)"
},
"enableSafetyMonitoring": {
"type": "boolean",
"default": true,
"title": "Enable Safety Monitoring",
"description": "Enable automatic safety monitoring and emergency stops"
},
"autoWakeUp": {
"type": "boolean",
"default": true,
"title": "Auto Wake-up Robot",
"description": "Automatically wake up robot when experiment starts"
},
"monitorBattery": {
"type": "boolean",
"default": true,
"title": "Monitor Battery",
"description": "Monitor robot battery level during experiments"
}
},
"required": ["robotIp", "websocketUrl"]
},
"actionDefinitions": [
{
"id": "nao_speak",
"name": "Speak Text",
"description": "Make the NAO robot speak the specified text using text-to-speech synthesis",
"category": "speech",
"icon": "volume2",
"parametersSchema": {
"type": "object",
"properties": {
"text": {
"type": "string",
"title": "Text to Speak",
"description": "The text that the robot should speak aloud",
"minLength": 1,
"maxLength": 500
},
"volume": {
"type": "number",
"title": "Volume",
"description": "Speech volume level (0.1 = quiet, 1.0 = loud)",
"default": 0.7,
"minimum": 0.1,
"maximum": 1.0,
"step": 0.1
},
"speed": {
"type": "number",
"title": "Speech Speed",
"description": "Speech rate multiplier (0.5 = slow, 2.0 = fast)",
"default": 1.0,
"minimum": 0.5,
"maximum": 2.0,
"step": 0.1
},
"waitForCompletion": {
"type": "boolean",
"title": "Wait for Speech to Complete",
"description": "Wait until speech finishes before continuing to next action",
"default": true
}
},
"required": ["text"]
},
"implementation": {
"type": "ros2_topic",
"topic": "/speech",
"messageType": "std_msgs/String",
"messageMapping": {
"data": "{{text}}"
}
}
},
{
"id": "nao_move",
"name": "Move Robot",
"description": "Move the NAO robot with specified linear and angular velocities",
"category": "movement",
"icon": "move",
"parametersSchema": {
"type": "object",
"properties": {
"direction": {
"type": "string",
"title": "Movement Direction",
"description": "Predefined movement direction",
"enum": ["forward", "backward", "left", "right", "turn_left", "turn_right", "custom"],
"enumNames": ["Forward", "Backward", "Step Left", "Step Right", "Turn Left", "Turn Right", "Custom"],
"default": "forward"
},
"distance": {
"type": "number",
"title": "Distance/Angle",
"description": "Distance in meters for linear movement, or angle in degrees for rotation",
"default": 0.1,
"minimum": 0.01,
"maximum": 2.0,
"step": 0.01
},
"speed": {
"type": "number",
"title": "Movement Speed",
"description": "Speed factor (0.1 = very slow, 1.0 = normal speed)",
"default": 0.5,
"minimum": 0.1,
"maximum": 1.0,
"step": 0.1
},
"customX": {
"type": "number",
"title": "Custom X Velocity (m/s)",
"description": "Forward/backward velocity (positive = forward)",
"default": 0.0,
"minimum": -0.3,
"maximum": 0.3,
"step": 0.01
},
"customY": {
"type": "number",
"title": "Custom Y Velocity (m/s)",
"description": "Left/right velocity (positive = left)",
"default": 0.0,
"minimum": -0.3,
"maximum": 0.3,
"step": 0.01
},
"customTheta": {
"type": "number",
"title": "Custom Angular Velocity (rad/s)",
"description": "Rotational velocity (positive = counter-clockwise)",
"default": 0.0,
"minimum": -1.5,
"maximum": 1.5,
"step": 0.01
},
"duration": {
"type": "number",
"title": "Duration (seconds)",
"description": "How long to maintain the movement",
"default": 2.0,
"minimum": 0.1,
"maximum": 10.0,
"step": 0.1
}
},
"required": ["direction"]
},
"implementation": {
"type": "ros2_topic",
"topic": "/cmd_vel",
"messageType": "geometry_msgs/Twist",
"messageMapping": {
"linear": {
"x": "{{#eq direction 'forward'}}{{multiply distance speed 0.1}}{{/eq}}{{#eq direction 'backward'}}{{multiply distance speed -0.1}}{{/eq}}{{#eq direction 'custom'}}{{customX}}{{/eq}}{{#default}}0.0{{/default}}",
"y": "{{#eq direction 'left'}}{{multiply distance speed 0.1}}{{/eq}}{{#eq direction 'right'}}{{multiply distance speed -0.1}}{{/eq}}{{#eq direction 'custom'}}{{customY}}{{/eq}}{{#default}}0.0{{/default}}",
"z": 0.0
},
"angular": {
"x": 0.0,
"y": 0.0,
"z": "{{#eq direction 'turn_left'}}{{multiply distance speed 0.1}}{{/eq}}{{#eq direction 'turn_right'}}{{multiply distance speed -0.1}}{{/eq}}{{#eq direction 'custom'}}{{customTheta}}{{/eq}}{{#default}}0.0{{/default}}"
}
}
}
},
{
"id": "nao_pose",
"name": "Set Posture",
"description": "Set the NAO robot to a specific posture or pose",
"category": "movement",
"icon": "user",
"parametersSchema": {
"type": "object",
"properties": {
"posture": {
"type": "string",
"title": "Posture",
"description": "Target posture for the robot",
"enum": ["Stand", "Sit", "SitRelax", "StandInit", "StandZero", "Crouch", "LyingBack", "LyingBelly"],
"enumNames": ["Stand", "Sit", "Sit Relaxed", "Stand Initial", "Stand Zero", "Crouch", "Lying on Back", "Lying on Belly"],
"default": "Stand"
},
"speed": {
"type": "number",
"title": "Movement Speed",
"description": "Speed of posture transition (0.1 = slow, 1.0 = fast)",
"default": 0.5,
"minimum": 0.1,
"maximum": 1.0,
"step": 0.1
},
"waitForCompletion": {
"type": "boolean",
"title": "Wait for Completion",
"description": "Wait until posture change is complete before continuing",
"default": true
}
},
"required": ["posture"]
},
"implementation": {
"type": "ros2_service",
"service": "/naoqi_driver/robot_posture/go_to_posture",
"serviceType": "naoqi_bridge_msgs/srv/SetString",
"requestMapping": {
"data": "{{posture}}"
}
}
},
{
"id": "nao_head_movement",
"name": "Move Head",
"description": "Control NAO robot head movement for gaze direction and attention",
"category": "movement",
"icon": "eye",
"parametersSchema": {
"type": "object",
"properties": {
"headYaw": {
"type": "number",
"title": "Head Yaw (degrees)",
"description": "Left/right head rotation (-90° = right, +90° = left)",
"default": 0.0,
"minimum": -90.0,
"maximum": 90.0,
"step": 1.0
},
"headPitch": {
"type": "number",
"title": "Head Pitch (degrees)",
"description": "Up/down head rotation (-25° = down, +25° = up)",
"default": 0.0,
"minimum": -25.0,
"maximum": 25.0,
"step": 1.0
},
"speed": {
"type": "number",
"title": "Movement Speed",
"description": "Speed of head movement (0.1 = slow, 1.0 = fast)",
"default": 0.3,
"minimum": 0.1,
"maximum": 1.0,
"step": 0.1
},
"presetDirection": {
"type": "string",
"title": "Preset Direction",
"description": "Use preset head direction instead of custom angles",
"enum": ["none", "center", "left", "right", "up", "down", "look_left", "look_right"],
"enumNames": ["Custom Angles", "Center", "Left", "Right", "Up", "Down", "Look Left", "Look Right"],
"default": "none"
}
},
"required": []
},
"implementation": {
"type": "ros2_topic",
"topic": "/joint_angles",
"messageType": "naoqi_bridge_msgs/JointAnglesWithSpeed",
"messageMapping": {
"joint_names": ["HeadYaw", "HeadPitch"],
"joint_angles": [
"{{#ne presetDirection 'none'}}{{#eq presetDirection 'left'}}1.57{{/eq}}{{#eq presetDirection 'right'}}-1.57{{/eq}}{{#eq presetDirection 'center'}}0.0{{/eq}}{{#eq presetDirection 'look_left'}}0.78{{/eq}}{{#eq presetDirection 'look_right'}}-0.78{{/eq}}{{#default}}{{multiply headYaw 0.0175}}{{/default}}{{/ne}}{{#eq presetDirection 'none'}}{{multiply headYaw 0.0175}}{{/eq}}",
"{{#ne presetDirection 'none'}}{{#eq presetDirection 'up'}}0.44{{/eq}}{{#eq presetDirection 'down'}}-0.44{{/eq}}{{#eq presetDirection 'center'}}0.0{{/eq}}{{#default}}{{multiply headPitch 0.0175}}{{/default}}{{/ne}}{{#eq presetDirection 'none'}}{{multiply headPitch 0.0175}}{{/eq}}"
],
"speed": "{{speed}}"
}
}
},
{
"id": "nao_gesture",
"name": "Perform Gesture",
"description": "Make NAO robot perform predefined gestures and animations",
"category": "interaction",
"icon": "hand",
"parametersSchema": {
"type": "object",
"properties": {
"gesture": {
"type": "string",
"title": "Gesture Type",
"description": "Select a predefined gesture or animation",
"enum": ["wave", "point_left", "point_right", "applause", "thumbs_up", "open_arms", "bow", "celebration", "thinking", "custom"],
"enumNames": ["Wave Hello", "Point Left", "Point Right", "Applause", "Thumbs Up", "Open Arms", "Bow", "Celebration", "Thinking Pose", "Custom Joint Movement"],
"default": "wave"
},
"intensity": {
"type": "number",
"title": "Gesture Intensity",
"description": "Intensity of the gesture movement (0.5 = subtle, 1.0 = full)",
"default": 0.8,
"minimum": 0.3,
"maximum": 1.0,
"step": 0.1
},
"speed": {
"type": "number",
"title": "Gesture Speed",
"description": "Speed of gesture execution (0.1 = slow, 1.0 = fast)",
"default": 0.5,
"minimum": 0.1,
"maximum": 1.0,
"step": 0.1
},
"repeatCount": {
"type": "integer",
"title": "Repeat Count",
"description": "Number of times to repeat the gesture",
"default": 1,
"minimum": 1,
"maximum": 5
}
},
"required": ["gesture"]
},
"implementation": {
"type": "ros2_service",
"service": "/naoqi_driver/animation_player/run_animation",
"serviceType": "naoqi_bridge_msgs/srv/SetString",
"requestMapping": {
"data": "{{#eq gesture 'wave'}}animations/Stand/Gestures/Hey_1{{/eq}}{{#eq gesture 'point_left'}}animations/Stand/Gestures/YouKnowWhat_1{{/eq}}{{#eq gesture 'point_right'}}animations/Stand/Gestures/YouKnowWhat_2{{/eq}}{{#eq gesture 'applause'}}animations/Stand/Gestures/Applause_1{{/eq}}{{#eq gesture 'thumbs_up'}}animations/Stand/Gestures/Yes_1{{/eq}}{{#eq gesture 'open_arms'}}animations/Stand/Gestures/Everything_1{{/eq}}{{#eq gesture 'bow'}}animations/Stand/Gestures/BowShort_1{{/eq}}{{#eq gesture 'celebration'}}animations/Stand/Gestures/Excited_1{{/eq}}{{#eq gesture 'thinking'}}animations/Stand/Gestures/Thinking_1{{/eq}}"
}
}
},
{
"id": "nao_led_control",
"name": "Control LEDs",
"description": "Control NAO robot LED colors and patterns for visual feedback",
"category": "interaction",
"icon": "lightbulb",
"parametersSchema": {
"type": "object",
"properties": {
"ledGroup": {
"type": "string",
"title": "LED Group",
"description": "Which LED group to control",
"enum": ["eyes", "ears", "chest", "feet", "all"],
"enumNames": ["Eyes", "Ears", "Chest", "Feet", "All LEDs"],
"default": "eyes"
},
"color": {
"type": "string",
"title": "LED Color",
"description": "Color for the LEDs",
"enum": ["red", "green", "blue", "yellow", "cyan", "magenta", "white", "orange", "purple", "off"],
"enumNames": ["Red", "Green", "Blue", "Yellow", "Cyan", "Magenta", "White", "Orange", "Purple", "Off"],
"default": "blue"
},
"intensity": {
"type": "number",
"title": "LED Intensity",
"description": "Brightness of the LEDs (0.0 = off, 1.0 = maximum)",
"default": 0.8,
"minimum": 0.0,
"maximum": 1.0,
"step": 0.1
},
"pattern": {
"type": "string",
"title": "LED Pattern",
"description": "LED animation pattern",
"enum": ["solid", "blink", "fade", "pulse", "rainbow"],
"enumNames": ["Solid", "Blink", "Fade In/Out", "Pulse", "Rainbow Cycle"],
"default": "solid"
},
"duration": {
"type": "number",
"title": "Duration (seconds)",
"description": "How long to maintain the LED state (0 = indefinite)",
"default": 0,
"minimum": 0,
"maximum": 60,
"step": 1
}
},
"required": ["ledGroup", "color"]
},
"implementation": {
"type": "ros2_topic",
"topic": "/led_control",
"messageType": "naoqi_bridge_msgs/Led",
"messageMapping": {
"name": "{{ledGroup}}",
"color": "{{color}}",
"intensity": "{{intensity}}"
}
}
},
{
"id": "nao_sensor_monitor",
"name": "Monitor Sensors",
"description": "Monitor NAO robot sensors for interaction detection and environmental awareness",
"category": "sensors",
"icon": "activity",
"parametersSchema": {
"type": "object",
"properties": {
"sensorType": {
"type": "string",
"title": "Sensor Type",
"description": "Which sensors to monitor",
"enum": ["touch", "bumper", "sonar", "camera", "audio", "all"],
"enumNames": ["Touch Sensors", "Foot Bumpers", "Ultrasonic Sensors", "Cameras", "Audio", "All Sensors"],
"default": "touch"
},
"duration": {
"type": "number",
"title": "Monitoring Duration (seconds)",
"description": "How long to monitor sensors (0 = continuous)",
"default": 10,
"minimum": 0,
"maximum": 300,
"step": 1
},
"sensitivity": {
"type": "number",
"title": "Detection Sensitivity",
"description": "Sensitivity level for sensor detection (0.1 = low, 1.0 = high)",
"default": 0.7,
"minimum": 0.1,
"maximum": 1.0,
"step": 0.1
},
"logEvents": {
"type": "boolean",
"title": "Log Sensor Events",
"description": "Log all sensor events to experiment data",
"default": true
},
"triggerAction": {
"type": "string",
"title": "Trigger Action",
"description": "Action to take when sensor is activated",
"enum": ["none", "speak", "gesture", "move", "led"],
"enumNames": ["No Action", "Speak Response", "Perform Gesture", "Move Robot", "LED Feedback"],
"default": "none"
}
},
"required": ["sensorType"]
},
"implementation": {
"type": "ros2_subscription",
"topics": [
"/naoqi_driver/bumper",
"/naoqi_driver/hand_touch",
"/naoqi_driver/head_touch",
"/naoqi_driver/sonar/left",
"/naoqi_driver/sonar/right"
],
"messageTypes": [
"naoqi_bridge_msgs/Bumper",
"naoqi_bridge_msgs/HandTouch",
"naoqi_bridge_msgs/HeadTouch",
"sensor_msgs/Range",
"sensor_msgs/Range"
]
}
},
{
"id": "nao_emergency_stop",
"name": "Emergency Stop",
"description": "Immediately stop all robot movement and animations for safety",
"category": "safety",
"icon": "stop-circle",
"parametersSchema": {
"type": "object",
"properties": {
"stopType": {
"type": "string",
"title": "Stop Type",
"description": "Type of emergency stop to perform",
"enum": ["movement", "all", "freeze"],
"enumNames": ["Stop Movement Only", "Stop All Actions", "Freeze in Place"],
"default": "all"
},
"safePosture": {
"type": "boolean",
"title": "Move to Safe Posture",
"description": "Automatically move to a safe posture after stopping",
"default": true
}
},
"required": []
},
"implementation": {
"type": "ros2_topic",
"topic": "/cmd_vel",
"messageType": "geometry_msgs/Twist",
"messageMapping": {
"linear": {"x": 0.0, "y": 0.0, "z": 0.0},
"angular": {"x": 0.0, "y": 0.0, "z": 0.0}
}
}
},
{
"id": "nao_wake_rest",
"name": "Wake Up / Rest Robot",
"description": "Wake up the robot or put it to rest position for power management",
"category": "system",
"icon": "power",
"parametersSchema": {
"type": "object",
"properties": {
"action": {
"type": "string",
"title": "Action",
"description": "Wake up robot or put to rest",
"enum": ["wake", "rest"],
"enumNames": ["Wake Up Robot", "Put Robot to Rest"],
"default": "wake"
},
"waitForCompletion": {
"type": "boolean",
"title": "Wait for Completion",
"description": "Wait until wake/rest action is complete",
"default": true
}
},
"required": ["action"]
},
"implementation": {
"type": "ros2_service",
"service": "/naoqi_driver/motion/{{action}}_up",
"serviceType": "std_srvs/srv/Empty",
"requestMapping": {}
}
},
{
"id": "nao_status_check",
"name": "Check Robot Status",
"description": "Get current robot status including battery, temperature, and system health",
"category": "system",
"icon": "info",
"parametersSchema": {
"type": "object",
"properties": {
"statusType": {
"type": "string",
"title": "Status Information",
"description": "What status information to retrieve",
"enum": ["basic", "battery", "sensors", "joints", "all"],
"enumNames": ["Basic Status", "Battery Info", "Sensor Status", "Joint Status", "Complete Status"],
"default": "basic"
},
"logToExperiment": {
"type": "boolean",
"title": "Log to Experiment Data",
"description": "Save status information to experiment logs",
"default": true
}
},
"required": ["statusType"]
},
"implementation": {
"type": "ros2_service",
"service": "/naoqi_driver/get_robot_config",
"serviceType": "naoqi_bridge_msgs/srv/GetRobotInfo",
"requestMapping": {}
}
}
],
"installation": {
"requirements": [
"ROS2 Humble or compatible",
"NAO6 robot with NAOqi 2.8.7.4+",
"Network connectivity to robot",
"naoqi_driver2 package",
"rosbridge_suite package"
],
"setup": [
{
"step": 1,
"description": "Install NAO ROS2 packages",
"command": "cd ~/naoqi_ros2_ws && colcon build"
},
{
"step": 2,
"description": "Start NAO integration",
"command": "ros2 launch nao_launch nao6_production.launch.py nao_ip:=nao.local password:=robolab"
},
{
"step": 3,
"description": "Configure HRIStudio plugin",
"description_detail": "Set WebSocket URL to ws://localhost:9090 in plugin configuration"
}
],
"verification": [
{
"description": "Test robot connectivity",
"command": "ping nao.local"
},
{
"description": "Verify ROS topics",
"command": "ros2 topic list | grep naoqi"
},
{
"description": "Test WebSocket bridge",
"command": "ros2 node list | grep rosbridge"
}
]
},
"troubleshooting": {
"commonIssues": [
{
"issue": "Robot not responding to commands",
"solution": "Ensure robot is awake. Use 'Wake Up / Rest Robot' action or press chest button for 3 seconds."
},
{
"issue": "WebSocket connection failed",
"solution": "Check that rosbridge is running: ros2 node list | grep rosbridge. Restart if needed."
},
{
"issue": "Robot movements too fast/unsafe",
"solution": "Adjust maxLinearVelocity and maxAngularVelocity in plugin configuration."
},
{
"issue": "Speech not working",
"solution": "Check robot volume settings and ensure speech synthesis service is active."
}
],
"safetyNotes": [
"Always ensure clear space around robot during movement",
"Use Emergency Stop action if robot behaves unexpectedly",
"Monitor battery level during long experiments",
"Start with slow movements to test robot response",
"Keep robot on stable, level surfaces"
]
},
"examples": [
{
"name": "Basic Greeting Interaction",
"description": "Simple greeting sequence with speech and gesture",
"actions": [
{"action": "nao_wake_rest", "parameters": {"action": "wake"}},
{"action": "nao_speak", "parameters": {"text": "Hello! Welcome to our experiment."}},
{"action": "nao_gesture", "parameters": {"gesture": "wave"}},
{"action": "nao_pose", "parameters": {"posture": "Stand"}}
]
},
{
"name": "Attention and Pointing",
"description": "Direct attention using head movement and pointing",
"actions": [
{"action": "nao_head_movement", "parameters": {"presetDirection": "left"}},
{"action": "nao_speak", "parameters": {"text": "Please look over there."}},
{"action": "nao_gesture", "parameters": {"gesture": "point_left"}},
{"action": "nao_head_movement", "parameters": {"presetDirection": "center"}}
]
},
{
"name": "Interactive Sensor Monitoring",
"description": "Monitor for touch interactions and respond",
"actions": [
{"action": "nao_speak", "parameters": {"text": "Touch my head when you're ready to continue."}},
{"action": "nao_sensor_monitor", "parameters": {"sensorType": "touch", "triggerAction": "speak"}},
{"action": "nao_speak", "parameters": {"text": "Thank you! Let's continue."}}
]
}
],
"createdAt": "2024-12-16T00:00:00Z",
"updatedAt": "2024-12-16T00:00:00Z"
}

0
public/simple-ws-test.html Normal file → Executable file
View File

0
public/test-websocket.html Normal file → Executable file
View File

0
public/ws-check.html Normal file → Executable file
View File

Submodule robot-plugins updated: bbfe6e80c3...f3db314c8a

57
scripts/seed-dev.ts Normal file → Executable file
View File

@@ -306,17 +306,6 @@ async function main() {
syncStatus: "pending" as const, syncStatus: "pending" as const,
createdBy: seanUser.id, createdBy: seanUser.id,
}, },
{
name: "NAO6 ROS2 Integration Repository",
url: "http://localhost:3000/nao6-plugins",
description:
"Official NAO6 robot plugins for ROS2-based Human-Robot Interaction experiments",
trustLevel: "official" as const,
isEnabled: true,
isOfficial: true,
syncStatus: "pending" as const,
createdBy: seanUser.id,
},
]; ];
const insertedRepos = await db const insertedRepos = await db
@@ -428,24 +417,30 @@ async function main() {
); );
} }
// Install NAO plugin for first study if available // Install NAO6 ROS2 plugin for first study if available
console.log("🤝 Installing NAO plugin (if available)..."); console.log("🤝 Installing NAO6 ROS2 plugin (if available)...");
const naoPlugin = await db const naoPlugin = await db
.select() .select()
.from(schema.plugins) .from(schema.plugins)
.where(eq(schema.plugins.name, "NAO Humanoid Robot")) .where(eq(schema.plugins.name, "NAO6 Robot (ROS2 Integration)"))
.limit(1); .limit(1);
if (naoPlugin.length > 0 && insertedStudies[0]) { if (naoPlugin.length > 0 && insertedStudies[0]) {
await db.insert(schema.studyPlugins).values({ await db.insert(schema.studyPlugins).values({
studyId: insertedStudies[0].id, studyId: insertedStudies[0].id,
pluginId: naoPlugin[0]!.id, pluginId: naoPlugin[0]!.id,
configuration: { voice: "nao-tts", locale: "en-US" }, configuration: {
robotIp: "nao.local",
websocketUrl: "ws://localhost:9090",
maxLinearVelocity: 0.3,
maxAngularVelocity: 1.0,
defaultSpeed: 0.5,
},
installedBy: seanUser.id, installedBy: seanUser.id,
}); });
console.log("✅ Installed NAO plugin in first study"); console.log("✅ Installed NAO6 ROS2 plugin in first study");
} else { } else {
console.log( console.log(
" NAO plugin not found in repository sync; continuing without it", " NAO6 ROS2 plugin not found in repository sync; continuing without it",
); );
} }
@@ -557,31 +552,31 @@ async function main() {
retryable: false, retryable: false,
}); });
// Resolve NAO plugin id/version for namespaced action type // Resolve NAO6 ROS2 plugin id/version for namespaced action type
const naoDbPlugin1 = await db const naoDbPlugin1 = await db
.select({ id: schema.plugins.id, version: schema.plugins.version }) .select({ id: schema.plugins.id, version: schema.plugins.version })
.from(schema.plugins) .from(schema.plugins)
.where(eq(schema.plugins.name, "NAO Humanoid Robot")) .where(eq(schema.plugins.name, "NAO6 Robot (ROS2 Integration)"))
.limit(1); .limit(1);
const naoPluginRow1 = naoDbPlugin1[0]; const naoPluginRow1 = naoDbPlugin1[0];
// Action 1.2: Robot/NAO says text (or wizard says fallback) // Action 1.2: Robot/NAO speaks text
await db.insert(schema.actions).values({ await db.insert(schema.actions).values({
stepId: step1Id, stepId: step1Id,
name: naoPluginRow1 ? "NAO Say Text" : "Wizard Say", name: naoPluginRow1 ? "NAO Speak Text" : "Wizard Say",
description: naoPluginRow1 description: naoPluginRow1
? "Make the robot speak using text-to-speech" ? "Make the robot speak using text-to-speech via ROS2"
: "Wizard speaks to participant", : "Wizard speaks to participant",
type: naoPluginRow1 ? `${naoPluginRow1.id}.say_text` : "wizard_say", type: naoPluginRow1 ? `${naoPluginRow1.id}.nao6_speak` : "wizard_say",
orderIndex: 1, orderIndex: 1,
parameters: naoPluginRow1 parameters: naoPluginRow1
? { text: "Hello, I am NAO. Let's begin!", speed: 110, volume: 0.75 } ? { text: "Hello, I am NAO. Let's begin!", volume: 0.8 }
: { message: "Hello! Let's begin the session.", tone: "friendly" }, : { message: "Hello! Let's begin the session.", tone: "friendly" },
sourceKind: naoPluginRow1 ? "plugin" : "core", sourceKind: naoPluginRow1 ? "plugin" : "core",
pluginId: naoPluginRow1 ? naoPluginRow1.id : null, pluginId: naoPluginRow1 ? naoPluginRow1.id : null,
pluginVersion: naoPluginRow1 ? naoPluginRow1.version : null, pluginVersion: naoPluginRow1 ? naoPluginRow1.version : null,
category: naoPluginRow1 ? "robot" : "wizard", category: naoPluginRow1 ? "robot" : "wizard",
transport: naoPluginRow1 ? "rest" : "internal", transport: naoPluginRow1 ? "ros2" : "internal",
retryable: false, retryable: false,
}); });
@@ -635,23 +630,23 @@ async function main() {
const naoDbPlugin2 = await db const naoDbPlugin2 = await db
.select({ id: schema.plugins.id, version: schema.plugins.version }) .select({ id: schema.plugins.id, version: schema.plugins.version })
.from(schema.plugins) .from(schema.plugins)
.where(eq(schema.plugins.name, "NAO Humanoid Robot")) .where(eq(schema.plugins.name, "NAO6 Robot (ROS2 Integration)"))
.limit(1); .limit(1);
const naoPluginRow2 = naoDbPlugin2[0]; const naoPluginRow2 = naoDbPlugin2[0];
if (naoPluginRow2) { if (naoPluginRow2) {
await db.insert(schema.actions).values({ await db.insert(schema.actions).values({
stepId: step3Id, stepId: step3Id,
name: "Set LED Color", name: "NAO Move Head",
description: "Change NAO's eye LEDs to reflect state", description: "Move NAO's head to look at participant",
type: `${naoPluginRow2.id}.set_led_color`, type: `${naoPluginRow2.id}.nao6_move_head`,
orderIndex: 0, orderIndex: 0,
parameters: { color: "blue", intensity: 0.6 }, parameters: { yaw: 0.0, pitch: -0.2, speed: 0.3 },
sourceKind: "plugin", sourceKind: "plugin",
pluginId: naoPluginRow2.id, pluginId: naoPluginRow2.id,
pluginVersion: naoPluginRow2.version, pluginVersion: naoPluginRow2.version,
category: "robot", category: "robot",
transport: "rest", transport: "ros2",
retryable: false, retryable: false,
}); });
} else { } else {

631
scripts/seed-nao6-plugin.ts Normal file
View File

@@ -0,0 +1,631 @@
#!/usr/bin/env bun
/**
* Seed NAO6 Plugin into HRIStudio Database
*
* This script adds the NAO6 ROS2 integration plugin to the HRIStudio database,
* including the plugin repository and plugin definition with all available actions.
*/
import { drizzle } from "drizzle-orm/postgres-js";
import postgres from "postgres";
import { env } from "~/env";
import {
plugins,
pluginRepositories,
robots,
users,
type InsertPlugin,
type InsertPluginRepository,
type InsertRobot,
} from "~/server/db/schema";
import { eq } from "drizzle-orm";
const connectionString = env.DATABASE_URL;
const client = postgres(connectionString);
const db = drizzle(client);
async function seedNAO6Plugin() {
console.log("🤖 Seeding NAO6 plugin into HRIStudio database...");
try {
// 0. Get system user for created_by fields
console.log("👤 Getting system user...");
const systemUser = await db
.select()
.from(users)
.where(eq(users.email, "sean@soconnor.dev"))
.limit(1);
if (systemUser.length === 0) {
throw new Error(
"System user not found. Please run 'bun db:seed' first to create initial users.",
);
}
const userId = systemUser[0]!.id;
console.log(`✅ Found system user: ${userId}`);
// 1. Create or update NAO6 robot entry
console.log("📋 Creating NAO6 robot entry...");
const existingRobot = await db
.select()
.from(robots)
.where(eq(robots.name, "NAO6"))
.limit(1);
let robotId: string;
if (existingRobot.length > 0) {
robotId = existingRobot[0]!.id;
console.log(`✅ Found existing NAO6 robot: ${robotId}`);
} else {
const newRobots = await db
.insert(robots)
.values({
name: "NAO6",
manufacturer: "SoftBank Robotics",
model: "NAO V6.0",
description:
"Humanoid robot designed for education, research, and human-robot interaction studies. Features bipedal walking, speech synthesis, cameras, and comprehensive sensor suite.",
capabilities: [
"bipedal_walking",
"speech_synthesis",
"head_movement",
"arm_gestures",
"touch_sensors",
"visual_sensors",
"audio_sensors",
"posture_control",
"balance_control",
],
communicationProtocol: "ros2",
} satisfies InsertRobot)
.returning();
robotId = newRobots[0]!.id;
console.log(`✅ Created NAO6 robot: ${robotId}`);
}
// 2. Create or update plugin repository
console.log("📦 Creating NAO6 plugin repository...");
const existingRepo = await db
.select()
.from(pluginRepositories)
.where(eq(pluginRepositories.name, "NAO6 ROS2 Integration Repository"))
.limit(1);
let repoId: string;
if (existingRepo.length > 0) {
repoId = existingRepo[0]!.id;
console.log(`✅ Found existing repository: ${repoId}`);
} else {
const newRepos = await db
.insert(pluginRepositories)
.values({
name: "NAO6 ROS2 Integration Repository",
url: "https://github.com/hristudio/nao6-ros2-plugins",
description:
"Official NAO6 robot plugins for ROS2-based Human-Robot Interaction experiments",
trustLevel: "official",
isActive: true,
isPublic: true,
createdBy: userId,
status: "active",
lastSyncedAt: new Date(),
metadata: {
author: {
name: "HRIStudio Team",
email: "support@hristudio.com",
},
license: "MIT",
ros2: {
distro: "humble",
packages: [
"naoqi_driver2",
"naoqi_bridge_msgs",
"rosbridge_suite",
],
},
supportedRobots: ["NAO6"],
categories: [
"movement",
"speech",
"sensors",
"interaction",
"vision",
],
},
} satisfies InsertPluginRepository)
.returning();
repoId = newRepos[0]!.id;
console.log(`✅ Created repository: ${repoId}`);
}
// 3. Create or update NAO6 plugin
console.log("🔌 Creating NAO6 enhanced plugin...");
const existingPlugin = await db
.select()
.from(plugins)
.where(eq(plugins.name, "NAO6 Robot (Enhanced ROS2 Integration)"))
.limit(1);
const actionDefinitions = [
{
id: "nao_speak",
name: "Speak Text",
description:
"Make the NAO robot speak the specified text using text-to-speech synthesis",
category: "speech",
icon: "volume2",
parametersSchema: {
type: "object",
properties: {
text: {
type: "string",
title: "Text to Speak",
description: "The text that the robot should speak aloud",
minLength: 1,
maxLength: 500,
},
volume: {
type: "number",
title: "Volume",
description: "Speech volume level (0.1 = quiet, 1.0 = loud)",
default: 0.7,
minimum: 0.1,
maximum: 1.0,
step: 0.1,
},
speed: {
type: "number",
title: "Speech Speed",
description: "Speech rate multiplier (0.5 = slow, 2.0 = fast)",
default: 1.0,
minimum: 0.5,
maximum: 2.0,
step: 0.1,
},
},
required: ["text"],
},
implementation: {
type: "ros2_topic",
topic: "/speech",
messageType: "std_msgs/String",
},
},
{
id: "nao_move",
name: "Move Robot",
description:
"Move the NAO robot with specified linear and angular velocities",
category: "movement",
icon: "move",
parametersSchema: {
type: "object",
properties: {
direction: {
type: "string",
title: "Movement Direction",
description: "Predefined movement direction",
enum: [
"forward",
"backward",
"left",
"right",
"turn_left",
"turn_right",
],
default: "forward",
},
distance: {
type: "number",
title: "Distance (meters)",
description: "Distance to move in meters",
default: 0.1,
minimum: 0.01,
maximum: 2.0,
step: 0.01,
},
speed: {
type: "number",
title: "Movement Speed",
description: "Speed factor (0.1 = very slow, 1.0 = normal speed)",
default: 0.5,
minimum: 0.1,
maximum: 1.0,
step: 0.1,
},
},
required: ["direction"],
},
implementation: {
type: "ros2_topic",
topic: "/cmd_vel",
messageType: "geometry_msgs/Twist",
},
},
{
id: "nao_pose",
name: "Set Posture",
description: "Set the NAO robot to a specific posture or pose",
category: "movement",
icon: "user",
parametersSchema: {
type: "object",
properties: {
posture: {
type: "string",
title: "Posture",
description: "Target posture for the robot",
enum: ["Stand", "Sit", "SitRelax", "StandInit", "Crouch"],
default: "Stand",
},
speed: {
type: "number",
title: "Movement Speed",
description:
"Speed of posture transition (0.1 = slow, 1.0 = fast)",
default: 0.5,
minimum: 0.1,
maximum: 1.0,
step: 0.1,
},
},
required: ["posture"],
},
implementation: {
type: "ros2_service",
service: "/naoqi_driver/robot_posture/go_to_posture",
serviceType: "naoqi_bridge_msgs/srv/SetString",
},
},
{
id: "nao_head_movement",
name: "Move Head",
description:
"Control NAO robot head movement for gaze direction and attention",
category: "movement",
icon: "eye",
parametersSchema: {
type: "object",
properties: {
headYaw: {
type: "number",
title: "Head Yaw (degrees)",
description:
"Left/right head rotation (-90° = right, +90° = left)",
default: 0.0,
minimum: -90.0,
maximum: 90.0,
step: 1.0,
},
headPitch: {
type: "number",
title: "Head Pitch (degrees)",
description: "Up/down head rotation (-25° = down, +25° = up)",
default: 0.0,
minimum: -25.0,
maximum: 25.0,
step: 1.0,
},
speed: {
type: "number",
title: "Movement Speed",
description: "Speed of head movement (0.1 = slow, 1.0 = fast)",
default: 0.3,
minimum: 0.1,
maximum: 1.0,
step: 0.1,
},
},
required: [],
},
implementation: {
type: "ros2_topic",
topic: "/joint_angles",
messageType: "naoqi_bridge_msgs/JointAnglesWithSpeed",
},
},
{
id: "nao_gesture",
name: "Perform Gesture",
description:
"Make NAO robot perform predefined gestures and animations",
category: "interaction",
icon: "hand",
parametersSchema: {
type: "object",
properties: {
gesture: {
type: "string",
title: "Gesture Type",
description: "Select a predefined gesture or animation",
enum: [
"wave",
"point_left",
"point_right",
"applause",
"thumbs_up",
],
default: "wave",
},
intensity: {
type: "number",
title: "Gesture Intensity",
description:
"Intensity of the gesture movement (0.5 = subtle, 1.0 = full)",
default: 0.8,
minimum: 0.3,
maximum: 1.0,
step: 0.1,
},
},
required: ["gesture"],
},
implementation: {
type: "ros2_service",
service: "/naoqi_driver/animation_player/run_animation",
serviceType: "naoqi_bridge_msgs/srv/SetString",
},
},
{
id: "nao_sensor_monitor",
name: "Monitor Sensors",
description: "Monitor NAO robot sensors for interaction detection",
category: "sensors",
icon: "activity",
parametersSchema: {
type: "object",
properties: {
sensorType: {
type: "string",
title: "Sensor Type",
description: "Which sensors to monitor",
enum: ["touch", "bumper", "sonar", "all"],
default: "touch",
},
duration: {
type: "number",
title: "Duration (seconds)",
description: "How long to monitor sensors",
default: 10,
minimum: 1,
maximum: 300,
},
},
required: ["sensorType"],
},
implementation: {
type: "ros2_subscription",
topics: [
"/naoqi_driver/bumper",
"/naoqi_driver/hand_touch",
"/naoqi_driver/head_touch",
],
},
},
{
id: "nao_emergency_stop",
name: "Emergency Stop",
description: "Immediately stop all robot movement for safety",
category: "safety",
icon: "stop-circle",
parametersSchema: {
type: "object",
properties: {
stopType: {
type: "string",
title: "Stop Type",
description: "Type of emergency stop",
enum: ["movement", "all"],
default: "all",
},
},
required: [],
},
implementation: {
type: "ros2_topic",
topic: "/cmd_vel",
messageType: "geometry_msgs/Twist",
},
},
{
id: "nao_wake_rest",
name: "Wake Up / Rest Robot",
description: "Wake up the robot or put it to rest position",
category: "system",
icon: "power",
parametersSchema: {
type: "object",
properties: {
action: {
type: "string",
title: "Action",
description: "Wake up robot or put to rest",
enum: ["wake", "rest"],
default: "wake",
},
},
required: ["action"],
},
implementation: {
type: "ros2_service",
service: "/naoqi_driver/motion/wake_up",
serviceType: "std_srvs/srv/Empty",
},
},
{
id: "nao_status_check",
name: "Check Robot Status",
description: "Get current robot status including battery and health",
category: "system",
icon: "info",
parametersSchema: {
type: "object",
properties: {
statusType: {
type: "string",
title: "Status Type",
description: "What status information to retrieve",
enum: ["basic", "battery", "sensors", "all"],
default: "basic",
},
},
required: ["statusType"],
},
implementation: {
type: "ros2_service",
service: "/naoqi_driver/get_robot_config",
serviceType: "naoqi_bridge_msgs/srv/GetRobotInfo",
},
},
];
const pluginData: InsertPlugin = {
repositoryId: repoId,
robotId: robotId,
name: "NAO6 Robot (Enhanced ROS2 Integration)",
version: "2.0.0",
description:
"Comprehensive NAO6 robot integration for HRIStudio experiments via ROS2. Provides full robot control including movement, speech synthesis, posture control, sensor monitoring, and safety features.",
author: "HRIStudio RoboLab Team",
repositoryUrl: "https://github.com/hristudio/nao6-ros2-plugins",
trustLevel: "official",
status: "active",
configurationSchema: {
type: "object",
properties: {
robotIp: {
type: "string",
default: "nao.local",
title: "Robot IP Address",
description: "IP address or hostname of the NAO6 robot",
},
robotPassword: {
type: "string",
default: "robolab",
title: "Robot Password",
description: "Password for robot authentication",
format: "password",
},
websocketUrl: {
type: "string",
default: "ws://localhost:9090",
title: "WebSocket URL",
description: "ROS bridge WebSocket URL for robot communication",
},
maxLinearVelocity: {
type: "number",
default: 0.2,
minimum: 0.01,
maximum: 0.5,
title: "Max Linear Velocity (m/s)",
description: "Maximum allowed linear movement speed for safety",
},
speechVolume: {
type: "number",
default: 0.7,
minimum: 0.1,
maximum: 1.0,
title: "Speech Volume",
description: "Default volume for speech synthesis",
},
enableSafetyMonitoring: {
type: "boolean",
default: true,
title: "Enable Safety Monitoring",
description:
"Enable automatic safety monitoring and emergency stops",
},
},
required: ["robotIp", "websocketUrl"],
},
actionDefinitions: actionDefinitions,
metadata: {
robotModel: "NAO V6.0",
manufacturer: "SoftBank Robotics",
naoqiVersion: "2.8.7.4",
ros2Distro: "humble",
launchPackage: "nao_launch",
capabilities: [
"bipedal_walking",
"speech_synthesis",
"head_movement",
"arm_gestures",
"touch_sensors",
"visual_sensors",
"posture_control",
],
tags: [
"nao6",
"ros2",
"speech",
"movement",
"sensors",
"hri",
"production",
],
},
};
if (existingPlugin.length > 0) {
await db
.update(plugins)
.set({
...pluginData,
updatedAt: new Date(),
})
.where(eq(plugins.id, existingPlugin[0]!.id));
console.log(`✅ Updated existing NAO6 plugin: ${existingPlugin[0]!.id}`);
} else {
const newPlugins = await db
.insert(plugins)
.values(pluginData)
.returning();
console.log(`✅ Created NAO6 plugin: ${newPlugins[0]!.id}`);
}
console.log("\n🎉 NAO6 plugin seeding completed successfully!");
console.log("\nNext steps:");
console.log("1. Install the plugin in a study via the HRIStudio interface");
console.log("2. Configure the robot IP and WebSocket URL");
console.log(
"3. Launch ROS integration: ros2 launch nao_launch nao6_production.launch.py",
);
console.log("4. Test robot actions in the experiment designer");
console.log("\n📊 Plugin Summary:");
console.log(` Robot: NAO6 (${robotId})`);
console.log(` Repository: NAO6 ROS2 Integration (${repoId})`);
console.log(` Actions: ${actionDefinitions.length} available`);
console.log(
" Categories: speech, movement, interaction, sensors, safety, system",
);
} catch (error) {
console.error("❌ Error seeding NAO6 plugin:", error);
throw error;
} finally {
await client.end();
}
}
// Run the seeding script
seedNAO6Plugin()
.then(() => {
console.log("✅ Database seeding completed");
process.exit(0);
})
.catch((error) => {
console.error("❌ Database seeding failed:", error);
process.exit(1);
});

0
scripts/test-seed-data.ts Normal file → Executable file
View File

View File

@@ -0,0 +1,561 @@
#!/bin/bash
#
# NAO6 HRIStudio Integration Verification Script
#
# This script performs comprehensive verification of the NAO6 integration with HRIStudio,
# checking all components from ROS2 workspace to database plugins and providing
# detailed status and next steps.
#
# Usage: ./verify-nao6-integration.sh [--robot-ip IP] [--verbose]
#
set -e
# =================================================================
# CONFIGURATION AND DEFAULTS
# =================================================================
NAO_IP="${1:-nao.local}"
VERBOSE=false
HRISTUDIO_DIR="${HOME}/Documents/Projects/hristudio"
ROS_WS="${HOME}/naoqi_ros2_ws"
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
PURPLE='\033[0;35m'
CYAN='\033[0;36m'
NC='\033[0m' # No Color
# =================================================================
# UTILITY FUNCTIONS
# =================================================================
log_info() {
echo -e "${BLUE}[INFO]${NC} $1"
}
log_success() {
echo -e "${GREEN}[✅ PASS]${NC} $1"
}
log_warning() {
echo -e "${YELLOW}[⚠️ WARN]${NC} $1"
}
log_error() {
echo -e "${RED}[❌ FAIL]${NC} $1"
}
log_step() {
echo -e "${PURPLE}[STEP]${NC} $1"
}
log_verbose() {
if [ "$VERBOSE" = true ]; then
echo -e "${CYAN}[DEBUG]${NC} $1"
fi
}
show_header() {
echo -e "${CYAN}"
echo "================================================================="
echo " NAO6 HRIStudio Integration Verification"
echo "================================================================="
echo -e "${NC}"
echo "Target Robot: $NAO_IP"
echo "HRIStudio: $HRISTUDIO_DIR"
echo "ROS Workspace: $ROS_WS"
echo ""
}
# =================================================================
# VERIFICATION FUNCTIONS
# =================================================================
check_prerequisites() {
log_step "Checking prerequisites and dependencies..."
local errors=0
# Check ROS2 installation
if command -v ros2 >/dev/null 2>&1; then
local ros_distro=$(ros2 --version 2>/dev/null | grep -o "humble\|iron\|rolling" || echo "unknown")
log_success "ROS2 found (distro: $ros_distro)"
else
log_error "ROS2 not found - install ROS2 Humble"
((errors++))
fi
# Check required tools
local tools=("ping" "ssh" "sshpass" "bun" "docker")
for tool in "${tools[@]}"; do
if command -v $tool >/dev/null 2>&1; then
log_success "$tool available"
else
log_warning "$tool not found (may be optional)"
fi
done
# Check ROS workspace
if [ -d "$ROS_WS" ]; then
log_success "NAOqi ROS2 workspace found"
if [ -f "$ROS_WS/install/setup.bash" ]; then
log_success "ROS workspace built and ready"
else
log_warning "ROS workspace not built - run: cd $ROS_WS && colcon build"
fi
else
log_error "NAOqi ROS2 workspace not found at $ROS_WS"
((errors++))
fi
# Check HRIStudio directory
if [ -d "$HRISTUDIO_DIR" ]; then
log_success "HRIStudio directory found"
if [ -f "$HRISTUDIO_DIR/package.json" ]; then
log_success "HRIStudio package configuration found"
else
log_warning "HRIStudio package.json not found"
fi
else
log_error "HRIStudio directory not found at $HRISTUDIO_DIR"
((errors++))
fi
return $errors
}
check_nao_launch_package() {
log_step "Verifying nao_launch package..."
local errors=0
local package_dir="$ROS_WS/src/nao_launch"
if [ -d "$package_dir" ]; then
log_success "nao_launch package directory found"
# Check launch files
local launch_files=(
"nao6_hristudio.launch.py"
"nao6_production.launch.py"
"nao6_hristudio_enhanced.launch.py"
)
for launch_file in "${launch_files[@]}"; do
if [ -f "$package_dir/launch/$launch_file" ]; then
log_success "Launch file: $launch_file"
else
log_warning "Missing launch file: $launch_file"
fi
done
# Check scripts
if [ -d "$package_dir/scripts" ]; then
log_success "Scripts directory found"
local scripts=("nao_control.py" "start_nao6_hristudio.sh")
for script in "${scripts[@]}"; do
if [ -f "$package_dir/scripts/$script" ]; then
log_success "Script: $script"
else
log_warning "Missing script: $script"
fi
done
else
log_warning "Scripts directory not found"
fi
# Check if package is built
if [ -f "$ROS_WS/install/nao_launch/share/nao_launch/package.xml" ]; then
log_success "nao_launch package built and installed"
else
log_warning "nao_launch package not built - run: cd $ROS_WS && colcon build --packages-select nao_launch"
fi
else
log_error "nao_launch package directory not found"
((errors++))
fi
return $errors
}
check_robot_connectivity() {
log_step "Testing NAO robot connectivity..."
local errors=0
# Test ping
log_verbose "Testing ping to $NAO_IP..."
if ping -c 2 -W 3 "$NAO_IP" >/dev/null 2>&1; then
log_success "Robot responds to ping"
else
log_error "Cannot ping robot at $NAO_IP - check network/IP"
((errors++))
return $errors
fi
# Test NAOqi port
log_verbose "Testing NAOqi service on port 9559..."
if timeout 5 bash -c "echo >/dev/tcp/$NAO_IP/9559" 2>/dev/null; then
log_success "NAOqi service accessible on port 9559"
else
log_error "Cannot connect to NAOqi on $NAO_IP:9559"
((errors++))
fi
# Test SSH (optional)
log_verbose "Testing SSH connectivity (optional)..."
if timeout 5 ssh -o StrictHostKeyChecking=no -o ConnectTimeout=3 -o BatchMode=yes nao@$NAO_IP echo "SSH test" >/dev/null 2>&1; then
log_success "SSH connectivity working"
else
log_warning "SSH connectivity failed - may need password for wake-up"
fi
return $errors
}
check_hristudio_database() {
log_step "Checking HRIStudio database and plugins..."
local errors=0
# Check if database is running
if docker ps | grep -q postgres || ss -ln | grep -q :5432 || ss -ln | grep -q :5140; then
log_success "Database appears to be running"
# Try to query database (requires HRIStudio to be set up)
cd "$HRISTUDIO_DIR" 2>/dev/null || true
if [ -f "$HRISTUDIO_DIR/.env" ] || [ -n "$DATABASE_URL" ]; then
log_success "Database configuration found"
# Check for NAO6 plugin (this would require running a query)
log_info "Database plugins check would require HRIStudio connection"
else
log_warning "Database configuration not found - check .env file"
fi
else
log_warning "Database not running - start with: docker compose up -d"
fi
return $errors
}
check_ros_dependencies() {
log_step "Checking ROS dependencies and packages..."
local errors=0
# Source ROS if available
if [ -f "/opt/ros/humble/setup.bash" ]; then
source /opt/ros/humble/setup.bash 2>/dev/null || true
log_success "ROS2 Humble environment sourced"
fi
# Check required ROS packages
local required_packages=(
"rosbridge_server"
"rosapi"
"std_msgs"
"geometry_msgs"
"sensor_msgs"
)
for package in "${required_packages[@]}"; do
if ros2 pkg list 2>/dev/null | grep -q "^$package$"; then
log_success "ROS package: $package"
else
log_warning "ROS package missing: $package"
fi
done
# Check NAOqi-specific packages
local naoqi_packages=(
"naoqi_driver"
"naoqi_bridge_msgs"
)
for package in "${naoqi_packages[@]}"; do
if [ -d "$ROS_WS/src/naoqi_driver2" ] || [ -d "$ROS_WS/install/$package" ]; then
log_success "NAOqi package: $package"
else
log_warning "NAOqi package not found: $package"
fi
done
return $errors
}
check_plugin_files() {
log_step "Checking HRIStudio plugin files..."
local errors=0
local plugin_dir="$HRISTUDIO_DIR/public/nao6-plugins"
if [ -d "$plugin_dir" ]; then
log_success "NAO6 plugins directory found"
# Check repository metadata
if [ -f "$plugin_dir/repository.json" ]; then
log_success "Repository metadata file found"
# Validate JSON
if command -v jq >/dev/null 2>&1; then
if jq empty "$plugin_dir/repository.json" 2>/dev/null; then
log_success "Repository metadata is valid JSON"
else
log_error "Repository metadata has invalid JSON"
((errors++))
fi
fi
else
log_warning "Repository metadata not found"
fi
# Check plugin definition
if [ -f "$plugin_dir/nao6-ros2-enhanced.json" ]; then
log_success "NAO6 plugin definition found"
# Validate JSON
if command -v jq >/dev/null 2>&1; then
if jq empty "$plugin_dir/nao6-ros2-enhanced.json" 2>/dev/null; then
local action_count=$(jq '.actionDefinitions | length' "$plugin_dir/nao6-ros2-enhanced.json" 2>/dev/null || echo "0")
log_success "Plugin definition valid with $action_count actions"
else
log_error "Plugin definition has invalid JSON"
((errors++))
fi
fi
else
log_warning "NAO6 plugin definition not found"
fi
else
log_warning "NAO6 plugins directory not found - plugins may be in database only"
fi
return $errors
}
show_integration_status() {
echo ""
echo -e "${CYAN}================================================================="
echo " INTEGRATION STATUS SUMMARY"
echo -e "=================================================================${NC}"
echo ""
echo -e "${GREEN}🤖 NAO6 Robot Integration Components:${NC}"
echo " ✅ ROS2 Workspace: $ROS_WS"
echo " ✅ nao_launch Package: Enhanced launch files and scripts"
echo " ✅ HRIStudio Plugin: Database integration with 9 actions"
echo " ✅ Plugin Repository: Local and remote plugin definitions"
echo ""
echo -e "${BLUE}🔧 Available Launch Configurations:${NC}"
echo " 📦 Production: ros2 launch nao_launch nao6_production.launch.py"
echo " 🔍 Enhanced: ros2 launch nao_launch nao6_hristudio_enhanced.launch.py"
echo " ⚡ Basic: ros2 launch nao_launch nao6_hristudio.launch.py"
echo ""
echo -e "${PURPLE}🎮 Robot Control Options:${NC}"
echo " 🖥️ Command Line: python3 scripts/nao_control.py --ip $NAO_IP"
echo " 🌐 Web Interface: http://localhost:3000/nao-test"
echo " 🧪 HRIStudio: Experiment designer with NAO6 actions"
echo ""
echo -e "${YELLOW}📋 Available Actions in HRIStudio:${NC}"
echo " 🗣️ Speech: Text-to-speech synthesis"
echo " 🚶 Movement: Walking, turning, positioning"
echo " 🧍 Posture: Stand, sit, crouch poses"
echo " 👀 Head: Gaze control and attention direction"
echo " 👋 Gestures: Wave, point, applause, custom animations"
echo " 📡 Sensors: Touch, bumper, sonar monitoring"
echo " 🛑 Safety: Emergency stop and status checking"
echo " ⚡ System: Wake/rest and robot management"
echo ""
}
show_next_steps() {
echo -e "${GREEN}🚀 Next Steps to Start Using NAO6 Integration:${NC}"
echo ""
echo "1. 📡 Start ROS Integration:"
echo " cd $ROS_WS && source install/setup.bash"
echo " ros2 launch nao_launch nao6_production.launch.py nao_ip:=$NAO_IP password:=robolab"
echo ""
echo "2. 🌐 Start HRIStudio:"
echo " cd $HRISTUDIO_DIR"
echo " bun dev"
echo ""
echo "3. 🧪 Test Integration:"
echo " • Open: http://localhost:3000/nao-test"
echo " • Click 'Connect' to establish WebSocket connection"
echo " • Try robot commands (speech, movement, etc.)"
echo ""
echo "4. 🔬 Create Experiments:"
echo " • Login to HRIStudio: sean@soconnor.dev / password123"
echo " • Go to Study → Plugins → Install NAO6 plugin"
echo " • Configure robot IP: $NAO_IP"
echo " • Design experiments using NAO6 actions"
echo ""
echo "5. 🛠️ Troubleshooting:"
echo " • Robot not responding: Wake up with chest button (3 seconds)"
echo " • Connection issues: Check network and robot IP"
echo " • WebSocket problems: Verify rosbridge is running"
echo " • Emergency stop: Use Ctrl+C or emergency action"
echo ""
}
show_comprehensive_summary() {
echo -e "${CYAN}================================================================="
echo " COMPREHENSIVE INTEGRATION SUMMARY"
echo -e "=================================================================${NC}"
echo ""
echo -e "${GREEN}✅ COMPLETED ENHANCEMENTS:${NC}"
echo ""
echo -e "${BLUE}📦 Enhanced nao_launch Package:${NC}"
echo " • Production-optimized launch files with safety features"
echo " • Comprehensive robot control and monitoring scripts"
echo " • Automatic wake-up and error recovery"
echo " • Performance-tuned sensor frequencies for HRIStudio"
echo " • Emergency stop and safety monitoring capabilities"
echo ""
echo -e "${BLUE}🔌 Enhanced Plugin Integration:${NC}"
echo " • Complete NAO6 plugin with 9 comprehensive actions"
echo " • Type-safe configuration schema for robot settings"
echo " • WebSocket integration for real-time robot control"
echo " • Safety parameters and velocity limits"
echo " • Comprehensive action parameter validation"
echo ""
echo -e "${BLUE}🛠️ Utility Scripts and Tools:${NC}"
echo " • nao_control.py - Command-line robot control and monitoring"
echo " • start_nao6_hristudio.sh - Comprehensive startup automation"
echo " • Enhanced CMakeLists.txt and package metadata"
echo " • Database seeding scripts for plugin installation"
echo " • Comprehensive documentation and troubleshooting guides"
echo ""
echo -e "${BLUE}📚 Documentation and Guides:${NC}"
echo " • Complete README with setup and usage instructions"
echo " • Plugin repository metadata and action definitions"
echo " • Safety guidelines and emergency procedures"
echo " • Troubleshooting guide for common issues"
echo " • Integration examples and common use cases"
echo ""
echo -e "${PURPLE}🎯 Production-Ready Features:${NC}"
echo " • Automatic robot wake-up on experiment start"
echo " • Safety monitoring with emergency stop capabilities"
echo " • Optimized sensor publishing for experimental workflows"
echo " • Robust error handling and recovery mechanisms"
echo " • Performance tuning for stable long-running experiments"
echo " • Comprehensive logging and status monitoring"
echo ""
echo -e "${YELLOW}🔬 Research Capabilities:${NC}"
echo " • Complete speech synthesis with volume/speed control"
echo " • Precise movement control with safety limits"
echo " • Posture control for experimental positioning"
echo " • Head movement for gaze and attention studies"
echo " • Gesture library for social interaction research"
echo " • Comprehensive sensor monitoring for interaction detection"
echo " • Real-time status monitoring for experimental validity"
echo ""
}
# =================================================================
# MAIN EXECUTION
# =================================================================
main() {
# Parse arguments
while [[ $# -gt 0 ]]; do
case $1 in
--robot-ip)
NAO_IP="$2"
shift 2
;;
--verbose)
VERBOSE=true
shift
;;
--help)
echo "Usage: $0 [--robot-ip IP] [--verbose]"
exit 0
;;
*)
NAO_IP="$1"
shift
;;
esac
done
# Show header
show_header
# Run verification checks
local total_errors=0
check_prerequisites
total_errors=$((total_errors + $?))
check_nao_launch_package
total_errors=$((total_errors + $?))
check_robot_connectivity
total_errors=$((total_errors + $?))
check_hristudio_database
total_errors=$((total_errors + $?))
check_ros_dependencies
total_errors=$((total_errors + $?))
check_plugin_files
total_errors=$((total_errors + $?))
# Show results
echo ""
if [ $total_errors -eq 0 ]; then
log_success "All verification checks passed! 🎉"
show_integration_status
show_next_steps
show_comprehensive_summary
echo -e "${GREEN}🎊 NAO6 HRIStudio Integration is ready for use!${NC}"
echo ""
else
log_warning "Verification completed with $total_errors issues"
echo ""
echo -e "${YELLOW}⚠️ Some components need attention before full integration.${NC}"
echo "Please resolve the issues above and run verification again."
echo ""
show_next_steps
fi
echo -e "${CYAN}================================================================="
echo " VERIFICATION COMPLETE"
echo -e "=================================================================${NC}"
}
# Run main function
main "$@"

0
src/app/(dashboard)/admin/page.tsx Normal file → Executable file
View File

0
src/app/(dashboard)/admin/repositories/page.tsx Normal file → Executable file
View File

0
src/app/(dashboard)/analytics/page.tsx Normal file → Executable file
View File

View File

@@ -0,0 +1,513 @@
"use client";
import { useState, useEffect, useRef } from "react";
import { Button } from "~/components/ui/button";
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "~/components/ui/card";
import { Input } from "~/components/ui/input";
import { Label } from "~/components/ui/label";
import { Textarea } from "~/components/ui/textarea";
import { Badge } from "~/components/ui/badge";
import { Separator } from "~/components/ui/separator";
import { Alert, AlertDescription } from "~/components/ui/alert";
import { PageHeader } from "~/components/ui/page-header";
import { PageLayout } from "~/components/ui/page-layout";
import { ScrollArea } from "~/components/ui/scroll-area";
import {
Wifi,
WifiOff,
AlertTriangle,
CheckCircle,
Play,
Square,
Trash2,
Copy,
} from "lucide-react";
export default function DebugPage() {
const [connectionStatus, setConnectionStatus] = useState<
"disconnected" | "connecting" | "connected" | "error"
>("disconnected");
const [rosSocket, setRosSocket] = useState<WebSocket | null>(null);
const [logs, setLogs] = useState<string[]>([]);
const [messages, setMessages] = useState<any[]>([]);
const [testMessage, setTestMessage] = useState("");
const [selectedTopic, setSelectedTopic] = useState("/speech");
const [messageType, setMessageType] = useState("std_msgs/String");
const [lastError, setLastError] = useState<string | null>(null);
const [connectionAttempts, setConnectionAttempts] = useState(0);
const logsEndRef = useRef<HTMLDivElement>(null);
const messagesEndRef = useRef<HTMLDivElement>(null);
const ROS_BRIDGE_URL = "ws://134.82.159.25:9090";
const addLog = (message: string, type: "info" | "error" | "success" = "info") => {
const timestamp = new Date().toLocaleTimeString();
const logEntry = `[${timestamp}] [${type.toUpperCase()}] ${message}`;
setLogs((prev) => [...prev.slice(-99), logEntry]);
console.log(logEntry);
};
const addMessage = (message: any, direction: "sent" | "received") => {
const timestamp = new Date().toLocaleTimeString();
setMessages((prev) => [
...prev.slice(-49),
{
timestamp,
direction,
data: message,
},
]);
};
useEffect(() => {
logsEndRef.current?.scrollIntoView({ behavior: "smooth" });
}, [logs]);
useEffect(() => {
messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
}, [messages]);
const connectToRos = () => {
if (rosSocket?.readyState === WebSocket.OPEN) return;
setConnectionStatus("connecting");
setConnectionAttempts((prev) => prev + 1);
setLastError(null);
addLog(`Attempting connection #${connectionAttempts + 1} to ${ROS_BRIDGE_URL}`);
const socket = new WebSocket(ROS_BRIDGE_URL);
// Connection timeout
const timeout = setTimeout(() => {
if (socket.readyState === WebSocket.CONNECTING) {
addLog("Connection timeout (10s) - closing socket", "error");
socket.close();
}
}, 10000);
socket.onopen = () => {
clearTimeout(timeout);
setConnectionStatus("connected");
setRosSocket(socket);
setLastError(null);
addLog("✅ WebSocket connection established successfully", "success");
// Test basic functionality by advertising
const advertiseMsg = {
op: "advertise",
topic: "/hristudio_debug",
type: "std_msgs/String",
};
socket.send(JSON.stringify(advertiseMsg));
addMessage(advertiseMsg, "sent");
};
socket.onmessage = (event) => {
try {
const data = JSON.parse(event.data);
addMessage(data, "received");
if (data.level === "error") {
addLog(`ROS Error: ${data.msg}`, "error");
} else if (data.op === "status") {
addLog(`Status: ${data.msg} (Level: ${data.level})`);
} else {
addLog(`Received: ${data.op || "unknown"} operation`);
}
} catch (error) {
addLog(`Failed to parse message: ${error}`, "error");
addMessage({ raw: event.data, error: String(error) }, "received");
}
};
socket.onclose = (event) => {
clearTimeout(timeout);
const wasConnected = connectionStatus === "connected";
setConnectionStatus("disconnected");
setRosSocket(null);
let reason = "Unknown reason";
if (event.code === 1000) {
reason = "Normal closure";
addLog(`Connection closed normally: ${event.reason || reason}`);
} else if (event.code === 1006) {
reason = "Connection lost/refused";
setLastError("ROS Bridge server not responding - check if rosbridge_server is running");
addLog(`❌ Connection failed: ${reason} (${event.code})`, "error");
} else if (event.code === 1011) {
reason = "Server error";
setLastError("ROS Bridge server encountered an error");
addLog(`❌ Server error: ${reason} (${event.code})`, "error");
} else {
reason = `Code ${event.code}`;
setLastError(`Connection closed with code ${event.code}: ${event.reason || "No reason given"}`);
addLog(`❌ Connection closed: ${reason}`, "error");
}
if (wasConnected) {
addLog("Connection was working but lost - check network/server");
}
};
socket.onerror = (error) => {
clearTimeout(timeout);
setConnectionStatus("error");
const errorMsg = "WebSocket error occurred";
setLastError(errorMsg);
addLog(`${errorMsg}`, "error");
console.error("WebSocket error details:", error);
};
};
const disconnectFromRos = () => {
if (rosSocket) {
addLog("Manually closing connection");
rosSocket.close(1000, "Manual disconnect");
}
};
const sendTestMessage = () => {
if (!rosSocket || connectionStatus !== "connected") {
addLog("Cannot send message - not connected", "error");
return;
}
try {
let message: any;
if (selectedTopic === "/speech" && messageType === "std_msgs/String") {
message = {
op: "publish",
topic: "/speech",
type: "std_msgs/String",
msg: { data: testMessage || "Hello from debug page" },
};
} else if (selectedTopic === "/cmd_vel") {
message = {
op: "publish",
topic: "/cmd_vel",
type: "geometry_msgs/Twist",
msg: {
linear: { x: 0.1, y: 0, z: 0 },
angular: { x: 0, y: 0, z: 0 },
},
};
} else {
// Generic message
message = {
op: "publish",
topic: selectedTopic,
type: messageType,
msg: { data: testMessage || "test" },
};
}
rosSocket.send(JSON.stringify(message));
addMessage(message, "sent");
addLog(`Sent message to ${selectedTopic}`, "success");
} catch (error) {
addLog(`Failed to send message: ${error}`, "error");
}
};
const subscribeToTopic = () => {
if (!rosSocket || connectionStatus !== "connected") {
addLog("Cannot subscribe - not connected", "error");
return;
}
const subscribeMsg = {
op: "subscribe",
topic: selectedTopic,
type: messageType,
};
rosSocket.send(JSON.stringify(subscribeMsg));
addMessage(subscribeMsg, "sent");
addLog(`Subscribed to ${selectedTopic}`, "success");
};
const clearLogs = () => {
setLogs([]);
setMessages([]);
addLog("Logs cleared");
};
const copyLogs = () => {
const logText = logs.join("\n");
navigator.clipboard.writeText(logText);
addLog("Logs copied to clipboard", "success");
};
const getStatusIcon = () => {
switch (connectionStatus) {
case "connected":
return <CheckCircle className="h-4 w-4 text-green-600" />;
case "connecting":
return <Wifi className="h-4 w-4 animate-pulse text-blue-600" />;
case "error":
return <AlertTriangle className="h-4 w-4 text-red-600" />;
default:
return <WifiOff className="h-4 w-4 text-gray-400" />;
}
};
const commonTopics = [
{ topic: "/speech", type: "std_msgs/String" },
{ topic: "/cmd_vel", type: "geometry_msgs/Twist" },
{ topic: "/joint_angles", type: "naoqi_bridge_msgs/JointAnglesWithSpeed" },
{ topic: "/naoqi_driver/joint_states", type: "sensor_msgs/JointState" },
{ topic: "/naoqi_driver/bumper", type: "naoqi_bridge_msgs/Bumper" },
];
return (
<PageLayout>
<PageHeader
title="ROS Bridge WebSocket Debug"
description="Debug and test WebSocket connection to ROS Bridge server"
/>
<div className="grid gap-6 md:grid-cols-2">
{/* Connection Control */}
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2">
{getStatusIcon()}
Connection Control
</CardTitle>
<CardDescription>
Connect to ROS Bridge at {ROS_BRIDGE_URL}
</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<div className="flex items-center gap-2">
<Badge
variant={
connectionStatus === "connected"
? "default"
: connectionStatus === "error"
? "destructive"
: "outline"
}
>
{connectionStatus.toUpperCase()}
</Badge>
<span className="text-sm text-muted-foreground">
Attempts: {connectionAttempts}
</span>
</div>
{lastError && (
<Alert variant="destructive">
<AlertTriangle className="h-4 w-4" />
<AlertDescription className="text-sm">{lastError}</AlertDescription>
</Alert>
)}
<div className="flex gap-2">
{connectionStatus !== "connected" ? (
<Button
onClick={connectToRos}
disabled={connectionStatus === "connecting"}
className="flex-1"
>
<Play className="mr-2 h-4 w-4" />
{connectionStatus === "connecting" ? "Connecting..." : "Connect"}
</Button>
) : (
<Button
onClick={disconnectFromRos}
variant="outline"
className="flex-1"
>
<Square className="mr-2 h-4 w-4" />
Disconnect
</Button>
)}
</div>
<Separator />
{/* Message Testing */}
<div className="space-y-3">
<Label>Test Messages</Label>
<div className="grid grid-cols-2 gap-2">
<div>
<Label htmlFor="topic" className="text-xs">
Topic
</Label>
<Input
id="topic"
value={selectedTopic}
onChange={(e) => setSelectedTopic(e.target.value)}
placeholder="/speech"
/>
</div>
<div>
<Label htmlFor="msgType" className="text-xs">
Message Type
</Label>
<Input
id="msgType"
value={messageType}
onChange={(e) => setMessageType(e.target.value)}
placeholder="std_msgs/String"
/>
</div>
</div>
<div>
<Label htmlFor="testMsg" className="text-xs">
Test Message
</Label>
<Input
id="testMsg"
value={testMessage}
onChange={(e) => setTestMessage(e.target.value)}
placeholder="Hello from debug page"
/>
</div>
<div className="flex gap-2">
<Button
onClick={sendTestMessage}
disabled={connectionStatus !== "connected"}
size="sm"
className="flex-1"
>
Publish
</Button>
<Button
onClick={subscribeToTopic}
disabled={connectionStatus !== "connected"}
size="sm"
variant="outline"
className="flex-1"
>
Subscribe
</Button>
</div>
{/* Quick Topic Buttons */}
<div className="space-y-1">
<Label className="text-xs">Quick Topics</Label>
<div className="grid grid-cols-1 gap-1">
{commonTopics.map((item) => (
<Button
key={item.topic}
onClick={() => {
setSelectedTopic(item.topic);
setMessageType(item.type);
}}
variant="ghost"
size="sm"
className="justify-start text-xs"
>
{item.topic}
</Button>
))}
</div>
</div>
</div>
</CardContent>
</Card>
{/* Connection Logs */}
<Card>
<CardHeader>
<CardTitle className="flex items-center justify-between">
Connection Logs
<div className="flex gap-1">
<Button onClick={copyLogs} size="sm" variant="ghost">
<Copy className="h-4 w-4" />
</Button>
<Button onClick={clearLogs} size="sm" variant="ghost">
<Trash2 className="h-4 w-4" />
</Button>
</div>
</CardTitle>
<CardDescription>
Real-time connection and message logs ({logs.length}/100)
</CardDescription>
</CardHeader>
<CardContent>
<ScrollArea className="h-64 w-full rounded border p-2">
<div className="space-y-1 font-mono text-xs">
{logs.map((log, index) => (
<div
key={index}
className={`${
log.includes("ERROR")
? "text-red-600"
: log.includes("SUCCESS")
? "text-green-600"
: "text-slate-600"
}`}
>
{log}
</div>
))}
{logs.length === 0 && (
<div className="text-muted-foreground">No logs yet...</div>
)}
<div ref={logsEndRef} />
</div>
</ScrollArea>
</CardContent>
</Card>
{/* Message Inspector */}
<Card className="md:col-span-2">
<CardHeader>
<CardTitle>Message Inspector</CardTitle>
<CardDescription>
Raw WebSocket messages sent and received ({messages.length}/50)
</CardDescription>
</CardHeader>
<CardContent>
<ScrollArea className="h-64 w-full rounded border p-2">
<div className="space-y-2">
{messages.map((msg, index) => (
<div
key={index}
className={`rounded p-2 text-xs ${
msg.direction === "sent"
? "bg-blue-50 border-l-2 border-blue-400"
: "bg-green-50 border-l-2 border-green-400"
}`}
>
<div className="flex items-center justify-between mb-1">
<Badge
variant={msg.direction === "sent" ? "default" : "secondary"}
className="text-xs"
>
{msg.direction === "sent" ? "SENT" : "RECEIVED"}
</Badge>
<span className="text-muted-foreground">{msg.timestamp}</span>
</div>
<pre className="whitespace-pre-wrap text-xs">
{JSON.stringify(msg.data, null, 2)}
</pre>
</div>
))}
{messages.length === 0 && (
<div className="text-center text-muted-foreground py-8">
No messages yet. Connect and send a test message to see data here.
</div>
)}
<div ref={messagesEndRef} />
</div>
</ScrollArea>
</CardContent>
</Card>
</div>
</PageLayout>
);
}

0
src/app/(dashboard)/experiments/[id]/edit/page.tsx Normal file → Executable file
View File

0
src/app/(dashboard)/experiments/[id]/page.tsx Normal file → Executable file
View File

0
src/app/(dashboard)/experiments/page.tsx Normal file → Executable file
View File

0
src/app/(dashboard)/layout.tsx Normal file → Executable file
View File

3
src/app/(dashboard)/nao-test/page.tsx Normal file → Executable file
View File

@@ -64,7 +64,8 @@ export default function NaoTestPage() {
const [sensorData, setSensorData] = useState<any>({}); const [sensorData, setSensorData] = useState<any>({});
const logsEndRef = useRef<HTMLDivElement>(null); const logsEndRef = useRef<HTMLDivElement>(null);
const ROS_BRIDGE_URL = "ws://134.82.159.25:9090"; const ROS_BRIDGE_URL =
process.env.NEXT_PUBLIC_ROS_BRIDGE_URL || "ws://localhost:9090";
const addLog = (message: string) => { const addLog = (message: string) => {
const timestamp = new Date().toLocaleTimeString(); const timestamp = new Date().toLocaleTimeString();

0
src/app/(dashboard)/not-found.tsx Normal file → Executable file
View File

0
src/app/(dashboard)/participants/page.tsx Normal file → Executable file
View File

0
src/app/(dashboard)/plugins/browse/page.tsx Normal file → Executable file
View File

0
src/app/(dashboard)/plugins/page.tsx Normal file → Executable file
View File

0
src/app/(dashboard)/profile/page.tsx Normal file → Executable file
View File

0
src/app/(dashboard)/studies/[id]/analytics/page.tsx Normal file → Executable file
View File

0
src/app/(dashboard)/studies/[id]/edit/page.tsx Normal file → Executable file
View File

View File

@@ -48,7 +48,7 @@ export function DesignerPageClient({
}, },
{ {
label: experiment.name, label: experiment.name,
href: `/experiments/${experiment.id}`, href: `/studies/${experiment.study.id}/experiments/${experiment.id}`,
}, },
{ {
label: "Designer", label: "Designer",

View File

@@ -11,7 +11,7 @@ import { DesignerPageClient } from "./DesignerPageClient";
interface ExperimentDesignerPageProps { interface ExperimentDesignerPageProps {
params: Promise<{ params: Promise<{
id: string; experimentId: string;
}>; }>;
} }
@@ -20,7 +20,7 @@ export default async function ExperimentDesignerPage({
}: ExperimentDesignerPageProps) { }: ExperimentDesignerPageProps) {
try { try {
const resolvedParams = await params; const resolvedParams = await params;
const experiment = await api.experiments.get({ id: resolvedParams.id }); const experiment = await api.experiments.get({ id: resolvedParams.experimentId });
if (!experiment) { if (!experiment) {
notFound(); notFound();
@@ -36,13 +36,13 @@ export default async function ExperimentDesignerPage({
// Only pass initialDesign if there's existing visual design data // Only pass initialDesign if there's existing visual design data
let initialDesign: let initialDesign:
| { | {
id: string; id: string;
name: string; name: string;
description: string; description: string;
steps: ExperimentStep[]; steps: ExperimentStep[];
version: number; version: number;
lastSaved: Date; lastSaved: Date;
} }
| undefined; | undefined;
if (existingDesign?.steps && existingDesign.steps.length > 0) { if (existingDesign?.steps && existingDesign.steps.length > 0) {
@@ -258,7 +258,7 @@ export async function generateMetadata({
}> { }> {
try { try {
const resolvedParams = await params; const resolvedParams = await params;
const experiment = await api.experiments.get({ id: resolvedParams.id }); const experiment = await api.experiments.get({ id: resolvedParams.experimentId });
return { return {
title: `${experiment?.name} - Designer | HRIStudio`, title: `${experiment?.name} - Designer | HRIStudio`,

View File

0
src/app/(dashboard)/studies/[id]/experiments/page.tsx Normal file → Executable file
View File

17
src/app/(dashboard)/studies/[id]/page.tsx Normal file → Executable file
View File

@@ -185,7 +185,7 @@ export default function StudyDetailPage({ params }: StudyDetailPageProps) {
</Link> </Link>
</Button> </Button>
<Button asChild> <Button asChild>
<Link href={`/experiments/new?studyId=${study.id}`}> <Link href={`/studies/${study.id}/experiments/new`}>
<Plus className="mr-2 h-4 w-4" /> <Plus className="mr-2 h-4 w-4" />
New Experiment New Experiment
</Link> </Link>
@@ -232,7 +232,7 @@ export default function StudyDetailPage({ params }: StudyDetailPageProps) {
description="Design and manage experimental protocols for this study" description="Design and manage experimental protocols for this study"
actions={ actions={
<Button asChild variant="outline" size="sm"> <Button asChild variant="outline" size="sm">
<Link href={`/experiments/new?studyId=${study.id}`}> <Link href={`/studies/${study.id}/experiments/new`}>
<Plus className="mr-2 h-4 w-4" /> <Plus className="mr-2 h-4 w-4" />
Add Experiment Add Experiment
</Link> </Link>
@@ -246,7 +246,7 @@ export default function StudyDetailPage({ params }: StudyDetailPageProps) {
description="Create your first experiment to start designing research protocols" description="Create your first experiment to start designing research protocols"
action={ action={
<Button asChild> <Button asChild>
<Link href={`/experiments/new?studyId=${study.id}`}> <Link href={`/studies/${study.id}/experiments/new`}>
Create First Experiment Create First Experiment
</Link> </Link>
</Button> </Button>
@@ -263,20 +263,19 @@ export default function StudyDetailPage({ params }: StudyDetailPageProps) {
<div className="flex items-center space-x-3"> <div className="flex items-center space-x-3">
<h4 className="font-medium"> <h4 className="font-medium">
<Link <Link
href={`/experiments/${experiment.id}`} href={`/studies/${study.id}/experiments/${experiment.id}`}
className="hover:underline" className="hover:underline"
> >
{experiment.name} {experiment.name}
</Link> </Link>
</h4> </h4>
<span <span
className={`inline-flex items-center rounded-full px-2 py-1 text-xs font-medium ${ className={`inline-flex items-center rounded-full px-2 py-1 text-xs font-medium ${experiment.status === "draft"
experiment.status === "draft"
? "bg-gray-100 text-gray-800" ? "bg-gray-100 text-gray-800"
: experiment.status === "ready" : experiment.status === "ready"
? "bg-green-100 text-green-800" ? "bg-green-100 text-green-800"
: "bg-blue-100 text-blue-800" : "bg-blue-100 text-blue-800"
}`} }`}
> >
{experiment.status} {experiment.status}
</span> </span>
@@ -300,12 +299,12 @@ export default function StudyDetailPage({ params }: StudyDetailPageProps) {
</div> </div>
<div className="flex items-center space-x-2"> <div className="flex items-center space-x-2">
<Button asChild variant="outline" size="sm"> <Button asChild variant="outline" size="sm">
<Link href={`/experiments/${experiment.id}/designer`}> <Link href={`/studies/${study.id}/experiments/${experiment.id}/designer`}>
Design Design
</Link> </Link>
</Button> </Button>
<Button asChild variant="outline" size="sm"> <Button asChild variant="outline" size="sm">
<Link href={`/experiments/${experiment.id}`}>View</Link> <Link href={`/studies/${study.id}/experiments/${experiment.id}`}>View</Link>
</Button> </Button>
</div> </div>
</div> </div>

View File

0
src/app/(dashboard)/studies/[id]/participants/page.tsx Normal file → Executable file
View File

View File

0
src/app/(dashboard)/studies/[id]/plugins/page.tsx Normal file → Executable file
View File

View File

View File

Some files were not shown because too many files have changed in this diff Show More