Making a torque throttle instead of a speed throttle for a cheap controller

AviatorTrainman

Pedelecer
Feb 9, 2018
35
6
23
Arizona, USA
Hello all, I recently made this device and thought I'd share it with the community, both to help others and to possibly get others to help me.

Essentially, it's an Arduino hooked up to the signal from the twist/thumb throttle, a PWM smoothing circuit which is in turn connected to the throttle signal the controller receives, and a contactless-type current sensor, like this one. It is powered by the power wire that goes to the throttle from the controller.

It works by mapping the throttle input to a target current, then modulating the throttle output signal to achieve this current. It should work with any controller that has a hall sensor throttle signal.

The smoothing circuit doesn't have to be perfect, just enough that the controller won't receive an unfiltered PWM signal, because it won't recognize that as a throttle signal. It can also never receive a straight 5 volts, or whatever the throttle power voltage is, because it will trigger a "stuck throttle" condition in the controller and cause it to shut down throttle input as a safety measure.

I will soon be implementing a proportionality to the feedback function so that it will change the throttle more drastically when it is farther from its target current. This will reduce throttle lag and increase safety, because as of right now, it takes a long time to "spool up" and "spool down" the throttle signal, essentially meaning that there is a significant delay between punching the throttle and getting to full current, or releasing it and getting to zero. This delay is on the order of a second or two. As such, I recommend that until there is such a proportionality, only those who have brake lever shutoffs use this. I do not have such sensors, and so have had a couple of instances where I was fighting the motor with my brakes while making a sudden stop. I take no responsibility for any harm or damage to you or your property or the property of others incurred by messing about with your throttle signal.

Proportionality is only a part of a Proportional-Integral-Derivative control loop, though, so if someone who has more experience with Arduino could point me in the right direction to implement the rest, I would be grateful for the extra help and smoothness of the control.

Code here. Sorry if it's over-commented, I just wanted to make sure someone who isn't familiar with Arduino can still understand what is happening.
Code:
const int throttleOutPin = 9; // The pin on which the throttle output to the controller is located. If you change this, you may also need to change the address
                            // of the referenced clock in the second-last line of the setup section. See https://playground.arduino.cc/Main/TimerPWMCheatsheet for more info.
const int throttleInPin = A0; // The pin on which the arduino will receive the incoming throttle signal
const int currentInPin = A1;  // The pin on which the arduino will recieve the current reading

const int minVoltIn = 1222;   // The minimum voltage the arduino should expect to receive from the throttle in mV. Measure your own throttle for a perfect value, but 1200 will do.
const int maxVoltIn = 4257;   // The maximum voltage expected from the throttle in mV. I set mine a little higher than actual so that the arduino never goes to 100% pwm duty cycle.
const int minVoltOut = 980;   // The minimum voltage the arduino will output in mV. It is higher than the throttle's because my smoothing circuit reduces the voltage slightly.
const int maxVoltOut = 4999;  // The maximum voltage the arduino will output in mV. It is higher than the throttle's because my smoothing circuit reduces the voltage slightly.
const int maxCurrent = 22000; // The maximum current the arduino will ever attempt to maintain flowing to the motor. Set this to the max of your motor or controller, whichever is lower.

int targetCurrent = 0;        // A placeholder for the current the arduino aims to maintain in mA
int throttleIn = 0;           // A placeholder for the present value of the throttle input
int throttleOut = 0;          // A placeholder for the present value of the throttle output
int current = 0;              // A placeholder for the present value of the measured current

int maxOut = 0;               // A placeholder for the maximum output pwm, with 255 being 100% duty cycle
int minOut = 0;               // A placeholder for the minimum output pwm, with 0 being 0% duty cycle
int maxIn = 0;                // A placeholder for the maximum throttle input voltage, with 1023 being 5 Volts
int minIn = 0;                // A placeholder for the minimum throttle input voltage, with 0 being 0 Volts

void setup() {
  // Mapping the throttle voltages from voltages to numbers the arduino can use
  maxOut = map (maxVoltOut, 0, 5000, 0, 255);
  minOut = map (minVoltOut, 0, 5000, 0, 255);
  maxIn = map (maxVoltIn, 0, 5000, 0, 1023);
  minIn = map (minVoltIn, 0, 5000, 0, 1023);

  // Changing the clock speed of the clock which controls the ouput pin to the highest setting, to make the pwm output easier to smooth out
  TCCR1B = (TCCR1B & 0b11111000) | 0x01;

  // Start the serial communication with the computer, to be able to troubleshoot faulty operation. Comment this out to increase operation speed and reduce lag.
  Serial.begin(9600);
}

void loop() {
  throttleIn = analogRead(throttleInPin);                         // Read the throttle setting
  targetCurrent = map(throttleIn, minIn, maxIn, 0, maxCurrent);   // Generate a target current based on the throttle setting
  targetCurrent = targetCurrent - 0;                              // A line to reduce or increase the current manually, if you need to do that for whatever reason. I don't.
  current = map(analogRead(currentInPin), 0, 1023, 0, 5000);      // Read the analog voltage from the current sensor and map it to a range from 0 to 5000 mV
  current = current - 2500;                                       // Set the numeric value to the voltage at which your current sensor reads zero. If your sensor is installed facing
                                                                // the other way, you may need to reverse "current" and the numeric value in the subtraction.
  current = current * 12;                                         // Set the numeric value to the ratio of mA to mV of your particular sensor.

  // The feedback loop to keep current stable. Ideally, this would be a PID control, but I don't really know how to implent that, so the controller surges a little at low currents.
  if (current > targetCurrent) {
    if (throttleOut > minOut) {
      throttleOut = throttleOut - 2;
    }
  }
  if (current < targetCurrent) {
    if (throttleOut < maxOut) {
      throttleOut = throttleOut + 2;
    }
  }

  analogWrite(throttleOutPin, throttleOut);   // Send the output that's been calculated to the controller

  // Tell any computer attached by serial connection the status of key values. This requires the "Serial.begin" line to be enabled. Comment these out to increase operation speed and reduce lag.
  Serial.print(throttleIn);
  Serial.print( "\t" );
  Serial.print(targetCurrent);
  Serial.print( "\t" );
  Serial.print(current);
  Serial.print( "\t" );
  Serial.println(throttleOut);
}

Let me know what you think, I'd like to receive some feedback on this project that isn't just a series of numbers! :p
 

danielrlee

Esteemed Pedelecer
May 27, 2012
1,348
688
Westbury, Wiltshire
torquetech.co.uk
Cool project.

You might want to take a look at how GrinTech implements current control on the Cycle Analyst. It features user definable 'gain' and 'dampening' parameters to fine tune the PID feedback loop, allowing compatibility across a wide range of power usage scenarios.
 
  • Like
Reactions: AviatorTrainman

AviatorTrainman

Pedelecer
Feb 9, 2018
35
6
23
Arizona, USA
I've updated my code with both the proportionality and a cut-throttle failsafe, so the lag is now less than half a second when throttling up or down, and less than a millisecond when releasing the throttle all the way. (Yes, I mean that. Dropping the throttle in programming now happens faster than the spring operates).

New code here. The changes are the 3 lines about the "delta" variable right before the feedback loop section, and the if statement right after "void loop() {", which is the part of the code that immediately cuts power if you let go of the throttle.
Code:
const int throttleOutPin = 9; // The pin on which the throttle output to the controller is located. If you change this, you may also need to change the address
                            // of the referenced clock in the second-last line of the setup section. See https://playground.arduino.cc/Main/TimerPWMCheatsheet for more info.
const int throttleInPin = A0; // The pin on which the arduino will receive the incoming throttle signal
const int currentInPin = A1;  // The pin on which the arduino will recieve the current reading

const int minVoltIn = 900;   // The minimum voltage the arduino should expect to receive from the throttle in mV. Measure your own throttle for a perfect value, but 1200 will do.
const int maxVoltIn = 4257;   // The maximum voltage expected from the throttle in mV. I set mine a little higher than actual so that the arduino never goes to 100% pwm duty cycle.
const int minVoltOut = 980;   // The minimum voltage the arduino will output in mV. It is higher than the throttle's because my smoothing circuit reduces the voltage slightly.
const int maxVoltOut = 4800;  // The maximum voltage the arduino will output in mV. It is higher than the throttle's because my smoothing circuit reduces the voltage slightly.
const int maxCurrent = 22000; // The maximum current the arduino will ever attempt to maintain flowing to the motor. Set this to the max of your motor or controller, whichever is lower.

int targetCurrent = 0;        // A placeholder for the current the arduino aims to maintain in mA
int throttleIn = 0;           // A placeholder for the present value of the throttle input
int throttleOut = 0;          // A placeholder for the present value of the throttle output
int current = 0;              // A placeholder for the present value of the measured current

int maxOut = 0;               // A placeholder for the maximum output pwm, with 255 being 100% duty cycle
int minOut = 0;               // A placeholder for the minimum output pwm, with 0 being 0% duty cycle
int maxIn = 0;                // A placeholder for the maximum throttle input voltage, with 1023 being 5 Volts
int minIn = 0;                // A placeholder for the minimum throttle input voltage, with 0 being 0 Volts

int delta = 0;                // A placeholder for the value the throttle should be changed by

void setup() {
  // Mapping the throttle voltages from voltages to numbers the arduino can use
  maxOut = map (maxVoltOut, 0, 5000, 0, 255);
  minOut = map (minVoltOut, 0, 5000, 0, 255);
  maxIn = map (maxVoltIn, 0, 5000, 0, 1023);
  minIn = map (minVoltIn, 0, 5000, 0, 1023);

  // Changing the clock speed of the clock which controls the ouput pin to the highest setting, to make the pwm output easier to smooth out
  TCCR1B = (TCCR1B & 0b11111000) | 0x01;

  // Start the serial communication with the computer, to be able to troubleshoot faulty operation. Comment this out to increase operation speed and reduce lag.
  Serial.begin(9600);
}

void loop() {
  throttleIn = analogRead(throttleInPin);                         // Read the throttle setting

  if (throttleIn < (minIn + 50) && throttleOut > (minOut + 25)) { // Before doing any other calculations, figure out if the throttle needs to be cut immediately
    throttleOut = minOut;
  }
  else {
    targetCurrent = map(throttleIn, minIn, maxIn, 0, maxCurrent);   // Generate a target current based on the throttle setting
    targetCurrent = targetCurrent - 0;                              // A line to reduce or increase the current manually, if you need to do that for whatever reason. I don't.
    current = map(analogRead(currentInPin), 0, 1023, 0, 5000);      // Read the analog voltage from the current sensor and map it to a range from 0 to 5000 mV
    current = current - 2500;                                       // Set the numeric value to the voltage at which your current sensor reads zero. If your sensor is installed facing
                                                                  // the other way, you may need to reverse "current" and the numeric value in the subtraction.
    current = current * 12;                                         // Set the numeric value to the ratio of mA to mV of your particular sensor.
   
    delta = targetCurrent-current;                                  // Perform a few operations to determine the delta value
    delta = abs(delta);
    delta = map (delta, 0, maxCurrent, 0, 10);

    // The feedback loop to keep current stable. Ideally, this would be a PID control, but I don't really know how to implent that, so the controller surges a little at low currents.
    if (current > targetCurrent) {
      if (throttleOut > minOut) {
        throttleOut = throttleOut - delta;
      }
    }
    else if (current < targetCurrent) {
      if (throttleOut < maxOut) {
        throttleOut = throttleOut + delta;
      }
    }
  }
  if (throttleOut > maxOut){
    throttleOut = maxOut;
  }
  else if ( throttleOut < minOut){
    throttleOut = minOut;
  }
  analogWrite(throttleOutPin, throttleOut);   // Send the output that's been calculated to the controller

  // Tell any computer attached by serial connection the status of key values. This requires the "Serial.begin" line to be enabled. Comment these out to increase operation speed and reduce lag.
  Serial.print(throttleIn);
  Serial.print( "\t" );
  Serial.print(targetCurrent);
  Serial.print( "\t" );
  Serial.print(current);
  Serial.print( "\t" );
  Serial.println(throttleOut);
}
In addition, here's a schematic I drew up:
arduino throttle control.PNG
Sorry it's messy, I wasn't really sure how to go about making it clear that the Red and Black lines from the controller are VCC and Ground.

Power delivery is very smooth now, with the "surging" being limited to about ±5 watts, which I can't feel while riding. I certainly got a workout carrying my bike up and down 2 flights of stairs each time I wanted to change a value in the new code!

It's possible a full PID solution wouldn't be required, since this seems much more usable than I had anticipated.
 
  • Like
Reactions: wayneb

AviatorTrainman

Pedelecer
Feb 9, 2018
35
6
23
Arizona, USA
Bad news, I won’t be able to test for a while. I’ve been having battery issues since late February, like cutting out over speed bumps, and I figured it was a loose connection. I opened up my battery today, and found this:

I count myself extremely lucky for 2 reasons. One, it was a clear night last night, so I stored my bike outside instead of in; and two, this cell from the middle of the pack did not cause any visible damage to any others (i.e. no lithium fire), although the one it was in parallel with is probably dead too. I found a stray nickel strip melted to the bottom of the bottle when I opened it up, so I assume this was my loose connection, and then somehow after some time it touched both the cap and the shoulder of this cell. This morning was the first that it wouldn’t take a charge, which is why I opened it up today. I will completely dissassemble the pack, and maybe if at least 20 cells of the 26 are okay, I’ll reassemble it as a 36v (or maybe 44v?) pack until I can get new cells in.
 

vfr400

Esteemed Pedelecer
Jun 12, 2011
9,822
3,985
Basildon
When you build a battery it's too easy to nick through the heat-shrink with the sharp edge of the nickel strip. The cardboard rings give you insurance against that.

There are various other useful insulators and sheet that you can buy to give your cells more protection, which you can find on Ebay or the Chinese websites.
 

Nealh

Esteemed Pedelecer
Aug 7, 2014
19,995
8,175
60
West Sx RH
Have to strongly agree no corners should be cut if tab welding your own packs and an insulator on the V+ end is a must.
 

AviatorTrainman

Pedelecer
Feb 9, 2018
35
6
23
Arizona, USA
I'm glad I learned that now, with a minor failure in a small pack, rather than say something catastrophic in a large pack that I'll be doing for my dad over the summer.
 

Woosh

Trade Member
May 19, 2012
19,407
16,387
Southend on Sea
wooshbikes.co.uk
Hello all, I recently made this device and thought I'd share it with the community, both to help others and to possibly get others to help me.

Essentially, it's an Arduino hooked up to the signal from the twist/thumb throttle, a PWM smoothing circuit which is in turn connected to the throttle signal the controller receives, and a contactless-type current sensor, like this one. It is powered by the power wire that goes to the throttle from the controller.

It works by mapping the throttle input to a target current, then modulating the throttle output signal to achieve this current. It should work with any controller that has a hall sensor throttle signal.

The smoothing circuit doesn't have to be perfect, just enough that the controller won't receive an unfiltered PWM signal, because it won't recognize that as a throttle signal. It can also never receive a straight 5 volts, or whatever the throttle power voltage is, because it will trigger a "stuck throttle" condition in the controller and cause it to shut down throttle input as a safety measure.

I will soon be implementing a proportionality to the feedback function so that it will change the throttle more drastically when it is farther from its target current. This will reduce throttle lag and increase safety, because as of right now, it takes a long time to "spool up" and "spool down" the throttle signal, essentially meaning that there is a significant delay between punching the throttle and getting to full current, or releasing it and getting to zero. This delay is on the order of a second or two. As such, I recommend that until there is such a proportionality, only those who have brake lever shutoffs use this. I do not have such sensors, and so have had a couple of instances where I was fighting the motor with my brakes while making a sudden stop. I take no responsibility for any harm or damage to you or your property or the property of others incurred by messing about with your throttle signal.

Proportionality is only a part of a Proportional-Integral-Derivative control loop, though, so if someone who has more experience with Arduino could point me in the right direction to implement the rest, I would be grateful for the extra help and smoothness of the control.

Code here. Sorry if it's over-commented, I just wanted to make sure someone who isn't familiar with Arduino can still understand what is happening.
Code:
const int throttleOutPin = 9; // The pin on which the throttle output to the controller is located. If you change this, you may also need to change the address
                            // of the referenced clock in the second-last line of the setup section. See https://playground.arduino.cc/Main/TimerPWMCheatsheet for more info.
const int throttleInPin = A0; // The pin on which the arduino will receive the incoming throttle signal
const int currentInPin = A1;  // The pin on which the arduino will recieve the current reading

const int minVoltIn = 1222;   // The minimum voltage the arduino should expect to receive from the throttle in mV. Measure your own throttle for a perfect value, but 1200 will do.
const int maxVoltIn = 4257;   // The maximum voltage expected from the throttle in mV. I set mine a little higher than actual so that the arduino never goes to 100% pwm duty cycle.
const int minVoltOut = 980;   // The minimum voltage the arduino will output in mV. It is higher than the throttle's because my smoothing circuit reduces the voltage slightly.
const int maxVoltOut = 4999;  // The maximum voltage the arduino will output in mV. It is higher than the throttle's because my smoothing circuit reduces the voltage slightly.
const int maxCurrent = 22000; // The maximum current the arduino will ever attempt to maintain flowing to the motor. Set this to the max of your motor or controller, whichever is lower.

int targetCurrent = 0;        // A placeholder for the current the arduino aims to maintain in mA
int throttleIn = 0;           // A placeholder for the present value of the throttle input
int throttleOut = 0;          // A placeholder for the present value of the throttle output
int current = 0;              // A placeholder for the present value of the measured current

int maxOut = 0;               // A placeholder for the maximum output pwm, with 255 being 100% duty cycle
int minOut = 0;               // A placeholder for the minimum output pwm, with 0 being 0% duty cycle
int maxIn = 0;                // A placeholder for the maximum throttle input voltage, with 1023 being 5 Volts
int minIn = 0;                // A placeholder for the minimum throttle input voltage, with 0 being 0 Volts

void setup() {
  // Mapping the throttle voltages from voltages to numbers the arduino can use
  maxOut = map (maxVoltOut, 0, 5000, 0, 255);
  minOut = map (minVoltOut, 0, 5000, 0, 255);
  maxIn = map (maxVoltIn, 0, 5000, 0, 1023);
  minIn = map (minVoltIn, 0, 5000, 0, 1023);

  // Changing the clock speed of the clock which controls the ouput pin to the highest setting, to make the pwm output easier to smooth out
  TCCR1B = (TCCR1B & 0b11111000) | 0x01;

  // Start the serial communication with the computer, to be able to troubleshoot faulty operation. Comment this out to increase operation speed and reduce lag.
  Serial.begin(9600);
}

void loop() {
  throttleIn = analogRead(throttleInPin);                         // Read the throttle setting
  targetCurrent = map(throttleIn, minIn, maxIn, 0, maxCurrent);   // Generate a target current based on the throttle setting
  targetCurrent = targetCurrent - 0;                              // A line to reduce or increase the current manually, if you need to do that for whatever reason. I don't.
  current = map(analogRead(currentInPin), 0, 1023, 0, 5000);      // Read the analog voltage from the current sensor and map it to a range from 0 to 5000 mV
  current = current - 2500;                                       // Set the numeric value to the voltage at which your current sensor reads zero. If your sensor is installed facing
                                                                // the other way, you may need to reverse "current" and the numeric value in the subtraction.
  current = current * 12;                                         // Set the numeric value to the ratio of mA to mV of your particular sensor.

  // The feedback loop to keep current stable. Ideally, this would be a PID control, but I don't really know how to implent that, so the controller surges a little at low currents.
  if (current > targetCurrent) {
    if (throttleOut > minOut) {
      throttleOut = throttleOut - 2;
    }
  }
  if (current < targetCurrent) {
    if (throttleOut < maxOut) {
      throttleOut = throttleOut + 2;
    }
  }

  analogWrite(throttleOutPin, throttleOut);   // Send the output that's been calculated to the controller

  // Tell any computer attached by serial connection the status of key values. This requires the "Serial.begin" line to be enabled. Comment these out to increase operation speed and reduce lag.
  Serial.print(throttleIn);
  Serial.print( "\t" );
  Serial.print(targetCurrent);
  Serial.print( "\t" );
  Serial.print(current);
  Serial.print( "\t" );
  Serial.println(throttleOut);
}

Let me know what you think, I'd like to receive some feedback on this project that isn't just a series of numbers! :p
I don't understand what you try to achieve.
If your controller has a throttle input and you use a throttle, the controller responds to the throttle's voltage by matching the current to it.
If you want to add the functionality of a throttle to a bike without one, then you need to add safeguards to the output generating, a job normally done by the brake sensors.
 

danielrlee

Esteemed Pedelecer
May 27, 2012
1,348
688
Westbury, Wiltshire
torquetech.co.uk
I don't understand what you try to achieve.
If your controller has a throttle input and you use a throttle, the controller responds to the throttle's voltage by matching the current to it.
If you want to add the functionality of a throttle to a bike without one, then you need to add safeguards to the output generating, a job normally done by the brake sensors.
Simple answer - He's creating a device to adapt a speed controller into a torque controller.
 
  • Like
Reactions: Paul Kay

Woosh

Trade Member
May 19, 2012
19,407
16,387
Southend on Sea
wooshbikes.co.uk
Simple answer - He's creating a device to adapt a speed controller into a torque controller.
what's wrong with using the throttle the way it is at the moment?
 

Nealh

Esteemed Pedelecer
Aug 7, 2014
19,995
8,175
60
West Sx RH
I'm glad I learned that now, with a minor failure in a small pack, rather than say something catastrophic in a large pack that I'll be doing for my dad over the summer.
You may count your self as being fortunate that the cell shorted and caused no further issue, it might look like a minor failure but in reality is quite a major failure.
Shame though as a good waste of a MJ1 cell.
On a safety note one reason why lithium -ion is preferred over li-polymer.
 

danielrlee

Esteemed Pedelecer
May 27, 2012
1,348
688
Westbury, Wiltshire
torquetech.co.uk
what's wrong with using the throttle the way it is at the moment?
A speed controller's input voltage is directly linked to motor speed. When the throttle is actuated, full power is applied until the desired speed is reached. When the motor load changes, power is automatically regulated to maintain this speed. This might sound like a desirable feature, but in practice speed controllers tend to give an all-or-nothing power response and make granular power control near-impossible.

A torque (current) controller's throttle input voltage is directly linked to motor torque. A torque controller provides constant power application, irrespective of the motor load and results in a far more controllable power delivery.

The OP is trying to make an intermediary PID feedback device to give a speed controller torque control in the same way a Cycle Analyst V3 does when operating in 'torque throttle' mode.
 
  • Like
Reactions: Paul Kay

Woosh

Trade Member
May 19, 2012
19,407
16,387
Southend on Sea
wooshbikes.co.uk
The Lishui controllers that I use do not jerk the bike whatsoever when you start on throttle. Other controllers may behave differently.
 

Woosh

Trade Member
May 19, 2012
19,407
16,387
Southend on Sea
wooshbikes.co.uk
Are they speed throttle or current throttle controllers?
I have never checked. Member mechaniker should know though, he works for Lishui.
 

danielrlee

Esteemed Pedelecer
May 27, 2012
1,348
688
Westbury, Wiltshire
torquetech.co.uk
I have never checked. Member mechaniker should know though, he works for Lishui.
If minimal throttle is applied with the drive wheel in the air, does the motor turn slowly (speed throttle) or ramp up to near full-speed (current throttle)?
 

Woosh

Trade Member
May 19, 2012
19,407
16,387
Southend on Sea
wooshbikes.co.uk

danielrlee

Esteemed Pedelecer
May 27, 2012
1,348
688
Westbury, Wiltshire
torquetech.co.uk
I tested on the Woosh Krieger (fitted with a CD motor), the speed is proportional to the rotation angle of the thumb lever throttle.
http://wooshbikes.co.uk/2018/krieger-hdb/krieger-thumb-throttle.jpg
So definitely a speed speed throttle controller, but with a better control implementation than the cheapest of speed throttle controllers out there. Better speed throttle controllers (such as your Lishui) do take into account the actual/target speed delta and vary current accordingly, although they are unable to maintain a constant motor torque as the motor load changes, since they always want to maintain the speed as dictated by the throttle input.
 

Advertisers