Fluzi - The Flying UsbIzi

There’s been a lot of activity in a number of threads about Quadcopters and building one on the Fez platform.

See http://www.tinyclr.com/forum/2/4291/#/3/ for the latest one going.

This is my attempt to focus the discussion in a specific direction by defining an architecture for such a flying machine and to discuss each aspect of the architecture individually and to conclusion.

The idea is to take the discussion happening here (and on the other threads) and combine it into a wiki where someone can then build a flying Fez.

So to start things off, attached is a stab at a loosely coupled architecture for Fluzi.

The design goals behind this architecture are:

  • Loose coupling - so you can slot your own blocks into the system.
  • Technology agnostic - so you can use a hardware Razor IMU or a Mongoose AHRS or your own rolled in RLP. It shouldn’t matter.
  • Service orientation and Seperation of concerns - or just the idea that each block does its own thing, autonomously, but in a standard way.
  • Simple. Most of all it must be simple (but still work!)

After some digging around other UAV architectures, I think these building blocks should cover what is needed (see diagram):

  • CPU: Does all the main controlling and logic.
  • IMU: Measures intertia (pitch, roll, yaw)
  • ARU: Adds intelligence to resolve inertial measurements to solve attitude and heading.
  • APU: Adds absolute positioning information - usually a GPS, compass and altimeter.
  • ICU: Consolidates various sensor inputs I can’t think about right now but need to cater for.
  • IQU: Queues work for the system on a scheduled and predictable basis. This is basically the very accurate task scheduler for the system.
  • MCU: Controls motion - e.g. motors, servos etc.
  • FRU: Records flight and debugging information.
  • PMU: Makes sure the batteries are full and the copter is humming nicely.
  • DPU: Makes sure the poo doesn’t hit the propellers by invoking Plan B in case of failure.
  • CLU: Handles comms with the outside world over whatever medium.
  • GLU: Glues it all together.
  • GCS: Deals with the dangerous carbon-based object twiddling the sticks on the ground.

So go for it! Let me hear your ideas around things like interfaces, protocols, abstraction, timing etc.

Good work! Navigation Control Unit, for navigating between navpoints? My setup has two modes, heli and airplane, in heli mode it will be controlled like à quad, but in airplane mode it will be controlled like any r/c airplane. So i can work on drafts for the flight control interfaces as a starter since i have quite alot ready.

Some of the blocks are conceptual at the moment.

NCU’s job is to send the craft where you point it. I guess this will take absolute GPS or relative proportional input to determine what you want done. I think this guy will be the brains of the operation.

A tricky aspect here is to layer it so RLP and managed can share the same “objects”. I’m thinking of using simple arrays with predefined STRUCTs for RLP and a friendly .Net object over the same array.

What’s next is to define all the interfaces. I’d love to hear your ideas on properties and methods for each block. Generic enough to abstract any hardware but specific enough to be clear on the function and keep it simple.

I have been over this probably at least 1000 times here, but .NETMF is totally inappropriate for a quadcopter or any other high speed stabilization/control loop.

There is not a single NETMF quadcopter flying, for good reason. Probably at least dozens of these projects have been attempted and failed.

I say this not because I want to rain on your project, but I think you would be far better off focusing your time on a different approach. There have been discussions on using RLP to run the control loops. That may be a reasonable target.

Otherwise, you are probably better off using NETMF as a flight management coprocessor with a non-managed chip running the actual stabilization functions.

@ sıɹɥɔ you are right, think many of us are aware of that, there has been lots of discussions about AHRS’s, which probably is the best solution, and/or RLP. but for the higher level functions, IMHO, netmf will do just fine. And i rather develop my non time-critical software in an object oriented language. So i’m still in for this one

Personally I’ve been off the idea of using NETMF for the time critical functions long ago - but that shouldn’t stop us from having an architecture that would still allow it. I’ll be putting my bets on RLP for now.

The point of this thread is to capture the knowledge of those that have tried into an architecture that may have a good chance of flying eventually.

The idea here is to define blocks that can be implemented using NETMF, RLP or even an external coprocessor or FPGA or something. It is an eco-system of cooperative modules.

What I want to get to first is a clear definition of what these blocks are and what they should do. Once that is clear, it becomes easy to decide something like “The IMU is implemented in RLP and the NCU is implemented in NETMF and the Motor Controller runs on an external board with a serial connection” or whatever. We are first abstracting the function from the hardware.

Right now every Quad project seem to be an IMU and a bunch of procedural code on an Arduino. I’m aiming for something more elegant and understandable (and modular).

Where people can really help is to put in your experience here. It can become a running journal of what works and what doesn’t (from an architecture perspective) - and then the Wiki can become a nice final document on how to build your own.

I think the easiest way to get the modules to “talk” to each other between RLP and NETMF is to use a shared memory model where access to the variables is done atomically. This is (very) loosely based on how CEBUS does it.

So the idea is to have memory organised with all the variables and the modules then either supply or comsume these variables.

The RLP side can create abstraction using STRUCTs and the NETMF side can have an object orientated view over the data model - but they still share common memory.

Here’s a lost of potential variables I collected from a variety of UAV and quad projects. I don’t know if it is complete, but it is a start.

[ulist]
Altitude
Attitude_Q1
Attitude_Q2
Attitude_Q3
Attitude_Q4
Climb_Val_Last
Climb_Val_Now
Climb_Val_Raw
Climb_Val_SampleFreq
Climb_Val_Scale
Climb_Val_Smooth
Climb_Val_Target
Climb_Val_Zero
Forward_Rate_Last
Forward_Rate_Now
Forward_Rate_Raw
Forward_Rate_SampleFreq
Forward_Rate_Scale
Forward_Rate_Smooth
Forward_Rate_Target
Forward_Rate_Zero
Forward_Val_Last
Forward_Val_Now
Forward_Val_Raw
Forward_Val_SampleFreq
Forward_Val_Scale
Forward_Val_Smooth
Forward_Val_Target
Forward_Val_Zero
GroundSpeed
Heading
Latitude
Longitude
Motor1_Rate_Last
Motor1_Rate_Now
Motor1_Rate_Raw
Motor1_Rate_Scale
Motor1_Rate_Smooth
Motor1_Rate_Target
Motor1_Rate_UpdateFreq
Motor1_Rate_Zero
Motor2_Rate_Last
Motor2_Rate_Now
Motor2_Rate_Raw
Motor2_Rate_Scale
Motor2_Rate_Smooth
Motor2_Rate_Target
Motor2_Rate_UpdateFreq
Motor2_Rate_Zero
Motor3_Rate_Last
Motor3_Rate_Now
Motor3_Rate_Raw
Motor3_Rate_Scale
Motor3_Rate_Smooth
Motor3_Rate_Target
Motor3_Rate_UpdateFreq
Motor3_Rate_Zero
Motor4_Rate_Last
Motor4_Rate_Now
Motor4_Rate_Raw
Motor4_Rate_Scale
Motor4_Rate_Smooth
Motor4_Rate_Target
Motor4_Rate_UpdateFreq
Motor4_Rate_Zero
Pitch_Rate_Last
Pitch_Rate_Now
Pitch_Rate_Raw
Pitch_Rate_SampleFreq
Pitch_Rate_Scale
Pitch_Rate_Smooth
Pitch_Rate_Target
Pitch_Rate_Zero
Pitch_Val_Last
Pitch_Val_Now
Pitch_Val_Raw
Pitch_Val_SampleFreq
Pitch_Val_Scale
Pitch_Val_Smooth
Pitch_Val_Target
Pitch_Val_Zero
Quality
RC_Ch1_Val
RC_Ch10_Val
RC_Ch2_Val
RC_Ch3_Val
RC_Ch4_Val
RC_Ch5_Val
RC_Ch6_Val
RC_Ch7_Val
RC_Ch8_Val
RC_Ch9_Val
Roll_Rate_Last
Roll_Rate_Now
Roll_Rate_Raw
Roll_Rate_SampleFreq
Roll_Rate_Scale
Roll_Rate_Smooth
Roll_Rate_Target
Roll_Rate_Zero
Roll_Val_Last
Roll_Val_Now
Roll_Val_Raw
Roll_Val_SampleFreq
Roll_Val_Scale
Roll_Val_Smooth
Roll_Val_Target
Roll_Val_Zero
Servo1_Val_Last
Servo1_Val_Now
Servo1_Val_Raw
Servo1_Val_Scale
Servo1_Val_Smooth
Servo1_Val_Target
Servo1_Val_UpdateFreq
Servo1_Val_Zero
Servo2_Val_Last
Servo2_Val_Now
Servo2_Val_Raw
Servo2_Val_Scale
Servo2_Val_Smooth
Servo2_Val_Target
Servo2_Val_UpdateFreq
Servo2_Val_Zero
Servo3_Val_Last
Servo3_Val_Now
Servo3_Val_Raw
Servo3_Val_Scale
Servo3_Val_Smooth
Servo3_Val_Target
Servo3_Val_UpdateFreq
Servo3_Val_Zero
Servo4_Val_Last
Servo4_Val_Now
Servo4_Val_Raw
Servo4_Val_Scale
Servo4_Val_Smooth
Servo4_Val_Target
Servo4_Val_UpdateFreq
Servo4_Val_Zero
Sideward_Val_Last
Sideward_Val_Now
Sideward_Val_Raw
Sideward_Val_SampleFreq
Sideward_Val_Scale
Sideward_Val_Smooth
Sideward_Val_Target
Sideward_Val_Zero
Time
Yaw_Rate_Last
Yaw_Rate_Now
Yaw_Rate_Raw
Yaw_Rate_SampleFreq
Yaw_Rate_Scale
Yaw_Rate_Smooth
Yaw_Rate_Target
Yaw_Rate_Zero
Yaw_Val_Last
Yaw_Val_Now
Yaw_Val_Raw
Yaw_Val_SampleFreq
Yaw_Val_Scale
Yaw_Val_Smooth
Yaw_Val_Target
Yaw_Val_Zero
[/ulist]

I got my sparkfun 9DOF AHRS last week but have had no time at all to tinker with this. i want to make sure it works in reality before i post something. been thinking about the abstraction, an example for attitude could be, with abstraction: (example. no where near complete)


class Attitude
{
private byte[] _Attitude=new byte [3];

public byte[] ByteRepresenation
{
get { return _Attitude;}
set { _Attitude = value;}
}

public byte Pitch {
get { return _Attitude[0];} 
set { _Attitude [0]=value;}
}

public byte Roll{
get { return _Attitude[1];} 
set { _Attitude [1]=value;}
}

public byte Jaw{
get { return _Attitude[2];} 
set { _Attitude [2]=value;}
}

}


so for .Net code it would be
var myAttitude=new Attitude();
myAttitude.Roll=0xf0;

and for c struct for handling the ByteRepresentation
struct Attitude{
   char *Pitch;
   char *Roll;
   char *Yaw;
};


i think that should work both ways. but as you said there are lots of other things to work out first but i just wanted to see if this would work for you.

public byte Jaw{

That’s a new one on me. What kind of monster you building? :wink:

This is happening… :clap:

Lets make it fly!

I’m overloaded at work at the moment so I’ve just been dumping info instead of unpacking them properly. Let me try again :slight_smile:

The abstraction c0ax is talking about is exactly what I have in mind. You’re just way more clever with this stuff than I am.

The idea with the massive variable dump in my second post is to try and map variables onto memory and then create C# abstraction over them.

First, somewhere, there is a declaration of the shared variable table… just a very big array. Don’t crit the code yet - this is just off the top of my head at the moment…


static protected int[] _FlightVars = new int[250];

Then there is an index mapping to each one…


const Pitch_Rate_Last = 1;
const Pitch_Rate_Now = 2;
const Pitch_Rate_Raw = 3;
const Pitch_Rate_SampleFreq = 4;
const Pitch_Rate_Scale = 5;
const Pitch_Rate_Smooth = 6;
const Pitch_Rate_Target = 7;
const Pitch_Rate_Zero = 8;
const Pitch_Val_Last = 9;
const Pitch_Val_Now = 10;
const Pitch_Val_Raw = 11;
const Pitch_Val_SampleFreq = 12;
const Pitch_Val_Scale = 13;
const Pitch_Val_Smooth = 14;
const Pitch_Val_Target = 15;
const Pitch_Val_Zero = 16;

Now there’s a big array in memory, shared between RLP and NETMF with a common understanding of the structure.

In C#, one could use a nice abstraction:


class Attitude
{

public byte Pitch {
get { return _FlightVars[Pitch_Val_Now];} 
set { _FlightVars[Pitch_Val_Raw]=value;} // Probably not a good idea to set this in NETMF - RLP owns the writing of the raw variables.
}
 
public byte Roll{
get { return _FlightVars[Roll_Val_Now];} 
set { _FlightVars[Roll_Val_Raw]=value;}
}
 
public byte Yaw{
get { return _FlightVars[Yaw_Val_Now];} 
set { _FlightVars[Yaw_Val_Raw]=value;}
}
 
}


On the C side one could just work directly with the vars or make some structs to abstract the same way.

So let’s assume RLP will be doing the PID loop work and NETMF will do the navigation.

In RLP, this might be going on (and forgive me, I’m yet to study the flight control logic). AHRS is calculated from yaw, pitch and roll and stabalised by the Rate variables and then return Quartenions to calculate the true heading.


// PID loop - called from timer.
_FlightVars[Pitch_Val_Raw] = ReadPitch();
_FlightVars[Roll_Val_Raw] = ReadRoll();
_FlightVars[Yaw_Val_Raw] = ReadYaw();

_FlightVars[Roll_Val_Now] = _FlightVars[Roll_Val_Zero] + _FlightVars[Roll_Val_Raw] * _FlightVars[Roll_Val_Scale];
_FlightVars[Roll_Val_Now] = _FlightVars[Roll_Val_Zero] + _FlightVars[Roll_Val_Raw] * _FlightVars[Roll_Val_Scale];
_FlightVars[Roll_Val_Now] = _FlightVars[Roll_Val_Zero] + _FlightVars[Roll_Val_Raw] * _FlightVars[Roll_Val_Scale];

_FlightVars[Yaw_Val_Now] = _FlightVars[Yaw_Val_Zero] + _FlightVars[Yaw_Val_Raw] * _FlightVars[Yaw_Val_Scale];
_FlightVars[Yaw_Val_Now] = _FlightVars[Yaw_Val_Zero] + _FlightVars[Yaw_Val_Raw] * _FlightVars[Yaw_Val_Scale];
_FlightVars[Yaw_Val_Now] = _FlightVars[Yaw_Val_Zero] + _FlightVars[Yaw_Val_Raw] * _FlightVars[Yaw_Val_Scale];

CalcAttitude();

// etc.

Back in C# one can work with the abstraction, but it simply maps over the same variables shared with RLP.

There is a lot of trickery one can now do to speed things up. For example, with careful thought, the result from an I2C read of the Gyro can be directly packed into the Raw variables in the array in one operation, if the variables are declared to be contiguous (next to each other). One can declare a helper Struct over those and you’ll never have to worry about how the memory is packed. This technique can shave off a lot of microseconds in each operation - and that is going to count in the acquisition and PID loop.

For example,


struct
{ 
 int yaw;
 int pitch;
 int roll;
}
gyro

struct
{
int yaw_rate
int pitch_rate
int roll_rate
}
accel

// Now my C is way too rusty to remember how to do this :)
gyro gyro_vals = _FlightVars[22]; - map onto 22, 23, 24
accel accel_rates = _FlightVars[26]; - map  onto 26,27,28

Do you get the idea?

Now I need a lot of help from real programmers to organise all this into structures and classes that make sense and works.

Next I’ll start modelling the NETMF class hierarchy (with your input of course).

[quote]public byte Jaw{

That’s a new one on me. What kind of monster you building?
[/quote]
Hehe, it should be yaw offcourse, but someone might be building a flying shark :wink:
@ realiser good point with const and one big byte array. I wonder though if we’ll run into lock/threading issues having just one array, maybe we need to have more and prioritize them? i am too occupied with work, those ERP’s just wont upgrade themselves! But coming weekend i hope to get something done

I’m not worried about locking and threading issues and here’s why…

Any variable is only ever written to by one provider (or thread) and read from by many consumers. Being a primitive type (int), the hardware really takes care of “locking” access to the variable in an atomic operation.

So far the idea is that there are no threads - just a synchronous scheduler with 3 priority intervals. We’ll have to see.

There’s some method in the madness with these variables:

Pitch_SampleFreq <- Write by Config (during start-up)
Pitch_Scale <- Write by Config (during start-up)
Pitch_Smooth <- Write by Config (during start-up)
Pitch_Zero <- Write by Config (during calibration)
Pitch_Raw <- Write by IMU in Acquisition loop
Pitch_Last <- Write by AHRS in CalcAttitude
Pitch_Now <- Write by AHRS in CalcAttitude
Pitch_Target <- Write by Navigator, because it determines the delta the PID will chase on the Motor controller.

Pitch_SampleFreq -> Read by Scheduler.
Pitch_Scale -> Read by Calc routines.
Pitch_Smooth -> Read by Calc routines.
Pitch_Zero -> Read by Calc routines.
Pitch_Raw -> Read by Calc routines.
Pitch_Last -> Read by Calc routines & Logger.
Pitch_Now -> Read by PID, Calc routines.
Pitch_Target -> Read by PID, Calc routines.

So what about variables vs. arrays vs. objects?

Consider that the following is basically the same thing - just mapped differently on the same block of memory:



// Variables - pinned to specific locations in the linker.
int pitch;
int roll;
int yaw;

// An array of 3 elements.
int[3] pitch_roll_yaw;

// A structure of 3 elements.
struct
{
 int pitch;
 int roll;
 int yaw;
}

// An object with 3 properties.
Gyro.Pitch
Gyro.Roll
Gyro.Yaw

With all this magic abstraction, everything ends up as 2 bytes in a block of memory in the end anyway - so just exploit it!

Now think about the work the routine that reads the Gyro values over I2C has to do - 10,000 times per second…

In .Net, it might look like this:


byte[] buffer = new byte[6];

Read_I2C(0x1, buffer)

Gyro.Pitch = buffer[0] + (buffer[1] << 8) // Not really - just an example.
Gyro.Roll = buffer[2] + (buffer[3] << 8) 
Gyro.Yaw = buffer[4] + (buffer[5] << 8)

That is a LOT of processing under NETMF for a single reading. Hundreds of microseconds - even milliseconds.

It gets lighter copying the data into an array and even lighter putting it into a Struct, but the most efficient way I can see is to pass a pointer to the first element of a block in the “big array” and write straight into that from the I2C reading routine itself. This can only be done in RLP because it exploits pointer manipulation you won’t find in NETMF. Think of it as DMA. In fact I’m looking at possibly exploiting some of the DMA features of the processor to help with some of this.

But NETMF folk must not worry… It will be abstracted in such a way that you won’t know the difference. You’ll be working with simple objects and will probably never see this nastyness in the back room.

Let’s make sure we have enough covered to make it upgradable to this!

[url]Here’s Your Flying Car | Hackaday

This vid is going viral in UAV circles… I think I would wear more protective gear. A helmet doesn’t seem like enough.

OMG!

Have you heard the old software architecture saying… “If I built my bridge with your software, would you walk over it?”… now upgraded to “If I built my UAV with your firmware, would you ride it?”

Awesome.

Chain mail is too heavy :wink:

I discovered something this week that has me all excited about this project again.

http://www.zeromq.org

Now think about the possibilities of an architecture where all the blocks can easily communicate with each other using messages - with no threads, no locks, no critical sections… just plain messages on queues - across different platforms, transports and technology.

These messages can fly between the RLP and C# worlds “in-process”, hop on a serial connection to an external module, jump over TCP/IP to a smartphone in your hand… without you having to worry how it does it.

A message orientated programming model is so elegant and simple for this kind of project! I’m making it my mission to port 0MQ to Fez as soon as possible. Who knows what else we’d be able to do with it on the TINYCLR platform!

So here is a conceptual architecture that uses Brokerless Message Queueing as the foundation for the inter-module communication. Think of the green lines as message queues and messages are transferred between modules.

  • The Ground Station sends Navigation Requests to the Absolute Positioning Unit (the Navigator).

  • The IMU and ARU runs on a deterministic timer in RLP. IMU collects measurements from the intertial sensors and posts them to the ARU. ARU crunches the numbers (in C) and sends results on to the APU.

  • APU runs in NETMF (I’ll just call it C# for now). It takes the input from the ground and the environment and creates a Navigational Intent message to the CPU. It is saying where we currently are and where we want to be and by when we need to be there. C# is good for this kind of “business logic”.

  • The CPU looks at the Navigational Intent as well as other inputs that may have been arriving and issues a command to the IQU. The command basically says “Increase thrust to 10 on motor 3 in the next 1 ms.”

  • The IQU takes the command and schedules it, based on priority, for the next slot that will make changes. It issues a Scheduled Command to the Motor Control Unit. It is basically saying “At exactly timer tick 723432, increase PWM on motor 3 to 10”.

  • The MCU acts on the instuctions in a deterministic way. It runs C in RLP on a timer - say 400Hz. It may be necessary to feed the Quartenion data directly from the ARU to the MCU to create a “hard wired PID loop” - but I hope not. The point here is we can hook data providers and consumers up to “feeds” in a pretty flexible way.

So why on earth do this and not just have a call-back from the IMU to the ARU and the ARU to the APU and the APU to the MCU? Because this is exactly where things get tightly coupled, brittle and difficult to trace. How would you put a GPS into the equation for example (and not change a whole bunch of base code)? By using (very very fast) messages, the coupling, inter-process comms, threading and locking problems go away like magic.

Too good to be true? Let’s see!

Realiser, did you got anywhere with ZeroMQ on NetMF ?