Psmove mouse emulation-Please help me to improve this script

Official forum for open source FreePIE discussion and development.
Post Reply
nalf3in
One Eyed Hopeful
Posts: 5
Joined: Tue Oct 11, 2016 11:07 pm

Psmove mouse emulation-Please help me to improve this script

Post by nalf3in »

Hi everyone, I'm a relatively new vr enthusiast and I recently bought a second hand psmove kit after being hyped by the psmoveservice project. I'm currently trying to get mouse emulation to work in vr with a psmove, the psmoveservice driver and psmovefreepiebride. Currently, the relative tracking (increase the mouse speed the farther you move from the origin point) is working fine with that code (pretty much a copy pasted of the example scripts with some values adjusted)

Code: Select all

def update():
	#define globals
	global lastX
	global lastY
	global cursorScale
	
	#bind left mouse to trigger button
	#Right mouse to Move button
	#Middle mouse to Square
	mouse.leftButton = joystick[0].getDown(20)
	mouse.rightButton = joystick[0].getDown(19)
	mouse.middleButton = joystick[0].getDown(15)
	
	
	#Mouse movement using Gryoscope
	mouse.deltaX = -1 * (freePieIO[0].yaw) * 2
	mouse.deltaY = -1 * (freePieIO[0].pitch) * 2
	
if starting:
	lastX = freePieIO[0].x
	lastY = freePieIO[0].y
	cursorScale = 10
	freePieIO[0].update += update

and here's a modified version inspired of a wiimote script that doesn't work for some reason (will edit when I will finally manage to understand why)

Code: Select all

def update():
	#define globals
	global cursorScale
	
	#bind left mouse to trigger button
	#Right mouse to Move button
	#Middle mouse to Square
	mouse.leftButton = joystick[0].getDown(20)
	mouse.rightButton = joystick[0].getDown(19)
	mouse.middleButton = joystick[0].getDown(15)
	
	#Mouse movement using Gryoscope
	mouse.deltaX = -1 * filters.delta(filters.deadband(freePieIO[0].yaw * 50, 10)) 
	mouse.deltaY = -1 * filters.delta(filters.deadband(freePieIO[0].yaw * 50, 10)) 
	
if starting:
	cursorScale = 10
	freePieIO[0].update += update

Unfortunately, because I do not want that behavior (I want the mouse speed to be simply proportional to the angle you are rotating at for vr uses) I need to modify the code. The problem is that I'm fairly new to freepie and programming in general (I'm at the second month of my programming class) and I'm pretty unsure how to do this and I would like to have some help/tips/similar example code.

Finally please forgive my bad english and thanks for anything you can provide :)

links:
Psmoveservice driver :https://github.com/cboulay/PSMoveService
Psmovefreepiebridge (the program that give a freepie output) : https://bitbucket.org/hawkinse/psmovefreepiebridge
User avatar
zelmon64
Cross Eyed!
Posts: 134
Joined: Thu Apr 09, 2015 4:27 am

Re: Psmove mouse emulation-Please help me to improve this sc

Post by zelmon64 »

Looks like I gave you those filters the wrong way round (I think). The following works reasonable well

Code: Select all

# PSMoveService FreePIE Bridge set to use one controller

def update():
	#define globals
	
	#define constants
	mag = 1000
	dz = 0.005
	i=0
	j=0
	
	#bind left mouse to cross button
	#Right mouse to circle button
	#Middle mouse to move
	mouse.leftButton = joystick[j].getDown(14)
	mouse.rightButton = joystick[j].getDown(13)
	mouse.middleButton = joystick[j].getDown(19)
		
	#Mouse movement using Gryoscope
	# Only moves when the trigger is held down
	mdX = -1 * filters.deadband(filters.delta(freePieIO[i].yaw),dz) * mag
	mdY = -1 * filters.deadband(filters.delta(freePieIO[i].pitch),dz) * mag
	if joystick[j].getDown(20):
		mouse.deltaX = mdX
		mouse.deltaY = mdY
		
	
if starting:
	freePieIO[0].update += update
nalf3in
One Eyed Hopeful
Posts: 5
Joined: Tue Oct 11, 2016 11:07 pm

Re: Psmove mouse emulation-Please help me to improve this sc

Post by nalf3in »

Thanks for the script ! It's working pretty well ! I'm starting to get the hang of freepie (at least I think I do :P) The only thing I'm not sure is the filters.delta method (I guess it take the two last value received and return the difference between them) and what this method accept as input (if I previously guessed right that would mean it would accept any double with a value changing over time) As I'm using this https://images-na.ssl-images-amazon.com ... pstjqL.jpg accessory with my psmove and it feels a bit awkward to maintain the trigger pressed to move the mouse with it, I changed the code and now we just need to press the 'o' button to start moving the mouse (as you probably don't have the same accessory, I guess you prefer to keep the sames key layout). I also added a way to adjust the sensitivity and the offset (not sure if thats the correct term here) while running the script.

Here's the new code if you are interested

Code: Select all

def update():
   #define globals
   
   global chooseAdjust
   global sensitivity 
   global tracking
   global offset
   
   #define constants
   i=0
   j=0
   
   #bind left mouse to cross button
   #Right mouse to circle button
   #Middle mouse to move
   mouse.leftButton = joystick[j].getDown(20)
   mouse.rightButton = joystick[j].getDown(13)
   mouse.middleButton = joystick[j].getDown(19)
      
   #Mouse movement without using the camera 
   # Only moves after the 'o' was pressed 
   mdX = -1 * filters.deadband(filters.delta(freePieIO[i].yaw),offset) * sensitivity
   mdY = -1 * filters.deadband(filters.delta(freePieIO[i].pitch),offset) * sensitivity
  
   if chooseAdjust:
      if joystick[j].getDown(15):
         offset += 0.00001   
      elif joystick[j].getDown(12):
         offset -= 0.00001
   else:
      if joystick[j].getDown(15):
         sensitivity += 2
      elif joystick[j].getDown(12):
         sensitivity -= 2
   
   if joystick[j].getDown(19):
      chooseAdjust = not chooseAdjust   
   
   if joystick[j].getDown(13):
      tracking = not tracking
   
   if tracking:
      mouse.deltaX = mdX
      mouse.deltaY = mdY
   
   diagnostics.watch(tracking)
   diagnostics.watch(chooseAdjust)
   diagnostics.watch(offset)
   diagnostics.watch(sensitivity)
   
if starting:
   chooseAdjust = True
   tracking = False
   offset = 0.0048
   sensitivity = 4000
   freePieIO[0].update += update
Here's the button mapping in case you don't have it

Code: Select all

        diagnostics.watch(joystick[0].getDown(11))
        diagnostics.watch(joystick[0].getDown(12)) # Triangle
        diagnostics.watch(joystick[0].getDown(13)) # o
        diagnostics.watch(joystick[0].getDown(14)) # x 
        diagnostics.watch(joystick[0].getDown(15)) # square 
        diagnostics.watch(joystick[0].getDown(16))
        diagnostics.watch(joystick[0].getDown(17))
        diagnostics.watch(joystick[0].getDown(18))
        diagnostics.watch(joystick[0].getDown(19)) # move button
        diagnostics.watch(joystick[0].getDown(20)) # Trigger
Finally with the best offset (dz) and sensitivity (mag) values I found so far (offset : 0.0048 sensitivity : 4000)the script is working pretty well but I feel like we need to put a offset (dz) too high to have a reliable tracking in games that require a lot of precision. Do you have any idea to improve this ?

In the meantime, I will look for the wiimote code you were talking about :)
User avatar
zelmon64
Cross Eyed!
Posts: 134
Joined: Thu Apr 09, 2015 4:27 am

Re: Psmove mouse emulation-Please help me to improve this sc

Post by zelmon64 »

If you'd like to use the three way switch on the Sharp Shooter you can use the PSMoveAPI FreePIE plugin. Having the plugin doesn't affect PSMoveService as long as you don't run a FreePIE script that uses it while PSMoveService is running. I do have one (I was actually a bit disappointed by the fact that it doesn't add much functionality unlike the Racing Wheel which adds a d-pad, two analogue triggers, two bumpers, two rumble motors and the weird throttle thingy) so I can help you with any configuration you like.

The "dz" is a "dead zone" for the "deadband" filter. The idea is if there is slow yaw (or pitch) drift the deadzone would ignore it (probably not really necessary with a well calibrated PSMove though). The delta filter is as you say. One interesting detail is that it compares the difference from the last time it was executed meaning that if it is placed inside the if condition the script will behave differently (you wouldn't get the ratcheting feature if you know what I mean).
nalf3in
One Eyed Hopeful
Posts: 5
Joined: Tue Oct 11, 2016 11:07 pm

Re: Psmove mouse emulation-Please help me to improve this sc

Post by nalf3in »

First thanks for the programming tips, that helps a lot ! Also, considering that the psmove sharpshooter only add a three way switch, I would switch to the PSMoveAPI FreePIE plugin only if that not much more work as that wouldn't be worth to code a few hours just to support one more button. However, once we get a smooth ouput out of freepie, I will probaby look on craiglist to buy the racing wheel and then using the PSMoveAPI FreePIE plugin will be more interesting. Finally, I (again) changed the code to (try) to get a script easier to read and I also added a moving average to help to smooth the values we receive from the sensors. Here's the new code:

Code: Select all

def update():
   #define globals
   global lastValuesX
   global lastValuesY
   global AdjustingParameter
   global sensitivity 
   global tracking
   global deadZone
   
   
   #define constants
   numberOfSamples = 3
   i=0
   j=0
   
   
   #This method take the output of the yaw or pitch axis, convert it to a delta and return a smoothed value by making a moving average
   #This method is only used in the mapControllerRotation Method
   def averageMd (axis):
      mdAxis = -1 * filters.deadband(filters.delta(axis),deadZone) * sensitivity
      if axis == freePieIO[i].yaw:
         if len(lastValuesX) != numberOfSamples:
            lastValuesX.append(mdAxis)
         else: 
            del lastValuesX[0]
            lastValuesX.append(mdAxis)
            return (sum(lastValuesX)/numberOfSamples)
     
      elif axis == freePieIO[i].pitch:
         if len(lastValuesY) != numberOfSamples:
            lastValuesY.append(mdAxis)
         else: 
            del lastValuesY[0]
            lastValuesY.append(mdAxis)
            return (sum(lastValuesY)/numberOfSamples)
       
   
   def mapControllerButtons():
      global chooseAdjustingParameter
      global tracking
      global deadZone
      global sensitivity
      #bind left mouse to cross button
      #Right mouse to circle button
      #Middle mouse to move
      mouse.leftButton = joystick[j].getDown(20)   
      mouse.rightButton = joystick[j].getDown(13)
      mouse.middleButton = joystick[j].getDown(19)
      #Decide if tracking mouse movement by pressing the circle button
      if joystick[j].getDown(13):
         tracking = not tracking
      #Decide if you want to adjust the deadzone or sensitivity by pressing the move button   
      if joystick[j].getDown(19):
         chooseAdjustingParameter = not chooseAdjustingParameter
     
      #Press the square or Triangle button to adjust the value choosed before (you can see the actual value in the watch section of freepie)
      if chooseAdjustingParameter:
         if joystick[j].getDown(15):
            deadZone += 0.00001   
         elif joystick[j].getDown(12):
            deadZone -= 0.00001
      else:
         if joystick[j].getDown(15):
            sensitivity += 2
         elif joystick[j].getDown(12):
            sensitivity -= 2 
            
   #Map the yaw and pitch to mouse movement          
   def mapControllerRotation():
      global tracking
      global controllerDeltaX
      global controllerDeltaY
      if tracking:
         #I'm creating the controllersDelta(x or y ) objects in order to add values to lastValues(x or y) even if they don't have the required number of samples 
         controllerDeltaX = averageMd(freePieIO[i].yaw)
         controllerDeltaY = averageMd(freePieIO[i].pitch)           
          #The script only start to move the mouse once the the yaw and pitch axis already recorded the desired number of samples. 
         if ((len(lastValuesX) == numberOfSamples) & (len(lastValuesY) == numberOfSamples)):   
            mouse.deltaX = controllerDeltaX
            mouse.deltaY = controllerDeltaY
           
           
   def printDiagnostics():
      diagnostics.watch(freePieIO[i].yaw)
      diagnostics.watch(freePieIO[i].pitch)
      diagnostics.watch(tracking)
      diagnostics.watch(chooseAdjustingParameter)
      diagnostics.watch(deadZone)
      diagnostics.watch(sensitivity)
   
   #The script start here
   mapControllerButtons()
   mapControllerRotation()
   printDiagnostics()
      
   
if starting:
   lastValuesX = []
   lastValuesY = []
   chooseAdjustingParameter = True
   tracking = False
   deadZone = 0.0048
   sensitivity = 4000
   freePieIO[0].update += update
If you don't mind, I would like if you can tell me if theres another way to write this code without retyping my global variables in the methods and if you could give it a look to find why there is this error : ''expected float : got NoneType'' at the lines 79-80 as even when I test the code method by method in idle, I don't have this error and when I type diagnostic.watch(controllerDeltaX) just before those lines it show a valid value..
User avatar
zelmon64
Cross Eyed!
Posts: 134
Joined: Thu Apr 09, 2015 4:27 am

Re: Psmove mouse emulation-Please help me to improve this sc

Post by zelmon64 »

I don't have access to my computer for the next few days so I can't test it out. Looking at it the conditions in averageMd probably won't do what you want. For example, "if axis == freePieIO.yaw:" will compare whether the previous value given and the current yaw value are equal and not whether the axis interrogated was yaw. Hope that helps.
nalf3in
One Eyed Hopeful
Posts: 5
Joined: Tue Oct 11, 2016 11:07 pm

Re: Psmove mouse emulation-Please help me to improve this sc

Post by nalf3in »

Thanks for finding that error :) , that could have created some more problems. Unfortunately, I still get the same error.. I will try to rewrite the code in a slightly different way tonight and see if I'm able to get why it is happening..
nalf3in
One Eyed Hopeful
Posts: 5
Joined: Tue Oct 11, 2016 11:07 pm

Re: Psmove mouse emulation-Please help me to improve this sc

Post by nalf3in »

Found the problem ! It was that in my method mapControllerRotation() I had two return in two different if statement and it looks like freepie didn't like it. Thanks for everything Zelmon64 !

For those (if any) interested, here's the new source code :

Code: Select all

def update(): #define globals global lastValuesX global lastValuesY
   global AdjustingParameter
   global sensitivity
   global tracking
   global deadZone
   
   
   #define constants
   numberOfSamples = 3
   i=0
   j=0
   
   
   #This method take the output of the yaw or pitch axis, convert it to a delta and return a smoothed value by making a moving average
   #This method is only used in the mapControllerRotation Method
   def averageMd (axis):
      finalValue = 0
      if axis == "yaw":
         mdAxis = -1 * filters.deadband(filters.delta(freePieIO[i].yaw),deadZone) * sensitivity
         if len(lastValuesX) != numberOfSamples:
            lastValuesX.append(mdAxis)
         else: 
            del lastValuesX[0]
            lastValuesX.append(mdAxis)
            finalValue += (sum(lastValuesX)/numberOfSamples)
     
      elif axis == "pitch":
         mdAxis = -1 * filters.deadband(filters.delta(freePieIO[i].pitch),deadZone) * sensitivity
         if len(lastValuesY) != numberOfSamples:
            lastValuesY.append(mdAxis)
         else: 
            del lastValuesY[0]
            lastValuesY.append(mdAxis)
            finalValue += (sum(lastValuesY)/numberOfSamples)
      return finalValue
       
   
   def mapControllerButtons():
      global chooseAdjustingParameter
      global tracking
      global deadZone
      global sensitivity
      #bind left mouse to cross button
      #Right mouse to circle button
      #Middle mouse to move
      mouse.leftButton = joystick[j].getDown(20)   
      mouse.rightButton = joystick[j].getDown(13)
      mouse.middleButton = joystick[j].getDown(19)
      #Decide if tracking mouse movement by pressing the circle button
      if joystick[j].getDown(13):
         tracking = not tracking
      #Decide if you want to adjust the deadzone or sensitivity by pressing the move button   
      if joystick[j].getDown(19):
         chooseAdjustingParameter = not chooseAdjustingParameter
     
      #Press the square or Triangle button to adjust the value choosed before (you can see the actual value in the watch section of freepie)
      if chooseAdjustingParameter:
         if joystick[j].getDown(15):
            deadZone += 0.00001   
         elif joystick[j].getDown(12):
            deadZone -= 0.00001
      else:
         if joystick[j].getDown(15):
            sensitivity += 2
         elif joystick[j].getDown(12):
            sensitivity -= 2 
            
            
   def mapControllerRotation():
      global tracking
      if tracking:
         #I'm creating the controllersDelta(x or y ) objects in order to add values to lastValues(x or y) even if they don't have the required number of samples 
         controllerDeltaX = averageMd("yaw")
         controllerDeltaY = averageMd("pitch")       
          #The script only start to move the mouse once the the yaw and pitch axis already recorded the desired number of samples. 
         if ((len(lastValuesX) == numberOfSamples) & (len(lastValuesY) == numberOfSamples)):   
            mouse.deltaX = controllerDeltaX
            mouse.deltaY = controllerDeltaY
           
           
   def printDiagnostics():
      diagnostics.watch(freePieIO[i].yaw)
      diagnostics.watch(freePieIO[i].pitch)
      diagnostics.watch(tracking)
      diagnostics.watch(chooseAdjustingParameter)
      diagnostics.watch(deadZone)
      diagnostics.watch(sensitivity)
      diagnostics.watch("Hello")
      
   
   #The script start here
   mapControllerButtons()
   mapControllerRotation()
   printDiagnostics()
      
   
if starting:
   lastValuesX = []
   lastValuesY = []
   chooseAdjustingParameter = True
   tracking = False
   deadZone = 0.0048
   sensitivity = 4000
   freePieIO[0].update += update

Post Reply

Return to “FreePIE”