Powershell helper for fast & reliable 360° panorama waypoint missions

Hi everyone,

I wanted to share a PowerShell script I’ve been working on that might be useful for anyone building more automated or repeatable 360° missions in Litchi for later panorama stitching from recorded video footage.

What it does

The advantage here is efficiency: portrait video gives you higher vertical coverage per frame, and continuous recording avoids stop-and-go capture delays. Combined with the even Fibonacci distribution, this results in a dataset that stitches cleanly into a high-resolution panorama or even a full 360° environment, depending on your pitch limits.

In short, you’re trading discrete photo capture for a smooth, continuous acquisition pass—while still preserving the geometric consistency needed for reliable stitching.

The script generates a ready-to-import .csv mission file for the Litchi Flight Hub using nothing more than a set of geo coordinates. It distributes waypoints over a partial sphere using a Fibonacci lattice and adds a micro-orbit pattern, which results in smooth and continuous capture— especially useful for 360° or structured scanning shots.

Key features

  • Automatic waypoint generation from a single lat/lon
  • Fibonacci-based spherical distribution for even coverage
  • Micro-orbit motion for allowing Litchi to recognize different waypoints
  • Configurable overlap between shots
  • Adjustable gimbal pitch limits (top to bottom coverage)
  • Optional portrait mode (9:16)
  • Built-in video start option at mission begin
  • Outputs directly to a Litchi-compatible CSV

The Fibunacci pattern in the 2D view as one picture may describe things better than thound words:

And a video of what the route would look like:

Raw video footage:

Usage examples

.\Generate-LitchiMission.ps1 -Latitude 52.5200 -Longitude 13.4050

With more options

.\Generate-LitchiMission.ps1 -Latitude 52.5200 -Longitude 13.4050 -Altitude 120 -PortraitMode -VideoMode

The created .csv file can be found in the folder of the powershell script.

The powershell script

Generate-LitchiMission.ps1
<#
.SYNOPSIS
    Generates a Litchi mission (Fibonacci spiral) as a CSV file for import into Litchi Flight Hub.

.DESCRIPTION
    Creates waypoints distributed over a partial sphere using a Fibonacci lattice.
    Applies a micro-orbit pattern to enable smooth, continuous video capture.

.PARAMETER Latitude
    Latitude of the capture location.

.PARAMETER Longitude
    Longitude of the capture location.

.PARAMETER PortraitMode
    Enables 9:16 (portrait) calculation.

.PARAMETER MinOverlap
    Desired overlap between shots (e.g., 0.30 = 30%).
    Default value is 0.30.

.PARAMETER MaxPitch
    Maximum upward gimbal tilt angle in degrees.
    Default value is 60.

.PARAMETER MinPitch
    Maximum downward gimbal tilt angle in degrees.
    Default value is -90.

.PARAMETER Radius
    Micro-orbit radius in meters.
    Default value is 50.

.PARAMETER Speed
    Drone speed factor (e.g., 0.30 = 0.3 meters/s).
    Default value is 0.30.

.PARAMETER VideoMode
Starts video recording at the first waypoint.

.PARAMETER Filename
    Output CSV file path.
    Default value is ".\litchi_360-degree-fibonacci-$Latitude_$Longitude.csv".

.EXAMPLE
    .\Generate-LitchiMission.ps1 -Latitude 52.5200 -Longitude 13.4050

.EXAMPLE
    .\Generate-LitchiMission.ps1 -Latitude 52.5200 -Longitude 13.4050 -Altitude 120 -PortraitMode -VideoMode

.NOTES
    Outputs a CSV file compatible with Litchi Flight Hub mission import.
#>

param(
    [Parameter(Mandatory=$true)][double]$Latitude,
    [Parameter(Mandatory=$true)][double]$Longitude,

    [double]$Altitude = 50,

    [switch]$PortraitMode,
    [double]$MinOverlap = 0.30,

    [double]$MinPitch = -90,
    [double]$MaxPitch = 60,

    [double]$Radius = 1.5,
    [double]$Speed = 0.3,

    [switch]$VideoMode,

    [string]$Filename = ".\litchi_360-degree-fibunacci-$Latitude-$Longitude-$Altitude.csv"
)

$culture = [System.Globalization.CultureInfo]::InvariantCulture

# --- 1. FOV & POINT CALCULATION ---
if ($PortraitMode) {
    $hFOV = 48.0; $vFOV = 75.0; $FormatName = "9:16 (Portrait)"
} else {
    $hFOV = 75.0; $vFOV = 48.0; $FormatName = "16:9 (Landscape)"
}

$hEff = ($hFOV * [Math]::PI / 180.0) * (1 - $MinOverlap)
$vEff = ($vFOV * [Math]::PI / 180.0) * (1 - $MinOverlap)
$omega_frame = $hEff * $vEff

$zMin = [Math]::Sin($MinPitch * [Math]::PI / 180.0)
$zMax = [Math]::Sin($MaxPitch * [Math]::PI / 180.0)
$zRange = $zMax - $zMin
$area_segment = 2 * [Math]::PI * $zRange
$requiredPoints = [Math]::Ceiling($area_segment / $omega_frame)

$PointsToGenerate = if ($requiredPoints -gt 99) { 99 } else { [Math]::Min(99, ($requiredPoints + 5)) }

Write-Host "Format: $FormatName | Radius: $Radius m" -ForegroundColor Cyan
Write-Host "Points: $PointsToGenerate (calculation for coverage)" -ForegroundColor Yellow

# --- 2. CSV HEADER ---
# Header needs to fit the Litchi format!
$header = "latitude,longitude,altitude(m),heading(deg),curvesize(m),rotationdir,gimbalmode,gimbalpitchangle," +
          "actiontype1,actionparam1,actiontype2,actionparam2,actiontype3,actionparam3,actiontype4,actionparam4," +
          "actiontype5,actionparam5,actiontype6,actionparam6,actiontype7,actionparam7,actiontype8,actionparam8," +
          "actiontype9,actionparam9,actiontype10,actionparam10,actiontype11,actionparam11,actiontype12,actionparam12," +
          "actiontype13,actionparam13,actiontype14,actionparam14,actiontype15,actionparam15," +
          "altitudemode,speed(m/s),poi_latitude,poi_longitude,poi_altitude(m),poi_altitudemode,photo_timeinterval,photo_distinterval"

$sb = New-Object System.Text.StringBuilder
[void]$sb.AppendLine($header)

# --- 3. GENERATION ---
$GoldenAngle = (2 * [Math]::PI) * (1 - 1/((1 + [Math]::Sqrt(5)) / 2))
$TotalTheoreticalPoints = [Math]::Round($PointsToGenerate * (2.0 / $zRange))

$EarthRadius = 6378137
$dLat_Per_M = 1 / ($EarthRadius * [Math]::PI / 180)

$count = 0

for ($i = 0; $i -lt $TotalTheoreticalPoints; $i++) {
    $z = -1 + (2 * $i / ($TotalTheoreticalPoints - 1))

    if ($z -ge $zMin -and $z -le $zMax) {
        if ($count -ge $PointsToGenerate) { break }

        # CAlculate angles
        $pitchDeg = ([Math]::Asin($z) * 180.0 / [Math]::PI)
        $theta = $i * $GoldenAngle
        $headingDeg = ($theta * 180.0 / [Math]::PI) % 360
        if ($headingDeg -lt 0) { $headingDeg += 360 }

        # Micro-orbit coordinates
        $dLat = [Math]::Cos($theta) * $Radius * $dLat_Per_M
        $latRad = $Latitude * [Math]::PI / 180
        $dLon = [Math]::Sin($theta) * $Radius * (1 / ($EarthRadius * [Math]::Cos($latRad) * [Math]::PI / 180))

        $sLat = ($Latitude + $dLat).ToString($culture)
        $sLon = ($Longitude + $dLon).ToString($culture)
        $sAlt = $Altitude.ToString($culture)
        $sHead = $headingDeg.ToString($culture)
        $sPitch = $pitchDeg.ToString($culture)
        $sCurve = ($Radius * 0.5).ToString($culture)
        $sSpeed = $Speed.ToString($culture)


        $act1_Type = "-1"; $act1_Param = "0"

        # Video Trigger at first point
        if ($VideoMode -and $count -eq 0) {
            $act1_Type = "2" # Start Recording
            $act1_Param = "0"
        }

        # Building strings
        $actionsString = "$act1_Type,$act1_Param"

        for ($k=0; $k -lt 14; $k++) {
            $actionsString += ",-1,0"
        }

        # AltitudeMode, hardcoded to 0 (relative to home / takeoff).
        $altMode = "0"

        # rotationdir=0, gimbalmode=2
        $line = "$sLat,$sLon,$sAlt,$sHead,$sCurve,0,2,$sPitch," +
                "$actionsString," +
                "$altMode,$sSpeed,0,0,0,0,-1,-1"

        [void]$sb.AppendLine($line)
        $count++
    }
}

try {
    $sb.ToString() | Out-File -FilePath $Filename -Encoding ASCII
    Write-Host "CSV successfully created: $Filename" -ForegroundColor Green
} catch { Write-Error $_ }

Testing

I have tested the created waypoint missions with my DJI Mini 3 Pro.

From what I understand one may good to use those settings:

  • Set a fitting White Balance for the conditions manually (for not having any color-shifts within parts of the pictures)
  • ISO 100
  • Allow the Shutter Speed do its’ thing in auto

This should produce the most crisp panorama images from my experiences. I was trying to set the SS manually and let lighting be adjusted by ISO being in auto - but results were having less contrast.

Stitching of panoramas

For stitching I am using some possibly not very well known Windows application, called “Image Compositing Editor”, the software allows to easily feed not only images, but videos for it working on a panorama. Additionally it allows for some further post corrections, also having a “auto-complete” function for filling gaps with some sort of algorithms (not known how exactly its done, think without any AI yet as it is an older piece of software already).

Example footage of stitched material

Raw & not further post edited panorama from the Image Composite Editor application:

Another example in Google Maps: Google Maps

Last comments

With the video footage of the waypoint missions I am getting very reliable results in ways of not having any huge gaps for any ground related objects - skies with fastly moving clouds or foggy conditions may create some artifacts.

If anyone is experimenting with automated capture workflows, 360 imagery, or just wants a faster way to build complex waypoint paths, feel free to try it out.

Example of a resulting route which has been imported as CSV: Litchi Hub

If anybody wants to take this proof-of-concept away to create even better solutions, please feel free - I would be happy for a backlink for me then knowing your better solution.

Best regards

2 Likes

Very cool,aFRI. I’ve looked at ICE’s “video pano” and wondered how to make it useful. I’ll be trying this…Thanks for your work!

1 Like

Such a nice post man! That was very clever! I want to try it!

1 Like

Worked great! I had to turn the video on and turn my Mini3 camera to Portrait before running the mission. and then in ICE (Image Compositing Editor) I cropped that “startup” camera extra video off ‘though I don’t know if it matters…anyways, it makes a nice Pano with good resolution just a tiny skyhole..oh, also works with Landscape too though the skyhole is going to be a little larger. (and there are several ways to fill a skyhole if it matters)

That was the thing I did forget, that in my poc I was working with manual start / stop. I left it there as I learned DJI may not allow SDKs available for all drones - so for the Mini 3 Pro, I need to adjust camera settings anyway manually & left it as is.

Glad it is working out for you as well @gorm !