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()