I am trying to use my phone as a mock data input for sensors that I am later connecting through bluetooth. All I need it for is for setting up relative positioning when given the acceleration, but I have this recurring issue where after I stop moving my phone, the estimated position goes back to where I started the motion, and swiftly moving my phone causes it to become very inaccurate very fast. I am aware that dead reckoning is not exactly the most precise method, but I also don't exactly need it to be perfect.
private func startRandomizerTask() {
guard motionManager.isDeviceMotionAvailable else { return }
motionManager.deviceMotionUpdateInterval = 1.0 / 200.0
motionManager.startDeviceMotionUpdates(using: .xArbitraryZVertical)
lastMotionTimestamp = nil
velocity = .init(0,0,0)
position = .init(-3.00, -9.25, 0)
accelBias = .init(0,0,0)
accelSmoothed = .init(0,0,0)
let smallVelocityThreshold: Double = 0.01
Task.detached { [weak self] in
guard let self = self else { return }
while !Task.isCancelled {
await MainActor.run {
guard self.settings.values["useOnboardPositioning"] == true,
let dm = self.motionManager.deviceMotion else { return }
let now = dm.timestamp
if let last = self.lastMotionTimestamp {
let dt = now - last
if dt <= 0 { self.lastMotionTimestamp = now; return }
// device acceleration
var userAccDevice = SIMD3(dm.userAcceleration.x, dm.userAcceleration.z, dm.userAcceleration.y) * 9.81
// rotate to world
var accWorldRaw = self.rotateToWorld(userAccDevice, attitude: dm.attitude)
accWorldRaw = SIMD3(
max(min(accWorldRaw.x, self.accelClampThreshold), -self.accelClampThreshold),
max(min(accWorldRaw.y, self.accelClampThreshold), -self.accelClampThreshold),
max(min(accWorldRaw.z, self.accelClampThreshold), -self.accelClampThreshold)
)
self.velocity += accWorldRaw * dt
let speed = sqrt(self.velocity.x*self.velocity.x + self.velocity.y*self.velocity.y + self.velocity.z*self.velocity.z)
if speed > 0 {
let dragFactor = self.dragCoefficient * speed
self.velocity -= self.velocity * dragFactor * dt
}
self.velocity *= self.velocityDamping
// clamp velocity
self.velocity = SIMD3(
max(min(self.velocity.x, self.maxVelocity), -self.maxVelocity),
max(min(self.velocity.y, self.maxVelocity), -self.maxVelocity),
max(min(self.velocity.z, self.maxVelocity), -self.maxVelocity)
)
// try and get rid of small numbers
if abs(self.velocity.x) < smallVelocityThreshold { self.velocity.x = 0 }
if abs(self.velocity.y) < smallVelocityThreshold { self.velocity.y = 0 }
if abs(self.velocity.z) < smallVelocityThreshold { self.velocity.z = 0 }
let newPosition = self.position + (self.velocity * dt * 20)
// max distance
let vecFromElbow = newPosition - (self.Sensors["Left"]?["Elbow"])!
let dist = sqrt(vecFromElbow.x*vecFromElbow.x + vecFromElbow.y*vecFromElbow.y + vecFromElbow.z*vecFromElbow.z)
if dist > self.armLength && dist > 0 {
let scale = self.armLength / dist
self.position = (self.Sensors["Left"]?["Elbow"])! + vecFromElbow * scale
} else {
self.position = newPosition
}
// update sensor position
if var left = self.Sensors["Left"] {
left["Wrist"] = Point3D(self.position.x, self.position.y, self.position.z)
self.Sensors["Left"] = left
}
//print("dt:\(dt) accMag:\(accMag) spike:\(spikeDetected) isStationary:\(isStationary) vel:(\(self.velocity.x),\(self.velocity.y),\(self.velocity.z)) pos:(\(self.position.x),\(self.position.y),\(self.position.z))")
print("\(round(self.velocity.x*10000)/10000),\(round(self.velocity.y*10000)/10000),\(round(self.velocity.z*10000)/10000) | \(accWorldRaw.y < 0 ? "+" : "-")")
}
self.lastMotionTimestamp = now
}
do {
try await Task.sleep(nanoseconds: 1_000_000)
}
catch {
break
}
}
}
}