Cubic drive

Hi, my team was trying to write a cubic drive function for the drive for more control in operator control

We are just cubing the joystick value (^3) and scaling it back to -127 to 127

This is the code we have so far

Right now it is not working as intended.
we get 0 drive movement unless the joysticks are at 100% or -100%
then the robot would jerk to ±100% power

We were wondering if it has something to do with the “unsigned long” of the variable as the value can go up to pretty high numbers or low decimals

right now both the ‘cubic formula 1’ and ‘cubic formula 2’ (the scaling formula) do not work and preforms as described above

note that the above was written and compiled using PROS

Thanks for your help in advance! :slight_smile:

The problem is the
joystick/127
this is defined as an int normally so when divided by an int it remains an integer. This means it cuts off the decimal so .95 becomes 0.
This should fix it

(channel2 * channel2 * channel2*1.0) / 2048383;

It thinks your evaluation should be an integer answer up until the point you tell it you want something else. Multiplying the number by 1.0 makes it a double so that the division gets you an accurate decimal place.

Also shouldn’t the number be
127^2 rather than 127^3 when u simplify the times 127 or does Pros take in decimal answers into its motor power function

Thanks! will try that during the next meeting

How are you getting -100% if both mapped variables are unsigned?
Have you tried splitting up your multiplication/division, and printing the results along the way in order to see where the problem arises?

~Jordan


that is true…

I will try to change it all to “float” as you suggested last time

and try what Tabor said
And if that still doesnt work, have lots of prints :slight_smile:

Thanks!

Let’s see, the joystick is +/- 127.

127^3 is 2,048,383
-127^3 is -2,048,383

2,048,383 / 2,048,383 = 1
-2,048,383 / 2,048,383 = -1

ANYTHING LESS THAN FULL STICK would result in…

2,048,382 / 2,048,383 = 0.99999951181005 which will round down to 0
-2,048,382 / 2,048,383 = -0.99999951181005 which will round down to 0

A.k.a less than 1 which will immediately be rounded down to 0 because this is a non-floating type.

Tabor is wrong, OK mostly wrong. You are dividing by the wrong number, fix that and you’re good regardless of any miniscule casting errors.

I believe 16,129 (2,048,382 / 127) is the number you want.

127^3 is 2,048,383
-127^3 is -2,048,383

2,048,383 / 16129 = 127
-2,048,383 / 16129 = -127

Here’s some untested but pretty code that should work, or at least be really close to working…


// 127^3 is not enough to overflow an int, no need for long
int mapped2, mapped3;

// Badly implemented pow function, probably several ways to do this faster
int pow(int value, unsigned char power) {

	// Init result to value
	int result = value;

	// Loop for power - 1 many times
	for(; power > 1; --power) result *= value;

	return result;

}

while (true) {

	int pot = analogRead(8);
	printf("%d\r\n", pot);

	mapped2 = pow(joystickGetAnalog(1, 2), 3) / 16129;
	mapped3 = pow(joystickGetAnalog(1, 3), 3) / 16129;

	printf("%d\r\n", mapped2);
	printf("%d\r\n", mapped3);

	// drive functions
	motorSet(motor1,	mapped2); // right drive back
	motorSet(motor2,	mapped2); // right drive front
	motorSet(motor10,	mapped3); // left drive back
	motorSet(motor9,	mapped3); // left drive front

}

Note that PROS doesn’t define true properly or something so I have these lines in a header file I tend to include everywhere…


#define true 1
#define yes 1
#define false 0
#define no 0
typedef short boolean;

Also Tabor, the correct way to fix a cast issue is to write a … cast, so…


int foo = 43, bar = 785;
double baz;

baz = foo * 1.0 / bar;		// Going to coin this a Tabor-cast
baz = (double) foo / bar;	// How the rest of the known world does a cast

Okay so Cody explained exactly what you need to do a lot better than I did. I did try and say the number was too large but Murdo stating it was running at full power led me to believe that Pros was accepting in motor values of a decimal value. In which case the casting was rounding away all the data he wanted.

Also I got a good laugh out of Tabor casting. :smiley:

I get it, yeah I came to the conclusion that murdomeek was wrong about the full power part. He can confirm when he reviews this. He should have been getting 0 power until his stick went to either -127 or 127, then he’d get 1/127 power.

ah, yes i see what Cody is saying

which is strange because 1/127 power would not move the robot
and it was (perceived as) full power…

Ill test it tomorrow during out meeting and update the results! :slight_smile:

Here is some code that I wrote. It allows the user to select the type of graph that they want (a cubic would be a 3, a parabolic would be 2, or you could even say 2.5)

byte joystickExponental(TVexJoysticks joystick, float graph)
{
	if (vexRT[joystick]>0)
	{
		float joyPercent=vexRT[joystick]/127;
		return pow(joyPercent,graph)*127;
	}
	else if (vexRT[joystick]<0)
	{
		float joyPercent=vexRT[joystick]/-127;
		return pow(joyPercent,graph)*-127;
	}
	else
		return 0;
}

//usage(in robotc)
motor[1]=joystickExponental(Ch2,2.5)

note that this is robotc code, I did not have time to convert it over

I have only thought of it but never done it. Cody has already answered the question perfectly well, but as I am a huge nerd and I get so excited wen I saw it, I have to comment on this one…
So what you want to do is to establish a cubic relationship between joystick input and motor output, which is amazing. It allows generally slower, but not crippled speed for drivers. Personally I would have used a quadratic model, but definitely a cubic model gives you much more freedom tweaking the graph.
One thing you need to mind is, no matter what algebraic model you use between joystick input(x) and motor output(f(x)), the function f(x) has to pass through points(0,0) and (127,127) and remain positive in this interval. The negative values will correspond since a cubic function you are refering to has a vertex at origin, thus being an odd function.
The general cubic formula is f(x) = ax^3+bx^2 +cx+d. To obtain a definite graph, meaning to solve for a,b,c,and d, you need four coordination pairs. However you needn’t get that complex. In your case, you just simplified the model to f(x)=ax^3, by assuming b and c are zero. This is a perfectly feasible shortcut. By plugging the (127,127) coordination pair you have above, you get equation 127=a*127^3. Solving this equation yields a=1/127^2, thus cody says that your cubed value needs to be divided by 127 squared. This is the basic theory.
If I were you, I would have experimented with different b and c values as well to obtain an absolutely bestfit function. Or with a potentiometer I might adjust the graph concavity manually using some calculus. But great job doing such interesting programming!

Yeah, this graph pretty much sums it up. In red are the boundaries of values that are meaningful to us, -127 to 127 on the X axle for the joystick, and -127 to 127 on the Y for the motor output.

In blue is the f(x) = x^3 / 127^2 path, when x = 127, y = 127. So basically you want that intersection.

The wrong f(x) = x^3 / 127^3 does the same thing, problem is the intersection at x = 127 is y = 1.

If you wanted a -1 to 1 output, that’s perfect. But we don’t in this case. There are places where that is useful this just isn’t one of them.

What CCA#3921 is explaining is that you don’t have to settle for a x^3 function. There are many other functions that could fit this need nicely and he explained how to solve for them.

This is one of those when math actually meets programming cases. In this case it’s better to think more like a mathematician than a programmer at first. Graph the curve, look at it, think about the input and output THEN figure out the code.

-Cody

Do keep in mind that the actual speed of the 393 motors is different from the “power” control value that you give to them (see this thread). By simply cubing the control values, you may get some undesirable results. For example, from tests I did on 24C last season (seen in the linked thread), I found that with fully charged batteries, our robot would not begin moving until we gave the drive motors a “power” control value of 21. If you were to use a cubic function based directly on the joystick control values, the motors would not be given a power of 21 until the joystick reaches a value of 70. 70 is over halfway to 127, so you essentially have a “dead zone” over half of your joystick’s control. If you check out the thread I linked to above, you can read more about the experiments I did and what I used as a solution to the problem.

~Jordan

https://vexforum.com/showpost.php?p=303628&postcount=7
This is jordan’s graph involving what he was talking about. I just saw it this morning and I am completely frustrated. For years I and many of us have been assuming the relationship between motor control value and motor speed is linear and this thread completely blew my mind. It is logarithmic! Thanks so much for letting me realize this. This means the idealistic theory of rerunning based on joystick input is basically inpractical.
Directly replying to the thread, maybe you need some little more work to make the robot work as you wish. Same thing, I would use a potentiometer to change the exponent value to manually determine the best concavity. The formula I wish to use is f(x)= 127^(1-a)*x^a, where a is a positive integer value determined by a potentiometer or some sort of input you can manually adjust. As a gets bigger the graph still passes (127,127) and (0,0) but it has a more sharp curve. As you construct a function that is concaving down it might be able to cancle out the weird concavity of the original logarithmic function, and you might get what you want. This is the easiest way I can approach your need.
- Martin from team 3921

I feel that a cubic drive has too small of a response range.
You can use this calculator to see the graph for different powers.
https://www.desmos.com/calculator/akts2sltu3