Experience with high-resistance gimbal motors for smooth slow closed-loop motion?

Hi everyone,

I’m working on a motorised aircraft throttle quadrant and I’m using an iFlight GM6208 gimbal motor with an AS5048A magnetic encoder and SimpleFOC. I’m currently using the SimpleFOC Mini v1.0 driver.

The GM6208 is a relatively high-resistance gimbal motor (around 32 Ω phase resistance), and I’m trying to get very smooth, slow movement in closed-loop position control while the motor tracks the physical throttle lever position.

At normal/faster movement the motor behaves reasonably well, but as the commanded movement becomes very slow, I get increasingly noticeable stop/start or “cogging/stiction”-like behaviour. This is particularly noticeable when trying to make the throttle follow small, smooth changes.

I’m currently using voltage-based control, without inline phase current sensing.

I’m curious if anyone here has experience using high-resistance gimbal motors for this kind of very slow, smooth closed-loop motion, and whether there are particular SimpleFOC settings or control approaches that work well.

I’m also considering adding inline current sensing (INA240 + shunt) and would be interested to know whether people think proper current/torque control is likely to make a significant difference with a motor like this, or whether I should instead be looking at a different motor/driver combination.

The goal isn’t high speed or high torque — it’s really smooth, controllable, low-speed movement with a good “throttle lever” feel.

Any experience or suggestions would be very welcome!

Edit: I also came across an older velocity_ultra_slow control mode in the SimpleFOC v1.0 documentation. It was specifically designed for very low speeds (<0.1 rad/s) and works by integrating the desired velocity into an angle target and then using a high-gain angle controller, rather than relying on the normal velocity calculation. This seems quite relevant to what I’m trying to achieve, but I don’t see this controller in the current SimpleFOC 2.x version I’m using (askuric/Simple FOC@^2.4.0). Is there a recommended equivalent approach in the current version?

13 Answers

13

That mode was equivalent to using angle mode and incrementing the target the way velocity_openloop does it. So you can still do the same thing manually, but in your case it doesn’t sound like you need the incrementing behavior. Just set target angle directly.

You could try angle_nocascade. I use it on my CNC machine in voltage control mode for fastest possible response, since the maximum speed is not high enough to need current control. Your high resistance motor is probably the same way. It needs much higher PID gains than regular angle mode. I use P_angle.P=75, I=10000, D=0

An experimental feature you could try is deadtime compensation. It improves linearity of response at low voltage, and greatly smooths out open loop motion, which is normally sort of steppy. It’s not perfect though, and usually results in a sort of “tick” at each zero crossing in open loop which you can feel if holding the motor in your hand or hear if it’s sitting on cardboard or something that can amplify it.

Velocity control is used for things like wheeled vehicles, drone propellers, machine tool spindles, where the total amount of movement is not critical (sometimes spindle movement is critical, like doing coordinated motion for threading, and then angle mode is used).

The regular angle mode with cascaded velocity works better with inertial loads than angle_nocascade. The angle PID outputs a target velocity proportional to the distance from the target, so velocity decreases as you approach.

Nocascade outputs a target torque proportional to distance from target, so it will continue accelerating until it goes past, and overshoot. Theoretically the D parameter can counteract that, but I’ve never been able to get any good out of it. Even very small values like 0.1 compared to I=10000 cause weird noises and instability.

But if you’re streaming target positions and expecting it to follow closely, the overshoot is always very small so the faster response can give better overall results.

It’s a relief to know I’m not the only one who doesn’t get any good out of those things :slight_smile: Sure you can see what they’re getting at once you already know how it works, but in that case there’s no need for a diagram anyway.

Interesting idea. I don’t think it would prevent overshoot any better, but could improve response time. Energy is proportional to velocity squared, so you could keep up higher speed until the last second where it doesn’t take much energy to cause a large change in speed. I think velocity_sp = sqrt(P_angle(target - shaft_angle)); would do it.

Depends on how fast your MCU is. SimpleFOC’s control loop is much more complicated than sensorless ESCs, but my CNC steppers running nocascade voltage mode on STM32G431 are able to do cycle-by-cycle control at 25KHz, thanks to a bit of code to eliminate the wait time on reading the sensor.

void SPIAngleStart() {
  HAL_SPI_Init(SPI.getHandle());
  digitalWrite(SPI1_NSS, 0);
  __HAL_SPI_ENABLE(SPI.getHandle());
#if (I2C_ADDR == I2C_ADDR_X)
  u8 data[2] = { AS5047_ANGLECOM_REG&0xff, (AS5047_ANGLECOM_REG|AS5047_PARITY|AS5047_RW)>>8 };
#elif (I2C_ADDR == I2C_ADDR_Y)
  u8 data[4] = { (MT6835_OP_ANGLE<<4), MT6835_REG_ANGLE1, 0, 0 };
#elif (I2C_ADDR == I2C_ADDR_Z)
  u8 data[2] = { AS5048A_ANGLE_REG&0xff, (AS5048A_ANGLE_REG|AS5048A_PARITY|AS5048A_RW)>>8 };
#endif
  for(int i = 0; i < (I2C_ADDR==I2C_ADDR_Y?4:2); i++) *(volatile u8*)&SPI1->DR = data[i];
}

void SPIAngleFinish() {
  u8 data[4];
  for(int i = 0; i < (I2C_ADDR==I2C_ADDR_Y?4:2); i++) { while(!(SPI1->SR&SPI_SR_RXNE)); data[i] = *(volatile u8*)&SPI1->DR; }
#if (I2C_ADDR == I2C_ADDR_X)
  spiAngle = ((((u16)data[0]<<8)|data[1]) & AS5047_RESULT_MASK) << 2;
#elif (I2C_ADDR == I2C_ADDR_Y)
  spiAngle = ((u16)data[2]<<8)|data[3];
#elif (I2C_ADDR == I2C_ADDR_Z)
  spiAngle = ((((u16)data[0]<<8)|data[1]) & AS5048A_RESULT_MASK) << 2;
#endif
  digitalWrite(SPI1_NSS, 1);
}

Oh, and hall current sensors work fine if you use a lot of oversampling. But requires custom ADC code to do it. What microcontroller are you using?

There have been a few threads discussing it in the past, with links to papers describing fancy algorithms, but nothing ever came of it. From my testing with deadtime compensation, cogging is typically much less significant than I used to think. The typical steppy motion in open loop is not due to the cogging you feel when turning an un-powered motor, but rather an expression of deadtime causing the effective voltage between phases to be near-zero up until it reaches voltage_power_supply*deadtime% and then begins rising as commanded. With compensation, it doesn’t take much voltage to overcome the cogging torque and move smoothly, aside from the ticking I mentioned before, which happens when instantaneously changing from one compensation direction to the next. It would be worth experimenting with ways to smooth that out. Might have to add a very small dead zone where the compensation is ramped out and then ramped back in at the handoff between two phases.

Deadtime compensation falls in the same category: prediction is better than reaction. Especially when the reaction is based on filtered, oversampled, cascaded or otherwise lagging parameters

I have recently been FOCing around with a 10R gimbal motor with a similar aim to you; smooth low speed motion. I too do not have useful current sensors (the SimpleFOC shield I have uses very noisy Hall current sensors alas).

At low speed theory suggests back EMF will be tiny so voltage control for the inner torque loop should be fine.

I would have a look at this example because although it lacks smoothing/low pass filtering, it allows very accurate angle control: Custom control | Arduino-FOC
Like the comment above, I have my motor affixed to cardboard and you can hear the noise from the normal angle control mode. With angle_nocascade, I found it easier to get rid of the noise. It doesn’t make sense to have a velocity control loop getting in the way unless you actually need to limit velocity.

Side note: I have never understood why you would need accurate velocity control of a motor - the much more common scenario is surely angular control?

That makes it so much clearer than any control loop diagram I have seen for the angle control mode. It makes me think that yet another control mode where the rotational energy of the motor is set so that it is proportional to the angular distance, instead of setting the angular velocity, might be an even better way to control the motor and prevent overshoot.

SimpleFOC is mainly meant for gimbal motors that don’t spin too fast though isn’t it? I feel drone propellers might be too fast.

ESP32S3

It does 8kHz with one 3PWM BLDC motor with a loop that just calls motor.loopFOC(), motor.move() and command.run(). I cannot recall if it was nocascade or normal angle mode.

Thanks everyone for the replies so far — this has already given me a much better understanding of the different control approaches.

What I’m still missing, though, is a bit of practical guidance on how to actually determine whether I can get the GM6208 to behave the way I need.

I’ve experimented with SimpleFOC before, including SimpleFOCStudio, but I’m not really sure what I should be tuning or what measurements/observations I should be looking for. I can get the motor to move reasonably well at higher speeds, but at very slow movement I get the stick/slip or cogging-like behaviour I’ve described.

Would someone be able to suggest a concrete step-by-step test procedure that I could follow with this motor?

For example, something along the lines of:

  1. Start with these motor/controller settings.

  2. Use angle or angle_nocascade.

  3. Set/tune these particular parameters first.

  4. Test a target moving at a specific low speed/range.

  5. Observe these particular things in SimpleFOCStudio.

  6. Change parameter X and see whether behaviour Y improves.

  7. Repeat until we can determine whether the limitation is the controller, the motor/driver, or something else.

I’m particularly interested in finding the minimum speed at which the GM6208 can move smoothly, and whether there is a sensible way to tune the controller specifically for this application.

I’m also happy to provide any measurements/logs from the motor if someone can tell me what would be useful to measure.

At this stage I’m not necessarily looking for a final solution — I’d mainly like to establish whether this motor + SimpleFOC Mini v1.0 + AS5048A can realistically achieve the smooth, very slow closed-loop movement I need, and if so, what the best path to get there is.

One other useful data point: I have actually managed to get the motor to track the MSFS2024 throttle smoothly at very slow speeds using a hybrid open-loop approach. The movement can be quite smooth in that configuration, so I’m reasonably confident the motor is physically capable of the slow movement I want. However, the open-loop approach has other problems, particularly when manually pushing against the lever, so I would really like to achieve the same smoothness with proper closed-loop control.

Thanks again!

It should be doable, no minimum speed. The typical parameters to tune for regular angle mode are PID_Velocity.P and I, P_Angle.P, and sometimes LPF_Velocity.Tf and sensor.min_elapsed_time if you’re getting noisy velocity readings. For angle_nocascade it’s just P_Angle.P and I.

Like I said before nocascade tends to work well for following a constant stream of positions, which it sounds like is what you have, so try that first since there are fewer parameters interacting.

My guess is you need more I, so it builds up quickly in response to small error. Like I said before, I had to crank it to 10k on nocascade to get fast enough response on my CNC. I haven’t tried commanding it to go to an angle far away from the current position, but I wouldn’t expect it to work. Probably instantly apply full voltage, overshoot, and oscillate around the target for a while.

My testing procedure is to plot target-shaft_angle and jiggle the motor by hand to see how far off it gets before responding, and then command it to move far enough that it will reach full speed and then decelerate (trajectory generated by GRBL, which believes it’s controlling a regular stepper motor), and see if it makes any weird noises or other signs of instability.

Just want to throw another thought in the ring:
The main reason why we have to deal with the reactive PID error control is motor cogging.
If I understand correctly, the FOC algo doesn’t have a way to predict torque-demand to overcome motor cogging smoothly?
But it would be pretty easy to implement once we have some torque-numbers that represent the climb- phase versus the descending phase.
We add those numbers ( pos and neg torque) to the FOC output et voilá smoother motion at any speed

Thanks everyone for all the suggestions, feedback and help!

After quite a bit of experimenting, I think I’ve finally got something that looks like it might work for my application. I’ve ended up using angle_nocascade with some filtering and carefully tuned the controller, and the results so far are very promising. The motor now tracks the commanded throttle position smoothly while still allowing a reasonably natural manual override.

I’m still working on the mechanical side of the throttle quadrant and haven’t tested it under the final load conditions yet, so there’s obviously more work to do, but I’m feeling quite positive about it now.

Thanks again to everyone who took the time to offer suggestions and point me in the right direction. It was very helpful!

Hi,

I have the very same setup (GM6208 + AS5048A). I experience the same problem, so I had to fine tune the PID to have an “acceptable” motion. I have reduced the “stepping” behavior quite a lot, but I am still not happy with the result. I modeled things so that the goal was for the motor to get to a certain angle in a given time, so it was receiving micro setpoints at 1KHz ( I tried with some feedforward, but I could not see any difference). FOC is running at 10KHz and the PWM modulation is 25KHz. The gains depend on the load (hence probably it is not useful but in any case my gains are K_position_P=12, K_velocity_p=10 K_velocity_integral=6.0), but can you please share your control parameters and the filtering you were mentioning? Thanks

Hi,

Thanks for sharing your experience. It’s interesting to hear that you’re seeing similar behaviour with the GM6208 + AS5048A combination.

I’m still very much in the testing/tuning phase, so I definitely wouldn’t consider my current settings a final solution. I’ve managed to get the behaviour to a point that looks promising for my particular application, but there is still some stepping/oscillation that I’m investigating.

I’m currently using SimpleFOC’s angle_nocascade controller. My latest settings are:

motor.P_angle.P = 150.0;
motor.P_angle.I = 0.0;
motor.P_angle.D = 1.2;
motor.P_angle.output_ramp = 0.0;
motor.P_angle.limit = 1.5;

motor.LPF_angle.Tf = 0.025;

motor.velocity_limit = 1.0;
motor.voltage_limit = 2.0;
motor.current_limit = 1.0;

I’m also using a phase resistance of approximately 32 ohms for the GM6208.

At the moment I don’t have current sensing implemented, so this is still voltage-based FOC rather than a proper closed-loop current-controlled setup.

On the host side, the application receives the MSFS2024 throttle position through SimConnect.NET at approximately 60 Hz. I currently apply a target LPF before sending the target to the ESP32, with:

TARGET_LPF_TF = 0.125f;

This is separate from the LPF_angle setting above — the former is filtering the incoming motion target on the host side, while the latter is part of the SimpleFOC angle controller.

Interestingly, the results are quite sensitive to the target movement and the mechanical load. Very slow movements can be quite smooth, while faster movements can still produce some stepping/oscillation. I’m currently investigating this further, so I wouldn’t want to suggest that these values are necessarily optimal for the GM6208.

My application is also somewhat different from a conventional position-control system. I’m developing a motorised flight-simulator throttle quadrant, where the motor needs to follow a relatively slowly changing simulator target, but the user must also be able to physically override the motor and move the lever by hand. The manual override behaviour is currently working quite nicely, which is encouraging.

I’m also planning to add proper current sensing later, so I expect there may be considerably more that can be improved once I have a proper current-control loop available.

Your approach of generating micro setpoints at 1 kHz is interesting. I haven’t tried that approach yet, so I’d be very interested to hear how it works for you.

Thanks again for sharing your parameters and experience. It’s reassuring to know that someone else using the same motor/encoder combination is seeing similar behaviour!