Log in

View Full Version : Arduino Mecanum drive using PWM?


cad321
12-07-2013, 10:52
I have been trying to program an Arduino Uno to take PWM signals from a spectrum RC receiver and mix those signals to control 4 motors on a mecanum drive bot. Although I don't have the bot built/designed yet I have been trying to get some of the code working. My issue is that when ever I try to use more than one pulseIn command it adds about 1 second of lag per pulseIn command. I am controlling a servo through this setup.

The code:
#include <Servo.h>

Servo FLS; //front left servo
Servo FRS; //front right servo...
Servo RLS;
Servo RRS;
int Forward = 1; //Inputs from RC receiver
int Strafe = 2;
int Rotate = 3;
unsigned long ForwardDuration; //To store the duration of the pulses
unsigned long StrafeDuration; //coming from the receiver
unsigned long RotateDuration;
unsigned long FL; //Front Left Value
unsigned long FR; //Front Right Value...
unsigned long RL;
unsigned long RR;

void setup()
{
FLS.attach(10);
FRS.attach(11);
RLS.attach(12);
RRS.attach(13);
pinMode(Forward, INPUT);
pinMode(Strafe, INPUT);
pinMode(Rotate, INPUT);
}

void loop()
{
ForwardDuration = pulseIn(Forward, HIGH); //read receiver inputs
StrafeDuration = pulseIn(Strafe, HIGH);
RotateDuration = pulseIn(Rotate, HIGH);
FL = ForwardDuration + StrafeDuration + RotateDuration;
FR = ForwardDuration - StrafeDuration - RotateDuration;
RL = ForwardDuration - StrafeDuration + RotateDuration;
RR = ForwardDuration + StrafeDuration - RotateDuration;
FLS.writeMicroseconds(FL);
FRS.writeMicroseconds(FR);
RLS.writeMicroseconds(RL);
RRS.writeMicroseconds(RR);
}

My initial thought was to add interrupts but I'm not exactly sure how to do that.

My attempt at using Interrupts:
#include <Servo.h>

Servo FLS; //front left servo
Servo FRS; //front right servo...
Servo RLS;
Servo RRS;
int Forward = 1; //Inputs from RC receiver
int Strafe = 2;
int Rotate = 3;
volatile unsigned long ForwardDuration; //To store the duration of the pulses
volatile unsigned long StrafeDuration; //coming from the receiver
volatile unsigned long RotateDuration;
volatile unsigned long FL; //Front Left Value
volatile unsigned long FR; //Front Right Value...
volatile unsigned long RL;
volatile unsigned long RR;

void setup()
{
attachInterrupt(0, reciever, CHANGE);
FLS.attach(10);
FRS.attach(11);
RLS.attach(12);
RRS.attach(13);
pinMode(Forward, INPUT);
pinMode(Strafe, INPUT);
pinMode(Rotate, INPUT);
}

void loop()
{
FLS.write(FL);
FRS.writeMicroseconds(FR);
RLS.writeMicroseconds(RL);
RRS.writeMicroseconds(RR);
}
void reciever()
{
ForwardDuration = pulseIn(Forward, HIGH); //read receiver inputs
StrafeDuration = pulseIn(Strafe, HIGH);
RotateDuration = pulseIn(Rotate, HIGH);

FL == ForwardDuration + StrafeDuration + RotateDuration;
FR == ForwardDuration - StrafeDuration - RotateDuration;
RL == ForwardDuration - StrafeDuration + RotateDuration;
RR == ForwardDuration + StrafeDuration - RotateDuration;
}
The reason I am posting here and not on the Arduino forums is that it wont load on my computer right now so I thought I'd try my shot with CD and see if anyone here could help.

tr6scott
12-07-2013, 14:09
http://forum.arduino.cc/index.php?PHPSESSID=5vfgovh9insep18stgbtnuuss5&topic=42462.60

Should Help, looks like there is an interrupt library developed for servo.

magnets
12-07-2013, 14:45
I don't think that you'll be needing interrupts. The arduino is fast enough to control 4 PWM's. Also, don't use the unsigned longs. If forwardDuration is 10, strafeDuration is 100, and rotateDuration is 100, you will have a negative value for FR, which is unsigned. I don't know what happens on the arduino, but it may wrap around to some massive number.

EDIT: Depending on what you're using to control your motors, you may have better sucsess with analogWrite. Also, how are you testing to see the lag?

cad321
12-07-2013, 15:25
Thanks for the help. I finally was able to load the arduino forums and now have my servos responding virtually instantly. I just needed to increase the timeout in the pulseIn function (the amount of time the arduino searched for a pulse coming in before moving on in the code).

Tungrus
12-07-2013, 16:05
Care to share your implementation? It may help other expri-mentors. Thanks.

cad321
12-07-2013, 18:44
I am planning on taking the pwm signals from my spectrum ar6210 and mixing them with the arduino using the following code to create a robot with a mecanum drive. For the motors I plan on using some continuous rotation servos and I am planning on buying some small mecanum wheels such as the 4in vex wheels.

#include <Servo.h>

Servo FLS; //front left servo
Servo FRS; //front right servo...
Servo RLS;
Servo RRS;
int Forward = 1; //Inputs from RC receiver
int Strafe = 2;
int Rotate = 3;
unsigned long ForwardDuration; //To store the duration of the pulses
unsigned long StrafeDuration; //coming from the receiver
unsigned long RotateDuration;
unsigned long FL; //Front Left Value
unsigned long FR; //Front Right Value...
unsigned long RL;
unsigned long RR;
unsigned long timeout = 20000;

void setup()
{
FLS.attach(10);
FRS.attach(11);
RLS.attach(12);
RRS.attach(13);
pinMode(Forward, INPUT);
pinMode(Strafe, INPUT);
pinMode(Rotate, INPUT);
}

void loop()
{
ForwardDuration = pulseIn(Forward, HIGH, timeout); //read receiver inputs
StrafeDuration = pulseIn(Strafe, HIGH, timeout);
RotateDuration = pulseIn(Rotate, HIGH, timeout);
FL = ForwardDuration + StrafeDuration + RotateDuration;
FR = ForwardDuration - StrafeDuration - RotateDuration;
RL = ForwardDuration - StrafeDuration + RotateDuration;
RR = ForwardDuration + StrafeDuration - RotateDuration;
if(FL < 1000){
FL = 1000;
}else if(FL > 2000){
FL = 2000;
}if(FR < 1000){
FR = 1000;
}else if(FR > 2000){
FR = 2000;
}if(RL < 1000){
RL = 1000;
}else if(RL > 2000){
RL = 2000;
}if(RR < 1000){
RR = 1000;
}else if(RR > 2000){
RR = 2000;
}
FLS.writeMicroseconds(FL);
FRS.writeMicroseconds(FR);
RLS.writeMicroseconds(RL);
RRS.writeMicroseconds(RR);
}

I am unsure at the moment if this code will work yet as I do not have a bot to test with at the moment, but hopefully I will be able to collect all of the resources I need this summer.

Mike Bortfeldt
14-07-2013, 15:49
cad321,

A couple of observations regarding the I/O. The pulseIn code (source) looks like it will return a zero (0) when it does not find the pulse within the timeout period. You may want to put in some code to check for this, otherwise your outputs may not be what you would like. Also, be aware that your "loop" execution time will be variable depending on how long it takes to obtain the three pulse values and output to your four servos. Assuming a standard 20 millisecond interval for the incoming servo PWM value, you will probably have a range between 8 to 65 milliseconds. This may not be a problem.

My knowledge of the hardware you are using is limited, but You may also want to review your mixing algorithm as well. Again, assuming a standard servo input (1-2 ms pulse), the pulseIn should return values between 1000 and 2000 (usec). Adding all three together for FL (for example) would yield 3000 to 6000, or basically your maximum all the time. This could be correct if the inputs are not what I assumed, but it doesn't look right for a standard servo input/output signal. My guess, based on your original post of the 1 second delays, indicate that you are probably not reading at least two of the signals (Strafe & Rotate perhaps?), so you are adding zero (0) to your forward value and the calculations are doing what you expect. The 1 second delay is approximately the timeout for the default pulseIn when it doesn't see a pulse and no explicit timeout is passed.

Mike

cad321
14-07-2013, 16:24
My knowledge of the hardware you are using is limited, but You may also want to review your mixing algorithm as well. Again, assuming a standard servo input (1-2 ms pulse), the pulseIn should return values between 1000 and 2000 (usec). Adding all three together for FL (for example) would yield 3000 to 6000, or basically your maximum all the time.

I to realized this and based on mecanum drive code i have found and tried using Java for my team and through research for this, I am now taking all of the values and dividing by the highest or lowest absolute value if one of them is either above 2000ms or below 1000ms. I am learning this as I go and so am having my ups and downs. I have however just realized I can map my 1000 - 2000ms inputs to an equal negative - positive range such as (-255) - (255). This has helped me a great deal.


My guess, based on your original post of the 1 second delays, indicate that you are probably not reading at least two of the signals (Strafe & Rotate perhaps?), so you are adding zero (0) to your forward value and the calculations are doing what you expect. The 1 second delay is approximately the timeout for the default pulseIn when it doesn't see a pulse and no explicit timeout is passed.

Mike
This is exactly what was happening so I lowered the timeout value to be 30ms and it was responding virtually instantly (give or take the lag of the 30ms per input if I wasn't receiving a pulse).