Hi All,

Currently I don't use the FBW B mode on my aircraft, and I wanted to modify it for a custom function.  Specifically, I want to make a doublet command, so that when I throw the switch into FBW B, it will do a quick succession of predetermined control-surface movements, and then go back into manual control.

At this stage, I've tried adding some code to move the rudder servo below Arduplane.pde line 1062 (case FLY_BY_WIRE_B), and likewise below line 1102 (case MANUAL) to see if I can just get different modes to toggle the servo, but it doesn't seem to do anything.

Could someone point me in the right direction here?  Perhaps there's some detailed code documentation that I could look at.  Alternately, if there's a simpler solution, suggestions would be appreciated.

Thanks,

- Lewis

Tags: ArduPlane, code, custom, doublet

Views: 303

Reply to This

Replies to This Discussion

Well, I've gone through ArduPlane.pde (now changed to ArduPlane.ino by the latest version of Arduino...) and I've managed to figure out that the update_current_flight_mode() function doesn't change the operation of the servos, even if it's disabled completely (by commenting out line 741).  But seeing as this function is described as "custom code/exceptions for flight modes", I'm really not sure why.  Do I have to activate it somehow in APM_Config.h?

For now, I'm going to delve into the other functions called in ArduPlane.ino and see if I can manually control the servos in there.

I've now determined that custom_mode exists in the code.  Perhaps I need to take advantage of this feature to implement my custom code without overwriting FBW_B.  Can anyone give advise on how to do this?

Thanks!

What code are you substituting in place of the normal FLY_BY_WIRE_B code to toggle the servo?

Something like this (in Arduplane.ino, around line 1062):

case FLY_BY_WIRE_B:
                // Substitute stick inputs for Navigation control output
                // We use g.pitch_limit_min because its magnitude is
                // normally greater than g.pitch_limit_max

                nav_roll = g.channel_roll.norm_input() * g.roll_limit;
                altitude_error = g.channel_pitch.norm_input() * g.pitch_limit_min;

                if ((current_loc.alt>=home.alt+g.FBWB_min_altitude) || (g.FBWB_min_altitude == 0)) {
                     altitude_error = g.channel_pitch.norm_input() * g.pitch_limit_min;
                } else {
                    if (g.channel_pitch.norm_input()<0)
                        altitude_error =( (home.alt + g.FBWB_min_altitude) - current_loc.alt) + g.channel_pitch.norm_input() * g.pitch_limit_min ;
                    else altitude_error =( (home.alt + g.FBWB_min_altitude) - current_loc.alt) ;
                }
                calc_throttle();
                calc_nav_pitch();

    

                //*** my code starts here

                // Custom code to cause rudder to move to a fixed position...
                g.channel_rudder.servo_out = 1800;    // this appears to do nothing

                //*** my code ends here


                break;

Since doing that, I managed to track down where the servo pwm is output in APM_RC_APM2.cpp, where it calls OutputCh().  And then I modified that in order to make the servos move.  But it's a rather clumsy approach, and I was hoping for something higher level than direct manipulation of the port pins ;)

I think that g.channel_xxx.radio_out has something to do with controlling the servos (have had some luck by writing to that variable as well), but I haven't been able to trace the link between the radio_out variable and FLY_BY_WIRE_B.

I was hoping that there would be a case statement containing an assignment of a value from a sensor to the servo variable, but I also have yet to discover that.

Cheers.

Well, that will not work for several reasons.

The g.channel_rudder.servo_out is not the structure controlling the actual servos.  (I think this is used only for the simulator) You're right that theAPM_RC.OutputCh() call actually sets the servos.  But this is set based on the g.channel_[roll][pitch][rudder][throttle].radio_out values.  These values are, in manual mode reflect the position of your sticks, and in guided mode, depend on a combination of sticks and nav/stabilization values.

So, in order to set a specific rudder value, you will need to override the g.channel_rudder.radio_out value to what rudder position you want.  Also the problem is that you are using the FLY_BY_WIRE_B switch, which means that set_servos will execute the code path related to that mode, and since you have added code in to either override or change the rudder values, you may end up with some weird behaviour.

What it seems to me like you want is to execute certain pre-programmed behaviours, but only in manual mode.  You used add a check in the MANUAL part of the case statement in Arduplane to check for the toggle of a free aux channel, and then adjust the radio_out channels for what ever surface you want to affect.

You should add or subtract values to the input values for the channels in question to ensure that your plane does not go haywire.  What you are attempting is not as simple as you think.  Basically, in order execute an acrobatic maneuver on a switch is a difficult task.  It's not just a matter of moving the flight surfaces at pre-defined values.  The actually values used are based on your speed and the attitude of your plane at the time that you flip the switch, requiring very complex calculations.

Thanks for the explanation of servo_out and radio_out.

You raise some good points - if I use FBW_B, then I will have to remove every instance of that in the rest of the code to ensure proper execution of my custom command.  On the other hand, I'm using this doublet for accelerometer and airframe response testing - so if it finishes the sequence without going back to manual, that would be fine too.  I'm thinking now that I could just add my servo movements to the beginning of FLY_BY_WIRE_B, and once they're complete, it will auto-stabilize.

I should explain that initially, all I'm going for is something as simple as

// begin

// put elevators (or ailerons or rudder) to max

// put elevators (or ailerons or rudder) to min

// end

So I'm not planning on doing any serious calculations at this stage.  However, you're completely correct that for more complicated manouvres, it's important to have some idea of airspeed and attitude.  I'll keep that in mind for the future.

I like your idea of using an AUX switch in manual mode and a temporary override of radio_out values, but I'm not sure how I would configure an AUX channel in the code.  I have APM Planner running, so I can see the channel values coming from the transmitter - is there some way to read those in directly?

Thanks again for the help.

You should be able read the channels using the APM_RC.InputCh(CH_x) method.

There's a lot of re-mapping going on, but the channel are define in APM_RC.h:

#define CH_1 0
#define CH_2 1
#define CH_3 2
#define CH_4 3
#define CH_5 4
#define CH_6 5
#define CH_7 6
#define CH_8 7
#define CH_9 8
#define CH_10 9
#define CH_11 10

I'm not too familiar with Arduplane, but according to the comments, looks like CH_5-CH_7 are free

/*

Channel assignments
1 Ailerons (rudder if no ailerons)
2 Elevator
3 Throttle
4 Rudder (if we have ailerons)
5 Aux5
6 Aux6
7 Aux7
8 Aux8/Mode
Each Aux channel can be configured to have any of the available auxiliary functions assigned to it.
See libraries/RC_Channel/RC_Channel_aux.h for more information
*/

Cool.  I'll have to take a look at that later, because I ended up hacking together a rough solution.  I went into the update_current_flight_mode() method and added the following code to the FLY_BY_WIRE_B case in the switch(control_mode) statement:

//*** Doublet command sequence
static int runflag = 0;
if(runflag == 0)
{
    APM_RC.OutputCh(CH_1, 900);        // move ailerons one direction
    delay(1000);
    APM_RC.OutputCh(CH_1, 2100);    // move ailerons the other direction
    delay(1000);
    runflag = 1;    // make sure we don't repeat the doublet constantly
}

In the MANUAL case, I then reset runflag to 0 so that I can perform the action again:

case MANUAL:
    // servo_out is for Sim control only
    // ---------------------------------
    g.channel_roll.servo_out = g.channel_roll.pwm_to_angle();
    g.channel_pitch.servo_out = g.channel_pitch.pwm_to_angle();
    g.channel_rudder.servo_out = g.channel_rudder.pwm_to_angle();
    
    runflag = 0;    //***  reset runflag
    
    break;

I notice that the comments state that servo_out is only for Sim control, but the code seems to work anyway.  I'm sure there's a more robust way of doing this, but for the time being it's moving the servos the way I want.

I'm now thinking that if I placed a line such as

set_mode(FLY_BY_WIRE_A);    // -or- set_mode(MANUAL);

immediately following the doublet command sequence, and then commented out the rest of FLY_BY_WIRE_B, I could effectively usurp the functionality of that mode for my custom command.  Perhaps not the most elegant solution, but functional in theory.

Does anyone see any pitfalls?

I don't have the experience with Arduplane code to comment any deeper, but I'm sure once you work with the code more, you'll see the best way to do things that will fit your needs.

Yeah, it's just tough with how relatively little documentation there is compared to projects I've worked on in the past (like PICMicro).  But there's this great forum here, and people like you who are willing to help, so that makes up for it ;)

Thanks for the tips!

Hey, I'm taking a look at where you talk about using the APM_RC.InputCh(CH_x) method to get radio input.  What's the difference between that and g.channel_xxxx.radio_in?  Do they produce the same integer value that can be fed to the outputs?

RSS

Social Networking

Contests

Season Two of the Trust Time Trial (T3) Contest has now begun. The fourth round is an accuracy round for multicopters, which requires contestants to fly a cube. The deadline is April 14th.

A list of all T3 contests is here

Groups

Advertisement

© 2013   Created by Chris Anderson.   Powered by

Badges  |  Report an Issue  |  Terms of Service