Making PS2 analog inputs exponential

I’ve been working on a way to increase precision of the analog playstation 2 controller inputs.

I’ve got a rover with an arm on top, and my goal here is to drastically slow down, for example, the base rotate. while it’s easy to just slow down the entire thing by a factor, this would also decrease the maximum speed when the controller is pushed fully outward. If one would manage to get the input scale exponentially, you’d get a much bigger band of slow movement and still retain maximum speed when pushing the stick outward fully.

This comes straight out of Rover_v1.2. (Edit: fixed problem)

IF ABS(Dualshock(3) - 128) > DeadZone THEN ;Right Stick L/R
        ;BaseAngle1 = BaseAngle1 -(Dualshock(3) - 128)/4 ;Base Rotation, this is the original.  
              
       	IF (Dualshock(3) - 128)  >= 0 THEN      
      		BaseAngle1 = BaseAngle1 -(((Dualshock(3) - 128)*(Dualshock(3) - 128))*128/16129)/4 ;Base Rotation, exponential positive      	
      		ELSE      		
      		BaseAngle1 = BaseAngle1 -(-((Dualshock(3) - 128)*(Dualshock(3) - 128))*128/16384)/4 ;Base Rotation, exponential negative      	
      	ENDIF
	  
	  ENDIF

You’ll already notice a big increase in the sensitive area of your stick, and you still keep the maximum speed at full extension.
However, I’d suggest using a combination of both exponential and linear for the best effect:

IF ABS(Dualshock(3) - 128) > DeadZone THEN ;Right Stick L/R
        ;BaseAngle1 = BaseAngle1 -(Dualshock(3) - 128)/4 ;Base Rotation, this is the original.  
              
       	IF (Dualshock(3) - 128)  >= 0 THEN      
      		BaseAngle1 = BaseAngle1 -(((Dualshock(3) - 128)*(Dualshock(3) - 128))*128/16129)/6 ;Base Rotation, exponential positive      	
      		ELSE      		
      		BaseAngle1 = BaseAngle1 -(-((Dualshock(3) - 128)*(Dualshock(3) - 128))*128/16384)/6 ;Base Rotation, exponential negative      	
      	ENDIF
	  
	  ENDIF

Now, the entire range has been decreased, thus slowing everything down linearly. Your maximum speed is less now, AND your range is exponential. I reckon this bit of programming can be further tweaked until your only limit is your servo’s precision.

What’s exponential about it? Actually, it seems you’ve just scaled it way down into the mud. Am I reading it wrong?

I think you’re saying you want to make the response of the pots non-linear, right?

Alan KM6VV

I think so yes, it’s rather late and I should be sleeping, really

Let’s look at an example. We’ll use the normal standard base rotate:
BaseAngle1 = BaseAngle1 -(Dualshock(3) - 128)/4
If my tests were correct, a stick returns a value of 0 to 255, mid position being 127,5.

So if we push the stick out fully, it’ll read:

BaseAngle1 = BaseAngle1 -(255 - 128)/4
BaseAngle1 = BaseAngle1 -127/4

so full blast is full blast. Half power forward will give us:

BaseAngle1 = BaseAngle1 -(192 - 128)/4
BaseAngle1 = BaseAngle1 -64/4

So half power does indeed give us half power. Gasp. The original code is quite logical, no?
Now we chuck in the ^2 at full blast:

BaseAngle1 = BaseAngle1 -((255 - 128)*(255 - 128))*128/16129)/4
BaseAngle1 = BaseAngle1 -( 127 * 127 )*128/16129)/4
BaseAngle1 = BaseAngle1 - 128 /4

So full power is still full power. No change at all, one would wonder why all that extra code is needed! So let’s try half power, now with the ^2:

BaseAngle1 = BaseAngle1 -((192 - 128)*(192 - 128))*128/16129)/4
BaseAngle1 = BaseAngle1 -( 64 * 64 )*128/16129)/4
BaseAngle1 = BaseAngle1 - 32.5 /4

So now, at half the stick, we get about 1/4th of the full scale. In other words, as you start pushing the stick forwards, the speed of the servo movement will only slowly increase at first, and will shoot up in speed towards the end of your stick range. If one could chuck it in an excel sheet, you’d get a nice curvy exponential graph. By all means, correct me if I’m wrong somewhere, as I’m just making all of this up right now :smiley:

So you’ll get a larger area of your stick movement to finetune slow movement of your arm/gripper/gun/doomsday device, and that’s always a bonus, definitely with the PS2 sticks being a bit funny around the deadzone! So far, all 5 minutes of testing on my arm so far, make a very noticeable difference in precision. Any more thoughts/comments/improvements?

Thanks for your explanation, I see the “square” now.

But your numbers have changed:

*128/16129)/4

was:

*128/16129)/6

and

*128/16384)/6

Typos?

Why the 16129? (16384 - 255)

Alan KM6VV

Aye indeed, well noticed! It is intentional: The /4 is the original, keeping full speed at full stick extension. The second version is /6, and doing so decreases the entire range, thus lowering the speed linearly, completely. Increasing this value further will, as you put it, scale the thing down into the mud :slight_smile:
This is preference mostly I think, while I do like smooth fast movements, changing it to /6 does help quite a bit for very fine movements with the base rotate on the AL5D arm.

The 16129 is an error! I came to that as a factor to compensate the positive side, which is a range of 0 to 127. However that range also includes 0, so that still is a range of 128! So all 16129’s need to be replaced by 16384’s. Thanks for the keen eye!

In the meantime I’ve done a bit more testing, I’ve added the exponential bit to all sticks, so also the virtual x and y point of the IK calculations, and it’s making a noticeable difference! I think I’ll also keep a bit of extra linear scaling on base and wrist, just to add to precision.

Cool idea. Thanks for posting your code changes! 8)

i agree. while coding the hawk i did, i change some of these values to for better control. works quite well. thakns for posting your findings. 8)

Here’s the final correct code with all adjustments. Both baserotate and wrist have a slightly smaller full range to increase precision. Glad you like it!

	  IF ABS(Dualshock(5) - 128) > DeadZone THEN ;Left Stick L/R
	    IF IKSolutionWarning THEN ;Allow only movement to the base
	      ;WristPosX = WristPosX -((Dualshock(5) - 128)/18) MIN 0 ;X Position, original
	      
	    	IF (Dualshock(5) - 128)  >= 0 THEN      
      		WristPosX = WristPosX -((((Dualshock(5) - 128)*(Dualshock(5) - 128))*128/16384)/18) MIN 0	;X Position, exponential
      		ELSE      		
      		WristPosX = WristPosX -((-((Dualshock(5) - 128)*(Dualshock(5) - 128))*128/16384)/18) MIN 0	;X Position, negative exponential    	
      		ENDIF
	      
	    ELSE  	    
	      ;WristPosX = WristPosX -(Dualshock(5) - 128)/18 ;X Position, original
	      
			IF (Dualshock(5) - 128)  >= 0 THEN      
      		WristPosX = WristPosX -(((Dualshock(5) - 128)*(Dualshock(5) - 128))*128/16384)/18	;X Position, exponential
      		ELSE      		
      		WristPosX = WristPosX -(-((Dualshock(5) - 128)*(Dualshock(5) - 128))*128/16384)/18	;X Position, negative exponential    	
      		ENDIF
	      
	    ENDIF
	  ENDIF
	   
	  IF ABS(Dualshock(6) - 128) > DeadZone THEN ;Left Stick U/D
	    IF IKSolutionWarning THEN
	      IF WristPosY > BaseLength THEN ;Allow only downward movement	      
	        ;WristPosY = WristPosY -((Dualshock(6) - 128)/18) MIN 0

	    	IF (Dualshock(6) - 128)  >= 0 THEN      
      		WristPosY = WristPosY -((((Dualshock(6) - 128)*(Dualshock(6) - 128))*128/16384)/18) MIN 0	;Y Position, exponential
      		ELSE      		
      		WristPosY = WristPosY -((-((Dualshock(6) - 128)*(Dualshock(6) - 128))*128/16384)/18) MIN 0	;Y Position, negative exponential    	
      		ENDIF
	      	        
	      ELSE ;Allow only upward movement	      
	        ;WristPosY = WristPosY -((Dualshock(6) - 128)/18) MAX 0   
	        
	    	IF (Dualshock(6) - 128)  >= 0 THEN      
      		WristPosY = WristPosY -((((Dualshock(6) - 128)*(Dualshock(6) - 128))*128/16384)/18) MAX 0	;Y Position, exponential
      		ELSE      		
      		WristPosY = WristPosY -((-((Dualshock(6) - 128)*(Dualshock(6) - 128))*128/16384)/18) MAX 0	;Y Position, negative exponential    	
      		ENDIF
	          
	      ENDIF	      
	    ELSE	    
	      ;WristPosY = WristPosY -(Dualshock(6) - 128)/18 ;Y Position
	              
	    	IF (Dualshock(6) - 128)  >= 0 THEN      
      		WristPosY = WristPosY -((((Dualshock(6) - 128)*(Dualshock(6) - 128))*128/16384)/18)		;Y Position, exponential
      		ELSE      		
      		WristPosY = WristPosY -((-((Dualshock(6) - 128)*(Dualshock(6) - 128))*128/16384)/18)	;Y Position, negative exponential    	
      		ENDIF
	      
	    ENDIF
	  ENDIF
  
	  IF ABS(Dualshock(3) - 128) > DeadZone THEN ;Right Stick L/R
        ;BaseAngle1 = BaseAngle1 -(Dualshock(3) - 128)/4 ;Base Rotation, this is the original.  
              
       	IF (Dualshock(3) - 128)  >= 0 THEN      
      		BaseAngle1 = BaseAngle1 -(((Dualshock(3) - 128)*(Dualshock(3) - 128))*128/16384)/6 ;Base Rotation, exponential positive      	
      		ELSE      		
      		BaseAngle1 = BaseAngle1 -(-((Dualshock(3) - 128)*(Dualshock(3) - 128))*128/16384)/6 ;Base Rotation, exponential negative      	
      	ENDIF
	  
	  ENDIF
	  
	  IF ABS(Dualshock(4) - 128) > DeadZone THEN ;Right Stick U/D
	    ;GlobalGripperAngle1 = GlobalGripperAngle1-(Dualshock(4) - 128)/2 ;Gripper Angle, original
	    
	    IF (Dualshock(4) - 128)  >= 0 THEN      
      		GlobalGripperAngle1 = GlobalGripperAngle1 -(((Dualshock(4) - 128)*(Dualshock(4) - 128))*128/16384)/5 ;Wrist, exponential positive      	
      		ELSE      		
      		GlobalGripperAngle1 = GlobalGripperAngle1 -(-((Dualshock(4) - 128)*(Dualshock(4) - 128))*128/16384)/5 ;Wrist, exponential negative      	
      	ENDIF
	    
	  ENDIF

Nice research. Thanks for the clean-up and final draft!

It’d be nice to plot some of these on a spreadsheet, as you’ve mentioned.

Alan KM6VV