Indoor flight with external navigation data


For autonomous indoor flight, My first try is using optitrack motion capture system to simulate GPS coordinates to copter. The basic idea is to transform motion capture system local coordinate to GPS WGS84 coordinate and use GPS_TYPE=MAV(14) to feed GPS data to copter. The test result is here, it works ok. However there is a problem with this approach. It still need magnetometer/compass onboard copter to navigate. But compass performance could be very unstable due to many indoor interference, for example, air condition. and loudspeaker.

Thanks to @priseborough great job and @peterbarker great job, we can feed both position and orientation data to copter from external source. So that copter can fly autonomously without magnetometer. I use use mavlink message ATT_POS_MOCAP to send external nav data to copter through wifi (COMPASS_USE = COMPASS_USE2 = COMPASS_USE3 = 0, EK2_GPS_TYPE=3). The reason why I use ATT_POS_MOCAP instead of VISION_POSITION_ESTIMATE is that former accept quaternion and later accept euler angles. Because optitrack data API ouput quaternion. I do not need to convert it to euler angles.

I use optitrack motion capture system, iMotive software and parrot bebop running arducopter

First, I setup optitrack motion capture system. I install 5 markers on the copter. According to optitrack user manual, at least 4 markers is required for rigid body orientation. In the 1st time setup, I put copter in the optirack origin point, align copter to the optitrack x axis and copter nose to x positive (optitrack use right hand coordinate system).
Optitrack positive X will be north of our indoor environment (it need not be real north). Then I select all 5 markers to create a rigid body, this rigid body will be used to track position and orientation of copter.

system architecture is like: optitrack --> desktop PC (running iMotive and modified SampleClientML.exe ) - wifi -> arducopter
My code is modified from optitrack natnet sdk sample code SampleClientML.cs
Basically I only add following code to send position and orientation data to copter

MAVLink.mavlink_att_pos_mocap_t att_pos = new MAVLink.mavlink_att_pos_mocap_t();`
att_pos.time_usec = (ulong)((cur - my_start).TotalMilliseconds * 1000);`
att_pos.x = rbData.x; //north`
att_pos.y = rbData.z; //east`
att_pos.z = -rbData.y; //down`
att_pos.q = new float[4] { rbData.qw, rbData.qx, rbData.qz, -rbData.qy };`
pkt = mavlinkParse.GenerateMAVLinkPacket10(MAVLink.MAVLINK_MSG_ID.ATT_POS_MOCAP, att_pos);
mavSock.SendTo(pkt, drone.ep);

Note that ardupilot will drop consecutive att_pos_mocap message less than 70ms

void NavEKF2_core::writeExtNavData(const Vector3f &sensOffset, const Vector3f &pos, const Quaternion &quat, float posErr, float angErr, uint32_t timeStamp_ms, uint32_t resetTime_ms)
    // limit update rate to maximum allowed by sensor buffers and fusion process
    // don't try to write to buffer until the filter has been initialised
    if ((timeStamp_ms - extNavMeasTime_ms) < 70) {
    } else {
        extNavMeasTime_ms = timeStamp_ms;

Basically you do not need to send vision estimated messages faster than 15hz

You need to use SET_GPS_GLOBAL_ORIGIN to assign lat/lng of EKF origin for auto takeoff in guided mode (thanks @khancyr). It do not need to be the correct lat/lng, any valid lat/lng will work. This also makes copter able to fly auto mission. However, use lat/lng to assign waypoint may be a little difficult for indoor environment. I create a PR support MAV_FRAME_LOCAL_NED in MISSION_ITEM for auto mode. It allow me to use local coordinate to assign waypoints, which is more convenient. Please note that arducopter minimum RTL altitude is 2m. It may be a little high for indoor flight. You can change it in RTL_ALT_MIN in ArduCopter/config.h

Here is my test flight video. It is a parrot bebop running ardupilot master. It is auto takeoff and then switch to circle mode. The result is quite well and reliable (no magnetic interference from indoor environment)

this is my ardupilot parameters
mav.parm (20.9 KB)


Please note that I just provided plumbing for other people’s work - most
notably, of course, @priseborough’s.

This is so, so cool! Hopefully we can get this to the point that your
work is trivially replicable so people can push the technology forward.


Really excellent work! It’s extremely stable! I’m super happy to see people making use so quickly of the efforts we put into improving our consumption of the mavlink vision-position-estimate messages.

1 Like

Note: auto takeoff only works in loiter mode

Another test flight video, Two copters fly in circle mode, face to face


@chobitsfan congratulation for your work!

It would be awesome to see something similar but using some kind of Visual Odometry (ie. SVO) feeding VISION_POSITION_ESTIMATE message instead of Motion Capture.

This is in my ToDo list but for me is an hobby and I have so little time to spend in this. Maybe sooner or later I will succeed. :slight_smile:


I got it. Thank you :slight_smile:

Cool flights! Highly impressed.
I’ve tried VICON_POSITION_ESTIMATE with my poor man’s indoor positioning system, too. It looks working fine.
Thanks for these great works!

1 Like

That is very good. It would be better if you shared the code and maybe create a sort of Tutorial. I feel there is a dramatic lack of good tutorial on this matter…

It would be better if you shared the code and maybe create a sort of Tutorial.

Not really a tutorial, but I updates with more details of my experiments. I hope it helps.

Very nice job! I have been using GPS_INPUT instead and it is working good

@chobitsfan Do you have any recommendations on parameter settings to achieve so little variance in position? Here is a video of my results using Arducopter SITL, flying in guided mode, attempting to hover at a stationary point. As you can see there is a lot of variance in the roll axis, over a meter. Any suggestions on improving tracking of the set position would be greatly appreciated!

hi @gs1mm0ns

sorry for late reply. I have attached my parameters in the end of my post. I hope it helps.

As you can see there is a lot of variance in the roll axis, over a meter

Maybe the axis of motion capture system and axis of ardupilot is mis-align?

Nice work. I’m trying to do something similar but in my case I’m only interested in x,y z estimates and not the angles. VISION_POSITION_ESTIMATE message has the angles, is there a way to ignore them and use only positioning information? Thanks

1 Like

Hi, I have the same need, I want to just input position (x,y,z) and not orientation (roll, pitch, yaw) because I don’t have that information.

For a simple solution I was thinking of passing a “zero” rotation and a very big angErr (spherical angle error in radians).
Is that a good idea @priseborough ?

Have you tried that solution? I manged to send the position to the controller and it looks to be working, it shows the location in the mission planner. I’m passing the angles in the VISION_POSITION_ESTIMATE message as zero, so I don’t know whats really happening inside with this data. Didn’t try to fly the quad yet.

No, I didn’t tested it yet.
Let me know if you can fly with it!

Maybe someone with more knowledge of this system can tell if it’s a good idea.

Just tried to fly it but no success. I’m using apriltags and a camera to get the position, I can see that the position is being updated in MP, it looks good but it can’t hold the position in flight. I don’t have much space here at the office but it didn’t look to be working properly.
Let me know if you have success with your setup.

By the way it seems that if you’re using compass for orientation the external orientation data it’s ignored.

    // if compass is disabled, also use it [the external nav] for yaw
    if (!use_compass()) {
        extNavUsedForYaw = true;
        if (!yawAlignComplete) {
            extNavYawResetRequest = true;
            magYawResetRequest = false;
            gpsYawResetRequest = false;
            finalInflightYawInit = true;
        } else {
    } else {
        extNavUsedForYaw = false;

I’m not using the compass. I’ve found that video position axis were not correct. Looking at the MP now it makes much more sense when I move it around, basically X was Y and Y was X. Need to fly it again and check if it can hold position.