Hoverboard motor vibrates but doesn't rotate

What do you mean, exactly? The maximum speed will depend on:

  • system voltage and voltage limit
  • motor KV rating
  • motor load
  • optimal commutation (here is where the MCU and software come into it)

So you can do some different things:

  • you can run the motor in torque-voltage mode. In this mode, the motor should assume its natural maximum speed given the load, the voltage set to the motor, the BEMF and, depending on the exact setup, the limit imposed by the MCU.

  • if you have current sensing you can run the motor in torque-foc-current mode. If you set a constant target current, then the motor should continue to accellerate until it reaches maximum speed attainable for the voltage, regardless of BEMF, but still subject to the MCU limits, if any.

  • you can also try without current sensing, by setting the motor resistance and KV rating you should be able to do the same as above using estimated currents.

To actually measure the speed, you need to use some kind of position sensor - hall sensors are one option, but you can also. use encoders, magnetic sensors and maybe others.

In open-loop mode, you can print the calculated speed, but it is not guaranteed the motor is actually following the commutation.

In the first sentence, I just need to install the application when running the following code, the speed will increase to the maximum, right?

/**
*

  • Torque control example using voltage control loop.
  • Most of the low-end BLDC driver boards doesn’t have current measurement therefore SimpleFOC offers
  • you a way to control motor torque by setting the voltage to the motor instead of the current.
  • This makes the BLDC motor effectively a DC motor, and you can use it in a same way.
    */
    // for creating a software interrupt pin
    #include <SimpleFOC.h>

// set up pins on arduino board
#define HallA 15 // yellow
#define HallB 16 // blue
#define HallC 17 // green

//InH PWM pins
#define INHA 9
#define INHB 10
#define INHC 11
//miscellaneous pins
#define En_Gate 35 //Enable gate driver and current shunt amplifiers.
#define OC_ADJ 38 //analog input adjusting the over-current limit - if you don’t care you can put it to high
#define M_OC 37 //Mode selection pin for over-current protection options. LOW = cycle by cycle. HIGH = overcurrent shutdown
#define M_PWM 36 //Mode selection pin for PWM input configuration. LOW = 6PWM. HIGH = 3PWM

// polar pairs
int pp = 15; // motor has 30 outer magnets
//phase resistance value of the motor A B and C wires.
//float RPhase = 1.4;

// BLDC motor & driver instance
// BLDCMotor motor = BLDCMotor(pole pair number);
BLDCMotor motor = BLDCMotor(pp);
// BLDCDriver3PWM driver = BLDCDriver3PWM(pwmA, pwmB, pwmC, Enable(optional));
BLDCDriver3PWM driver = BLDCDriver3PWM(INHA, INHB, INHC, En_Gate);

// hall sensor instance
HallSensor sensor = HallSensor(HallA, HallB, HallC, pp);

// Interrupt routine intialisation
// channel A and B callbacks
void doA(){
sensor.handleA();
}
void doB(){
sensor.handleB();
}
void doC(){
sensor.handleC();
}

// voltage set point variable
float target_voltage = 1;
// instantiate the commander
Commander command = Commander(Serial);
void doTarget(char* cmd) { command.scalar(&target_voltage, cmd); }

void setup() {

// initialize Hall sensor hardware
sensor.pullup = Pullup::USE_EXTERN;
sensor.velocity_max = 1000; // 1000 rad/s is around 10,000 rpm
//simplefoc activate sensor
sensor.init();
sensor.enableInterrupts(doA, doB, doC);
//extra step for HallC
// link the motor to the sensor
motor.linkSensor(&sensor);

// driver config
//DRV802 driver specific code
//3 or 6 PWM put on high for 3 pwm driver
pinMode(M_PWM, OUTPUT);
digitalWrite(M_PWM, HIGH);
//over current protection
pinMode(M_OC, OUTPUT);
digitalWrite(M_OC, LOW);
pinMode(OC_ADJ, OUTPUT); //adjuster voltage limit for M_OC
digitalWrite(OC_ADJ, HIGH); // for now put on high to give maximum over current limit but better to connect it with a pot meter.

driver.voltage_power_supply = 36; // power supply voltage [V]
driver.init(); //start driver
motor.linkDriver(&driver); // link driver
motor.voltage_limit = 5; //voltage limit
motor.voltage_sensor_align = 1; // aligning voltage limit

//motor mode selection
// choose FOC modulation (optional)
motor.foc_modulation = FOCModulationType::SpaceVectorPWM;
//set torque mode:
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);
motor.monitor_variables = _MON_TARGET | _MON_VEL | _MON_ANGLE;
// downsampling
motor.monitor_downsample = 10; // default 10

// initialize motor
motor.init();
// align sensor and start FOC
motor.initFOC();

// add target command T
command.add(‘T’, doTarget, “target_voltage”);

Serial.println(F(“Motor ready.”));
Serial.println(F(“Set the target voltage using serial terminal:”));
_delay(1000);
}

void loop() {

// main FOC algorithm function
// the faster you run this function the better
// Arduino UNO loop ~1kHz
// Bluepill loop ~10kHz
motor.loopFOC();

// Motion control function
// velocity, position or voltage (defined in motor.controller)
// this function can be run at much lower frequency than loopFOC() function
// You can also use motor.move() and set the motor.target in the code
motor.move(target_voltage);
motor.monitor();
// user communication
command.run();
}

What @runger said is correct, but I often just set an impossible speed request in velocity mode.
Then add this line in the SerialComm() routine and you can read the real motor speed in rad/s (it’s NOT an engine :slight_smile: )

case 's': Serial.print("shaft_velocity "); Serial.println(motor.shaft_velocity); break;

PS: If you need RoundsPerMinute (RPM) speed, multiply rads/s with 9.55

yes. But it will be the maximum speed of your setup for the given target voltage. That does not always mean it is the maximum speed of the motor, since your setup may be limiting it.

If you use velocity mode, and command a very big velocity, then the velocity PID should basically output the maximum voltage (as allowed by the limits). So the results should be the same as if you do the torque-voltage test using the maximum voltage value.

  1. Thank you, I used mode torque and checked the maximum speed of the motor.
  2. But there is a problem that I don’t understand: I used the code to calibrate the PID yesterday, the motor ran very smoothly, but today when I run it again, it jerks, the speed seems unstable, Sometimes the engine jerks, the speed chart has a value very far from the set value. I don’t know what the cause is.

Hi, that’s strange - something must be different in the setup between last night and today.

One possibility is the calibration value found during the motor calibration. If last night motor was unloaded during calibration, and today you have some load, for example?

I have the motor in the lab so tomorrow I will send you the velocity graph I have. I think the PID values may not be correct

I don’t know how to properly describe the problem I’m having. Specifically, the motor does not rotate smoothly, it jerks like when it rotates for a distance it stops very quickly and then rotates again causing this phenomenon to occur.

Unfortunately it can be very hard to tune the PID for velocity control with HallSensors. Especially low speeds are unlikely to work well.

The response of the HallSensor is very low resolution, so the position changes like a step-function. This means the velocity signal will not be a very smooth signal at all.

It’s unlikely that you’ll get fine control at slow speeds. With careful tuning, high values for the LPF filter and motion downsample parameter you might have more luck in holding speeds more steadily, but the system will not be very reactive to load changes.

With SmoothingSensor from the dev branch, I was able to run a hoverboard motor at 2-3 rad/s and it could hold the speed under different load conditions.
It took several days of tuning and tweaking (also phase resistance and kV were part of it).

Then I found out it would only be stable with a fixed (PSU) voltage. A battery powered controller needs even more tuning and extra code to deal with voltage drops under load and the overall voltage drop from a semi-discharged battery.

It would be helpful to have a battery model in sFOC, were we can set noLoad_voltage and Ri of the battery to compensate voltage drops. (based on measured or calculated DC_current)
Batt_Capacity would be a nice_to_have, but it would also work, if we check the momentary batt voltage every now and then.

I don’t know what others think, but personally I wouldn’t touch that…

If we ask users to characterise the impedance of their PSUs before SimpleFOC will work right, then I think we won’t be very simple.

Users that want this can easily add it and just adjust driver.voltage_power_supply dynamically.
And other users can just get a few extra battery packs and wire them in parallel…

1 Like
  1. Is there any way that when running FOC for a BLDC motor, it will self-align to find the appropriate PID parameters for the motor? Or do I think I have to simulate on matlab to find the appropriate PID set?
  2. Regarding the voltage issue, I am currently using a battery taken from the balance bike. When fully charged, its voltage is about 40V and I use a circuit to reduce the voltage to 36V, so when used for a period of time, the voltage may not change. but the current has been changed

I’ll try changing the LDF filter to see if it makes any difference

Write some code to check if you’ve gotten a large number of hall sensor interrupts since the previous update. I was having trouble with that yesterday when trying 41F hall sensors instead of U1881 for the first time, in the hopes of getting out of soldering a bunch of tiny capacitors every time (41F’s datasheet doesn’t say anything about needing capacitors).

In my case with a little 2204 motor, it would jerk to a stop and freeze the program until I moved it by hand, but a hoverboard motor has enough inertia it would probably only jerk and continue. I added some 4.7nF capacitors between the hall outputs and ground and haven’t gotten any more freezes. Still not running smoothly, but that’s probably some other problem. I’ll continue diagnosing tomorrow.

Some people on the ODrive forum recommend 22nF, but I don’t think it needs to be too specific. Just something to prevent it oscillating when near the transition points.

EDIT: Looks like 4.7nF is not enough. I’m still getting multiple triggerings of the same hall sensor interrupt in a single frame. My printout looks like this:

0, 0, 0
0, 0, 0
0, 8, 0
0, 0, 0
0, 0, 0
12, 0, 0
0, 0, 0
0, 0, 0
0, 0, 9
0, 0, 0
0, 0, 0
0, 0, 0
0, 1, 0
0, 0, 0

Each row is one frame (call to loopFOC and motor.move), the columns are hall sensors A, B, C, and the number is how many times that sensor’s interrupt triggered that frame. Main loop executes in about 400 microseconds, and spinning at 100rad/s is around 670 hall transitions per second, so 3-4 frames per hall transition looks correct. But sometimes the sensor gives multiple interrupts. Not very many in this case, but at higher speed I saw some over 100, which causes enough time delay to feel vibration when holding the motor.

PID-autotuning might be on the roadmap of simpleFOC development, but don’t hold your breath and wait for it to happen soon.

Matlab will only give you a theoretic answer which will not hold up in real live, unless you can provide a 100% realistic motor model.

If your 36V buck converter is strong and fast enough to deal with changing load conditions, you don’t have to worry, but keep in mind that motor current can reach peaks 2-3 times the rated current.

This is the speed symbol I got when I set target = 6. It’s pretty bad.

and here:

I think that’s just normal low-speed cogging behavior for a hall sensor motor. Try 20-30 rad/s and see if it smooths out.

And to follow up on my previous post, you probably shouldn’t add capacitors after all.

Detailed summary for those interested: I tried adding 10nF to one sensor (giving a total 14.7nF) and couldn’t tell any difference, so then I tried 100nF and that made it much worse. The interrupt is delayed by a millisecond or two, and retriggers several hundred times over the next millisecond.

I removed the capacitors entirely and tried again, and it almost always gives one interrupt per transition right up until it freezes the CPU with infinite retriggering, which seems to be related to driving current. So I think it’s true after all that 41F sensors should not have capacitors, and my problem is due to induction between the motor wires and sensor wires, which are all bundled together in a heat shrink tube. The motor wires are braided together, which I had hoped would prevent such problems, but probably the distance is just too close. The 4.7nF capacitors apparently filter it out for the most part, but not entirely. And they cause around 10 interrupt retriggers after each transition, which is not enough to cause significant slowdown but still undesirable.

Out of curiosity, I checked my older motors with U1881 sensors, which have two capacitors soldered to the leads very close to the sensor as specified in the datasheet (100nF between VCC and GND and 4.7nF between output and GND). They mostly give one interrupt per hall transition, but sometimes two or three.

Another interesting note is that in all cases (U1881, 41F without capacitors, and 41F with 4.7nF capacitors), the retriggering interrupts are usually not oscillating after all, but retriggering with the same pin value. The SimpleFOC hall sensor class checks for that (the “glitch avoidance #1” in updateState), so it doesn’t really matter, but I still thought I’d mention it.

thank you for your architecture i will continue testing