I’m not sure how useful it is for the general public, but I wrote some functions to measure gimbal resistance, inductance (both Q and D), and KV in SimpleFOC.
(My doubts about usefulness are related to needing current and position sensors for this, and if you have those, you typically don’t really need to know these motor parameters.)
I plan to do some feed forward control on the current loop, only using the PID for the last bit of error.
With my GBM2804-100T I’m consistently getting 6.3Ω, 2.0mH q, 1.5mH d, and 220kv testing with 3V.
Here’s the code, might anyone ever need something similar:
// 20ms time constant filter
LowPassFilter currentFilter(0.02f);
// Function to measure phase resistance
float measureResistance(float testVoltage) {
// Update sensor and electrical angle
motor.sensor->update();
motor.electrical_angle = motor.electricalAngle();
// Set test voltage on d-axis
motor.setPhaseVoltage(0, testVoltage, motor.electrical_angle);
// Let current stabilize with filtering
uint32_t timestamp = micros();
float filteredCurrent = 0;
while (micros() - timestamp < 100000) {
filteredCurrent = currentFilter(motor.current_sense->getFOCCurrents(motor.electrical_angle).d);
delayMicroseconds(500);
}
// Clear voltage and let system settle
motor.setPhaseVoltage(0, 0, motor.electrical_angle);
delayMicroseconds(50000);
// Return calculated resistance
return testVoltage / filteredCurrent;
}
// Enum for axis selection
enum Axis {
AXIS_D,
AXIS_Q
};
// Function to measure inductance on a specific axis
float measureAxisInductance(float testVoltage, float resistance, Axis axis) {
// Calculate target current (63.2% of steady state)
float steadyStateCurrent = testVoltage / resistance;
float targetCurrent = steadyStateCurrent * 0.632f;
// Clear voltage and let system settle
motor.setPhaseVoltage(0, 0, motor.electrical_angle);
delayMicroseconds(50000);
// Update sensor and electrical angle
motor.sensor->update();
motor.electrical_angle = motor.electricalAngle();
// Apply voltage to appropriate axis
motor.setPhaseVoltage(axis == AXIS_D ? 0 : testVoltage, axis == AXIS_D ? testVoltage : 0, motor.electrical_angle);
// Measure time to reach target current
float current = 0;
uint32_t endTime = 0;
uint32_t startTime = micros();
uint32_t timeout = startTime + 5000; // 5ms timeout
while (current < targetCurrent && micros() < timeout) {
// Get current on appropriate axis
current = (axis == AXIS_D)
? motor.current_sense->getFOCCurrents(motor.electrical_angle).d
: motor.current_sense->getFOCCurrents(motor.electrical_angle).q;
endTime = micros();
}
// Clear voltage
motor.setPhaseVoltage(0, 0, motor.electrical_angle);
// Calculate time constant in seconds
float timeConstant = (endTime - startTime) / 1000000.0f;
// Return inductance
return resistance * timeConstant;
}
LowPassFilter velocityFilter(0.05f);
LowPassFilter currentFilterKV(0.1f);
uint32_t measurement_duration = 1000000; // 1 second measurement
float filtered_velocity;
float filteredCurrent;
// Function to measure KV rating (RPM/volt)
float measureKV(float resistance, float testVoltage) {
// Measure velocity over a period of time
filtered_velocity = 0;
uint32_t measurement_start = micros();
//
while (micros() - measurement_start < measurement_duration) {
// Update sensor readings
motor.sensor->update();
motor.electrical_angle = motor.electricalAngle();
// Filter the velocity
filtered_velocity = velocityFilter(motor.sensor->getVelocity());
float currentD = motor.current_sense->getFOCCurrents(motor.electrical_angle).d;
float currentQ = motor.current_sense->getFOCCurrents(motor.electrical_angle).q;
float totalCurrent = sqrtf(currentD * currentD + currentQ * currentQ);
filteredCurrent = currentFilterKV(totalCurrent);
// Update FOC algorithm to maintain proper field orientation
motor.setPhaseVoltage(testVoltage, 0, motor.electrical_angle);
// Brief delay for next measurement
delayMicroseconds(100);
}
// Stop motor and wait for spindown
motor.setPhaseVoltage(0, 0, motor.electrical_angle);
delayMicroseconds(200000);
// Convert rad/s to RPM (rad/s * 60 / 2π)
float rpm = filtered_velocity * 60.0f / (TWO_PI);
// Calculate KV rating (RPM per volt)
return abs(rpm / (testVoltage - resistance * filteredCurrent));
}
// Main measurement function
void measureFOCQDRI(float testVoltage, float kv_test_voltage) {
// Measure phase resistance
resistance = measureResistance(testVoltage);
// Measure inductances
inductanceq = measureAxisInductance(testVoltage, resistance, AXIS_Q);
inductanced = measureAxisInductance(testVoltage, resistance, AXIS_D);
// Measure KV rating (using a lower voltage for safety)
kv_rating = measureKV(resistance, kv_test_voltage);
}