This makes it excellent for managing radiocontrol PWM signals in complex RC vehicles.
That wrapped up red component in my latest RC vehicle (another hobby, I build RC cars...) is an arduino that reads in steering and another channel and processes it to output a new signal that lets me choose if I want the truck to have 4 wheel steering, or crabwalk (both wheels point the same way making it go mostly sideways).
Definitely one of the more interesting vehicles I've built, and a decent summer project (though I did start last year wrecking the transmission pushing myself in a chair).
This actually started out with me receiving a non-working unit for handling quadsteer that I had purchased on amazon. When using the unit, the servos lacked power, wouldn't center, and were slow. I used an oscilloscope to diagnose the signal and saw the pulses were 40 milliseconds apart. For Radio control PWM signals, the leading edge should be 20 milliseconds apart. What was essentially happening was the server would get a signal and act on it for 20 milliseconds, and then not do anything for the next 20 milliseconds.
So I turned to arduino nano in the hopes to making my own. Below I've setup that arduino nano on the breadboard to mix signals as I'd like. Each square on the oscilloscope is 10 ms, so my working prototype is showing the proper waveform. This is what was wrapped up in electrical tape in the first picture.
Below is the code I wrote to handle this. The idea is to read two channels - the steering and a spare channel to know if to invert the steering between front and rear. Since I'm reading the time of the pulse, I need a little math to normalize 1000-2000μs to -1 to 1 and back again. This lets me multiply the channels so I can then smoothly transition between crabwalk/quadsteer.
Should be noted we're using interrupts to gather input channel data. This is a non-blocking method for gathering the input. I just requires us to determine the time of the pulse by subtracting the time the clock had at the signal's leading edge.
Edit: 20200907 - code had some bugs, turning off/on interrupts wasn't needed and led to jerky servo output. Tidied it up a bit.
#include <Servo.h> volatile unsigned long leadingedge1; volatile unsigned long leadingedge2; volatile int pulsetime1; volatile int pulsetime2; //declare servo pins int servoin1 = 2; // pin 2 - steering int servoin2 = 3; // pin 3 - inversion channel int servoout1 = 9; // output read servo is pin 9, front is pin 2 Servo rearservo; int ch1_hist[3]; int ch2_hist[3]; int execute=0; long RServo=1500; //value to write to rear servo void setup() { pinMode(servoin1, INPUT); // sets the digital pin 1 as input pinMode(servoin2, INPUT); // sets the digital pin 1 as input pinMode(servoout1, OUTPUT); // sets the digital pin 9 as output // pinMode(servoout2, OUTPUT); // sets the digital pin 9 as output leadingedge1 = 0; leadingedge2 = 0; pulsetime1 = 1500; pulsetime2 = 1500; attachInterrupt(0, chan1, CHANGE); attachInterrupt(1, chan2, CHANGE); rearservo.attach(servoout1); Serial.begin(115200); // for debugging } int amode(int a[]){ //mode or average of an array - use to smooth glitches if (a[0]==a[1]) { return a[0]; } if (a[0]==a[2]) { return a[0]; } if (a[1]==a[2]) { return a[1]; } return (a[0]+a[1]+a[2])/3; } int pusharr(int a[],int pushval) { a[2]=a[1]; a[1]=a[0]; a[0]=pushval; return 0; } void dostuff(){ execute=0; RServo = 1500+((((long)pulsetime1-1500)*((long)pulsetime2-1500))/500); if (RServo > 2050) { RServo=2050; } else { if (RServo < 950) { RServo=950; } } Serial.print("P1."); Serial.print(pulsetime1); Serial.print("--P2."); Serial.print(pulsetime2); Serial.print("--SRear."); Serial.print(RServo); Serial.print("\n" ); execute=1; } void chan1() { if(digitalRead(servoin1) == HIGH) { leadingedge1 = micros(); } else { if (leadingedge1 > 0) { pulsetime1 = ((volatile long)micros() - leadingedge1)-14;//14 us added from other operations? center needed normalizing to 1500 if ((pulsetime1 > 800 and pulsetime1 < 2200)) {pusharr(ch1_hist,pulsetime1);} leadingedge1 = 0; pulsetime1= amode(ch1_hist); } } } void chan2() { if(digitalRead(servoin2) == HIGH) { leadingedge2 = micros(); } else { if(leadingedge2 > 0) { pulsetime2 = ((volatile long)micros() - leadingedge2)-14; if ((pulsetime2 > 800 and pulsetime2 < 2200)) {pusharr(ch2_hist,pulsetime2);} leadingedge2 = 0; pulsetime2= amode(ch2_hist); } } } void loop() { delay(2); //delay is non blocking dostuff(); //do stuff after the receiver has sent all pulses if (execute==1) { rearservo.write(RServo);} }
No comments:
Post a Comment