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 days1,744

Things used in this project

Story

Read more

Schematics

Servo Controller Schematic

How the servo controller board connects to Raspberry Pi T cobbler board
Servo schematic m7jo1iphlx

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

12 projects • 53 followers
Embedded software guy for a long time

Comments