Programming

Stop Fighting Your Hardware: Why Letting the Flight Controller Do Its Job Changed How I Think About System Design

A

Admin User

Author

Jun 25, 2026
5 min read
14 views
Stop Fighting Your Hardware: Why Letting the Flight Controller Do Its Job Changed How I Think About System Design

I spent three hours last month debugging a Python script that was supposed to control a robotic arm. The arm would jerk sporadically, miss targets by centimeters, and generally behave like it was actively resisting my commands. Turns out, I was sending conflicting instructions—my high-level logic was fighting the arm's built-in stabilization routine instead of working with it. Reading through a deep dive on autonomous drone control reminded me why this happens so often in embedded systems: we treat specialized hardware like dumb actuators when they're actually sophisticated decision-makers.

The article I came across was about something very specific—keeping a GPS-denied drone hovering indoors using only optical flow and LiDAR. But the real lesson buried in there applies to any system where you've got a capable microcontroller and a companion computer trying to cooperate. The author stumbled onto a problem that probably deserves more attention in our industry: the architecture question of who decides what, and when.

The Problem Nobody Warns You About: Mode Conflicts

Here's the scenario that got my attention. A developer manages to get their drone airborne in GUIDED mode by directly sending attitude commands to bypass ArduPilot's GPS requirement. Smart workaround. But then they switch to LOITER mode expecting stable hovering, and immediately the drone drops like someone cut the strings.

The reason? LOITER is designed as a hybrid mode. It's supposed to blend autonomous stabilization with pilot input. So when the flight controller entered LOITER, it checked the RC transmitter. The throttle stick was at zero—a legacy behavior that meant "descend" to the firmware. The drone obeyed.

This is the kind of bug that makes you want to throw equipment out a window, because the hardware is working perfectly. The bug is purely architectural. Two subsystems with different assumptions about what the current state means. I've seen this exact pattern in production code—different parts of the system building conflicting mental models of reality.

The Elegant Solution: Velocity Commands and Bitmasks

Instead of fighting the firmware's assumptions, the developer stayed in GUIDED mode and used a bitmask-based velocity command. By setting VELOCITY_MASK = 3527, they told the Pixhawk: "Ignore everything except velocity. I'm commanding zero velocity on all axes."

This is genuinely clever. Rather than wrestle with mode semantics, they found a communication protocol that's unambiguous. The flight controller now has a single job: maintain zero velocity. If optical flow detects drift, the EKF3 sensor fusion automatically adjusts motor outputs. No conflicting commands. No guessing about pilot intent.

The velocity-over-position approach also reveals something important about sensor constraints. Position estimates drift indoors without GPS. But velocity is something the flight controller can measure right now through optical flow. That's a much stronger signal to work with.

What Worked, What Didn't, and What I'd Question

I appreciate the architecture here—separating decision-making (companion computer) from execution (flight controller). But I noticed the failsafe implementation, while good instinct, is still incomplete.

Their emergency handler catches exceptions and keyboard interrupts, which covers maybe 70% of real failure modes. What if the serial connection drops? What if the MAVLink message queue backs up? A truly reliable system would also include heartbeat monitoring. If the flight controller stops receiving velocity commands after, say, 500 milliseconds, it should auto-trigger a failsafe regardless of what the companion computer thinks it's doing.

I would also be cautious about how long this approach stays reliable. The continuous velocity hold loop runs every 0.5 seconds. For a proof-of-concept, fine. But in production indoor drone operations with multiple units, I'd want better telemetry on IMU saturation and sensor fusion confidence metrics. The optical flow sensor can be fooled by featureless walls or rapid motion. You need visibility into why the drone is where it is.

The Bigger Lesson

What stuck with me most is this: the moment a system has enough autonomy to make decisions, your job as an engineer shifts. You're no longer trying to micromanage every output. You're negotiating with intelligent hardware.

Stop sending granular commands that fight the controller's internal logic. Instead, define clear boundaries—"maintain this velocity"—and let the hardware do what it's built for: sensor fusion, stabilization, and recovery. Your companion computer focuses on strategy. The flight controller handles tactics.

This principle applies whether you're working with drones, robots, embedded systems, or even distributed backend services. Architecture matters more than you think, and "talking to your hardware in its language" beats "forcing it to listen to yours" every single time.

What Would You Do Differently?

I'm curious how others handle the autonomy handoff in their embedded systems. Would you trust the failsafe approach described here, or do you layer additional safety mechanisms? Drop a note—I'm genuinely interested.

Source: This post was inspired by "Building a Custom Autonomous Drone Stack - Part 3: The Zero-Velocity Hover" by Dev.to. Read the original article

Share this article

Written by Adil Sher

Full stack developer building high-traffic platforms, AI services, and custom web applications. Explore my portfolio, learn about my background, or get in touch.

Related Articles

Code Isn't Engineering Until You Stop Thinking It Is
Programming Jul 17

Code Isn't Engineering Until You Stop Thinking It Is

Someone asked me last week what I actually do for a living. Not in the polite "oh that's nice" way—genuinely curious. I said "I'm a developer" and watched their face go blank. They nodded like I'd said "I work in an office." It hit me that I couldn't explain my job any better tha...

PHP Forms Aren't Broken—Your Expectations Are
Programming Jul 16

PHP Forms Aren't Broken—Your Expectations Are

I spent two hours last week debugging a form that "wasn't working." The client said data wasn't being saved. I pulled up the network tab, saw the POST request going out clean, checked the database, and found... nothing. Then I looked at the form's action attribute. It was pointin...