PS2 analog sticks - Normalization of readings

Yes and No. The other issue here is that by the time you get to this serout, your values have gone through your normalize function. So if something does not look right you don’t know if it was the raw value or the normalize that screwed it.

You do have another serout in the Get_PS2_Data subroutine that is commented out that can give you this information:

' serout S_OUT,i9600,", Left H: ",dec3 lhori\3,", Left V: ",dec3 lvert\3,", Right H: ",dec3 rhori\3,", Right V: ",dec3 rvert\3,13]
Of course it will print lots of times unless you do the check to see if anything changed.

Kurt

Yes, I know. However, I am just printing the same data for the left stick as that is. :slight_smile:

8-Dale

I’ve been working on this some this morning, and it’s giving me a headache (literally). :frowning: :frowning: I am going to need to leave it be for now. :frowning: I just can’t seem to get my head around the normalization stuff with the analog sticks and converting the readings to something that works for HSERVO (-12000 to +12000).

8-Dale

Good morning Dale,

This is why I suggested to simplify. Get rid of the calls to Normalize. I don’t currently have a pan/tilt setup, but my MechBrat (modified from James code…) does use the joystick to turn the turret and changes the angle of the hip to allow you to aim the guns, which is more or less the same idea… Taking a look at it, it looks like i need to modify it to work with the new IDES. I have edited part of it. But for one direction the code looks like:

IF (ABS(DualShock(3)-128) > TravelDeadZone) THEN ; We also only process this if the turret servo is idle if HservoIDLE(Turret)then TurretAngle = HservoPos(Turret) + ((Dualshock(3) - 128)*4) IF TurretAngle > 12000 THEN TurretAngle = 12000 ELSEIF TurretAngle < -12000 TurretAngle = -12000 ENDIF hservo [Turret\TurretAngle\100] endif ENDIF
You can reduce the number of lines of code here by using the min/max functions on the original calculation. Obviously you would need to replace variable names and the like. Ie change DualShock(3) to lhori, TravelDeadZone to DeadBand, Turret to PanServo… You may wish to play with the timing of the move, etc.

Kurt

I’m back to working on this, now that my head isn’t hurting. I can either get half range with stick self centering properly, OR full range and the stick does NOT center properly.

Here is the code in question:

[code] ’ SET LINEARIZED JOYSTICK DISPLACEMENT BASED ON JOYSTICK CENTER POSITION
’ gosub Normalize [lhori, lhori_null], lhori
’ gosub Normalize [lvert, lvert_null], lvert
’ gosub Normalize [rhori, rhori_null], rhori
’ gosub Normalize [rvert, rvert_null], rvert

lhori_s = lhori - 127
lvert_s = lvert - 127
rhori_s = rhori - 127
rvert_s = rvert - 127

' Main processing
if Autonomous then
	' Autonomous control
	gosub Steering [CurrRFAngle, CurrLFAngle, CurrRRAngle, CurrLRAngle, MoveSpeed]
else
	' Remote control
	PanPos = (lhori_s * 100)  MAX 12000 MIN -12000
	TiltPos = (lvert_s * 100)  MAX 12000 MIN -12000
	
	hservo [PanServo\PanPos\MoveSpeed, TiltServo\TiltPos\MoveSpeed]

	serout S_OUT,i9600,"Remote, Left Hr: ",sdec4 lhori\4,", Left Vr: ",sdec4 lvert\4,", Right Hr: ",sdec4 rhori\4,", Right Vr: ",sdec4 rvert\4,13]
	serout S_OUT,i9600,"Remote, Left Hs: ",sdec4 lhori_s\4,", Left Vs: ",sdec4 lvert_s\4,", Right Hs: ",sdec4 rhori_s\4,", Right Vs: ",sdec4 rvert_s\4,", PanPos: ",sdec6 PanPos\6,", TiltPos: ",sdec6 TiltPos\6,13,13]
endif

[/code]
This code gives me full stick range, but the stick doesn’t self center properly when I release it.

Below is the new Normalize subroutine (not currently being called):

[code]Value var byte
Null var byte
Normalize [Value, Null]
if Value < (Null - DeadBand) then
Value = ((127 * Value) / (Null - DeadBand)) MAX 127
elseif Value > (Null + DeadBand)
Value = ((127 * (Value - Null)) / ((255 - DeadBand - Null) + 127)) MAX 255
else
Value = 127
endif

return Value

[/code]

I have not worked with this yet, but am going to try it out next. I’ve attached my complete code, which gives me full range on the stick, but does not self center properly.

8-Dale
walter.bas (10.1 KB)

I don’t think I am actually seeing the zero values (-5 to + 5) that I am looking for to center the sticks. I’ll try widening the range and see what happens.

8-Dale

The PS2 controller sends 0 when centered, which would be -127 to get a +/- range (stick reading - 127). This is not cool, because it means there are two positions the controller sends a 0 reading - true stick position 0 and centered stick 0. I’m using Kurte’s version of a test program.

Can somebody please confirm that this is or is not true?

8-Dale

That isn’t true. The analog sticks return a value from 0 to 255. IIRC this is a 2s complement signed byte. There is only one position you will get 0. That is at center. However I’ve seen several different brands of PS2 controller that don’t go back to 0 when the sticks are released. I’ve seen them as far off as ±5 when the sticks are released. Also there is a large dead zone on some sticks so you will get 0 for a significant movement of the sticks at the center area(eg as much as a millimeter).

With this range, how can 0 be center? Wouldn’t 0 be all the way in one direction and 255 be all the way the other direction?

I never really did get 2’s complement, so maybe this is my problem. I’ve been treating the stick readings as raw regular (not complemented in any way) binary bytes.

8-Dale

Exactly. The center point is somewhere near 127 or 128. That is why when you look through programs that use the PS2, they often have the code that looks like: IF (ABS(DualShock(3)-128) > TravelDeadZone) THEN
They are testing to see if the joystick is outside the center point by some slop factor. My current program has the TravelDeadZone set to 4. As Nathan mentioned some have a very large dead zone.

I have seen this as well. Actually with the Sony wired one I had the worst results, It never appeared to return the same zero point twice and sometimes was off by as much as 8…

I am not sure it is a 2s compliment signed value as well. That is in Two’s complement the hex value 80 which is 128 as a signed value would be -128 and hex 0xff in a signed byte would be the value -1. If you look at the Wikipedia entry for signed numbers: en.wikipedia.org/wiki/Signed_num … sentations The PS2 values are more like Excess-N values… What we are typically trying to do is find out what side from center we are and by how much. First things choose what you think the center is: Some use 127 others choose 128, does not matter much other than which direction will have one extra value.

So for example if you look at some of the code I am currently using to test the roboclaw (borrowed from code that Xan did earlier), you will see the following:

[code] IF ABS(Dualshock(5) - 128) > DeadZone THEN ;Left Stick L/R
LStickY = (Dualshock(5) - 128) ;Wheels
ELSE
LStickY=0
ENDIF

	IF ABS(Dualshock(6) - 128) > DeadZone THEN ;Left Stick U/D
		LStickX = -(Dualshock(6) - 127) ;Wheels
	ELSE
		LStickX=0
	ENDIF

[/code]
I show the code for two different axis as sometimes the + or - the stick returns may not be logically the + or - that we wish to use. Note: LStickX and LstickY are defined as SBYTE.

So from the above the different values returned from the PS2 should map like:

	DS(value)		LStickX		LStickY
	124-132		0		0
	0		-128		127
	1		-127		126
	123		-5		4
	133		5		-6
	254		126		-127
	255		127		-128		

From doing the above, I do see a problem some may run into if they are not careful. That is if instead the code was:

IF ABS(Dualshock(5) - 127) > DeadZone THEN ;Left Stick L/R LStickY = (Dualshock(5) - 127) ;Wheels ELSE LStickY=0 ENDIF
You could run into problems. Why? because if the value from the dual shock is 255 (0x80), we would find we are trying to assign:
LSticky = 255-127 = 128 but the value +128 is not a valid number in a signed byte, it will actually read as: -128

Kurt

So for the fun of it, I edited the simple PS2 test program with a few different #ifdefs. One to define it for the standard pins or for having it on pins 0-3. Another to decide if you want all 18 bytes/vibration or if you simply want the standard 6 bytes all of our programs typically use, and last and hopefully not least, will use the code I mentioned in the previous post:

IF ABS(Dualshock(5) - 128) > DeadZone THEN ;Left Stick L/R LStickY = (Dualshock(5) - 128) ;Wheels ELSE LStickY=0 ENDIF
To map the joystick values from the center point and print them. Have fun!

Kurt

Edit: as an added bonus, I put in some simple pan/tilt code on the left joystick that you can ifdef in or out. You will have to change the IO pins that they are on. The tilt works fine, but I am having problems with my Pan servo in a base rotate unit that I need to replace…
test.bas (5.67 KB)

I just tried your new code. It does work for positioning the Pan servo. I have to install a servo for the Tilt, so have not checked that yet. Is there any way to get the sticks to auto center when they are not being operated? That does not seem to be happening now.

8-Dale

If what you desire is for the pan and tilt to mimic the position of the joystick, then you can simplify the code. That is instead of looking at the current position, you simply scale the Joystick return values. That is you could do something like:

IF (ABS(DualShock(3)-128) > TravelDeadZone) THEN hservo [Turret\(Dualshock(3) - 128)*4) \100] ELSE hservo [Turret\0 \100] ENDIF
Personally I don’t think this will work very well, as the joystick return values are not fluid.

Other options:

  1. TImeout - if the joystick has been at the center position for a certain amount of time, set it to zero point.

  2. use a button like: clicking the joystick to say Move me back to the idle position.

Kurt