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

Has anyone decoded the log headers/messages from the Tello?

I could only guess. It seems odd they need 6 of them. I did a google search and there are several scholarly articles on optical flow uncertainty. Seems like some next level stuff.


I'll have to look into that, and at least compare it to the yaw value from the imu. Looking at some of my position plots it seems like it is able to regain the position again really well even when optical flow is lost. I wonder if they arn't also factoring inertial measurements into the position data.

I was curious about those reserved fields. They do look like flags. Maybe it will be more obvious when they are separated.
I didn't look close enough to see that the IMU computes a Yaw (via the quaternion just like the other platforms). Since the Tello doesn't have a compass this Yaw is probably being computed by integrating the Z axis gyro with corrections from the accelerometers. Does that sound right? If that were the case I'd expect Yaw to be initialized at 0.0, but your 3 .DATs show it being initialized to 3 different values.

The other DJI platforms fuse IMU, compass, and GPS data. The "GPS" data you see in the IMU record is actually computed from inertial, compass and GPS data. It wouldn't surprise me if the Tello is fusing IMU and MVO data.

I've seen references to a "vision" compass. Any idea what this is? The MVO data doesn't have anything that could be construed as a "vision" compass.

The MVO Engineered signals will be in the next CsvView/DatCon release.

I don't have a Tello. Maybe I should get one, looks like you guys are having fun. Just what I need - another drone and project....
 
Cool guys !!
Finally flight logs are analyzed.

Hi Krag,
You seemed to make a mistake about log format.
I think it should be..

1 byte magic : 55
2 bytes length : 5C 00
1 byte hdr checksum : E1
2 byes id : 1D 00
4 bytes tick : 68 71 35 05

Below codes show the data format.
BudWalkerJava/DatCon

ending number of the class name seems to mean log id.
e.g) new_mvo_feedback_29
 
Last edited:
  • Like
Reactions: Krag
The changes to TelloLib to add position and orientation are up below. It should be easy to adapt this code to Go and Python for the other libraries.

Basically you need to Ack cmd id 4176 to start getting 4177. Then send the payload of 4177 to parseLog(). No need to Ack 4177. 4178 and sendAckLogConfig()) are NOT required.

Tello interface now includes Position and Orentation! · Kragrathea/TelloLib@cf1a9b0
FYI, the reserved1 field contains bits that specify if the velocity and position values are valid. Here is the code that will be in the next DatCon
printCsvValue(Vel_X, mvoVel, "velX", lineT, ((flags & 0x01) != 0));
printCsvValue(Vel_Y, mvoVel, "velY", lineT, ((flags & 0x02) != 0));
printCsvValue(Vel_Z, mvoVel, "velZ", lineT, ((flags & 0x04) != 0));
printCsvValue(Pos_X, mvoPos, "posX", lineT, ((flags & 0x10) != 0));
printCsvValue(Pos_Y, mvoPos, "posY", lineT, ((flags & 0x20) != 0));
printCsvValue(Pos_Z, mvoPos, "posZ", lineT, ((flags & 0x40) != 0));
flags is the variable name for the reserved1.

Also, height is valid only if heightUncertainty < 100.0
printCsvValue(height, mvoHeight, "height", lineT, (heightUncertainty < 100.0f));

Pardon the explanation by way of the Java code. But, I'm sure you'll know what it says.:)
 
BudWalker - to save me a lot of trial and error, can you tell me the offset of the flags byte? Also, is there another 'flags' field for the quaternion data?

fscorx - yes - once I've got the above sorted out!
 
BudWalker - to save me a lot of trial and error, can you tell me the offset of the flags byte? Also, is there another 'flags' field for the quaternion data?

fscorx - yes - once I've got the above sorted out!
I assume you mean the flags byte in the MVO record. That's at 76 offset in the payload.
public void process(Payload _payload) {
super.process(_payload);
try {
valid = true;
visionObservationCount = _payload.getUnsignedShort(0);
Vel_X = ((float) _payload.getShort(2)) / 100.0f;
Vel_Y = ((float) _payload.getShort(4)) / 100.0f;
Vel_Z = ((float) _payload.getShort(6)) / 100.0f;
Pos_X = ((float) _payload.getFloat(8)) / 100.0f;
Pos_Y = ((float) _payload.getFloat(12)) / 100.0f;
Pos_Z = ((float) _payload.getFloat(16)) / 100.0f;
hoverPointUncertainty1 = _payload.getFloat(20);
hoverPointUncertainty2 = _payload.getFloat(24);
hoverPointUncertainty3 = _payload.getFloat(28);
hoverPointUncertainty4 = _payload.getFloat(32);
hoverPointUncertainty5 = _payload.getFloat(36);
hoverPointUncertainty6 = _payload.getFloat(40);
velocityUncertainty1 = _payload.getFloat(44);
velocityUncertainty2 = _payload.getFloat(48);
velocityUncertainty3 = _payload.getFloat(52);
velocityUncertainty4 = _payload.getFloat(56);
velocityUncertainty5 = _payload.getFloat(60);
velocityUncertainty6 = _payload.getFloat(64);
height = _payload.getFloat(68);
heightUncertainty = _payload.getFloat(72);
flags = _payload.getUnsignedByte(76);
// reserved2 = _payload.getUnsignedByte(77);
// reserved3 = _payload.getUnsignedByte(78);
// reserved4 = _payload.getUnsignedByte(79);
} catch (Exception e) {
RecordException(e);
}
}

If you're referring to the quaternion in the IMU record those 4 floats start at 48 offset in the payload of the IMU record.
public void process(Payload _payload) {
super.process(_payload);
valid = true;
longRad = payloadBB.getDouble(0);
latRad = payloadBB.getDouble(8);
baroRaw = payloadBB.getFloat(16);
accelX = payloadBB.getFloat(20);
accelY = payloadBB.getFloat(24);
accelZ = payloadBB.getFloat(28);
gyroX = (float) Math.toDegrees(payloadBB.getFloat(32));
gyroY = (float) Math.toDegrees(payloadBB.getFloat(36));
gyroZ = (float) Math.toDegrees(payloadBB.getFloat(40));
baroSmooth = payloadBB.getFloat(44);
quatW = payloadBB.getFloat(48);
quatX = payloadBB.getFloat(52);
quatY = payloadBB.getFloat(56);

quatZ = payloadBB.getFloat(60);
ag_X = payloadBB.getFloat(64);
ag_Y = payloadBB.getFloat(68);
ag_Z = payloadBB.getFloat(72);
velN = payloadBB.getFloat(76);
velE = payloadBB.getFloat(80);
velD = payloadBB.getFloat(84);
gb_X = payloadBB.getFloat(88);
gb_Y = payloadBB.getFloat(92);
gb_Z = payloadBB.getFloat(96);
magX = payloadBB.getShort(100);
magY = payloadBB.getShort(102);
magZ = payloadBB.getShort(104);
imuTemp = (payloadBB.getShort(106)) / 100.0;
i2 = payloadBB.getShort(108);
i3 = payloadBB.getShort(110);
i4 = payloadBB.getShort(112);
i5 = payloadBB.getShort(114);

The source for DatCon can be found here
DatCon source
 
  • Like
Reactions: SMerrony
I didn't look close enough to see that the IMU computes a Yaw (via the quaternion just like the other platforms). Since the Tello doesn't have a compass this Yaw is probably being computed by integrating the Z axis gyro with corrections from the accelerometers. Does that sound right? If that were the case I'd expect Yaw to be initialized at 0.0, but your 3 .DATs show it being initialized to 3 different values.
Yes. The initial values are possibly because when I set it down after turning it on?

I've seen references to a "vision" compass. Any idea what this is? The MVO data doesn't have anything that could be construed as a "vision" compass.
I think it is a fake compass that just uses the vision system to simulate one. When you start whatever direction it establishes with the vision system is "north" or whatever.

I don't have a Tello. Maybe I should get one, looks like you guys are having fun. Just what I need - another drone and project....
[/QUOTE]
You should! It is huge fun and you have a good reason with your DatCon project. :)
 
Cool guys !!
Finally flight logs are analyzed.

Hi Krag,
You seemed to make a mistake about log format.
I think it should be..

1 byte magic : 55
2 bytes length : 5C 00
1 byte hdr checksum : E1
2 byes id : 1D 00
4 bytes tick : 68 71 35 05

Below codes show the data format.
BudWalkerJava/DatCon

ending number of the class name seems to mean log id.
e.g) new_mvo_feedback_29
Yeah, that makes more sense. :) I'll fix it.
 
We should try to put together a glossary of the fields in the logs. Some are obvious, and some are obviously worthless, but there could also be some useful info in the more obscure ones.
 
Yep. That is exactly what I had in mind. :) I should have known you would have one started. How complete is it?
It's way out of date. I just haven't kept up with the documentation. If you want to know about a particular field it may be documented here.

Also, this documentation only describes the Engineered fields. By definition the DatDefined fields don't have any documentation.
 
I've just release v0.4.0 of my Go package which incorporates the MVO and IMU fields discussed above.

SMerrony/tello

If someone could verify the quaternion data that would be appreciated :)
 
  • Like
Reactions: fcsorx
I've just release v0.4.0 of my Go package which incorporates the MVO and IMU fields discussed above.

SMerrony/tello

If someone could verify the quaternion data that would be appreciated :)

C:\Users\Giuseppe\go\src\github.com\SMerrony\telloterm>go run telloterm.go joystick.go
# command-line-arguments
.\telloterm.go:447:51: newFd.MVO undefined (type tello.FlightData has no field or method MVO)
.\telloterm.go:448:51: newFd.MVO undefined (type tello.FlightData has no field or method MVO)
.\telloterm.go:449:51: newFd.MVO undefined (type tello.FlightData has no field or method MVO)
.\telloterm.go:451:47: newFd.MVO undefined (type tello.FlightData has no field or method MVO)
.\telloterm.go:452:47: newFd.MVO undefined (type tello.FlightData has no field or method MVO)
.\telloterm.go:453:47: newFd.MVO undefined (type tello.FlightData has no field or method MVO)
.\telloterm.go:455:47: newFd.IMU undefined (type tello.FlightData has no field or method IMU)
.\telloterm.go:456:47: newFd.IMU undefined (type tello.FlightData has no field or method IMU)
.\telloterm.go:457:47: newFd.IMU undefined (type tello.FlightData has no field or method IMU)
.\telloterm.go:458:47: newFd.IMU undefined (type tello.FlightData has no field or method IMU)
.\telloterm.go:458:47: too many errors

... is telloterm updated with the latest release v0.4.0 of Go package ?
 
I've just release v0.4.0 of my Go package which incorporates the MVO and IMU fields discussed above.

SMerrony/tello

If someone could verify the quaternion data that would be appreciated :)

Hello Stephen, I'm really a newbie with golang. I wrote a function to convert quaternions to euler angles (source wikipedia) to verify rotation along z-axis (yaw):

func toEuler(qX float32, qY float32, qZ float32, qW float32) (float32, float32, float32) {
var sinp, sinr, cosr, siny, cosy, pitch, roll, yaw float64
sinp = 2.0 * float64(qW * qY - qZ * qX)
if math.Abs(sinp) >= 1 {
pitch = math.Pi / 2 * sinp / math.Abs(sinp)
} else {
pitch = math.Asin(sinp)
}

sinr = +2.0 * float64(qW * qX + qY * qZ)
cosr = +1.0 - 2.0 * float64(qX * qX + qY * qY)
roll = math.Atan2(sinr, cosr)

siny = +2.0 * float64(qW * qZ + qX * qY)
cosy = +1.0 - 2.0 * float64(qY * qY + qZ * qZ)
yaw = math.Atan2(siny, cosy)

return float32(pitch), float32(roll), float32(yaw)
}

Pitch, roll , yaw are in radians (multiply 180/pi to have degrees). I didn't write a file log, but "by visual inspection", rotation on z-axis return correct values.
I instead didn't figure out if position is working correctly.
 
  • Like
Reactions: SMerrony
I didn't realize I didn't check in the toEuler code to TelloLib. I am lousy at math so I have no idea if that code is right or not. This is toEuler was adapted from DatCon. Can anyone tell if amounts to the same thing?

Code:
        double[] toEuler(float qX,float qY, float qZ, float qW)
        {
            double sqW = qW * qW;
            double sqX = qX * qX;
            double sqY = qY * qY;
            double sqZ = qZ * qZ;
            double yaw = 0.0;
            double roll = 0.0;
            double pitch = 0.0;
            double[] retv = new double[3];
            double unit = sqX + sqY + sqZ + sqW; // if normalised is one, otherwise
                                                 // is correction factor
            double test = qW * qX + qY * qZ;
            if (test > 0.499 * unit)
            { // singularity at north pole
                yaw = 2 * Math.Atan2(qY, qW);
                pitch = Math.PI / 2;
                roll = 0;
            }
            else if (test < -0.499 * unit)
            { // singularity at south pole
                yaw = -2 * Math.Atan2(qY, qW);
                pitch = -Math.PI / 2;
                roll = 0;
            }
            else
            {
                yaw = Math.Atan2(2.0 * (qW * qZ - qX * qY),
                        1.0 - 2.0 * (sqZ + sqX));
                roll = Math.Asin(2.0 * test / unit);
                pitch = Math.Atan2(2.0 * (qW * qY - qX * qZ),
                        1.0 - 2.0 * (sqY + sqX));
            }
            retv[0] = pitch;
            retv[1] = roll;
            retv[2] = yaw;
            return retv;
        }
 
I didn't realize I didn't check in the toEuler code to TelloLib. I am lousy at math so I have no idea if that code is right or not. This is toEuler was adapted from DatCon. Can anyone tell if amounts to the same thing?

Code:
        double[] toEuler(float qX,float qY, float qZ, float qW)
        {
            double sqW = qW * qW;
            double sqX = qX * qX;
            double sqY = qY * qY;
            double sqZ = qZ * qZ;
            double yaw = 0.0;
            double roll = 0.0;
            double pitch = 0.0;
            double[] retv = new double[3];
            double unit = sqX + sqY + sqZ + sqW; // if normalised is one, otherwise
                                                 // is correction factor
            double test = qW * qX + qY * qZ;
            if (test > 0.499 * unit)
            { // singularity at north pole
                yaw = 2 * Math.Atan2(qY, qW);
                pitch = Math.PI / 2;
                roll = 0;
            }
            else if (test < -0.499 * unit)
            { // singularity at south pole
                yaw = -2 * Math.Atan2(qY, qW);
                pitch = -Math.PI / 2;
                roll = 0;
            }
            else
            {
                yaw = Math.Atan2(2.0 * (qW * qZ - qX * qY),
                        1.0 - 2.0 * (sqZ + sqX));
                roll = Math.Asin(2.0 * test / unit);
                pitch = Math.Atan2(2.0 * (qW * qY - qX * qZ),
                        1.0 - 2.0 * (sqY + sqX));
            }
            retv[0] = pitch;
            retv[1] = roll;
            retv[2] = yaw;
            return retv;
        }

Hello Krag,
I believe that quaternions coming from IMU are normalized: so there is likely no need to perform normalization test.
Regarding the equivalence of the codes: I guess that differences depend on the choice of rotation axis sequence for Euler angles (to be verified, that isn't my field of expertise).
From a practical point of view: I implemented both codes and I found the same outputs for yaw axis. Actually I can't think of functional use for pitch and roll: except someone would like to monitor them during flips?
 

New Posts

Members online

No members online now.

Forum statistics

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

New Posts