Note
Hello, welcome to the SunFounder Raspberry Pi & Arduino & ESP32 Enthusiasts Community on Facebook! Dive deeper into Raspberry Pi, Arduino, and ESP32 with fellow enthusiasts.
Why Join?
Expert Support: Solve post-sale issues and technical challenges with help from our community and team.
Learn & Share: Exchange tips and tutorials to enhance your skills.
Exclusive Previews: Get early access to new product announcements and sneak peeks.
Special Discounts: Enjoy exclusive discounts on our newest products.
Festive Promotions and Giveaways: Take part in giveaways and holiday promotions.
👉 Ready to explore and create with us? Click [here] and join today!
14. Action Gallery
This example provides an interactive menu that lets you trigger a variety of pre-programmed actions for PiCrawler, including waving, shaking hands, fighting, nodding, push-ups, and more.
Run the Code
cd ~/picrawler/examples
sudo python3 14_action_gallery.py
After running the program, a numbered list of available actions is printed in the terminal. Type the number of the action you want, then press Enter to execute it. Press Enter again to repeat the last action.
If Ctrl+C is pressed, the program exits safely and the robot returns to a sitting position.
Code
Note
You can Modify/Reset/Copy/Run/Stop the code below. But before that, you
need to go to source code path like picrawler\examples. After modifying
the code, you can run it directly to see the effect.
from time import sleep
BODY_LENGTH = 77
BODY_WIDTH = 77
BODY_DIAGONAL = 108.9
DELTA = 45
def sit(spider):
spider.do_action('sit', speed=40)
def stand(spider):
spider.do_action('stand', speed=40)
def look_up(spider):
coords = [
# stand
[[45, 45, -50], [45, 0, -50], [45, 0, -50], [45, 45, -50]],
[[45, 45, -76], [45, 0, -76], [45, 0, -38], [45, 45, -30]],
]
for coord in coords:
spider.do_step(coord, 60)
def look_down(spider):
coords = [
# stand
[[45, 45, -50], [45, 0, -50], [45, 0, -50], [45, 45, -50]],
[[45, 45, -28], [45, 0, -40], [45, 0, -68], [45, 45, -76]],
]
for coord in coords:
spider.do_step(coord, 60)
def dance(spider):
spider.do_action('dance', speed=60)
def wave_hand(spider):
coords = [
# stand
[[45, 45, -50], [45, 0, -50], [45, 0, -50], [45, 45, -50]],
# wave hand
[[45, 45, -70], [60, 0, 120], [45, 0, -60], [45, 45, -30]],
[[45, 45, -70], [-20, 60, 120], [45, 0, -60], [45, 45, -30]],
[[45, 45, -70], [60, 0, 120], [45, 0, -60], [45, 45, -30]],
[[45, 45, -70], [-20, 60, 120], [45, 0, -60], [45, 45, -30]],
# return to stand
[[45, 45, -50], [45, 0, -30], [45, 0, -50], [45, 45, -50]],
[[45, 45, -50], [45, 0, -40], [45, 0, -50], [45, 45, -50]],
[[45, 45, -50], [45, 0, -50], [45, 0, -50], [45, 45, -50]],
]
for coord in coords:
spider.do_step(coord, 58)
def shake_hand(spider):
coords = [
# stand
[[45, 45, -50], [45, 0, -50], [45, 0, -50], [45, 45, -50]],
# shake hand
[[45, 45, -65], [5, 280, 80], [45, 0, -60], [45, 45, -40]],
[[45, 45, -65], [5, 280, 100], [45, 0, -60], [45, 45, -40]],
[[45, 45, -65], [5, 280, -10], [45, 0, -60], [45, 45, -40]],
[[45, 45, -65], [5, 280, 100], [45, 0, -60], [45, 45, -40]],
[[45, 45, -65], [5, 280, -10], [45, 0, -60], [45, 45, -40]],
[[45, 45, -65], [5, 280, 100], [45, 0, -60], [45, 45, -40]],
[[45, 45, -65], [5, 280, -10], [45, 0, -60], [45, 45, -40]],
[[45, 45, -65], [5, 100, 10], [45, 0, -60], [45, 45, -40]],
[[45, 45, -65], [5, 100, 10], [45, 0, -60], [45, 45, -40]],
# return to stand
[[45, 45, -50], [45, 0, -30], [45, 0, -50], [45, 45, -50]],
[[45, 45, -50], [45, 0, -40], [45, 0, -50], [45, 45, -50]],
[[45, 45, -50], [45, 0, -50], [45, 0, -50], [45, 45, -50]],
]
for coord in coords:
spider.do_step(coord, 52)
def fighting(spider):
ready = [
# stand
[[45, 45, -50], [45, 0, -50], [45, 0, -50], [45, 45, -50]],
# fighting ready
[[45, 45, -40], [45, 0, -40], [50, 20, -20], [45, 45, -50]],
[[45, 45, -40], [45, 0, -40], [40, 20, -45], [45, 45, -50]],
[[45, 45, -40], [45, 0, -40], [60, 40, -60], [45, 45, -40]],
#
[[45, 45, -40], [45, 30, -30], [60, 40, -60], [45, 45, -40]],
[[45, 45, -30], [45, 30, -30], [60, 40, -60], [60, 40, -60]],
]
twist_butt = [
# twist butt
[[55, 7, -30], [19, 48, -30], [77, 12, -60], [36, 63, -60]],
[[19, 48, -30], [55, 7, -30], [36, 63, -60], [77, 12, -60]],
#
[[55, 7, -30], [19, 48, -30], [77, 12, -60], [36, 63, -60]],
[[19, 48, -30], [55, 7, -30], [36, 63, -60], [77, 12, -60]],
#
[[40, 30, -30], [40, 30, -30], [60, 40, -60], [60, 40, -60]],
# shrink
[[40, 50, -30], [40, 50, -30], [60, 20, -60], [60, 20, -60]],
]
pounce_bite = [
[[40, 40, -60], [20, 60, 110], [60, 60, -60], [60, 60, -60]],
[[40, 40, -40], [20, 30, -40], [60, 60, -60], [60, 60, -60]],
[[20, 60, 110], [20, 30, -60], [60, 60, -60], [60, 60, -60]],
[[20, 30, -40], [20, 30, -40], [60, 60, -60], [60, 60, -60]],
]
return_stand = [
[[45, 45, -50], [45, 0, -30], [45, 0, -50], [45, 45, -50]],
[[45, 45, -50], [45, 0, -40], [45, 0, -50], [45, 45, -50]],
[[45, 45, -50], [45, 0, -50], [45, 0, -50], [45, 45, -50]],
]
for coord in ready:
spider.do_step(coord, 50)
for coord in twist_butt:
spider.do_step(coord, 52)
sleep(0.2)
for coord in pounce_bite:
spider.do_step(coord, 40)
sleep(0.1)
sleep(1)
for coord in return_stand:
spider.do_step(coord, 52)
def excited(spider):
coords = [
# stand
[[45, 45, -50], [45, 0, -50], [45, 0, -50], [45, 45, -50]],
#
[[45, 45, -30], [45, 0, -30], [45, 0, -30], [45, 45, -30]],
[[45, 45, -80], [45, 0, -80], [45, 0, -80], [45, 45, -80]],
[[45, 45, -30], [45, 0, -30], [45, 0, -30], [45, 45, -30]],
[[45, 45, -80], [45, 0, -80], [45, 0, -80], [45, 45, -80]],
[[45, 45, -30], [45, 0, -30], [45, 0, -30], [45, 45, -30]],
[[45, 45, -80], [45, 0, -80], [45, 0, -80], [45, 45, -80]],
#
[[45, 45, -50], [45, 0, -50], [45, 0, -50], [45, 45, -50]],
]
for coord in coords:
spider.do_step(coord, 40)
sleep(0.08)
def play_dead(spider):
sit = [
# stand
[[45, 45, -50], [45, 0, -50], [45, 0, -50], [45, 45, -50]],
#
[[45, 45, -10], [45, 0, -10], [45, 0, -10], [45, 45, -10]],
]
play_dead = [
[[45, 45, 100], [45, 45, 100], [45, 45, 100], [45, 45, 100]],
#
[[45, 35, 60], [35, 45, 80], [35, 45, 80], [45, 35, 60]],
[[35, 45, 80], [45, 35, 60], [45, 35, 60], [35, 45, 80]],
[[45, 35, 60], [35, 45, 80], [35, 45, 80], [45, 35, 60]],
[[35, 45, 80], [45, 35, 60], [45, 35, 60], [35, 45, 80]],
[[45, 35, 60], [35, 45, 80], [35, 45, 80], [45, 35, 60]],
[[35, 45, 80], [45, 35, 60], [45, 35, 60], [35, 45, 80]],
[[45, 35, 60], [35, 45, 80], [35, 45, 80], [45, 35, 60]],
[[35, 45, 80], [45, 35, 60], [45, 35, 60], [35, 45, 80]],
[[45, 35, 60], [35, 45, 80], [35, 45, 80], [45, 35, 60]],
[[35, 45, 80], [45, 35, 60], [45, 35, 60], [35, 45, 80]],
#
[[45, 45, 100], [45, 45, 100], [45, 45, 100], [45, 45, 100]],
]
return_stand = [
[[45, 45, -50], [45, 0, -50], [45, 0, -50], [45, 45, -50]],
]
for coord in sit:
spider.do_step(coord, 60)
for coord in play_dead:
spider.do_step(coord, 55)
for coord in return_stand:
spider.do_step(coord, 60)
def nod(spider):
stand = [
# stand
[[45, 45, -50], [45, 0, -50], [45, 0, -50], [45, 45, -50]],
]
nod = [
[[45, 45, -80], [45, 0, -50], [45, 0, -20], [45, 45, -30]],
[[45, 45, -20], [45, 0, -36], [45, 20, -52], [40, 20, -80]],
[[45, 45, -80], [45, 0, -50], [45, 0, -20], [45, 45, -30]],
[[45, 45, -20], [45, 0, -36], [45, 20, -52], [40, 20, -80]],
[[45, 45, -80], [45, 0, -50], [45, 0, -20], [45, 45, -30]],
]
return_stand = [
[[45, 45, -80], [45, 0, -50], [45, 0, -40], [45, 45, -40]],
[[45, 45, -60], [45, 0, -50], [45, 0, -40], [45, 45, -40]],
[[45, 45, -50], [45, 0, -50], [45, 0, -50], [45, 45, -50]],
]
for coord in stand:
spider.do_step(coord, 60)
for coord in nod:
spider.do_step(coord, 45)
sleep(.2)
for coord in return_stand:
spider.do_step(coord, 50)
sleep(1)
def shake_head(spider):
ready = [
# stand
[[45, 45, -50], [45, 0, -50], [45, 20, -50], [45, 45, -50]],
[[45, 45, -50], [45, 20, -30], [45, 20, -50], [45, 45, -50]],
[[45, 45, -50], [45, 45, -50], [45, 20, -50], [45, 45, -50]],
]
twist_butt = [
# twist butt
[[55, 7, -50], [19, 48, -50], [77, 12, -50], [36, 63, -50]],
[[19, 48, -50], [55, 7, -50], [36, 63, -50], [77, 12, -50]],
#
[[51, 15, -50], [27, 43, -50], [72, 22, -50], [45, 56, -50]],
[[27, 43, -50], [51, 15, -50], [45, 56, -50], [72, 22, -50]],
#
[[45, 45, -50], [45, 45, -50], [45, 45, -50], [45, 45, -50]],
]
return_stand = [
[[45, 45, -50], [45, 20, -30], [45, 0, -50], [45, 45, -50]],
[[45, 45, -50], [45, 0, -50], [45, 0, -50], [45, 45, -50]],
]
for coord in ready:
spider.do_step(coord, 50)
for coord in twist_butt:
spider.do_step(coord, 58)
sleep(.5)
for coord in return_stand:
spider.do_step(coord, 52)
def look_left(spider):
stand = [
[[45, 45, -50], [45, 0, -50], [45, 0, -50], [45, 45, -50]],
]
look_left = [
[[45, 0, -50], [45, 45, -50], [45, 45, -50], [45, 0, -50]],
[[0, 45, -50], [45, 45, -50], [45, 45, -50], [45, 0, -50]],
[[0, 45, -50], [45, 45, -35], [45, 45, -50], [45, 0, -50]],
[[45, 45, -50], [45, 0, -50], [45, 0, -50], [45, 45, -50]],
]
for coord in stand:
spider.do_step(coord, 50)
for coord in look_left:
spider.do_step(coord, 50)
def look_right(spider):
stand = [
[[45, 45, -50], [45, 0, -50], [45, 0, -50], [45, 45, -50]],
]
look_right = [
[[45, 45, -50], [45, 0, -50], [45, 0, -50], [45, 45, -50]],
[[45, 45, -50], [0, 45, -50], [45, 0, -50], [45, 45, -50]],
[[45, 45, -35], [0, 45, -50], [45, 0, -50], [45, 45, -50]],
[[45, 0, -50], [45, 45, -50], [45, 45, -50], [45, 0, -50]],
]
for coord in stand:
spider.do_step(coord, 50)
for coord in look_right:
spider.do_step(coord, 50)
def warm_up(spider):
stand = [
[[45, 45, -50], [45, 0, -50], [45, 0, -50], [45, 45, -50]],
[[45, 45, -50], [45, 45, -50], [45, 45, -50], [45, 45, -50]],
]
left_right = [
[[45, 37, -85], [45, 37, -14], [45, 37, -14], [45, 37, -85]],
[[45, 45, -50], [45, 45, -50], [45, 45, -50], [45, 45, -50]],
[[45, 37, -85], [45, 37, -14], [45, 37, -14], [45, 37, -85]],
[[45, 45, -50], [45, 45, -50], [45, 45, -50], [45, 45, -50]],
#
[[45, 37, -14], [45, 37, -85], [45, 37, -85], [45, 37, -14]],
[[45, 45, -50], [45, 45, -50], [45, 45, -50], [45, 45, -50]],
[[45, 37, -14], [45, 37, -85], [45, 37, -85], [45, 37, -14]],
[[45, 45, -50], [45, 45, -50], [45, 45, -50], [45, 45, -50]],
]
clockwise = []
clockwise.append(spider.move_list.move_body_absolute(0, 25, 10))
clockwise.append(spider.move_list.move_body_absolute(12.5, 21.65, 10))
clockwise.append(spider.move_list.move_body_absolute(21.65, 12.5, 10))
clockwise.append(spider.move_list.move_body_absolute(25, 0, 10))
clockwise.append(spider.move_list.move_body_absolute(21.65, -12.5, 10))
clockwise.append(spider.move_list.move_body_absolute(12.5, -21.65, 10))
clockwise.append(spider.move_list.move_body_absolute(0, -25, 10))
clockwise.append(spider.move_list.move_body_absolute(-12.5, -21.65, 10))
clockwise.append(spider.move_list.move_body_absolute(-21.65, -12.5, 10))
clockwise.append(spider.move_list.move_body_absolute(-25, 0, 10))
clockwise.append(spider.move_list.move_body_absolute(-21.65, 12.5, 10))
clockwise.append(spider.move_list.move_body_absolute(-12.5, 21.65, 10))
clockwise.append(spider.move_list.move_body_absolute(0, 25, 10))
anticlockwise = []
anticlockwise.append(spider.move_list.move_body_absolute(0, 25, 10))
anticlockwise.append(spider.move_list.move_body_absolute(-12.5, 21.65, 10))
anticlockwise.append(spider.move_list.move_body_absolute(-21.65, 12.5, 10))
anticlockwise.append(spider.move_list.move_body_absolute(-25, 0, 10))
anticlockwise.append(spider.move_list.move_body_absolute(-21.65, -12.5, 10))
anticlockwise.append(spider.move_list.move_body_absolute(-12.5, -21.65, 10))
anticlockwise.append(spider.move_list.move_body_absolute(0, -25, 10))
anticlockwise.append(spider.move_list.move_body_absolute(12.5, -21.65, 10))
anticlockwise.append(spider.move_list.move_body_absolute(21.65, -12.5, 10))
anticlockwise.append(spider.move_list.move_body_absolute(25, 0, 10))
anticlockwise.append(spider.move_list.move_body_absolute(21.65, 12.5, 10))
anticlockwise.append(spider.move_list.move_body_absolute(12.5, 21.65, 10))
anticlockwise.append(spider.move_list.move_body_absolute(0, 25, 10))
return_stand = [
[[45, 45, -50], [45, 45, -40], [45, 0, -50], [45, 45, -50]],
[[45, 45, -50], [45, 0, -50], [45, 0, -50], [45, 45, -50]],
]
for coord in stand:
spider.do_step(coord, 50)
sleep(0.5)
for coord in left_right:
spider.do_step(coord, 48)
sleep(.3)
for coord in clockwise:
spider.do_step(coord, 58)
sleep(.3)
for coord in anticlockwise:
spider.do_step(coord, 58)
sleep(.3)
for coord in return_stand:
spider.do_step(coord, 50)
def push_up(spider):
ready = [
# stand
[[45, 45, -50], [45, 0, -50], [45, 0, -50], [45, 45, -50]],
#
[[60, 10, -60], [60, 0, -60], [20, 60, 10], [10, 65, -40]],
[[70, 0, -76], [70, 0, -76], [0, 130, -40], [0, 130, -40]],
]
push_up = [
[[70, 0, -40], [70, 0, -40], [0, 130, -40], [0, 130, -40]],
[[70, 0, -76], [70, 0, -76], [0, 130, -40], [0, 130, -40]],
[[70, 0, -40], [70, 0, -40], [0, 130, -40], [0, 130, -40]],
[[70, 0, -76], [70, 0, -76], [0, 130, -40], [0, 130, -40]],
[[70, 0, -40], [70, 0, -40], [0, 130, -40], [0, 130, -40]],
[[70, 0, -76], [70, 0, -76], [0, 130, -40], [0, 130, -40]],
]
for coord in ready:
spider.do_step(coord,70)
for coord in push_up:
spider.do_step(coord, 35)
sleep(0.1)
actions_dict = {
"sit": sit,
"stand": stand,
"wave_hand": wave_hand,
"shake_hand": shake_hand,
"fighting": fighting,
"excited": excited,
"play_dead": play_dead,
"nod": nod,
"shake_head": shake_head,
"look_left": look_left,
"look_right": look_right,
"look_up": look_up,
"look_down": look_down,
"warm_up": warm_up,
"push_up": push_up,
}
sounds_dict = {
}
if __name__ == "__main__":
from picrawler import Picrawler
my_spider = Picrawler()
actions = list(actions_dict.keys())
for i, key in enumerate(actions):
print(f'{i} {key}')
last_key = None
try:
while True:
key = input()
if key == '':
print(actions[last_key])
actions_dict[actions[last_key]](my_spider)
else:
key = int(key)
if key > (len(actions) - 1):
print("Invalid key")
else:
last_key = key
print(actions[key])
actions_dict[actions[key]](my_spider)
except KeyboardInterrupt:
pass
except Exception as e:
print(f'Error:\n {e}')
finally:
my_spider.do_action("sit", speed=40)
sleep(.1)
How it works?
When the program starts, it builds a dictionary of available actions and prints a numbered menu.
actions_dict = { "sit": sit, "stand": stand, "wave_hand": wave_hand, "shake_hand": shake_hand, ... } for i, key in enumerate(actions): print(f'{i} {key}')
Each key in
actions_dictis a human-readable name, and each value is a function. This pattern makes it easy to look up and call the right function when the user enters a number.Interactive Menu Loop
while True: key = input() if key == '': actions_dict[actions[last_key]](my_spider) else: key = int(key) last_key = key actions_dict[actions[key]](my_spider)
The main loop waits for user input:
Enter a number to select and immediately execute an action.
Press Enter (empty input) to repeat the last action.
Press Ctrl+C to exit.
What Is a Coordinate Step?
Each pose is defined as a list of four leg coordinates in the format
[[x, y, z], [x, y, z], [x, y, z], [x, y, z]], corresponding to:Right front leg (index 0)
Left front leg (index 1)
Left rear leg (index 2)
Right rear leg (index 3)
Each coordinate has three values:
X: forward/backward position of the leg tip
Y: left/right position of the leg tip
Z: up/down position (more negative = higher up)
# A standing pose for all four legs [[45, 45, -50], [45, 0, -50], [45, 0, -50], [45, 45, -50]]
Playing a Sequence of Poses
Most actions are built from a list of coordinate frames. The
do_step()method smoothly interpolates the servos from the current pose to the target pose.coords = [ [[45, 45, -50], [45, 0, -50], [45, 0, -50], [45, 45, -50]], [[45, 45, -76], [45, 0, -76], [45, 0, -38], [45, 45, -30]], ] for coord in coords: spider.do_step(coord, 60)
The second argument to
do_step()is the speed (higher = faster). Typical values range from 40 (slow) to 80 (fast).Built-in vs. Custom Actions
Some functions use built-in actions via
do_action():def sit(spider): spider.do_action('sit', speed=40)
Others construct poses manually with
do_step(), giving you full control over the robot’s posture.def wave_hand(spider): coords = [ [[45, 45, -70], [60, 0, 120], [45, 0, -60], [45, 45, -30]], [[45, 45, -70], [-20, 60, 120], [45, 0, -60], [45, 45, -30]], ] for coord in coords: spider.do_step(coord, 58)
The waving motion alternates the right-front leg between two extreme positions, creating a waving gesture.
Adding Delays Between Movements
The
sleep()function inserts pauses between motion phases. This is useful for multi-phase actions likefighting()ornod():for coord in twist_butt: spider.do_step(coord, 52) sleep(0.2) for coord in pounce_bite: spider.do_step(coord, 40) sleep(0.1) sleep(1) for coord in return_stand: spider.do_step(coord, 52)
The pauses let each phase complete visually before the next one begins.
Using
move_body_absolute()for Circular MotionThe
warm_up()function demonstratesmove_body_absolute(), which shifts the body center relative to the feet:clockwise = [] clockwise.append(spider.move_list.move_body_absolute(0, 25, 10)) clockwise.append(spider.move_list.move_body_absolute(12.5, 21.65, 10)) ...
By stepping through points on a circle (using sine/cosine values), the robot performs a circular body sway — a warm-up exercise that loosens all joints.
Safe Exit
finally: my_spider.do_action("sit", speed=40) sleep(.1)
No matter how the program exits (Ctrl+C or error), the
finallyblock ensures the robot returns to a safe sitting position.