3D Robotics
Most autopilots have the ability to read signals from a RC receiver and drive servos. For instance, in the case of ArduPilot, we read the current throttle setting when the autopilot is engaged (so we can maintain that setting, although we'll later nudge it up and down to maintain altitude) and we drive the rudder servo and ESC. In the case of BlimpDuino in RC mode, we read the throttle and left/right commands from the RC receiver and translate them into commands to the motor drivers (the vectoring thruster servo is connected straight to the receiver and doesn't have to go through the autopilot at all). In Arduino, you read RC signals by using the PulseIn command, and you drive servos with the servo.write function added by the servo library. The problem is that PulseIn waits for the next pulse, and the more channels you're reading the more time your CPU is spent waiting. A good rule of thumb is that each PulseIn, without modification, takes about 30% of your CPU time, so you can see that about two channels is the max. However, the form of the function is pulseIn(pin, HIGH, timeout) and you can adjust "timeout" to minimize the waiting time and improve that effeciency. I've been playing around with 50000 (it's in microseconds), but your mileage may vary. Anybody having better luck with a different value? Anyway, if you want to see how this works, here is a simple demo that just reads two RC channels and mirrors the commands with two servos. It's just like regular RC, but it's all going through the Arduino (so it can take over and go into autonomous mode anytime you want). Just read the comments in the code and plug the RC channels and servos into the right pins in your dev board (a Decimila and ProtoShield--don't forget the breadboard--work great). Here's a picture of my setup:

E-mail me when people leave their comments –

You need to be a member of diydrones to add comments!

Join diydrones

Comments

  • 3D Robotics
    Here's the correct code: RCTest.pde

    I'm running it on a dev board with a boarduino right now, so I can get the RC channels in and choose which motor driver channels to use. But the next version of the Blimpduino board (coming out in a day or two) will be designed for RC in and vectoring thrusters and will run this code natively.
    https://storage.ning.com/topology/rest/1.0/file/get/3691901567?profile=original
  • 3D Robotics
    While I figure out the most effecient way to read RC signals, I modified the code to drive blimp motors rather than servos. The code below drives two channels of a TI L293D motor driver chip from atmega pins D6 and D7. It's for steering (differential thrust) and throttle (total thrust).

    So to fly the blimp in RC mode, channel one (steering) drives differential thrust on the twin blimp motors, channel two (up/down) drives the vectoring thruster servo directly from the RC receiver, and channel three (throttle) dictates the total power to the blimp motors. So two channels are handled by Arduino, translating RC command to motor signals, while one is straight RC.

    [UPDATE: I posted the wrong code and I'm not near my PC to get the right one (I'm in Munich on my way to Milan for a day). I'll repost when I get home]
  • i was experimenting a bit with my code. the servo glitched slightly every 3-4 seconds. the problem is that delaymicrosecond will disable interupts, which means that the measurements will be way off... no matter if i do it inside the interupt or inside loop (every 20ms).

    i looked through the atmel docs, and it seems that it should be possible to setup a interupt that would handle the (multiple) servo driving (next to the existing interupt for reading).

    you would set the pulse-length as timer-value, and set to decrement, when zero, the interupt is called, where you set the current servo-pin to low, set the timer-value for the next servo and its pin to high... count-down to zero, interupt is called, current servo-pin to low, timer-value for next servo, pin high, etc....

    any comments on this?
  • 3D Robotics
    Using a line like:

    if (channel1value == 0) {channel1value = lastgood1;} else {lastgood1 = channel1value;}

    I can use timeouts of between 15000 and 20000 in the PulseIn function and read two channels simultaneously. But only a 10% gain in performance (ie, still losing about 60% of CPU time to PulseIns). Next will try the interrupt technique.
  • 3D Robotics
    quix-fz,

    That's a cool idea. Let me try both ways and benchmark them...I'll let you know how it goes.
  • getting the raw ppm signal would be the best way.

    i just had the idea:

    you could connect both servo-signals to pin2 and adjust the above code! since the ppm decoder in the receiver is usually a simple shifter, the signals will come one after the other... separated by a short 0.3ms pause, if the channels are next to eachother.

    could be tricky to keep them apart, but doable. they'll always come in the same order. and if they are next to eachother, after the two pulses there will be a longer pause!!
  • 3D Robotics
    Phil,

    >On to the multiple channels question, You can read the multiplex pulse train on digital 2
    >and use a long pulse value to reset a counter.

    That works if you're willing to hack your RC receiver to get the PPM signal, as per this post, but what if you just want to read straight PWM, one channel per pin? Is there a way to extend this technique to multiple digital pins?

    My instinct is to try PulseIn(pin, HIGH, [some small timeout number) and replace any returned 0s (timed out) with the last known good value. (Typical RC pulses are from 1200 to 2500 so 0 should never be a valid result.) That should minimize waiting time and I'll optimize the timeout value to sample often enough.
  • Last lines
    For some reason I get extra lines in my comments !!
    Phil
  • Hi Chris,
    Thanks quiz-fx for the code.

    I have one comment.

    Is it possible to read a TCNT0 value just after the TC0 overflow interrupt has been
    triggered but before the overflow count has been updated in the ISR.

    This will mean that the overflow value may not have been updated when we read both values. This will cause a jump back in time.
    I have not studied the architecture too much to see if this is possible.

    On to the multiple channels question.

    You can read the multiplex pulse train on digital 2 and use a long pulse value to reset a counter.

    As each pulse arrives below the sync pulse length save the value in an array indexed by the counter then increment the counter.

    Also,

    In the example, the digital pulse output is inside the interrupt routine.

    It may be a good idea
    to put that in a loop outside the ISR but you will have to watch out for updates to the incoming pulse values caused by the interrupt.

    So thinking on the fly... read into an input array and update an output array after the long sync pulse.

    The pulse outputs will be read from the output array.

    This code is a great help. Thanks


    Phil


    I guess you could turn individual incoming channels into a multiplex signal using an or gate.
  • 3D Robotics
    quiz-fx,

    Thanks for the cool code--I'll benchmark that when I get back to my hardware. But when you say that the incoming RC signal has to be on digital 2, do you mean that you can't read multiple channels simultaneously this way? The main point of our exercise is to find a way to read multiple channels (at least 2 or 3) efficiently.
This reply was deleted.