Skip to content

Commit

Permalink
Write buffered sfusion for higher CPU util
Browse files Browse the repository at this point in the history
  • Loading branch information
nekomona committed Mar 18, 2024
1 parent ac49912 commit 606686c
Show file tree
Hide file tree
Showing 6 changed files with 155 additions and 8 deletions.
2 changes: 1 addition & 1 deletion src/consts.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ enum class ImuID {
#define IMU_BNO086 BNO080Sensor
#define IMU_BMI160 BMI160Sensor
#define IMU_ICM20948 ICM20948Sensor
#define IMU_ICM42688 SoftFusionICM42688
#define IMU_ICM42688 SoftFusionICM42688Buffered
#define IMU_BMI270 SoftFusionBMI270
#define IMU_LSM6DS3TRC SoftFusionLSM6DS3TRC
#define IMU_LSM6DSV SoftFusionLSM6DSV
Expand Down
7 changes: 5 additions & 2 deletions src/sensors/SensorManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
#include "icm20948sensor.h"
#include "sensoraddresses.h"
#include "softfusion/softfusionsensor.h"
#include "softfusion/softfusionsensorbuffered.h"
#include "softfusion/drivers/lsm6ds3trc.h"
#include "softfusion/drivers/icm42688.h"
#include "softfusion/drivers/bmi270.h"
Expand All @@ -50,6 +51,7 @@ namespace SlimeVR
using SoftFusionICM42688 = SoftFusionSensor<SoftFusion::Drivers::ICM42688, SoftFusion::I2CImpl>;
using SoftFusionBMI270 = SoftFusionSensor<SoftFusion::Drivers::BMI270, SoftFusion::I2CImpl>;
using SoftFusionLSM6DSV = SoftFusionSensor<SoftFusion::Drivers::LSM6DSV, SoftFusion::I2CImpl>;
using SoftFusionICM42688Buffered = SoftFusionSensorBuffered<SoftFusion::Drivers::ICM42688, SoftFusion::I2CImpl>;

// TODO Make it more generic in the future and move another place (abstract sensor interface)
void SensorManager::swapI2C(uint8_t sclPin, uint8_t sdaPin)
Expand Down Expand Up @@ -183,8 +185,9 @@ namespace SlimeVR
bool allSensorsReady = true;
for (auto &sensor : m_Sensors) {
if (!sensor->isWorking()) continue;
if (sensor->hasNewDataToSend()) shouldSend = true;
allSensorsReady &= sensor->hasNewDataToSend();
bool sensorReady = sensor->hasNewDataToSend();
if (sensorReady) shouldSend = true;
allSensorsReady &= sensorReady;
}

if (now - m_LastBundleSentAtMicros < PACKET_BUNDLING_BUFFER_SIZE_MICROS) {
Expand Down
2 changes: 1 addition & 1 deletion src/sensors/sensor.h
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ class Sensor
const Quat& getFusedRotation() {
return fusedRotation;
};
bool hasNewDataToSend() {
virtual bool hasNewDataToSend() {
return newFusedRotation || newAcceleration;
};

Expand Down
11 changes: 8 additions & 3 deletions src/sensors/softfusion/drivers/icm42688.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ struct ICM42688
};
struct GyroConfig {
static constexpr uint8_t reg = 0x4f;
static constexpr uint8_t value = (0b001 << 5) | 0b0111; //1000dps, odr=500Hz
static constexpr uint8_t value = (0b001 << 5) | 0b0111; //1000dps, odr=200Hz
};
struct AccelConfig {
static constexpr uint8_t reg = 0x50;
Expand Down Expand Up @@ -123,6 +123,13 @@ struct ICM42688
const auto fifo_bytes = i2c.readReg16(Regs::FifoCount);

std::array<uint8_t, FullFifoEntrySize * 8> read_buffer; // max 8 readings

// Flush FIFO if excessive data in FIFO
if (fifo_bytes > read_buffer.size()) {
i2c.writeReg(0x4B, 0x02);
i2c.writeReg(0x4B, 0x00);
}

const auto bytes_to_read = std::min(static_cast<size_t>(read_buffer.size()),
static_cast<size_t>(fifo_bytes)) / FullFifoEntrySize * FullFifoEntrySize;
digitalWrite(9, HIGH);
Expand All @@ -131,9 +138,7 @@ struct ICM42688
for (auto i=0u; i<bytes_to_read; i+=FullFifoEntrySize) {
FifoEntryAligned entry;
memcpy(entry.raw, &read_buffer[i+0x1], sizeof(FifoEntryAligned)); // skip fifo header
digitalWrite(20, HIGH);
processGyroSample(entry.part.gyro, GyrTs);
digitalWrite(20, LOW);

if (entry.part.accel[0] != -32768) {
processAccelSample(entry.part.accel, AccTs);
Expand Down
4 changes: 3 additions & 1 deletion src/sensors/softfusion/softfusionsensor.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ class SoftFusionSensor : public Sensor
m_fusion = SensorFusionRestDetect(m_calibration.G_Ts, m_calibration.A_Ts, m_calibration.M_Ts);
}

public:
void processAccelSample(const int16_t xyz[3], const sensor_real_t timeDelta)
{
sensor_real_t accelData[] = {
Expand All @@ -92,6 +93,7 @@ class SoftFusionSensor : public Sensor
static_cast<sensor_real_t>(GScale * (static_cast<sensor_real_t>(xyz[2]) - m_calibration.G_off[2]))};
m_fusion.updateGyro(scaledData, m_calibration.G_Ts);
}
private:

void eatSamplesForSeconds(const size_t seconds) {
const auto targetDelay = millis() + 1000 * seconds;
Expand Down Expand Up @@ -142,7 +144,7 @@ class SoftFusionSensor : public Sensor
m_fusion(imu::GyrTs, imu::AccTs, imu::MagTs), m_sensor(I2CImpl(imu::Address + addrSuppl), m_Logger) {}
~SoftFusionSensor(){}

void motionLoop() override final
void motionLoop() override
{
sendTempIfNeeded();

Expand Down
137 changes: 137 additions & 0 deletions src/sensors/softfusion/softfusionsensorbuffered.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
#pragma once

#include "softfusionsensor.h"

#include <vector>

namespace SlimeVR::Sensors
{

template <template<typename I2CImpl> typename T, typename I2CImpl>
class SoftFusionSensorBuffered : public SoftFusionSensor<T, I2CImpl>
{
static constexpr auto BufferLength = 128;

enum class RawDataPacketType:std::uint16_t {
Unknown = 0,
Gyro,
Accel
};

struct SensorRawDataPacket {
RawDataPacketType type;
int16_t xyz[3];
};

std::vector<SensorRawDataPacket> m_buffer;

using s = SoftFusionSensor<T, I2CImpl>;

void setAcceleration(Vector3 a)
{
s::acceleration = a;
s::newAcceleration = true;
}

void setFusedRotation(Quat r)
{
s::fusedRotation = r;
s::newFusedRotation = true;
}

void processAccelSample(const int16_t xyz[3], const sensor_real_t timeDelta)
{
SensorRawDataPacket packet = {RawDataPacketType::Accel, xyz[0], xyz[1], xyz[2]};
m_buffer.push_back(packet);
if (m_buffer.size() >= BufferLength) {
m_buffer.clear();
}
}

void processGyroSample(const int16_t xyz[3], const sensor_real_t timeDelta)
{
SensorRawDataPacket packet = {RawDataPacketType::Gyro, xyz[0], xyz[1], xyz[2]};
m_buffer.push_back(packet);
if (m_buffer.size() >= BufferLength) {
m_buffer.clear();
}
}

public:
SoftFusionSensorBuffered(uint8_t id, uint8_t addrSuppl, float rotation, uint8_t sclPin, uint8_t sdaPin, uint8_t intPin)
: SoftFusionSensor<T, I2CImpl>(id, addrSuppl, rotation, sclPin, sdaPin, intPin) {
m_buffer.reserve(BufferLength);
}

void motionLoop() override
{
// sendTempIfNeeded();

// read fifo updating fusion
uint32_t now = micros();
constexpr uint32_t targetPollIntervalMicros = 6000;
uint32_t elapsed = now - s::m_lastPollTime;
if (elapsed >= targetPollIntervalMicros) {
s::m_lastPollTime = now - (elapsed - targetPollIntervalMicros);

xSemaphoreTake(s::updateMutex, portMAX_DELAY);
s::m_sensor.bulkRead(
[&](const int16_t xyz[3], const sensor_real_t timeDelta) { processAccelSample(xyz, timeDelta); },
[&](const int16_t xyz[3], const sensor_real_t timeDelta) { processGyroSample(xyz, timeDelta); }
);
xSemaphoreGive(s::updateMutex);

optimistic_yield(100);
}
}

bool hasNewDataToSend() override
{
if (xSemaphoreTake(s::updateMutex, 0) == pdTRUE) {
for (auto & u : m_buffer) {
if (u.type == RawDataPacketType::Gyro) {
s::processGyroSample(u.xyz, 0);
} else if (u.type == RawDataPacketType::Accel) {
s::processAccelSample(u.xyz, 0);
}
}
m_buffer.clear();

xSemaphoreGive(s::updateMutex);
optimistic_yield(100);
}

// send new fusion values when time is up
uint32_t now = micros();
constexpr float maxSendRateHz = 120.0f;
constexpr uint32_t sendInterval = 1.0f/maxSendRateHz * 1e6;
uint32_t elapsed = now - s::m_lastRotationPacketSent;
if (elapsed >= sendInterval) {
s::m_lastRotationPacketSent = now - (elapsed - sendInterval);

setFusedRotation(s::m_fusion.getQuaternionQuat());
setAcceleration(s::m_fusion.getLinearAccVec());
}

if (s::newFusedRotation) {
return true;
}
return false;
}

void sendData() override
{
if (s::newFusedRotation) {
s::newFusedRotation = false;
Quat rotation = s::getFusedRotation();
networkConnection.sendRotationData(s::sensorId, &rotation, DATA_TYPE_NORMAL, s::calibrationAccuracy);
if (s::newAcceleration) {
s::newAcceleration = false;
networkConnection.sendSensorAcceleration(s::sensorId, s::acceleration);
}
}
}

};

} // namespace

0 comments on commit 606686c

Please sign in to comment.