MAVSDK
(
c
++)
takeoff_
and_land
程序分析
takeoff_
and_land
程序是用
c
++
语言调用
MAVSDK
API
做起飞和降落的控制。
MAVSDK
(
C++
)
API
是对
mavlink API
的一层抽象,让开发者在用程序语言控制飞机时更简单了。用
c
语言直接调用
mavlink API
对
PX4
操作时你需要用
mavlink
消息与
PX
4
通讯
,需要显式地建立
mavlink
消息的读
/
写线程,需要对
mavlink
消息在传输前后做
编码和解码,在使用了
MAVSDK API
后,这些都省了
。然而,在使用
MAVSDK
时还是要注意两个方面一个是
C
++
的技巧,另一方面是对
MAVSDK API
的理解。
既然是比
mavlink
的操作更抽象了,可以理解,新的
API
和语言也更抽象了。
抽象的方法是不亮出操作的对象具体是什么,你也不能直接建立这个对象,但是你可以用操作这个对象的函数来操作它,包括得到它、改变它并传递它。
对于
get_
system()
这个函数,我们做分析:
get_
system()
函数原型是
std::shared_ptr
表示:这个函数将返回指向
System
对象的指针。
其中,
shared_
ptr
它的原型是
std::shared_ptr<
类型
>
,
shared_ptr
类型
是一个
c++
标准库中的一种
smart
指针,是一个类模板。当
shared_ptr
指向的对象生命周期结束时(比如:离开控制块作用的范围),会自动调用其析构函数,释放内存。而不再需要程序员显式地调用
delete
释放对象。
auto p
rom =
std::
promise
>{};
表示
:
用指向
System
对象的指针对
promise
类进行初始化,并赋值给
prom
变量
,其中指向
System
对象的指针是
shared_ptr
类型,
prom
保存了一个
promise
类的实例。
在
C++11
中,
auto
表示,对于声明的变量,编译器可以根据变量初始值的类型自动为此变量选择匹配的类型。
prom
是一个
promise
对象的实例,它保存了一个指向
System
对象的指针
auto fut = prom.get_future();
表示:利用
prom.
get_future()
得到一个
fut
ure
对象,这样
prom
和
fut
就关联起来了,
fut
和
prom
有了一个共享的状态,并且
fut
可以从
prom
中获取指向
System
的指针。
在
C++
中,
promise
和
future
对象是成对出现的。一个线程拥有
prom
,另外一个线程拥有
fut
,两个线程就可以通过
prom
和
fut
这两个对象交换数据。这样,利用
promise
和
future
机制就把两个线程之间交换数据变成了两个对象之间交换数据。尤其面向这样一种情况,拥有
prom
的那个线程不是马上就可以得到结果,而是要过一段不确定的时间才能得到结果,在结果得到后,等待它出现结果的线程需要与这个产生结果的线程有一个同步的操作,在
promise
和
future
这两个对象引入后,虽然你看不见这个同步的具体操作,但是你可以在
prom
和
fut
这两个对象共享的状态上进行操作,比如查看状态的变化。事实上,两个异步线程之间交换数据并不是一件容易的事,尤其是一个线程还要与另一个线程在某个时刻同步。采用
promise/future
技术简化了这个过程。
本例中,
prom
被
mavsdk.subscribe_on_new_system
拥有,
fut
被主线程拥有。
mavsdk.subscribe_on_new_system
的
原型:
void mavsdk::Mavsdk::subscribe_on_new_system(NewSystemCallback callback)
对这个函数的解释:
systems
一旦发生变化这个程序将得到通知。(原文:
Get notification about a change in systems
)
一旦有一个新的
system
加入进来就调用这个函数 。(原文:
This gets called whenever a system is added
)
上述说明中的‘
systems’
是指可以从
mavsdk
的成员函数
systems
()得到的一个向量。原型:
std::vector
其中
System
是
MAVSDK
中定义的一个类
,代表了一个系统,由一个或多个部件组成(自驾仪,照相机,云台,舵机
...)
。也就是说在
MAVSDK
系统中存在一个向量:
system1, system2 ….
这里
s
ystem
对象不能直接由应用程序建立,想得到它,要通过调用
MAVSDK API
对它进行操作。
比如:想得到这个向量的最后一个成员
auto system = mavsdk.system().back();
本例是直接把回调函数体作为参数传给
subscribe_
on_new_system
。
虽然你看不出
mavsdk.subscribe_on_new_system
是一个线程,但是你应该想象它启动了一个线程,这个线程在通讯接口上等待接收
heatbeat
消息。
mavsdk
系统(
server
)在发现了一个新的
System
后会通知
mavsdk.subscribe_on_new_system
执行回调函数。回调函数体如下:
callback[&mavsdk, &prom]() {
//
获取刚刚获得的
system
auto system = mavsdk.systems().back();
//
判断这个
system
有没有
autopilot
部件
if (system->has_autopilot()) {
std::cout << "Discovered autopilot\n";
// Unsubscribe again as we only want to find one system.
//
不再接收其他发现的系统
mavsdk.subscribe_on_new_system(nullptr);
//
与
fut
做同步,将
future_
status
的状态改为
ready
,
//
并
将
system
传给
fut
prom.set_value(system);
}
});
//
主线成阻塞在这里,等待
future_
status
状态改变
//
如果在等待的
3
秒之内发现状态是
timeout
,则返回空值。
if (
fut.wait_for(seconds(3))
== std::future_status::timeout) {
std::cerr << "No autopilot found.\n";
return {};
}
//
如果在等待的
3
秒之内发现不是
timeout
,则返回发现的
system
。
return
fut.get()
;
当然
,
如果一个线程不是等待一段不确定的时间才拿到结果,我们就不需要用
promise/
future
技术,因为
future
是确定的。
比如:想从数传中得到高度信息,
MAVSDK
给出的
API
是
telemetry.
subscribe_position
,片段如下:
//
先申请一个
telemetry
对象
auto telemetry = Telemetry{system};
//
然后确定监测飞机
position
消息的频率
(
1Hz
)
const auto set_rate_result = telemetry.set_rate_position(1.0);
//
最后,查看并打印
position
中的
altitude_
m
分量(高度)
telemetry.subscribe_position([](Telemetry::Position position) {
std::cout << "Altitude: " << position.relative_altitude_m << " m\n";
});
可以想象
,
telemetry_
subscribe_postion
也是启动了一个线程,它从飞机定期得到位置消息
,
每
1
秒
(
1Hz
)
就会启动一次回调函数
,回调函数做打印操作。由于是确定性事件
,所以没有必要用
promise/future
技术。