Tom Minnich
Published © GPL3+

Walabot Security Robot with Alexa Command and Control

Use Walabot and a vision system to identify new objects arriving in the roving space of the robot. Alexa will be used to command and control

AdvancedWork in progressOver 2 days2,665

Things used in this project

Hardware components

Walabot Creator
Walabot Creator
×1
Raspberry Pi 3 Model B
Raspberry Pi 3 Model B
×1
Adafruit 16 channel PWM controller
×1

Software apps and online services

Alexa Skills Kit
Amazon Alexa Alexa Skills Kit
AWS IoT
Amazon Web Services AWS IoT
AWS Lambda
Amazon Web Services AWS Lambda

Story

Read more

Schematics

Servo Controller Schematic

How the servo controller board connects to Raspberry Pi T cobbler board

Code

Added Target Tracking To Walabot Sensor Demo Python Code

Python
used to track the nearest target detected by moving azimuth elevation mount
from __future__ import print_function # WalabotAPI works on both Python 2 an 3.
from __future__ import division
from sys import platform
from os import system
from imp import load_source
from os.path import join
# merging stuff learned from Medicine Reminder project
#import paho.mqtt.client as mqtt
import json, time, sys, ssl
import logging, logging.handlers
#import RPi.GPIO as GPIO #GPIO is not exactly what we want for I2C
# Import the PCA9685 module.
import Adafruit_PCA9685
import math

if platform == 'win32':
	modulePath = join('C:/', 'Program Files', 'Walabot', 'WalabotSDK',
		'python', 'WalabotAPI.py')
elif platform.startswith('linux'):
    modulePath = join('/usr', 'share', 'walabot', 'python', 'WalabotAPI.py')     

wlbt = load_source('WalabotAPI', modulePath)
wlbt.Init()

def PrintSensorTargets(targets):
    system('cls' if platform == 'win32' else 'clear')
    if targets:
        for i, target in enumerate(targets):
            print('Target #{}:\nx: {}\ny: {}\nz: {}\namplitude: {}\n'.format(
                i + 1, target.xPosCm, target.yPosCm, target.zPosCm,
                target.amplitude))
    else:
        print('No Target Detected')

def RobotSensorApp():
    deadband = math.pi * (6/180)
    # Initialise the PCA9685 using the default address (0x40).
    pwm = Adafruit_PCA9685.PCA9685()
    # Configure min and max servo pulse lengths
    el_servo_min = 350 #320  #25 degrees inclined down
    el_servo_max = 420  #25 degrees inclined up
    el_servo_ctr = 370 #level
    el_servo_cmd = el_servo_ctr #start from center position
    az_servo_min = 270 #ahead
    az_servo_max = 455 #45 degrees to the right
    az_servo_ctr = 365 #45 degrees to the left
    az_servo_cmd = az_servo_ctr #start from center position
    
    # Set frequency to 60hz, good for servos.
    pwm.set_pwm_freq(60)
    pwm.set_pwm(0, 0, el_servo_ctr) #elevation
    pwm.set_pwm(1, 0, az_servo_ctr) #azimuth
       
    # wlbt.SetArenaR - input parameters
    minInCm, maxInCm, resInCm = 30, 200, 3
    # wlbt.SetArenaTheta - input parameters
    minIndegrees, maxIndegrees, resIndegrees = -15, 15, 5
    # wlbt.SetArenaPhi - input parameters
    minPhiInDegrees, maxPhiInDegrees, resPhiInDegrees = -60, 60, 5
    # Set MTI mode
    mtiMode = False
    # Configure Walabot database install location (for windows)
    wlbt.SetSettingsFolder()
    # 1) Connect : Establish communication with walabot.
    wlbt.ConnectAny()
    # 2) Configure: Set scan profile and arena
    # Set Profile - to Sensor.
    wlbt.SetProfile(wlbt.PROF_SENSOR)
    # Setup arena - specify it by Cartesian coordinates.
    wlbt.SetArenaR(minInCm, maxInCm, resInCm)
    # Sets polar range and resolution of arena (parameters in degrees).
    wlbt.SetArenaTheta(minIndegrees, maxIndegrees, resIndegrees)
    # Sets azimuth range and resolution of arena.(parameters in degrees).
    wlbt.SetArenaPhi(minPhiInDegrees, maxPhiInDegrees, resPhiInDegrees)
    # Moving Target Identification: standard dynamic-imaging filter
    filterType = wlbt.FILTER_TYPE_MTI if mtiMode else wlbt.FILTER_TYPE_NONE
    wlbt.SetDynamicImageFilter(filterType)
    # 3) Start: Start the system in preparation for scanning.
    wlbt.Start()
    if not mtiMode: # if MTI mode is not set - start calibrartion
        # calibrates scanning to ignore or reduce the signals
        wlbt.StartCalibration()
        while wlbt.GetStatus()[0] == wlbt.STATUS_CALIBRATING:
            wlbt.Trigger()
    while True:
        appStatus, calibrationProcess = wlbt.GetStatus()
        # 5) Trigger: Scan(sense) according to profile and record signals
        # to be available for processing and retrieval.
        wlbt.Trigger()
        # 6) Get action: retrieve the last completed triggered recording
        targets = wlbt.GetSensorTargets()
        rasterImage, _, _, sliceDepth, power = wlbt.GetRawImageSlice()
        # PrintSensorTargets(targets)
        #PrintSensorTargets(targets)
        # Move servos
        dist_sq = 10000000 #infinity
        x_ang = 0
        y_ang = 0
        if targets:
            for i, target in enumerate(targets):
                dist_calc_sq = target.xPosCm * target.xPosCm
                dist_calc_sq += target.yPosCm * target.yPosCm
                dist_calc_sq += target.zPosCm * target.zPosCm
                if dist_calc_sq <= dist_sq:
                    dist_sq = dist_calc_sq
                    x_ang = math.atan(target.xPosCm / target.zPosCm)
                    y_ang = math.atan(target.yPosCm / target.zPosCm)

        #move if not against a travel limit
        if x_ang > deadband:
           if az_servo_cmd < az_servo_max:
                   az_servo_cmd+=10
        if x_ang < -deadband:
           if az_servo_cmd > az_servo_min:
                   az_servo_cmd-=10
        if y_ang > deadband:
           if el_servo_cmd < el_servo_max:
                   el_servo_cmd+=10
        if y_ang < -deadband:
           if el_servo_cmd > el_servo_min:
                   el_servo_cmd-=10
        #print(az_servo_cmd)            
        pwm.set_pwm(0, 0, el_servo_cmd) #elevation
        pwm.set_pwm(1, 0, az_servo_cmd) #azimuth
    
    # 7) Stop and Disconnect.
    wlbt.Stop()
    wlbt.Disconnect()
    print('Terminate successfully')

if __name__ == '__main__':
    RobotSensorApp()

freedom security robot - interaction model

JSON
{
    "interactionModel": {
        "languageModel": {
            "invocationName": "freedom security robot",
            "intents": [
                {
                    "name": "AMAZON.CancelIntent",
                    "samples": []
                },
                {
                    "name": "AMAZON.HelpIntent",
                    "samples": []
                },
                {
                    "name": "AMAZON.StopIntent",
                    "samples": []
                },
                {
                    "name": "DirectionLeftTurn",
                    "slots": [
                        {
                            "name": "TurnRadius",
                            "type": "AMAZON.NUMBER"
                        }
                    ],
                    "samples": [
                        "Turn Left {TurnRadius}",
                        "Left Turn {TurnRadius}",
                        "Go Left {TurnRadius}",
                        "Make Left Turn {TurnRadius}",
                        "Direction Left Turn {TurnRadius}"
                    ]
                },
                {
                    "name": "DirectionRightTurn",
                    "slots": [
                        {
                            "name": "TurnRadius",
                            "type": "AMAZON.NUMBER"
                        }
                    ],
                    "samples": [
                        "Turn Right {TurnRadius}",
                        "Right Turn {TurnRadius}",
                        "Go Right {TurnRadius}",
                        "Make Right Turn {TurnRadius}",
                        "Direction Right Turn {TurnRadius}"
                    ]
                },
                {
                    "name": "DirectionStraight",
                    "slots": [],
                    "samples": [
                        "Go Straight",
                        "Straight Ahead",
                        "Direction Straight"
                    ]
                },
                {
                    "name": "MotionForward",
                    "slots": [
                        {
                            "name": "move",
                            "type": "AMAZON.NUMBER"
                        }
                    ],
                    "samples": [
                        "Forward {move}",
                        "Ahead {move}",
                        "Move Ahead {move}",
                        "Motion Forward {move}"
                    ]
                },
                {
                    "name": "MotionBackward",
                    "slots": [
                        {
                            "name": "move",
                            "type": "AMAZON.NUMBER"
                        }
                    ],
                    "samples": [
                        "Go Back {move}",
                        "Go Backward {move}",
                        "Move Back {move}",
                        "Move Backward {move}",
                        "Motion Backward {move}"
                    ]
                },
                {
                    "name": "MoveCommand",
                    "slots": [],
                    "samples": [
                        "Start Motion",
                        "Start Move",
                        "Move Robot",
                        "Robot Move",
                        "Do a move",
                        "Move"
                    ]
                },
                {
                    "name": "SetAuto",
                    "slots": [],
                    "samples": [
                        "Go Auto",
                        "Go Automatic",
                        "Set Auto",
                        "Set Automatic Mode"
                    ]
                },
                {
                    "name": "ClearAuto",
                    "slots": [],
                    "samples": [
                        "Stop Automatic Mode",
                        "Stop Auto",
                        "Clear Auto",
                        "Clear Automatic Mode"
                    ]
                },
                {
                    "name": "LookLeft",
                    "slots": [
                        {
                            "name": "angleincr",
                            "type": "AMAZON.NUMBER"
                        }
                    ],
                    "samples": [
                        "left {angleincr}",
                        "Look left {angleincr}",
                        "Look to the left {angleincr}"
                    ]
                },
                {
                    "name": "LookRight",
                    "slots": [
                        {
                            "name": "angleincr",
                            "type": "AMAZON.NUMBER"
                        }
                    ],
                    "samples": [
                        "right {angleincr}",
                        "look right {angleincr}",
                        "look to the right {angleincr}"
                    ]
                },
                {
                    "name": "LookUp",
                    "slots": [
                        {
                            "name": "angleincr",
                            "type": "AMAZON.NUMBER"
                        }
                    ],
                    "samples": [
                        "look up {angleincr}",
                        "tilt backward {angleincr}",
                        "tilt back {angleincr}",
                        "tilt up {angleincr}",
                        "up {angleincr}",
                        "upward {angleincr}",
                        "look upward {angleincr}"
                    ]
                },
                {
                    "name": "LookDown",
                    "slots": [
                        {
                            "name": "angleincr",
                            "type": "AMAZON.NUMBER"
                        }
                    ],
                    "samples": [
                        "Look Downward {angleincr}"
                    ]
                }
            ],
            "types": []
        }
    }
}

Azimuth Elevation Servo Test

Python
# Simple demo of of the PCA9685 PWM servo/LED controller library.
# This will move channel 0 from min to max position repeatedly.
# Author: Tony DiCola
# License: Public Domain
from __future__ import division
import time

# Import the PCA9685 module.
import Adafruit_PCA9685


# Uncomment to enable debug output.
#import logging
#logging.basicConfig(level=logging.DEBUG)

# Initialise the PCA9685 using the default address (0x40).
pwm = Adafruit_PCA9685.PCA9685()

# Alternatively specify a different address and/or bus:
#pwm = Adafruit_PCA9685.PCA9685(address=0x41, busnum=2)

# Configure min and max servo pulse lengths
servo_min = 375  #TWM was 150  Min pulse length out of 4096
servo_max = 400  #TWM was 600 Max pulse length out of 4096
az_servo_min = 350
az_servo_max = 450

# Helper function to make setting a servo pulse width simpler.
def set_servo_pulse(channel, pulse):
    pulse_length = 1000000    # 1,000,000 us per second
    pulse_length //= 60       # 60 Hz
    print('{0}us per period'.format(pulse_length))
    pulse_length //= 4096     # 12 bits of resolution
    print('{0}us per bit'.format(pulse_length))
    pulse *= 1000
    pulse //= pulse_length
    pwm.set_pwm(channel, 0, pulse)

# Set frequency to 60hz, good for servos.
pwm.set_pwm_freq(60)

print('Moving servo on channel 0, press Ctrl-C to quit...')
while True:
    # Move servo on channel O between extremes.
    pwm.set_pwm(0, 0, servo_min) #elevation
    pwm.set_pwm(1, 0, az_servo_min) #azimuth
    time.sleep(5)
    pwm.set_pwm(0, 0, servo_max)
    pwm.set_pwm(1, 0, az_servo_max)
    time.sleep(5)

Improved code for smooth motion of azimuth elevation mount

Python
from __future__ import print_function # WalabotAPI works on both Python 2 an 3.
from __future__ import division
from sys import platform
from os import system
from imp import load_source
from os.path import join
# merging stuff learned from Medicine Reminder project
#import paho.mqtt.client as mqtt
import json, time, sys, ssl
import logging, logging.handlers
#import RPi.GPIO as GPIO #GPIO is not exactly what we want for I2C
# Import the PCA9685 module.
import Adafruit_PCA9685
import math

if platform == 'win32':
	modulePath = join('C:/', 'Program Files', 'Walabot', 'WalabotSDK',
		'python', 'WalabotAPI.py')
elif platform.startswith('linux'):
    modulePath = join('/usr', 'share', 'walabot', 'python', 'WalabotAPI.py')     

wlbt = load_source('WalabotAPI', modulePath)
wlbt.Init()

def PrintSensorTargets(targets):
    system('cls' if platform == 'win32' else 'clear')
    if targets:
        for i, target in enumerate(targets):
            print('Target #{}:\nx: {}\ny: {}\nz: {}\namplitude: {}\n'.format(
                i + 1, target.xPosCm, target.yPosCm, target.zPosCm,
                target.amplitude))
    else:
        print('No Target Detected')

def RobotSensorApp():
    deadband = math.pi * (6/180)
    # Initialise the PCA9685 using the default address (0x40).
    pwm = Adafruit_PCA9685.PCA9685()
    # Configure min and max servo pulse lengths
    el_servo_min = 350 #320  #25 degrees inclined down
    el_servo_max = 420  #25 degrees inclined up
    el_servo_ctr = 370 #level
    el_servo_cmd = el_servo_ctr #start from center position
    az_servo_min = 270 #ahead
    az_servo_max = 455 #45 degrees to the right
    az_servo_ctr = 365 #45 degrees to the left
    az_servo_cmd = az_servo_ctr #start from center position
    
    # Set frequency to 60hz, good for servos.
    pwm.set_pwm_freq(60)
    pwm.set_pwm(0, 0, el_servo_ctr) #elevation
    pwm.set_pwm(1, 0, az_servo_ctr) #azimuth
       
    # wlbt.SetArenaR - input parameters
    minInCm, maxInCm, resInCm = 30, 200, 3
    # wlbt.SetArenaTheta - input parameters
    minIndegrees, maxIndegrees, resIndegrees = -15, 15, 5
    # wlbt.SetArenaPhi - input parameters
    minPhiInDegrees, maxPhiInDegrees, resPhiInDegrees = -60, 60, 5
    # Set MTI mode
    mtiMode = False
    # Configure Walabot database install location (for windows)
    wlbt.SetSettingsFolder()
    # 1) Connect : Establish communication with walabot.
    wlbt.ConnectAny()
    # 2) Configure: Set scan profile and arena
    # Set Profile - to Sensor.
    wlbt.SetProfile(wlbt.PROF_SENSOR)
    # Setup arena - specify it by Cartesian coordinates.
    wlbt.SetArenaR(minInCm, maxInCm, resInCm)
    # Sets polar range and resolution of arena (parameters in degrees).
    wlbt.SetArenaTheta(minIndegrees, maxIndegrees, resIndegrees)
    # Sets azimuth range and resolution of arena.(parameters in degrees).
    wlbt.SetArenaPhi(minPhiInDegrees, maxPhiInDegrees, resPhiInDegrees)
    # Moving Target Identification: standard dynamic-imaging filter
    filterType = wlbt.FILTER_TYPE_MTI if mtiMode else wlbt.FILTER_TYPE_NONE
    wlbt.SetDynamicImageFilter(filterType)
    # 3) Start: Start the system in preparation for scanning.
    wlbt.Start()
    if not mtiMode: # if MTI mode is not set - start calibrartion
        # calibrates scanning to ignore or reduce the signals
        wlbt.StartCalibration()
        while wlbt.GetStatus()[0] == wlbt.STATUS_CALIBRATING:
            wlbt.Trigger()
        x_ang = 0
        y_ang = 0
    while True:
        appStatus, calibrationProcess = wlbt.GetStatus()
        # 5) Trigger: Scan(sense) according to profile and record signals
        # to be available for processing and retrieval.
        wlbt.Trigger()

        for i in range(20):
                #move if not against a travel limit
                if x_ang > deadband:
                   if az_servo_cmd < az_servo_max:
                           az_servo_cmd+=1
                if x_ang < -deadband:
                   if az_servo_cmd > az_servo_min:
                           az_servo_cmd-=1
                if y_ang > deadband:
                   if el_servo_cmd < el_servo_max:
                           el_servo_cmd+=1
                if y_ang < -deadband:
                   if el_servo_cmd > el_servo_min:
                           el_servo_cmd-=1
                #print(az_servo_cmd)            
                pwm.set_pwm(0, 0, el_servo_cmd) #elevation
                pwm.set_pwm(1, 0, az_servo_cmd) #azimuth
                time.sleep(0.1)

        
        # 6) Get action: retrieve the last completed triggered recording
        targets = wlbt.GetSensorTargets()
        # rasterImage, _, _, sliceDepth, power = wlbt.GetRawImageSlice()
        # PrintSensorTargets(targets)
        #PrintSensorTargets(targets)
        # Move servos
        dist_sq = 10000000 #infinity
        x_ang = 0
        y_ang = 0
        if targets:
            for i, target in enumerate(targets):
                dist_calc_sq = target.xPosCm * target.xPosCm
                dist_calc_sq += target.yPosCm * target.yPosCm
                dist_calc_sq += target.zPosCm * target.zPosCm
                if dist_calc_sq <= dist_sq:
                    dist_sq = dist_calc_sq
                    x_ang = math.atan(target.xPosCm / target.zPosCm)
                    y_ang = math.atan(target.yPosCm / target.zPosCm)


    
    # 7) Stop and Disconnect.
    wlbt.Stop()
    wlbt.Disconnect()
    print('Terminate successfully')

if __name__ == '__main__':
    RobotSensorApp()

Credits

Tom Minnich

Tom Minnich

3 projects • 75 followers
Embedded software guy for a long time

Comments

Add projectSign up / Login