|
|
|
@@ -25,8 +25,9 @@ struct CalibrationContext
|
|
|
|
|
CalibrationState state = None;
|
|
|
|
|
int referenceID, targetID;
|
|
|
|
|
|
|
|
|
|
vr::HmdQuaternion_t calibratedRotation;
|
|
|
|
|
vr::HmdVector3d_t calibratedTranslation;
|
|
|
|
|
Eigen::Vector3d calibratedRotation;
|
|
|
|
|
Eigen::Vector3d calibratedTranslation;
|
|
|
|
|
|
|
|
|
|
std::string calibratedTrackingSystem;
|
|
|
|
|
|
|
|
|
|
bool profileLoaded = false, validProfile = false;
|
|
|
|
@@ -103,6 +104,22 @@ struct DSample
|
|
|
|
|
Eigen::Vector3d ref, target;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
bool StartsWith(const std::string &str, const std::string &prefix)
|
|
|
|
|
{
|
|
|
|
|
if (str.length() < prefix.length())
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
return str.compare(0, prefix.length(), prefix) == 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool EndsWith(const std::string &str, const std::string &suffix)
|
|
|
|
|
{
|
|
|
|
|
if (str.length() < suffix.length())
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
return str.compare(str.length() - suffix.length(), suffix.length(), suffix) == 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Eigen::Vector3d AxisFromRotationMatrix3(Eigen::Matrix3d rot)
|
|
|
|
|
{
|
|
|
|
|
return Eigen::Vector3d(rot(2,1) - rot(1,2), rot(0,2) - rot(2,0), rot(1,0) - rot(0,1));
|
|
|
|
@@ -135,41 +152,28 @@ DSample DeltaRotationSamples(Sample s1, Sample s2)
|
|
|
|
|
return ds;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool StartsWith(const std::string &str, const std::string &prefix)
|
|
|
|
|
{
|
|
|
|
|
if (str.length() < prefix.length())
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
return str.compare(0, prefix.length(), prefix) == 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool EndsWith(const std::string &str, const std::string &suffix)
|
|
|
|
|
{
|
|
|
|
|
if (str.length() < suffix.length())
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
return str.compare(str.length() - suffix.length(), suffix.length(), suffix) == 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Eigen::Matrix3d CalibrateRotation(const std::vector<Sample> &samples)
|
|
|
|
|
Eigen::Vector3d CalibrateRotation(const std::vector<Sample> &samples)
|
|
|
|
|
{
|
|
|
|
|
std::vector<DSample> deltas;
|
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i < samples.size(); i++) {
|
|
|
|
|
for (size_t j = 0; j < i; j++) {
|
|
|
|
|
for (size_t i = 0; i < samples.size(); i++)
|
|
|
|
|
{
|
|
|
|
|
for (size_t j = 0; j < i; j++)
|
|
|
|
|
{
|
|
|
|
|
auto delta = DeltaRotationSamples(samples[i], samples[j]);
|
|
|
|
|
if (delta.valid)
|
|
|
|
|
deltas.push_back(delta);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
printf("\ngot %zd samples with %zd delta samples\n", samples.size(), deltas.size());
|
|
|
|
|
printf("got %zd samples with %zd delta samples\n", samples.size(), deltas.size());
|
|
|
|
|
|
|
|
|
|
// Kabsch algorithm
|
|
|
|
|
|
|
|
|
|
Eigen::MatrixXd refPoints(deltas.size(), 3), targetPoints(deltas.size(), 3);
|
|
|
|
|
Eigen::Vector3d refCentroid(0,0,0), targetCentroid(0,0,0);
|
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i < deltas.size(); i++) {
|
|
|
|
|
for (size_t i = 0; i < deltas.size(); i++)
|
|
|
|
|
{
|
|
|
|
|
refPoints.row(i) = deltas[i].ref;
|
|
|
|
|
refCentroid += deltas[i].ref;
|
|
|
|
|
|
|
|
|
@@ -180,7 +184,8 @@ Eigen::Matrix3d CalibrateRotation(const std::vector<Sample> &samples)
|
|
|
|
|
refCentroid /= (double) deltas.size();
|
|
|
|
|
targetCentroid /= (double) deltas.size();
|
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i < deltas.size(); i++) {
|
|
|
|
|
for (size_t i = 0; i < deltas.size(); i++)
|
|
|
|
|
{
|
|
|
|
|
refPoints.row(i) -= refCentroid;
|
|
|
|
|
targetPoints.row(i) -= targetCentroid;
|
|
|
|
|
}
|
|
|
|
@@ -191,7 +196,8 @@ Eigen::Matrix3d CalibrateRotation(const std::vector<Sample> &samples)
|
|
|
|
|
auto svd = bdcsvd.compute(crossCV, Eigen::ComputeThinU | Eigen::ComputeThinV);
|
|
|
|
|
|
|
|
|
|
Eigen::Matrix3d i = Eigen::Matrix3d::Identity();
|
|
|
|
|
if ((svd.matrixU() * svd.matrixV().transpose()).determinant() < 0) {
|
|
|
|
|
if ((svd.matrixU() * svd.matrixV().transpose()).determinant() < 0)
|
|
|
|
|
{
|
|
|
|
|
i(2,2) = -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -199,16 +205,19 @@ Eigen::Matrix3d CalibrateRotation(const std::vector<Sample> &samples)
|
|
|
|
|
rot.transposeInPlace();
|
|
|
|
|
|
|
|
|
|
Eigen::Vector3d euler = rot.eulerAngles(2, 1, 0) * 180.0 / EIGEN_PI;
|
|
|
|
|
printf("rotation yaw=%.2f pitch=%.2f roll=%.2f\n", euler[1], euler[0], euler[2]);
|
|
|
|
|
return rot;
|
|
|
|
|
|
|
|
|
|
printf("rotation yaw=%.2f pitch=%.2f roll=%.2f\n", euler[1], euler[2], euler[0]);
|
|
|
|
|
return euler;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Eigen::Vector3d CalibrateTranslation(const std::vector<Sample> &samples)
|
|
|
|
|
{
|
|
|
|
|
std::vector<std::pair<Eigen::Vector3d, Eigen::Matrix3d>> deltas;
|
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i < samples.size(); i++) {
|
|
|
|
|
for (size_t j = 0; j < i; j++) {
|
|
|
|
|
for (size_t i = 0; i < samples.size(); i++)
|
|
|
|
|
{
|
|
|
|
|
for (size_t j = 0; j < i; j++)
|
|
|
|
|
{
|
|
|
|
|
auto QAi = samples[i].ref.rot.transpose();
|
|
|
|
|
auto QAj = samples[j].ref.rot.transpose();
|
|
|
|
|
auto dQA = QAj - QAi;
|
|
|
|
@@ -226,19 +235,20 @@ Eigen::Vector3d CalibrateTranslation(const std::vector<Sample> &samples)
|
|
|
|
|
Eigen::VectorXd constants(deltas.size() * 3);
|
|
|
|
|
Eigen::MatrixXd coefficients(deltas.size() * 3, 3);
|
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i < deltas.size(); i++) {
|
|
|
|
|
for (int axis = 0; axis < 3; axis++) {
|
|
|
|
|
for (size_t i = 0; i < deltas.size(); i++)
|
|
|
|
|
{
|
|
|
|
|
for (int axis = 0; axis < 3; axis++)
|
|
|
|
|
{
|
|
|
|
|
constants(i * 3 + axis) = deltas[i].first(axis);
|
|
|
|
|
coefficients.row(i * 3 + axis) = deltas[i].second.row(axis);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Eigen::Vector3d trans = coefficients.bdcSvd(Eigen::ComputeThinU | Eigen::ComputeThinV).solve(constants);
|
|
|
|
|
trans(0) *= -1;
|
|
|
|
|
auto transcm = trans * 100.0;
|
|
|
|
|
|
|
|
|
|
printf("\ntranslation x=%.2f y=%.2f z=%.2f\n", transcm[0], transcm[1], transcm[2]);
|
|
|
|
|
return trans;
|
|
|
|
|
printf("translation x=%.2f y=%.2f z=%.2f\n", transcm[0], transcm[1], transcm[2]);
|
|
|
|
|
return transcm;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Sample CollectSample(const CalibrationContext &ctx)
|
|
|
|
@@ -247,7 +257,7 @@ Sample CollectSample(const CalibrationContext &ctx)
|
|
|
|
|
reference.bPoseIsValid = false;
|
|
|
|
|
target.bPoseIsValid = false;
|
|
|
|
|
|
|
|
|
|
vr::VRSystem()->GetDeviceToAbsoluteTrackingPose(vr::TrackingUniverseStanding, 0.0f, devicePoses, vr::k_unMaxTrackedDeviceCount);
|
|
|
|
|
vr::VRSystem()->GetDeviceToAbsoluteTrackingPose(vr::TrackingUniverseRawAndUncalibrated, 0.0f, devicePoses, vr::k_unMaxTrackedDeviceCount);
|
|
|
|
|
|
|
|
|
|
reference = devicePoses[ctx.referenceID];
|
|
|
|
|
target = devicePoses[ctx.targetID];
|
|
|
|
@@ -269,7 +279,7 @@ bool PickDevices(CalibrationContext &ctx)
|
|
|
|
|
char buffer[vr::k_unMaxPropertyStringSize];
|
|
|
|
|
|
|
|
|
|
vr::TrackedDevicePose_t devicePoses[vr::k_unMaxTrackedDeviceCount];
|
|
|
|
|
vr::VRSystem()->GetDeviceToAbsoluteTrackingPose(vr::TrackingUniverseStanding, 0.0f, devicePoses, vr::k_unMaxTrackedDeviceCount);
|
|
|
|
|
vr::VRSystem()->GetDeviceToAbsoluteTrackingPose(vr::TrackingUniverseRawAndUncalibrated, 0.0f, devicePoses, vr::k_unMaxTrackedDeviceCount);
|
|
|
|
|
|
|
|
|
|
for (uint32_t id = 0; id < vr::k_unMaxTrackedDeviceCount; ++id)
|
|
|
|
|
{
|
|
|
|
@@ -334,13 +344,12 @@ void LoadProfile(CalibrationContext &ctx)
|
|
|
|
|
|
|
|
|
|
file
|
|
|
|
|
>> ctx.calibratedTrackingSystem
|
|
|
|
|
>> ctx.calibratedRotation.w
|
|
|
|
|
>> ctx.calibratedRotation.x
|
|
|
|
|
>> ctx.calibratedRotation.y
|
|
|
|
|
>> ctx.calibratedRotation.z
|
|
|
|
|
>> ctx.calibratedTranslation.v[0]
|
|
|
|
|
>> ctx.calibratedTranslation.v[1]
|
|
|
|
|
>> ctx.calibratedTranslation.v[2];
|
|
|
|
|
>> ctx.calibratedRotation(1) // yaw
|
|
|
|
|
>> ctx.calibratedRotation(2) // pitch
|
|
|
|
|
>> ctx.calibratedRotation(0) // roll
|
|
|
|
|
>> ctx.calibratedTranslation(0) // x
|
|
|
|
|
>> ctx.calibratedTranslation(1) // y
|
|
|
|
|
>> ctx.calibratedTranslation(2); // z
|
|
|
|
|
|
|
|
|
|
ctx.validProfile = true;
|
|
|
|
|
}
|
|
|
|
@@ -352,42 +361,92 @@ void SaveProfile(CalibrationContext &ctx)
|
|
|
|
|
file
|
|
|
|
|
<< ctx.calibratedTrackingSystem << std::endl
|
|
|
|
|
<< std::setprecision(std::numeric_limits<double>::digits10 + 1)
|
|
|
|
|
<< ctx.calibratedRotation.w << " "
|
|
|
|
|
<< ctx.calibratedRotation.x << " "
|
|
|
|
|
<< ctx.calibratedRotation.y << " "
|
|
|
|
|
<< ctx.calibratedRotation.z << std::endl
|
|
|
|
|
<< ctx.calibratedTranslation.v[0] << " "
|
|
|
|
|
<< ctx.calibratedTranslation.v[1] << " "
|
|
|
|
|
<< ctx.calibratedTranslation.v[2] << std::endl;
|
|
|
|
|
<< ctx.calibratedRotation(1) << " " // yaw
|
|
|
|
|
<< ctx.calibratedRotation(2) << " " // pitch
|
|
|
|
|
<< ctx.calibratedRotation(0) << std::endl // roll
|
|
|
|
|
<< ctx.calibratedTranslation(0) << " " // x
|
|
|
|
|
<< ctx.calibratedTranslation(1) << " " // y
|
|
|
|
|
<< ctx.calibratedTranslation(2) << std::endl; //z
|
|
|
|
|
|
|
|
|
|
ctx.profileLoaded = true;
|
|
|
|
|
ctx.validProfile = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
vr::HmdQuaternion_t VRRotationQuat(Eigen::Vector3d eulerdeg)
|
|
|
|
|
{
|
|
|
|
|
auto euler = eulerdeg * EIGEN_PI / 180.0;
|
|
|
|
|
|
|
|
|
|
Eigen::Quaterniond rotQuat =
|
|
|
|
|
Eigen::AngleAxisd(euler(0), Eigen::Vector3d::UnitZ()) *
|
|
|
|
|
Eigen::AngleAxisd(euler(1), Eigen::Vector3d::UnitY()) *
|
|
|
|
|
Eigen::AngleAxisd(euler(2), Eigen::Vector3d::UnitX());
|
|
|
|
|
|
|
|
|
|
vr::HmdQuaternion_t vrRotQuat;
|
|
|
|
|
vrRotQuat.x = rotQuat.coeffs()[0];
|
|
|
|
|
vrRotQuat.y = rotQuat.coeffs()[1];
|
|
|
|
|
vrRotQuat.z = rotQuat.coeffs()[2];
|
|
|
|
|
vrRotQuat.w = rotQuat.coeffs()[3];
|
|
|
|
|
return vrRotQuat;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
vr::HmdVector3d_t VRTranslationVec(Eigen::Vector3d transcm)
|
|
|
|
|
{
|
|
|
|
|
auto trans = transcm * 0.01;
|
|
|
|
|
vr::HmdVector3d_t vrTrans;
|
|
|
|
|
vrTrans.v[0] = trans[0];
|
|
|
|
|
vrTrans.v[1] = trans[1];
|
|
|
|
|
vrTrans.v[2] = trans[2];
|
|
|
|
|
return vrTrans;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ScanAndApplyProfile(const CalibrationContext &ctx)
|
|
|
|
|
{
|
|
|
|
|
char buffer[vr::k_unMaxPropertyStringSize];
|
|
|
|
|
|
|
|
|
|
vr::TrackedDevicePose_t devicePoses[vr::k_unMaxTrackedDeviceCount];
|
|
|
|
|
vr::VRSystem()->GetDeviceToAbsoluteTrackingPose(vr::TrackingUniverseRawAndUncalibrated, 0.0f, devicePoses, vr::k_unMaxTrackedDeviceCount);
|
|
|
|
|
|
|
|
|
|
for (uint32_t id = 0; id < vr::k_unMaxTrackedDeviceCount; ++id)
|
|
|
|
|
{
|
|
|
|
|
auto deviceClass = vr::VRSystem()->GetTrackedDeviceClass(id);
|
|
|
|
|
if (deviceClass == vr::TrackedDeviceClass_Invalid)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
/*if (deviceClass == vr::TrackedDeviceClass_HMD) // for debugging unexpected universe switches
|
|
|
|
|
{
|
|
|
|
|
vr::ETrackedPropertyError err = vr::TrackedProp_Success;
|
|
|
|
|
auto universeId = vr::VRSystem()->GetUint64TrackedDeviceProperty(id, vr::Prop_CurrentUniverseId_Uint64, &err);
|
|
|
|
|
printf("uid %d err %d\n", universeId, err);
|
|
|
|
|
continue;
|
|
|
|
|
}*/
|
|
|
|
|
|
|
|
|
|
vr::ETrackedPropertyError err = vr::TrackedProp_Success;
|
|
|
|
|
vr::VRSystem()->GetStringTrackedDeviceProperty(id, vr::Prop_TrackingSystemName_String, buffer, vr::k_unMaxPropertyStringSize, &err);
|
|
|
|
|
|
|
|
|
|
if (err == vr::TrackedProp_Success)
|
|
|
|
|
{
|
|
|
|
|
std::string trackingSystem(buffer);
|
|
|
|
|
if (err != vr::TrackedProp_Success)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
if (trackingSystem == ctx.calibratedTrackingSystem)
|
|
|
|
|
{
|
|
|
|
|
//printf("setting calibration for %d (%s)\n", id, buffer);
|
|
|
|
|
InputEmulator.setWorldFromDriverRotationOffset(id, ctx.calibratedRotation);
|
|
|
|
|
InputEmulator.setWorldFromDriverTranslationOffset(id, ctx.calibratedTranslation);
|
|
|
|
|
InputEmulator.enableDeviceOffsets(id, true);
|
|
|
|
|
}
|
|
|
|
|
std::string trackingSystem(buffer);
|
|
|
|
|
|
|
|
|
|
if (trackingSystem != ctx.calibratedTrackingSystem)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
if (deviceClass == vr::TrackedDeviceClass_TrackingReference)
|
|
|
|
|
{
|
|
|
|
|
// TODO(pushrax): detect zero reference switches and adjust calibration automatically
|
|
|
|
|
//auto p = devicePoses[id].mDeviceToAbsoluteTracking.m;
|
|
|
|
|
//printf("%d: %f %f %f\n", id, p[0][3], p[1][3], p[2][3]);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
//printf("setting calibration for %d (%s)\n", id, buffer);
|
|
|
|
|
auto vrRotQuat = VRRotationQuat(ctx.calibratedRotation);
|
|
|
|
|
InputEmulator.setWorldFromDriverRotationOffset(id, vrRotQuat);
|
|
|
|
|
|
|
|
|
|
auto vrTransVec = VRTranslationVec(ctx.calibratedTranslation);
|
|
|
|
|
InputEmulator.setWorldFromDriverTranslationOffset(id, vrTransVec);
|
|
|
|
|
|
|
|
|
|
InputEmulator.enableDeviceOffsets(id, true);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@@ -457,39 +516,28 @@ void CalibrationTick()
|
|
|
|
|
|
|
|
|
|
if (samples.size() == totalSamples)
|
|
|
|
|
{
|
|
|
|
|
printf("\n");
|
|
|
|
|
if (ctx.state == Rotation)
|
|
|
|
|
{
|
|
|
|
|
auto rot = CalibrateRotation(samples);
|
|
|
|
|
Eigen::Quaterniond rotQuat(rot);
|
|
|
|
|
|
|
|
|
|
vr::HmdQuaternion_t vrRotQuat;
|
|
|
|
|
vrRotQuat.x = rotQuat.coeffs()[0];
|
|
|
|
|
vrRotQuat.y = rotQuat.coeffs()[1];
|
|
|
|
|
vrRotQuat.z = rotQuat.coeffs()[2];
|
|
|
|
|
vrRotQuat.w = rotQuat.coeffs()[3];
|
|
|
|
|
ctx.calibratedRotation = CalibrateRotation(samples);
|
|
|
|
|
|
|
|
|
|
auto vrRotQuat = VRRotationQuat(ctx.calibratedRotation);
|
|
|
|
|
InputEmulator.setWorldFromDriverRotationOffset(ctx.targetID, vrRotQuat);
|
|
|
|
|
InputEmulator.enableDeviceOffsets(ctx.targetID, true);
|
|
|
|
|
|
|
|
|
|
ctx.calibratedRotation = vrRotQuat;
|
|
|
|
|
ctx.state = Translation;
|
|
|
|
|
}
|
|
|
|
|
else if (ctx.state == Translation)
|
|
|
|
|
{
|
|
|
|
|
auto trans = CalibrateTranslation(samples);
|
|
|
|
|
|
|
|
|
|
vr::HmdVector3d_t vrTrans;
|
|
|
|
|
vrTrans.v[0] = trans[0];
|
|
|
|
|
vrTrans.v[1] = trans[1];
|
|
|
|
|
vrTrans.v[2] = trans[2];
|
|
|
|
|
ctx.calibratedTranslation = CalibrateTranslation(samples);
|
|
|
|
|
|
|
|
|
|
auto vrTrans = VRTranslationVec(ctx.calibratedTranslation);
|
|
|
|
|
InputEmulator.setWorldFromDriverTranslationOffset(ctx.targetID, vrTrans);
|
|
|
|
|
|
|
|
|
|
ctx.calibratedTranslation = vrTrans;
|
|
|
|
|
ctx.state = None;
|
|
|
|
|
|
|
|
|
|
SaveProfile(ctx);
|
|
|
|
|
printf("finished calibration, profile saved\n");
|
|
|
|
|
|
|
|
|
|
ctx.state = None;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
samples.clear();
|
|
|
|
|