赞
踩
本文主要通过Qt5+opmapcontrol实现一个简单的无人机地面站效果。opmapcontrol是一个比较古老的QT开源地面站库,可选择谷歌地图,必应地图, 雅虎地图,GIS等。可直接使用源码,也可以编译生成库进行调用。
实现效果:
Qt:5.15.2
编译器: Qt 5.15.2 MinGW 64-bit
系统:windows 10
(1) 支持缓存地图
(2)支持选择各厂商地图,以及切换街道地图
(3)支持地图互动:拖动、放大缩小
(4)支持添加航点,以及航点的编辑、删除、保存、加载、航点信息显示
(5)支持设置home,以及安全区域
(6)支持显示运动轨迹
从github上下载下来的源码包调用的时候,不同版本的Qt会有些小bug,可以直接搜opmapcontrol。
CSDN免积分下载链接(包含编译好的库、和简单的调用demo):
https://download.csdn.net/download/ever__ever/89290882?spm=1001.2014.3001.5503
(以下为opmapcontrol中mapwidget子项目各个类的功能讲解,只想知道如何使用的话可以跳过本节,直接前往第五节)
这个库中我们主要看mapwidget子项目,里面提供了对地图、无人机、航点、轨迹、home等相关操作的接口。
这个类主要继承QGraphicsView,提供一个地图交互场景视图。
(1)void SetShowDiagnostics(bool const & value)
设置是否显示诊断信息。当传入的值为true时,会创建定时器用于刷新诊断信息,同时创建GPS对象(如果不存在),并设置透明度。当传入的值为false时,会清除诊断信息、定时器、GPS对象等。
(2)void SetUavPic(QString UAVPic)
用于设置无人机的图片。如果无人机对象存在,会调用其SetUavPic函数设置图片。
(3)UAVItem* AddUAV(int id)
用于添加一个新的无人机对象,并将其添加到地图中。它返回指向新添加的无人机对象的指针。
(4)void AddUAV(int id, UAVItem* uav)
用于添加一个已存在的无人机对象,并将其添加到地图中。
(5)void DeleteUAV(int id)
用于删除指定ID的无人机对象。
(6)UAVItem* GetUAV(int id)
用于获取指定ID的无人机对象。
(7)const QList<UAVItem*> GetUAVS()
用于获取所有无人机对象的列表。
(8)WayPointLine *WPLineCreate(WayPointItem *from, WayPointItem *to, QColor color, bool dashed, int width)
用于创建两个航点之间的连线。
(9)WayPointLine *WPLineCreate(HomeItem *from, WayPointItem *to, QColor color, bool dashed, int width)
用于创建Home点到航点之间的连线。
这个类包含了一些成员变量,如EmptytileBrush、EmptyTileText、EmptyTileBorders等,用于配置地图空白瓦片的绘制、文本、边框等样式。同时,它还包含了一些成员函数,用于设置和获取地图的访问模式、语言、内存缓存使用情况、缓存文件的位置等。
总的来说,Configuration类的作用是提供一个集中管理地图控件配置选项的地方,使得在代码中可以方便地对这些配置进行设置和获取。
继承自QObject和QGraphicsItem的类,用于在地图上表示一个无人机(UAV)。提供了一系列函数用于管理无人机的位置、轨迹以及到达状态等属性,以及相应的信号与槽机制用于与其他对象进行交互。
这个类的作用主要有以下几点:
继承自QObject和QGraphicsItem的类,用于在地图上表示一个home点的位置。提供了一系列函数用于管理home的位置、安全区域以及相应的交互功能。
这个类的作用主要有以下几点:
这个类的作用主要有以下几点:
用于在地图上显示路径轨迹的图形项,它可以根据传入的经纬度坐标和颜色刷,在地图上绘制路径。
表示无人机(UAV)的图形项,用于在地图上显示无人机的位置、航向、速度等信息。可以根据无人机的位置和状态信息进行绘制,同时提供了一系列方法和信号,用于处理与无人机相关的逻辑。
这个类的作用主要有以下几点:
表示航点的图形项,用于在地图上显示航点的位置、描述和相关信息。可以根据航点的位置和状态信息进行绘制,同时提供了一系列方法和信号,用于处理与航点相关的逻辑。
这个类的作用主要有以下几点:
├── UAVMapDemo
├── UAVMapDemo.pro
├── main.cpp (程序入口:加载样式表)
├── mainwindow.h (主界面,没什么操作)
├── mainwindow.cpp
├── mainwindow.ui
├── 3rdparty (包括opmapcontrol的头文件和库文件)
│ ├── opmapcontrol
│ │ └── include
│ │ └── MinGW
├── MapControl
│ ├── MapControl.pri
│ ├── MapWidget.h (继承OPMapWidget,调用opmapwidget相关接口实现地图和无人机操作)
│ ├── MapWidget.cpp
│ ├── UAS_types.h (存放一些数据类型和转化)
│ ├── UAS_types.cpp
├── Robot
│ ├── Robot.pri
│ ├── uavmanagerwidget.h (无人机地面站界面,实现模型飞行、起飞、降落等接口)
│ ├── uavmanagerwidget.cpp
│ ├── uavmanagerwidget.ui
│ ├── mapsetmiddleform.h (一个小界面,不用关心)
│ ├── mapsetmiddleform.h
│ ├── mapsetmiddleform.h
├── res
│ ├── image (图片资源)
│ ├── styleSheet (样式资源)
│ │ └── default.qss
添加opmapcontrol相关头文件和库文件
CONFIG(debug, debug|release){
win32-g++ {
}
else:msvc {
}
}
else {
win32-g++ {
INCLUDEPATH += $$PWD/3rdparty/opmapcontrol/include/mapwidget
LIBS += -L$$PWD/3rdparty/opmapcontrol/MinGW -lopmapwidget
}
else:msvc {
}
}
initWidget() : 新建一个配置文件,存放经纬度、home点经纬度、缩放比例、添加一个无人机类,编号为0,不显示无人信息,设置无人机的位置为home位置。
handleUISignals() : 处理界面上的一些ui信号,加了一个定时器,用于模拟飞行。
flySimulation() : 模拟飞行,随机经纬度。
void UAVManagerWidget::initWidget() { m_mapSetForm = new MapSetMiddleForm(); m_mapSetForm->setRobotBtnName(u8"无人机"); m_conf = new QSettings("./MapControl/data/UAV.ini", QSettings::IniFormat); int zoom; m_homeLat = m_conf->value("mapWidget_home_lat", 34.257287).toDouble(); m_homeLng = m_conf->value("mapWidget_home_lng", 108.888931).toDouble(); m_uavLat = m_homeLat; m_uavLng = m_homeLng; zoom = m_conf->value("lastZoom", 13).toInt(); internals::PointLatLng p(m_homeLat, m_homeLng); ui->widget_map->SetCurrentPosition(p); ui->widget_map->SetZoom(zoom); ui->widget_map->setConf(m_conf); uav = ui->widget_map->AddUAV(0); uav->SetNumber(0); uav->SetShowUAVInfo(false); uav->SetMapFollowType(mapcontrol::UAVMapFollowType::Types::CenterMap); uav->SetUAVPos(p, 10); } void UAVManagerWidget::handleUISignals() { // 连接 connect(ui->btn_connect, &QPushButton::clicked, this, &UAVManagerWidget::connectUav); // 起飞 connect(ui->btn_takeoff, &QPushButton::clicked, this, &UAVManagerWidget::uavTakeoff); // 降落 connect(ui->btn_land, &QPushButton::clicked, this, &UAVManagerWidget::uavLand); // 返回 connect(ui->btn_back, &QPushButton::clicked, this, &UAVManagerWidget::uavGoHome); // plan connect(ui->btn_plan, &QPushButton::clicked, this, &UAVManagerWidget::flyByWaypoint); // 地图中心点 connect(ui->btn_home, &QPushButton::clicked, this, [=]() { m_mapSetForm->setCurrentPos(ui->widget_map->CurrentPosition().Lng(), ui->widget_map->CurrentPosition().Lat()); QPoint buttonPos = ui->btn_home->mapToGlobal(ui->btn_home->rect().center()); int dialogX = buttonPos.x() + ui->btn_home->width() - 15; int dialogY = buttonPos.y() - m_mapSetForm->height() / 2; m_mapSetForm->showWidget(QPoint(dialogX, dialogY)); }); // 置于home connect(m_mapSetForm, &MapSetMiddleForm::signal_setHome, this, [=]() { ui->widget_map->SetCurrentPosition(internals::PointLatLng(m_homeLat, m_homeLng)); }); // 置于无人机 connect(m_mapSetForm, &MapSetMiddleForm::signal_setRobot, this, [=]() { if (uav) { ui->widget_map->SetCurrentPosition(internals::PointLatLng(m_uavLat, m_uavLng)); } }); // 置于自定义中心 connect(m_mapSetForm, &MapSetMiddleForm::signal_setCustom, this, [=](double lng, double lat) { if (uav) { ui->widget_map->SetCurrentPosition(internals::PointLatLng(lat, lng)); } }); // 无人机超出home设定安全范围 connect(uav, SIGNAL(UAVLeftSafetyBouble(internals::PointLatLng)), this, SLOT(uavWarnning(internals::PointLatLng))); // 用于模拟飞行的定时器,随机生成无人机位置 timer = new QTimer(this); connect(timer, SIGNAL(timeout()), this, SLOT(flySimulation())); // 模拟飞行按钮 connect(ui->btn_simFly, &QPushButton::clicked, this, [=]() { if (ui->btn_simFly->isChecked()) { timer->start(100); } else { timer->stop(); } }); } void UAVManagerWidget::flySimulation() { m_uavLat += 0.000001; m_uavLng += QRandomGenerator::global()->bounded(0.000001) - 0.000005; internals::PointLatLng ll(m_uavLat, m_uavLng); uav->SetUAVPos(ll, 10); }
MapWidget() : 构造函数,选择必应地图,其他的需要科学上网。
MapWidget::MapWidget(QWidget* parent) : mapcontrol::OPMapWidget(parent) { m_conf = NULL; configuration->SetAccessMode(core::AccessMode::CacheOnly); configuration->SetTileMemorySize(200); configuration->SetCacheLocation("./data/"); SetZoom(4); SetMinZoom(4); // 地图选择必应 SetMapType(MapType::BingHybrid); // 显示罗盘 SetShowCompass(true); // set initial values m_bSelectArea = 0; m_pSelectArea1.SetLat(-9999); m_pSelectArea1.SetLng(-9999); m_pSelectArea2.SetLat(-9999); m_pSelectArea2.SetLng(-9999); m_homeShow = 1; m_homeAlt = 440; m_homePos.SetLat(-9999); m_homePos.SetLng(-9999); m_homeSafearea = 100; m_flightHeight = 20; // setup menus setupMenu(); }
*setConf(QSettings conf) : **配置文件设置,地图加载接口(服务器、缓存)、地图类型、缓存位置、home点和安全区域设置。
void MapWidget::setConf(QSettings* conf) { m_conf = conf; if (m_conf == NULL) return; // map type & access mode { MapType::Types mapType; core::AccessMode::Types accessMode; QString cacheLocation; // load settings accessMode = (core::AccessMode::Types)m_conf->value("mapWidget_accessMode", (int)(core::AccessMode::CacheOnly)) .toInt(); mapType = (MapType::Types)m_conf->value("mapWidget_mapType", (int)(MapType::GoogleSatellite)) .toInt(); cacheLocation = m_conf->value("mapWidget_cacheLocation", "./MapControl/data/").toString(); // set configurations configuration->SetAccessMode(accessMode); configuration->SetCacheLocation(cacheLocation); SetMapType(mapType); // set accessMode actions if (accessMode == core::AccessMode::ServerAndCache) { m_actMapAccess_ServerAndCache->setChecked(true); m_actMapAccess_Cache->setChecked(false); } else if (accessMode == core::AccessMode::CacheOnly) { m_actMapAccess_ServerAndCache->setChecked(false); m_actMapAccess_Cache->setChecked(true); } } // home & safe area { m_homeShow = m_conf->value("mapWidget_home_show", m_homeShow).toInt(); m_homePos.SetLat(m_conf->value("mapWidget_home_lat", m_homePos.Lat()).toDouble()); m_homePos.SetLng(m_conf->value("mapWidget_home_lng", m_homePos.Lng()).toDouble()); m_homeAlt = m_conf->value("mapWidget_home_alt", m_homeAlt).toDouble(); m_homeSafearea = m_conf->value("mapWidget_home_safeArea", m_homeSafearea).toDouble(); m_flightHeight = m_conf->value("mapWidget_flightHeight", m_flightHeight).toDouble(); if (m_homeShow) { this->SetShowHome(true); this->Home->SetCoord(m_homePos); this->Home->SetAltitude((int)(m_homeAlt)); this->Home->SetSafeArea((int)(m_homeSafearea)); m_actHome_ShowHide->setChecked(true); } else { m_actHome_ShowHide->setChecked(false); } } }
**setupMenu() **: 右键菜单,提供对地图、航点、home、安全区域等各个功能的接口。
int MapWidget::setupMenu(void) { // setup actions m_actMapType = new QAction(tr(u8"地图类型"), this); connect(m_actMapType, SIGNAL(triggered()), this, SLOT(actMapType_SelectMap())); m_actMapAccess_ServerAndCache = new QAction(tr(u8"服务器和缓存"), this); m_actMapAccess_ServerAndCache->setCheckable(true); m_actMapAccess_ServerAndCache->setChecked(false); connect(m_actMapAccess_ServerAndCache, SIGNAL(triggered()), this, SLOT(actMapAccess_ServerAndCache())); m_actMapAccess_Cache = new QAction(tr(u8"缓存"), this); m_actMapAccess_Cache->setCheckable(true); m_actMapAccess_Cache->setChecked(true); connect(m_actMapAccess_Cache, SIGNAL(triggered()), this, SLOT(actMapAccess_Cache())); m_actWaypoint_add = new QAction(tr(u8"航点添加"), this); connect(m_actWaypoint_add, SIGNAL(triggered()), this, SLOT(actWaypoint_add())); m_actWaypoint_del = new QAction(tr(u8"航点删除"), this); connect(m_actWaypoint_del, SIGNAL(triggered()), this, SLOT(actWaypoint_del())); m_actWaypoint_edit = new QAction(tr(u8"航点编辑"), this); connect(m_actWaypoint_edit, SIGNAL(triggered()), this, SLOT(actWaypoint_edit())); m_actWaypoint_clear = new QAction(tr(u8"航点清除"), this); connect(m_actWaypoint_clear, SIGNAL(triggered()), this, SLOT(actWaypoint_clear())); m_actWaypoint_save = new QAction(tr(u8"航点保存"), this); connect(m_actWaypoint_save, SIGNAL(triggered()), this, SLOT(actWaypoint_save())); m_actWaypoint_load = new QAction(tr(u8"航点加载"), this); connect(m_actWaypoint_load, SIGNAL(triggered()), this, SLOT(actWaypoint_load())); m_actSelectArea_beg = new QAction(tr(u8"选择区域起点"), this); connect(m_actSelectArea_beg, SIGNAL(triggered()), this, SLOT(actSelectArea_beg())); m_actSelectArea_end = new QAction(tr(u8"选择区域终点"), this); connect(m_actSelectArea_end, SIGNAL(triggered()), this, SLOT(actSelectArea_end())); m_actSelectArea_clear = new QAction(tr(u8"选择区域清除"), this); connect(m_actSelectArea_clear, SIGNAL(triggered()), this, SLOT(actSelectArea_clear())); m_actHome_Set = new QAction(tr(u8"设置Home"), this); connect(m_actHome_Set, SIGNAL(triggered()), this, SLOT(actHome_Set())); m_actHome_Safearea = new QAction(tr(u8"设置Home安全区域"), this); connect(m_actHome_Safearea, SIGNAL(triggered()), this, SLOT(actHome_Safearea())); m_actHome_ShowHide = new QAction(tr(u8"显示/隐藏Home"), this); m_actHome_ShowHide->setCheckable(true); m_actHome_ShowHide->setChecked(true); connect(m_actHome_ShowHide, SIGNAL(triggered()), this, SLOT(actHome_ShowHide())); m_actCacheMap = new QAction(tr(u8"缓存地图"), this); connect(m_actCacheMap, SIGNAL(triggered()), this, SLOT(actCacheMap())); m_actTrail_clear = new QAction(tr(u8"清除轨迹"), this); connect(m_actTrail_clear, SIGNAL(triggered()), this, SLOT(actClearTrail())); // setup menu m_popupMenu = new QMenu("Menu"); QMenu* menuAccessMode = m_popupMenu->addMenu(u8"接口类型"); m_popupMenu->addAction(m_actMapType); m_popupMenu->addAction(m_actCacheMap); m_popupMenu->addSeparator(); m_popupMenu->addAction(m_actTrail_clear); m_popupMenu->addSeparator(); m_popupMenu->addAction(m_actWaypoint_add); m_popupMenu->addAction(m_actWaypoint_del); m_popupMenu->addAction(m_actWaypoint_edit); m_popupMenu->addAction(m_actWaypoint_clear); m_popupMenu->addAction(m_actWaypoint_save); m_popupMenu->addAction(m_actWaypoint_load); m_popupMenu->addSeparator(); m_popupMenu->addAction(m_actSelectArea_beg); m_popupMenu->addAction(m_actSelectArea_end); m_popupMenu->addAction(m_actSelectArea_clear); m_popupMenu->addSeparator(); m_popupMenu->addAction(m_actHome_Set); m_popupMenu->addAction(m_actHome_Safearea); m_popupMenu->addAction(m_actHome_ShowHide); menuAccessMode->addAction(m_actMapAccess_ServerAndCache); menuAccessMode->addAction(m_actMapAccess_Cache); return 0; }
https://download.csdn.net/download/ever__ever/89291117?spm=1001.2014.3001.5503
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。