///*===================================================================== // //QGroundControl Open Source Ground Control Station // //(c) 2009, 2010 QGROUNDCONTROL PROJECT // //This file is part of the QGROUNDCONTROL project // // QGROUNDCONTROL is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // QGROUNDCONTROL is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with QGROUNDCONTROL. If not, see . // //======================================================================*/ /** * @file * @brief Definition of the class Pixhawk3DWidget. * * @author Lionel Heng * */ #include "Pixhawk3DWidget.h" #include #include #include #include #include #include "PixhawkCheetahGeode.h" #include "UASManager.h" #include "UASInterface.h" #include "QGC.h" Pixhawk3DWidget::Pixhawk3DWidget(QWidget* parent) : Q3DWidget(parent) , uas(NULL) , displayGrid(true) , displayTrail(false) , displayTarget(false) , displayWaypoints(true) , displayRGBD2D(false) , displayRGBD3D(false) , followCamera(true) , lastRobotX(0.0f) , lastRobotY(0.0f) , lastRobotZ(0.0f) { setCameraParams(2.0f, 30.0f, 0.01f, 10000.0f); init(15.0f); // generate Pixhawk Cheetah model vehicleModel = PixhawkCheetahGeode::instance(); egocentricMap->addChild(vehicleModel); // generate grid model gridNode = createGrid(); rollingMap->addChild(gridNode); // generate empty trail model trailNode = createTrail(); rollingMap->addChild(trailNode); #ifdef QGC_OSGEARTH_ENABLED // generate map model mapNode = createMap(); root->addChild(mapNode); #endif // generate target model allocentricMap->addChild(createTarget()); // generate waypoint model waypointsNode = createWaypoints(); rollingMap->addChild(waypointsNode); #ifdef QGC_LIBFREENECT_ENABLED freenect.reset(new Freenect()); freenect->init(); #endif // generate RGBD model rgbd3DNode = createRGBD3D(); egocentricMap->addChild(rgbd3DNode); setupHUD(); // find available vehicle models in models folder vehicleModels = findVehicleModels(); buildLayout(); connect(UASManager::instance(), SIGNAL(activeUASSet(UASInterface*)), this, SLOT(setActiveUAS(UASInterface*))); } Pixhawk3DWidget::~Pixhawk3DWidget() { } /** * * @param uas the UAS/MAV to monitor/display with the HUD */ void Pixhawk3DWidget::setActiveUAS(UASInterface* uas) { if (this->uas != NULL && this->uas != uas) { // Disconnect any previously connected active MAV //disconnect(uas, SIGNAL(valueChanged(UASInterface*,QString,double,quint64)), this, SLOT(updateValue(UASInterface*,QString,double,quint64))); } this->uas = uas; } void Pixhawk3DWidget::showGrid(int32_t state) { if (state == Qt::Checked) { displayGrid = true; } else { displayGrid = false; } } void Pixhawk3DWidget::showTrail(int32_t state) { if (state == Qt::Checked) { if (!displayTrail) { trail.clear(); } displayTrail = true; } else { displayTrail = false; } } void Pixhawk3DWidget::showWaypoints(int state) { if (state == Qt::Checked) { displayWaypoints = true; } else { displayWaypoints = false; } } void Pixhawk3DWidget::selectVehicleModel(int index) { egocentricMap->removeChild(vehicleModel); vehicleModel = vehicleModels.at(index); egocentricMap->addChild(vehicleModel); } void Pixhawk3DWidget::recenter(void) { float robotX = 0.0f, robotY = 0.0f, robotZ = 0.0f; if (uas != NULL) { robotX = uas->getLocalX(); robotY = uas->getLocalY(); robotZ = uas->getLocalZ(); } recenterCamera(robotY, robotX, -robotZ); } void Pixhawk3DWidget::toggleFollowCamera(int32_t state) { if (state == Qt::Checked) { followCamera = true; } else { followCamera = false; } } #include QVector< osg::ref_ptr > Pixhawk3DWidget::findVehicleModels(void) { QDir directory("models"); QStringList files = directory.entryList(QStringList("*.osg")); QVector< osg::ref_ptr > nodes; // add Pixhawk Bravo model nodes.push_back(PixhawkCheetahGeode::instance()); // add all other models in folder for (int i = 0; i < files.size(); ++i) { osg::ref_ptr node = osgDB::readNodeFile(directory.absoluteFilePath(files[i]).toStdString().c_str()); nodes.push_back(node); } return nodes; } void Pixhawk3DWidget::buildLayout(void) { QCheckBox* gridCheckBox = new QCheckBox(this); gridCheckBox->setText("Grid"); gridCheckBox->setChecked(displayGrid); QCheckBox* trailCheckBox = new QCheckBox(this); trailCheckBox->setText("Trail"); trailCheckBox->setChecked(displayTrail); QCheckBox* waypointsCheckBox = new QCheckBox(this); waypointsCheckBox->setText("Waypoints"); waypointsCheckBox->setChecked(displayWaypoints); QLabel* modelLabel = new QLabel("Vehicle Model", this); QComboBox* modelComboBox = new QComboBox(this); for (int i = 0; i < vehicleModels.size(); ++i) { modelComboBox->addItem(vehicleModels[i]->getName().c_str()); } targetButton = new QPushButton(this); targetButton->setCheckable(true); targetButton->setChecked(false); targetButton->setIcon(QIcon(QString::fromUtf8(":/images/status/weather-clear.svg"))); QPushButton* recenterButton = new QPushButton(this); recenterButton->setText("Recenter Camera"); QCheckBox* followCameraCheckBox = new QCheckBox(this); followCameraCheckBox->setText("Follow Camera"); followCameraCheckBox->setChecked(followCamera); QGridLayout* layout = new QGridLayout(this); layout->setMargin(0); layout->setSpacing(2); layout->addWidget(gridCheckBox, 1, 0); layout->addWidget(trailCheckBox, 1, 1); layout->addWidget(waypointsCheckBox, 1, 2); layout->addItem(new QSpacerItem(10, 0, QSizePolicy::Expanding, QSizePolicy::Expanding), 1, 3); layout->addWidget(modelLabel, 1, 4); layout->addWidget(modelComboBox, 1, 5); layout->addWidget(targetButton, 1, 6); layout->addItem(new QSpacerItem(10, 0, QSizePolicy::Expanding, QSizePolicy::Expanding), 1, 7); layout->addWidget(recenterButton, 1, 8); layout->addWidget(followCameraCheckBox, 1, 9); layout->setRowStretch(0, 100); layout->setRowStretch(1, 1); setLayout(layout); connect(gridCheckBox, SIGNAL(stateChanged(int)), this, SLOT(showGrid(int))); connect(trailCheckBox, SIGNAL(stateChanged(int)), this, SLOT(showTrail(int))); connect(waypointsCheckBox, SIGNAL(stateChanged(int)), this, SLOT(showWaypoints(int))); connect(modelComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(selectVehicleModel(int))); connect(recenterButton, SIGNAL(clicked()), this, SLOT(recenter())); connect(followCameraCheckBox, SIGNAL(stateChanged(int)), this, SLOT(toggleFollowCamera(int))); } void Pixhawk3DWidget::display(void) { if (uas == NULL) { return; } float robotX = uas->getLocalX(); float robotY = uas->getLocalY(); float robotZ = uas->getLocalZ(); float robotRoll = uas->getRoll(); float robotPitch = uas->getPitch(); float robotYaw = uas->getYaw(); if (lastRobotX == 0.0f && lastRobotY == 0.0f && lastRobotZ == 0.0f) { lastRobotX = robotX; lastRobotY = robotY; lastRobotZ = robotZ; recenterCamera(robotY, robotX, -robotZ); return; } if (followCamera) { float dx = robotY - lastRobotY; float dy = robotX - lastRobotX; float dz = lastRobotZ - robotZ; moveCamera(dx, dy, dz); } robotPosition->setPosition(osg::Vec3(robotY, robotX, -robotZ)); robotAttitude->setAttitude(osg::Quat(-robotYaw, osg::Vec3f(0.0f, 0.0f, 1.0f), robotPitch, osg::Vec3f(1.0f, 0.0f, 0.0f), robotRoll, osg::Vec3f(0.0f, 1.0f, 0.0f))); updateTrail(robotX, robotY, robotZ); updateTarget(); updateWaypoints(); #ifdef QGC_LIBFREENECT_ENABLED updateRGBD(); #endif updateHUD(robotX, robotY, robotZ, robotRoll, robotPitch, robotYaw); // set node visibility rollingMap->setChildValue(gridNode, displayGrid); rollingMap->setChildValue(trailNode, displayTrail); rollingMap->setChildValue(targetNode, displayTarget); rollingMap->setChildValue(waypointsNode, displayWaypoints); egocentricMap->setChildValue(rgbd3DNode, displayRGBD3D); hudGroup->setChildValue(rgb2DGeode, displayRGBD2D); hudGroup->setChildValue(depth2DGeode, displayRGBD2D); lastRobotX = robotX; lastRobotY = robotY; lastRobotZ = robotZ; } void Pixhawk3DWidget::keyPressEvent(QKeyEvent* event) { if (!event->text().isEmpty()) { switch (*(event->text().toAscii().data())) { case '1': displayRGBD2D = !displayRGBD2D; break; case '2': displayRGBD3D = !displayRGBD3D; break; } } Q3DWidget::keyPressEvent(event); } void Pixhawk3DWidget::mousePressEvent(QMouseEvent* event) { if (event->button() == Qt::LeftButton && targetButton->isChecked()) { markTarget(); } Q3DWidget::mousePressEvent(event); } osg::ref_ptr Pixhawk3DWidget::createGrid(void) { osg::ref_ptr geode(new osg::Geode()); osg::ref_ptr fineGeometry(new osg::Geometry()); osg::ref_ptr coarseGeometry(new osg::Geometry()); geode->addDrawable(fineGeometry); geode->addDrawable(coarseGeometry); float radius = 10.0f; float resolution = 0.25f; osg::ref_ptr fineCoords(new osg::Vec3Array); osg::ref_ptr coarseCoords(new osg::Vec3Array); // draw a 20m x 20m grid with 0.25m resolution for (float i = -radius; i <= radius; i += resolution) { if (fabsf(i - roundf(i)) < 0.01f) { coarseCoords->push_back(osg::Vec3(i, -radius, 0.0f)); coarseCoords->push_back(osg::Vec3(i, radius, 0.0f)); coarseCoords->push_back(osg::Vec3(-radius, i, 0.0f)); coarseCoords->push_back(osg::Vec3(radius, i, 0.0f)); } else { fineCoords->push_back(osg::Vec3(i, -radius, 0.0f)); fineCoords->push_back(osg::Vec3(i, radius, 0.0f)); fineCoords->push_back(osg::Vec3(-radius, i, 0.0f)); fineCoords->push_back(osg::Vec3(radius, i, 0.0f)); } } fineGeometry->setVertexArray(fineCoords); coarseGeometry->setVertexArray(coarseCoords); osg::ref_ptr color(new osg::Vec4Array); color->push_back(osg::Vec4(0.5f, 0.5f, 0.5f, 1.0f)); fineGeometry->setColorArray(color); coarseGeometry->setColorArray(color); fineGeometry->setColorBinding(osg::Geometry::BIND_OVERALL); coarseGeometry->setColorBinding(osg::Geometry::BIND_OVERALL); fineGeometry->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::LINES, 0, fineCoords->size())); coarseGeometry->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::LINES, 0, coarseCoords->size())); osg::ref_ptr fineStateset(new osg::StateSet); osg::ref_ptr fineLinewidth(new osg::LineWidth()); fineLinewidth->setWidth(0.25f); fineStateset->setAttributeAndModes(fineLinewidth, osg::StateAttribute::ON); fineStateset->setMode(GL_LIGHTING, osg::StateAttribute::OFF); fineGeometry->setStateSet(fineStateset); osg::ref_ptr coarseStateset(new osg::StateSet); osg::ref_ptr coarseLinewidth(new osg::LineWidth()); coarseLinewidth->setWidth(2.0f); coarseStateset->setAttributeAndModes(coarseLinewidth, osg::StateAttribute::ON); coarseStateset->setMode(GL_LIGHTING, osg::StateAttribute::OFF); coarseGeometry->setStateSet(coarseStateset); return geode; } osg::ref_ptr Pixhawk3DWidget::createTrail(void) { osg::ref_ptr geode(new osg::Geode()); trailGeometry = new osg::Geometry(); trailGeometry->setUseDisplayList(false); geode->addDrawable(trailGeometry.get()); trailVertices = new osg::Vec3Array; trailGeometry->setVertexArray(trailVertices); trailDrawArrays = new osg::DrawArrays(osg::PrimitiveSet::LINE_STRIP); trailGeometry->addPrimitiveSet(trailDrawArrays); osg::ref_ptr color(new osg::Vec4Array); color->push_back(osg::Vec4(1.0f, 0.0f, 0.0f, 1.0f)); trailGeometry->setColorArray(color); trailGeometry->setColorBinding(osg::Geometry::BIND_OVERALL); osg::ref_ptr stateset(new osg::StateSet); osg::ref_ptr linewidth(new osg::LineWidth()); linewidth->setWidth(1.0f); stateset->setAttributeAndModes(linewidth, osg::StateAttribute::ON); stateset->setMode(GL_LIGHTING, osg::StateAttribute::OFF); trailGeometry->setStateSet(stateset); return geode; } #ifdef QGC_OSGEARTH_ENABLED osg::ref_ptr Pixhawk3DWidget::createMap(void) { osg::ref_ptr model = osgDB::readNodeFile("map.earth"); osg::ref_ptr node = osgEarth::MapNode::findMapNode(model); return node; } #endif osg::ref_ptr Pixhawk3DWidget::createTarget(void) { targetPosition = new osg::PositionAttitudeTransform; targetNode = new osg::Geode; targetPosition->addChild(targetNode); return targetPosition; } osg::ref_ptr Pixhawk3DWidget::createWaypoints(void) { osg::ref_ptr group(new osg::Group()); return group; } osg::ref_ptr Pixhawk3DWidget::createRGBD3D(void) { int frameSize = 640 * 480; osg::ref_ptr geode(new osg::Geode); osg::ref_ptr geometry(new osg::Geometry); osg::ref_ptr vertices(new osg::Vec3Array(frameSize)); geometry->setVertexArray(vertices); osg::ref_ptr colors(new osg::Vec4Array(frameSize)); geometry->setColorArray(colors); geometry->setColorBinding(osg::Geometry::BIND_PER_VERTEX); geometry->setUseDisplayList(false); geode->addDrawable(geometry); return geode; } void Pixhawk3DWidget::setupHUD(void) { osg::ref_ptr hudColors(new osg::Vec4Array); hudColors->push_back(osg::Vec4(0.0f, 0.0f, 0.0f, 0.2f)); hudBackgroundGeometry = new osg::Geometry; hudBackgroundGeometry->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::POLYGON, 0, 4)); hudBackgroundGeometry->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::POLYGON, 4, 4)); hudBackgroundGeometry->setColorArray(hudColors); hudBackgroundGeometry->setColorBinding(osg::Geometry::BIND_OVERALL); hudBackgroundGeometry->setUseDisplayList(false); statusText = new osgText::Text; statusText->setCharacterSize(11); statusText->setFont("images/Vera.ttf"); statusText->setAxisAlignment(osgText::Text::SCREEN); statusText->setColor(osg::Vec4(255, 255, 255, 1)); resizeHUD(); osg::ref_ptr statusGeode = new osg::Geode; statusGeode->addDrawable(hudBackgroundGeometry); statusGeode->addDrawable(statusText); hudGroup->addChild(statusGeode); rgbImage = new osg::Image; rgb2DGeode = new ImageWindowGeode("RGB Image", osg::Vec4(0.0f, 0.0f, 0.1f, 1.0f), rgbImage); hudGroup->addChild(rgb2DGeode); depthImage = new osg::Image; depth2DGeode = new ImageWindowGeode("Depth Image", osg::Vec4(0.0f, 0.0f, 0.1f, 1.0f), depthImage); hudGroup->addChild(depth2DGeode); } void Pixhawk3DWidget::resizeHUD(void) { int topHUDHeight = 30; int bottomHUDHeight = 25; osg::Vec3Array* vertices = static_cast(hudBackgroundGeometry->getVertexArray()); if (vertices == NULL || vertices->size() != 8) { osg::ref_ptr newVertices = new osg::Vec3Array(8); hudBackgroundGeometry->setVertexArray(newVertices); vertices = static_cast(hudBackgroundGeometry->getVertexArray()); } (*vertices)[0] = osg::Vec3(0, height(), -1); (*vertices)[1] = osg::Vec3(width(), height(), -1); (*vertices)[2] = osg::Vec3(width(), height() - topHUDHeight, -1); (*vertices)[3] = osg::Vec3(0, height() - topHUDHeight, -1); (*vertices)[4] = osg::Vec3(0, 0, -1); (*vertices)[5] = osg::Vec3(width(), 0, -1); (*vertices)[6] = osg::Vec3(width(), bottomHUDHeight, -1); (*vertices)[7] = osg::Vec3(0, bottomHUDHeight, -1); statusText->setPosition(osg::Vec3(10, height() - 20, -1.5)); if (rgb2DGeode.valid() && depth2DGeode.valid()) { int windowWidth = (width() - 20) / 2; int windowHeight = 3 * windowWidth / 4; rgb2DGeode->setAttributes(10, (height() - windowHeight) / 2, windowWidth, windowHeight); depth2DGeode->setAttributes(width() / 2, (height() - windowHeight) / 2, windowWidth, windowHeight); } } void Pixhawk3DWidget::updateHUD(float robotX, float robotY, float robotZ, float robotRoll, float robotPitch, float robotYaw) { resizeHUD(); std::pair cursorPosition = getGlobalCursorPosition(getMouseX(), getMouseY(), -robotZ); std::ostringstream oss; oss.setf(std::ios::fixed, std::ios::floatfield); oss.precision(2); oss << " x = " << robotX << " y = " << robotY << " z = " << robotZ << " r = " << robotRoll << " p = " << robotPitch << " y = " << robotYaw << " Cursor [" << cursorPosition.first << " " << cursorPosition.second << "]"; statusText->setText(oss.str()); if (!rgb.isNull()) { rgbImage->setImage(640, 480, 1, GL_RGB, GL_RGB, GL_UNSIGNED_BYTE, reinterpret_cast(rgb->data()), osg::Image::NO_DELETE); rgbImage->dirty(); depthImage->setImage(640, 480, 1, GL_RGB, GL_RGB, GL_UNSIGNED_BYTE, reinterpret_cast(coloredDepth->data()), osg::Image::NO_DELETE); depthImage->dirty(); } } void Pixhawk3DWidget::updateTrail(float robotX, float robotY, float robotZ) { if (robotX == 0.0f || robotY == 0.0f || robotZ == 0.0f) { return; } bool addToTrail = false; if (trail.size() > 0) { if (fabsf(robotX - trail[trail.size() - 1].x()) > 0.01f || fabsf(robotY - trail[trail.size() - 1].y()) > 0.01f || fabsf(robotZ - trail[trail.size() - 1].z()) > 0.01f) { addToTrail = true; } } else { addToTrail = true; } if (addToTrail) { osg::Vec3 p(robotX, robotY, robotZ); if (trail.size() == trail.capacity()) { memcpy(trail.data(), trail.data() + 1, (trail.size() - 1) * sizeof(osg::Vec3)); trail[trail.size() - 1] = p; } else { trail.append(p); } } trailVertices->clear(); for (int i = 0; i < trail.size(); ++i) { trailVertices->push_back(osg::Vec3(trail[i].y() - robotY, trail[i].x() - robotX, -(trail[i].z() - robotZ))); } trailDrawArrays->setFirst(0); trailDrawArrays->setCount(trailVertices->size()); trailGeometry->dirtyBound(); } void Pixhawk3DWidget::updateTarget(void) { static double radius = 0.2; static bool expand = true; if (radius < 0.1) { expand = true; } else if (radius > 0.25) { expand = false; } if (targetNode->getNumDrawables() > 0) { targetNode->removeDrawables(0, targetNode->getNumDrawables()); } osg::ref_ptr sd = new osg::ShapeDrawable; osg::ref_ptr sphere = new osg::Sphere; sphere->setRadius(radius); sd->setShape(sphere); sd->setColor(osg::Vec4(0.0f, 0.7f, 1.0f, 1.0f)); targetNode->addDrawable(sd); if (expand) { radius += 0.02; } else { radius -= 0.02; } } void Pixhawk3DWidget::updateWaypoints(void) { if (uas) { if (waypointsNode->getNumChildren() > 0) { waypointsNode->removeChild(0, waypointsNode->getNumChildren()); } const QVector& list = uas->getWaypointManager().getWaypointList(); for (int i = 0; i < list.size(); i++) { osg::ref_ptr sd = new osg::ShapeDrawable; osg::ref_ptr sphere = new osg::Sphere; sphere->setRadius(0.2); sd->setShape(sphere); if (list.at(i)->getCurrent()) { sd->setColor(osg::Vec4(1.0f, 0.3f, 0.3f, 1.0f)); } else { sd->setColor(osg::Vec4(0.0f, 1.0f, 1.0f, 1.0f)); } osg::ref_ptr geode = new osg::Geode; geode->addDrawable(sd); osg::ref_ptr pat = new osg::PositionAttitudeTransform; pat->setPosition(osg::Vec3d(list.at(i)->getY() - uas->getLocalY(), list.at(i)->getX() - uas->getLocalX(), 0.0)); waypointsNode->addChild(pat); pat->addChild(geode); } } } #ifdef QGC_LIBFREENECT_ENABLED void Pixhawk3DWidget::updateRGBD(void) { rgb = freenect->getRgbData(); coloredDepth = freenect->getColoredDepthData(); } #endif void Pixhawk3DWidget::markTarget(void) { float robotZ = 0.0f; if (uas != NULL) { robotZ = uas->getLocalZ(); } std::pair cursorWorldCoords = getGlobalCursorPosition(getMouseX(), getMouseY(), -robotZ); double targetX = cursorWorldCoords.first; double targetY = cursorWorldCoords.second; double targetZ = robotZ; targetPosition->setPosition(osg::Vec3d(targetY, targetX, -targetZ)); displayTarget = true; if (uas) { uas->setTargetPosition(targetX, targetY, targetZ, 0.0f); } targetButton->setChecked(false); }