Open cheater opened 4 years ago
Hi @cheater ,
This all seems like great information.
Unfortunately, I haven't used my PSMove controllers in years and I don't see when I'll have time to test this out. If you can put together a pull request then I can make a test build and upload it and ask users to try it out. If it seems to work then I can add it to the release page.
Hi @cboulay, thanks. This is specifically about the PSVR headset - why are PSMove controllers relevant? Thanks
Some of the code is common across devices. Any fix for PSVR would have to not-break PSMove controllers. I'm optimistic that it could also improve PSMove controller operation.
Sorry, I didn't fully understand the scope of the proposed changes. I don't have a PSVR and I've never looked at the HMD-exclusive code. I thought this was a more fundamental change to the way the IMU was calibrated. But it seems like it's scoped to only the HMD. In that case, I would have no way to test it.
Some of the code is common across devices. Any fix for PSVR would have to not-break PSMove controllers. I'm optimistic that it could also improve PSMove controller operation.
Sorry, I didn't fully understand the scope of the proposed changes. I don't have a PSVR and I've never looked at the HMD-exclusive code. I thought this was a more fundamental change to the way the IMU was calibrated. But it seems like it's scoped to only the HMD. In that case, I would have no way to test it.
If this code is representative of IMU calibration in other places in this code (including PSMove and other devices, even non-Sony) then it likely all needs to be updated in this way. However, it's lucky that the changes aren't /huge/ per se.
Can you suggest an open source project that uses PSMoveService to provide SteamVR tracking for the PSVR? If I can get that to build, then maybe I could try to make this into a PR.
I've also had another question. For both the accelerometer and gyroscope, you store the data variance during calibration. What is that used for?
I'm asking about the variance because depending on what it is used for, it may have to be re-calculated after calibration is complete.
Can you suggest an open source project that uses PSMoveService to provide SteamVR tracking for the PSVR?
Maybe https://github.com/HipsterSloth/PSMoveSteamVRBridge ? I've never used a PSMove via SteamVR so I don't know how this works.
I haven't heard from him in a while, but maybe @HipsterSloth knows.
Hi! Thanks a lot for making the PSMoveService. It's a great library and very useful to the VR community. I really appreciate everyone who worked on it. I've posted a similar issue to the PSVRTracker github repository, as the relevant code is essentially the same.
I've been trying to track down an issue that I've started experiencing in a software that uses PSMoveService called iVRy, and I've figured out where the issue lies within PSMoveService.
In this file:
ServerRequestHandler.cpp#L2887-L2893
you compare the measured gravity vector to a straight down vector, create a difference vector (
raw_accelerometer_bias
), and then you calibrate everything by adding that difference. So inMorpheusHMD.cpp
you doCalibratedAccel = raw_accel - raw_accelerometer_bias
(I don't include the.i
.j
.k
andX
Y
Z
). You do it the same way for both accel and gyro.That is however not correct. I'll explain below what that affects, but first let me present how the calibration could be done in a better way. You would find the rotation between the measured gravity vector, and the ideal straight-down gravity vector, and then you would find a linear matrix (call it
rotCal
) that rotates the measured gravity into the straight-down vector. Only after that rotation is performed, should you perform any scaling - not before. After you do the rotation, takemeasured_g
, rotate it by the matrix, and figure out its length - the inverse of that number is your scaling calibration number for the accelerometer, call itaccelScale
. Next up, to calibrate the gyro, take the raw gyro data, and rotate it withrotCal
. After this, get the numbers from the gyro, and those are your bias numbers (call thatrotated_gyro_bias
).The difference might not be big for most headsets, however I have a headset where the IMU's internal MEMS gyroscope seems to have drifted out to a pretty steep angle. It has a clear measurement error when using PSMoveService, however it works perfectly when used with the PS4 using Sony's algorithms. I've found out that those IMU chips actually don't have any guarantee of how they will drift and it's normal for them to drift out of calibration over time. At first the author of iVRy said my IMU might be broken or maybe it moved physically, but I took apart my PSVR v2 and the IMU is located in such a way that the PCB (printed circuit board) can't move at all, not one bit, and it's not physically broken. So I figured that the IMU might have drifted internally and sure enough, that is a common mode of operation for those chips. It's not a mode of failure as the chip didn't fail - it is expected to do that. Essentially, there are tiny gyroscopes inside the chip, and the angle at which those rotate can change over time. This is what happens to all these chips, and in my case it's stronger than in other headsets.
So this is what the headset looks like with just basic calibration. You can see that the view is permanently tilted to the side. And if you try to calibrate it using the code that exists in PSMoveService as of today, this is what it looks like. As you can see, if you look straight ahead it looks good, however, if you turn your head, then two things are apparent. First of all, the
raw_accelerometer_bias
is applied incorrectly when the head is turned 90 degrees to either side, making the tilt worse. Second of all, there's this weird drift that happens right after the headset is moved. This is because of howraw_gyro_bias
is calculated currently.So the fix is to instead rotate the data coming from the IMU, because physically, that's what happens - it's almost as if the PCB inside the headset is at an angle. Here's a good a good link to how you can quickly calculate the rotation matrix between two vectors (Math Stackexchange).
So the pseudocode for the new calibration would be roughly like you can see below. Bear in mind I'm not a C++ programmer, so the
&
s and*
s might be wrong.Note one thing. When calibrating the accelerometer, you can only ever get the scaling in one direction. To fully calibrate in x, y, and z, you would have to have a 3D jig that can rotate the headset so the gravity is first downward (normal calibration), then so gravity is towards the side of the headset, and then once more so that gravity is towards the front of the headset. It's crucial that you rotate the headset by a somewhat precise angle of 90 degrees. You could do that by making a box to put the headset in. The box has 90 degree angles, so you will be able to rotate it on its sides and it will stay in the right position. However, for this to be correct, the headset would need to sit tightly inside the box. For this reason you could create small cardboard cutouts that you stick in the box, and the headset rests on them.
Also note that you could theoretically calibrate the gyro scaling, but to do that you would need to put the headset-in-a-box (like described above) on a motorized turntable with a revolution sensor to measure the angular velocity of the turntable. A revolution sensor tells you when a full revolution of the turntable has happened. It can be done cheaply. In that case, during calibration, you would first calculate
rotated_gyro
as above, thenconfig->rotated_gyro_bias
as above, and then after that you would calibrate the scaling for each of the 3 rotation axes, in separate runs.One more thing to note. I assume that the accelerometer measures the gravity vector with no bias (ie constant component), and I assume there's just a scaling error. This is somewhat of a large assumption. To properly test it, you would need to put the IMU in vertical free fall to see if it registers any sort of bias, and then you could figure out the bias and scaling separately. You would have to perform the test in all three directions. This is however not very practical. And without that, you can only assume either a bias or a scaling error, and I assume a scaling error. With a bit of work towards automatically cleaning up the sensor data, you essentially could drop the headset inside like a soft foam carrying case, and nothing bad would happen to it, mechanically speaking. As in it wouldn't break. But it would be a bit more involved than the code above, you would have to cull the recorded data to just the period where it's clearly in free fall (which can be detected), and you would have to somehow correct for the pull of the cable and make sure that it's not rotating at the same time - or use the rotation as an additional source of data, and calculate the calibration that way. It's an interesting problem in classical mechanics, that's for sure, but it's by no means very difficult. Another thing you could do is put the headset on the perimeter of a round turntable - in that case it would always be experiencing some sort of acceleration, which you could calculate on paper, and compare to what the headset's IMU has measured. The turntable would not only need a revolution sensor (i.e. a sensor that tells you when the turntable has turned a full 360 degrees), but also a position sensor that measures the angle at which the turntable is with precision comparable to the 2000Hz sampling rate the headset's IMU works at.
Another thing is the fact that even after all this calibration, a short term calibration will not find the long term drift that happens when using PSMoveService. Invariably it's a drift to the left, like you're slowly turning your head to the right, and it is only noticeable after 10 minutes or so, but it is invariably there and in all headsets. I think it should be calibrated separately from the calibrations described above. I think the source of this drift might be either integration error in PSMoveService (i.e. when you add floats many many times over, then there's a statistical error that becomes more significant over an hour or so), or it could be an intrinsic data bias in the output of the IMU being used in the PSVR. I think it should be calibrated separately because properly calibrating it away takes at least 10 minutes of measurement, and so you should be able to perform the quick calibrations described above in the pseudocode without erasing the long-term-drift calibration data, which takes ages to do, so people won't want to do this every time they turn on the headset. However, that is a conversation for another time. Thanks for reading this very long post! And thanks for your work!