KT motor controllers -- Flexible OpenSource firmware for BMSBattery S/Kunteng KT motor controllers (0.25kW up to 5kW)

nieles said:
if the current code doesn't fit, then we might want to rethink this whole idea and go for an external controller. or do you also already have code for 6 step commutation?
I do have for 6 steps commutation code, it is pretty basic! I did that about 10 months ago (26 May 2017):



So basic the base code for 6 steps:
Code:
void hall_sensors_read_and_action (void)
{
  unsigned char hall_sensors = 0;

  // read hall sensors signal pins and mask other pins
  hall_sensors = (GPIO_ReadInputData (HALL_SENSORS__PORT) & (HALL_SENSORS_MASK));

  switch (hall_sensors)
  {
    case 3:
      pwm_phase_a_enable_pwm ();
      pwm_phase_b_disable ();
      pwm_phase_c_enable_low ();

    break;

    case 1:
      pwm_phase_a_enable_pwm ();
      pwm_phase_b_enable_low ();
      pwm_phase_c_disable ();
    break;

    case 5:
      pwm_phase_a_disable ();
      pwm_phase_b_enable_low ();
      pwm_phase_c_enable_pwm ();
    break;

    case 4:
      pwm_phase_a_enable_low ();
      pwm_phase_b_disable ();
      pwm_phase_c_enable_pwm ();
    break;

    case 6:
      pwm_phase_a_enable_low ();
      pwm_phase_b_enable_pwm ();
      pwm_phase_c_disable ();
    break;

    case 2:
      pwm_phase_a_disable ();
      pwm_phase_b_enable_pwm ();
      pwm_phase_c_enable_low ();
    break;

    default:
    return;
    break;
  }
}
 
Measured with oscilloscope that ebike_app_controller() that is called every 100ms on main loop, where battery current/motor torque controller and wheel speed controller runs, takes about 13ms!!
This means we can increase the frequency of the controllers (as Stancecoke suggested before), at least I think the battery current/motor torque controller may be need to run faster, as maybe the oscillations I feel on the direct drive motor may happen because of slow current control:

Code:
void ebike_app_controller (void)
{
  // reads battery voltage and also protects for undervoltage
  read_battery_voltage ();

  // calc battery current filtered and save the value on global variable i16_battery_current_filtered
  calc_battery_current_filtered ();

  // calc wheel speed and save the value on global variable ui8_wheel_speed
  calc_wheel_speed ();

  // NOTE: PAS reading must be done before torque sensor reading!
  // read PAS cadence to global variable: ui8_pas_cadence_rps
  read_pas_cadence_and_direction ();

#if (EBIKE_THROTTLE_TYPE == EBIKE_THROTTLE_TYPE_THROTTLE_PAS)
  // map throttle value from 0 up to 255 to global variable: ui8_throttle_value
  // setup ui8_is_throttle_released flag
  read_throttle ();
#elif (EBIKE_THROTTLE_TYPE == EBIKE_THROTTLE_TYPE_TORQUE_SENSOR)
  read_torque_sensor_throttle ();
#else
#error
#endif

  // control the motor using specific algorithm
#if (EBIKE_THROTTLE_TYPE == EBIKE_THROTTLE_TYPE_THROTTLE_PAS)
  ebike_throttle_type_throttle_pas ();
#elif (EBIKE_THROTTLE_TYPE == EBIKE_THROTTLE_TYPE_TORQUE_SENSOR)
  ebike_throttle_type_torque_sensor ();
#else
#error
#endif

  // execute the state machine after previous getting data from inputs like wheel speed, throttle, etc
  ebike_app_state_machine ();

  // send and received information to/from the LCD as also setup the configuration variables
  communications_controller ();
}
 
I am enjoying the sun and riding my ebike with S06S controller running our firmware, Q85 motor and 24V battery. Torque sensor only and it is a great feeling :)

I need to understand why my direct drive motor make vibrations while accelerating. I want to debug while riding and so I will putting together the resources needed to record serial port data on an Android bluetooth app, that after I will visualize on PC. I did follow the same steps as Stancecoke, I used a module that can convert 48V up to 24V battery voltage to 5V for powering the bluetooth UART module (got this boards from another old project):

 
honya96 said:
DC-DC converter
https://rover.ebay.com/rover/0/0/0?mpre=https%3A%2F%2Fwww.ebay.com%2Fulk%2Fitm%2F112634082572

18fet 100v mod
https://drive.google.com/file/d/1ZYZQLLNsMb31ZCvtUd0TWQtc2Stz3m5U/view?usp=drivesdk

wow nice!
I guess i will upgrade my 18S bike from good old 12fet infineon (running at 75A!) to this controller

can you provide some more information?
  • Whats the base version you used,18fet 72V?
  • Where did you buy it?
  • Did you beef up the current?
  • Was a firmware change required?
  • Does the DC/DC fit into the housing?
  • Current Sensor is ACS75B? So this is different from the 6FET version?
Thanks
 
casainho said:
Measured with oscilloscope that ebike_app_controller() that is called every 100ms on main loop, where battery current/motor torque controller and wheel speed controller runs, takes only about 1.5ms!!
This means we can increase the frequency of the controllers (as Stancecoke suggested before), at least I think the battery current/motor torque controller may be need to run faster, as maybe the oscillations I feel on the direct drive motor may happen because of slow current control:

Code:
void ebike_app_controller (void)
{
  // reads battery voltage and also protects for undervoltage
  read_battery_voltage ();

  // calc battery current filtered and save the value on global variable i16_battery_current_filtered
  calc_battery_current_filtered ();

  // calc wheel speed and save the value on global variable ui8_wheel_speed
  calc_wheel_speed ();

  // NOTE: PAS reading must be done before torque sensor reading!
  // read PAS cadence to global variable: ui8_pas_cadence_rps
  read_pas_cadence_and_direction ();

#if (EBIKE_THROTTLE_TYPE == EBIKE_THROTTLE_TYPE_THROTTLE_PAS)
  // map throttle value from 0 up to 255 to global variable: ui8_throttle_value
  // setup ui8_is_throttle_released flag
  read_throttle ();
#elif (EBIKE_THROTTLE_TYPE == EBIKE_THROTTLE_TYPE_TORQUE_SENSOR)
  read_torque_sensor_throttle ();
#else
#error
#endif

  // control the motor using specific algorithm
#if (EBIKE_THROTTLE_TYPE == EBIKE_THROTTLE_TYPE_THROTTLE_PAS)
  ebike_throttle_type_throttle_pas ();
#elif (EBIKE_THROTTLE_TYPE == EBIKE_THROTTLE_TYPE_TORQUE_SENSOR)
  ebike_throttle_type_torque_sensor ();
#else
#error
#endif

  // execute the state machine after previous getting data from inputs like wheel speed, throttle, etc
  ebike_app_state_machine ();

  // send and received information to/from the LCD as also setup the configuration variables
  communications_controller ();
}

My mistake because I had sending UART bytes code commented. The time measured time for ebike_app_controller() is 13ms. Maybe I will try to run it every 20ms (to save some free time for any future additions) instead of current 100ms, so I will increase 5x the frequency of current/torque controller, etc.
 
dr_lulz said:
can you provide some more information?

Its modded from 48 to "100v" (which means 20s batt.) higher voltage than that is not better. As I said before.

Dc-dc fits
You dont have to worry about current sensor.
Its "beefed" 8mm2 copper

That 18fet is not working yet.

It's not as easy as it may look. I suggest buying 72V 18fet and do only beef up and FW and run 80A. Even that is hard enough to run reliably (FW)..... but worth it.

Just start the easy way..
 
Just saw my first logs with a realtime ridding:

 
honya96 said:
dr_lulz said:
can you provide some more information?

Its modded from 48 to "100v" (which means 20s batt.) higher voltage than that is not better. As I said before.

Dc-dc fits
You dont have to worry about current sensor.
Its "beefed" 8mm2 copper

That 18fet is not working yet.

It's not as easy as it may look. I suggest buying 72V 18fet and do only beef up and FW and run 80A. Even that is hard enough to run reliably (FW)..... but worth it.

Just start the easy way..

Ok why Is the 18fet Not Working?

Do you mean the firmware is not mature enough yet to run on a real bike?

Maybe its better to keep my 12fet infineon?
 
dr_lulz said:
Ok why Is the 18fet Not Working?

Do you mean the firmware is not mature enough yet to run on a real bike?

Maybe its better to keep my 12fet infineon?
Some problem after changing mosfets and other things.. but I expect easy fix. They worked on 12 fet.

Its not just flash and run yet, but expected to be before summer? I think..
For now you need to spend XX hours and its not gonna be 100% ok.

Keep it, but try this also. Its not expensive.
 
casainho said:
I want to debug while riding and so I will putting together the resources needed to record serial port data on an Android bluetooth app, that after I will visualize on PC

Great to see the BT-logging working :)

I still don't understand the difference between your wording battery_current and motor_current. battery_current is the filtered current at the shunt. That's OK. motor_current is the unfiltered current at the shunt measured directly after the fast loop timer interrupt. Do you have an oszillograph of the unfiltered current within one PWM period and mark the time the adc value for motor_current is taken,

regards
stancecoke
 
stancecoke said:
battery_current is the filtered current at the shunt. That's OK.
I believe that we cant measure real (stable) battery current, that shunt is connected after capacitors so then its "phase_current_combined"?

Real battery current should be the calculated value from phase and duty cycle as I understand?

Just my thoughts......
 
stancecoke said:
I still don't understand the difference between your wording battery_current and motor_current. battery_current is the filtered current at the shunt. That's OK. motor_current is the unfiltered current at the shunt measured directly after the fast loop timer interrupt. Do you have an oszillograph of the unfiltered current within one PWM period and mark the time the adc value for motor_current is taken,
For linear torque control, it is important that we sample the average phase current as
feedback to the current regulator. It is best to get this information from the DC link current
using only a shunt resistor because of its low cost and simplicity. However, the DC link
current is not continuous and is present only during PWM on time.~
(...)
A close look at the load current waveform reveals that its average value is equal to its
instantaneous value during the middle of PWM on time or off time. Since the load current
flows through the DC link during PWM on time, sampling the DC link current during the
middle of PWM on time gives the average load current.

So, what I call motor current is the "average load current"/"average MOTOR current".

Yes, I have screenshots for unfiltered current within one PWM period and mark the time the adc value for motor_current is taken and I even wrote a note about this: https://opensourceebikefirmware.bitbucket.io/development/heets_and_application_notes--Endless-sphere.com_forum_messages--2018.02.20_-_Reading_motor_phase_current_from_the_DC_link_current_(shunt).html
Guys, please read the application note (PDF file) and say what you think should be different...

One question: should I regulate motor or battery current to regulate motor torque??
 
Ah, thank you, now I understand. We've discussed that before, but i didn't remember. The "motor_current" is the average DC rail current while PWM on time, the "battery_current" is the average whithin the whole PWM period, so the higher the duty cycle the lower the difference between motor_ and battery_current. (as the simple formula motor_current = battery_current/DC describes, the resolution of your screenshot is to bad to verfiy this formula with measured values :( )
But I still don't like the expression "motor_current" :), even if it's used quite frequently...

Had we still grilled Mosfets with controlling the "motor current" or does it protect the mosfets reliably?

regards
stancecoke
 
stancecoke said:
Had we still grilled Mosfets with controlling the "motor current" or does it protect the mosfets reliably?
Once I did disable motor current controller and I saw for the first time the fuse I have on my 48v battery, working - at it was on the very first motor startup with load on the motor. So yes, I think it works well.

Since I remember, I tried to control the motor current for torque and got not satisfactory results. Then I started to control battery current and I feel constant force/assist power from the motor.

Ok this days, I found that the PI controler for battery current is hard to setup and the best way is to use the fast bang controller on the PWM duty-cycle. My direct drive motor still vibrates when battery current is being limited... Don't know if it can be improved...
 
stancecoke said:
Had we still grilled Mosfets with controlling the "motor current" or does it protect the mosfets reliably?
I have not tested much but I would say "reliably" maybe, but as I understand now, we cant do real phase current limiting or even battery, without the second hall current sensor.

For real battery current measurement the shunt would have to be before all capacitors and for real phase current combined - after all of them. Which its not, and just cant be.. Capacitors have to be as close to mosfets as possible.
 
Most advanced phase current limiting should survive shorting phases even while running near 100% duty cycle.

So thats a possible test.. but if capacitors store enough power to blow the mosfets, they will, anyway. (With current HW)
 
I tried to make an animated sketch of the center aligned PWM with SVM that we use for our firmware. But again I don't know how to calculate the right phase current. The way it is shown in the bars now is not correct I think :-(

[youtube]lXK9HPvEzTQ[/youtube]

regards
stancecoke
 
Great work on that simulation! Always good to study and learn.

About the vibrations on my direct drive motor, I found that they always stopped at about 13kn/h... Then I thought it could be the startup phase, as the sinewave phase FOC comes after that 13km/h in the case of my motor and wheel size.

So the initial angle at startup is #define MOTOR_ROTOR_OFFSET_ANGLE 202. I changed to - 25, to 177, and the vibrations were much higher and the motor had much less torque... Then I changed to +25, to 227 and I got less vibrations and more torque!! I never felt this motor so good at startup :)
And I think this explains why I always felt that this battery had a smal range on this motor... So I think the issue was that the startup phase was very inefficient!! Now I want to test more and see if I can get a value were startup phase is smooth and has the max possible torque.
 
I have tested FOC erps at 5 - like 3km/h allready and it was working.. much better than that vibrating :lol: guess you will find it better to set lower also. After you find the right angle value..
 
A quick note about the LCD data: I tried to send the data to LCD at 25ms period and the LCD don't show nothing. Going back to 100ms period and it works :)
 
Hm, why don't you send the data as an answer only when you received a valid message from the LCD, like the original firmware from Kunteng does? This will relieve the processor...

regards
stancecoke
 
stancecoke said:
Hm, why don't you send the data as an answer only when you received a valid message from the LCD, like the original firmware from Kunteng does? This will relieve the processor...
I implemented like that and it worked at first time. Like you suggest, while it may relieve the processor, I prefer to have it working with my defined fixe time because current code implements the PI controllers and low pass filters and they need to have constant interval time.
 
I just tried the master branch with my BionX DD and the modded BMS-Torque-sensor, but as I expected, the PAS signal processing fails. I always get regen. I already changed the PAS direction from RIGHT to LEFT. But no difference. :(

Where in the code is the PAS direction detection to improve this part?

regards
stancecoke
 
stancecoke said:
I just tried the master branch with my BionX DD and the modded BMS-Torque-sensor, but as I expected, the PAS signal processing fails. I always get regen. I already changed the PAS direction from RIGHT to LEFT. But no difference. :(

Where in the code is the PAS direction detection to improve this part?
One question: you only use PAS or torque sensor + PAS to get the human power?

On my current testing code, I eliminated the PI controller for battery_current and that control is done instead on PWM interrupt code, bit bang code. The PI controller is used only for the wheel speed controller.
And the battery_current seems to work well like this.

If you have more questions, please ask and I will be able to help.

PAS code is on motor.c, this section:
Code:
  /****************************************************************************/
  // calc PAS1 timming between each positive pulses, in PWM cycles ticks
  // calc PAS1 on and off timming of each pulse, in PWM cycles ticks
  ui16_pas1_counter++;

  // detect PAS signal changes
  if ((PAS1__PORT->IDR & PAS1__PIN) == 0)
  {
    ui8_pas1_state = 0;
    ui16_pas1_off_time_counter++;
  }
  else
  {
    ui8_pas1_state = 1;
    ui16_pas1_on_time_counter++;
  }

  if (ui8_pas1_state != ui8_pas1_state_old) // PAS signal did change
  {
    ui8_pas1_state_old = ui8_pas1_state;

    if (ui8_pas1_state == 1) // consider only when PAS signal transition from 0 to 1
    {
      // limit PAS cadence to be less than PAS_ABSOLUTE_MAX_CADENCE_PWM_CYCLE_TICKS
      if (ui16_pas1_counter < ((uint16_t) PAS_ABSOLUTE_MAX_CADENCE_PWM_CYCLE_TICKS)) { ui16_pas1_counter = PAS_ABSOLUTE_MAX_CADENCE_PWM_CYCLE_TICKS; }

      ui16_pas1_pwm_cycles_ticks = ui16_pas1_counter;
      ui16_pas1_counter = 0;
    }
    else
    {
#if (PAS_DIRECTION == PAS_DIRECTION_RIGHT)
      if (ui16_pas1_on_time_counter > ui16_pas1_off_time_counter)
#else
      if (ui16_pas1_on_time_counter <= ui16_pas1_off_time_counter)
#endif
      { ui8_pas1_direction = 1; }
      else { ui8_pas1_direction = 0; }

      ui16_pas1_off_time_counter = 0;
      ui16_pas1_on_time_counter = 0;
    }

    // filter the torque signal, by saving the max value of each one pedal rotation
    ui8_torque_sensor_throttle_value = UI8_ADC_THROTTLE;
    ui8_torque_sensor_pas_signal_change_counter++;
    if (ui8_torque_sensor_pas_signal_change_counter > PAS_NUMBER_MAGNETS_X2) // PAS_NUMBER_MAGNETS*2 means a full pedal rotation
    {
      ui8_torque_sensor_pas_signal_change_counter = 1; // this is the first cycle
      ui8_torque_sensor_throttle_processed_value = ui8_torque_sensor_throttle_max_value; // store the max value on the output variable of this algorithm
      ui8_torque_sensor_throttle_max_value = 0; // reset the max value
    }
    else
    {
      // store the max value
      if (ui8_torque_sensor_throttle_value > ui8_torque_sensor_throttle_max_value)
      {
	ui8_torque_sensor_throttle_max_value = ui8_torque_sensor_throttle_value;
      }
    }
  }

  // limit min PAS cadence
  if (ui16_pas1_counter > ((uint16_t) PAS_ABSOLUTE_MIN_CADENCE_PWM_CYCLE_TICKS))
  {
    ui16_pas1_pwm_cycles_ticks = (uint16_t) PAS_ABSOLUTE_MIN_CADENCE_PWM_CYCLE_TICKS;
    ui16_pas1_counter = 0;
    ui16_pas1_on_time_counter = 0;
    ui16_pas1_off_time_counter = 0;
    ui8_pas1_direction = 1;
    ui8_torque_sensor_throttle_processed_value = 0;
  }
  /****************************************************************************/
 
Back
Top