Overview
Though the goal of the system was autonomous flight, the microcontroller still had to communicate with the transmitter for testing and safety as well as to enable the auto mode. This post will focus on the steps taken to communicate between the remote control receiver, microcontroller, and servos.
Breadboard Communications Testing Setup
Transmitter and Receiver
The receiver used was a 6 Channel Spektrum receiver that communicated with my Spektrum transmitter. I'd used this pair for all previous RC aircraft and had good experiences with the consistency and range. The transmitter outputs 6 separate PWM values, one for each channel.
Servos
The servos are used to actually actuate the control surfaces of the aircraft. The current aircraft is 4 channel, meaning that it uses Rudder, Elevator, Ailerons, and Throttle. Note that I'm going to refer to the throttle as another "Servo" because I'm treating it the same as the others. The servos I'm using ran on PWM (pulse width modulation). This is common for RC aircraft and matches what the receiver outputs.
The goal was to put the microcontroller in between the receiver and servos so that it could read the input signals from the receiver and then output its own signals to the servos. I took some inspiration from this site.
However, that site used relays to switch between the microcontroller talking to the servos and the receiver talking to the servos. I wanted the microcontroller to read all of the signals and modulate them as it saw fit before outputting to the servos. This would allow a full manual mode where the receiver communicated directly to the servos, a full auto mode where the control system communicated directly to the servos, and some hybrid modes in between.
Easy Way: Pulsein()
The simplest solution was to use the pulsein() function from arduino to read a single PWM signal, convert it to angle through a mapping algorithm, and output it to the servo with the Servo object. This method worked great for a single signal but broke down once multiple signals were involved. This is because the way that interrupts are handled to make pulsein() work does not accommodate multiple independent interrupts or simultaneous interrupts.
Better Way: Pin Change Interrupts
The better way to do this was to take advantage of the pin change interrupt vectors available on the microcontroller. Though I believe pin change interrupt vectors are available on a variety of microcontrollers, I found the most documentation for their use on Arduino boards.
Instead of having an interrupt be attached to a single pin as they would with something like attachIinterrupt(), the entire vector (or set of pins) is attached so that if any of them change, the interrupt is triggered. However, it is not known which pin or pins changed so the interrupt service routine must find what the change was. Fortunately, I found this bit of code online to do exactly what I needed. I chose to use Arduino boards for the rest of this project primarily because of this code as well as how well documented their pin change interrupt systems are.
Results
Using that code, I was able to write a program that read the PWM signals from the receiver and outputted them to the servos. On the Arduino Uno, I was limited to only 4 pins to read the RC signal, so the throttle was connected directly to the receiver, and only the Aileron, Rudder, and Elevator were read. Having the throttle connected directly to the receiver is a safety feature I still use because I don't trust my code to control a scary propellor just yet.
Now you might notice that I said 4 pins but only mentioned 3 control surfaces. I needed the 4th pin to act as my toggle for autopilot and manual mode. This single switch on the transmitter output either a high or low signal which was easily translated to a boolean value to switch between auto and manual mode.
Mapping Code
Servos tend to output in the range of 0-180 degrees. The PWM signal comes in at 50Hz with about 5% to 10% duty cycles. This leads to ON-TIME of between 1100 and 1900 ms. By linearly mapping between 1100-1900 and 0-180, the PWM signal can be translated to a servo angle. The conversion from angle back to PWM happens behind the scenes in the Servo library.
static double pwm_map(volatile unsigned long pwm_onTime, int ANGLE_MAX, int ANGLE_MIN)
{
double Angle = double((ANGLE_MAX - ANGLE_MIN) * (double(pwm_onTime) - PWM_MIN) / (PWM_MAX - PWM_MIN) + ANGLE_MIN);
if (Angle < ANGLE_MIN)
{
Angle = ANGLE_MIN;
}
else if (Angle > ANGLE_MAX)
{
Angle = ANGLE_MAX;
}
return Angle;
}
Code snippet for mapping
For an actual aircraft, the allowable servo range will likely not be that full range. There is the option to tune that range in this mapping function. However, I chose to keep all trim on the transmitter and having the mapping stay consistent, making the aircraft flyable even without the microcontroller.
No comments:
Post a Comment