13 个不稳定版本 (4 个重大更改)

0.6.3 2021 年 9 月 9 日
0.6.2 2021 年 9 月 9 日
0.5.2 2021 年 9 月 2 日
0.5.1 2021 年 8 月 30 日
0.2.1 2020 年 1 月 16 日

#36机器人 中排名

每月 29 次下载

MIT 许可证

415KB
1.5K SLoC

Tello 无人机

Tello 无人机有两种接口。基于文本的接口和非公开接口,由原生应用程序使用。来自 tellopilots 论坛 的人们通过逆向工程这个接口,并为 go、python 等公共存储库提供支持,做了令人惊叹的工作。

这个库结合了与无人机通信并获取元数据的网络协议,并提供了一个远程控制框架,用于简化与键盘或摇杆的连接。

在源代码中,您可以找到如何创建 SDL-Ui 并使用键盘控制无人机的示例。您可以使用以下命令运行它:cargo run --example fly

请注意,高级动作需要在明亮的环境中完成。(翻转、弹跳等)

通信

当无人机接收到启用包(drone.connect(11111);)时,Tello 无人机将在两个 UDP 通道上发送数据。A 命令通道(端口:8889)和B(WIP)视频通道(默认:端口:11111)。在 AP 模式下,无人机将显示默认 IP 192.168.10.1。所有发送调用都是同步进行的。要接收数据,您必须轮询无人机。以下是一个示例:

示例

use tello::{Drone, Message, Package, PackageData, ResponseMsg};
use std::time::Duration;

fn main() -> Result<(), String> {
    let mut drone = Drone::new("192.168.10.1:8889");
    drone.connect(11111);
    loop {
        if let Some(msg) = drone.poll() {
            match msg {
                Message::Data(Package {data: PackageData::FlightData(d), ..}) => {
                    println!("battery {}", d.battery_percentage);
                }
                Message::Response(ResponseMsg::Connected(_)) => {
                    println!("connected");
                    drone.throw_and_go().unwrap();
                }
                _ => ()
            }
        }
        ::std::thread::sleep(Duration::new(0, 1_000_000_000u32 / 20));
    }
}

命令模式

您可以将无人机切换到命令模式。要返回到“自由飞行模式”,您必须重新启动无人机。CommandMode 会提供以下信息:

  • state_receiver(): Option<Receiver<CommandModeState>>: 解析来自无人机的状态包。您将获得所有权,您只能这样做一次。
  • video_receiver(): Option<Receiver<Vec<u8>>>: 来自无人机的视频帧(h264)。您将获得所有权,您只能这样做一次。
  • odometry: Odometry 移动测距数据。

示例

use futures::executor::block_on;
use std::{string::String, thread::sleep, time::Duration};
use tello::Drone;

fn main() -> Result<(), String> {
    block_on(async {
        let mut drone = Drone::new("192.168.10.1:8889").command_mode();
        let state = drone.state_receiver().unwrap();
        drone.enable().await?;

        // if you use "tokio_async" this will be a `tokio::sync::watch::Receiver`
        match state.recv_timeout(Duration::from_secs(5)) {
            Ok(message) => println!(
                "Battery {}% Height {}dm POS {:?}",
                message.bat, message.h, drone.odometry
            ),
            _ => println!("No state package received"),
        }

        println!("take_off {:?}", drone.take_off().await);

        for _ in 0..6 {
            println!("forward {:?}", drone.forward(30).await);
            println!("cw {:?}", drone.cw(60).await);
        }

        println!("land {:?}", drone.land().await);
        Ok(())
    })
}

遥控器

此轮询不仅接收来自无人机的消息,还会发送一些默认设置,回复确认,触发关键帧或发送实时移动命令的遥控器状态。

无人机包含一个rc_state来控制移动。例如:drone.rc_state.go_down()drone.rc_state.go_forward_back(-0.7)

以下示例展示了如何使用SDL打开窗口,处理键盘输入以及连接游戏手柄或操纵杆。

示例

use sdl2::event::Event;
use sdl2::keyboard::Keycode;
use tello::{Drone, Message, Package, PackageData, ResponseMsg};
use std::time::Duration;

fn main() -> Result<(), String> {
    let mut drone = Drone::new("192.168.10.1:8889");
    drone.connect(11111);

    let sdl_context = sdl2::init()?;
    let video_subsystem = sdl_context.video()?;
    let window = video_subsystem.window("TELLO drone", 1280, 720).build().unwrap();
    let mut canvas = window.into_canvas().build().unwrap();

    let mut event_pump = sdl_context.event_pump()?;
    'running: loop {
        // draw some stuff
        canvas.clear();
        // [...]

        // handle input from a keyboard or something like a game-pad
        // ue the keyboard events
        for event in event_pump.poll_iter() {
            match event {
                Event::Quit { .. }
                | Event::KeyDown { keycode: Some(Keycode::Escape), .. } =>
                    break 'running,
                Event::KeyDown { keycode: Some(Keycode::K), .. } =>
                    drone.take_off().unwrap(),
                Event::KeyDown { keycode: Some(Keycode::L), .. } =>
                    drone.land().unwrap(),
                Event::KeyDown { keycode: Some(Keycode::A), .. } =>
                    drone.rc_state.go_left(),
                Event::KeyDown { keycode: Some(Keycode::D), .. } =>
                    drone.rc_state.go_right(),
                Event::KeyUp { keycode: Some(Keycode::A), .. }
                | Event::KeyUp { keycode: Some(Keycode::D), .. } =>
                    drone.rc_state.stop_left_right(),
                //...
            }
        }

        // or use a game pad (range from -1 to 1)
        // drone.rc_state.go_left_right(dummy_joystick.axis.1);
        // drone.rc_state.go_forward_back(dummy_joystick.axis.2);
        // drone.rc_state.go_up_down(dummy_joystick.axis.3);
        // drone.rc_state.turn(dummy_joystick.axis.4);

        // the poll will send the move command to the drone
        drone.poll();

        canvas.present();
        ::std::thread::sleep(Duration::new(0, 1_000_000_000u32 / 20));
    }
}

依赖项

~1–10MB
~87K SLoC