BLDC motor for laser pointing

The project

I want to use a BLDC motor with a SimpleFOCShield+Nucleo-64 to point a laser at a precise angle.

Moving to the target angle needs to be as fast as possible. And the torque applied to the motor needs to be strong enough, so to strongly maintain the laser in position, as the device is mounted on a moving/shaking vehicle.

At a later stage, I will also need to compensate part of the vehicle angular movement using an IMU, just like in a camera gimbal, but as you’ll see below, that is the least of my concerns right now :slightly_smiling_face:

What I did until now

magnetic_sensor_spi_example, open_loop_velocity_example and single_full_control_example seem to work well.

What I’m doing now

I’m experimenting with MotionControlType::angle.

Here is my code:

#include <SimpleFOC.h>
#include "SimpleFOCDrivers.h"
#include "encoders/as5048a/MagneticSensorAS5048A.h"

//----------------------------------------

BLDCMotor motor = BLDCMotor(7);
BLDCDriver3PWM driver = BLDCDriver3PWM(9, 5, 6, 8);
MagneticSensorAS5048A sensor(10);

// inline current sensor instance
InlineCurrentSense current_sense = InlineCurrentSense(0.01, 50.0, A0, A2);

//----------------------------------------

void setup() {
  // Sensor
  sensor.init();
  motor.linkSensor(&sensor);

  // Driver
  driver.voltage_power_supply = 14.8;
  driver.init();
  motor.linkDriver(&driver);

  // Control loop type
  // INITIALLY DISABLED -> motor.torque_controller = TorqueControlType::foc_current;
  motor.controller = MotionControlType::angle;

  // Motion control limits
  motor.velocity_limit = 20;
  motor.voltage_limit = 5;

  // Current sense
  current_sense.init();
  motor.linkCurrentSense(&current_sense);

  // Initialise motor
  motor.init();

  // Align encoder and start FOC (current_sense settings below specific to SimpleFOCShield)
  current_sense.gain_b *=-1;
  current_sense.skip_align = true;  
  motor.initFOC(); 
 
  _delay(1000);
}

//----------------------------------------

float target_angle = 3.1416;
long timestamp_us = _micros();

void loop() {
  // Inverse angle each four seconds
  if(_micros() - timestamp_us > 4e6) {
      timestamp_us = _micros();
      target_angle = -target_angle;   
  }

  // Move motor
  motor.loopFOC();
  motor.move(target_angle);
}

//----------------------------------------

Initial Questions

With motor.torque_controller = TorqueControlType::voltage, I get the following behavior:

  • EDIT: Every 4s, the angle goes from -PI/2 to PI/2 (instead of -PI to PI as I expect).
  • The movement is jerky.
  • When the motor stops at its target, it remains still, silent, with no vibration.
  • The applied torque is very weak. Sometimes the motor doesn’t even move and I need to manually help it starting. EDIT: Sometimes the motor stops before reaching the -PI or PI value, unless I help it manually.

With motor.torque_controller = TorqueControlType::foc_current, I get the following behavior:

  • Every 4s, the angle goes from -PI to PI as expected.
  • The movement is smooth.
  • When the motor stops at its target, it vibrates a lot.
  • The applied torque is very strong.
  • The motor overheats.

Are these the expected behaviors? Is it all about PID tuning now?

If it is, where should I start? It seems I have four PIDs to tune (PID_current_q, PID_current_d, PID_velocity, P_angle), what tools do you guys use?

Thanks.

Hey @quentin,

We need a bit more information to help you out. What kind of motor are you using?
A lot of what you’re experiencing can be explain with bad PID tuning but it should be much easier to tune a position controller with torque control type set to voltage mode.

Thanks @David_Gonzalez.

It’s an IPower GBM2804R (in combo with a AS5048A magnetic sensor).

The advertised characteristics are:

   RPM:2149~2375
   Torque (g):250-350
   Voltage (V):10V
   Cunnrent (A):0.07
   Configuration:12N14P
   Winding Turns:0.19*1-100T
   Resistance(Ω):5.57Ω

I’ve edited my initial post above to reflect the behavior with TorqueControlType::voltage (in fact the target angle is respected, but the torque is so weak that the motor often stops before it reaches it). How do you explain the weak torque in this mode?

I added motor.phase_resistance = 5.57 and this is what I get:

  • The movement is smooth.
  • When the motor stops at its target, it starts vibrating (but not as much as with TorqueControlType::foc_current)
  • The applied torque is not very strong, but sufficient to move the motor normally.
  • The more I decrease motor.phase_resistance, the weaker the torque and the vibrations.
  • The more I increase motor.phase_resistance, the stronger the torque and the vibrations.

That begins to make sense to me. I will now change for MotionControlType::velocity and try to tune the (hence isolated) velocity PID.

@quentin

Would you mind checking this link below, it might be of help in your endeavors. Also please don’t forget that the sensor sample averaging time might also contribute to jitter feedback, in some cases amplifying the jitter instead of reducing it.

Cheers,
Valentine

Thanks @Valentine. I’ve been playing with this amazing tool all day long yesterday and came to the conclusion that PID manual tuning is an art that I might not be gifted for :slight_smile:

I should have thought about my sensor, especially because I once knew there’s a problem with my velocity computation (for reference, the thread is here). I will try tuning motor.motion_downsample.

Thank you, I am sure the developers will be happy to hear that.

@quentin, were you able to solve your problem? Do you need further help or guidance?

Cheers, Valentine

It’s much better now. I think I’ve solved my issues in TorqueControlType::voltage mode. My new settings are:

    motor.PID_velocity.P = 0.2;
    motor.PID_velocity.I = 20;
    motor.PID_velocity.D = 0.001;
    motor.PID_velocity.output_ramp = 1000;
    motor.LPF_velocity.Tf = 0.1; 

    motor.P_angle.P = 20;
    motor.P_angle.I = 0;
    motor.P_angle.D = 0;
    motor.P_angle.output_ramp = 1e6;
    motor.LPF_angle.Tf = 0.002;

So, basically, what was required was a significant increase in low-pass filtering, both for velocity and angle. I guess this puts me in the “noisy sensor” category?

I’ve now moved to TorqueControlType::dc_current. With default PID_current_q values, I get crazy vibrations and motor overheat, but changing the values gets me some improvements. My idea is to get back to MotionControlType::torque, set a very low current target and tune the PID until I get a very smooth and slow movement. Does it sound like a good strategy?

That’s great. Yes it does. May be others can give more feedback. Yes noisy sensor is a possibility.

Good luck, Valentine

1 Like

So, basically, what was required was a significant increase in low-pass filtering, both for velocity and angle.

It turns out that I was wrong. Increasing the filtering was the quick fix, but what was really needed was to tune the PIDs, which I did with SimpleFOCStudio.

For reference, here are my settings for the IPower GBM2804R:

//----- SimpleFOCStudio Generated Code -----
	// velocity loop PID
	motor.PID_velocity.P = 0.05;
	motor.PID_velocity.I = 1.0;
	motor.PID_velocity.D = 0.0;
	motor.PID_velocity.output_ramp = 1000.0;
	motor.PID_velocity.limit = 0.2;
	// Low pass filtering time constant
	motor.LPF_velocity.Tf = 0.01;
	// angle loop PID
	motor.P_angle.P = 20.0;
	motor.P_angle.I = 0.0;
	motor.P_angle.D = 0.0;
	motor.P_angle.output_ramp = 0.0;
	motor.P_angle.limit = 50.0;
	// Low pass filtering time constant
	motor.LPF_angle.Tf = 0.0;
	// current q loop PID
	motor.PID_current_q.P = 3.0;
	motor.PID_current_q.I = 100.0;
	motor.PID_current_q.D = 0.001;
	motor.PID_current_q.output_ramp = 0.0;
	motor.PID_current_q.limit = 5.0;
	// Low pass filtering time constant
	motor.LPF_current_q.Tf = 0.005;
	// current d loop PID
	motor.PID_current_d.P = 3.0;
	motor.PID_current_d.I = 0;
	motor.PID_current_d.D = 0;
	motor.PID_current_d.output_ramp = 0.0;
	motor.PID_current_d.limit = 5.0;
	// Low pass filtering time constant
	motor.LPF_current_d.Tf = 0.005;
2 Likes