JOYSTICKS JOYSTICKS JOYSTICKS !

Lets figure out how we can control InMoov & mobile platforms with the Xbox (and other controllers)

TODO

  • get visual controller pictures describing possible input
  • get Joystick service working with each and document the output events & number of component each controller has
  • make an easy & unified interface which allows configurable mapping,  deadzone and other capabilities
  • layout a strategy to map InMoov movements to this unified interface - so we would actually expect  different controllers to do the same thing when attached to InMoov
Button Map
Button XBOX 360 PS3 DualShock3 (win/xbox driver PS3 (Mac/KMC layout) RumblePad2
POV Directional Pad Directional Pad N/A Directional Pad
x Left stick (left/right) Left stick (left/right) Left stick (left/right) Left stick (left/right)
y Left stick (up/down) Left stick (up/down) Left stick (up/down) Left stick (up/down)
rx Right stick (left/right) Right stick (left/right) N/A  
ry Right stick (up/down) Right stick (up/down) N/A  
z 1-0Left trigger  -1 to 0 Right trigger 1-0Left trigger  -1 to 0 Right trigger Right stick (left/right)   Right stick (left/right)
rz N/A? N/A? Right stick (up/down) Right stick (up/down)
Button 0 a x Select 1
Button 1 b circle L3 2
Button 2 x square R3 3
Button 3 y triangle Start 4
Button 4 Left Bumper L1 Dir Up 5
Button 5 Right Bumper R1 Dir Right 6
Button 6 Back Select Dir Down 7
Button 7 Start Start Dir Left 8
Button 8 Left Stick Press Left Stick Press L2 9
Button 9 Right Stick Press Right Stick Press R2 10
Button 10   N/A? L1  
Button 11   N/A R1  

UBERJOY

UberJoy is the "perfect" Archtype Software GamePad Controller
This is what is looks like.

It currently has 3 Axis & 1 POV and 12 Buttons. Here also is my silly Rumblepad2 interfacing it. My Rumblepad onlly has 2 Axis and they are identified x y and z rz

But I thought I'd half impersonate an Xbox  controller. So I did this

joy = Runtime.start("joy", "Joystick")
joy.mapId("x", "rx")
This maps my x axis into an XBOX rx axis. Easy no ?
Later I decided I wanted to map the output values of my y axis to Servo positions
 
joy.map("y", -1, 1, 0, 180)
Easy.
Connecting to a Servo or Motor will be a single line.
 
joy.addRZListener(servo)
and a motor
 
joy.addZListener(motor)
And we can even map a button to an arbitrary identifier (it could just as easy be Stop or Go or something more useful :)
 
 
joy.mapId("0", "UberJoy")

XBOX

Number of components ???
RX & RY are analog events of the 2nd joystick vs Z & RZR on the RumblePad

Logitech Rumblepad2

PS3

PS3 Controller button layout

 

Left bumper - open left hand
Left trigger - close left hand
Right bumper - open right hand
Right trigger - close right hand

Platform movement ?

Using a PS3 controller on windows works well if you fake it out to use the XBOX 360 device driver.  There's a video link below that shows how to set it up.  If you plug in the PS3 controller and you see it show up as an XBox 360 controller in your "Device and Printers" settings page (in windows control panel), it will work with MRL.

 

References:

 

Other Controllers

 

Razor Hydra

There appears to be a java warpper for the razor hydra

http://sixense.com/forum/vbulletin/showthread.php?4056-Java-wrapper-for-...

Razor Hydra Button Layout

 


#file : home/kwatters/UberJoyInMoov.py edit raw
import time

leftPort = "COM15"
rightPort = "COM19"

############################
# Start the InMoov Service
############################
i01 = Runtime.createAndStart("i01", "InMoov")
i01.startAll(leftPort, rightPort)

############################
# Start the Joystick service
############################
uberjoy = Runtime.createAndStart("uberjoy", "Joystick")
# Update this to be your controller
uberjoy.setController(2)
uberjoy.startPolling()

############################
# Attach the joystick to 
# the inmoov service servos
############################

def AListener(value):
  i01.rightHand.close()
  
def XListener(value):
  i01.rightHand.open()  

########################################################
# Left Arm Control  (left joystick for rotate and shoulder)
########################################################
uberjoy.map("x", -1, 1, i01.rightArm.rotate.min, i01.rightArm.rotate.max)
uberjoy.addXListener(i01.rightArm.rotate)

uberjoy.map("y", -1, 1, i01.rightArm.shoulder.max, i01.rightArm.shoulder.min)
uberjoy.addYListener(i01.rightArm.shoulder)

uberjoy.map("z", 0, 1, i01.rightArm.bicep.max, i01.rightArm.bicep.min)
uberjoy.addZListener(i01.rightArm.bicep)

uberjoy.map("rx", -1, 1, i01.rightHand.wrist.max, i01.rightHand.wrist.min)
uberjoy.addRXListener(i01.rightHand.wrist)

uberjoy.map("ry", -1, 1, i01.rightArm.omoplate.max, i01.rightArm.omoplate.min)
uberjoy.addRYListener(i01.rightArm.omoplate)

uberjoy.addListener("publish0", "python", "AListener")
uberjoy.addListener("publish2", "python", "XListener")



 


Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
kmcgerald's picture

My controllers

For what it's worth I have a pair of PS3 controllers (one Sixaxis and one Dualshock) both have accel and one has the vibration too.  They do Bluetooth and USB.  I have some PS2 controllers in the basement somewhere including one that was wireless.  I also have 2 of the PS3 Move wands and 2 of the Move navigation controllers (like the Wii nunchuck).

The PS3 controller has two finger buttons on each shoulder.  The top ones (L1,R1) are digital buttons. The bottom ones (R2, L2) are analog triggers.  Then there's the 4 buttons on the right face, the 4 way direction on the left face, and the two analog thumb joysticks which each can be pushed/clicked in like a button and are usually referred to as L3/R3.  There's also the select, start, and PS3 buttons in the middle of the face.

kwatters's picture

PS3 / XBox controller to InMoov mapping

I think, we need a list of the types of motions that we want to be able to drive with the controllers just to keep in mind what we need to map.  The challenge comes in that each servo has a forward  & a backward motion... those can consume all of the buttons pretty quickly, so we might have to consider button combos to map all of the motions fully to the controllers.  The left & right sticks are nice that they are analog and symetrical.   The sticks provide us 2 axis of movement.   up/down  left/right.  mapping that to 2 of the servos in the shoulder of the inmoov would be pretty cool so you could control.   I think controlling each finger independently would be too tedious with a controller so we can short cut it and just provide open/close functionality, rather than granular finger control  (that'd be better with some flex sensors and a glove)

Head Movement:

RotHead - left right - directional buttons left/right

Neck - up down. - directional buttons up/down

 

Shoulder  (left arm = left stick and L1/L2)  (right arm = right stick R1/R2)

Omoplate - chicken dance  - 

shoulder - forward/backward - stick   up / down movement

rotate - left /right - stick left-right movement.

bicep - in/out - L1 for up, L2 for down

 

Hand:

Gestures

left open - square button

left close -  triangle button

right open - x button

right close - circle button

 

There needs to be a wrist rotate left/right control somewhere? any ideas where to put it?

 

These mapping are pretty analogous to the Xbox 360 controller, it's just slightly less symetrical.

 

 

This leaves us with start and select buttons unused.  Start could be attach all / detach all  

Select , could maybe convert the joystick movement to control the walking/base platform.   I.e. press select, and the buttons switch to the walk around mode... hit select again and the buttons switch to arm and hand control mode.

 

kmcgerald's picture

PS3 worky on Mac!

I roughly followed these instructions to get the PS3 Dual Shock 3 joystick connected to my Macbook. http://theultralinx.com/2014/02/connect-playstation-3-controller-mac-os-mavericks.html  I say roughly because I'm on 10.8.5 not Mavericks (10.9). I also pushed the little reset button on the undeside with a paperclip so the controller wouldn't wake up my PS3.  

I had to download the JNI file from the repo and move it into the right place but I'm sure Grog will have it all sorted out in the repo shortly. The file is an Apple Universal Binary so it will work on pretty much any Mac OS X system.

mv ~/Downloads/libjinput-osx.jnilib libraries/native/x86.64.mac/

Here's how the buttons map out between the GUI and the Controller

L1 = 11

L2 = 9 (analog)

L3 = 2

R1 = 12

R2 = 10 (analog)

R3 = 3

Square =  Did Not Register (DNR): Array Out of Bounds Exception

Circle = DNR

Triangle = DNR

X = DNR

Select = 1

Start = 4

PS = DNR

On the 4 way cross pad:

Up = 5

Down = 7

Left = 8

Right = 6

Joysticks Map as follows

Left stick = xy

Right stick = zRz

kmcgerald's picture

Retested with 1.0.82 and updated the table

I updated the table with a fresh test from my Mac with PS3 controller.  The L2 and R2 analog triggers are coming across as digital outputs.

 

GroG's picture

UPDATES UPDATES UPDATES

There is now a refresh button - which is nice .. I noticed on Azul the controller would disappear when it fell asleep, so it would require a restart of MRL to find it - refresh button is nicer.

The output section now displays all the Components of the currelty selected Controller and their current value.

yep left middle right on my mouse.  X Y work too but its a mouse so it gives relative positioning - pretty cool though . not sure what z is or does .. and could not get mysterious component "3" & "4" to change values .. hmm wonder if "z" is a tap on the touchpad .. Now we can see this stuff at least !

WhooHoo look at that game controller ! (heh keyboard sure has a lot of buttons) Where's my  Kana & Kanji button !?!? :D

Ok it all worky now some things :

JINPUT ROCKS !

But now we have to think on how to "publish" these components

There are 3 categories defined by JINPUT - they are KEY, AXIS, & BUTTON

These make 3 Groups.  So we "might" want to use that.
At the most general level we have a COMPONENT which has a NAME, a TYPE & a VALUE (all values are floats) so the most general publishing method would be something like publishComponent(Component c)

If Joystick was to only publish this then all other services (Python, Servo, Motor, InMoov.. etc) would have to have internal logic to determine, "Is this the component I'm interested in ?" 

The other extreme is to have a different publish method for each component.  In this way other services would just subscribe to the components they wish to consume.

A possible 3rd option (middle ground) the groups of components would use the same publish method ..  There would be 3  publishKey(Component c), publishAxis(Component c) and publishButton(Component c).

Just some ideas I'm throwing around....

kwatters's picture

UberJoy InMoov test python script...

Here's some example code that shows using a joystick to control the left&right arms of the InMoov
 


#file : home/kwatters/UberJoyInMoov.py edit raw
import time

leftPort = "COM15"
rightPort = "COM19"

############################
# Start the InMoov Service
############################
i01 = Runtime.createAndStart("i01", "InMoov")
i01.startAll(leftPort, rightPort)

############################
# Start the Joystick service
############################
uberjoy = Runtime.createAndStart("uberjoy", "Joystick")
# Update this to be your controller
uberjoy.setController(2)
uberjoy.startPolling()

############################
# Attach the joystick to 
# the inmoov service servos
############################

def AListener(value):
  i01.rightHand.close()
  
def XListener(value):
  i01.rightHand.open()  

########################################################
# Left Arm Control  (left joystick for rotate and shoulder)
########################################################
uberjoy.map("x", -1, 1, i01.rightArm.rotate.min, i01.rightArm.rotate.max)
uberjoy.addXListener(i01.rightArm.rotate)

uberjoy.map("y", -1, 1, i01.rightArm.shoulder.max, i01.rightArm.shoulder.min)
uberjoy.addYListener(i01.rightArm.shoulder)

uberjoy.map("z", 0, 1, i01.rightArm.bicep.max, i01.rightArm.bicep.min)
uberjoy.addZListener(i01.rightArm.bicep)

uberjoy.map("rx", -1, 1, i01.rightHand.wrist.max, i01.rightHand.wrist.min)
uberjoy.addRXListener(i01.rightHand.wrist)

uberjoy.map("ry", -1, 1, i01.rightArm.omoplate.max, i01.rightArm.omoplate.min)
uberjoy.addRYListener(i01.rightArm.omoplate)

uberjoy.addListener("publish0", "python", "AListener")
uberjoy.addListener("publish2", "python", "XListener")



kwatters's picture

New Servo control functions.

Here's an example using the new "setDelay()" and "isSweeping()" methods for the servos to do incremental movement of a servo...  I hope to work up better examples of this.. below is just a work in progress.
  

#file : home/kwatters/JoystickSweeping.py edit raw
import time
import math

############################
# Start the InMoov Service
############################
leftPort = "COM15"
rightPort = "COM19"
i01 = Runtime.createAndStart("i01", "InMoov")

# tell the inmoov to be a quiet and obiedient slave.
i01.setMute(True)
i01.startAll(leftPort, rightPort)

############################
# Start the Joystick service
############################
joystickId = 2
uberjoy = Runtime.createAndStart("uberjoy", "Joystick")
uberjoy.setController(joystickId)
uberjoy.startPolling()


# Configure the servos to handle sweeping 
# with a thread in my robot lab and not on the arduinio
i01.rightArm.shoulder.setSpeedControlOnUC(False)
i01.rightArm.rotate.setSpeedControlOnUC(False)
i01.leftArm.shoulder.setSpeedControlOnUC(False)
i01.leftArm.rotate.setSpeedControlOnUC(False)


  
def StickYListener(value):
  print "Stick Y :" + str(value) + " Current pos: " + str(i01.rightArm.shoulder.pos)
  absValue = math.fabs(value)
  if (absValue < 0.175):
    print "Stop sweep"
    i01.rightArm.shoulder.stop()
    return
  absValue = absValue-0.01
  print "Set Speed " + str(absValue)
  i01.rightArm.shoulder.setSpeed(absValue)
  delay = int((1-absValue) * 200)
  if (value > 0.0):
    if (i01.rightArm.shoulder.isSweeping()):
      i01.rightArm.shoulder.setSweeperDelay(delay)
    else:    
      i01.rightArm.shoulder.sweep(i01.rightArm.shoulder.pos, i01.rightArm.shoulder.max, delay, 1, True)
  else:
    if (i01.rightArm.shoulder.isSweeping()):
      i01.rightArm.shoulder.setSweeperDelay(delay)
    else:
      i01.rightArm.shoulder.sweep(i01.rightArm.shoulder.min, i01.rightArm.shoulder.pos, delay, -1, True)

def StickXListener(value):
  print "Stick X :" + str(value) + " Current pos: " + str(i01.rightArm.rotate.pos)
  absValue = math.fabs(value)
  if (absValue < 0.175):
    print "Stop sweep"
    i01.rightArm.rotate.stop()
    return
  absValue = absValue-0.01
  print "Set Speed " + str(absValue)
  i01.rightArm.rotate.setSpeed(absValue)
  delay = int((1-absValue) * 200)
  if (value > 0.0):
    if (i01.rightArm.rotate.isSweeping()):
      i01.rightArm.rotate.setSweeperDelay(delay)
    else:    
      i01.rightArm.rotate.sweep(i01.rightArm.rotate.pos, i01.rightArm.rotate.max, delay, 1, True)
  else:
    if (i01.rightArm.rotate.isSweeping()):
      i01.rightArm.rotate.setSweeperDelay(delay)
    else:
      i01.rightArm.rotate.sweep(i01.rightArm.rotate.min, i01.rightArm.rotate.pos, delay, -1, True)


def StickRYListener(value):
  print "Stick RY :" + str(value) + " Current pos: " + str(i01.leftArm.shoulder.pos)
  absValue = math.fabs(value)
  if (absValue < 0.175):
    print "Stop sweep"
    i01.leftArm.shoulder.stop()
    return
  absValue = absValue-0.01
  print "Set Speed " + str(absValue)
  i01.leftArm.shoulder.setSpeed(absValue)
  delay = int((1-absValue) * 200)
  if (value > 0.0):
    if (i01.leftArm.shoulder.isSweeping()):
      i01.leftArm.shoulder.setSweeperDelay(delay)
    else:    
      i01.leftArm.shoulder.sweep(i01.leftArm.shoulder.pos, i01.leftArm.shoulder.max, delay, 1, True)
  else:
    if (i01.leftArm.shoulder.isSweeping()):
      i01.leftArm.shoulder.setSweeperDelay(delay)
    else:
      i01.leftArm.shoulder.sweep(i01.leftArm.shoulder.min, i01.leftArm.shoulder.pos, delay, -1, True)

def StickRXListener(value):
  print "Stick RX :" + str(value) + " Current pos: " + str(i01.leftArm.rotate.pos)
  absValue = math.fabs(value)
  if (absValue < 0.175):
    print "Stop sweep"
    i01.leftArm.rotate.stop()
    return
  absValue = absValue-0.01
  print "Set Speed " + str(absValue)
  i01.leftArm.rotate.setSpeed(absValue)
  delay = int((1-absValue) * 200)
  if (value > 0.0):
    if (i01.leftArm.rotate.isSweeping()):
      i01.leftArm.rotate.setSweeperDelay(delay)
    else:    
      i01.leftArm.rotate.sweep(i01.leftArm.rotate.pos, i01.leftArm.rotate.max, delay, 1, True)
  else:
    if (i01.leftArm.rotate.isSweeping()):
      i01.leftArm.rotate.setSweeperDelay(delay)
    else:
      i01.leftArm.rotate.sweep(i01.leftArm.rotate.min, i01.leftArm.rotate.pos, delay, -1, True)

### Gesture control
############################
# Attach the joystick to 
# the inmoov service servos
# only activate when the value is 1.0
############################
def AButtonListener(value):
  print "A button pressed"  + str(value)
  if value == 1.0:
    i01.rightHand.close()
   
def XButtonListener(value):
  if value == 1.0:
    i01.rightHand.open()  

def BButtonListener(value):
  if value == 1.0:
    i01.leftHand.close()
   
def YButtonListener(value):
  if value == 1.0:
    i01.leftHand.open()

########################################################
# Left Arm Control  (left joystick for rotate and shoulder)
########################################################
# invert control for the y axis
uberjoy.map("y", -1, 1, 1, -1)
uberjoy.map("ry", -1, 1, 1, -1)

uberjoy.addListener("publishX", "python", "StickXListener")
uberjoy.addListener("publishY", "python", "StickYListener")

uberjoy.addListener("publishRX", "python", "StickRXListener")
uberjoy.addListener("publishRY", "python", "StickRYListener")

uberjoy.addListener("publish0", "python", "AButtonListener")
uberjoy.addListener("publish1", "python", "BButtonListener")
uberjoy.addListener("publish2", "python", "XButtonListener")
uberjoy.addListener("publish3", "python", "YButtonListener")

# TODO: figure how to control both biceps ..
# the z button (trigger)is a bit tricky
uberjoy.map("z", 0, 1, i01.rightArm.bicep.max, i01.rightArm.bicep.min)
uberjoy.addZListener(i01.rightArm.bicep)