Matt Sunna
Published © GPL3+

LEGO Mindstorms Alexa-Enabled Dancing Robot

This dancing robot is one of my creations from a few years ago that has become popular over time. And now I can sync it to any song!

IntermediateFull instructions provided3 hours2,101
LEGO Mindstorms Alexa-Enabled Dancing Robot

Things used in this project

Story

Read more

Schematics

Dancing Robot Build Instructions

The build instructions and speed build video are both available here: https://teachkidsengineering.com/lego-mindstorms-dancing-robot/ Attached is the written instructions.

DancingRobot with EchoDot Video

Code

DancingRobot

Python
This code uses Example2 as a starting point so that it can capture the tempo of the music playing from Alexa. Added the Medium motor to control the robot's arm movements and sequenced the movements to make the robot dance to the music.
#!/usr/bin/env python3
# Copyright 2019 Amazon.com, Inc. or its affiliates.  All Rights Reserved.
# 
# You may not use this file except in compliance with the terms and conditions 
# set forth in the accompanying LICENSE.TXT file.
#
# THESE MATERIALS ARE PROVIDED ON AN "AS IS" BASIS. AMAZON SPECIFICALLY DISCLAIMS, WITH 
# RESPECT TO THESE MATERIALS, ALL WARRANTIES, EXPRESS, IMPLIED, OR STATUTORY, INCLUDING 
# THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT.

import os
import sys
import time
import logging
import threading
import random
from time import sleep
from PIL import Image


from agt import AlexaGadget

from ev3dev2.led import Leds
from ev3dev2.sound import Sound
from ev3dev2.motor import OUTPUT_B, OUTPUT_C, LargeMotor
from ev3dev2.motor import OUTPUT_A, MediumMotor, SpeedPercent #DancingRobotMod

# Set the logging level to INFO to see messages from AlexaGadget
logging.basicConfig(level=logging.INFO, stream=sys.stdout, format='%(message)s')
logging.getLogger().addHandler(logging.StreamHandler(sys.stderr))
logger = logging.getLogger(__name__)


class MindstormsGadget(AlexaGadget):
    """
    A Mindstorms gadget that performs movement in sync with music tempo.
    """

    def __init__(self):
        """
        Performs Alexa Gadget initialization routines and ev3dev resource allocation.
        """
        super().__init__()

        # Ev3dev initialization
        self.leds = Leds()
        self.sound = Sound()
        self.left_motor = LargeMotor(OUTPUT_B)
        self.right_motor = LargeMotor(OUTPUT_C)
        self.arms = MediumMotor(OUTPUT_A) # DancingRobotMod
        # Gadget states
        self.bpm = 0
        self.trigger_bpm = "off"

    def on_connected(self, device_addr):
        """
        Gadget connected to the paired Echo device.
        :param friendly_name: the friendly name of the gadget that has connected to the echo device
        """
        self.leds.set_color("LEFT", "GREEN")
        self.leds.set_color("RIGHT", "GREEN")
        logger.info("{} connected to Echo device".format(self.friendly_name))

    def on_disconnected(self, device_addr):
        """
        Gadget disconnected from the paired Echo device.
        :param friendly_name: the friendly name of the gadget that has disconnected from the echo device
        """
        self.leds.set_color("LEFT", "BLACK")
        self.leds.set_color("RIGHT", "BLACK")
        logger.info("{} disconnected from Echo device".format(self.friendly_name))

    def on_alexa_gadget_musicdata_tempo(self, directive):
        """
        Provides the music tempo of the song currently playing on the Echo device.
        :param directive: the music data directive containing the beat per minute value
        """
        tempo_data = directive.payload.tempoData
        for tempo in tempo_data:

            print("tempo value: {}".format(tempo.value), file=sys.stderr)
            if tempo.value > 0:
                # dance pose
                #self.right_motor.run_timed(speed_sp=750, time_sp=2500)  #DancingRobotMod
                #self.left_motor.run_timed(speed_sp=-750, time_sp=2500)  #DancingRobotMod
                self.leds.set_color("LEFT", "GREEN")
                self.leds.set_color("RIGHT", "GREEN")
                time.sleep(3)
                # starts the dance loop
                self.trigger_bpm = "on"
                threading.Thread(target=self._dance_loop, args=(tempo.value,)).start()

            elif tempo.value == 0:
                # stops the dance loop
                self.trigger_bpm = "off"
                self.leds.set_color("LEFT", "BLACK")
                self.leds.set_color("RIGHT", "BLACK")

    def _dance_loop(self, bpm):
        """
        Perform motor movement in sync with the beat per minute value from tempo data.
        :param bpm: beat per minute from AGT
        """
        color_list = ["GREEN", "RED", "AMBER", "YELLOW"]
        led_color = random.choice(color_list)
        motor_speed = 200 #DancingRobotMod (was 400)
        milli_per_beat = min(1000, (round(60000 / bpm)) * 0.65)
        print("Adjusted milli_per_beat: {}".format(milli_per_beat), file=sys.stderr)
        while self.trigger_bpm == "on":

            # Alternate led color and motor direction
            led_color = "BLACK" if led_color != "BLACK" else random.choice(color_list)
            #motor_speed = -motor_speed

			#DancingRobotMod 
            """
            lcd = Screen()
            logo = Image.open('pics/Neutral.bmp')
            lcd.image.paste(logo, (0,0))
            lcd.update()
            sleep(5)
            """

            self.leds.set_color("LEFT", led_color)
            self.leds.set_color("RIGHT", led_color)

            #time.sleep(milli_per_beat / 1000)

            self.arms.on_for_rotations(SpeedPercent(50), 0.1)
            self.arms.on_for_rotations(SpeedPercent(50), -0.1)			
            self.right_motor.run_timed(speed_sp=motor_speed, time_sp=150)
            self.left_motor.run_timed(speed_sp=-motor_speed, time_sp=150)
            time.sleep(milli_per_beat / 1000)
            #self.arms.on_for_rotations(SpeedPercent(50), 0.1)
            #self.arms.on_for_rotations(SpeedPercent(50), -0.1)			
            self.right_motor.run_timed(speed_sp=-motor_speed, time_sp=150)
            self.left_motor.run_timed(speed_sp=motor_speed, time_sp=150)
            time.sleep(milli_per_beat / 1000)

        print("Exiting BPM process.", file=sys.stderr)


if __name__ == '__main__':

    gadget = MindstormsGadget()

    # Set LCD font and turn off blinking green LEDs
    os.system('setfont Lat7-Terminus12x6')
    gadget.leds.set_color("LEFT", "BLACK")
    gadget.leds.set_color("RIGHT", "BLACK")

    # Startup sequence 
    gadget.sound.play_song((('C4', 'e'), ('D4', 'e'), ('E5', 'q')))
    gadget.leds.set_color("LEFT", "GREEN")
    gadget.leds.set_color("RIGHT", "GREEN")

    # Gadget main entry point
    gadget.main()

    # Shutdown sequence
    gadget.sound.play_song((('E5', 'e'), ('C4', 'e')))
    gadget.leds.set_color("LEFT", "BLACK")
    gadget.leds.set_color("RIGHT", "BLACK")

Credits

Matt Sunna

Matt Sunna

1 project • 1 follower

Comments