mirror of
https://github.com/soconnor0919/hristudio.git
synced 2025-12-11 14:44:44 -05:00
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:
7
public/nao6-plugins/plugins/index.json
Normal file
7
public/nao6-plugins/plugins/index.json
Normal file
@@ -0,0 +1,7 @@
|
||||
[
|
||||
"nao6-movement.json",
|
||||
"nao6-speech.json",
|
||||
"nao6-sensors.json",
|
||||
"nao6-vision.json",
|
||||
"nao6-interaction.json"
|
||||
]
|
||||
342
public/nao6-plugins/plugins/nao6-movement.json
Normal file
342
public/nao6-plugins/plugins/nao6-movement.json
Normal 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 }
|
||||
}
|
||||
}
|
||||
}
|
||||
464
public/nao6-plugins/plugins/nao6-sensors.json
Normal file
464
public/nao6-plugins/plugins/nao6-sensors.json
Normal 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%"
|
||||
}
|
||||
}
|
||||
}
|
||||
338
public/nao6-plugins/plugins/nao6-speech.json
Normal file
338
public/nao6-plugins/plugins/nao6-speech.json
Normal 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"
|
||||
}
|
||||
}
|
||||
}
|
||||
44
public/nao6-plugins/repository.json
Normal file
44
public/nao6-plugins/repository.json
Normal 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"
|
||||
}
|
||||
Reference in New Issue
Block a user