On this page
Torque control using voltage
This torque control approach allows you to run the BLDC and Stepper motor as it is simple DC motor, where you set the target voltage \(u_q\) to be set to the motor and the FOC algorithm calculates the necessary phase voltages \(u_a\) ,\(u_b\) and \(u_c\) for smooth operation. This mode is enabled by:
// voltage torque control mode
motor.torque_controller = TorqueControlType::voltage;
How does it work?
Choose the motor type:
BLDC motors Stepper motors Hybrid Stepper motors
The voltage control algorithm reads the angle \(a\) from the position sensor and the gets target \(u_q\) voltage value from the user and using the FOC algorithm sets the appropriate \(u_a\), \(u_b\) and \(u_c\) voltages to the motor. FOC algorithm ensures that these voltages generate the magnetic force in the motor rotor exactly with 90 degree offset from its permanent magnetic field, which guarantees maximal torque, this is called commutation.
The assumption of the pure voltage control is that the torque generated (which is proportional to the current \(\tau = K_t i_q\)) in the motor is proportional the voltage as \(u_q\) set by the user. Maximal torque corresponds to the maximal \(u_q\) which is conditioned by the power supply voltage available, and the minimal torque is of course for \(u_q= 0\).
\[\tau \propto i_q \propto u_q\]This equation is a rough approximation. In practice it is true only for low speeds, where the motors Back EMF voltage is negligible. As the motor speed increases, the Back EMF voltage generated by the motor will reduce the voltage set to the motor and therefore the current and torque generated will be lower than expected. Therefore in practice the voltage is proportional to the torque at low speeds, while it is proportional to the motor velocity at high speeds.
🔍 Where does this proportionality come from?
The motor electical equation (in static conditions - ex. constant speed ) has a form of:
\[u_q = i_q R + K_{e} v\]where the \(R\) is the phase resistance of the motor, \(K_{e}\) is the back-emf constant of the motor and \(v\) is the motor velocity. If we assume that the current \(i_q\) is proportional to the torque generated \(\tau\) as \(K_t i_q =\tau\) (\(K_t\) is the torque constant), we can rewrite the equation as
\[\tau = \frac{K_t}{R} u_q - \frac{K_t K_{e}}{R} v\]So in this equation we can see the when the motor is not moving (v=0) the voltage is proportional to the torque (through the \(\frac{K_t}{R}\) constant) but as the motor starts moving the back-emf voltage generated by the motor reduces the voltage set to the motor, and therefore the torque generated is lower than expected.
If we imagine that in an ideal conditions, where the motor turns without any load, the torque necesary to maintain the motor velocity is \(\tau=0\), then the equation can be rewritten as
\[u_q = K_{e} v\]This means that in the ideal conditions the voltage set to the motor is proportional to the motor velocity, and therefore the motor behaves as if it is a simple DC motor where you set the voltage and the velocity reached is proportional to the voltage set.
In practice, these ideal conditions are never met, but the behavior of the motor is similar to the one described above, where at low speeds the voltage is proportional to the torque, while at high speeds it is proportional to the velocity.
Because Voltage Mode does not account for this velocity-dependent drop, the torque will always fade as you go faster. This is exactly what the Estimated Current Mode aims to fix.
⚠️ Practical limitations
This torque control approach is the fastest and most simple one to setup by it does not limit the current in any way!
If you can find motor parameters (phase resistance and the KV rating) using estimated current mode is recommended.
Expected motor behavior
If the user sets the desired voltage \(u_q\) of 0 Volts, the motor should not move and have some resistance, not too much, but more than when the motor is disconnected from the driver.
If you set a certain desired voltage \(u_q\) your motor should start moving and the velocity reached should be proportional to the voltage set \(u_q\). The behavior should be very similar to the DC motor controlled by changing the voltage on its wires.
Configuration and Torque Limits
To ensure safe operation and prevent hardware damage, you must configure the limits of your motor and driver. In Voltage Mode, your primary safety tool is motor.voltage_limit.
// a setter function to set the voltage limit
motor.updateVoltageLimit(2.0);
// or you can set it directly as a variable (not recommended)
// [V] - set the maximum voltage allowed
motor.voltage_limit = 2.0;
Choosing your voltage limit
Since Voltage Mode does not sense current, it cannot “know” if the motor is pulling too much power. A common mistake is setting the limit to the power supply voltage (e.g., 12V), which can easily fry a low-resistance motor.
The Rule of Thumb:
- Gimbal Motors (\(R>5\Omega\)): You can safely set the limit to 30–50% of your power supply.
- Drone Motors (\(R<1\Omega\)): Start very low (0.5V to 1.5V). Even a small voltage on these motors creates massive current.
⚠️ Don't be fooled by the motor size! A small drone motor can draw 10A at 2V, which can easily damage your driver and power supply if not limited.
- Stepper Motors: (\(R \approx 2\Omega\)): Start with 2–5V and adjust based on performance and heat. Steppers can often handle higher voltages due to their construction, but they do have lower resistance than gimbal motors, so be cautious.
- Heat Check: If the motor or driver becomes too hot to touch after 30 seconds of operation, decrease the
motor.voltage_limitimmediately.
Understanding the Voltage-Torque-Velocity relationship
In this mode, the voltage you set is split between overcoming the motor’s internal resistance (to create torque) and overcoming the Back-EMF (the “generator” effect of the spinning motor).
| State | Equation | Result |
|---|---|---|
| Motor Stalled/Static | \(u_q = i_q \cdot R\) | Maximum torque is produced. All voltage goes into current. |
| Motor Spinning | \(u_q = i_q \cdot R + K_{e}\cdot v\) | Torque drops because \(u_q\) is now shared with the BEMF (proportional to speed \(v\)). |
| At Max Speed | \(u_q \approx K_{e}\cdot v\) | Current (\(i_q\)) drops to near zero. No torque is left to accelerate. |
đź’ˇ Tip
If you find that your motor is too weak at high speeds, you have reached the BEMF limit of your current
voltage_limit. To fix this, you would either need to increase the limit (carefully) or switch to Estimated Current Mode, which automatically increases voltage to compensate for Back-EMF.
Related topics
See here for a dive deep into the torque theory. Go here for the implementation details.
Torque control example code
A simple example of the voltage based torque control and setting the target voltage by serial command interface.
BLDC motors Stepper motors Hybrid Stepper motors
#include <SimpleFOC.h>
// BLDC motor & driver instance
BLDCMotor motor = BLDCMotor(11);
BLDCDriver3PWM driver = BLDCDriver3PWM(9, 5, 6, 8);
// encoder instance
Encoder encoder = Encoder(2, 3, 500);
// channel A and B callbacks
void doA(){encoder.handleA();}
void doB(){encoder.handleB();}
// instantiate the commander
Commander command = Commander(Serial);
void doMotor(char* cmd) { command.motor(&motor, cmd); }
void setup() {
// initialize encoder sensor hardware
encoder.init();
encoder.enableInterrupts(doA, doB);
// link the motor to the sensor
motor.linkSensor(&encoder);
// driver config
// power supply voltage [V]
driver.voltage_power_supply = 12;
driver.init();
// link driver
motor.linkDriver(&driver);
// set the torque control type
motor.torque_controller = TorqueControlType::voltage;
// set motion control loop to be used
motor.controller = MotionControlType::torque;
// use monitoring with serial
Serial.begin(115200);
// comment out if not needed
motor.useMonitoring(Serial);
// initialize motor
motor.init();
// align sensor and start FOC
motor.initFOC();
// add target command M
command.add('M', doMotor, "motor");
Serial.println(F("Motor ready."));
Serial.println(F("Set the target voltage using serial terminal:"));
_delay(1000);
}
void loop() {
// main FOC algorithm function
motor.loopFOC();
// Motion control function
motor.move();
// user communication
command.run();
}