Main Menu

My life, My coding

Started by Rx7man, May 27, 2015, 09:09:36 AM

Rx7man

OK, since I have ADD when it comes to coding, and always float around in my projects, here's a place I'll be posting my code, whatever section of project it belongs to

This morning's breakthrough.. I got a preliminary working test of my live tuning working... I was able to change the map values over the serial port.. it takes a lot of parsing.. and I will yet set some of the parameters in stone.

Attached is the project, I have it set to not read from EEPROM at this point because you won't have any valid data in there to load anyhow.
So here's how I have the data structure laid out so far when sent from the serial monitor

int Command, int Address, int Value
to read a variable, the low bit of Command is 0, to write the bit is 1
The high byte of the address currently refers to which data structure to read from (0000 0001 for Boostmap), and the low byte the address of the map (0 to 9*14)

So to write 60 to map address 4 (Row 0, column 4) send the following via the serial monitor
1,260,60
to read it
0,260,0

When reading, it is still required to specify the value, but it is ignored
'94 dually,  67/67 HE351VE, NV5600, ~600hp
'93 ECLB 47RH, new toy truck, H pump project, 1000hp goal, 300K miles
93 XCLB auto, bone stock, 350K miles
93 XCLB 5spd, bone stock, 100K miles

Rx7man

Update... VB now populates the cell values and correctly writes updated values to the map.. it is certainly a beta version, and I will *try* to find a better programatic way of addressing, either with bitflags or something to reduce the number of if-then-else and switch/case statements to reduce Arduino load, but this works for now.. I will also have to look at setting up an interrupt to write to the CAN bus, but that's a ways off yet.

I had a bit of an issue because I still wanted to print out debug statements to the serial port, but be able to separate the debugging lines from the data lines, so for now I have data lines starting with "Data:" and it's parsed out in the VB code easily enough.  I will also have to figure out how to switch between different maps, and maps of different sizes... I currently have a control array of textboxes, I may have to try and use datagrids instead
'94 dually,  67/67 HE351VE, NV5600, ~600hp
'93 ECLB 47RH, new toy truck, H pump project, 1000hp goal, 300K miles
93 XCLB auto, bone stock, 350K miles
93 XCLB 5spd, bone stock, 100K miles

Rx7man

Still to be tested, I put this in a function that will be used when reading in most analog inputs, and probably some outputs as well.. I just find it ties everything into one neat little function, even though it does take a bit of time to execute, it may be worth it.


float GetSmoothedValue(float Newvalue, float NewvalueOldvalue, float smoothing, float Minchange, float Maxchange) {
  //provides a decaying infinite average.. Smoothing goes from 0 to 1, where 0 will yield an unchanging output! (you have been warned)
  //depending on read frequency and desired aggressiveness  The benefit to this method
  //is it doesn't require an array to hold the history
  //The Deadband will prevent any change at all below a certain value (You have been warned)
  //MaxChange will limit the maximum change any given iteration... Make sure it is bigger than the deadband (You have been warned)

  smoothing = constrain(smoothing, 0, 1); //funky stuff can happen if we're not within bounds

  float returnval = (Newvalue * smoothing) + (Oldvalue * (1 - smoothing));

  float change  = abs(returnval - Oldvalue);

  if (change < Minchange) {
    return Oldvalue; //If the change isn't big enough, return the old value
  }
  else if ( change > Maxchange) {
    returnval = constrain(Newvalue, Oldvalue - Maxchange, Oldvalue + Maxchange); //otherwise constrain it to the max values
  }

  return returnval;
}


then I have different smoothing, minchange and maxchange values for every different input to tailor the response curve.


Next I will work on a function to linearize/delinearize, which I have used before.. it's especially handy when you have something you'd like to look linear which isn't.. perfect example is the VGT nozzle size where a small change at wide open is negligible, while a small change near closed is significant.. I'll just have to port it out to C++.
'94 dually,  67/67 HE351VE, NV5600, ~600hp
'93 ECLB 47RH, new toy truck, H pump project, 1000hp goal, 300K miles
93 XCLB auto, bone stock, 350K miles
93 XCLB 5spd, bone stock, 100K miles

Rx7man

Here's the code for recurving


float MyVar::RecurvedValue() {
  //Returns a recurved value between Min and Max
  float tempval = mapf(Storage, MinValue, MaxValue, 0, 1); //remap from min -> max to 0->1
  tempval = pow(tempval, Response);                  //recurve
  tempval = mapf(tempval, 0, 1, MinValue, MaxValue); //unmap it back from 0->1 to min->max
  return tempval;
}



To prevent getting huge numbers, overflows, etc, I map the value so it can only fall between 0 and 1, which makes the output of the power function fall between 0 and 1 as well, after that I remap it back to it's original bounds.
if you want a quick response at the top end, use a Response value of about 2 (squaring it).. A linear response uses 1, and a Square root response (quick near 0) is 0.5

I have all this in a class right now, but I'm fighting another weird bug in the arduino.. my loop times are wildly varying... Sometimes it's doing 60000 loops per second, then it'll fall on it's face and only do 60... don't know what gives yet.
'94 dually,  67/67 HE351VE, NV5600, ~600hp
'93 ECLB 47RH, new toy truck, H pump project, 1000hp goal, 300K miles
93 XCLB auto, bone stock, 350K miles
93 XCLB 5spd, bone stock, 100K miles

hakcenter

What is your full arduino code ?
TS2009 Deḇarim 8:2
"And you shall remember that יהוה your Elohim led you all the way these forty years in the wilderness, to humble you, prove you, to know what is in your heart, whether you guard His commands or not.

Rx7man

I'll post it when it's a bit more completed, and commented.. Right now I'm just trying ideas out for certain things
'94 dually,  67/67 HE351VE, NV5600, ~600hp
'93 ECLB 47RH, new toy truck, H pump project, 1000hp goal, 300K miles
93 XCLB auto, bone stock, 350K miles
93 XCLB 5spd, bone stock, 100K miles

Rx7man

Oh, and I should mention something too.. Not to be offensive, but I'm only glancing over the code you folk are posting here, the reason being I'd like to write something unique, problem-solve (at this point only in theory since I don't have the hardware yet), and try to find other ways to do things... Once I get my software a little more ironed out, and at least basically functional, I'll gladly try and study other code.
Cheers :)
'94 dually,  67/67 HE351VE, NV5600, ~600hp
'93 ECLB 47RH, new toy truck, H pump project, 1000hp goal, 300K miles
93 XCLB auto, bone stock, 350K miles
93 XCLB 5spd, bone stock, 100K miles

Rx7man

Oh the joys of OO programming, when you get it to work!

So all my input variables, as well as some of the output variables (like VGT position, AFC position) are of my own data type.. a type which keeps track of the update times, smooths out the jitter, limits the rate of change (min and max), and limits the extents..  It also recurves from linear to non-linear, so you can get a faster response at one end than the other.  Not only that, but it also calculates the acceleration and rate of acceleration, which could be very handy later when optimizing VGT nozzle size for best spoolup.. but I'm not at that point yet.. for now it's just really neat.

For example with the minimum change, you apply a deadband, so it won't move unless it's a certain amount greater than the last value, excellent for minimizing VGT workload, the maximum change is good to prevent overshooting a target and preventing hunting for a setpoint.  VGT example of that would be greater motion when it's wide open than when it's near closed, despite the rest of the code staying linear (which is easier that way)

I know that if I did have everything set up in my truck, I wouldn't be playing around with the code so much and adding so many features :P (aka bugs)
'94 dually,  67/67 HE351VE, NV5600, ~600hp
'93 ECLB 47RH, new toy truck, H pump project, 1000hp goal, 300K miles
93 XCLB auto, bone stock, 350K miles
93 XCLB 5spd, bone stock, 100K miles

Rx7man

OK, so here's a sneak peek...

digital pins 2 and 3 are for RPM sensors, I have them wired to pins 12 and 13 that I toggle on and off to simulate an input.

Analog pin 0 is wired to a 0-5V pot and simulates TPS position, this you will see in the serial monitor (115200 baud) when you sweep the pot.. you will see the raw TPS value, smoothed value, as well as the slope and rate of change of slope...

I commented out some parts in the Constants file that are incompatible with non-Mega2560's... specifically the definition of analog input pins A8-15

To play with the smoothing settings, look in the "Initialization" tab, modify whatever.. I haven't implemented "Multiplier" yet though.

I also just added a bunch of boolean checks in "MyVar.h" that can disable deadband and limit enforcing, as well as disabling the slope calculations to save some CPU cycles on variables that really don't need it.

You will notice I also only read my analog inputs once, but do a bit of stuff between reads.. It appears to be pretty steady, on my pot on A0 I get at most a 1 ADC point fluctuation, and that's to be expected really
'94 dually,  67/67 HE351VE, NV5600, ~600hp
'93 ECLB 47RH, new toy truck, H pump project, 1000hp goal, 300K miles
93 XCLB auto, bone stock, 350K miles
93 XCLB 5spd, bone stock, 100K miles

hakcenter

Semi complicated.

Remember division is slow, so avoid it and use bitwise if at all possible.
TS2009 Deḇarim 8:2
"And you shall remember that יהוה your Elohim led you all the way these forty years in the wilderness, to humble you, prove you, to know what is in your heart, whether you guard His commands or not.

Rx7man

it is, but unless you just happen to be dividing by 2, you gotta do it the long way...

I also read somewhere that as long as the code fits onto the unit, and can keep up with what it needs to do, optimizing more than that is a waste of energy... Of course you don't want to deliberately write bloated or inefficient code, but there comes a time when you shouldn't worry too much about it


Semi-complicated... that's because I'm not done with it yet :P
'94 dually,  67/67 HE351VE, NV5600, ~600hp
'93 ECLB 47RH, new toy truck, H pump project, 1000hp goal, 300K miles
93 XCLB auto, bone stock, 350K miles
93 XCLB 5spd, bone stock, 100K miles

hakcenter

#11
Division is so much slower, it isn't funny. If you land in a spot where you're doing a ton of division in 1 iteration, that could very well be where the super slow down is. It is multitudes slower.

You can do, divide by 6, multiply by 2. to get division of 3. So there are sneaky ways to do bitwise.

x / 3 = ((x >> 2 + x >> 1) << 1) as an example. ( x / 6 ) * 2

Which if you add up the time, 7,792 us is faster than 153,236 us

Faster code Fridays: Understand division and speed of operations | EngBlaze
Quote
[...]
Running the division test again, we reach completion in 153236 us, the same as our original case. This makes sense; if the compiler wasn't optimizing the division code to begin with, adding an empty assembly instruction wouldn't affect the compiled code at all.

For the multiplication test, each run now completes in 3896 us. Holy difference-maker, Batman. That's blazing fast. It also is much more realistic than the first case, suggesting that our compiler trick worked and the loops are no longer being optimized away.

The end result is that multiplication is 97.46% quicker, or alternatively, 39.3x faster than division in this case. Interestingly, results for addition and subtraction were exactly the same: 3896 us per test.
TS2009 Deḇarim 8:2
"And you shall remember that יהוה your Elohim led you all the way these forty years in the wilderness, to humble you, prove you, to know what is in your heart, whether you guard His commands or not.

Rx7man

If working with floats... is division still slower than multiplication?

I guess the best way to minimize divisions is to refactor your code to minimize them...

Such as 1/10+2/10+3/10 would be the same as (1+2+3)/10... However.. lets say it's (1.0+2.0+3.0)/10.0.. would you gain much by getting rid of the division, and writing (1.0+2.0+3.0)*0.1 ?


Oh, I forgot to mention.. I found my terrible slowdown... it's amazing how much you can optimize your code and still have it run slow when you forgot about a delay(100) in it!!


Meanwhile, I've made a very preliminary  VGT logic, which doesn't do braking yet..


//the following values are all wild guesses..  There's a CERTAINTY that some will need to be negative, and some may be zero
//for example with the EGPV we aren't concerned about what the actual number is, but rather to maximize it, in which case
//the slope and rate of change of slope may be of greater interest... at least in theory

float MAPsensitivity = .1;
float MAPslopeSensitivity = .1;
//float MAPdeltaSlopeSensitivity = .1; //I don't think I'll need this here

float DPRsensitivity = .1;
float DPRslopeSensitivity = .1;
float DPRdeltaSlopeSensitivity = .1;

float EGPVsensitivity = .1;
float EGPVslopeSensitivity = .1;
float EGPVdeltaSlopeSensitivity = .1;

float TSSoverRevSensitivity = 10;
float TSSunderRevSensitivity = 10;

float TSSoverRevSlopeSensitivity = 10;
float TSSunderRevSlopeSensitivity = 10;

float TSSmaxShaftSpeed = 150000;
float TSSminShaftSpeed = 60000;
int GetNewVGTposition() {


  //This is the main component controlling the VGT, thus the largest values ought to be for this
  float BoostCompensation = (MAP.Value() - DesiredBoost.Value() * 4) * MAPsensitivity + (MAP.Slope - DesiredBoost.Slope * 4) * MAPslopeSensitivity;

  //Seems as though Drive pressure ratio is a big deal, and should be kept around 1.0, so I'd expect moderate values
  float DPRcompensation = (1 - DPR.Value()) * DPRsensitivity + DPR.Slope * DPRslopeSensitivity + DPR.DeltaSlope * DPRdeltaSlopeSensitivity;

  //Exhaust gas pressure*volume/nozzle size... the hope here is to maximize it thus maximizing acceleration of the turbine
  float EGPVcompensation = EGPV.Value() * EGPVsensitivity + EGPV.Slope * EGPVslopeSensitivity + EGPV.DeltaSlope * EGPVdeltaSlopeSensitivity;



  float TSScompensation;
  //A little more complicated is the turbine shaft speed limits
if (TSS.Value() > (TSSmaxShaftSpeed -10000)) {
    TSScompensation = mapf(TSS.Value(), TSS.MaxValue-10000, TSS.MaxValue, 0, TSSoverRevSensitivity);
    TSScompensation += TSS.Slope*TSSoverRevSlopeSensitivity;
   
}else if (TSS.Value() < TSSminShaftSpeed){
    TSScompensation = mapf(TSS.Value(), TSSminShaftSpeed, TSSminShaftSpeed/2, 0, TSSunderRevSensitivity);
    TSScompensation += TSS.Slope*TSSunderRevSlopeSensitivity;
}
   
  int NewVGT = VGT.Value() + BoostCompensation + DPRcompensation + EGPVcompensation + TSScompensation;

  VGT.UpdateValue(NewVGT);
}

float GetNozzleSize(int VGTposition) {
  //returns the nozzle size in Cm^2
  //so a VGT position of 458 will return 15
  return mapf(VGTposition, 40, 960, 25, 3);
}



And on the bright side, I'm starting to be able to write more than one line of code at a time without screwing up the syntax... though to go as far as to say "C++ is growing on me" would be a stretch.
'94 dually,  67/67 HE351VE, NV5600, ~600hp
'93 ECLB 47RH, new toy truck, H pump project, 1000hp goal, 300K miles
93 XCLB auto, bone stock, 350K miles
93 XCLB 5spd, bone stock, 100K miles

hakcenter

The  AVR simply doesn't have any division help at all,  regardless of type.

Floats / doubles same size and speed of eacb other...  I think floats end up still being as accurate as doubles actually.


But ya if you're doing division of 10,  x >> 4 + x >> 2 + x >> 1.

I don't think fraction multipication is any faster...  Would have to time it. And add an empty opcode to make sure code optimiser ignores to verify speed
TS2009 Deḇarim 8:2
"And you shall remember that יהוה your Elohim led you all the way these forty years in the wilderness, to humble you, prove you, to know what is in your heart, whether you guard His commands or not.

Rx7man

According to the arduino documentation I read, floats ARE doubles.. Same structure with a different name..

I wish they had 16 bit floats though.. that would be good enough for me in ALL my calculations.. ALAS.. no such luck.. I'll have to write my own!


Meanwhile, we had some rainy weather here and I took advantage of that to work on more code...
Trailer brake controller is done.
VGT control is done, but evidently will need tweaking.. lots of it
VGT has normal mode, warmup mode (increases backpressure), and jake mode

AFC control is done.. again lots of tweaking will need to happen
Cruise control is in progress, will be done once I stop surfing... I'm getting the hang of these outputs.. they all have PID controls on them and feedback loops, greatly facilitated with the "MyVar" class that calculates slope over time regardless of when you're updated it (it keeps track of the last update time).

I'll post an update tonight.
'94 dually,  67/67 HE351VE, NV5600, ~600hp
'93 ECLB 47RH, new toy truck, H pump project, 1000hp goal, 300K miles
93 XCLB auto, bone stock, 350K miles
93 XCLB 5spd, bone stock, 100K miles