H-Drive Programming

I figure I might as well throw in my two cents. The code (at least for my team’s untested implementation) will look a lot like RobotDrive’s mecanumDrive_Polar/Cartesian.

  1. You want to take the joystick degrees and break it up into x and y vectors (kind of like what you do in physics).
  2. Use the magnitude to compute the size of the x and y vectors.
  3. Divide by the original x and y vectors so that you can bring your magnitudes back to a scale of -1 to 1. Without this last step, you robot won’t be able to go diagonal at max speed.

If you extend or emulate mecanumDrive, you will also want to make some adjustments for the different amount of force you are capable of providing in each direction. If you turned all four tank wheels forward, and the two strafe wheels to the right, you wouldn’t go off in a 45-degree angle right of forward, but a 26-degree angle right of forward (assuming equal weight loading on each wheel and equivalent wheels all around, ignoring differences in roller cross-friction). For a classic 5-wheel H-drive with the same assumptions, the answer would be only 14 degrees from forward.

Huh, I did not think about that at all! Looks like I’ll have to bring the programmers back to the white board.

And that is why I’d rather post my design early than keep it secret.

Wait, where is this said post? I think we got it be using a ratio relative to the x-axis, but I would like to know if there is a better way. Our way is hacky, but should do the trick.

No, I am not familiar with anyone else having emulated HolonomicDrive with an H-drive. I was just remarking that the value of sharing and getting more eyes on your idea usually has better payoff than keeping it secret does.

I can think of several solutions to this issue, none of them perfect. They pretty much come down to variations on:

  1. Accept the fact that left to right strafing is at a lower speed and work through it in driver practice
  2. Reduce the power on the tank drives (not recommended as an only solution; you’d be giving up the reasons for selecting H).
  3. allow some way to switch between these two, or one of these and regular tank-drive or tank-drive-plus-strafe (or any three or four)

So we ended up doing is making our own Gaussian function to adjust the y speed based upon the angle of the x and y vectors from the joysticks. The major downside is that we won’t be able to go diagonally at “full speed,” but as you said, this may be something we’ll just have to accept.
We should be able to actually test this approach on tomorrow and I’ll try to update this post with the result.

Gaussian function?

Does sound strange. From context, I believe they’re planning to “amplify” the strafe by a factor of four at low values so that response is essentially isotropic, and use a Gaussian taper on the strafe axis so that at large values of strafe they’re back at a multiplier of one so that response on the main drive axis is four times as good as on the strafe axis.

Yes, exactly this.
So I wasn’t there when we actually tested the drive train, but from my understanding, it seems like we are having dinner center of gravity issues that essentially messes with it values, so we’re going to wait for the elevator to be put on before we do more extensive testing and tuning. But it went reasonably well for the most part.

@kinganu123: Is the following a correct interpretation of what you meant?**

**Let X be the value of the joystick strafe axis,
in the range 0 to +1 for strafe right.

Let G(X) be your Gaussian taper function, such that G(0)=4 and G(1)=1.

Let Xt(X) be the tapered value of X: Xt(X)=X*G(X).**
If so, would you be so kind as to post your G(X) function.

Our formula is e^(gaussianInput^2/-.18) where the gaussianInput is the polar degrees from 0 to pi where pi/2 is 0, 0 is 1, and pi is -1.
I’m putting the code below, in case what I’ve typed doesn’t make sense, here’s the code:

	centerCurrent = xAxis;
    	 double gaussianInput=angleToGaussianInput(Math.atan2(yAxis, xAxis));
    	 leftCurrent =gaussianConversion(gaussianInput)+rotate;

	private double gaussianConversion(double gaussianInput) {
		return Math.exp(Math.pow(gaussianInput, 2)/(-.18));

	private double angleToGaussianInput(double rad) {
		return (rad-Math.PI/2.0)/(Math.PI/2.0);

OK, to see if I’m understanding your formula correctly, can we take a simple numerical example?

Suppose the driver pushes the joystick to Y=1, X=0.5, rotate=0. What will be the “tapered” value of X?

Oh, sorry, I meant to say that the y-axis is the one that is modified, not the x.
So, x will be .5 and y will be 0.9702, or e^(((atan(1/.5)-pi/2)/pi/2)^2/-.18).

I believe you left out a pair of parentheses:


This is quite different from the previous description.

See attachments.

Have you guys driven with this yet? How is it working?


Yes, I didn’t realize that he specified the strafe axis, which would result in the confusion.
And we haven’t been able to test it yet since the mechanical guys are currently assembling the elevator on the robot, so we’re losing a few days on testing and tuning this.
Also, I thought I’d note here that we tweaked the code above, in case anyone else wanted to try our stuff out, because I noticed a slight flaw in getting the y values

centerCurrent = xAxis;
double gaussianInput=angleToGaussianInput(Math.atan2(yAxis, xAxis));
double scaledGaussianOutput=yAxis*gaussianConversion(gaussianInput)
leftCurrent =scaledGaussianOutput+rotate;

It’s not just the axis; the gains are quite different from what was described.

I’m a math enthusiast, so don’t take this wrong, but why the complicated function with exponential and inverse trig? Wouldn’t a simple linear interpolated 6x6 LUT do the job… and be far more easily tuned to compensate for actual behavior once you’ve done your testing?

So we do have a simpler version coded up with some sort of linear adjustment instead of this one (I had some of the other students write that as a backup), but the main reasoning behind this method was to allow y to increase significantly to make up the lost y speed as the driver went at a smaller angle (with respect to the front of the robot).

*Instead of using arctangent and exponential functions, why not just multiply the X axis by a factor of 4, clip it at 1, and normalize the resulting X,Y pair?


*void adjust(float Xj, float Yj, float *Xa, float *Ya){

	float mag;

	if (Xj<=-0.25) *Xa=-1; 
	else if (Xj<0.25) *Xa=4.0*Xj;
	else *Xa=1.0;

	// normalize adjusted values:
	if (mag>1) {*Xa/=mag; *Ya/=mag;}

See attached Excel spreadsheet. The 100 tab shows an overview of adjusted XY pairs (cyan cells) for the entire first quadrant. The 25 tab zooms in for a closer look for small joystick values.

The attached plot shows a family of Xa vs Xj curves for Yj=0 to 1.

Or you could make the X gain a function of Yj, like so:


*void adjust(float Xj, float Yj, float *Xa, float *Ya){

	float mag, gain;


        if (*Xa>1) *Xa=1;
        else if (*Xa<-1) *Xa=-1;

	// normalize adjusted values:
	if (mag>1) {*Xa/=mag; *Ya/=mag;}

See attached plots for Xa vs Xj for Yj=0 to 1
and Ya vs Yj for Xj=0 to 1


XY.xls (47 KB)

XY.xls (47 KB)