mirror of
https://github.com/soconnor0919/hristudio.git
synced 2026-05-08 13:58:55 -04:00
feat(nao6): add SSH-based animation execution for NAO6 robot
- Add play_animation actions to robots/command API using qicli SSH - Add SSH-based animation execution to robot-communication service - Animations: bow, hey, show_floor, show_sole, enthusiastic, think, yes, no, idontknow This bypasses ROS2 cross-container issues by using direct SSH connection.
This commit is contained in:
@@ -89,6 +89,42 @@ export async function POST(request: NextRequest) {
|
|||||||
command = `sshpass -p "${password}" ssh -o StrictHostKeyChecking=no "nao@${robotIp}" "python2 -c \\"import sys; sys.path.append('/opt/aldebaran/lib/python2.7/site-packages'); import naoqi; m = naoqi.ALProxy('ALMotion', '127.0.0.1', 9559); m.rest()\\""`;
|
command = `sshpass -p "${password}" ssh -o StrictHostKeyChecking=no "nao@${robotIp}" "python2 -c \\"import sys; sys.path.append('/opt/aldebaran/lib/python2.7/site-packages'); import naoqi; m = naoqi.ALProxy('ALMotion', '127.0.0.1', 9559); m.rest()\\""`;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case "play_animation_bow":
|
||||||
|
command = `sshpass -p "${password}" ssh -o StrictHostKeyChecking=no "nao@${robotIp}" "qicli call ALAnimationPlayer.run 'animations/Stand/Gestures/BowShort_1'"`;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "play_animation_hey":
|
||||||
|
command = `sshpass -p "${password}" ssh -o StrictHostKeyChecking=no "nao@${robotIp}" "qicli call ALAnimationPlayer.run 'animations/Stand/Gestures/Hey_1'"`;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "play_animation_show_floor":
|
||||||
|
command = `sshpass -p "${password}" ssh -o StrictHostKeyChecking=no "nao@${robotIp}" "qicli call ALAnimationPlayer.run 'animations/Stand/Gestures/ShowFloor_1'"`;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "play_animation_show_sole":
|
||||||
|
command = `sshpass -p "${password}" ssh -o StrictHostKeyChecking=no "nao@${robotIp}" "qicli call ALAnimationPlayer.run 'animations/Stand/Gestures/ShowSole_1'"`;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "play_animation_enthusiastic":
|
||||||
|
command = `sshpass -p "${password}" ssh -o StrictHostKeyChecking=no "nao@${robotIp}" "qicli call ALAnimationPlayer.run 'animations/Stand/Gestures/Enthusiastic_4'"`;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "play_animation_think":
|
||||||
|
command = `sshpass -p "${password}" ssh -o StrictHostKeyChecking=no "nao@${robotIp}" "qicli call ALAnimationPlayer.run 'animations/Stand/Gestures/Think_1'"`;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "play_animation_yes":
|
||||||
|
command = `sshpass -p "${password}" ssh -o StrictHostKeyChecking=no "nao@${robotIp}" "qicli call ALAnimationPlayer.run 'animations/Stand/Gestures/Yes_1'"`;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "play_animation_no":
|
||||||
|
command = `sshpass -p "${password}" ssh -o StrictHostKeyChecking=no "nao@${robotIp}" "qicli call ALAnimationPlayer.run 'animations/Stand/Gestures/No_3'"`;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "play_animation_idontknow":
|
||||||
|
command = `sshpass -p "${password}" ssh -o StrictHostKeyChecking=no "nao@${robotIp}" "qicli call ALAnimationPlayer.run 'animations/Stand/Gestures/IDontKnow_1'"`;
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return NextResponse.json(
|
return NextResponse.json(
|
||||||
{ error: `System action ${id} not implemented` },
|
{ error: `System action ${id} not implemented` },
|
||||||
|
|||||||
@@ -6,6 +6,10 @@
|
|||||||
|
|
||||||
import WebSocket from "ws";
|
import WebSocket from "ws";
|
||||||
import { EventEmitter } from "events";
|
import { EventEmitter } from "events";
|
||||||
|
import { exec } from "child_process";
|
||||||
|
import { promisify } from "util";
|
||||||
|
|
||||||
|
const execAsync = promisify(exec);
|
||||||
|
|
||||||
export interface RobotCommunicationConfig {
|
export interface RobotCommunicationConfig {
|
||||||
rosBridgeUrl: string;
|
rosBridgeUrl: string;
|
||||||
@@ -212,7 +216,23 @@ export class RobotCommunicationService extends EventEmitter {
|
|||||||
action: RobotAction,
|
action: RobotAction,
|
||||||
actionId: string,
|
actionId: string,
|
||||||
): void {
|
): void {
|
||||||
const { implementation, parameters } = action;
|
const { implementation, parameters, actionId: actionType } = action;
|
||||||
|
|
||||||
|
// Use SSH for play_animation actions
|
||||||
|
if (actionType.startsWith("play_animation_")) {
|
||||||
|
this.executeAnimationViaSSH(actionType).then(() => {
|
||||||
|
this.completeAction(actionId, {
|
||||||
|
success: true,
|
||||||
|
duration:
|
||||||
|
Date.now() -
|
||||||
|
(this.pendingActions.get(actionId)?.startTime || Date.now()),
|
||||||
|
data: { method: "ssh", action: actionType },
|
||||||
|
});
|
||||||
|
}).catch((error) => {
|
||||||
|
this.pendingActions.get(actionId)?.reject(error);
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Build ROS message from template
|
// Build ROS message from template
|
||||||
const message = this.buildRosMessage(
|
const message = this.buildRosMessage(
|
||||||
@@ -244,6 +264,40 @@ export class RobotCommunicationService extends EventEmitter {
|
|||||||
}, 100);
|
}, 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async executeAnimationViaSSH(actionType: string): Promise<void> {
|
||||||
|
const animationMap: Record<string, string> = {
|
||||||
|
"play_animation_bow": "animations/Stand/Gestures/BowShort_1",
|
||||||
|
"play_animation_hey": "animations/Stand/Gestures/Hey_1",
|
||||||
|
"play_animation_show_floor": "animations/Stand/Gestures/ShowFloor_1",
|
||||||
|
"play_animation_show_sole": "animations/Stand/Gestures/ShowSole_1",
|
||||||
|
"play_animation_enthusiastic": "animations/Stand/Gestures/Enthusiastic_4",
|
||||||
|
"play_animation_think": "animations/Stand/Gestures/Think_1",
|
||||||
|
"play_animation_yes": "animations/Stand/Gestures/Yes_1",
|
||||||
|
"play_animation_no": "animations/Stand/Gestures/No_3",
|
||||||
|
"play_animation_idontknow": "animations/Stand/Gestures/IDontKnow_1",
|
||||||
|
};
|
||||||
|
|
||||||
|
const animation = animationMap[actionType];
|
||||||
|
if (!animation) {
|
||||||
|
throw new Error(`Unknown animation: ${actionType}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const robotIp = process.env.NAO_IP || "134.82.159.168";
|
||||||
|
const password = process.env.NAO_PASSWORD || "robolab";
|
||||||
|
|
||||||
|
console.log(`[RobotComm] Executing animation via SSH: ${animation}`);
|
||||||
|
|
||||||
|
const command = `sshpass -p "${password}" ssh -o StrictHostKeyChecking=no -o ConnectTimeout=5 "nao@${robotIp}" "qicli call ALAnimationPlayer.run '${animation}'"`;
|
||||||
|
|
||||||
|
const { stdout, stderr } = await execAsync(command);
|
||||||
|
|
||||||
|
if (stderr && !stderr.includes("null")) {
|
||||||
|
console.warn(`[RobotComm] SSH stderr: ${stderr}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`[RobotComm] Animation result: ${stdout}`);
|
||||||
|
}
|
||||||
|
|
||||||
private buildRosMessage(
|
private buildRosMessage(
|
||||||
template: Record<string, unknown>,
|
template: Record<string, unknown>,
|
||||||
parameters: Record<string, unknown>,
|
parameters: Record<string, unknown>,
|
||||||
|
|||||||
Reference in New Issue
Block a user