feat: Complete NAO6 ROS2 integration for HRIStudio

🤖 Full NAO6 Robot Integration with ROS2 and WebSocket Control

## New Features
- **NAO6 Test Interface**: Real-time robot control via web browser at /nao-test
- **ROS2 Integration**: Complete naoqi_driver2 + rosbridge setup with launch files
- **WebSocket Control**: Direct robot control through HRIStudio web interface
- **Plugin System**: NAO6 robot plugins for movement, speech, and sensors
- **Database Integration**: Updated seed data with NAO6 robot and plugin definitions

## Key Components Added
- **Web Interface**: src/app/(dashboard)/nao-test/page.tsx - Complete robot control dashboard
- **Plugin Repository**: public/nao6-plugins/ - Local NAO6 plugin definitions
- **Database Updates**: Updated robots table with ROS2 protocol and enhanced capabilities
- **Comprehensive Documentation**: Complete setup, troubleshooting, and quick reference guides

## Documentation
- **Complete Integration Guide**: docs/nao6-integration-complete-guide.md (630 lines)
- **Quick Reference**: docs/nao6-quick-reference.md - Essential commands and troubleshooting
- **Updated Setup Guide**: Enhanced docs/nao6-ros2-setup.md with critical notes
- **Updated Main Docs**: docs/README.md with robot integration section

## Robot Capabilities
-  **Speech Control**: Text-to-speech with emotion and language support
-  **Movement Control**: Walking, turning, stopping with configurable speeds
-  **Head Control**: Precise yaw/pitch positioning with sliders
-  **Sensor Monitoring**: Joint states, touch sensors, sonar, cameras, IMU
-  **Safety Features**: Emergency stop, movement limits, real-time monitoring
-  **Real-time Data**: Live sensor data streaming through WebSocket

## Critical Discovery
**Robot Wake-Up Requirement**: NAO robots start in safe mode with loose joints and must be explicitly awakened via SSH before movement commands work. This is now documented with automated solutions.

## Technical Implementation
- **ROS2 Humble**: Complete naoqi_driver2 integration with rosbridge WebSocket server
- **Topic Mapping**: Correct namespace handling for control vs. sensor topics
- **Plugin Architecture**: Extensible NAO6 action definitions with parameter validation
- **Database Schema**: Enhanced robots table with comprehensive NAO6 capabilities
- **Import Consistency**: Fixed React import aliases to use ~ consistently

## Testing & Verification
-  Tested with NAO V6.0 / NAOqi 2.8.7.4 / ROS2 Humble
-  Complete end-to-end testing from web interface to robot movement
-  Comprehensive troubleshooting procedures documented
-  Production-ready launch scripts and deployment guides

## Production Ready
This integration is fully tested and production-ready for Human-Robot Interaction research with complete documentation, safety guidelines, and troubleshooting procedures.
This commit is contained in:
2025-10-16 17:37:52 -04:00
parent 816b2b9e31
commit c206f86047
11 changed files with 2703 additions and 8 deletions

View File

@@ -0,0 +1,7 @@
[
"nao6-movement.json",
"nao6-speech.json",
"nao6-sensors.json",
"nao6-vision.json",
"nao6-interaction.json"
]

View File

@@ -0,0 +1,342 @@
{
"name": "NAO6 Movement Control",
"version": "1.0.0",
"description": "Complete movement control for NAO6 robot including walking, turning, and joint manipulation",
"platform": "NAO6",
"category": "movement",
"manufacturer": {
"name": "SoftBank Robotics",
"website": "https://www.softbankrobotics.com"
},
"documentation": {
"mainUrl": "https://docs.hristudio.com/robots/nao6/movement",
"quickStart": "https://docs.hristudio.com/robots/nao6/movement/quickstart"
},
"ros2Config": {
"namespace": "/naoqi_driver",
"topics": {
"cmd_vel": {
"type": "geometry_msgs/Twist",
"description": "Velocity commands for robot base movement"
},
"joint_angles": {
"type": "naoqi_bridge_msgs/JointAnglesWithSpeed",
"description": "Individual joint angle control with speed"
},
"joint_states": {
"type": "sensor_msgs/JointState",
"description": "Current joint positions and velocities"
}
}
},
"actions": [
{
"id": "walk_forward",
"name": "Walk Forward",
"description": "Make the robot walk forward at specified speed",
"category": "movement",
"parameters": [
{
"name": "speed",
"type": "number",
"description": "Walking speed in m/s",
"required": true,
"min": 0.01,
"max": 0.3,
"default": 0.1,
"step": 0.01
},
{
"name": "duration",
"type": "number",
"description": "Duration to walk in seconds (0 = indefinite)",
"required": false,
"min": 0,
"max": 30,
"default": 0,
"step": 0.1
}
],
"implementation": {
"topic": "/naoqi_driver/cmd_vel",
"messageType": "geometry_msgs/Twist",
"messageTemplate": {
"linear": { "x": "{{speed}}", "y": 0, "z": 0 },
"angular": { "x": 0, "y": 0, "z": 0 }
}
}
},
{
"id": "walk_backward",
"name": "Walk Backward",
"description": "Make the robot walk backward at specified speed",
"category": "movement",
"parameters": [
{
"name": "speed",
"type": "number",
"description": "Walking speed in m/s",
"required": true,
"min": 0.01,
"max": 0.3,
"default": 0.1,
"step": 0.01
},
{
"name": "duration",
"type": "number",
"description": "Duration to walk in seconds (0 = indefinite)",
"required": false,
"min": 0,
"max": 30,
"default": 0,
"step": 0.1
}
],
"implementation": {
"topic": "/naoqi_driver/cmd_vel",
"messageType": "geometry_msgs/Twist",
"messageTemplate": {
"linear": { "x": "-{{speed}}", "y": 0, "z": 0 },
"angular": { "x": 0, "y": 0, "z": 0 }
}
}
},
{
"id": "turn_left",
"name": "Turn Left",
"description": "Make the robot turn left at specified angular speed",
"category": "movement",
"parameters": [
{
"name": "speed",
"type": "number",
"description": "Angular speed in rad/s",
"required": true,
"min": 0.1,
"max": 1.0,
"default": 0.3,
"step": 0.1
},
{
"name": "duration",
"type": "number",
"description": "Duration to turn in seconds (0 = indefinite)",
"required": false,
"min": 0,
"max": 30,
"default": 0,
"step": 0.1
}
],
"implementation": {
"topic": "/naoqi_driver/cmd_vel",
"messageType": "geometry_msgs/Twist",
"messageTemplate": {
"linear": { "x": 0, "y": 0, "z": 0 },
"angular": { "x": 0, "y": 0, "z": "{{speed}}" }
}
}
},
{
"id": "turn_right",
"name": "Turn Right",
"description": "Make the robot turn right at specified angular speed",
"category": "movement",
"parameters": [
{
"name": "speed",
"type": "number",
"description": "Angular speed in rad/s",
"required": true,
"min": 0.1,
"max": 1.0,
"default": 0.3,
"step": 0.1
},
{
"name": "duration",
"type": "number",
"description": "Duration to turn in seconds (0 = indefinite)",
"required": false,
"min": 0,
"max": 30,
"default": 0,
"step": 0.1
}
],
"implementation": {
"topic": "/naoqi_driver/cmd_vel",
"messageType": "geometry_msgs/Twist",
"messageTemplate": {
"linear": { "x": 0, "y": 0, "z": 0 },
"angular": { "x": 0, "y": 0, "z": "-{{speed}}" }
}
}
},
{
"id": "stop_movement",
"name": "Stop Movement",
"description": "Immediately stop all robot movement",
"category": "movement",
"parameters": [],
"implementation": {
"topic": "/naoqi_driver/cmd_vel",
"messageType": "geometry_msgs/Twist",
"messageTemplate": {
"linear": { "x": 0, "y": 0, "z": 0 },
"angular": { "x": 0, "y": 0, "z": 0 }
}
}
},
{
"id": "move_head",
"name": "Move Head",
"description": "Control head orientation (yaw and pitch)",
"category": "movement",
"parameters": [
{
"name": "yaw",
"type": "number",
"description": "Head yaw angle in radians",
"required": true,
"min": -2.09,
"max": 2.09,
"default": 0,
"step": 0.1
},
{
"name": "pitch",
"type": "number",
"description": "Head pitch angle in radians",
"required": true,
"min": -0.67,
"max": 0.51,
"default": 0,
"step": 0.1
},
{
"name": "speed",
"type": "number",
"description": "Movement speed (0.1 = slow, 1.0 = fast)",
"required": false,
"min": 0.1,
"max": 1.0,
"default": 0.3,
"step": 0.1
}
],
"implementation": {
"topic": "/naoqi_driver/joint_angles",
"messageType": "naoqi_bridge_msgs/JointAnglesWithSpeed",
"messageTemplate": {
"joint_names": ["HeadYaw", "HeadPitch"],
"joint_angles": ["{{yaw}}", "{{pitch}}"],
"speed": "{{speed}}"
}
}
},
{
"id": "move_arm",
"name": "Move Arm",
"description": "Control arm joint positions",
"category": "movement",
"parameters": [
{
"name": "arm",
"type": "select",
"description": "Which arm to control",
"required": true,
"options": [
{ "value": "left", "label": "Left Arm" },
{ "value": "right", "label": "Right Arm" }
],
"default": "right"
},
{
"name": "shoulder_pitch",
"type": "number",
"description": "Shoulder pitch angle in radians",
"required": true,
"min": -2.09,
"max": 2.09,
"default": 1.4,
"step": 0.1
},
{
"name": "shoulder_roll",
"type": "number",
"description": "Shoulder roll angle in radians",
"required": true,
"min": -0.31,
"max": 1.33,
"default": 0.2,
"step": 0.1
},
{
"name": "elbow_yaw",
"type": "number",
"description": "Elbow yaw angle in radians",
"required": true,
"min": -2.09,
"max": 2.09,
"default": 0,
"step": 0.1
},
{
"name": "elbow_roll",
"type": "number",
"description": "Elbow roll angle in radians",
"required": true,
"min": -1.54,
"max": -0.03,
"default": -0.5,
"step": 0.1
},
{
"name": "speed",
"type": "number",
"description": "Movement speed (0.1 = slow, 1.0 = fast)",
"required": false,
"min": 0.1,
"max": 1.0,
"default": 0.3,
"step": 0.1
}
],
"implementation": {
"topic": "/naoqi_driver/joint_angles",
"messageType": "naoqi_bridge_msgs/JointAnglesWithSpeed",
"messageTemplate": {
"joint_names": [
"{{arm === 'left' ? 'L' : 'R'}}ShoulderPitch",
"{{arm === 'left' ? 'L' : 'R'}}ShoulderRoll",
"{{arm === 'left' ? 'L' : 'R'}}ElbowYaw",
"{{arm === 'left' ? 'L' : 'R'}}ElbowRoll"
],
"joint_angles": ["{{shoulder_pitch}}", "{{shoulder_roll}}", "{{elbow_yaw}}", "{{elbow_roll}}"],
"speed": "{{speed}}"
}
}
}
],
"safety": {
"maxSpeed": 0.3,
"emergencyStop": {
"action": "stop_movement",
"description": "Immediately stops all movement"
},
"jointLimits": {
"HeadYaw": { "min": -2.09, "max": 2.09 },
"HeadPitch": { "min": -0.67, "max": 0.51 },
"LShoulderPitch": { "min": -2.09, "max": 2.09 },
"RShoulderPitch": { "min": -2.09, "max": 2.09 },
"LShoulderRoll": { "min": -0.31, "max": 1.33 },
"RShoulderRoll": { "min": -1.33, "max": 0.31 },
"LElbowYaw": { "min": -2.09, "max": 2.09 },
"RElbowYaw": { "min": -2.09, "max": 2.09 },
"LElbowRoll": { "min": 0.03, "max": 1.54 },
"RElbowRoll": { "min": -1.54, "max": -0.03 }
}
}
}

View File

@@ -0,0 +1,464 @@
{
"name": "NAO6 Sensors & Feedback",
"version": "1.0.0",
"description": "Complete sensor suite for NAO6 robot including touch sensors, sonar, IMU, cameras, and joint state monitoring",
"platform": "NAO6",
"category": "sensors",
"manufacturer": {
"name": "SoftBank Robotics",
"website": "https://www.softbankrobotics.com"
},
"documentation": {
"mainUrl": "https://docs.hristudio.com/robots/nao6/sensors",
"quickStart": "https://docs.hristudio.com/robots/nao6/sensors/quickstart"
},
"ros2Config": {
"namespace": "/naoqi_driver",
"topics": {
"joint_states": {
"type": "sensor_msgs/JointState",
"description": "Current positions, velocities, and efforts of all joints"
},
"imu": {
"type": "sensor_msgs/Imu",
"description": "Inertial measurement unit data (acceleration, angular velocity, orientation)"
},
"bumper": {
"type": "naoqi_bridge_msgs/Bumper",
"description": "Foot bumper sensor states"
},
"hand_touch": {
"type": "naoqi_bridge_msgs/HandTouch",
"description": "Hand tactile sensor states"
},
"head_touch": {
"type": "naoqi_bridge_msgs/HeadTouch",
"description": "Head tactile sensor states"
},
"sonar/left": {
"type": "sensor_msgs/Range",
"description": "Left ultrasonic range sensor"
},
"sonar/right": {
"type": "sensor_msgs/Range",
"description": "Right ultrasonic range sensor"
},
"camera/front/image_raw": {
"type": "sensor_msgs/Image",
"description": "Front camera image feed"
},
"camera/bottom/image_raw": {
"type": "sensor_msgs/Image",
"description": "Bottom camera image feed"
},
"battery": {
"type": "sensor_msgs/BatteryState",
"description": "Battery level and charging status"
}
}
},
"actions": [
{
"id": "get_joint_states",
"name": "Get Joint States",
"description": "Read current positions and velocities of all robot joints",
"category": "sensors",
"parameters": [
{
"name": "specific_joints",
"type": "multiselect",
"description": "Specific joints to monitor (empty = all joints)",
"required": false,
"options": [
{ "value": "HeadYaw", "label": "Head Yaw" },
{ "value": "HeadPitch", "label": "Head Pitch" },
{ "value": "LShoulderPitch", "label": "Left Shoulder Pitch" },
{ "value": "LShoulderRoll", "label": "Left Shoulder Roll" },
{ "value": "LElbowYaw", "label": "Left Elbow Yaw" },
{ "value": "LElbowRoll", "label": "Left Elbow Roll" },
{ "value": "RShoulderPitch", "label": "Right Shoulder Pitch" },
{ "value": "RShoulderRoll", "label": "Right Shoulder Roll" },
{ "value": "RElbowYaw", "label": "Right Elbow Yaw" },
{ "value": "RElbowRoll", "label": "Right Elbow Roll" }
]
}
],
"implementation": {
"topic": "/naoqi_driver/joint_states",
"messageType": "sensor_msgs/JointState",
"mode": "subscribe"
}
},
{
"id": "get_touch_sensors",
"name": "Get Touch Sensors",
"description": "Monitor all tactile sensors on head and hands",
"category": "sensors",
"parameters": [
{
"name": "sensor_type",
"type": "select",
"description": "Type of touch sensors to monitor",
"required": false,
"options": [
{ "value": "all", "label": "All Touch Sensors" },
{ "value": "head", "label": "Head Touch Only" },
{ "value": "hands", "label": "Hand Touch Only" }
],
"default": "all"
}
],
"implementation": {
"topics": [
"/naoqi_driver/head_touch",
"/naoqi_driver/hand_touch"
],
"messageTypes": [
"naoqi_bridge_msgs/HeadTouch",
"naoqi_bridge_msgs/HandTouch"
],
"mode": "subscribe"
}
},
{
"id": "get_sonar_distance",
"name": "Get Sonar Distance",
"description": "Read ultrasonic distance sensors for obstacle detection",
"category": "sensors",
"parameters": [
{
"name": "sensor_side",
"type": "select",
"description": "Which sonar sensor to read",
"required": false,
"options": [
{ "value": "both", "label": "Both Sensors" },
{ "value": "left", "label": "Left Sensor Only" },
{ "value": "right", "label": "Right Sensor Only" }
],
"default": "both"
},
{
"name": "min_range",
"type": "number",
"description": "Minimum detection range in meters",
"required": false,
"min": 0.1,
"max": 1.0,
"default": 0.25,
"step": 0.05
},
{
"name": "max_range",
"type": "number",
"description": "Maximum detection range in meters",
"required": false,
"min": 1.0,
"max": 3.0,
"default": 2.55,
"step": 0.05
}
],
"implementation": {
"topics": [
"/naoqi_driver/sonar/left",
"/naoqi_driver/sonar/right"
],
"messageType": "sensor_msgs/Range",
"mode": "subscribe"
}
},
{
"id": "get_imu_data",
"name": "Get IMU Data",
"description": "Read inertial measurement unit data (acceleration, gyroscope, orientation)",
"category": "sensors",
"parameters": [
{
"name": "data_type",
"type": "select",
"description": "Type of IMU data to monitor",
"required": false,
"options": [
{ "value": "all", "label": "All IMU Data" },
{ "value": "orientation", "label": "Orientation Only" },
{ "value": "acceleration", "label": "Linear Acceleration" },
{ "value": "angular_velocity", "label": "Angular Velocity" }
],
"default": "all"
}
],
"implementation": {
"topic": "/naoqi_driver/imu",
"messageType": "sensor_msgs/Imu",
"mode": "subscribe"
}
},
{
"id": "get_camera_image",
"name": "Get Camera Image",
"description": "Capture image from robot's cameras",
"category": "sensors",
"parameters": [
{
"name": "camera",
"type": "select",
"description": "Which camera to use",
"required": true,
"options": [
{ "value": "front", "label": "Front Camera" },
{ "value": "bottom", "label": "Bottom Camera" }
],
"default": "front"
},
{
"name": "resolution",
"type": "select",
"description": "Image resolution",
"required": false,
"options": [
{ "value": "160x120", "label": "QQVGA (160x120)" },
{ "value": "320x240", "label": "QVGA (320x240)" },
{ "value": "640x480", "label": "VGA (640x480)" }
],
"default": "320x240"
},
{
"name": "fps",
"type": "number",
"description": "Frames per second",
"required": false,
"min": 1,
"max": 30,
"default": 15,
"step": 1
}
],
"implementation": {
"topic": "/naoqi_driver/camera/{{camera}}/image_raw",
"messageType": "sensor_msgs/Image",
"mode": "subscribe"
}
},
{
"id": "get_battery_status",
"name": "Get Battery Status",
"description": "Monitor robot battery level and charging status",
"category": "sensors",
"parameters": [],
"implementation": {
"topic": "/naoqi_driver/battery",
"messageType": "sensor_msgs/BatteryState",
"mode": "subscribe"
}
},
{
"id": "detect_obstacle",
"name": "Detect Obstacle",
"description": "Check for obstacles using sonar sensors with customizable thresholds",
"category": "sensors",
"parameters": [
{
"name": "detection_distance",
"type": "number",
"description": "Distance threshold for obstacle detection (meters)",
"required": true,
"min": 0.1,
"max": 2.0,
"default": 0.5,
"step": 0.1
},
{
"name": "sensor_side",
"type": "select",
"description": "Which sensors to use for detection",
"required": false,
"options": [
{ "value": "both", "label": "Both Sensors" },
{ "value": "left", "label": "Left Sensor Only" },
{ "value": "right", "label": "Right Sensor Only" }
],
"default": "both"
}
],
"implementation": {
"topics": [
"/naoqi_driver/sonar/left",
"/naoqi_driver/sonar/right"
],
"messageType": "sensor_msgs/Range",
"mode": "subscribe",
"processing": "obstacle_detection"
}
},
{
"id": "monitor_fall_detection",
"name": "Monitor Fall Detection",
"description": "Monitor robot stability using IMU data to detect potential falls",
"category": "sensors",
"parameters": [
{
"name": "tilt_threshold",
"type": "number",
"description": "Maximum tilt angle before fall alert (degrees)",
"required": false,
"min": 10,
"max": 45,
"default": 25,
"step": 5
},
{
"name": "acceleration_threshold",
"type": "number",
"description": "Acceleration threshold for impact detection (m/s²)",
"required": false,
"min": 5,
"max": 20,
"default": 10,
"step": 1
}
],
"implementation": {
"topic": "/naoqi_driver/imu",
"messageType": "sensor_msgs/Imu",
"mode": "subscribe",
"processing": "fall_detection"
}
},
{
"id": "wait_for_touch",
"name": "Wait for Touch",
"description": "Wait for user to touch a specific sensor before continuing",
"category": "sensors",
"parameters": [
{
"name": "sensor_location",
"type": "select",
"description": "Which sensor to wait for",
"required": true,
"options": [
{ "value": "head_front", "label": "Head Front" },
{ "value": "head_middle", "label": "Head Middle" },
{ "value": "head_rear", "label": "Head Rear" },
{ "value": "left_hand", "label": "Left Hand" },
{ "value": "right_hand", "label": "Right Hand" },
{ "value": "any_head", "label": "Any Head Sensor" },
{ "value": "any_hand", "label": "Any Hand Sensor" },
{ "value": "any_touch", "label": "Any Touch Sensor" }
],
"default": "head_front"
},
{
"name": "timeout",
"type": "number",
"description": "Maximum time to wait for touch (seconds, 0 = infinite)",
"required": false,
"min": 0,
"max": 300,
"default": 30,
"step": 5
}
],
"implementation": {
"topics": [
"/naoqi_driver/head_touch",
"/naoqi_driver/hand_touch"
],
"messageTypes": [
"naoqi_bridge_msgs/HeadTouch",
"naoqi_bridge_msgs/HandTouch"
],
"mode": "wait_for_condition",
"condition": "touch_detected"
}
}
],
"sensorSpecifications": {
"touchSensors": {
"head": {
"locations": ["front", "middle", "rear"],
"sensitivity": "capacitive",
"responseTime": "< 50ms"
},
"hands": {
"locations": ["left", "right"],
"sensitivity": "capacitive",
"responseTime": "< 50ms"
}
},
"sonarSensors": {
"count": 2,
"locations": ["left", "right"],
"minRange": "0.25m",
"maxRange": "2.55m",
"fieldOfView": "60°",
"frequency": "40kHz"
},
"cameras": {
"front": {
"resolution": "640x480",
"maxFps": 30,
"fieldOfView": "60.9° x 47.6°"
},
"bottom": {
"resolution": "640x480",
"maxFps": 30,
"fieldOfView": "60.9° x 47.6°"
}
},
"imu": {
"accelerometer": {
"range": "±2g",
"sensitivity": "high"
},
"gyroscope": {
"range": "±500°/s",
"sensitivity": "high"
},
"magnetometer": {
"available": false
}
},
"joints": {
"count": 25,
"encoderResolution": "12-bit",
"positionAccuracy": "±0.1°"
}
},
"dataTypes": {
"jointState": {
"position": "radians",
"velocity": "radians/second",
"effort": "arbitrary units"
},
"imu": {
"orientation": "quaternion",
"angularVelocity": "radians/second",
"linearAcceleration": "m/s²"
},
"range": {
"distance": "meters",
"minRange": "meters",
"maxRange": "meters"
},
"image": {
"encoding": "rgb8",
"width": "pixels",
"height": "pixels"
}
},
"safety": {
"fallDetection": {
"enabled": true,
"defaultThreshold": "25°"
},
"obstacleDetection": {
"enabled": true,
"safeDistance": "0.3m"
},
"batteryMonitoring": {
"lowBatteryWarning": "20%",
"criticalBatteryShutdown": "5%"
}
}
}

View File

@@ -0,0 +1,338 @@
{
"name": "NAO6 Speech & Audio",
"version": "1.0.0",
"description": "Text-to-speech and audio capabilities for NAO6 robot including voice synthesis, volume control, and language settings",
"platform": "NAO6",
"category": "speech",
"manufacturer": {
"name": "SoftBank Robotics",
"website": "https://www.softbankrobotics.com"
},
"documentation": {
"mainUrl": "https://docs.hristudio.com/robots/nao6/speech",
"quickStart": "https://docs.hristudio.com/robots/nao6/speech/quickstart"
},
"ros2Config": {
"namespace": "/naoqi_driver",
"topics": {
"speech": {
"type": "std_msgs/String",
"description": "Text-to-speech commands"
},
"set_language": {
"type": "std_msgs/String",
"description": "Set speech language"
},
"audio_volume": {
"type": "std_msgs/Float32",
"description": "Control audio volume level"
}
}
},
"actions": [
{
"id": "say_text",
"name": "Say Text",
"description": "Make the robot speak the specified text using text-to-speech",
"category": "speech",
"parameters": [
{
"name": "text",
"type": "text",
"description": "Text for the robot to speak",
"required": true,
"maxLength": 500,
"placeholder": "Enter text for NAO to say..."
},
{
"name": "wait_for_completion",
"type": "boolean",
"description": "Wait for speech to finish before continuing",
"required": false,
"default": true
}
],
"implementation": {
"topic": "/naoqi_driver/speech",
"messageType": "std_msgs/String",
"messageTemplate": {
"data": "{{text}}"
}
}
},
{
"id": "say_with_emotion",
"name": "Say Text with Emotion",
"description": "Speak text with emotional expression using SSML-like markup",
"category": "speech",
"parameters": [
{
"name": "text",
"type": "text",
"description": "Text for the robot to speak",
"required": true,
"maxLength": 500,
"placeholder": "Enter text for NAO to say..."
},
{
"name": "emotion",
"type": "select",
"description": "Emotional tone for speech",
"required": false,
"options": [
{ "value": "neutral", "label": "Neutral" },
{ "value": "happy", "label": "Happy" },
{ "value": "sad", "label": "Sad" },
{ "value": "excited", "label": "Excited" },
{ "value": "calm", "label": "Calm" }
],
"default": "neutral"
},
{
"name": "speed",
"type": "number",
"description": "Speech speed multiplier",
"required": false,
"min": 0.5,
"max": 2.0,
"default": 1.0,
"step": 0.1
}
],
"implementation": {
"topic": "/naoqi_driver/speech",
"messageType": "std_msgs/String",
"messageTemplate": {
"data": "\\rspd={{speed}}\\\\rst={{emotion}}\\{{text}}"
}
}
},
{
"id": "set_volume",
"name": "Set Volume",
"description": "Adjust the robot's audio volume level",
"category": "speech",
"parameters": [
{
"name": "volume",
"type": "number",
"description": "Volume level (0.0 = silent, 1.0 = maximum)",
"required": true,
"min": 0.0,
"max": 1.0,
"default": 0.5,
"step": 0.1
}
],
"implementation": {
"topic": "/naoqi_driver/audio_volume",
"messageType": "std_msgs/Float32",
"messageTemplate": {
"data": "{{volume}}"
}
}
},
{
"id": "set_language",
"name": "Set Language",
"description": "Change the robot's speech language",
"category": "speech",
"parameters": [
{
"name": "language",
"type": "select",
"description": "Speech language",
"required": true,
"options": [
{ "value": "en-US", "label": "English (US)" },
{ "value": "en-GB", "label": "English (UK)" },
{ "value": "fr-FR", "label": "French" },
{ "value": "de-DE", "label": "German" },
{ "value": "es-ES", "label": "Spanish" },
{ "value": "it-IT", "label": "Italian" },
{ "value": "ja-JP", "label": "Japanese" },
{ "value": "ko-KR", "label": "Korean" },
{ "value": "zh-CN", "label": "Chinese (Simplified)" }
],
"default": "en-US"
}
],
"implementation": {
"topic": "/naoqi_driver/set_language",
"messageType": "std_msgs/String",
"messageTemplate": {
"data": "{{language}}"
}
}
},
{
"id": "say_random_phrase",
"name": "Say Random Phrase",
"description": "Make the robot say a random phrase from predefined categories",
"category": "speech",
"parameters": [
{
"name": "category",
"type": "select",
"description": "Category of phrases",
"required": true,
"options": [
{ "value": "greeting", "label": "Greetings" },
{ "value": "encouragement", "label": "Encouragement" },
{ "value": "question", "label": "Questions" },
{ "value": "farewell", "label": "Farewells" },
{ "value": "instruction", "label": "Instructions" }
],
"default": "greeting"
}
],
"implementation": {
"topic": "/naoqi_driver/speech",
"messageType": "std_msgs/String",
"messageTemplate": {
"data": "{{getRandomPhrase(category)}}"
}
},
"phrases": {
"greeting": [
"Hello! Nice to meet you!",
"Hi there! How are you today?",
"Welcome! I'm excited to work with you.",
"Good day! Ready to get started?",
"Greetings! What shall we do today?"
],
"encouragement": [
"Great job! Keep it up!",
"You're doing wonderfully!",
"Excellent work! I'm impressed.",
"That's fantastic! Well done!",
"Perfect! You've got this!"
],
"question": [
"How can I help you today?",
"What would you like to do next?",
"Is there anything you'd like to know?",
"Shall we try something different?",
"What are you thinking about?"
],
"farewell": [
"Goodbye! It was great working with you!",
"See you later! Take care!",
"Until next time! Have a wonderful day!",
"Farewell! Thanks for spending time with me!",
"Bye for now! Look forward to seeing you again!"
],
"instruction": [
"Please follow my movements.",
"Let's try this step by step.",
"Watch carefully and then repeat.",
"Take your time, there's no rush.",
"Remember to stay focused."
]
}
},
{
"id": "spell_word",
"name": "Spell Word",
"description": "Have the robot spell out a word letter by letter",
"category": "speech",
"parameters": [
{
"name": "word",
"type": "text",
"description": "Word to spell out",
"required": true,
"maxLength": 50,
"placeholder": "Enter word to spell..."
},
{
"name": "pause_duration",
"type": "number",
"description": "Pause between letters in seconds",
"required": false,
"min": 0.1,
"max": 2.0,
"default": 0.5,
"step": 0.1
}
],
"implementation": {
"topic": "/naoqi_driver/speech",
"messageType": "std_msgs/String",
"messageTemplate": {
"data": "{{word.split('').join('\\pau={{pause_duration * 1000}}\\\\pau=0\\')}}"
}
}
},
{
"id": "count_numbers",
"name": "Count Numbers",
"description": "Have the robot count from one number to another",
"category": "speech",
"parameters": [
{
"name": "start",
"type": "number",
"description": "Starting number",
"required": true,
"min": 0,
"max": 100,
"default": 1,
"step": 1
},
{
"name": "end",
"type": "number",
"description": "Ending number",
"required": true,
"min": 0,
"max": 100,
"default": 10,
"step": 1
},
{
"name": "pause_duration",
"type": "number",
"description": "Pause between numbers in seconds",
"required": false,
"min": 0.1,
"max": 2.0,
"default": 0.8,
"step": 0.1
}
],
"implementation": {
"topic": "/naoqi_driver/speech",
"messageType": "std_msgs/String",
"messageTemplate": {
"data": "{{Array.from({length: end - start + 1}, (_, i) => start + i).join('\\pau={{pause_duration * 1000}}\\\\pau=0\\')}}"
}
}
}
],
"features": {
"languages": [
"en-US", "en-GB", "fr-FR", "de-DE", "es-ES",
"it-IT", "ja-JP", "ko-KR", "zh-CN"
],
"emotions": [
"neutral", "happy", "sad", "excited", "calm"
],
"voiceEffects": [
"speed", "pitch", "volume", "emotion"
],
"ssmlSupport": true,
"maxTextLength": 500
},
"safety": {
"maxVolume": 1.0,
"defaultVolume": 0.5,
"profanityFilter": true,
"maxSpeechDuration": 60,
"emergencyQuiet": {
"action": "set_volume",
"parameters": { "volume": 0 },
"description": "Immediately mute robot audio"
}
}
}

View File

@@ -0,0 +1,44 @@
{
"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"
},
"urls": {
"git": "https://github.com/hristudio/nao6-ros2-plugins",
"documentation": "https://docs.hristudio.com/robots/nao6",
"issues": "https://github.com/hristudio/nao6-ros2-plugins/issues"
},
"trust": "official",
"license": "MIT",
"robots": [
{
"name": "NAO6",
"manufacturer": "SoftBank Robotics",
"model": "NAO V6",
"communicationProtocol": "ros2"
}
],
"categories": [
"movement",
"speech",
"sensors",
"interaction",
"vision"
],
"ros2": {
"distro": "humble",
"packages": [
"naoqi_driver2",
"naoqi_bridge_msgs",
"rosbridge_suite"
],
"bridge": {
"protocol": "websocket",
"defaultPort": 9090
}
},
"lastUpdated": "2025-01-16T00:00:00Z"
}