Compare commits

1 Commits

21 changed files with 3421 additions and 109 deletions
-4
View File
@@ -1,4 +0,0 @@
#!/bin/bash
pactl set-source-volume alsa_input.pci-0000_00_1b.0.analog-stereo 32%
#python3.11 naowalk.py --ip spike.local --port 9561 --mic-gain 9 --video-scale 3.0
python3.11 naowalk.py --ip 127.0.0.1 --port 9561 --mic-gain 9 --video-scale 3.0 --nao-ssh-port 2222 --media-relay 127.0.0.1:8000
+20
View File
@@ -0,0 +1,20 @@
# Loaded by naowalk.py's gesture loader (_load_gestures). The filename's
# leading digit ("1") is which number key triggers this. Every gesture
# file just needs a run(motion, tts) function - motion is the connected
# ALMotion proxy, tts is ALTextToSpeech. Optionally set NAME below to
# control the display name shown in the on-screen controls hint.
NAME = "Wave"
def run(motion, tts):
motion.setStiffnesses("RArm", 1.0)
names = ["RShoulderPitch", "RShoulderRoll", "RElbowYaw", "RElbowRoll", "RWristYaw", "RHand"]
angles = [[-1.5]*5, [-0.4,-0.7,-0.4,-0.7,-0.4], [0.6,1.1,0.6,1.1,0.6],
[0.8,0.3,0.8,0.3,0.8], [0.0,1.5,0.0,-1.5,0.0], [1.0]*5]
times = [[0.5,1.0,1.5,2.0,2.5]] * 6
motion.angleInterpolation(names, angles, times, True)
neutral = [1.5, -0.15, 1.2, 0.5, 0.0, 0.0]
motion.angleInterpolationWithSpeed(names, neutral, 0.3)
# Relaxed stiffness after completion
motion.setStiffnesses("RArm", 0.3)
+23
View File
@@ -0,0 +1,23 @@
NAME = "Cena"
def run(motion, tts):
tts.say("You can't see me")
motion.setStiffnesses("RArm", 1.0)
names = ["RShoulderPitch", "RShoulderRoll", "RElbowYaw", "RElbowRoll", "RWristYaw", "RHand"]
# Setup for tight, face-level sweep
tight_setup = [-0.4, -0.2, 0.0, 1.2, 1.5, 1.0]
motion.angleInterpolation(names, tight_setup, [0.5]*6, True)
# The horizontal "backhand" sweep
motion.angleInterpolation(["RElbowYaw"], [0.8], 0.4, True)
motion.angleInterpolation(["RElbowYaw"], [-0.8], 0.6, True)
motion.angleInterpolation(["RElbowYaw"], [0.0], 0.4, True)
# Return to neutral
neutral = [1.5, -0.15, 1.2, 0.5, 0.0, 0.0]
motion.angleInterpolationWithSpeed(names, neutral, 0.3)
# Relaxed stiffness after completion
motion.setStiffnesses("RArm", 0.3)
+47
View File
@@ -0,0 +1,47 @@
import threading
NAME = "6-7"
def run(motion, tts):
# Threaded so the TTS call doesn't block the arm motion below.
threading.Thread(target=tts.say, args=("six seven",), daemon=True).start()
motion.setStiffnesses(["RArm", "LArm"], 1.0)
# Lock both arms up in front of the body, hands held out like a scale.
# ElbowYaw is rotated ~90° off zero here - that's what points the elbow's
# bend axis so flexing tips the forearm forward/down instead of curling
# it in across the chest. These joints don't move again until the return.
hold_names = [
"RShoulderPitch", "RShoulderRoll", "RElbowYaw", "RWristYaw", "RHand",
"LShoulderPitch", "LShoulderRoll", "LElbowYaw", "LWristYaw", "LHand"
]
hold_angles = [
0.2, -0.2, 1.4, 1.57, 0.6,
0.2, 0.2, -1.4, -1.57, 0.6
]
motion.angleInterpolationWithSpeed(hold_names, hold_angles, 0.25)
# Only the elbows seesaw now - a small tip back and forth, one arm
# rising while the other falls, like weighing something in each hand
# ("six... seven..."). Small amplitude so it stays a wobble, not a fold.
elbow_names = ["RElbowRoll", "LElbowRoll"]
r_low, r_high = 0.6, 1.0
l_low, l_high = -0.6, -1.0
elbow_angles = [
[r_low, r_high, r_low, r_high, r_low, r_high],
[l_high, l_low, l_high, l_low, l_high, l_low],
]
times = [[0.35, 0.7, 1.05, 1.4, 1.75, 2.1]] * 2
motion.angleInterpolation(elbow_names, elbow_angles, times, True)
# Return everything to neutral
neutral_names = hold_names + elbow_names
neutral = [1.5, -0.15, 1.2, 0.0, 0.0,
1.5, 0.15, -1.2, 0.0, 0.0,
0.5, -0.5]
motion.angleInterpolationWithSpeed(neutral_names, neutral, 0.3)
# Relaxed stiffness after completion
motion.setStiffnesses(["RArm", "LArm"], 0.3)
+22
View File
@@ -0,0 +1,22 @@
import os
NAME = "Gangnam Style"
# The Choregraphe project lives in a sibling folder with the same name
# as this file (minus the .py) - e.g. gestures/4_gangnam_style/ next
# to gestures/4_gangnam_style.py - containing manifest.xml and a
# GangnamStyle/behavior.xar subfolder, exactly what Choregraphe's
# "Export as package" produces.
PACKAGE_DIR = os.path.splitext(os.path.abspath(__file__))[0]
BEHAVIOR_SUBDIR = "GangnamStyle"
def run(motion, tts, teleop):
# Deploys (first run only, cached after) + runs the actual
# Choregraphe behavior on the robot - this blocks until it
# finishes, same as the old hand-coded version did.
teleop.behavior_runner.ensure_running(PACKAGE_DIR, BEHAVIOR_SUBDIR)
# The .xar moves legs/hips, unlike the old arms-only version, so
# bring him back to a known walk-ready stance afterward.
teleop.idle_crouch()
File diff suppressed because it is too large Load Diff
Binary file not shown.
+16
View File
@@ -0,0 +1,16 @@
<?xml version='1.0' encoding='UTF-8'?>
<package uuid=".lastUploadedChoregrapheBehavior" version="0.0.0" installOnlyAtStartup="false">
<names>
<name lang="en_US">.lastUploadedChoregrapheBehavior</name>
<name lang="en_US-x-scrald">.lastUploadedChoregrapheBehavior</name>
</names>
<descriptionLanguages>
<language>en_US</language>
</descriptionLanguages>
<contents>
<behaviorContent path="GangnamStyle">
<nature>interactive</nature>
<permissions/>
</behaviorContent>
</contents>
</package>
+22
View File
@@ -0,0 +1,22 @@
import os
NAME = "Tai Chi"
# The Choregraphe project lives in a sibling folder with the same name
# as this file (minus the .py) - e.g. gestures/5_tai_chi/ next to
# gestures/5_tai_chi.py - containing manifest.xml and a
# behavior_1/behavior.xar subfolder, exactly what Choregraphe's
# "Export as package" produces.
PACKAGE_DIR = os.path.splitext(os.path.abspath(__file__))[0]
BEHAVIOR_SUBDIR = "behavior_1"
def run(motion, tts, teleop):
# Deploys (first run only, cached after) + runs the actual
# Choregraphe behavior on the robot - this blocks until it
# finishes, same pattern as the Gangnam Style gesture.
teleop.behavior_runner.ensure_running(PACKAGE_DIR, BEHAVIOR_SUBDIR)
# Tai chi moves legs/hips through slow stances, so bring him back
# to a known walk-ready stance afterward.
teleop.idle_crouch()
+14
View File
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Package name="5_tai_chi" format_version="5">
<Manifest src="manifest.xml" />
<BehaviorDescriptions>
<BehaviorDescription name="behavior" src="tai_chi" xar="behavior.xar" />
</BehaviorDescriptions>
<Dialogs />
<Resources />
<Topics />
<IgnoredPaths />
<Translations auto-fill="en_US">
<Translation name="translation_en_US" src="translations/translation_en_US.ts" language="en_US" />
</Translations>
</Package>
+18
View File
@@ -0,0 +1,18 @@
<?xml version='1.0' encoding='UTF-8'?>
<package version="0.0.0" uuid="5_tai_chi-0cc7f3" installOnlyAtStartup="false">
<names>
<name lang="en_US">Untitled</name>
</names>
<supportedLanguages>
<language>en_US</language>
</supportedLanguages>
<descriptionLanguages>
<language>en_US</language>
</descriptionLanguages>
<contents>
<behaviorContent path="tai_chi">
<nature>interactive</nature>
<permissions/>
</behaviorContent>
</contents>
</package>
+857
View File
@@ -0,0 +1,857 @@
<?xml version="1.0" encoding="UTF-8" ?>
<ChoregrapheProject xmlns="http://www.ald.softbankrobotics.com/schema/choregraphe/project.xsd" xar_version="3">
<Box name="root" id="-1" localization="8" tooltip="Root box of Choregraphe&apos;s behavior. Highest level possible." x="0" y="0">
<bitmap>media/images/box/root.png</bitmap>
<script language="4">
<content>
<![CDATA[]]>
</content>
</script>
<Input name="onLoad" type="1" type_size="1" nature="0" inner="1" tooltip="Signal sent when diagram is loaded." id="1" />
<Input name="onStart" type="1" type_size="1" nature="2" inner="0" tooltip="Box behavior starts when a signal is received on this input." id="2" />
<Input name="onStop" type="1" type_size="1" nature="3" inner="0" tooltip="Box behavior stops when a signal is received on this input." id="3" />
<Output name="onStopped" type="1" type_size="1" nature="1" inner="0" tooltip="Signal sent when box behavior is finished." id="4" />
<Timeline enable="0">
<BehaviorLayer name="behavior_layer1">
<BehaviorKeyframe name="keyframe1" index="1">
<Diagram>
<Box name="Tai Chi Chuan" id="11" localization="8" tooltip="Arms part of Tai Chi Chuan dance." x="198" y="164">
<bitmap>media/images/box/movement/dance.png</bitmap>
<script language="4">
<content>
<![CDATA[]]>
</content>
</script>
<Input name="onLoad" type="1" type_size="1" nature="0" inner="1" tooltip="Signal sent when diagram is loaded." id="1" />
<Input name="onStart" type="1" type_size="1" nature="2" inner="0" tooltip="Box behavior starts when a signal is received on this input." id="2" />
<Input name="onStop" type="1" type_size="1" nature="3" inner="0" tooltip="Box behavior stops when a signal is received on this input." id="3" />
<Output name="onStopped" type="1" type_size="1" nature="1" inner="0" tooltip="Signal sent when box behavior is finished." id="4" />
<Timeline enable="1" fps="5" start_frame="1" end_frame="-1" size="250">
<BehaviorLayer name="LED">
<BehaviorKeyframe name="keyframe15" index="15">
<Diagram>
<Box name="Light_ConcentratedEyes" id="1" localization="8" tooltip="Set an animated gaze which expresses concentration." x="231" y="105">
<bitmap>media/images/box/interaction/LED.png</bitmap>
<script language="4">
<content>
<![CDATA[class MyClass(GeneratedClass):
def __init__(self):
try: # disable autoBind;
GeneratedClass.__init__(self, False);
except TypeError: # if NAOqi < 1.14;
GeneratedClass.__init__( self );
def onLoad(self):
self.bIsRunning = False;
self.leds = self.session().service("ALLeds")
def onUnload(self):
self.onInput_onStop(); # will stop current loop execution
def onInput_onStart(self):
#self.log( self.getName() + ": start - begin" );
if( self.bIsRunning ):
#print( self.getName() + ": already started => nothing" );
return;
self.bIsRunning = True;
self.bMustStop = False;
rDuration = 1.0;
self.leds.fadeRGB( "FaceLedsTop", 0x0000ff, rDuration, _async=True );
self.leds.fadeRGB( "FaceLedsInternal", 0x00ff00, rDuration, _async=True );
self.leds.fadeRGB( "FaceLedsBottom", 0x0000ff, rDuration, _async=True );
self.leds.fadeRGB( "FaceLedsExternal", 0x0000ff, rDuration );
while( not self.bMustStop ):
rTime=1.0
self.leds.fadeRGB( "FaceLedsInternal", 0x000000, rTime );
if( self.bMustStop ):
break;
self.leds.fadeRGB( "FaceLedsInternal", 0x00ff00, rDuration );
# end while
self.bIsRunning = False;
self.onStopped();
def onInput_onStop(self):
self.bMustStop = True; # will stop current loop execution]]>
</content>
</script>
<Input name="onLoad" type="1" type_size="1" nature="0" inner="1" tooltip="Signal sent when diagram is loaded." id="1" />
<Input name="onStart" type="1" type_size="1" nature="2" inner="0" tooltip="Box behavior starts when a signal is received on this input." id="2" />
<Input name="onStop" type="1" type_size="1" nature="3" inner="0" tooltip="Box behavior stops when a signal is received on this input." id="3" />
<Output name="onStopped" type="1" type_size="1" nature="1" inner="0" tooltip="Signal sent when box behavior is finished." id="4" />
</Box>
<Box name="Tocolor" id="2" localization="8" tooltip="tags:&#x0A;LED" x="490" y="183">
<bitmap>media/images/box/interaction/LED.png</bitmap>
<script language="4">
<content>
<![CDATA[class MyClass(GeneratedClass):
def __init__(self):
GeneratedClass.__init__(self)
def onLoad(self):
#~ puts code for box initialization here
self.leds = self.session().service("ALLeds")
def onUnload(self):
#~ puts code for box cleanup here
self.leds.reset("EarLeds")
pass
def onInput_onStart(self):
rDuration = 0.2
self.leds.fadeRGB( "BrainLeds", 0xffffff, rDuration)
self.onStopped()
def onInput_onStop(self):
self.onUnload() #~ it is usually a good idea to call onUnload of this box in a onStop method, as the code written in onUnload is used to finish the working of the box as well
pass]]>
</content>
</script>
<Input name="onLoad" type="1" type_size="1" nature="0" inner="1" tooltip="Signal sent when diagram is loaded." id="1" />
<Input name="onStart" type="1" type_size="1" nature="2" inner="0" tooltip="Box behavior starts when a signal is received on this input." id="2" />
<Input name="onStop" type="1" type_size="1" nature="3" inner="0" tooltip="Box behavior stops when a signal is received on this input." id="3" />
<Output name="onStopped" type="1" type_size="1" nature="1" inner="0" tooltip="Signal sent when box behavior is finished." id="4" />
<Resource name="Left eye leds" type="Lock" timeout="1" />
<Resource name="Right eye leds" type="Lock" timeout="1" />
</Box>
<Link inputowner="1" indexofinput="2" outputowner="0" indexofoutput="1" />
</Diagram>
</BehaviorKeyframe>
<BehaviorKeyframe name="Eye48" index="247">
<Diagram>
<Box name="Blink" id="3" localization="8" tooltip="tags:&#x0A;LED" x="339" y="161">
<bitmap>media/images/box/interaction/LED.png</bitmap>
<script language="4">
<content>
<![CDATA[class MyClass(GeneratedClass):
def __init__(self):
GeneratedClass.__init__(self)
def onLoad(self):
#~ puts code for box initialization here
self.leds = self.session().service("ALLeds")
def onUnload(self):
#~ puts code for box cleanup here
pass
def onInput_onStart(self):
rDuration = 0.05;
self.leds.fadeRGB( "FaceLed0", 0x000000, rDuration, _async=True );
self.leds.fadeRGB( "FaceLed1", 0x000000, rDuration, _async=True );
self.leds.fadeRGB( "FaceLed2", 0xffffff, rDuration, _async=True );
self.leds.fadeRGB( "FaceLed3", 0x000000, rDuration, _async=True );
self.leds.fadeRGB( "FaceLed4", 0x000000, rDuration, _async=True );
self.leds.fadeRGB( "FaceLed5", 0x000000, rDuration, _async=True );
self.leds.fadeRGB( "FaceLed6", 0xffffff, rDuration, _async=True );
self.leds.fadeRGB( "FaceLed7", 0x000000, rDuration );
time.sleep( 0.1 );
rDuration = 0.05;
self.leds.fadeRGB( "FaceLeds", 0xffffff, rDuration );
self.onStopped()
def onInput_onStop(self):
self.onUnload() #~ it is usually a good idea to call onUnload of this box in a onStop method, as the code written in onUnload is used to finish the working of the box as well
pass]]>
</content>
</script>
<Input name="onLoad" type="1" type_size="1" nature="0" inner="1" tooltip="Signal sent when diagram is loaded." id="1" />
<Input name="onStart" type="1" type_size="1" nature="2" inner="0" tooltip="Box behavior starts when a signal is received on this input." id="2" />
<Input name="onStop" type="1" type_size="1" nature="3" inner="0" tooltip="Box behavior stops when a signal is received on this input." id="3" />
<Output name="onStopped" type="1" type_size="1" nature="1" inner="0" tooltip="Signal sent when box behavior is finished." id="4" />
<Resource name="Left eye leds" type="Lock" timeout="1" />
<Resource name="Right eye leds" type="Lock" timeout="1" />
</Box>
<Link inputowner="3" indexofinput="2" outputowner="0" indexofoutput="1" />
</Diagram>
</BehaviorKeyframe>
</BehaviorLayer>
<BehaviorLayer name="music">
<BehaviorKeyframe name="keyframe12" index="12">
<Diagram>
<Box name="Play Sound" id="1" localization="8" tooltip="Play a sound file. Select the file in parameters. The format of the file can be wav or ogg (on virtual robot) and also mp3 (on a real robot).&#x0A;&#x0A;Note: There can be some delay to play ogg or mp3 (on a robot) files. We advise you to use wav if&#x0A;you want a fast play of the file." x="193" y="90">
<bitmap>media/images/box/interaction/play_music.png</bitmap>
<script language="4">
<content>
<![CDATA[class MyClass(GeneratedClass):
def __init__(self):
GeneratedClass.__init__(self, False)
def onLoad(self):
self.bIsRunning = False
def onUnload(self):
self.bIsRunning = False
def onInput_onStart(self):
self.bIsRunning = True
def onInput_onStop(self):
if( self.bIsRunning ):
self.onUnload()
self.onStopped()]]>
</content>
</script>
<Input name="onLoad" type="1" type_size="1" nature="0" inner="1" tooltip="Signal sent when Diagram is loaded." id="1" />
<Input name="onStart" type="1" type_size="1" nature="2" inner="0" tooltip="Starts the music." id="2" />
<Input name="onStop" type="1" type_size="1" nature="3" inner="0" tooltip="Stops the music." id="3" />
<Output name="onStopped" type="1" type_size="1" nature="1" inner="0" tooltip="Signal sent when box behavior is finished or stopped." id="4" />
<Parameter name="File name" inherits_from_parent="0" content_type="4" value="/sounds/mikhael-landscape-paisaje.ogg" default_value="" tooltip="Name of the file you want to play.&#x0A;&#x0A;Note: You can click on the folder icon to browse the project content or import&#x0A;new files to the project." id="5" />
<Parameter name="Begin position (s)" inherits_from_parent="0" content_type="2" value="0" default_value="0" min="0" max="600" tooltip="Position in seconds where the playing must start." id="6" />
<Parameter name="Volume (%)" inherits_from_parent="0" content_type="1" value="100" default_value="100" min="0" max="100" tooltip="Volume the file is played with." id="7" />
<Parameter name="Balance L/R" inherits_from_parent="0" content_type="2" value="0" default_value="0" min="-1" max="1" tooltip="Value which determines if the sound is played more on the robot&apos;s left or right.&#x0A;You can particularly set it to:&#x0A;- -1 to play only on the left loudspeaker.&#x0A;- 0 to play on both loudspeakers.&#x0A;- 1 to play only on the right loudspeaker." id="8" />
<Parameter name="Play in loop" inherits_from_parent="0" content_type="0" value="0" default_value="0" tooltip="This parameter allows to play the file in loop. The playing will start each time at&#x0A;the beginning of the file." id="9" />
<Timeline enable="0">
<BehaviorLayer name="behavior_layer1">
<BehaviorKeyframe name="keyframe1" index="1">
<Diagram>
<Box name="Play Sound File" id="2" localization="8" tooltip="Play the sound." x="442" y="70">
<bitmap>media/images/box/interaction/play_music.png</bitmap>
<script language="4">
<content>
<![CDATA[import time
class MyClass(GeneratedClass):
def __init__(self):
GeneratedClass.__init__(self, False)
def onLoad(self):
self.player = self.session().service('ALAudioPlayer')
self.playerStop = self.session().service('ALAudioPlayer') #Create another service as wait is blocking if audioout is remote
self.bIsRunning = False
self.ids = []
def onUnload(self):
for id in self.ids:
try:
self.playerStop.stop(id)
except:
pass
while( self.bIsRunning ):
time.sleep( 0.2 )
def onInput_onStart(self, p):
self.bIsRunning = True
try:
if (self.getParameter("Play in loop")) :
id = self.player.pCall("playFileInLoop",p,self.getParameter("Volume (%)")/100.,self.getParameter("Balance L/R"))
else :
id = self.player.pCall("playFileFromPosition",p,self.getParameter("Begin position (s)"),self.getParameter("Volume (%)")/100.,self.getParameter("Balance L/R"))
self.ids.append(id)
self.player.wait(id)
finally:
try:
self.ids.remove(id)
except:
pass
if( self.ids == [] ):
self.onStopped() # activate output of the box
self.bIsRunning = False
def onInput_onStop(self):
self.onUnload()]]>
</content>
</script>
<Input name="onLoad" type="1" type_size="1" nature="0" inner="1" tooltip="Signal sent when Diagram is loaded." id="1" />
<Input name="onStart" type="3" type_size="1" nature="2" inner="0" tooltip="Box behavior starts when a signal is received on this Input." id="2" />
<Input name="onStop" type="1" type_size="1" nature="3" inner="0" tooltip="Box behavior stops when a signal is received on this Input." id="3" />
<Output name="onStopped" type="1" type_size="1" nature="1" inner="0" tooltip="Signal sent when Box behavior is finished." id="4" />
<Parameter name="Begin position (s)" inherits_from_parent="1" content_type="2" value="0" default_value="0" min="0" max="600" tooltip="Position in seconds where the playing must start." id="5" />
<Parameter name="Volume (%)" inherits_from_parent="1" content_type="1" value="100" default_value="100" min="0" max="100" tooltip="Volume the file is played with." id="6" />
<Parameter name="Balance L/R" inherits_from_parent="1" content_type="2" value="0" default_value="0" min="-1" max="1" tooltip="Value which determines if the sound is played more on the robot&apos;s left or right.&#x0A;You can particularly set it to:&#x0A;- -1 to play only on the left loudspeaker.&#x0A;- 0 to play on both loudspeakers.&#x0A;- 1 to play only on the right loudspeaker." id="7" />
<Parameter name="Play in loop" inherits_from_parent="1" content_type="0" value="0" default_value="0" tooltip="This parameter allows to play the file in loop. The playing will start each time at&#x0A;the beginning of the file." id="8" />
</Box>
<Box name="Get Attached File" id="1" localization="8" tooltip="Use this box to choose an attached file in its parameters. The filename will be sent on&#x0A;the output when the input is stimulated." x="216" y="71">
<bitmap>media/images/box/folder.png</bitmap>
<script language="4">
<content>
<![CDATA[class MyClass(GeneratedClass):
def __init__(self):
GeneratedClass.__init__(self, False)
def onLoad(self):
pass
def onInput_onStart(self):
self.onStopped(self.behaviorAbsolutePath() + self.getParameter("File name"))]]>
</content>
</script>
<Input name="onLoad" type="1" type_size="1" nature="0" inner="1" tooltip="Signal sent when diagram is loaded." id="1" />
<Input name="onStart" type="1" type_size="1" nature="1" inner="0" tooltip="To send the filepath on the output." id="2" />
<Output name="onStopped" type="3" type_size="1" nature="2" inner="0" tooltip="The filepath of the selected resource file." id="3" />
<Parameter name="File name" inherits_from_parent="1" content_type="4" value="" default_value="" tooltip="Name of the file which is going to be sent on the box output.&#x0A;&#x0A;Note: You can click on the folder icon to browse the project content or import&#x0A;new files to the project." id="4" />
</Box>
<Link inputowner="0" indexofinput="4" outputowner="2" indexofoutput="4" />
<Link inputowner="1" indexofinput="2" outputowner="0" indexofoutput="2" />
<Link inputowner="2" indexofinput="2" outputowner="1" indexofoutput="3" />
</Diagram>
</BehaviorKeyframe>
</BehaviorLayer>
</Timeline>
<Resource name="Audio player" type="Lock" timeout="0" />
</Box>
<Link inputowner="1" indexofinput="2" outputowner="0" indexofoutput="1" />
</Diagram>
</BehaviorKeyframe>
</BehaviorLayer>
<ActuatorList model="nao">
<ActuatorCurve name="value" actuator="HeadYaw" mute="0" unit="0">
<Key frame="15" value="0" />
<Key frame="25" value="4.82967e-06" />
<Key frame="35" value="4.82968e-06" />
<Key frame="45" value="4.82968e-06" />
<Key frame="55" value="-2.73208e-05" />
<Key frame="65" value="18" />
<Key frame="75" value="-17" />
<Key frame="85" value="-68" />
<Key frame="95" value="-16" />
<Key frame="105" value="12" />
<Key frame="118" value="90" />
<Key frame="131" value="12" />
<Key frame="142" value="8" />
<Key frame="152" value="0" />
<Key frame="162" value="-8" />
<Key frame="172" value="16" />
<Key frame="185" value="-12" />
<Key frame="198" value="-90" />
<Key frame="211" value="-12" />
<Key frame="222" value="-8" />
<Key frame="231" value="0" />
<Key frame="250" value="0" />
</ActuatorCurve>
<ActuatorCurve name="value" actuator="HeadPitch" mute="0" unit="0">
<Key frame="15" value="0" />
<Key frame="25" value="5.12931e-06" />
<Key frame="35" value="-2.73208e-05" />
<Key frame="45" value="5.0962e-06" />
<Key frame="55" value="6.01466e-06" />
<Key frame="65" value="19" />
<Key frame="75" value="18" />
<Key frame="85" value="5.26559e-06" />
<Key frame="95" value="-19" />
<Key frame="105" value="8" />
<Key frame="118" value="-5" />
<Key frame="131" value="8" />
<Key frame="142" value="22" />
<Key frame="152" value="32" />
<Key frame="162" value="22" />
<Key frame="172" value="-19" />
<Key frame="185" value="8" />
<Key frame="198" value="-5" />
<Key frame="211" value="8" />
<Key frame="222" value="22" />
<Key frame="231" value="0" />
<Key frame="250" value="-10.0411" />
</ActuatorCurve>
<ActuatorCurve name="value" actuator="LShoulderPitch" mute="0" unit="0">
<Key frame="15" value="90" />
<Key frame="25" value="110" />
<Key frame="35" value="120" />
<Key frame="45" value="90" />
<Key frame="55" value="0" />
<Key frame="65" value="21" />
<Key frame="75" value="20" />
<Key frame="85" value="11" />
<Key frame="95" value="-46" />
<Key frame="105" value="-10" />
<Key frame="118" value="-17" />
<Key frame="131" value="-10" />
<Key frame="142" value="30" />
<Key frame="152" value="27" />
<Key frame="162" value="19" />
<Key frame="172" value="-27" />
<Key frame="185" value="4" />
<Key frame="198" value="-4" />
<Key frame="211" value="4" />
<Key frame="222" value="19" />
<Key frame="231" value="97" />
<Key frame="250" value="83.0164" />
</ActuatorCurve>
<ActuatorCurve name="value" actuator="LShoulderRoll" mute="0" unit="0">
<Key frame="15" value="10" />
<Key frame="25" value="20" />
<Key frame="35" value="10" />
<Key frame="45" value="10" />
<Key frame="55" value="10" />
<Key frame="65" value="40" />
<Key frame="75" value="0" />
<Key frame="85" value="5" />
<Key frame="95" value="10" />
<Key frame="105" value="23" />
<Key frame="118" value="66" />
<Key frame="131" value="23" />
<Key frame="142" value="23" />
<Key frame="152" value="10" />
<Key frame="162" value="0" />
<Key frame="172" value="23" />
<Key frame="185" value="0" />
<Key frame="198" value="0" />
<Key frame="211" value="0" />
<Key frame="222" value="12" />
<Key frame="231" value="54" />
<Key frame="250" value="12.7723" />
</ActuatorCurve>
<ActuatorCurve name="value" actuator="LElbowYaw" mute="0" unit="0">
<Key frame="15" value="-90" />
<Key frame="25" value="-90" />
<Key frame="35" value="-90" />
<Key frame="45" value="-90" />
<Key frame="55" value="-90" />
<Key frame="65" value="-22" />
<Key frame="75" value="0" />
<Key frame="85" value="0" />
<Key frame="95" value="0" />
<Key frame="105" value="0" />
<Key frame="118" value="0" />
<Key frame="131" value="0" />
<Key frame="142" value="0" />
<Key frame="152" value="12" />
<Key frame="162" value="11" />
<Key frame="172" value="-24" />
<Key frame="185" value="-24" />
<Key frame="198" value="-5" />
<Key frame="211" value="-24" />
<Key frame="222" value="11" />
<Key frame="227" value="-21.7" />
<Key frame="231" value="-14" />
<Key frame="250" value="-68.8135" />
</ActuatorCurve>
<ActuatorCurve name="value" actuator="LElbowRoll" mute="0" unit="0">
<Key frame="15" value="0" />
<Key frame="25" value="-40" />
<Key frame="35" value="-60" />
<Key frame="45" value="0" />
<Key frame="55" value="0" />
<Key frame="65" value="-95" />
<Key frame="75" value="-55" />
<Key frame="85" value="-85" />
<Key frame="95" value="-58" />
<Key frame="105" value="-58" />
<Key frame="118" value="0" />
<Key frame="131" value="-58" />
<Key frame="142" value="-58" />
<Key frame="152" value="-51" />
<Key frame="162" value="-49" />
<Key frame="172" value="-64" />
<Key frame="185" value="-49" />
<Key frame="198" value="-72" />
<Key frame="211" value="-49" />
<Key frame="222" value="-49" />
<Key frame="227" value="-57" />
<Key frame="231" value="-81.4" />
<Key frame="250" value="-23.6757" />
</ActuatorCurve>
<ActuatorCurve name="value" actuator="LWristYaw" mute="0" unit="0">
<Key frame="15" value="-88" />
<Key frame="250" value="5.78932" />
</ActuatorCurve>
<ActuatorCurve name="value" actuator="LHand" mute="0" unit="1">
<Key frame="15" value="0" />
<Key frame="250" value="0.291787" />
</ActuatorCurve>
<ActuatorCurve name="value" actuator="RShoulderPitch" mute="0" unit="0">
<Key frame="15" value="90" />
<Key frame="25" value="110" />
<Key frame="35" value="120" />
<Key frame="45" value="90" />
<Key frame="55" value="0" />
<Key frame="65" value="10" />
<Key frame="75" value="35" />
<Key frame="85" value="60" />
<Key frame="95" value="-27" />
<Key frame="105" value="4" />
<Key frame="118" value="-4" />
<Key frame="131" value="4" />
<Key frame="142" value="19" />
<Key frame="152" value="27" />
<Key frame="162" value="30" />
<Key frame="172" value="-46" />
<Key frame="185" value="-10" />
<Key frame="198" value="-17" />
<Key frame="211" value="-10" />
<Key frame="222" value="30" />
<Key frame="231" value="97" />
<Key frame="250" value="82.7438" />
</ActuatorCurve>
<ActuatorCurve name="value" actuator="RShoulderRoll" mute="0" unit="0">
<Key frame="15" value="-10" />
<Key frame="25" value="-10" />
<Key frame="35" value="-20" />
<Key frame="45" value="-10" />
<Key frame="55" value="-9.99896" />
<Key frame="65" value="-4" />
<Key frame="75" value="-48" />
<Key frame="85" value="-87" />
<Key frame="95" value="-23" />
<Key frame="105" value="0" />
<Key frame="118" value="0" />
<Key frame="131" value="0" />
<Key frame="142" value="0" />
<Key frame="152" value="-10" />
<Key frame="162" value="-23" />
<Key frame="172" value="-10" />
<Key frame="185" value="-23" />
<Key frame="198" value="-66" />
<Key frame="211" value="-23" />
<Key frame="222" value="-32" />
<Key frame="231" value="-54" />
<Key frame="250" value="-12.6315" />
</ActuatorCurve>
<ActuatorCurve name="value" actuator="RElbowYaw" mute="0" unit="0">
<Key frame="15" value="90" />
<Key frame="25" value="90" />
<Key frame="35" value="90" />
<Key frame="45" value="90" />
<Key frame="55" value="90" />
<Key frame="65" value="11" />
<Key frame="75" value="20" />
<Key frame="85" value="90" />
<Key frame="95" value="24" />
<Key frame="105" value="24" />
<Key frame="118" value="5" />
<Key frame="131" value="24" />
<Key frame="142" value="-11" />
<Key frame="152" value="-12" />
<Key frame="162" value="0" />
<Key frame="172" value="0" />
<Key frame="185" value="0" />
<Key frame="198" value="0" />
<Key frame="211" value="0" />
<Key frame="222" value="0" />
<Key frame="227" value="19.6" />
<Key frame="231" value="14" />
<Key frame="250" value="68.8565" />
</ActuatorCurve>
<ActuatorCurve name="value" actuator="RElbowRoll" mute="0" unit="0">
<Key frame="15" value="0" />
<Key frame="25" value="40" />
<Key frame="35" value="60" />
<Key frame="45" value="1.47493e-05" />
<Key frame="55" value="0" />
<Key frame="65" value="71" />
<Key frame="75" value="94" />
<Key frame="85" value="4" />
<Key frame="95" value="64" />
<Key frame="105" value="49" />
<Key frame="118" value="72" />
<Key frame="131" value="49" />
<Key frame="142" value="49" />
<Key frame="152" value="51" />
<Key frame="162" value="58" />
<Key frame="172" value="58" />
<Key frame="185" value="58" />
<Key frame="198" value="2" />
<Key frame="211" value="58" />
<Key frame="222" value="58" />
<Key frame="227" value="64.9" />
<Key frame="231" value="78.3" />
<Key frame="250" value="23.387" />
</ActuatorCurve>
<ActuatorCurve name="value" actuator="RWristYaw" mute="0" unit="0">
<Key frame="15" value="88" />
<Key frame="250" value="6.23278" />
</ActuatorCurve>
<ActuatorCurve name="value" actuator="RHand" mute="0" unit="1">
<Key frame="15" value="0" />
<Key frame="250" value="0.301984" />
</ActuatorCurve>
<ActuatorCurve name="value" actuator="LHipYawPitch" mute="0" unit="0">
<Key frame="15" value="-6.2" />
<Key frame="25" value="-6.9" />
<Key frame="35" value="-6.94104" />
<Key frame="45" value="-7.04938" />
<Key frame="55" value="-8.2" />
<Key frame="65" value="-9.6" />
<Key frame="75" value="-5.7" />
<Key frame="85" value="0" />
<Key frame="95" value="0" />
<Key frame="105" value="0" />
<Key frame="118" value="0" />
<Key frame="131" value="0" />
<Key frame="142" value="-0.566539" />
<Key frame="152" value="0" />
<Key frame="162" value="0" />
<Key frame="172" value="0" />
<Key frame="185" value="0" />
<Key frame="198" value="0" />
<Key frame="211" value="0" />
<Key frame="222" value="-3.9" />
<Key frame="231" value="0" />
<Key frame="250" value="-9.74084" />
</ActuatorCurve>
<ActuatorCurve name="value" actuator="LHipRoll" mute="0" unit="0">
<Key frame="15" value="-3" />
<Key frame="25" value="-7" />
<Key frame="35" value="-11.4235" />
<Key frame="45" value="5.96288" />
<Key frame="55" value="6" />
<Key frame="65" value="-10" />
<Key frame="75" value="10" />
<Key frame="85" value="24.1" />
<Key frame="95" value="30.3" />
<Key frame="105" value="28.7" />
<Key frame="118" value="32.4" />
<Key frame="131" value="28.7" />
<Key frame="142" value="19.9829" />
<Key frame="152" value="0" />
<Key frame="162" value="-15" />
<Key frame="167" value="14.4" />
<Key frame="172" value="15" />
<Key frame="185" value="8" />
<Key frame="198" value="40" />
<Key frame="211" value="8" />
<Key frame="222" value="-15" />
<Key frame="231" value="0" />
<Key frame="250" value="6.82441" />
</ActuatorCurve>
<ActuatorCurve name="value" actuator="LHipPitch" mute="0" unit="0">
<Key frame="15" value="0" />
<Key frame="25" value="0" />
<Key frame="35" value="-19.8395" />
<Key frame="45" value="-0.0382666" />
<Key frame="55" value="0" />
<Key frame="65" value="-40" />
<Key frame="75" value="-35" />
<Key frame="85" value="-60" />
<Key frame="95" value="-60" />
<Key frame="105" value="-60" />
<Key frame="118" value="-60" />
<Key frame="131" value="-60" />
<Key frame="142" value="-59.6787" />
<Key frame="152" value="-50" />
<Key frame="162" value="-42.5" />
<Key frame="172" value="-7" />
<Key frame="185" value="-50" />
<Key frame="198" value="0" />
<Key frame="211" value="-50" />
<Key frame="222" value="-37.5" />
<Key frame="231" value="-60" />
<Key frame="250" value="7.30057" />
</ActuatorCurve>
<ActuatorCurve name="value" actuator="LKneePitch" mute="0" unit="0">
<Key frame="15" value="0" />
<Key frame="25" value="0" />
<Key frame="35" value="42.8009" />
<Key frame="45" value="0.0951225" />
<Key frame="55" value="-5.69182e-06" />
<Key frame="65" value="80" />
<Key frame="75" value="70" />
<Key frame="85" value="120" />
<Key frame="95" value="120" />
<Key frame="105" value="120" />
<Key frame="118" value="120" />
<Key frame="131" value="120" />
<Key frame="142" value="120.887" />
<Key frame="152" value="100" />
<Key frame="162" value="85" />
<Key frame="172" value="7" />
<Key frame="185" value="100" />
<Key frame="198" value="0" />
<Key frame="211" value="100" />
<Key frame="222" value="75" />
<Key frame="231" value="120" />
<Key frame="250" value="-5.09006" />
</ActuatorCurve>
<ActuatorCurve name="value" actuator="LAnklePitch" mute="0" unit="0">
<Key frame="15" value="5.75265e-06" />
<Key frame="25" value="0" />
<Key frame="35" value="-22.1511" />
<Key frame="45" value="-0.569877" />
<Key frame="55" value="0" />
<Key frame="65" value="-37.1" />
<Key frame="75" value="-35" />
<Key frame="85" value="-60" />
<Key frame="95" value="-60" />
<Key frame="105" value="-60" />
<Key frame="118" value="-60" />
<Key frame="131" value="-60" />
<Key frame="142" value="-59.6787" />
<Key frame="152" value="-50" />
<Key frame="162" value="-42.5" />
<Key frame="172" value="0" />
<Key frame="185" value="5.75265e-06" />
<Key frame="198" value="30" />
<Key frame="211" value="5.75265e-06" />
<Key frame="217" value="-31.8" />
<Key frame="222" value="-37.5" />
<Key frame="231" value="-60" />
<Key frame="250" value="5.00876" />
</ActuatorCurve>
<ActuatorCurve name="value" actuator="LAnkleRoll" mute="0" unit="0">
<Key frame="15" value="3" />
<Key frame="25" value="7" />
<Key frame="35" value="9.83048" />
<Key frame="45" value="-6.94104" />
<Key frame="55" value="-6" />
<Key frame="65" value="10" />
<Key frame="75" value="-15" />
<Key frame="85" value="3.6" />
<Key frame="95" value="4.2" />
<Key frame="105" value="0.5" />
<Key frame="118" value="1.8" />
<Key frame="131" value="0.5" />
<Key frame="142" value="6.97225" />
<Key frame="152" value="0" />
<Key frame="162" value="-13.8" />
<Key frame="167" value="-31.9" />
<Key frame="172" value="-24.3" />
<Key frame="185" value="-20" />
<Key frame="198" value="0" />
<Key frame="211" value="-20" />
<Key frame="222" value="-17.9" />
<Key frame="231" value="0" />
<Key frame="250" value="-6.34798" />
</ActuatorCurve>
<ActuatorCurve name="value" actuator="RHipRoll" mute="0" unit="0">
<Key frame="15" value="3" />
<Key frame="25" value="-7" />
<Key frame="35" value="-24.8982" />
<Key frame="45" value="-6.04821" />
<Key frame="55" value="-6" />
<Key frame="65" value="-20" />
<Key frame="75" value="-45" />
<Key frame="85" value="-31" />
<Key frame="95" value="-12.7" />
<Key frame="105" value="-25.6" />
<Key frame="118" value="-40" />
<Key frame="131" value="-25.6" />
<Key frame="142" value="15.2077" />
<Key frame="152" value="0" />
<Key frame="162" value="-20" />
<Key frame="172" value="-30.9" />
<Key frame="185" value="-29.3" />
<Key frame="198" value="-34.2" />
<Key frame="211" value="-35" />
<Key frame="222" value="-30.5" />
<Key frame="231" value="0" />
<Key frame="250" value="-6.82407" />
</ActuatorCurve>
<ActuatorCurve name="value" actuator="RHipPitch" mute="0" unit="0">
<Key frame="15" value="0" />
<Key frame="25" value="0" />
<Key frame="35" value="0.134711" />
<Key frame="45" value="0.21464" />
<Key frame="55" value="0" />
<Key frame="65" value="-40" />
<Key frame="75" value="-10" />
<Key frame="85" value="-6" />
<Key frame="95" value="-7" />
<Key frame="105" value="-50" />
<Key frame="118" value="0" />
<Key frame="131" value="-50" />
<Key frame="142" value="-45.3545" />
<Key frame="152" value="-50" />
<Key frame="162" value="-60" />
<Key frame="172" value="-60" />
<Key frame="185" value="-60" />
<Key frame="198" value="-60" />
<Key frame="211" value="-60" />
<Key frame="222" value="-60" />
<Key frame="231" value="-60" />
<Key frame="250" value="7.30058" />
</ActuatorCurve>
<ActuatorCurve name="value" actuator="RKneePitch" mute="0" unit="0">
<Key frame="15" value="0" />
<Key frame="25" value="0" />
<Key frame="35" value="0.178188" />
<Key frame="45" value="-0.410883" />
<Key frame="55" value="0" />
<Key frame="65" value="80" />
<Key frame="75" value="20" />
<Key frame="85" value="7" />
<Key frame="95" value="7" />
<Key frame="105" value="100" />
<Key frame="118" value="0" />
<Key frame="131" value="100" />
<Key frame="142" value="90.0035" />
<Key frame="152" value="100" />
<Key frame="162" value="120" />
<Key frame="172" value="120" />
<Key frame="185" value="120" />
<Key frame="198" value="120" />
<Key frame="211" value="120" />
<Key frame="222" value="120" />
<Key frame="231" value="120" />
<Key frame="250" value="-5.26102" />
</ActuatorCurve>
<ActuatorCurve name="value" actuator="RAnklePitch" mute="0" unit="0">
<Key frame="15" value="5.75266e-06" />
<Key frame="25" value="0" />
<Key frame="35" value="0.266079" />
<Key frame="45" value="-0.349163" />
<Key frame="55" value="0" />
<Key frame="65" value="-40" />
<Key frame="75" value="-10" />
<Key frame="85" value="0" />
<Key frame="95" value="0" />
<Key frame="105" value="5.75266e-06" />
<Key frame="118" value="30" />
<Key frame="131" value="5.75266e-06" />
<Key frame="142" value="-45.8771" />
<Key frame="152" value="-50" />
<Key frame="162" value="-60" />
<Key frame="172" value="-60" />
<Key frame="185" value="-60" />
<Key frame="198" value="-60" />
<Key frame="211" value="-60" />
<Key frame="222" value="-60" />
<Key frame="231" value="-60" />
<Key frame="250" value="5.00876" />
</ActuatorCurve>
<ActuatorCurve name="value" actuator="RAnkleRoll" mute="0" unit="0">
<Key frame="15" value="-3" />
<Key frame="25" value="7.5" />
<Key frame="35" value="22.6325" />
<Key frame="45" value="6.68218" />
<Key frame="55" value="6" />
<Key frame="65" value="16.9" />
<Key frame="75" value="35.6" />
<Key frame="85" value="45" />
<Key frame="95" value="42.6" />
<Key frame="105" value="25" />
<Key frame="118" value="0" />
<Key frame="131" value="25" />
<Key frame="142" value="9.83317" />
<Key frame="152" value="0" />
<Key frame="162" value="-10" />
<Key frame="172" value="-2.41531" />
<Key frame="185" value="-3.18747" />
<Key frame="198" value="-0.718272" />
<Key frame="211" value="-0.558977" />
<Key frame="222" value="-1.55043" />
<Key frame="231" value="0" />
<Key frame="250" value="6.34774" />
</ActuatorCurve>
<ActuatorCurve name="RHipYawPitch (RHipYawPitch)" actuator="RHipYawPitch" mute="0" unit="0">
<Key frame="15" value="-6.2" />
<Key frame="25" value="-6.9" />
<Key frame="35" value="-7.55628" />
<Key frame="45" value="-7.04938" />
<Key frame="55" value="-8.2" />
<Key frame="65" value="-9.6" />
<Key frame="142" value="0.178188" />
<Key frame="250" value="-9.74084" />
</ActuatorCurve>
</ActuatorList>
</Timeline>
<Resource name="Standing" type="Lock" timeout="1" />
<Resource name="All motors" type="Lock" timeout="1" />
</Box>
<Link inputowner="11" indexofinput="2" outputowner="0" indexofoutput="2" />
<Link inputowner="0" indexofinput="4" outputowner="11" indexofoutput="4" />
</Diagram>
</BehaviorKeyframe>
</BehaviorLayer>
</Timeline>
</Box>
</ChoregrapheProject>
@@ -0,0 +1,3 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE TS>
<TS version="2.1" language="en_US"/>
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+239 -103
View File
@@ -15,6 +15,10 @@ import os
import json import json
import struct import struct
import socket import socket
import importlib.util
import shutil
import tempfile
import inspect
# Import the new music module # Import the new music module
from naomusic import NaoMusicPlayer from naomusic import NaoMusicPlayer
@@ -456,6 +460,115 @@ class VideoServerLauncher:
self.ready = False self.ready = False
# ==========================================
# CHOREGRAPHE BEHAVIOR DEPLOY + RUN (for gestures)
# ==========================================
class ChoregrapheBehaviorRunner:
"""Deploys a Choregraphe-exported behavior folder (a manifest.xml
plus one or more '<BehaviorName>/behavior.xar' dirs, i.e. exactly
what "Export as package" produces before you zip it) to the robot
over SFTP, installs it via the PackageManager service, and runs it
via ALBehaviorManager - the same three steps Choregraphe's own
"Connect to robot" + Play button does, done here in code so a
gesture script can just point at a local project folder.
Caches per local folder for the lifetime of this object: once a
package has been deployed+installed this session, repeat runs
just call runBehavior() again instead of re-uploading.
NOTE: PackageManager.install() is documented as "install a package
from a path" without saying whether that path can be a raw folder
or must be a zipped .pkg - this tries the raw folder first (since
that's genuinely what Choregraphe's live "current project" push
to a connected robot does) and falls back to zipping it into a
.pkg if the robot's PackageManager rejects the folder.
"""
def __init__(self, session, nao_ip, ssh_user, ssh_password, ssh_port):
self.nao_ip = nao_ip
self.ssh_user = ssh_user
self.ssh_password = ssh_password
self.ssh_port = ssh_port
try:
self.package_mgr = session.service("PackageManager")
except Exception:
self.package_mgr = session.service("ALPackageManager")
self.behavior_mgr = session.service("ALBehaviorManager")
self._installed = {} # local_dir -> resolved "<package>/<behavior>" name
def _sftp_put_dir(self, sftp, local_dir, remote_dir):
"""Recursively mirror local_dir onto remote_dir over an
already-open SFTP session - paramiko has no built-in recursive
put, only single-file put()."""
try:
sftp.mkdir(remote_dir)
except IOError:
pass # already exists, fine
for entry in sorted(os.listdir(local_dir)):
local_path = os.path.join(local_dir, entry)
remote_path = remote_dir + "/" + entry
if os.path.isdir(local_path):
self._sftp_put_dir(sftp, local_path, remote_path)
else:
sftp.put(local_path, remote_path)
def ensure_running(self, local_dir, behavior_subdir):
"""Deploy+install local_dir (only if not already done this
session), then block until '<package>/<behavior_subdir>' has
finished running on the robot. local_dir's own folder name
becomes the package id, matching how Choregraphe names an
exported project after its folder."""
if not HAS_SSH:
raise RuntimeError("Choregraphe gesture deploy needs paramiko: pip install paramiko")
if not os.path.isdir(local_dir):
raise RuntimeError(f"No Choregraphe project folder at {local_dir}")
package_name = os.path.basename(os.path.normpath(local_dir))
behavior_name = self._installed.get(local_dir)
if behavior_name is None:
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(self.nao_ip, port=self.ssh_port, username=self.ssh_user,
password=self.ssh_password, timeout=10)
try:
sftp = ssh.open_sftp()
try:
remote_home = sftp.normalize(".")
remote_dir = f"{remote_home}/naowalk_gestures/{package_name}"
print(f"🕺 Copying {package_name} -> {self.nao_ip}:{remote_dir}")
self._sftp_put_dir(sftp, local_dir, remote_dir)
print(f"🕺 Installing package {package_name}...")
try:
self.package_mgr.install(remote_dir)
except Exception as e:
print(f"🕺 install() on the raw folder failed ({e}) - "
f"retrying as a zipped .pkg...")
local_zip = shutil.make_archive(
os.path.join(tempfile.gettempdir(), package_name), "zip", local_dir)
remote_zip = f"{remote_home}/naowalk_gestures/{package_name}.pkg"
sftp.put(local_zip, remote_zip)
os.remove(local_zip)
self.package_mgr.install(remote_zip)
finally:
sftp.close()
finally:
ssh.close()
behavior_name = f"{package_name}/{behavior_subdir}"
if not self.behavior_mgr.isBehaviorInstalled(behavior_name):
installed = self.behavior_mgr.getInstalledBehaviors()
candidates = [b for b in installed if b.endswith("/" + behavior_subdir)]
if not candidates:
raise RuntimeError(
f"'{behavior_name}' not found after install - installed behaviors: {installed}")
behavior_name = candidates[0]
self._installed[local_dir] = behavior_name
print(f"🕺 Running {behavior_name}...")
self.behavior_mgr.runBehavior(behavior_name) # blocks until it finishes
# ========================================== # ==========================================
# MAIN TELEOP # MAIN TELEOP
# ========================================== # ==========================================
@@ -464,9 +577,13 @@ class NaoTeleop:
def __init__(self, session, nao_ip, audio_device_index=None, mic_gain=8.0, video_scale=2.0, def __init__(self, session, nao_ip, audio_device_index=None, mic_gain=8.0, video_scale=2.0,
nao_ssh_user="nao", nao_ssh_password="nao", nao_ssh_port=22, relay_rate=16000, nao_ssh_user="nao", nao_ssh_password="nao", nao_ssh_port=22, relay_rate=16000,
media_relay=None, deploy_video_server=True, video_server_path=None): media_relay=None, deploy_video_server=True, video_server_path=None,
gestures_dir=None):
self.session = session self.session = session
self.nao_ip = nao_ip self.nao_ip = nao_ip
self.nao_ssh_user = nao_ssh_user
self.nao_ssh_password = nao_ssh_password
self.nao_ssh_port = nao_ssh_port
self.motion = session.service("ALMotion") self.motion = session.service("ALMotion")
self.posture = session.service("ALRobotPosture") self.posture = session.service("ALRobotPosture")
self.tts = session.service("ALTextToSpeech") self.tts = session.service("ALTextToSpeech")
@@ -507,9 +624,12 @@ class NaoTeleop:
self.battery_level = 100 self.battery_level = 100
self.last_batt_check = 0 self.last_batt_check = 0
self.volume = 50 self.volume = 50
self.walk_speed = 0.6 self.walk_speed = 1.0 # fixed, not adjustable at runtime - 0.60 was too slow
# for the walk engine to stay balanced and NAO kept
# tipping over
self.typing_mode = False self.typing_mode = False
self.is_resting = False # tracks manual [DEL] crouch, toggled by idle_crouch()
self.chat_message = "" self.chat_message = ""
self.music_search_mode = False self.music_search_mode = False
self.music_search_text = "" self.music_search_text = ""
@@ -546,6 +666,16 @@ class NaoTeleop:
if HAS_AUDIO: if HAS_AUDIO:
self._init_mic_stream() self._init_mic_stream()
# Lets gesture scripts run full Choregraphe-authored behaviors
# (.xar, exported as a package folder) instead of hand-coding
# angleInterpolation calls - see ChoregrapheBehaviorRunner.
self.behavior_runner = ChoregrapheBehaviorRunner(
session, nao_ip, nao_ssh_user, nao_ssh_password, nao_ssh_port)
self.gestures_dir = gestures_dir or os.path.join(
os.path.dirname(os.path.abspath(__file__)), "gestures")
self._load_gestures()
pygame.init() pygame.init()
self.screen = pygame.display.set_mode((self.display_width, self.display_height)) self.screen = pygame.display.set_mode((self.display_width, self.display_height))
pygame.display.set_caption("NAO Teleop + Music on Robot") pygame.display.set_caption("NAO Teleop + Music on Robot")
@@ -693,95 +823,104 @@ class NaoTeleop:
except Exception as e: except Exception as e:
print(f"❌ Mic init failed: {e}") print(f"❌ Mic init failed: {e}")
def wave_gesture(self): def _load_gestures(self):
if self.typing_mode: return """Load number-key gestures from .py files in self.gestures_dir.
print("🤚 Waving hello...")
Each file is named '<digit>_<name>.py' (e.g. '4_gangnam_style.py')
and must define a module-level run(motion, tts) function - that's
the minimum contract, gets the already-connected ALMotion and
ALTextToSpeech proxies and does whatever it wants with them.
Gestures that need more - e.g. running a Choregraphe behavior via
self.behavior_runner, or resetting posture via
self.stand_for_walk() - can instead define run(motion, tts,
teleop) and get this NaoTeleop instance as a third argument; the
arity is detected here at load time so both signatures work. The
leading digit is which number key (0-9) triggers it; everything
after the underscore becomes its display name in the controls
hint, unless the file sets its own module-level NAME string.
"""
self.gestures = {} # pygame key constant -> (display_name, run_fn, n_params)
gestures_dir = self.gestures_dir
if not os.path.isdir(gestures_dir):
print(f"⚠️ No gestures folder at {gestures_dir}, skipping gesture load")
return
for fname in sorted(os.listdir(gestures_dir)):
if not fname.endswith(".py") or fname.startswith("_"):
continue
stem = fname[:-3]
digit_str, sep, rest = stem.partition("_")
if not (sep and len(digit_str) == 1 and digit_str.isdigit()):
print(f"⚠️ Skipping {fname}: name it '<digit>_name.py' (e.g. '4_gangnam_style.py')")
continue
path = os.path.join(gestures_dir, fname)
try: try:
self.motion.setStiffnesses("RArm", 1.0) spec = importlib.util.spec_from_file_location(f"nao_gesture_{stem}", path)
names = ["RShoulderPitch", "RShoulderRoll", "RElbowYaw", "RElbowRoll", "RWristYaw", "RHand"] module = importlib.util.module_from_spec(spec)
angles = [[-1.5]*5, [-0.4,-0.7,-0.4,-0.7,-0.4], [0.6,1.1,0.6,1.1,0.6], spec.loader.exec_module(module)
[0.8,0.3,0.8,0.3,0.8], [0.0,1.5,0.0,-1.5,0.0], [1.0]*5] run_fn = getattr(module, "run", None)
times = [[0.5,1.0,1.5,2.0,2.5]] * 6 if not callable(run_fn):
self.motion.angleInterpolation(names, angles, times, True) print(f"⚠️ Skipping {fname}: no run(motion, tts) function defined")
neutral = [1.5, -0.15, 1.2, 0.5, 0.0, 0.0] continue
self.motion.angleInterpolationWithSpeed(names, neutral, 0.3)
# Relaxed stiffness after completion
self.motion.setStiffnesses("RArm", 0.3)
except Exception as e:
print(f"Wave error: {e}")
def cena_gesture(self):
if self.typing_mode: return
print("👋 YOU CAN'T SEE ME!")
try: try:
self.tts.say("You can't see me") n_params = len(inspect.signature(run_fn).parameters)
except (TypeError, ValueError):
self.motion.setStiffnesses("RArm", 1.0) n_params = 2
names = ["RShoulderPitch", "RShoulderRoll", "RElbowYaw", "RElbowRoll", "RWristYaw", "RHand"]
# Setup for tight, face-level sweep
tight_setup = [-0.4, -0.2, 0.0, 1.2, 1.5, 1.0]
self.motion.angleInterpolation(names, tight_setup, [0.5]*6, True)
# The horizontal "backhand" sweep
self.motion.angleInterpolation(["RElbowYaw"], [0.8], 0.4, True)
self.motion.angleInterpolation(["RElbowYaw"], [-0.8], 0.6, True)
self.motion.angleInterpolation(["RElbowYaw"], [0.0], 0.4, True)
# Return to neutral
neutral = [1.5, -0.15, 1.2, 0.5, 0.0, 0.0]
self.motion.angleInterpolationWithSpeed(names, neutral, 0.3)
# Relaxed stiffness after completion
self.motion.setStiffnesses("RArm", 0.3)
except Exception as e: except Exception as e:
print(f"Cena gesture error: {e}") print(f"⚠️ Failed to load gesture {fname}: {e}")
continue
def six_seven_gesture(self): key = getattr(pygame, f"K_{digit_str}")
if self.typing_mode: return name = getattr(module, "NAME", rest.replace("_", " ").title() or stem)
print("🖐️ SIX... SEVEN...") if key in self.gestures:
print(f"⚠️ {fname} overrides number key {digit_str} (was {self.gestures[key][0]})")
self.gestures[key] = (name, run_fn, n_params)
print(f"✅ Loaded gesture: {digit_str}={name}")
def run_gesture(self, key):
if self.typing_mode or self.is_resting: return
entry = self.gestures.get(key)
if not entry: return
name, run_fn, n_params = entry
print(f"🕺 {name}...")
try: try:
threading.Thread(target=self.tts.say, args=("six seven",), daemon=True).start() if n_params >= 3:
run_fn(self.motion, self.tts, self)
self.motion.setStiffnesses(["RArm", "LArm"], 1.0) else:
run_fn(self.motion, self.tts)
# Lock both arms up in front of the body, hands held out like a scale.
# ElbowYaw is rotated ~90° off zero here - that's what points the elbow's
# bend axis so flexing tips the forearm forward/down instead of curling
# it in across the chest. These joints don't move again until the return.
hold_names = [
"RShoulderPitch", "RShoulderRoll", "RElbowYaw", "RWristYaw", "RHand",
"LShoulderPitch", "LShoulderRoll", "LElbowYaw", "LWristYaw", "LHand"
]
hold_angles = [
0.2, -0.2, 1.4, 1.57, 0.6,
0.2, 0.2, -1.4, -1.57, 0.6
]
self.motion.angleInterpolationWithSpeed(hold_names, hold_angles, 0.25)
# Only the elbows seesaw now - a small tip back and forth, one arm
# rising while the other falls, like weighing something in each hand
# ("six... seven..."). Small amplitude so it stays a wobble, not a fold.
elbow_names = ["RElbowRoll", "LElbowRoll"]
r_low, r_high = 0.6, 1.0
l_low, l_high = -0.6, -1.0
elbow_angles = [
[r_low, r_high, r_low, r_high, r_low, r_high],
[l_high, l_low, l_high, l_low, l_high, l_low],
]
times = [[0.35, 0.7, 1.05, 1.4, 1.75, 2.1]] * 2
self.motion.angleInterpolation(elbow_names, elbow_angles, times, True)
# Return everything to neutral
neutral_names = hold_names + elbow_names
neutral = [1.5, -0.15, 1.2, 0.0, 0.0,
1.5, 0.15, -1.2, 0.0, 0.0,
0.5, -0.5]
self.motion.angleInterpolationWithSpeed(neutral_names, neutral, 0.3)
# Relaxed stiffness after completion
self.motion.setStiffnesses(["RArm", "LArm"], 0.3)
except Exception as e: except Exception as e:
print(f"Six seven gesture error: {e}") print(f"{name} gesture error: {e}")
def stand_for_walk(self):
"""Re-stiffen and drop straight into walk-ready posture -
mirrors the startup sequence in run() (stiffen -> walk arms ->
relax arm stiffness -> moveInit) rather than
goToPosture("Stand"), which goes through its own separate
stand-up animation instead of just being walk-ready. Shared by
the [DEL] wake-up toggle and by any gesture that needs to
reset his stance afterward (e.g. after a Choregraphe behavior
that moved the legs/hips)."""
self._safe("Restiffen", lambda: self.motion.setStiffnesses("Body", 1.0))
self._safe("Walk arms", lambda: self.motion.setMoveArmsEnabled(True, True))
self._safe("Relax arms", lambda: self.motion.setStiffnesses(["RArm", "LArm"], 0.3))
self._safe("moveInit", self.motion.moveInit)
self.is_resting = False
def idle_crouch(self):
"""The relaxed sit/crouch posture NAO settles into on shutdown -
toggled on demand with the Delete key. rest() cuts stiffness to
the whole body (that's the point of "rest"), so simply calling
it again does nothing on the second press - we have to
explicitly re-stiffen ourselves via stand_for_walk()."""
if not self.is_resting:
print("😴 Resting (idle crouch)...")
self._safe("stopMove", self.motion.stopMove)
self._safe("rest", self.motion.rest)
self.is_resting = True
else:
print("🧍 Waking up...")
self.stand_for_walk()
def handle_head_movement(self): def handle_head_movement(self):
if self.typing_mode or self.music_search_mode: return if self.typing_mode or self.music_search_mode: return
@@ -872,7 +1011,11 @@ class NaoTeleop:
self._movement_thread = threading.Thread(target=self._movement_loop, daemon=True) self._movement_thread = threading.Thread(target=self._movement_loop, daemon=True)
self._movement_thread.start() self._movement_thread.start()
print("\n🎮 Controls: WASD/Arrows=Walk | [/]=Speed | IJKL=Head | SPACE=Reset head | 1=Wave | 2=Cena | 3=6-7 | M=Music search | X=Stop | -/==Volume | T=TTS | V=Push-to-talk (hold) | ESC=Quit") gesture_hint = " | ".join(
f"{pygame.key.name(key)}={name}" for key, (name, *_rest) in sorted(self.gestures.items())
)
print(f"\n🎮 Controls: WASD/Arrows=Walk | IJKL=Head | SPACE=Reset head | {gesture_hint} | "
f"DEL=Toggle crouch/stand | M=Music search | X=Stop | -/==Volume | T=TTS | V=Push-to-talk (hold) | ESC=Quit")
try: try:
while self.running: while self.running:
@@ -948,12 +1091,10 @@ class NaoTeleop:
else: else:
if event.key == pygame.K_ESCAPE: if event.key == pygame.K_ESCAPE:
self.running = False self.running = False
elif event.key == pygame.K_1: elif event.key == pygame.K_DELETE:
self.wave_gesture() self.idle_crouch()
elif event.key == pygame.K_2: elif event.key in self.gestures:
self.cena_gesture() self.run_gesture(event.key)
elif event.key == pygame.K_3:
self.six_seven_gesture()
elif event.key == pygame.K_SPACE: elif event.key == pygame.K_SPACE:
self.reset_head() self.reset_head()
elif event.key == pygame.K_m: elif event.key == pygame.K_m:
@@ -984,16 +1125,6 @@ class NaoTeleop:
if getattr(self, "sound_receiver", None): if getattr(self, "sound_receiver", None):
self.sound_receiver.muted = False self.sound_receiver.muted = False
speed_changed = False
if keys[pygame.K_LEFTBRACKET]:
self.walk_speed = max(0.1, self.walk_speed - 0.01)
speed_changed = True
if keys[pygame.K_RIGHTBRACKET]:
self.walk_speed = min(1.0, self.walk_speed + 0.01)
speed_changed = True
if speed_changed:
self.update_title()
x = y = theta = 0.0 x = y = theta = 0.0
if keys[pygame.K_w] or keys[pygame.K_UP]: x = self.walk_speed if keys[pygame.K_w] or keys[pygame.K_UP]: x = self.walk_speed
if keys[pygame.K_s] or keys[pygame.K_DOWN]: x = -self.walk_speed if keys[pygame.K_s] or keys[pygame.K_DOWN]: x = -self.walk_speed
@@ -1161,6 +1292,10 @@ if __name__ == "__main__":
parser.add_argument("--video-server-path", type=str, default=None, parser.add_argument("--video-server-path", type=str, default=None,
help="Local path to nao_video_server.py to deploy (default: the " help="Local path to nao_video_server.py to deploy (default: the "
"copy sitting next to naowalk.py)") "copy sitting next to naowalk.py)")
parser.add_argument("--gestures-dir", type=str, default=None,
help="Folder of number-key gestures to load, each file named "
"'<digit>_name.py' with a run(motion, tts) function "
"(default: the 'gestures' folder next to naowalk.py)")
args = parser.parse_args() args = parser.parse_args()
session = qi.Session() session = qi.Session()
@@ -1181,4 +1316,5 @@ if __name__ == "__main__":
nao_ssh_port=args.nao_ssh_port, nao_ssh_port=args.nao_ssh_port,
relay_rate=args.relay_rate, relay_rate=args.relay_rate,
deploy_video_server=not args.no_deploy_video_server, deploy_video_server=not args.no_deploy_video_server,
video_server_path=args.video_server_path).run() video_server_path=args.video_server_path,
gestures_dir=args.gestures_dir).run()
Executable
+2
View File
@@ -0,0 +1,2 @@
#!/bin/bash
nmcli c up naorobotwifi\ \(WAP\) && nmcli c up BC-VR\ \(USBWIFI\)
Submodule other-repos/nao-dances added at 5cae0354f5