For the first time our team is experimenting with holonomic drive (4 omnis placed at 45 degree angles relative to the frame) and it has fallen to me to code the movement. I have a system in place, but it came to my attention after the initial code was written that different people have different opinions as to how the joystick movements should coorelate to robot movement. As such I’m reaching out to teams who have done this in the past…how’d you guys do it?
At the moment we’ve got a two joystick design. One stick changes position but not orientation. The robot maintains “front” while moving in whatever direction the joystick tells it to. The second stick controls spin while maintaining position (they won’t mix at the moment). This was the first control method that came to mind for me since it allows us to make use of all three degrees of freedom to the greatest extent, but our coach believes that it won’t be natural after our years of straight drivetrains.
The other suggested method of control is one sticck that drives much as arcade has in the past, but accompanied by a second stick for strafing to take advantage of our added range of motion. Our coach thinks this will be more natural as a transition, but I don’t think it has the same potential since we won’t be able to strafe will moving forward.
So for teams who’ve been successful in the past, how did yours move? Did you find any particular strengths or weaknesses in the control system? Features you wished you’d had? Thanks in advance for the help!
I’m trying to make sense out of the above. Did you add extra code to purposely zero-out the first joystick when the second one is giving a command (or vice versa)? If so, why did you think that was necessary?
When we did it in the past (not for competition) we programmed it the first way you said, with one joystick that controls movement and the other to control rotation. This is actually very intuitive since which ever way you move the joystick, that’s the direction the robot will move in. Driver’s should adapt to the new control method quite easily.
My personal favorite from the little I’ve played with Omni-Drive Prototypes is Solution 1. It’s probably the easiest and most straight forward way to team someone how to drive and Omni-Drive.
Solution 2 also works extremely well - especially if you’ve got a bunch of gamers on your team.
Solution 1 (3 axis) is somewhat difficult to use because you may find yourself twisting the joystick slightly during normal operation - the “twist” motion isn’t too intuitive for me personally.
Solution 2 (one stick for motion one for reorientation) is my personal favorite.
With all control systems, the number one rule is driver preference. What they feel best with is what they get.
Ditto what Chris said about #1 and #2 and driver preference.
Since this is your first experience with holonomic, I hesitate to bring this up, but some have found that using the gyro as a fixed-angle reference to provide “field-centric” control (sometimes aka “driver-centric control”) provides a superior driver experience with a quicker learning curve.
We used mecanum last year which is pretty much the same from a control point of view. We tried both a single 3-axis joystick and 2 dual axis. We ended up going with 2 dual axis, using one for position control, and one for rotation because that was what our driver prefered, I personally liked the 3 axis better. So I would recommend trying both and just seeing what feels more natural, if your code is writen well, it should be trivial to change.
Also, I would STRONGLY recommend that you look into using a gyro to compensate for robot rotation. Basicly, when you push the movement stick forward, the robot knows how it is oriented and will always move away from you. Likewise, when you move the stick left, it will always go left relative to the control station. This takes just a few minutes to get used to, and is extremely natural after that. If you go with this it is very important to have a button that turns it off also because sometimes the gyro gets messed up.
I agree with the button, but for a different reason. When the robot is very close to and pointing straight at an object that you want to drive up to, it might in some cases be more intuitive and accurate to push the joystick straight forward.
As for the gyro losing calibration, I suggest a second button you can use to re-calibrate the gyro. Rotate the robot to the zero degree orientation and momentarily press the button to tell the gyro “here, this is zero degrees”.
We ran mecanum last year as well and very pleased with it. As stated above, solution #1 is what we went with. Early there was a problem with inadvertent spinning of the robot, so we programmed a deadband to avoid spurious rotations. We also found we had to limit the rate of rotation. If we spun at max speed, this is when gyro error would creep in. I forget the response limit of the gyro, but it was obvious we were exceeding it. I would also concur with adding switches (joystick or other hardware) that allow re-zeroing and canceling gyro compensation completely. Drivers didn’t use it often, but it did prove convenient a few times.
I wasn’t entirely sure how it would mix. I programmed it in the method that I first came up with, which consisted of converting the joystick inputs to a polar coordinate scale (rotated 45 degrees) and mapped each set of motors across from one another to either the X or Y coordinate of the point (respectively). This doesn’t exactly lead to an easy blend with spinning as far as I can tell. While it’s something I’ll probably try to tackle later, it’s not done YET.
I saw a system similar to that several years ago with swerve drive actually. We’re tossing on a gyro for the camera anyway, so I don’t see any reason we wouldn’t at least attempt that system if you say your drivers were successful with it.
And thanks all for the responses, I’ve been thinking CD was down for a few days now when my Droid wouldn’t access it XD.
The drivetrain you described in your original post (4 omnis placed at 45 degree angles relative to the frame) is holonomic, which means it can perform fore/aft (Y), right/left (X), and rotate (Z) motion simultaneously, if the inverse kinematic calculation for wheel speeds is done correctly.
I programmed it in the method that I first came up with, which consisted of converting the joystick inputs to a polar coordinate scale (rotated 45 degrees) and mapped each set of motors across from one another to either the X or Y coordinate of the point (respectively). This doesn’t exactly lead to an easy blend with spinning as far as I can tell. While it’s something I’ll probably try to tackle later, it’s not done YET.
You don’t have to re-invent the wheel (sorry for the pun) if you don’t want to. The FRC-provided software development environment contains library functions for the omni drive you described. In LabVIEW for example, using the Cartesian Holonomic vi, all you have to do is wire your Y, X, and Rotate inputs into the vi, and it does all the math. If you have a gyro, there is an input on the vi for that too, so you can have field-centric control.
I didn’t set out to re-invent the wheel, but it came to that after a small series of events. Basically, our cRio is stuck on last year’s bot, so we’re using the '07 robot controller and player station mounted to a piece of plywood. As such, I’m writing in C, not LabVIEW (our normal programming language at this point is Java anyway, but I assume there’s a class for that as well). And I’m sure it’s capable of two dimensional movement while spinning, I just haven’t put forth the energy into performing those calculations yet. Good to know that there’s a class waiting for me when we transition to this year’s real bot though.
You are correct, but the only degree of freedom that is affected by the wheelbase and trackwidth dimensions as far as the inverse kinematic calculation is concerned is the “rotate” command, and this can safely be tuned empirically to a suitable value (by multiplying the joystick rotate command by a scale factor, for example).
I posted a paper (see link below) which goes into more technical detail showing how the wheel positions affect the calculations, if you are interested. It even covers cases where the wheels are not located equidistant from the chosen centerpoint.
Hey, if you’d mentioned that earlier, I would have posted this for you:
// 3-axis joystick interface to an omni drive
// with robot-centric driver interface
// first define your driver interface,
// in this case a 3-axis joystick:
forward = -Y; // push joystick forward to go forward
right = X; // push joystick to the right to strafe right
clockwise = Z; // twist joystick clockwise turn clockwise
// here is where you would put any special shaping of the joystick
// response curve, such as deadband or gain adjustment
// now apply the inverse kinematic tranformation
// to convert your vehicle motion command
// to 4 wheel speed commands:
front_left = forward + clockwise + right;
front_right = forward - clockwise - right;
rear_left = forward + clockwise - right;
rear_right = forward - clockwise + right;
// finally, normalize the wheel speed commands
// so that no wheel speed command exceeds magnitude of 1:
max = abs(front_left);
if (abs(front_right)>max) max = abs(front_right);
if (abs(rear_left)>max) max=abs(rear_left);
if (abs(rear_right)>max) max=abs(rear_right);
if (max>1)
{front_left/=max; front_right/=max; rear_left/=max; rear_right/=max;}
// you're done. send these four wheel commands to their respective wheels
… it’s C code for fully holonomic operation of your omni bot.
If you want to add the gyro angle for field-centric control, it only adds a small amount of complexity:
// 3-axis joystick interface to an omni drive
// with gyro (field-centric driver interface)
// first define your driver interface,
// in this case a 3-axis joystick:
forward = -Y; // push joystick forward to go forward
right = X; // push joystick to the right to strafe right
clockwise = Z; // twist joystick clockwise turn clockwise
// here is where you would put any special shaping of the joystick
// response curve, such as deadband or gain adjustment
// use the gyro angle for field-centric control
// "theta" is the gyro angle, measured CCW from the zero reference
forward' = forward*cos(theta) - right*sin(theta);
right' = forward*sin(theta) + right*cos(theta);
// now apply the inverse kinematic tranformation
// to convert your vehicle motion command
// to 4 wheel speed commands:
front_left = forward' + clockwise + right';
front_right = forward' - clockwise - right';
rear_left = forward' + clockwise - right';
rear_right = forward' - clockwise + right';
// finally, normalize the wheel speed commands
// so that no wheel speed command exceeds magnitude of 1:
max = abs(front_left);
if (abs(front_right)>max) max = abs(front_right);
if (abs(rear_left)>max) max=abs(rear_left);
if (abs(rear_right)>max) max=abs(rear_right);
if (max>1)
{front_left/=max; front_right/=max; rear_left/=max; rear_right/=max;}
// you're done. send these four wheel commands to their respective wheels