Motorbike Part 8: Fixing problems, Riding Home, and achieving High Speed

High speed was achieved in part 7 after the complete hardware replacement and complete code rewrite. However, Turbo Speed was not accomplished.

What is Turbo Speed? Turbo Speed is a relative term, but basically it means that you make your project go faster than it ever should. I set a goal of achieving 50 mph on this bike. 50 mph is really terrifyingly fast. The previous record for my bike of 44.96 mph was set during a track run around the Palmer motorsports track. The firmware which did this was exceedingly sketchy, with no lookup tables, a ton of noise, and most terrifyingly a huge jump in torque around 20mph when the d axis loop stopped tracking (!). Here is some of the log for this drive:

Note the jump in speed at the beginning of the drive. This was most unsettling to ride as you were like uh oh uh oh brace for imminent light wheelie at 20mph. Additionally, it seemed that 3/4 throttle had more torque than full throttle. The motor also got incredibly hot while running this firmware.

Nevertheless, 45 mph was achieved, on more or less level ground, without any lookup tables at all. That is quite good. With lookup tables, 50 should be achievable. However, there were still a lot of clear problems which needed to be addressed before a Turbo Speed run was attempted. Most importantly, the currents and voltages had huge noise, and also I needed to solve for and compensate out some artifacts in the system.

The first step was to find the cause of the horrible noise present on the currents and the voltages. This noise only appeared with simultaneous high speed and high current, although that was not clear at first. I initially hypothesized this noise was related to a position sensor error, although this ultimately proved to not be true.

Often times seemingly random noise on a slow log (in this case 20 Hz) is in fact very periodic, often related to either electrical frequency or mechanical frequency. The noise’s relation to either mechanical or electrical frequency can often give clues to its origin. To know details about either the mechanical or electrical frequency, you need to log FAST- in this case 10kHz.

To debug this it was time to fire up the synchronous logger. This logger is a strange logger which records data to huge arrays every loop cycle. When the huge arrays are full, they are dumped over serial to the data logger, which stores the data on an SD card. This means that everything is logged at 10kHz, but only at 10% duty cycle. This is fine for what I am doing. It turns out that continuously logging at 10kHz is in fact really hard- logging eight values (throttle, speed, d_ref, d, q_ref, q, vd, vq) even at only 8-bit resolution is 640,000 bits per sec. This means if your micro was doing nothing but spitting data out over serial (and that the serial link had zero overhead, which is false) you would need at least 640,000 baud, probably more to write a newline character. Your microcontroller also needs to do other things at 10kHz (like run FOC, kind of important on a motor controller) which pushes the required baud rate even faster. Hypothetically, an STM32F303K8’s serial interface can run at up to 9 MHz, but Bayley reported poor results past just 1 MHz on the 446 he uses for the go-kart. Additionally, it is not clear that an SD card can even take data at this rate, because it occasionally pauses for garbage collection. This all adds up to say its less effort to just use the code I already wrote:

In the 10kHz loop:

log_data_short( int(e_rad_sec/25), int(focc.i_d_ref*6), int(focc.i_d*6), int(focc.i_q_ref*6), int(focc.i_q*6), int(focc.v_d_ref), int(focc.v_q),);   

In the main loop:

if (data_count == DATA_LEN) {
   pc.printf(" 0,0,0,0,0,0,0 \n");
   data_count = 0;

And the functions:

void log_data_short( short input1, short input2, short input3, short input4, short input5, short input6, short input7 )  {
        if (data_count < DATA_LEN) {
            data_array1[data_count] = input1;
            data_array2[data_count] = input2;
            data_array3[data_count] = input3;
            data_array4[data_count] = input4;
            data_array5[data_count] = input5;
            data_array6[data_count] = input6;
            data_array7[data_count] = input7;

void print_data_array_short() {
    for( int a = 0; a < DATA_LEN; a++ ) {
        pc.printf("%i, %i, %i, %i, %i, %i, %i ",data_array1[a],data_array2[a],data_array3[a],data_array4[a],data_array5[a],data_array6[a],data_array7[a]);

The arrays are filled with data synchronously with the loop until they are full, upon which they are no longer filled with data and they are dumped out over serial. A row of zeros is printed to denote the end of a dump. When the dumping is done, the cycle repeats.

Here is what a log from this logger looks like. Note the periodic blocks when each block is dumped. These blocks are actually recorded many samples apart, as it takes a lot longer to dump the block to the SD card than it does to fill the block at switching frequency.

I plotted the currents and their setpoints, along with electrical position.

This log told me a few critical things:

  • The currents do track their setpoints. Notice how the green line is perfectly centered around the purple line and the orange line is perfectly centered around the red line.
  • The position sensor is behaving perfectly. Notice how the blue line (electrical position) is a perfect triangle wave. I was expecting this line to have weird excursions.
  • Additionally, the noise has a frequency component of 2x the electrical frequency. This means it is most likely unrelated to mechanical issues, as mechanical issues would show up at 1/3 the electrical frequency.

Further digging was necessary. For the next log I decided to dive deeper and log the phase currents.

Here it is clear that something is wrong with one of the currents. But why?

To ensure everything was being scaled properly I also logged the raw ADC registers. I plotted them in matlab, scaled against the calculated currents (scaled wrongly by 1% so the traces do not overlap). This proved to be the gold mine.

There is a one-sample lag on one ADC channel. It is clear that one of the two currents has exactly one sample period of lag. Why is this?

This has to do with how the ADCs work. When a “start conversion” command is issued, the ADC register takes 0.3us to perform the conversion. If the register is read before that 0.3us time is complete, the old data from the previous conversion will still be present in the register, causing a one-sample delay. The data logging command is the last event to run in the loop, so by the time the data logger read out the ADC value the sampling was long finished.

The original code:

ADC1->CR  |= ADC_CR_ADSTART;        //Begin sample and conversion
for (volatile int t = 0; t < 8; t++) {} focc.adc2_raw = ADC2->DR;
focc.adc1_raw = ADC1->DR;

The fix:

ADC1->CR  |= ADC_CR_ADSTART;        //Begin sample and conversion
for (volatile int t = 0; t < 12; t++) {} //MUST BE 12!!!! focc.adc2_raw = ADC2->DR;
focc.adc1_raw = ADC1->DR;

The value of 12 is a bodge- it depends on your ADC setup, your ADC speed, and how fast instructions are executed on your CPU.

One more short data log later:

The one-sample delay has been removed.

Time for another joyride around the block. The motor became noticeably quieter with this bug fixed.


Next up it was time to put back on the lookup tables. It just so happened that the day of lookup table installation, I had planned a trip home to Lincoln. I was planning on taking the train originally- but, if I had a powerful long range electric bike…  hmmm…. wait is that possible?

I knew I had at least a 10 mile range from frolicking around Cambridge, but the trip home was 18 miles. Driving all the way home was sort of the stuff of legends, I’d always dreamed of doing it on a skateboard but it was definitely more of a pipe-dream deal than an actual desire.

I called up my dad and asked him if he could possibly pick me and a bike up in Waltham. He was around, so the drive was on!!

It was pretty nuts. The lookup table code was completely untested. I decided to charge the thing all the way up at MITERS and drive across the bridge to my fraternity (to get clothes and stuff for going home), where the journey would begin. At my fraternity, I would inspect the log to ensure everything was working, and make changes if necessary. If it was, I would make the trip.

Onward! The log across the bridge. A top speed of 40 was made to pass some pleb bikers. Man it really ripped.

Slight voltage saturation was experienced. I changed a scaling constant by 5% or so and off we went.

The drive home was quite serene. The first half of the drive was on the Charles river Esplanade bike path, where I averaged between 20 and 25 mph. Here is the log. Integrating the speed of this log gives a traveled distance of 9.8 miles.

At waltham center, I stopped to take a break, as the motor was getting pretty warm from continuous running. I measured the battery voltage to be 148.4, or 3.71 v/cell. Not bad! Pretty much halfway discharged- so I’ll be running on fumes if/when I make it to Lincoln….

The bike resting in Waltham.

After a 20 minute rest, we were off again!

I drove another 4.1 miles to Dairy joy, where I stopped again to cool the motor and inverter.  At Dairy joy I had 3.6 volts/cell. From there I drove another 2.7 miles to my house! I made it! At my house I was only down to 3.51 v/cell, so I drove to Aarons, another 0.9 miles. A total of about 20 miles on one charge! WOW!!

Anyways, after the brief excursion at Aarons to enjoy the countryside, it was back to MIT to continue the science.

One quick change which I had wanted to do for a while was a slight alteration of the gear ratio. I changed from an 11 tooth to a 12 tooth pinion on the output shaft of the gearbox. This change was done because I actually had too much torque at zero speed- the bike would pretty much wheelie above half throttle. This means that the motor was a poor impedance match for the bike, with the motor spinning faster than it should for a given speed. This change should hopefully yield greater efficiency at higher speeds.

The next change was slightly more interesting. FOC, when naively implemented, works pretty well, but there are a few interesting properties which can be taken into account to boost performance.

One interesting feature is that because the control loop and the PWM is fundamentally a discrete process, a delay of one switching cycle shows up. This causes strange effects. Most notably, when the motor is spinning fast, the position of the motor at the beginning of the interrupt is vastly different from the position at the end of the interrupt. Therefore, when the voltages are written to the phases, the position is no longer correct.

This strange effect is most noticeable in a spin-down of the motor. The motor is spun up to a high speed and then commanded zero torque. The friction of the system slowly decelerates the motor down to a stop. During this spin-down, some number of volts on the Q axis are necessary to cancel the back-EMF of the motor. However, if some of these volts end up on D because of this one sample position lag, the D controller will need to put some extra negative volts there to cancel out the lagged Q volts. This generally makes performance confusing, as the voltages are not accurate.

It wasn’t actually clear that the delay was exactly one sample, as center-aligned PWM greatly complicates things, introducing the possibility of 1.5 and 0.5 sample delays. I predicted the what the voltage-speed relation would look like and found it to be a dead on one sample delay.

The way to implement this in the code was not clear at first. The proper way to do it was to use the sampled position of the dq0 transform, and use the advanced position on the abc transform.

With this modification, the D voltage artifacts were removed!

No strange D volts. Success.

Next up, with the voltages fixed, it was time to look at the lookup tables. Here is a full throttle acceleration to maximum speed.

Notice how the voltage hovers around 120 at higher speeds. This means the controller is field weakening too hard.

The solution was to diddle the lookup table ever so slightly to reduce field weakening. I went a bit too far with my diddling, as shown by the loss of D axis tracking, but performance was increased!

I decided to go for a turbo speed esplanade run to see if this code would get me to Turbo Speed. The max speed recorded on this log was 49.2 mph…. just short of turbo speed. But, not bad!!


In summary:

  • some tough bugs were fixed
  • the lookup tables were put back on
  • performance was amazing and I rode it all the way home, a distance of 19 miles, on a single charge.
  • Position feed-forward was implemented to reduce strange voltage errors
  • Performance was good
  • The lookup tables were diddled to increase performance
  • Performance is now very good, but just short of Turbo Speed

Next up: TURBO SPEED!!!!!

Main Project Page

Prius A/C bike part 7: Blowing up everything, Deleting the firmware, but achieving high speed

Much has happened since Part 6: I rode it around a lot I put lookup tables on it and performance was acceptable However there were several problems: I blew up all the electronics I accidentally deleted all the code and therefore rewrote it from scratch But eventually: I made a data logger to observe performance […]

Prius Motorbike Part 3: Controlller Tuning, No-Load and Dyno Testing

This post is a bit backlogged but its some good stuff. This post will go over some of testing of my first rehoused motor and JaredTroller. Main page here. First up some controller gain tuning. Gain tuning required knowledge of the resistance and the inductance of the motor. Resistance measurement should be not that hard […]

Toyota Highlander Hybrid (Denso ES27C) A/C Compressor Teardown

Here is a teardown of the 2015-2016 Toyota Highlander Hybrid A/C Compressor, the Denso ES27C. A lot of different cars use Denso air conditioner compressors. The which car uses which compressor can be found here. During this teardown I will reference the 2006-2008 Prius air conditioner teardown (an ES18C), which can be viewed as part […]

Prius Motorbike: The Bike of Motor Science

Recently I have been working on a new project centered around ~5kW IPM (interior permanent magnet) brushless motors, with the goal of eventually using these motors to drive some bicycles. IPM motors offer a number of benefits for traction applications, such as field weakening and reluctance torque. The motors I have been using are the rotor and stator […]

Prius A/C IPM Part 2: JaredTroller Assembly and Housing Machining

Main page here. Much Progress! last post here. The JaredTroller boards arrived. The text got a bit messed up but other than that they looked great. The footprint I made fit the brick perfectly. It fit very snugly. The capacitors did not fit very well though, as they were wider than expected. Some slight belt sanding […]