Can't get Guided, Indoor flight using no GPS or Compass: "Need Position Estimate"

Hello everyone.

I am looking to create a script that does the following: Arm my quadcopter then hover at 1m altitude, hover in place, rotate slowly in yaw, then descend. Since this will be indoors, I can’t use GPS and have so far been not using a compass.

I’m running my scripts from a companion pi zero w on the drone itself through pymavlink which is connected through a UART connection.

Here’s some context:

Root issue: EKF stuck in CONST_POS_MODE, POS_HORIZ_REL never set

  • In Mission Planner’s Flight Data > Status tab ekfflag is always = 167

EKF flags always show:
ATTITUDE, VELOCITY_HORIZ, VELOCITY_VERT, POS_VERT_ABS, CONST_POS_MODE

POS_HORIZ_REL is never set. GUIDED mode requires POS_HORIZ_REL to arm (“Need Position Estimate”).

Hardware

  • ArduCopter 4.6.3 on Matek F405-HDTE flight controller
  • Raspberry Pi Zero W on the drone as companion computer
  • MTF-02P sensor: combined optical flow + ToF rangefinder (single unit)
    • Optical flow → EK3_SRC1_VELXY=5
    • Rangefinder → EK3_SRC1_POSZ=2, reading ~2cm on ground
  • No GPS installed
  • No compass installed

Relevant parameters (confirmed from FC)

Parameter Value Notes
EK3_SRC1_POSXY 0 None (optical flow gives velocity, not pos)
EK3_SRC1_VELXY 5 Optical flow
EK3_SRC1_POSZ 2 Rangefinder
EK3_SRC1_VELZ 0 None
EK3_SRC1_YAW 1 Compass — but no compass installed ← problem
COMPASS_USE 0 Compass disabled
EK3_GSF_RUN_MASK 3 GSF running on both IMUs
EK3_GSF_USE_MASK 3 GSF use enabled
EK3_MAG_CAL 3
FLOW_TYPE 5 MTF-02P
RNGFND1_TYPE 10 MTF-02P
RNGFND1_MIN_CM 0
RNGFND1_MAX_CM 600
RNGFND1_GNDCLEAR 2 Set to match actual ground reading
ARMING_CHECK 0 All pre-arm checks bypassed
GUID_OPTIONS 8 SET_ATTITUDE_TARGET thrust = true throttle
FS_EKF_ACTION 0 EKF failsafe disabled

Sensor status (confirmed working)

  • Rangefinder: reading 0.020m on ground via DISTANCE_SENSOR MAVLink message
  • Optical flow: quality 110-130, velocity data flowing via OPTICAL_FLOW MAVLink message
  • EKF origin: accepted when sent via set_gps_global_origin_send() — FC logs “EKF3 IMU0 origin set”

Things tried

Fake GPS origin

  • send_gps_global_origin_send() with Canberra coords
  • FC accepts it: logs “EKF3 IMU0 origin set” and “Field Elevation Set: 0m”
  • EKF flags completely unchanged — CONST_POS_MODE remains

EK3_SRC1_YAW=8 (GSF)

  • Hard-rejected at arm time: “AHRS: EK3 sources require GPS”
  • GSF bootstraps from GPS velocity changes — dead end without GPS

RNGFND1_GNDCLEAR=2

  • Matched to actual ground reading (was 10, sensor reads 2cm)
  • No effect on EKF flags

EK3_SRC1_YAW=6 (ExternalNav / VisualOdom)

  • FC error: “AHRS: EK3 sources require VisualOdom”
  • Attempted to satisfy this by sending VISION_POSITION_DELTA (ardupilotmega dialect),
    ATT_POS_MOCAP, and VISION_POSITION_ESTIMATE from the Pi — all ignored/rejected
  • Root cause: VISO_TYPE param does not exist on this firmware build — the VisualOdom
    subsystem is not compiled in. EK3_SRC1_YAW=6 is entirely unavailable.

EK3_SRC1_YAW=0 (None)

  • “Need Position Estimate” — same as YAW=1
  • EKF has no heading at all, can’t fuse optical flow into position estimate

  • I’m running out of ideas to try, I’ve tried the ideas on this post but it seems that the ekfflag = 167 doesn’t change when I lift up the drone manually.

Any ideas on what’s keeping CONST_POS_MODE stuck?

Thank you for you time!

Have you set the EKF origin point via right-clicking on the map in Mission Planner?

EK3_SRC1_POSZ=2 is wrong unless you can guarantee that the overflown surface is absolutely flat. Using surface tracking with baro set as POSZ source is the recommended way.

ARMING_CHECK=0 is rarely the correct choice, and this is not one of those cases, your drone should work with ARMING_CHECK=1. You need fully calibrated and compensated compass for optical flow navigation.

For OF navigation to work to work you need.

  • Healthy optical flow, the sensor must report sufficiently high quality score.
  • Healthy rangefinder, present and reporting range within configured limits.
  • EKF origin externally set.
  • EKF configured to use optical flow as velocity source.
2 Likes

Thanks for your reply!

Yes, I’ve already tried setting it both manually and with pymavlink: send_gps_global_origin_send(). With the pymavlink the FC reports back “EKF3 IMU0 origin set”; hasn’t worked with either

This is the correct way. Ensure you got this right:

drone.mav.set_gps_global_origin_send(
    0,                  # target_system 0 = broadcast
    int(lat*1e7),       # latitude
    int(lon*1e7),       # longitude
    int(alt*1000)       # altitude in mm
)

After sending, you can verify it worked by checking the GCS map which should show an icon where drone EKF origin’s location is

Thanks for checking this out!

I tried setting EK3_SRC1_POSZ=1 to use Baro, but still hasn’t changed.

Also tried with ARMING_CHECK=1 and the FC error changes from “Need Position Estimate” to “AHRS: waiting for home”; looking into that now.

For your comments on OF navigation:

  • There is healthy optical flow, Flight Data > Status > opt_qua reports >110
  • Healthy rangefinder: I test by holding drone sensor over my desk viewing down and reported range matches a physical tape measurer, and I can put my finger under the sensor and the reported values change back to 2cm.
  • EKF Origin has been set, see my comments above
  • EKF_SRC1_VELXY is set to 5, where the options show “OpticalFlow”

You need fully calibrated and compensated compass for optical flow navigation.

I’m curious about this, I was under the impression I wouldn’t need a compass for optical nav? I had read that there could be strong interference when flying inside and was opting not to use one.

Thanks for sending this! This is indeed what I’m using, and an icon of my drone pops up in Mission Planner as expected:

FAKE_LAT = int(-35.363262 * 1e7)
FAKE_LON = int(149.165237 * 1e7)
FAKE_ALT = 0  # mm above sea level

master = mavutil.mavlink_connection(SERIAL_PORT, baud=BAUD_RATE)

# (much later in the script)
log("--- Sending fake GPS origin ---")
master.mav.set_gps_global_origin_send(
	master.target_system,
	FAKE_LAT,
	FAKE_LON,
	FAKE_ALT
)

What’s your rangefinder min distance? Should be lesser than the reading it should give when the drone is on the ground. Better set it to 1cm.

1 Like

I’m using:

RNGFND1_MIN_CM,0
RNGFND1_GNDCLEAR,10
RNGFND1_MAX_CM,600

I was curious if GNDCLEAR was throwing an issue but haven’t tested that.

Can you just set min_cm to 1 or 2 and see anyway?

Checked, neither worked :confused:

Maybe its due to no compass

Optical flow sensors typically don’t provide yaw rate data. EKF needs to know where the North is to correctly integrate your velocity.

2 Likes

Setting the EKF origin point via right-clicking on the map in Mission Planner indeed works (I have used it many times in indoor missions with only MTF-01 (no companion)). The sign that shows it is working is that the drone icon appears immediately on map.

Two points here, 1. for builds without GPS / compasses how do they provide yaw data?

  1. I have been spoofing the yaw data with the below function, iirc this got past an error but didn’t allow flight. I can run this again later and provide logs

def send_vision_position_estimate(master, yaw=0.0):
“”"
This is for spoofing the yaw value for EKF.
Send VISION_POSITION_DELTA (ardupilotmega dialect) — the incremental odometry
message that ArduPilot’s VisualOdom subsystem (EK3_SRC1_YAW=6) actually uses.
We send zero deltas with a fixed yaw, giving the EKF a heading reference.
“”"
usec = int(time.time() * 1e6) & 0xFFFFFFFFFFFFFFFF
master.mav.vision_position_delta_send(
usec,
50000, # time_delta_usec (50ms)
[0.0, 0.0, yaw], # angle_delta [roll, pitch, yaw] radians
[0.0, 0.0, 0.0], # position_delta [x, y, z] meters
50.0 # confidence (0-100)
)

Only things left are mocap or visual (inertial) odometry.

I have confirmed that the drone icon appears on my map from both right clicking and from function call but this still has no flight. Note that my ekfflag in the Flight Data page continues to read 167, still blocked by CONST_POS_MODE

I can post some sample logs in a bit, but here’s the script I’m running hover-v3.py

Next, check optical flow positioning and orientation (over a not uniform colours floor):

  • If you move the drone north, it should move north on MP;
  • If you move the drone east, it should move east;

No need to arm or fly for this check. 1-2m movements are enough.

Can you do indoor missions (without and with the companion)?