Shelly. A unique Alexa-powered Beach Clean Up Rover

A solution built with LEGO MINDSTORMS EV3, Amazon Alexa and works with an Echo Device through the Alexa Gadgets Toolkit over the internet.

ExpertFull instructions providedOver 4 days1,028

Things used in this project

Hardware components

EV3 Programming Brick / Kit
LEGO MindStorms EV3 Programming Brick / Kit
×2
Lego MindStorms EV3 Expansion Kit
×1
LEGO MindStorms LEGO® MINDSTORMS® Education EV3 Touch Sensor
×1
LEGO MindStorms LEGO® MINDSTORMS® Education EV3 Ultrasonic Sensor
×1
Echo Dot
Amazon Alexa Echo Dot
×1

Software apps and online services

Alexa Skills Kit
Amazon Alexa Alexa Skills Kit
Alexa Gadgets Toolkit
Amazon Alexa Alexa Gadgets Toolkit
ev3dev v2
RPyC
AWS Lambda
Amazon Web Services AWS Lambda

Story

Read more

Custom parts and enclosures

Step by Step Instructions to Build Shelly on your own

This will help you to create your very own version of Shelly though a detailed visual guide. This has been achieved by using Lego Digital Designer.

Schematics

A High Level Architectural Diagram of Shelly

It gives you an insight into how each and every component of the solution talks to each other and gives a holistic view of the build.

Connectivity Diagram of LEGO MINDSTORMS EV3, Motors and the Sensors

An insight into how the EV3 Bricks are connected to the Motors and the Sensors which are controlling Shelly's movements and actions.

Code

Shelly Beach Cleaning Rover

Python
#!/usr/bin/env python3
import os
import sys
import time
import logging
import json
import random
import threading

from enum import Enum
from agt import AlexaGadget

from ev3dev2.led import Leds
from ev3dev2.sound import Sound
from ev3dev2.motor import OUTPUT_A,OUTPUT_B, OUTPUT_C, OUTPUT_D, MoveTank, MoveSteering, SpeedPercent, SpeedRPM, MediumMotor, LargeMotor
#from ev3dev2.sensor.lego import TouchSensor,UltrasonicSensor
# 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__)

from ev3dev2.sensor.lego import TouchSensor,UltrasonicSensor
from ev3dev2.sensor import INPUT_1

import rpyc
conn = rpyc.classic.connect('192.168.43.143') 
slave_ev3 = conn.modules['ev3dev2.motor']
slave_out_a = slave_ev3.LargeMotor('outA')

sensor_inp = conn.modules['ev3dev2.sensor.lego']
ts = sensor_inp.TouchSensor('in1')
us = sensor_inp.UltrasonicSensor('in2')

class EventName(Enum):
    """
    The list of custom event name sent from this gadget
    """
    SENTRY = "Sentry"
    BINSTATUS = "bin"
    SPEECH = "Speech"


class Direction(Enum):
    """
    The list of directional commands and their variations.
    These variations correspond to the skill slot values.
    """
    FORWARD = ['forward', 'forwards', 'go forward','move forward']
    BACKWARD = ['back', 'backward', 'backwards', 'go backward', 'move backward']
    LEFT = ['left', 'go left']
    RIGHT = ['right', 'go right']
    STOP = ['stop moving', 'brake', 'stop', 'stop movement']


class Command(Enum):
    """
    The list of preset commands and their invocation variation.
    These variations correspond to the skill slot values.
    """
    MOVE_CIRCLE = ['circle', 'spin']
    MOVE_SQUARE = ['square']
    PATROL = ['patrol', 'guard mode', 'sentry mode']
    FIRE_ONE = ['cannon', '1 shot', 'one shot']
    FIRE_ALL = ['all shot']
    OPEN_GRIP = ['open','release']
    CLOSE_GRIP = ['close','hold','pickup','catch', 'pick up', 'pick']
    LIFT_ARM = ['lift', 'pull']
    DROP_ARM = ['drop', 'throw', 'leave']
    BIN = ['bin', 'release']
    BACK = ['back','back to','back to position', 'back to the position']


class MindstormsGadget(AlexaGadget):
    """
    A Mindstorms gadget that performs movement based on voice commands.
    Two types of commands are supported, directional movement and preset.
    """

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

        # Gadget state
        self.patrol_mode = False

        # Ev3dev initialization
        self.leds = Leds()
        self.sound = Sound()

        self.gripper = MediumMotor(OUTPUT_A)
        self.bin = LargeMotor(OUTPUT_B)
        self.drive_on = MoveTank(OUTPUT_C, OUTPUT_D)
        self.steering = MoveSteering(OUTPUT_C, OUTPUT_D)

        # Start threads
        threading.Thread(target=self._patrol_thread, daemon=True).start()

    def on_connected(self, device_addr):
        """
        Gadget connected to the paired Echo device.
        :param device_addr: the address of the device we connected to
        """
        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 device_addr: the address of the device we disconnected from
        """
        self.leds.set_color("LEFT", "BLACK")
        self.leds.set_color("RIGHT", "BLACK")
        logger.info("{} disconnected from Echo device".format(self.friendly_name))

    def on_custom_mindstorms_gadget_control(self, directive):
        """
        Handles the Custom.Mindstorms.Gadget control directive.
        :param directive: the custom directive with the matching namespace and name
        """
        print (directive, file=sys.stderr)
        try:
            payload = json.loads(directive.payload.decode("utf-8"))
            print("Control payload: {}".format(payload), file=sys.stderr)
            control_type = payload["type"]
            if control_type == "move":
                print("(******)")
                # Expected params: [direction, duration, speed]
                self._move(payload["direction"])

            if control_type == "command":
                # Expected params: [command]
                self._activate(payload["command"])

        except KeyError:
            print("Missing expected parameters: {}".format(directive), file=sys.stderr)

    def _send_event(self, name: EventName, payload):
        """
        Sends a custom event to trigger a sentry action.
        :param name: the name of the custom event
        :param payload: the sentry JSON payload
        """
        self.send_custom_event('Custom.Mindstorms.Gadget', name.value, payload)



    def _move(self, direction):
        """
        Handles move commands from the directive.
        Right and left movement can under or over turn depending on the surface type.
        :param direction: the move direction
        :param duration: the duration in seconds
        :param speed: the speed percentage as an integer
        :param is_blocking: if set, motor run until duration expired before accepting another command
        """

        print("********DIRECTION********")
        speed = 35
        seconds = 3
        rotation = 10
        turn_speed = 5
        left_speed = 20
        right_speed = 20
        degree = 180
        
        is_blocking = False
        if direction in Direction.FORWARD.value:
            self.drive_on.on_for_seconds(left_speed, right_speed, 10, brake=True, block=is_blocking)

        if direction in Direction.BACKWARD.value:
            self.drive_on.on_for_seconds(-left_speed, -right_speed,10, brake=True, block=is_blocking)

        if direction in Direction.LEFT.value:
            self.steering.on_for_seconds(-25, speed,seconds, brake=False, block=is_blocking)
            time.sleep(seconds)
            self.steering.on_for_seconds(25, speed,seconds, brake=False, block=is_blocking)
            time.sleep(seconds)
            self.drive_on.on_for_seconds(left_speed, right_speed, 5, brake=True, block=is_blocking)

        if direction in Direction.RIGHT.value:
            self.steering.on_for_seconds(25, speed, seconds, brake=False, block=is_blocking)
            time.sleep(seconds)
            self.steering.on_for_seconds(-25, speed,seconds, brake=False, block=is_blocking)
            time.sleep(seconds)
            self.drive_on.on_for_seconds(left_speed, right_speed, 5, brake=True, block=is_blocking)


        if direction in Direction.STOP.value:
            self.drive_on.off()


    def _activate(self, command, speed=50):
        """
        Handles preset commands.
        :param command: the preset command
        :param speed: the speed if applicable
        """
        print("Activate command: ({}, {})".format(command, speed), file=sys.stderr)
        if command in Command.MOVE_CIRCLE.value:
            self.drive.on_for_seconds(SpeedPercent(int(speed)), SpeedPercent(5), 12)

        if command in Command.MOVE_SQUARE.value:
            for i in range(4):
                self._move("right", 2, speed, is_blocking=True)

        if command in Command.PATROL.value:
            self.patrol_mode = True

        if command in Command.FIRE_ONE.value:
            self.weapon.on_for_rotations(SpeedPercent(100), 3)

        if command in Command.FIRE_ALL.value:
            self.weapon.on_for_rotations(SpeedPercent(100), 10)
        if command in Command.OPEN_GRIP.value:
            speed = SpeedPercent(10)
            print (speed)
            rotations = 0.30
            is_blocking = True
            print("***",slave_out_a,"***")
            #slave_out_a.run_timed(time_sp=1000, speed_sp=90)
            self.gripper.on_for_rotations(speed, rotations, brake=True, block=is_blocking)
            time.sleep(2)
            distance = us.value()
            self._send_event(EventName.BINSTATUS,{'distance': distance})  
        if command in Command.CLOSE_GRIP.value:
            speed = 10
            rotations = -0.30
            is_blocking = True
            self.gripper.on_for_rotations(SpeedPercent(speed), rotations, brake=True, block=is_blocking)

        if command in Command.LIFT_ARM.value:
            speed = -5
            rotations = 0.80
            is_blocking = True
            slave_out_a.run_timed(time_sp=2000, speed_sp=-180, stop_action='brake')

        
        if command in Command.DROP_ARM.value:
            speed = 5
            rotations = 0.80
            is_blocking = True
            slave_out_a.run_timed(time_sp=2000, speed_sp=180)

        if command in Command.BIN.value:
            print("ultrasonic sensor", us.value(), file=sys.stderr)
            distance = us.value()
            self._send_event(EventName.BINSTATUS,{'distance': distance})
            speed = -10
            rotations = 1.75
            is_blocking = True
            self.bin.on_for_rotations(SpeedPercent(speed), rotations, brake=False, block=is_blocking)

        if command in Command.BACK.value:
            speed = 10
            rotations = 0.2
            is_blocking = True
            while not ts.value():
                self.bin.on_for_rotations(SpeedPercent(speed), rotations, brake=False, block=is_blocking)

    
        
    def _turn(self, direction, speed):
        """
        Turns based on the specified direction and speed.
        Calibrated for hard smooth surface.
        :param direction: the turn direction
        :param speed: the turn speed
        """
        if direction in Direction.LEFT.value:
            self.drive.on_for_seconds(SpeedPercent(0), SpeedPercent(speed), 2)

        if direction in Direction.RIGHT.value:
            self.drive.on_for_seconds(SpeedPercent(speed), SpeedPercent(0), 2)

    def _patrol_thread(self):
        """
        Performs random movement when patrol mode is activated.
        """
        while True:
            while self.patrol_mode:
                print("Patrol mode activated randomly picks a path", file=sys.stderr)
                direction = random.choice(list(Direction))
                duration = random.randint(1, 5)
                speed = random.randint(1, 4) * 25

                while direction == Direction.STOP:
                    direction = random.choice(list(Direction))

                # direction: all except stop, duration: 1-5s, speed: 25, 50, 75, 100
                self._move(direction.value[0], duration, speed)
                time.sleep(duration)
            time.sleep(1)


if __name__ == '__main__':

    gadget = MindstormsGadget()
    
    # Set LCD font and turn off blinking 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")

Lambda Function

Python
from __future__ import print_function
import json
from requests import *
# from feedparser import parse


# --------------- Helpers that build all of the responses ----------------------

def build_speechlet_response(title, output, reprompt_text, should_end_session):
    return {
        'outputSpeech': {
            'type': 'PlainText',
            'text': output
        },
        'card': {
            'type': 'Simple',
            'title': title,
            'reprompt': {
                'outputSpeechcontent': output
            },
            '': {
                'type': 'PlainText',
                'text': reprompt_text
            }
        },
        'shouldEndSession': should_end_session
    }



def build_response(session_attributes, speechlet_response):
    return {
        'version': '1.0',
        'sessionAttributes': session_attributes,
        'response': speechlet_response
    }


def build_ev3_response(session_attributes, shouldEndSession, directives):
    print("directives",'----------------',directives)
    return {
        "version": "1.01",
        "sessionAttributes": session_attributes,
        "response": {
            "directives": directives,
            "shouldEndSession": shouldEndSession
        }
    }


# --------------- Functions that control the skill's behavior ------------------


def get_welcome_response(session):
    session_attributes = json.loads(session['attributes'])
    card_title = "Mindstorms Shelly"
    if session_attributes["endpoints"]:
        session_attributes['endpoint_id'] = session_attributes["endpoints"][0]['endpointId']
        speech_output = "You are now connected to Shelly. what would you like Shelly to do?."
        reprompt_text = "I didn't get any input, please say a voice command. If you need help, say help. "
        should_end_session = False
    else:
        speech_output = "Your echo device is not connected to any E V 3 brick. Please connect and try again. "
        reprompt_text = "I didn't get any input, please say a voice command. If you need help, say help. "
        should_end_session = True
    return build_response(session_attributes, build_speechlet_response(
        card_title, speech_output, reprompt_text, should_end_session))
        
        
def move_control(intent,session, context):
    session_attributes = session["attributes"]
    shouldEndSession = False
    direction = intent['slots']['direction']['resolutions']['resolutionsPerAuthority'][0]['values'][0]['value']['id']
    directives = [
      {
        "type": "CustomInterfaceController.SendDirective",
        "endpoint": {
          "endpointId": session_attributes["endpoint_id"]
        },
        "header": {
          "namespace": "Custom.Mindstorms.Gadget",
          "name": "control"
        },
        "payload": {
          "type": "move",
          "direction": direction
        }
      }
    ]
    return build_ev3_response(session_attributes, shouldEndSession, directives)



def operate_gripper(intent,session, context):
    session_attributes = session["attributes"]
    shouldEndSession = False
    oper = intent['slots']['oper']['resolutions']['resolutionsPerAuthority'][0]['values'][0]['value']['id']
    directives = [
      {
        "type": "CustomInterfaceController.SendDirective",
        "endpoint": {
          "endpointId": session_attributes["endpoint_id"]
        },
        "header": {
          "namespace": "Custom.Mindstorms.Gadget",
          "name": "control"
        },
        "payload": {
          "type": "command",
          "command": oper
        }
      },
      
        {
            "type": "CustomInterfaceController.StartEventHandler",
            "token": "1234abcd-40bb-11e9-9527-6b98b093d166",
            "expiration": {
                "durationInMilliseconds": 90000,
                "expirationPayload": {
                    "gameOverSpeech": "Game over! Would you like to hear your stats?"
                }
            },
            "eventFilter": {
                "filterExpression":{
                    "and": [
                        {"==": [{"var": "header.namespace"}, "Custom.Mindstorms.Gadget"]},
                        { "==": [{ "var": "endpoint.endpointId" }, session_attributes["endpoint_id"]]}
                    ]
                },
                "filterMatchAction": "SEND_AND_TERMINATE"  
            }
        }
    ]
    print("session_attributes",'---------',session_attributes)
    return build_ev3_response(session_attributes, shouldEndSession, directives)
    
    

def lift_drop_arm(intent,session, context):
    session_attributes = session["attributes"]
    shouldEndSession = False
    lift_drop = intent['slots']['lift_drop']['resolutions']['resolutionsPerAuthority'][0]['values'][0]['value']['id']
    directives = [
      {
        "type": "CustomInterfaceController.SendDirective",
        "endpoint": {
          "endpointId": session_attributes["endpoint_id"]
        },
        "header": {
          "namespace": "Custom.Mindstorms.Gadget",
          "name": "control"
        },
        "payload": {
          "type": "command",
          "command": lift_drop
        }
      }
    ]
    return build_ev3_response(session_attributes, shouldEndSession, directives)


def bin_control(intent,session, context):
    session_attributes = session["attributes"]
    shouldEndSession = False
    release = 'bin'
    directives = [
      {
        "type": "CustomInterfaceController.SendDirective",
        "endpoint": {
          "endpointId": session_attributes["endpoint_id"]
        },
        "header": {
          "namespace": "Custom.Mindstorms.Gadget",
          "name": "control"
        },
        "payload": {
          "type": "command",
          "command": release
        }
      },
      
        {
            "type": "CustomInterfaceController.StartEventHandler",
            "token": "1234abcd-40bb-11e9-9527-6b98b093d166",
            "expiration": {
                "durationInMilliseconds": 90000,
                "expirationPayload": {
                    "gameOverSpeech": "Game over! Would you like to hear your stats?"
                }
            },
            "eventFilter": {
                "filterExpression":{
                    "and": [
                        {"==": [{"var": "header.namespace"}, "Custom.Mindstorms.Gadget"]},
                        { "==": [{ "var": "endpoint.endpointId" }, session_attributes["endpoint_id"]]}
                    ]
                },
                "filterMatchAction": "SEND_AND_TERMINATE"  
            }
        }
    ]
    return build_ev3_response(session_attributes, shouldEndSession, directives)
    
def back_to_position(intent,session, context):
    session_attributes = session["attributes"]
    shouldEndSession = False
    back = 'back'
    directives = [
      {
        "type": "CustomInterfaceController.SendDirective",
        "endpoint": {
          "endpointId": session_attributes["endpoint_id"]
        },
        "header": {
          "namespace": "Custom.Mindstorms.Gadget",
          "name": "control"
        },
        "payload": {
          "type": "command",
          "command": back
        }
      }
    ]
    return build_ev3_response(session_attributes, shouldEndSession, directives)

def handle_session_end_request():
    card_title = "Session Ended"
    speech_output = "Thank you"
    # Setting this to true ends the session and exits the skill
    should_end_session = True
    return build_response({}, build_speechlet_response(
        card_title, speech_output, None, should_end_session))


# --------------- Events ------------------

def on_session_started(session_started_request, session):
    """ Called when the session starts """
    print("on_session_started requestId=" + session_started_request['requestId']
          + ", sessionId=" + session['sessionId'])


def on_launch(launch_request, session):
    """ Called when the user launches the skill without specifying what they
    want
    """
    print("on_launch requestId=" + launch_request['requestId'] +
          ", sessionId=" + session['sessionId'])
    # Dispatch to your skill's launch
    return get_welcome_response(session)


def on_intent(intent_request, session, context):
    """ Called when the user specifies an intent for this skill """
    print("on_intent requestId=" + intent_request['requestId'] +
          ", sessionId=" + session['sessionId'])
    print(intent_request)

    intent = intent_request['intent']
    intent_name = intent_request['intent']['name']
    
    if intent_name == "MoveIntent":
        return move_control(intent, session, context)

    if intent_name == "gripper":
        return operate_gripper(intent, session, context)
        
    if intent_name == "LiftDropIntent":
        return lift_drop_arm(intent, session, context)
    
    if intent_name == "BinIntent":
        return bin_control(intent, session, context)
        
    if intent_name == "BackPositionIntent":
        return back_to_position(intent, session, context)
        
    elif intent_name == "AMAZON.HelpIntent":
        return get_welcome_response(session)
    elif intent_name == "AMAZON.CancelIntent" or intent_name == "AMAZON.StopIntent":
        return handle_session_end_request()
    else:
        raise ValueError("Invalid intent")
        
def check_ultrasonic_distance(request, session):
    session_attributes = session['attributes']
    card_title = "Mindstorms Shelly"
    distance = request['events'][0]['payload']['distance']
    if int(distance) < 90:
        speech_output = "Bin is full. Please clear the bin."
    else:
        speech_output = " "
    reprompt_text = "I didn't get any input, please say a voice command. If you need help, say help. "
    should_end_session = False
    return build_response(session_attributes, build_speechlet_response(
        card_title, speech_output, reprompt_text, should_end_session))
        


def on_session_ended(session_ended_request, session):
    """ Called when the user ends the session.

    Is not called when the skill returns should_end_session=true
    """
    print("on_session_ended requestId=" + session_ended_request['requestId'] +
          ", sessionId=" + session['sessionId'])
    # add cleanup logic here


# --------------- Main handler ------------------

def lambda_handler(event, context):
    # print("event.session.application.applicationId=" + event['session']['application']['applicationId'])
    print(event)
    try:
        if event['session']['new']:
            on_session_started({'requestId': event['request']['requestId']}, event['session'])
    except:
        pass
    if event['request']['type'] == "LaunchRequest":
        url = event['context']['System']['apiEndpoint']+'/v1/endpoints'
        key = event['context']['System']['apiAccessToken']
        r = get(url, headers={'Authorization': 'Bearer '+key})
        event['session']['attributes'] = r.text
        print(r.text,"---------------------------------","R>TEXT")
        return on_launch(event['request'], event['session'])
    elif event['request']['type'] == "IntentRequest":
        return on_intent(event['request'], event['session'], event['context'])
    elif event['request']['type'] == "SessionEndedRequest":
        return on_session_ended(event['request'], event['session'])
    elif event['request']['type'] == 'CustomInterfaceController.EventsReceived':
        print("******************")
        return check_ultrasonic_distance(event['request'],event['session'])

Alexa Skill Model

JSON
{
  "interactionModel": {
    "languageModel": {
      "invocationName": "shelly",
      "intents": [
        {
          "name": "AMAZON.CancelIntent",
          "samples": []
        },
        {
          "name": "AMAZON.HelpIntent",
          "samples": []
        },
        {
          "name": "AMAZON.StopIntent",
          "samples": []
        },
        {
          "name": "AMAZON.NavigateHomeIntent",
          "samples": []
        },
        {
          "name": "MoveIntent",
          "slots": [
            {
              "name": "direction",
              "type": "DirectionType"
            },
            {
              "name": "duration",
              "type": "AMAZON.NUMBER"
            }
          ],
          "samples": [
            "{direction}",
            "{direction} now",
            "{direction} {duration} seconds",
            "move {direction} for {duration} seconds"
          ]
        },
        {
          "name": "SetSpeedIntent",
          "slots": [
            {
              "name": "Speed",
              "type": "AMAZON.NUMBER"
            }
          ],
          "samples": [
            "set speed {Speed} percent",
            "set {Speed} percent speed",
            "set speed to {Speed} percent"
          ]
        },
        {
          "name": "gripper",
          "slots": [
            {
              "name": "oper",
              "type": "operation"
            }
          ],
          "samples": [
            "{oper} hand",
            "{oper} gripper"
          ]
        },
        {
          "name": "LiftDropIntent",
          "slots": [
            {
              "name": "lift_drop",
              "type": "lift_drop"
            }
          ],
          "samples": [
            "{lift_drop}"
          ]
        },
        {
          "name": "BinIntent",
          "slots": [],
          "samples": [
            "bin",
            "put in bin",
            "put into bin"
          ]
        },
        {
          "name": "BackPositionIntent",
          "slots": [],
          "samples": [
            "back to the position",
            "back to position",
            "back"
          ]
        }
      ],
      "types": [
        {
          "values": [
            {
              "id": "brake",
              "name": {
                "value": "brake",
                "synonyms": [
                  "stop movement",
                  "stop moving",
                  "brake"
                ]
              }
            },
            {
              "id": "right",
              "name": {
                "value": "right",
                "synonyms": [
                  "turn right",
                  "take right",
                  "go right"
                ]
              }
            },
            {
              "id": "left",
              "name": {
                "value": "left",
                "synonyms": [
                  "turn left",
                  "go left",
                  "take left"
                ]
              }
            },
            {
              "id": "backward",
              "name": {
                "value": "backward",
                "synonyms": [
                  "move backward",
                  "go backward"
                ]
              }
            },
            {
              "id": "forward",
              "name": {
                "value": "forward",
                "synonyms": [
                  "move forward",
                  "go forward"
                ]
              }
            }
          ],
          "name": "DirectionType"
        },
        {
          "values": [
            {
              "name": {
                "value": "circle"
              }
            },
            {
              "name": {
                "value": "square"
              }
            },
            {
              "name": {
                "value": "patrol"
              }
            },
            {
              "name": {
                "value": "cannon"
              }
            },
            {
              "name": {
                "value": "all shot"
              }
            },
            {
              "name": {
                "value": "one shot"
              }
            }
          ],
          "name": "CommandType"
        },
        {
          "values": [
            {
              "id": "close",
              "name": {
                "value": "close",
                "synonyms": [
                  "catch",
                  "pick ",
                  "pick up",
                  "pickup",
                  "hold"
                ]
              }
            },
            {
              "id": "open",
              "name": {
                "value": "open",
                "synonyms": [
                  "release"
                ]
              }
            }
          ],
          "name": "operation"
        },
        {
          "values": [
            {
              "id": "drop",
              "name": {
                "value": "drop",
                "synonyms": [
                  "throw",
                  "drop",
                  "leave"
                ]
              }
            },
            {
              "id": "lift",
              "name": {
                "value": "lift",
                "synonyms": [
                  "pull gripper",
                  "lift gripper"
                ]
              }
            }
          ],
          "name": "lift_drop"
        }
      ]
    }
  },
  "version": "17"
}

Credits

Nikhil Fernandes

Nikhil Fernandes

2 projects • 4 followers
Sr Digital Creative Director. Ideator. Creative Strategist. Maker. Digital & Design Thinker. Work on the entire digital project lifecycle
Giamaria Fernandes

Giamaria Fernandes

1 project • 3 followers
I'm a Designer with over 10 yrs in Branding, User Interface & Experience Design, Ideation, Platform Innovation, Graphic & Product Designer.
Sreekanth Kanugovi

Sreekanth Kanugovi

1 project • 2 followers
Innovator with keen interest in deriving creative technical solutions to real world problems. Have over 24 years of Experience in IT
Vinesh Nandikol

Vinesh Nandikol

1 project • 3 followers
Digital Strategy, Planning, Insight Mining, Infusion of Digital Culture in the Company, Digital Transformation Projects, UI and UX Projects,

Comments