Radio Control

From PaparazziUAV
Jump to navigation Jump to search

PPM Radio Configuration

This XML file, usually located in the conf/radios directory, contains a description of the radio control transmitter PPM signal. It should follow the grammar described in radio.dtd.

CAUTION! This is only used for receivers with PPM output, so it does not apply to receivers/satellites with serial output, e.g. SBus, Spektrum and SuperbitRF.

The contents are an ordered sequence of elements describing each channel with its name and its range:

<!DOCTYPE radio SYSTEM "radio.dtd">
  <radio name="cockpitMM" data_min="900" data_max="2100" sync_min ="5000" sync_max ="15000" pulse_type="POSITIVE">
  <channel ctl="D" function="ROLL"     min="2000" neutral="1498" max="1000" average="0"/>
  <channel ctl="E" function="MODE"     min="2000" neutral="1500" max="1000" average="1"/>

The order of the channels must be the order of the pulses in the PPM signal.

Among the top attributes, we find

  • name: used only in debug traces.
  • data_min (resp. _max): the minimum (resp. max) width (in microseconds) used to code one channel of the PPM signal.
  • sync_min (resp. _max): the minimum (resp. max) width (in microseconds) between two impulses set of the PPM signal.
  • pulse_type: the polarity of the PPM pulse. Can be either POSITIVE (FUTABA, Hitec, Multiplex etc) or NEGATIVE (JR, Graupner etc.).
    With POSITIVE it will trigger on the rising flank to measure the time of the positive pulse, with NEGATIVE on the falling flank (for a negative pulse width).

Each channel is described with its transmitter name (ctl), its function name, its range in microseconds and its neutral value in microseconds. These values are used by the autopilot to compute a normalized input from the PPM signal (this file is preprocessed and the produced code is included in the airborne code). Note that the min and max attributes can be exchanged to reverse the direction of the command.

Wrong attributes of the "radio" element will prevent the decoder to recognize any PPM frame; same for a wrong number or channels.

Number of channels

Note that the number of channels in your radio file must exactly match the number of channels in the ppm sum signal the autopilot receives.
Also the function names must be unique.

  • in case of analog 35MHz receivers it is the RC - TRANSMITTER that makes the pulse train (receiver only does RF part)
    • radio file depends on transmitter, you could swap to another receiver (and e.g. you can use a 4channel receiver to receive 9 pulses :-) )
  • in case of digital receivers it is not the transmitter but the receiver that makes the pulses
    • e.g. Futaba Fasst 6017: you can use any compatible transmitter without need to change the radio file
  • in case of an encoder it is the encoder that makes the summed pulse
    • if the encoder software makes 8 pulses, even with a 5 channel RC connected paparazzi will get 8: so the radio file should read 8

This means if your transmitter sends for example a PPM18 (Graupner/JR) signal you have nine (9) different servo channels. Even if you use a receiver with less channels you must set up all transmitting servos, even if you do not use them then you must use "bogus switches" related to functions. For example a six (6) servo channels out receiver with five (5) functions used in combination with a nine (9) servo channels transmitter would need four (4) bogus entries. (5 + 4 = 9)

 <channel ctl="NoneA"	              function="NOTUSEDA"    	min="1100" neutral="1500" max="1900" average="0"/>
 <channel ctl="NoneB"	              function="NOTUSEDB"    	min="1100" neutral="1500" max="1900" average="0"/>
 <channel ctl="NoneC"	              function="NOTUSEDC"    	min="1100" neutral="1500" max="1900" average="0"/>
 <channel ctl="NoneD"	              function="NOTUSEDD"    	min="1100" neutral="1500" max="1900" average="0"/>


The average attribute must be set to 1..x for discrete channels for which a trivial averaging filter will be applied. The neutral of a discrete channel need to be halfway through the min and max values. Otherwise it won't be mapped properly by the filter. Note that averaging a channel gives the output an additional small delay.


The philosophy is to follow standard aerospace conventions:

  • ROLL: The max attribute value stands for the right position of the roll stick (banking right)
  • PITCH: The max attribute value stands for the "up" position of the pitch stick pulled towards you (pitching up)
  • YAW: The max attribute value stands for the right position of the yaw stick (yawing clock-wise)

If you still use a Paparazzi version older than v4.0, this roll and pitch might be reverse. seriously consider updating your Paparazzi version.

Practical test

In the RC message:

  • when the ROLL stick is pulled to the right (aircraft banks right), you should see a value close to MAX_PPRZ (9600) in the ROLL channel
  • When the PITCH stick is pulled (aircraft pitches up), you should see a value close to MAX_PPRZ (9600) in the PITCH channel
  • When the YAW stick is pulled to the right (aircraft yawing clock-wise), you should see a value close to MAX_PPRZ (9600) in the YAW channel

This works with all kind of radios (PPM, USB, 2.4GHz)


Below you find an example of a radio file.

<?xml version="1.0"?>

-- Attributes of root (Radio) tag :
-- name: name of RC
-- min: min width of a pulse to be considered as a data pulse
-- max: max width of a pulse to be considered as a data pulse
-- sync: min width of a pulse to be considered as a synchro pulse
-- min, max and sync are expressed in micro-seconds

-- Attributes of channel tag :
-- ctl: name of the command on the transmitter - only for displaying
-- function: logical command
-- average: (boolean) channel filtered through several frames (for discrete commands)
-- min: minimum pulse length (micro-seconds)
-- max: maximum pulse length (micro-seconds)
-- neutral: neutral pulse length (micro-seconds)
Note: a command may be reversed by exchanging min and max values

<radio name="GraupnerMC20" data_min="950" data_max="2150" sync_min="5000" sync_max="15000" pulse_type="NEGATIVE">
  <channel ctl="A" function="THROTTLE" min="1100" neutral="1110" max="1900" average="0"/> <!-- left stick up/down -->
  <channel ctl="B" function="ROLL" min="1100" neutral="1500" max="1900" average="0"/>     <!-- right stick left/right -->
  <channel ctl="C" function="PITCH" min="1900" neutral="1500" max="1100" average="0"/>    <!-- right stick up/down -->
  <channel ctl="D" function="YAW" min="1100" neutral="1500" max="1900" average="0"/>	  <!-- left stick left/right-->
  <channel ctl="E" function="MODE" min="1100" neutral="1500" max="1900" average="1"/>     <!-- left middle 3-pos switch -->
  <channel ctl="F" function="GAIN1" min="1100" neutral="1500" max="1900" average="0"/>    <!-- left slider prop channel -->
  <channel ctl="G" function="GAIN2" min="1100" neutral="1500" max="1900" average="0"/>    <!-- right slider prop channel -->
  <channel ctl="H" function="SWITCH1" min="1100" neutral="1500" max="1900" average="4"/>  <!-- switch (channel 8) -->
  <channel ctl="I" function="UNUSED" min="1100" neutral="1500" max="1900" average="1"/>   <!-- channel 9 is transmitted but cannot be controlled -->

PPM input to output chain

The chain from ppm input to servo output has intermediate steps and roughly looks like this:

ppm input --(radio.xml)--> ppm_pulses[] --(radio.xml)--> radio_control.values[] --(rc_commands)--> commands[] --(command_laws,servos)--> actuators_pwm_values[] ppm out

Written in parenthesis is where you configure how to transform one value into the other.

So in a simple case without fancy mixing, e.g. looking at the roll command / aileron servo while in MANUAL mode:

  1. PPM sum signal is read and split into channels as specified in your radio xml file.
    It does some sanity checking on the pulse length and frame lenght according to the top attributes in that file.
    This is written to the ppm_pulses array, where you now have the length of the pulse in system ticks.
  2. Now these get converted to radio_control.values array according to the channels in your radio xml file.
    They get scaled to MAX_PPRZ (9600), meaning what you specified as max is 9600, neutral is 0, etc.
    radio_control.values[RADIO_ROLL] now has the value 9600 if the pulse width of that channels was "max"
  3. Mapping of RC commands to the actual commands as specified in rc_commands section.
    In your case simply copy radio_control.values[RADIO_ROLL] to commands[COMMAND_ROLL]
  4. Applying command_laws (mixing) to get the commands for each servo.
    Again just copying in your case...
  5. Scaling from commands with MAX_PPRZ scale to the actual pwm output signal according to the servos section.

Measuring the PPM time values

R/C receiver timing diagram

There are two common ways to measure the time characteristics of the PPM signal:

  1. Using an oscilloscope: easy to achieve with a high level digital scope with capture and measure facilities.
  2. Using the telemetry of the autopilot: the PPM message (defined in conf/messages.xml) contains the sequence of a (recently) received PPM signal.

With the default telemetry configuration file (conf/telemetry/default_fixedwing.xml), this message is not sent in the default fbw telemetry mode (numbered 0).

There are two ways to enable this message:

  • Use the settings mechanism to change the FBW telemetry mode while the autopilot is already running:
    In the GCS: Settings -> telemetry -> tele_FBW -> set to debug (1)
  • This mode can be permanently changed to debug (numbered 1) in the airframe file by setting the TELEMETRY_MODE_FBW constant in your firmware section:
<define name="TELEMETRY_MODE_FBW" value="1"/>

Since "v3.9" the values in the PPM message are in µs.

Before "v3.9" the time unit used in this PPM message was hardware dependent:

  • On the obsolete AVR hardware, 1 microsecond = 16 units (because the crystal is running at 16MHz)
  • on the LPC hardware, 1 microsecond = 15 units (because the cristal is running at 12MHz)
    (conf/autopilot/tiny.h), the CPU clock is 5 times more, the peripheral bus is 4 times less, and the timer is not prescaled (sw/airborne/arch/lpc21/mcu_periph/sys_time_arch.h) !!!)


If one has a good servo tester the output values at the receiver side can be measured