Leap Motion Control

In this project I use hand gestures to control Neopixel LEDs and Servo motors. Check it out in action below:

As you may already know, you can write Python code for the PiSoC that lets you control various peripherals. Here I demonstrate why that is so powerful; you can work with other Python libraries simultaneously, which lets you do nearly anything you want. In this case we use the Leap Motion Python API to get my hand position. The Leap Motion is a device that lets you track your hands in 3d space, perfect for VR/AR and gesture controls.

I made two simple demos that track my hand in 2d Space. I get my hand’s x-position, which is side to side, and the y-position, which is up and down. You could easily expand these to use the z-position too, which is towards and away from the screen. This kind of control works great for controlling 2d or 3d art in real-time. I could also see this used to add interactive physics to projection mapping, or combined with a projection mapping project I did in the past.

To use these demos you’ll have to have your PiSoC Python API installed, as well as the Leap Motion Orion Beta SDK. Then you’ll need to put the Leap Motion SDK lib files (LeapSDK->lib->x64/x86) in the same folder as the example Python code below, or add them to your path.

Neopixels

In this example I map my hand’s x-position to a Neopixel column, and my y-position to the Neopixel brightness. I also change color for each column I’m in. The code is straight forward, first I poll continuously for my hand’s position from the Leap Motion. Next , I map the x-position from Leap Motion coordinates (-255 to 255 or so) to Neopixel columns (0-7). Then I map the y-position to Neopixel Brightness (1-5). I only update the Neopixel column when my hand is over a new position to avoid possible flickering. Download the code or hit expand source below.

'''Use the leap motion to control neopixels with the pisoc!

This program maps the leap motion's x and y axis to control our neopixel shield.
It also demonstrates how simple it is to integrate the pisoc with other python libraries and devices.

'''

import Leap, sys, thread, time
from pisoc import *

PiSoC(log_level = 'info') #start pisoc with connection info displayed

pixels = NeoPixelShield() #neopixel class, 40 pixels arranged 8x5
pixels.Start()
pixels.SetBrightness(1)
colors = [pixels.Green, pixels.Yellow, pixels.Orange, pixels.Red, pixels.Purple, pixels.Blue, pixels.PowderBlue, pixels.White] #pick out some colors, you can also use hex values

controller = Leap.Controller() #start up our leap motion

def main():
    
    last_loc = 4 #start the neopixels in the middle

    while 1:

        sleep(0.01)#we poll for frames in this example, but you could use a listener to get new leap frames, as seen in sample.py in the leap sdk

        if(controller.is_connected): #controller is a Leap.Controller object
            frame = controller.frame() #The latest frame

        # Get hands
        for hand in frame.hands:

            handType = "Left hand" if hand.is_left else "Right hand" #You could check which hands the leap sees and make it so the neopixels only respond to one hand

            filtered_hand_position = hand.stabilized_palm_position #get filtered hand position for cleaner movement

            #set our coordinates we want to work in. Here its the neopixel shield
            app_width = 7 #this is the number of columns on the neopixel shield, which we will map to the leap's x-axis
            app_height = 5 #this is number of neopixel brightness levels available, which we will map to the leap's y-axis

            #map leap coordinates to neopixel columns
            if filtered_hand_position.is_valid:
                iBox = frame.interaction_box
                leapPoint = filtered_hand_position
                normalizedPoint = iBox.normalize_point(leapPoint, True)

                app_x = normalizedPoint.x * app_width
                app_y = (1 - normalizedPoint.y) * app_height
                #The z-coordinate is not used here

            x_loc = int(round(app_x)) #round to nearest whole, so we can call on the corresponding neopixel column
            y_loc = int(round(app_y))

            if(y_loc > 0 and y_loc <= 5): pixels.SetBrightness(y_loc) #set our brightness according to the mapped y-axis value if(x_loc >= 0 and x_loc < 8 and last_loc != x_loc):
                pixels.ClearColumn(last_loc)
                pixels.DrawColumn(x_loc, colors[x_loc]) #set our column and color according to the mapped x-axis value
                last_loc = x_loc


            print "%s, id %d, position: %s" % (handType, hand.id, hand.palm_position) #print out original leap hand values
            print "Neopixel x: %d, y: %d" %(app_x,app_y) #print out our remapped x,y values
            #You can also get a z-axis, fingers, roll, pitch, yaw etc with the leap api.
            
if __name__ == '__main__':
    try:
        main()
    except KeyboardInterrupt:
        pixels.Clear() #clear neopixels when done

A simply way to expand this example would be to use the z-axis to pick out individual Neopixels to light up, instead of lighting up whole columns. Or you could pick out colors with the y-axis or z-axis.

Servos

This example reuses most of the same code. I control the servo’s pan with my hand’s x-position, and the tilt with the y-postion. This time I map the Leap’s coordinates to servo degrees, 0-180. Download here or hit expand source.

'''Use the leap motion to control servos with the pisoc!

This program maps the leap motion's x and y axis to control our pan/tilt servos.
It also demonstrates how simple it is to integrate the pisoc with other python libraries and devices.

'''

import Leap, sys, thread, time
from pisoc import *

PiSoC(log_level = 'info') #start pisoc with connection info displayed

#normally max_angle is 180, but you can change the scale to be whatever is best for the project
Pan = Servo(0, max_angle = 180)   # I chose 180 to correspond to degrees, but you can choose whatever servo range you want
Tilt = Servo(1, max_angle = 180)

Pan.Start()
Tilt.Start()

controller = Leap.Controller() #start up our leap motion

def main():
    
    last_loc = 4 #start the neopixels in the middle

    while 1:

        sleep(0.01)#we poll for frames in this example, but you could use a listener to get new leap frames, as seen in sample.py in the leap sdk

        if(controller.is_connected): #controller is a Leap.Controller object
            frame = controller.frame() #The latest frame

        # Get hands
        for hand in frame.hands:

            handType = "Left hand" if hand.is_left else "Right hand" #You could check which hands the leap sees and make it so the neopixels only respond to one hand

            filtered_hand_position = hand.stabilized_palm_position #get filtered hand position for cleaner movement

            #set our coordinates we want to work in. Here its the neopixel shield
            app_width = 180 #this is the number of columns on the neopixel shield, which we will map to the leap's x-axis
            app_height = 180 #this is number of neopixel brightness levels available, which we will map to the leap's y-axis

            #map leap coordinates to neopixel columns
            if filtered_hand_position.is_valid:
                iBox = frame.interaction_box
                leapPoint = filtered_hand_position
                normalizedPoint = iBox.normalize_point(leapPoint, True)

                app_x = normalizedPoint.x * app_width
                app_y = (1 - normalizedPoint.y) * app_height
                #The z-coordinate is not used here

            x_loc = int(round(app_x)) #round to nearest whole, so we can call on the corresponding neopixel column
            y_loc = int(round(app_y))


            if(y_loc > 0 and y_loc <= 180): Tilt.SetAngle(y_loc) if(x_loc >= 0 and x_loc < 180 and last_loc != x_loc):
                Pan.SetAngle(x_loc)
                last_loc = x_loc


            print "%s, id %d, position: %s" % (handType, hand.id, hand.palm_position) #print out original leap hand values
            print "Neopixel x: %d, y: %d" %(app_x,app_y) #print out our remapped x,y values
            #You can also get a z-axis, fingers, roll, pitch, yaw etc with the leap api.
            
if __name__ == '__main__':
    try:
        main()
    except KeyboardInterrupt:
        Pan.Stop()
        Tilt.Stop()

Leave a Reply

Your email address will not be published.

Published on: 29 June 2016
Posted by: Robert Barron
Discussion: Leave a comment