What motor to choose for constant torque at low or no velocity?

I’m thinking of building a sort of exercise machine to see if this works https://onlinelibrary.wiley.com/doi/abs/10.1111/sms.14220. An exercise machine that can provide constant torque when needed for an eccentric movement, then moved back to the starting position with no effort.

Basically, I need a motor that when powered on will exert a constant torque at stall speed, then maintain that same torque while slowly pulling and winding a rope (and the user resisting the force and slowly moving along). Turn it off, move the handle in the starting position again, re-engage the motor to repeat the same movement as above. The amount of torque will depend on the gearing of the machine and various levers, still all TBD.

I started looking at SimpleFOC as a solution and tried with a few of the brushless motors I have, but none seems to be the right type of motor. The lowest kV motor I have is a 360kV one, and it’s clear that it will not work: torque at close to stall speeds is a fraction of the motor potential, not to mention quick overheating (ok, it’s a drone motor, so that might improve with a fan, which is an option). I will probably need a gimbal-like motor, but those get expensive quickly. I also have bought a used hoverboard and plan to test its motor next.

I started wondering if, though, a stepper motor might be better for my use. Stepper motors have the highest torque at stall speeds, and even NEMA34 motors with lots of torque can be had for not much more than $50-$75.

Asking the experts here: what are the best options to experiment with? I can definitely look at using a gearbox, but low/stall speeds will still be required.

I was going to suggest a hoverboard motor :slight_smile: Though the built-in hall sensors are not ideal, as they will move in 4 degree steps. But most likely it would be possible to replace them with linear halls and transform the three signals into a more precise angle. That’s on my to-do list, but I probably won’t get to it for a few months at least. If you want to try it, I’ll mail you some 49E sensors. I bought a 100 pack a while back because it was about the same price as 10, so I have more than I’ll ever need.

If the signals they produce are sinusoidal, I think you could use Clarke+Park transform to get the angle. But most likely there will be a lot of saturation due to the close proximity to the magnets, so it will need a little more work. Possibly just throwing out the most saturated one and using a + b + c = 0 to calculate its value from the other two.

Stepper could certainly work too. Good use for Juan-Antonio’s new driver

1 Like

I’ve done this. Use a hoverboard motor. Or a large AC servo motor. Industrial AC servos are designed to minimize cogging torque. Make sure to use an encoder as opposed to halls. Also, you will regenerate significant energy when you rotate against a torque, so make sure you use a battery, or have a circuit to shunt the current on the DC bus and prevent the voltage from rising.

You can use SimpleFOC to control the torque as a function of angular position, etc.

1 Like

Thanks for the info and the offer to send hall sensors. Given the low velocity I was actually thinking of using an encoder (optical or magnetic). Would using 3 hall sensors inside the motor be better? I mean, obviously more compact and easier from a mechanical point of view, but I’m asking from a control point of view. In my still limited understanding, an encoder should provide the best possible control loop especially for near-stall speeds.

And, yes, I have been keeping an eye on the Field Stack driver, really cool.

Thanks!

In my case, when the motor is providing torque, the movement is always driven by the motor rotation direction, so there should be no regen. The idea of the machine is to use the muscles in “eccentric mode”, i.e. the muscle resists movement and slowly gives, like when you lower slowly a weight after having lifted it.

The return phase, I was thinking of setting the driver output in high-z mode, and basically disconnecting the motor from the power source. A freely rotating brushless motor should not generate significant energy, or am I wrong?

Unless I looked at the wrong products, AC servo motors seem to be rather expensive (compared to steppers or hoverboard motors) due to the extra driver circuit. Do you have any to suggest looking at?

Hoverboard motor with metal housing and drill holes on both sides for air cooling, add a large computer 12v cooling fan for forced convection. Remove the rubber tire and you got exactly what you need. The built-in halls are more than enough for what you need. You can get this whole thing for may be less that $50. I’ve done something very similar for a different project.

A much bigger question is will your motor driver support this motor.

Cheers,
Valentine

1 Like

Gotcha. If you’re just controlling the eccentric then you’ll be fine as power will be going into the motor. Yep, AC servo motors are expensive…you can usually find them pretty cheap on eBay though. The nice thing about them is they have near zero cogging…

Overall, going the hoverboard route will work, as the pole count is pretty high and cogging torque is not that much. +1 to @Valentine s comment on cooling. You’ll definitely want to use an encoder. Something around 1000 CPR should work well.

1 Like

Thanks!

I have a DRV8302 Aliexpress board (same as DRV8302 example | Arduino-FOC). That is supposed to be 45V, 15A, and should be enough for a 36V, 10A motor, possibly with extra cooling.

I’m assuming I will have to use torque control and monitor the current… is that correct? Actually, any pointer on what to look out for would help. I never tried to drive a motor at constant torque near stall velocity and I’m still a bit confused on how it will work

Speaking of which, the example DRV8302 example | Arduino-FOC mentions

BEWARE📢
Up to this moment (version 1.4.1 ) the library doesn’t implement the current control loop. The motor torque is controlled via voltage directly.

I think I saw current control (low side) working for that board. It requires a fast MCU with low side current sensing, but I plan to use a Nucleo G474, with should be plenty fast (worst case, I also have a Nucleo H741, which is a beast :slight_smile: ). Is that warning simply an old leftover in the docs, or would the DRV8302 be a bad board for what I have in mind?

For what you need you only implement voltage position control. It’s a really simple use case. Forget about current , etc. The torque is estimated with voltage, start the motor, set the position, lock it and done.

Then when you start pilling the rope to unwind it, the motor will keep constant torque by trying to bring the motor to zero. Control the torque by changing the phase voltage.

There was a guy he did something exactly the same but with a smaller motor for performing some magic tricks, that was last year. If I got the time I may find the posts. I ended up designing a tiny driver for him because the magic required something really small.

Cheers,
Valentine

Just a comment on that point:

You’re right and an encoder or magnetic sensor (SPI-based) would be far better for low speed / holding position type applications than the hall sensors…

1 Like

Yeah the guy who did that before used an SPI magnetic sensor.

Well, with the halls, you will feel very interesting haptics when you pull on the ropes :slight_smile:
The motor will be fighting you…

Cheers,
Valentine

Encoder is better, go with that. It’s just mechanically more difficult to set up, but on second thought it probably won’t be too bad in a stationary exercise machine compared to most robotics stuff that I’m used to.

I would say that you may be able to use voltage mode to control torque, but by doing do, you implicitly couple motor torque and speed, and that may not be what you want. If you use true current control, the motor torque becomes independent of speed, which may be better for your application. As @Valentine said, you could control the voltage based on position to overcome this coupling of torque and speed, but if you use current control, then you don’t need to go through that effort.

In my setup, I used a DRV8302 board from AliExpress with a hoverboard motor and used full current control, all run by a Nucleo F401RE. It worked well for me, and I did exactly the same thing you’re trying to do here.

Terrific, thanks! Would you consider sharing your code? (or a generic framework I could use as a starting point, if you don’t want to share everything?)

In my initial G431B-ESC experiments I had quite a lot of problems figuring out parameters to avoid excessive currents and avoid problems. And I’m a bit scared by how powerful the hoverboard motor is :slight_smile: Your setup is basically identical to what I plan to use

@robca I’ve also been able to use the same technique with the B-G431 board - we can message about that if you want. I think I have a decent understanding how to get it working with current control and an I2C or incremental encoder.

Below is the code I used with my large AC servo motor and the DRV8302 board, along with a Nucleo F401RE board. I use PlatformIO for development. This is just to get you started - you can play around with the logic to get the control you want. I like setting the torque as a function of rotor angle…

#include <Arduino.h>
#include <SimpleFOC.h>

// DRV8302 pins connections
#define INH_A PA8
#define INH_B PA9
#define INH_C PA10

#define EN_GATE PC4
#define M_PWM PB4
#define M_OC PB3
#define OC_ADJ PB13
#define OC_GAIN PB5

#define IOUTA A0
#define IOUTB A1
#define IOUTC A2

#define button PC13

// Motor instance
BLDCMotor motor = BLDCMotor(21);
BLDCDriver3PWM driver = BLDCDriver3PWM(INH_A, INH_B, INH_C, EN_GATE);

// DRV8302 board has 0.005Ohm shunt resistors and the gain of 12.22 V/V
LowsideCurrentSense cs = LowsideCurrentSense(0.005f, 12.22f, IOUTA, IOUTB, IOUTC);

// encoder instance
MagneticSensorSPI encoder = MagneticSensorSPI(AS5048_SPI, 14);

// commander interface
Commander command = Commander(Serial);
void onMotor(char* cmd){ command.motor(&motor, cmd); }


void setup() {

  pinMode(button, INPUT_PULLUP);
  
  // initialize encoder sensor hardware
  encoder.init();
  
  // link the motor to the sensor
  motor.linkSensor(&encoder);

  // DRV8302 specific code
  // M_OC  - enable overcurrent protection
  pinMode(M_OC,OUTPUT);
  digitalWrite(M_OC,LOW);
  // M_PWM  - enable 3pwm mode
  pinMode(M_PWM,OUTPUT);
  digitalWrite(M_PWM,HIGH);
  // OD_ADJ - set the maximum overcurrent limit possible
  // Better option would be to use voltage divisor to set exact value
  pinMode(OC_ADJ,OUTPUT);
  digitalWrite(OC_ADJ,HIGH);
  pinMode(OC_GAIN,OUTPUT);
  digitalWrite(OC_GAIN,LOW);

  // driver config
  // power supply voltage [V]
  driver.voltage_power_supply = 12.0;
  driver.pwm_frequency = 20000; // suggested under 18khz

  driver.init();
  // link the motor and the driver
  motor.linkDriver(&driver);
  // link current sense and the driver
  cs.linkDriver(&driver);

  // align voltage
  motor.voltage_sensor_align = 0.9;
  
  // control loop type and torque mode 
  motor.torque_controller = TorqueControlType::foc_current;
  motor.controller = MotionControlType::torque;
  motor.motion_downsample = 3.0;

  // velocity loop PID
  motor.PID_velocity.P = 0.4;
  motor.PID_velocity.I = 6.0;
  motor.LPF_velocity.Tf = 0.009;
  
  // angle loop PID
  motor.P_angle.P = 20.0;
  motor.LPF_angle.Tf = 0.005;

  // current q loop PID 
  motor.PID_current_q.P = 2.0;
  motor.PID_current_q.I = 200.0;
  motor.LPF_current_q.Tf = 0.004;

  // current d loop PID
  motor.PID_current_d.P = 2.0;
  motor.PID_current_d.I = 200.0;
  motor.LPF_current_d.Tf = 0.004;

  // Limits 
  motor.velocity_limit = 2.0; 
  motor.voltage_limit = 7.0;  
  motor.current_limit = 1.5;    

  // use monitoring with serial for motor init
  // monitoring port
  Serial.begin(115200);
  motor.useMonitoring(Serial);
  motor.monitor_variables = _MON_CURR_Q | _MON_CURR_D; // monitor the two currents d and q
  motor.monitor_downsample = 0;

  // initialise motor
  motor.init();

  cs.init();
  cs.skip_align = true;
  // driver 8302 has inverted gains on all channels
  cs.gain_a *= -1;
  cs.gain_b *= -1;
  cs.gain_c *= -1;
  motor.linkCurrentSense(&cs);

  // align encoder and start FOC
  motor.initFOC();
  
  // set the inital target value
  motor.target = 0.0;

  // define the motor id
  command.add('M', onMotor, "motor");

  _delay(1000);
  
  
}

void loop() {

  motor.loopFOC();
  
  motor.move();
  
  motor.monitor();

  command.run(); 

}
2 Likes

I think you will need to use a large gearing ratio anyway, and thus the frequency of the cogging torque will be increased, in terms of cogs per revolution. I doubt you need a low cogging motor for this. I have investigated low cogging motors and was able to get some but they are not common. The Surface mount type motors( SMPMSM, surface magnet permanent magnet synchronous machine) tend to be considerably lower cogging than IP (internal plate) plate type motors, that was enough for my purposes. Gimbal motors tend to be good as they are supposed to be controlling a camera, but not all of them, and sounds like you would need a pretty big one.

1 Like

This is great, thanks so much for sharing!

Just one question: I was under the impression that 6pwm gives more control than 3pwm. Do you have a specific reason to use 3pwm? Actually, I’m not entirely sure if loopFOC really does anything different with 6pwm compared to 3pwm… maybe 6pwm only adds value if I implement my control logic using the driver directly without loopFOC?

Possibly, yes. But since I plan to use a rope to connect to the handle, I also can use the diameter of the shaft as a sort of gearing ratio. A very small shaft wrapping a rope needs many turns to move the handle a small amount. I might even actually use a couple of pulleys if I only need an additional 2:1 ratio. That would be cheaper and simpler than finding the right motor with the right gearbox. A rope also reduces the cogging effect somewhat.

Glad to help! I’m not an expert on motor control, but I think using 6 PWM gives you the ability to independently control the high and low side FETs, instead of relying on the driver chip to do so automatically. This could allow you to alter the deadtime value between FET transitions, or control only the low side ones to do things like active regeneration (instead of passive regen with the FET body diodes). I’m not sure how the loopFOC() function acts when using 6 PWM, outside of being able to control deadtime based on a user supplied value.

FWIW, 3 PWM worked well in my application which was nearly the same as yours.

Re: gearing - I dont think you’ll need it if you use a hoverboard motor and a pulley attached with the proper diameter to get the torque/speed you require.

High gear reduction means friction. In this case you want lots of friction because the user expends biological watts overcoming that friction without requiring electrical watts to directly counteract it. But not so much friction that a user cannot escape (backdrive) the system if it loses power.
Any motor you use will need to be derated to operate continuously in locked rotor condition. Wheelchair motors operate near stall frequently. They have peak output above 3000W for 10 seconds but only rated 200W continuous. They achieve this partly by having lots of thermal mass. You can better afford to carry extra mass in a stationary application. Your limiting factor will likely be preventing coil overheat. The simplest way to do that is use a motor oversized for the application which has larger mass, larger surface area for dissipation, plain bearings to transfer heat from rotor to stator housing. Probably windings of larger gauge, fewer turns, more poles.
If you know the expected duration of the exercise, you can use a motor heatsink large enough to just begin to heat soak at the end of the exercise and dissipate during the downtime.

Steppers have lots of thermal mass because they are designed for holding torque. Through a gearbox they get the torque multiplication they require for this application and their limited top speed isn’t a factor here. Provided you can find a stepper with sufficient torque it is probably the right choice. Or other high cogging motors. The cogging effect may even be beneficial to the exercise, promoting micro tears in the muscle.

Perhaps test the largest weight comfortably moved by your weakest test subject and use that to set the maximum gearbox static friction. Then test the maximum effort of your strongest test subject and use that to size the motor.

1 Like