Hello Tello Pilot!
Join our DJI Tello community & remove this banner.
Sign up

Unofficial Standalone Golang Tello Package Released

I have just released v0.9.0 of my Tello package. SMerrony/tello

The release improves robustness, but does have a couple of API changes.

Details...

This release adds a new function StopStickListener() which should be used when restarting/reconnecting to the drone.

The old StartVideo() function has been renamed to the more correct GetVideoSpsPps() as it actually requests SPS & PPS frames in the video stream. It should probably be called every 0.5 ~ 2.0 seconds for a smooth stream.

MVO position data is now only stored if all three (X, Y & Z) valid flags are OK - this seems to result in much better track fidelity.

There were a couple of minor changes to avoid potential race conditions.

API Changes
  • StopStickListener() has been added - as above
  • StartVideo() becomes GetVideoSpsPps() - as above
  • FlightData.OverTemp becomes FlightData.ErrorState as the flag seems to appear on other error conditions

The last two API changes may require updates in your code
 
I have just released v0.9.0 of my Tello package. SMerrony/tello

The release improves robustness, but does have a couple of API changes.

Details...

This release adds a new function StopStickListener() which should be used when restarting/reconnecting to the drone.

The old StartVideo() function has been renamed to the more correct GetVideoSpsPps() as it actually requests SPS & PPS frames in the video stream. It should probably be called every 0.5 ~ 2.0 seconds for a smooth stream.

MVO position data is now only stored if all three (X, Y & Z) valid flags are OK - this seems to result in much better track fidelity.

There were a couple of minor changes to avoid potential race conditions.

API Changes
  • StopStickListener() has been added - as above
  • StartVideo() becomes GetVideoSpsPps() - as above
  • FlightData.OverTemp becomes FlightData.ErrorState as the flag seems to appear on other error conditions

The last two API changes may require updates in your code

I'm curious into how the MVO flags are verified. I've been using the autopilot function for a bit now, and it seems that it is never consistent about where it is; sometimes, it will move continuously in an unspecified direction until it hits a wall, or it incur a large amount of drift from its original position. Sometimes it takes a while to figure out where to angle, as well.

I got the drone to output its x, y and yaw in a custom version of autopilot_test.go once it reaches certain points, by doing this:

currentYaw := drone.fd.IMU.Yaw currentX := drone.fd.MVO.PositionX currentY := drone.fd.MVO.PositionY log.Println(currentYaw, currentX, currentY)

I've attached my code to the next post.

A table is below of two attempts, showing its output. In both attempts, the drone takes off from the same place (a tiled floor), in the same orientation.



Attempt 1​

Attempt 2​

Home Coords - SetHome​

-2 -5.1685343 -3.424315​

1 17.323376 -16.950474

Navigation 1 Coords - drone.AutoFlyToXY(0, 0.5)
(between Navigation 1 and 2, it does drone.AutoTurnByDeg(-90))​

-2 -5.0918627 -3.1843414​

1 17.327072 -16.350191


Navigation 2 Coords - drone.AutoFlyToXY(0.5, 0.5)​

2 -0.009670662 0.015946528 (it has rotated at this point)

-92 17.961498 -16.209326

Navigation 3 Coords - drone.AutoFlyToXY(0, 0)​

N/A (The drone reverses and crashes into a wall)​

-92 17.094069 -17.255774


As it can be seen, the coordinates are inconsistent between attempts.

Could this be due to errors in conversion?
 
@SMerrony
Code is here:

Code:
// autopilot_test.go

// Copyright (C) 2018  Steve Merrony and then FlandersNed

// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package tello

import (
    "log"
    "testing"
    "time"
        "os/exec"
)

var (
    drone      Tello
)


func TestLaava(t *testing.T) {

    err := drone.ControlConnectDefault()
    if err != nil {
        log.Fatalf("CCD failed with error %v", err)
    }
    log.Println("Connected to Tello control channel")
    time.Sleep(2 * time.Second)
       
        startVideo()
       
        time.Sleep(250 * time.Millisecond)
       
        startDisplay()
       
        time.Sleep(3 * time.Second)
       
        log.Println("Taking off...")
       
    drone.TakeOff()
   
    time.Sleep(6 * time.Second)
       
       
        log.Println("Flying to height 1.2 Metres")
       
    done, err := drone.AutoFlyToHeight(12)
    if err != nil { // should go up to 1.25m
        t.Errorf("Error %v calling AutoFlyToHeight(12)", err)
    }
    <-done
   
    log.Println("Done to fly height")
   
    time.Sleep(2 * time.Second)
       
        log.Println("Set Home")
       
        err = drone.SetHome()
    if err != nil {
        t.Errorf("Error %v calling SetOrigin()", err)
    }
   
    currentYaw := drone.fd.IMU.Yaw
        currentX := drone.fd.MVO.PositionX
        currentY := drone.fd.MVO.PositionY
       
        log.Println(currentYaw, currentX, currentY)
       
        time.Sleep(1 * time.Second)
       
        log.Println("Commence coordinate flying.")
       
        time.Sleep(1 * time.Second)

    done, err = drone.AutoFlyToXY(0, 0.5)
    if err != nil { // should fly forward 50cm
        t.Errorf("Error %v calling AutoFlyTo(0.0, 0.50)", err)
    }
    <-done
   
    time.Sleep(3 * time.Second)
       
        currentYaw1 := drone.fd.IMU.Yaw
        currentX1 := drone.fd.MVO.PositionX
        currentY1 := drone.fd.MVO.PositionY
       
        log.Println(currentYaw1, currentX1, currentY1)

    log.Println("Navigation 1 completion notified")
       
        log.Println("Turning by 90 degrees counterclockwise")
   
    doneYB, err := drone.AutoTurnByDeg(-90)
    if err != nil { // should rotate counterclockwise 90 deg
        t.Errorf("Error %v calling AutoTurnByDeg(-90)", err)
    }
    <-doneYB
   
    log.Println("Done to turn counterclockwise")
       
        time.Sleep(1 * time.Second)

    done, err = drone.AutoFlyToXY(0.5, 0.5)
    if err != nil { // should fly to next location
        t.Errorf("Error %v calling AutoFlyTo(0.5, 0.5)", err)
    }
    <-done
   
    time.Sleep(3 * time.Second)
       
        currentYaw2 := drone.fd.IMU.Yaw
        currentX2 := drone.fd.MVO.PositionX
        currentY2 := drone.fd.MVO.PositionY
       
        log.Println(currentYaw2, currentX2, currentY2)

    log.Println("Navigation 2 completion notified")
   
        done, err = drone.AutoFlyToXY(0, 0)
    if err != nil { // should fly to home
        t.Errorf("Error %v calling AutoFlyTo(0.0, 0.0)", err)
    }
    <-done
       
        time.Sleep(3 * time.Second)
    log.Println("Navigation completion notified")
       
        currentYaw3 := drone.fd.IMU.Yaw
        currentX3 := drone.fd.MVO.PositionX
        currentY3 := drone.fd.MVO.PositionY
       
        log.Println(currentYaw3, currentX3, currentY3)
       
        log.Println("Turning by 90 degrees clockwise")
   
    doneBB, err := drone.AutoTurnByDeg(90)
    if err != nil { // should rotate counterclockwise 40 deg
        t.Errorf("Error %v calling AutoTurnByDeg(90)", err)
    }
    <-doneBB
   
    log.Println("Done to turn counterclockwise")
       
        time.Sleep(1 * time.Second)

        log.Println("Landing...")
       
    drone.Land()
       
       
        time.Sleep(3 * time.Second)

    drone.ControlDisconnect()
    log.Println("Disconnected normally from Tello")
}

func startVideo() {
   
        videochan, err := drone.VideoConnectDefault()
    if err != nil {
        log.Fatalf("Tello VideoConnectDefault() failed with error %v", err)
    }
       
        //log.Println(videochan)
    // start external mplayer instance...
    // the -vo X11 parm allows it to run nicely inside a virtual machine
    // setting the FPS to 60 seems to produce smoother video
    var record *exec.Cmd
   
        record = exec.Command("mplayer", "-",  "-nosound", "-fps", "60", "-dumpstream", "-dumpfile", "test.mp4")

    recordIn, err := record.StdinPipe()
    if err != nil {
        log.Fatalf("Unable to get STDIN for mplayer %v", err)
    }
    if err := record.Start(); err != nil {
        log.Fatalf("Unable to start mplayer - %v", err)
        return
    }
   
    // start video feed when drone connects
    drone.GetVideoSpsPps()
    go func() {
        for {
            drone.GetVideoSpsPps()
            time.Sleep(500 * time.Millisecond)
        }
    }()

    go func() {
        for {
            vbuf := <-videochan
            _, err := recordIn.Write(vbuf)
            if err != nil {
                log.Fatalf("Error writing to mplayer %v\n", err)
            }
        }
    }()
        // recording using mplayer. WARNING: Either the bitrate or the RAM is not large enough to record and view the screen at the same time with multiple channels!
       /* go func() {
        for {
            vbufR := <-videochan
            _, err := recordIn.Write(vbufR)
            if err != nil {
                log.Fatalf("Error recording %v\n", err)
            }
        }
    }()*/
       
}


func startDisplay() {
    var player *exec.Cmd
    //you may be able to use 'tee' to copy the pipe to another process
    player = exec.Command("mplayer", "-fps", "30", "test.mp4")
       
    if err := player.Start(); err != nil {
        log.Fatalf("Unable to start player - %v", err)
        return
    }
}
 
Hi @FlandersNed ,

Thanks for the detailed report.

Firstly, you are pushing the auto-flight funcs pretty hard by asking them to move the drone just 50cm - the tolerance built-in to the package (exported as AutoXYToleranceM) is 30cm. I found that figure by experimentation - much lower and the automatic flights often never quite end if there is even a mild breeze.

Secondly, your 'Attempt 2' seems to have behaved correctly - do you agree?

'Attempt 1' certainly looks like trash :confused: But that coordinate response at the end of 'Navigation 2' looks very much like the low-light situation I have tried to eliminate with the latest release - are you using v0.9.0 of the package?
 
I have just released v0.9.0 of my Tello package. SMerrony/tello

The release improves robustness, but does have a couple of API changes.

Details...

This release adds a new function StopStickListener() which should be used when restarting/reconnecting to the drone.

The old StartVideo() function has been renamed to the more correct GetVideoSpsPps() as it actually requests SPS & PPS frames in the video stream. It should probably be called every 0.5 ~ 2.0 seconds for a smooth stream.

MVO position data is now only stored if all three (X, Y & Z) valid flags are OK - this seems to result in much better track fidelity.

There were a couple of minor changes to avoid potential race conditions.

API Changes
  • StopStickListener() has been added - as above
  • StartVideo() becomes GetVideoSpsPps() - as above
  • FlightData.OverTemp becomes FlightData.ErrorState as the flag seems to appear on other error conditions

The last two API changes may require updates in your code

What are the XYZ valid flags? Are they part of the MVO log files, or something you calculated yourself? I'd like to implement that in my project as well
 
  • Like
Reactions: clydemcqueen
Hi @FlandersNed ,

Thanks for the detailed report.

Firstly, you are pushing the auto-flight funcs pretty hard by asking them to move the drone just 50cm - the tolerance built-in to the package (exported as AutoXYToleranceM) is 30cm. I found that figure by experimentation - much lower and the automatic flights often never quite end if there is even a mild breeze.

Secondly, your 'Attempt 2' seems to have behaved correctly - do you agree?

'Attempt 1' certainly looks like trash :confused: But that coordinate response at the end of 'Navigation 2' looks very much like the low-light situation I have tried to eliminate with the latest release - are you using v0.9.0 of the package?

Sorry for the extremely late reply, but yes - this was using the 0.9.0 package.
 
I have just released v0.9.1.

Besides some minor documentation changes this release fixes the joystick mapping function so that the full range of values is now sent to the Tello.

This means it is now possible to use the 'engine start' function via joysticks in the lower-inward position for more controlled takeoffs.

Fully in-and-down joysticks now cause the Tello to start the motors.

:cool:
 
Hello,

I would like to ask you for help. I am trying to start video stream from Tello Drone but receiving the following error. It seems that there is some issue with the GetVideoSpsPps. The error is pointing to this line in GetVideoSpsPps() tello.ctrlConn.Write(packetToBuffer(pkt)). Thank you in advance.

Code:
2018/11/08 21:03:05 Connected to Tello control channel
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x10f280d]

goroutine 1 [running]:
github.com/SMerrony/tello.(*Tello).GetVideoSpsPps(0x1237020)
    /usr/local/Cellar/go/1.11.2/libexec/src/github.com/SMerrony/tello/video.go:116 +0xed
main.startVideo()
    /Users/xxxxx/Library/Application Support/LiteIDE/liteide/goplay.go:132 +0x21f
main.main()
    /Users/xxxxx/Library/Application Support/LiteIDE/liteide/goplay.go:47 +0xf2
exit status 2

Error: process exited with code 1.
 
Last edited:
It's difficult to guess what's causing that without seeing the code you are using to make the calls into the tello package.

My first guess would be that you haven't successfully established a network connection to the Tello via eg. ControlConnectDefault(), and a video connection via eg, VideoConnectDefault() before requesting the first SPS/PPS data.
 
The error was making two duplicit instances of the drone...

1) var drone tello.Tello
2) drone := new(tello.Tello)

Code:
package main

import (
    "log"
    "flag"
    "os/exec"
    "time"
    "github.com/SMerrony/tello"
)

var (
    drone tello.Tello
    x11Flag = flag.Bool("x11", false, "Use '-vo x11' flag in case mplayer takes over entire window")
)

func main() {
    drone := new(tello.Tello)
    err := drone.ControlConnectDefault()
    if err != nil {
        log.Fatalf("CCD failed with error %v", err)
    }
    log.Println("Connected to Tello control channel")
    
    ...
 
Not sure if this is just my drone, but running either this or TelloTerm leads to a rapid increase in temperature recorded by the drone.

It goes from 35 degrees to 65 degrees in about 2 minutes; it actually shuts off soon after this due to the excessive temp.
 
Not sure if this is just my drone, but running either this or TelloTerm leads to a rapid increase in temperature recorded by the drone.

It goes from 35 degrees to 65 degrees in about 2 minutes; it actually shuts off soon after this due to the excessive temp.
This is normal behaviour(!) for the Tello, and nothing to do with my package.

See this thread and this one for more details.
 
  • Like
Reactions: FlandersNed
Hi Everyone,

I just released a standalone Go (Golang) package for the Tello at GitHub.com/SMerrony/tello

It couldn't have been built without information gleaned from this forum - so thanks guys!

There's also an enhanced version of my tello-desktop telloterm application which uses the package - it's linked to in the README.

Hope someone else has some fun with this :)

Steve

Thank you so much for writing and sharing this code!
I have not yet done much with it, but so far it worked smoothly.
I am looking forward to exploring it in more depth.

For everyone who speaks German or is happy with a translator, I wrote a post on my blog:
Tello GoLang - programming beyond SDK

Greetings, Martin
 

New Posts

Members online

No members online now.

Forum statistics

Threads
5,690
Messages
39,934
Members
17,023
Latest member
Repiv

New Posts