[Rust] Rust 操控大疆可编程 tello 无人机
💥 内容涉及著作权,均归属作者本人。若非作者注明,默认欢迎转载:请注明出处,及相关链接。
Summary: 大疆的 tello 无人机也提供了可编程的接口,官方已经支持了 Scratch 图形化编程。由此分析,我们可以得出 tello 无人机实际上提供了 2 个接口:tello 无人机应用程序使用的基于文本的接口,以及一个非公共接口。因为提供了开放的接口,才能和图形化编程进行文本交互,实现用户的编程控制。
大疆旗下最便宜的无人机品牌 tello 采用了英特尔的视觉处理芯片,虽然相比于大疆御、悟等系列,功能简陋。但比起与其它如小米和华强北的众多品牌,可算的上非常有用的玩具了。
大疆的 tello 无人机也提供了可编程的接口,官方已经支持了 Scratch 图形化编程。由此分析,我们可以得出 tello 无人机实际上提供了 2 个接口:tello 无人机应用程序使用的基于文本的接口,以及一个非公共接口。因为提供了开放的接口,才能和图形化编程进行文本交互,实现用户的编程控制。在 tellopilots 论坛(微信公众号不能贴连接,请自行搜索),有玩家做了很棒的工作,对 tello edu app 的编程界面进行了反向工程,从而可以支持其它诸如 python、golang 等……
但本文讨论的主角是 Rust。
因为 tello 无人机是通过网络协议于操作器(手机、手柄等)交互通信的。因此,我们可以结合了网络协议与无人机进行通信,并获得可用的元数据。
当然,籍此拓展思维之上,我们也可以提供一个远程控制框架,用键盘或操纵杆来控制。甚至更为简化,命令组合为批处理方式,然后简单触发(想象一下好莱坞大片)。
我们简单尝试下,从原理分析,到编码实现——
和 tello 无人机通信
首先,请保证无人机在明亮的环境中翻转、反弹……
然后,我们分析下和 tello 无人机的沟通原理:当 tello 无人机得到一个启动命令包(drone.connect(11111);)时,tello 无人机会在两个 UDP 通道上发送数据。命令通道 A(端口:8889)和视频通道 B(WIP)(端口:11111)。在 AP 模式下,tello 无人机将以默认 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));
}
}
远程控制 tello 无人机
无人机飞行时,具有一个状态码:rc_state。只要无人机联网(无人机联网不限于手机、手柄。如果失联,那就是布朗运动了,结局就是所谓“炸机”),此状态用来操纵 tello 无人机的运动。所以远程控制的原理就是我们通过向 tello 无人机传输或者迭代状态码,来远程控制 tello 无人机的状态和行为。
例如,如果我们需要远程控制无人机下坠,使用:
drone.rc_state.go_down()
如果我们需要远程控制无人机前进或者后退多少单位,使用:
drone.rc_state.go_forward_back(-0.7)
上面文章介绍到了和 tello 无人机交互通信的原理和方法。远程控制时,和 tello 无人机的通信中,我们是需要对无人机状态进行轮询的。其不仅包括接收来自无人机的消息,还将发送一些默认设置、回复确认、触发关键帧,或者发送实时移动命令等等,才能远程控制状态。
上面提到,无人机联网不限于手机、手柄。我们可以使用 SDL 打开窗口,处理键盘输入,并显示如何连接游戏板或操纵杆等。
如下例子比较长,但原理如上所述,并不复杂,核心部分就是轮询 tello 无人机状态。
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));
}
}
Rust 操控大疆可编程无人机这两篇文章,仅仅是个人一时兴趣的尝试,并非什么深入或复杂的应用,后续是否继续也不确定。感兴趣的朋友,您如果有更深入的见解和应用,我将十分期待您的指导。
谢谢您的阅读。