Programming Mecanum Wheels

Not a Problem…

Maybe I missed it, but when using an Example Number, say, “For Example, a DEADBAND of 20 is used in certain cases.” or “Your DEADBAND value is dependent upon you Gear Ratio, I used 20 for this Example”.

I expect that this forum will be around for a long time, so having Complete and Accurate information contained in a Thread is a high priority of mine, besides, Mecanum Wheels are my Interest, Right Now… :wink:

**As an Aside, but Directly related to this: **
Think about when you were searching for answer to some question on the Internet… How many times do you find just a piece of the problem, that then leads you to other pieces, that occasionally leads you to the Whole Solution?? Do you sometimes wish, one of those first 5 Threads, had the Complete Solution, especially when a Deadline in looming, and you have been up for 18 Hours, and the Dew or Rock-Star is running Low??

For an Example, yesterday I was searching for Information on the CSF/LFD Firewall installed on a Debian 6.0.3 system.
Because, I was doing some reconfiguration on the Server, and it *appeared *that I killed the Web Server, which did not make any sense after rebooting and saw everything running… Then is occurred to me, that maybe the Firewall was messed up, disabling it “fixed the problem”. But leaving the Firewall down is not the answer for a Web Server, so into the CSF/LFD documentation I went… Most Documentation is Pretty Bad, so the first 25 or so links of Uses Help, which I expected to be the Most Helpful were not very helpful at all… Strangely Enough, the actual Documentation was the exact help I needed, much to my Surprise… :eek:
It turns out, if you have Multiple Failed SSH Logins to your Server, your IP Address get written to the csf.deny file, and ALL your Interactions with that Server, from your IP Address are blocked, HTTP, FTP, SSH, etc… After clearing my IP address from the csf.deny file, it all worked again… WOW, Bullet Dodged!!

You might not realize it, but the Vex Forum is Publicly Readable, and ALL your Posts are visible On the 'Net. Your words are bound to become part of someone’s Search Query Results. Imagine your “Good Karma” quantity increasing because of your Complete and Accurate posts… ( If you believe in Karma, or that someone is keeping track of things in The Big Picture… )

As an Aside/Aside, a Google Search of “Vex Forum marko easyc” netted about 50 Links, including this page, that I have never seen before on the Vex Forum, Show Groups.

So, to Recap, Your Words are Forever on the Internet, and if you Lead someone to right destination, that someone, or someone else is going to remember…

Try to Leave the World a Better Place than you found it… :wink:

( P.S. I see you were checking out the RoboShop Rover… That’s OK, I won’t tell your Vex Robot… :wink: )

Ok, thanks MarkO, that’s a good Idea, and I’ll edit my post with your “disclaimer”. I don’t know who that “RoboShop Rover” comment was directed at, but if it was directed at me, I have absolutely no idea what you’re talking about.

dontworryaboutit, a really simple nonlinear mapping would be:


int chAddition = Ch3 + Ch1 + Ch4;
chAddition = abs(chAddition) > 127 ? sgn(chAddition) * 127 : chAddition;
int motorValue = (int) (pow(chAddition, 2) / 127);
motor[wheel] = motorValue > DEADZONE ? sgn(chAddition) * motorValue : 0;

Thanks, but what does the bolded line do? Why is the “int” in parentheses? And what is this “pow” function? Why is its second parameter 2?

Sorry for all the questions, but I have a grand total of 6 months of experience with RobotC.

As for MarkO’s interesting post, when I search “robotshop rover magicode” on Google, I get this:

Which comes from this website.
Is Google having a bad day or something? This is just weird…

In engish, that line says: motorPower = ((chAddition squared) divided by 127) converted to an integer.
the pow function: pow(base, power) raises the base to the power.
The (int) typecasts the float returned by the pow function and division by 127 to an int.

Ahh… D-E-N-I-A-L… Look at your Post #20, in the Quoting of me… See it Now??? Don’t Worry, your Secret Shopping is safe with us…

Cool… I will need to Crunch Some Numbers…

That’s OK… We are all here to learn… EasyC has a few Functions like that as well…

Realize that ‘C’ ( and C++ , Java IIRC ) are a Basic Language, with a lot of Library Support.
‘C’ was envisioned to replace Assembly Language, which ( if your lucky has Addition, Subtraction, Multiplication and Division, and if unlucky has only Addition ) has very Basic in Math routines because most processors only have Basic Math Routines in their Op-Codes. All upper level Math Function and String Handling are done as Libraries, written with the Basic Language.

Hot Diggity!!! That Was fast!!! magicode posted that today at 07:42 PM (CDT UTC - 6) and Google Already Indexed It!!! ( See Post #20 )

Oh, wow, it still took me a few minutes to see that. I was shopping around for an Arduino before, and it must have copied over. Sorry about that. I changed it, so people don’t have WTF moments when reading this.
If you’re crunching numbers, you’ll probably see that the curve increases too slowly for many applications. If I were using this for a drivetrain, I would probably do something like this:


#define EXPONENT 1.3
const float divisor  =  pow(127, (EXPONENT - 1));

int chAddition = Ch3 + Ch1 + Ch4;
chAddition = abs(chAddition) > 127 ? sgn(chAddition) * 127 : chAddition;
int motorValue = (int) (pow(chAddition, EXPONENT) / divisor);
motor[wheel] = motorValue > DEADZONE ? sgn(chAddition) * motorValue : 0;

This would probably work nicely for a 1:1 drive, but as you said, the variables will change based on gear ratio and configuration.

Oh that makes a lot of sense now :slight_smile:

Square the input to get the output…very nice. Our programmer preferred to do a kind of stepped linear thing, and I never understood why not just use a power function for mapping. So it can actually be done I guess.

Yes… We could see that you were shopping… Around…

I’m not offended… But Google already Indexed it, so you better not let you Vex Robot use Google for a while… It might get Jealous :wink:

Ahh… This is Good Stuff… Very Good Stuff…

Are your Variables [FONT=Courier New]Ch1[/FONT], [FONT=Courier New]Ch3[/FONT], and [FONT=Courier New]Ch4[/FONT], Signed Integers as well???

I believe that Most ‘C’ Compilers would Promote the Variables [FONT=Courier New]Ch1[/FONT], [FONT=Courier New]Ch3[/FONT], and [FONT=Courier New]Ch4[/FONT] to Signed Integers, before doing the Addition and assigning them to the Variable [FONT=Courier New]chAddition[/FONT], so there most likely would not be any High Order Truncation occurring if you had them as Signed Char variables.

So do you Calculate the [FONT=Courier New]EXPONENT[/FONT] and [FONT=Courier New]DEADZONE[/FONT] values, or do you just use Iterative Testing??

Does that mean I shouldn’t equip it with a wifi sensor?

I just used those as placeholders for adding the values of the channels together, as that is what dontworryaboutit used in this post. These would be replaced by vexRT[Ch1] + vexRT[Ch3] + vexRT[Ch4], or some combination of those, depending on what wheel you’re driving.

I just roughly calculated what seemed like good values to go by. I haven’t actually implemented this, so I have no idea if these values would work well in the real world.

motor[backLeft] = vexRT[Ch3] +vexRT[Ch1] + vexRT[Ch4];
motor[backRight] = vexRT[Ch3] -vexRT[Ch1] - vexRT[Ch4];
motor[middleLeft] = vexRT[Ch3] +vexRT[Ch1];
motor[middleRIght] = vexRT[Ch3] -vexRT[Ch1];
motor[frontLeft] = vexRT[Ch3] +vexRT[Ch1] - vexRT[Ch4];
motor[frontRight] = vexRT[Ch3] -vexRT[Ch1] + vexRT[Ch4];

mine is a 6 motor drive, 4 mecanums and 2 omnis. this is so i can still go as fast while strafing

“That would be Prudent”.

Oh, OK… You use the Immediate Value of the Joysticks to get your Value and Move On with the Processing…
I wasn’t sure if you needed the “raw values” any place else in your Code, like for Graphing or Trending.

“Trust, but Verify”.

I am planning on putting all these questions to Good Use, in the next week… :wink:

( One Presidential Quote, One Anti-Presidential Quote, in One Post… )

Are you using the 4" Omnis, or the 2"?? I just got a Second Pair of 2" Omnis, so I have 4 Total now…

Hmm… I could try that with Double 2" Omnis, on a Single Motor, on each side, in the Middle of each Side… Like a Square Bot, with Four more Motors, one on each Corner, driving One 4" Mecanum Wheel.

Thanks for the Code Snippet!!!

So if I wanted to incorporate the MINPOWER I was talking about earlier, I would do this:

const int MINPOWER = 20;
const int DEADZONE = 2;
const float EXPONENT = 1.3;
const float DIVISOR  =  pow(127, **EXPONENT**) / (**127 - MINPOWER**);

int chAddition = vexRT[Ch3] + vexRT[Ch1] + vexRT[Ch4];
chAddition = abs(chAddition) > 127 ? sgn(chAddition) * 127 : chAddition;
int motorValue = (int) (pow(**abs**(chAddition), EXPONENT) / DIVISOR + **MINPOWER)**;
motor[wheel] = motorValue > DEADZONE ? sgn(chAddition) * motorValue : 0;

correct?

I’ve looked over your previous post as well as this, but I still don’t quite understand what you’re trying to achieve by using MINPOWER.

One thing to consider when using math functions such as this is that they can be quite computationally expensive on embedded systems. It’s actually not too bad with ROBOTC, perhaps 100uS, but one common optimization is to pre-calculate everything and create lookup table (LUT) when the code first starts and then use an index into that in the main control loop. There is one example where I did that at the end of this post. You are trading memory for speed.

Thank you for mentioning this; computing at least 4 things to the 1.3rd power each iteration was making me sort of queasy.
Here’s a little look up table for the algorithm I described before:


#define EXPONENT 1.5
#define DEADZONE 7
//Both of these are example numbers.
const float divisor  =  pow(127, (EXPONENT - 1));
int chAddTable[255];
for(byte i = 127; i >= 0; i--){
  int motorValue = (int) (floor(pow(i, EXPONENT) / divisor));
  motorValue = abs(motorValue) > DEADZONE ? motorValue : 0;
  chAddTable[127 - i] = -1 * motorValue;
  chAddTable[127 + i] = motorValue;
}

You can directly get the motor power by doing:
int chAddition = add channels here;
chAddition = abs(chAddition) > 127 ? sgn(chAddition) * 127 : chAddition;
motor[wheel] = chAddTable[chAddition + 127];

  • Disclaimer: I haven’t actually tested any of this, and it may perform horribly/not work.

There is some range of power near zero that, when you send it to the motors, is too low of a power for the robot to actually move (too much friction). The MINPOWER makes it so that when you move the joystick just past the DEADZONE, the robot will actually start to move. It really helps the drivers who are trying to do very fine movements because they don’t have to keep moving the joystick forward until the robot finally starts to move; they know that the robot will start to move if they just barely push the joystick forward.

This seems like a ridiculously good idea.
A few questions though:
What does this do?

for(i=0;i<128;i++) {
stuff }

My guess is that it increments i (starting at i=0) each time it goes through the loop until i gets to 127, at which point it stops running the loop. Do you have to declare i? Or does it declare it automatically when you run that loop?

What is the test6 in that thread actually doing? I have no idea how tables work in RobotC (or any other language for that matter, except my graphing calculator’s language, which hasn’t been a tremendous help for learning RobotC, I must say).

My justification to myself for asking these very noobish questions is that hopefully someone else can learn from the answer in the future.

Thanks!
I think I understand what this is doing. Would you put the table-creating (snipped) part in pre-auton in competition code?

And concerning:


for(byte i = 127; i >= 0; i--)

Will i start at 127 when it goes through the loop for the first time? Or 126? Meaning does it do the – or the ++ at the end of the loop or at the beginning?
Is the “byte” necessary?


    int x1 = THRESHOLD(vexRT[Ch4], 15);
    int y1 = THRESHOLD(vexRT[Ch3], 15);
    int x2 = THRESHOLD(vexRT[Ch1], 15);
    int y2 = THRESHOLD(vexRT[Ch2], 15);
  int yComp = ((abs(y1) > abs(y2)) ? y1 : y2);
  int turnComp = ((abs(x2) == 0) ? x1/2 : x1);
  int strafeComp = ((abs(x1) == 0) ? x2/2 : x2);
    output_Left_Front = LIMIT(127, -127, yComp + turnComp + strafeComp);
    output_Left_Rear = LIMIT(127, -127, yComp + turnComp - strafeComp);
    output_Right_Front = LIMIT(127, -127, yComp - turnComp - strafeComp);
    output_Right_Rear = LIMIT(127, -127, yComp - turnComp + strafeComp)

That’s exactly what I do. 50% power if both joysticks, 100% power if only one.

As an aside, what I found with the mecanum wheel robot I drove at worlds, was that driving diagonally cause the drive motors to overheat and trip their breakers VERY quickly, especially when coming into contact with other robots. This more than likely had something to with the rather poor weight distribution (the arm was so long that most of the weight of the robot was over the front wheels). My solution to this problem was to “lock” the strafe, such that if the average of the two horizontal channels (1 and 4?) was above a tolerance, the robot would ONLY strafe (at a power given by the average of the two channels), otherwise the robot would control and drive like a normal tank. This greatly reduced the risk of motor overheating. The obvious downside to this approach is that it limits the degrees of control you get, but on the whole it was worth it.

As I mentioned, the robot could have been built better and would have likely not suffered from this problem, but if you ever run into it this is a potential solution.