From 4703aa078046d9483b16805c87eb76396ecfb22b Mon Sep 17 00:00:00 2001 From: hywel Date: Fri, 10 Aug 2018 16:54:07 +0800 Subject: [PATCH] [gsoc2018]implement ardrone navigation with ompl --- exercises/ardrone_navigation/CMakeLists.txt | 14 + exercises/ardrone_navigation/MyAlgorithm.py | 103 + .../ardrone_navigation/Navigation3dOmpl.py | 72 + exercises/ardrone_navigation/README.md | 70 + .../ardrone_navigation/ardrone-inHouse.world | 29 + exercises/ardrone_navigation/ardrone_conf.yml | 38 + exercises/ardrone_navigation/generateGUI | 9 + exercises/ardrone_navigation/gui/GUI.py | 202 ++ exercises/ardrone_navigation/gui/__init__.py | 0 .../ardrone_navigation/gui/cameraWidget.py | 84 + .../ardrone_navigation/gui/communicator.py | 24 + .../ardrone_navigation/gui/logoWidget.py | 44 + .../ardrone_navigation/gui/sensorsWidget.py | 254 +++ .../ardrone_navigation/gui/teleopWidget.py | 113 ++ exercises/ardrone_navigation/gui/threadGUI.py | 43 + exercises/ardrone_navigation/gui/ui_gui.py | 120 ++ exercises/ardrone_navigation/gui/ui_gui.ui | 311 +++ exercises/ardrone_navigation/map_image.png | Bin 0 -> 168570 bytes .../models/inHouse/model.config | 19 + .../models/inHouse/model.sdf | 877 +++++++++ .../ardrone_navigation/navigation/__init__.py | 0 .../navigation/control/__init__.py | 0 .../navigation/control/ardroneControl.py | 88 + .../navigation/control/noHolomonicControl.py | 159 ++ .../navigation/laser/__init__.py | 0 .../navigation/laser/laser_handle.py | 59 + .../navigation/map/__init__.py | 0 .../navigation/map/costmap_2d.py | 322 ++++ .../navigation/map/costmap_3d.py | 298 +++ .../navigation/map/occGridMap.py | 135 ++ .../navigation/navigation.py | 341 ++++ .../ompl_planner/Point2DPlanning.py | 161 ++ .../ompl_planner/RigidBodyPlanning.py | 308 +++ .../RigidBodyPlanningWithControls.py | 256 +++ ...gidBodyPlanningWithODESolverAndControls.py | 257 +++ .../navigation/ompl_planner/__init__.py | 0 .../navigation/ompl_planner/ompl_planner.py | 69 + .../navigation/path_processing/__init__.py | 0 .../navigation/path_processing/smoothPath.py | 120 ++ .../ardrone_navigation/resources/__init__.py | 0 .../ardrone_navigation/resources/ball.png | Bin 0 -> 1869 bytes .../ardrone_navigation/resources/jderobot.png | Bin 0 -> 14487 bytes .../ardrone_navigation/resources/play.png | Bin 0 -> 5207 bytes .../resources/resources.qrc | 8 + .../ardrone_navigation/resources/stop.png | Bin 0 -> 4817 bytes exercises/ardrone_navigation/resources_rc.py | 1711 +++++++++++++++++ 46 files changed, 6718 insertions(+) create mode 100644 exercises/ardrone_navigation/CMakeLists.txt create mode 100644 exercises/ardrone_navigation/MyAlgorithm.py create mode 100644 exercises/ardrone_navigation/Navigation3dOmpl.py create mode 100644 exercises/ardrone_navigation/README.md create mode 100644 exercises/ardrone_navigation/ardrone-inHouse.world create mode 100644 exercises/ardrone_navigation/ardrone_conf.yml create mode 100755 exercises/ardrone_navigation/generateGUI create mode 100644 exercises/ardrone_navigation/gui/GUI.py create mode 100644 exercises/ardrone_navigation/gui/__init__.py create mode 100644 exercises/ardrone_navigation/gui/cameraWidget.py create mode 100644 exercises/ardrone_navigation/gui/communicator.py create mode 100644 exercises/ardrone_navigation/gui/logoWidget.py create mode 100644 exercises/ardrone_navigation/gui/sensorsWidget.py create mode 100644 exercises/ardrone_navigation/gui/teleopWidget.py create mode 100644 exercises/ardrone_navigation/gui/threadGUI.py create mode 100644 exercises/ardrone_navigation/gui/ui_gui.py create mode 100644 exercises/ardrone_navigation/gui/ui_gui.ui create mode 100644 exercises/ardrone_navigation/map_image.png create mode 100644 exercises/ardrone_navigation/models/inHouse/model.config create mode 100644 exercises/ardrone_navigation/models/inHouse/model.sdf create mode 100644 exercises/ardrone_navigation/navigation/__init__.py create mode 100644 exercises/ardrone_navigation/navigation/control/__init__.py create mode 100644 exercises/ardrone_navigation/navigation/control/ardroneControl.py create mode 100644 exercises/ardrone_navigation/navigation/control/noHolomonicControl.py create mode 100644 exercises/ardrone_navigation/navigation/laser/__init__.py create mode 100644 exercises/ardrone_navigation/navigation/laser/laser_handle.py create mode 100644 exercises/ardrone_navigation/navigation/map/__init__.py create mode 100644 exercises/ardrone_navigation/navigation/map/costmap_2d.py create mode 100644 exercises/ardrone_navigation/navigation/map/costmap_3d.py create mode 100644 exercises/ardrone_navigation/navigation/map/occGridMap.py create mode 100644 exercises/ardrone_navigation/navigation/navigation.py create mode 100755 exercises/ardrone_navigation/navigation/ompl_planner/Point2DPlanning.py create mode 100755 exercises/ardrone_navigation/navigation/ompl_planner/RigidBodyPlanning.py create mode 100755 exercises/ardrone_navigation/navigation/ompl_planner/RigidBodyPlanningWithControls.py create mode 100755 exercises/ardrone_navigation/navigation/ompl_planner/RigidBodyPlanningWithODESolverAndControls.py create mode 100644 exercises/ardrone_navigation/navigation/ompl_planner/__init__.py create mode 100644 exercises/ardrone_navigation/navigation/ompl_planner/ompl_planner.py create mode 100644 exercises/ardrone_navigation/navigation/path_processing/__init__.py create mode 100644 exercises/ardrone_navigation/navigation/path_processing/smoothPath.py create mode 100644 exercises/ardrone_navigation/resources/__init__.py create mode 100644 exercises/ardrone_navigation/resources/ball.png create mode 100644 exercises/ardrone_navigation/resources/jderobot.png create mode 100644 exercises/ardrone_navigation/resources/play.png create mode 100644 exercises/ardrone_navigation/resources/resources.qrc create mode 100644 exercises/ardrone_navigation/resources/stop.png create mode 100644 exercises/ardrone_navigation/resources_rc.py diff --git a/exercises/ardrone_navigation/CMakeLists.txt b/exercises/ardrone_navigation/CMakeLists.txt new file mode 100644 index 000000000..8c27b9c9d --- /dev/null +++ b/exercises/ardrone_navigation/CMakeLists.txt @@ -0,0 +1,14 @@ + +if( ICE_PY_MODULES AND NUMPY_MODULES AND OpenCV_PY_MODULES AND QT4_PY_MODULES AND QWT5_PY_MODULES) + #install + #INSTALL (DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/ DESTINATION share/jderobot/python/introrob_py + # COMPONENT core + # FILES_MATCHING + # PATTERN "*.py" + #) + + #INSTALL (PROGRAMS ${CMAKE_CURRENT_SOURCE_DIR}/introrob_py DESTINATION bin OPTIONAL COMPONENT core) + +endif(ICE_PY_MODULES AND NUMPY_MODULES AND OpenCV_PY_MODULES AND QT4_PY_MODULES AND QWT5_PY_MODULES) + + diff --git a/exercises/ardrone_navigation/MyAlgorithm.py b/exercises/ardrone_navigation/MyAlgorithm.py new file mode 100644 index 000000000..2e87bcbb2 --- /dev/null +++ b/exercises/ardrone_navigation/MyAlgorithm.py @@ -0,0 +1,103 @@ +import threading +import time +from datetime import datetime + +import math +import jderobot + +from parallelIce.cameraClient import CameraClient +from parallelIce.navDataClient import NavDataClient +from parallelIce.cmdvel import CMDVel +from parallelIce.extra import Extra +from parallelIce.pose3dClient import Pose3DClient + +from navigation.navigation import navigation +from navigation.control.ardroneControl import ardroneControl + +time_cycle = 80 + +class MyAlgorithm(threading.Thread): + + def __init__(self, camera, navdata, pose, cmdvel, extra): + self.camera = camera + self.navdata = navdata + self.pose = pose + self.cmdvel = cmdvel + self.extra = extra + + self.minError=0.01 + + self.stop_event = threading.Event() + self.kill_event = threading.Event() + self.lock = threading.Lock() + threading.Thread.__init__(self, args=self.stop_event) + + self.navigation = navigation(None,[0,0,0],0.2,3) + + self.find_path = False + self.get_point = True + #self.goal_point = [[7.5,7.5,1],[-5,-7.5,1],[-7.5,-7.5,5],[-7.5,7.5,5],[7.5,-7.5,5]] + self.goal_point = [[7.5, 0 ,6], [7.5,7.5,2],[-7.5, 7.5, 2],[-7.5,7.5,6]] + self.num = 0 + + def run (self): + + self.stop_event.clear() + + while (not self.kill_event.is_set()): + + start_time = datetime.now() + + if not self.stop_event.is_set(): + self.execute() + + finish_Time = datetime.now() + + dt = finish_Time - start_time + ms = (dt.days * 24 * 60 * 60 + dt.seconds) * 1000 + dt.microseconds / 1000.0 + #print (ms) + if (ms < time_cycle): + time.sleep((time_cycle - ms) / 1000.0) + + def stop (self): + self.stop_event.set() + + def play (self): + if self.is_alive(): + self.stop_event.clear() + else: + self.start() + + def kill (self): + self.kill_event.set() + + + def execute(self): + # Add your code here + # use: self.pose.getPose3d().x + # self.pose.getPose3d().y + # to get the coordinates of the drone in the x,y plane + pose_n = [self.pose.getPose3d().x, self.pose.getPose3d().y, self.pose.getPose3d().z, self.pose.getPose3d().yaw] + print (pose_n) + + if not self.find_path and self.get_point and self.num < len(self.goal_point): + self.cmdvel.setVX(0) + self.cmdvel.setVY(0) + self.cmdvel.setVZ(0) + self.cmdvel.setYaw(0) + self.cmdvel.sendVelocities() + if self.num == 0: + self.navigation.update_map_3d() + goal_pose = self.goal_point[self.num] + print ("goal", goal_pose) + self.find_path = self.navigation.path_planning_3d(pose_n,goal_pose) + else: + [self.get_point, data] = self.navigation.path_following_3d(pose_n) + if self.get_point: + self.find_path = False + self.num += 1 + self.cmdvel.setVX(data[0]) + self.cmdvel.setVY(data[1]) + self.cmdvel.setVZ(data[2]) + self.cmdvel.setYaw(data[3]) + self.cmdvel.sendVelocities() diff --git a/exercises/ardrone_navigation/Navigation3dOmpl.py b/exercises/ardrone_navigation/Navigation3dOmpl.py new file mode 100644 index 000000000..c4f6fb1dd --- /dev/null +++ b/exercises/ardrone_navigation/Navigation3dOmpl.py @@ -0,0 +1,72 @@ +#!/usr/bin/python3 +# +# Copyright (C) 1997-2016 JDE Developers Team +# +# This program 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. +# +# This program 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 this program. If not, see http://www.gnu.org/licenses/. +# Authors : +# Alberto Martin Florido +# Aitor Martinez Fernandez +# + +import sys +import config +import comm +from MyAlgorithm import MyAlgorithm +import easyiceconfig as EasyIce +from gui.threadGUI import ThreadGUI +from parallelIce.cameraClient import CameraClient +from parallelIce.navDataClient import NavDataClient +from parallelIce.cmdvel import CMDVel +from parallelIce.extra import Extra +from parallelIce.pose3dClient import Pose3DClient +from gui.GUI import MainWindow +from PyQt5.QtWidgets import QApplication + +import signal + +signal.signal(signal.SIGINT, signal.SIG_DFL) + +if __name__ == '__main__': + + cfg = config.load(sys.argv[1]) + + #starting comm + jdrc= comm.init(cfg, 'Introrob') + + camera = jdrc.getCameraClient("Introrob.Camera") + navdata = jdrc.getNavdataClient("Introrob.Navdata") + pose = jdrc.getPose3dClient("Introrob.Pose3D") + cmdvel = jdrc.getCMDVelClient("Introrob.CMDVel") + extra = jdrc.getArDroneExtraClient("Introrob.Extra") + + + algorithm=MyAlgorithm(camera, navdata, pose, cmdvel, extra) + + app = QApplication(sys.argv) + frame = MainWindow() + frame.setCamera(camera) + frame.setNavData(navdata) + frame.setPose3D(pose) + frame.setCMDVel(cmdvel) + frame.setExtra(extra) + frame.setAlgorithm(algorithm) + frame.show() + + + + t2 = ThreadGUI(frame) + t2.daemon=True + t2.start() + + sys.exit(app.exec_()) diff --git a/exercises/ardrone_navigation/README.md b/exercises/ardrone_navigation/README.md new file mode 100644 index 000000000..589f82470 --- /dev/null +++ b/exercises/ardrone_navigation/README.md @@ -0,0 +1,70 @@ +# Navigation3DOmpl Exercise + +In this practice we will learn the use of ompl to implement a local navigation algorithm in the quadricopters. +For this practice a world has been designed for the Gazebo simulator. This world has a 3D model of the AR.Drone and a indoor environment. + +You can see this [repo](https://github.com/TheRoboticsClub/colab-gsoc2018-HanqingXie) and [web](https://jderobot.org/Club-hanqingxie) for more detials. + +## Dependency +This project requires the follow dependency: +* jedrobot +* python2.7 +* [OMPL](http://ompl.kavrakilab.org/) + +### install ompl +[Download the OMPL installation script](http://ompl.kavrakilab.org/install-ompl-ubuntu.sh). First, make the script executable: + +```chmod u+x install-ompl-ubuntu.sh``` + +Next, there are three ways to run this script: + +```./install-ompl-ubuntu.sh --python will install OMPL with Python bindings``` + +## How to run + +1. Copy the inHouse model into jderobot gazebo models + + `$ cp -r models/inHouse /root_jderobot/share/jderobot/gazebo/models/` + +2. In a terminal launch the gazebo simulator: + + `$ gazebo ardrone-inHouse.world` + +3. In other terminal launch the position_control component with your algorithm: + + `$ python2 ./Navigation3dOmpl.py ardrone_conf.yml` + +## How to do the practice +To carry out the practice, you must edit the MyAlgorithm.py file and insert the control logic into it. + +## Where to insert the code +[MyAlgorithm.py](MyAlgorithm.py#L65) +``` + def execute(self): + + # Add your code here + print "Runing" + + #EXAMPLE OF HOW TO SEND INFORMATION TO THE ROBOT ACTUATORS + #self.motors.sendV(10) + #self.motors.sendW(5) + # TODO + +``` + +## API + +* self.pose.getPose3d().x - returns the position values ​​of the drone in X axis +* self.pose.getPose3d().y - returns the position values ​​of the drone in Y axis +* self.pose.getPose3d().roll, self.pose.getPose3d().pitch, self.pose.getPose3d().yaw: returns the rotation values ​​of the drone in space. +* self.camera.getImage().data: returns the image captured by the active camera of the drone (frontal or ventral). +* self.cmdvel.setVX(velx): set linear speed of the drone. +* self.cmdvel.setVY(vely): set linear speed of the drone. +* self.cmdvel.setVZ(vely): set linear speed of the drone. +* self.cmdvel.setVYaw(vely): set angular speed of the drone. +* self.cmdvel.sendVelocities(): send set velocities to the drone. +* self.cmdvel.sendCMDVel(self,vx,vy,vz,ax,ay,az): sends linear and angular speed commands to the drone. +* self.extra.toggleCam(): changes the drone's active camera (frontal or the one below). +* self.extra.takeOff(): Takeoff of the drone. +* self.extra.land(): landing of the drone. + diff --git a/exercises/ardrone_navigation/ardrone-inHouse.world b/exercises/ardrone_navigation/ardrone-inHouse.world new file mode 100644 index 000000000..c60c15692 --- /dev/null +++ b/exercises/ardrone_navigation/ardrone-inHouse.world @@ -0,0 +1,29 @@ + + + + + + model://ground_plane + + + + model://ArDrone2 + 7.5 0 4.3 0 0 0 + + + + model://sun + 0 0 3 0 0 0 + + + model://sun + + + + inHouse + model://inHouse + 0 0 0 0 0 0 + + + + diff --git a/exercises/ardrone_navigation/ardrone_conf.yml b/exercises/ardrone_navigation/ardrone_conf.yml new file mode 100644 index 000000000..64e235fb8 --- /dev/null +++ b/exercises/ardrone_navigation/ardrone_conf.yml @@ -0,0 +1,38 @@ +Introrob: + + Camera: + Server: 1 # 0 -> Deactivate, 1 -> Ice , 2 -> ROS + Proxy: "Camera:tcp -h localhost -p 9000" + Format: RGB8 + Topic: "/IntrorobROS/image_raw" + Name: IntrorobCamera + + Pose3D: + Server: 1 # 0 -> Deactivate, 1 -> Ice , 2 -> ROS + Proxy: "Pose3D:tcp -h localhost -p 9000" + Topic: "/IntrorobROS/Pose3D" + Name: IntrorobPose3d + + CMDVel: + Server: 1 # 0 -> Deactivate, 1 -> Ice , 2 -> ROS + Proxy: "CMDVel:tcp -h localhost -p 9000" + Topic: "/IntrorobROS/CMDVel" + Name: IntrorobCMDVel + + Navdata: + Server: 1 # 0 -> Deactivate, 1 -> Ice , 2 -> ROS + Proxy: "Navdata:tcp -h localhost -p 9000" + Topic: "/IntrorobROS/Navdata" + Name: IntrorobNavdata + + Extra: + Server: 1 # 0 -> Deactivate, 1 -> Ice , 2 -> ROS + Proxy: "Extra:tcp -h localhost -p 9000" + Topic: "/IntrorobROS/Extra" + Name: IntrorobExtra + + Xmax: 1 + Ymax: 1 + Zmax: 1 + Yawmax: 1 +NodeName: Introrob diff --git a/exercises/ardrone_navigation/generateGUI b/exercises/ardrone_navigation/generateGUI new file mode 100755 index 000000000..d2d26b788 --- /dev/null +++ b/exercises/ardrone_navigation/generateGUI @@ -0,0 +1,9 @@ +#!/bin/bash + cd resources + pyrcc5 resources.qrc -o resources_rc.py + mv resources_rc.py .. + + cd ../gui + pyuic5 ui_gui.ui > ui_gui.py + cd .. + diff --git a/exercises/ardrone_navigation/gui/GUI.py b/exercises/ardrone_navigation/gui/GUI.py new file mode 100644 index 000000000..e4f7466df --- /dev/null +++ b/exercises/ardrone_navigation/gui/GUI.py @@ -0,0 +1,202 @@ +# +# Copyright (C) 1997-2016 JDE Developers Team +# +# This program 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. +# +# This program 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 this program. If not, see http://www.gnu.org/licenses/. +# Authors : +# Alberto Martin Florido +# Aitor Martinez Fernandez +# + + +from PyQt5.QtCore import pyqtSignal, Qt +from PyQt5 import QtCore, QtGui, QtWidgets +from PyQt5.QtWidgets import QMainWindow +from gui.ui_gui import Ui_MainWindow +from gui.teleopWidget import TeleopWidget +from gui.cameraWidget import CameraWidget +from gui.communicator import Communicator +from gui.sensorsWidget import SensorsWidget +from gui.logoWidget import LogoWidget + +class MainWindow(QMainWindow, Ui_MainWindow): + + updGUI=pyqtSignal() + def __init__(self, parent=None): + super(MainWindow, self).__init__(parent) + self.setupUi(self) + self.teleop=TeleopWidget(self) + self.tlLayout.addWidget(self.teleop) + self.teleop.setVisible(True) + + self.logo = LogoWidget(self, self.logoLayout.parent().width(), self.logoLayout.parent().height()) + self.logoLayout.addWidget(self.logo) + self.logo.setVisible(True) + + self.record = False + + self.updGUI.connect(self.updateGUI) + + self.cameraCheck.stateChanged.connect(self.showCameraWidget) + self.sensorsCheck.stateChanged.connect(self.showSensorsWidget) + + self.rotationDial.valueChanged.connect(self.rotationChange) + self.altdSlider.valueChanged.connect(self.altitudeChange) + + self.cameraWidget=CameraWidget(self) + self.sensorsWidget=SensorsWidget(self) + + self.cameraCommunicator=Communicator() + self.trackingCommunicator = Communicator() + + #self.stopButton.clicked.connect(self.stopClicked) + self.playButton.clicked.connect(self.playClicked) + self.playButton.setCheckable(True) + self.resetButton.clicked.connect(self.resetClicked) + self.takeoffButton.clicked.connect(self.takeOffClicked) + self.takeoff=False + self.reset=False + + def getCamera(self): + return self.camera + + def setCamera(self,camera): + self.camera = camera + + def getNavData(self): + return self.navdata + + def setNavData(self,navdata): + self.navdata = navdata + + def getPose3D(self): + return self.pose + + def setPose3D(self,pose): + self.pose = pose + + def getCMDVel(self): + return self.cmdvel + + def setCMDVel(self,cmdvel): + self.cmdvel = cmdvel + + def getExtra(self): + return self.extra + + def setExtra(self,extra): + self.extra = extra + + def setAlgorithm(self, algorithm ): + self.algorithm=algorithm + + def getAlgorithm(self): + return self.algorithm + + def updateGUI(self): + self.cameraWidget.imageUpdate.emit() + self.sensorsWidget.sensorsUpdate.emit() + + def playClicked(self): + if self.playButton.isChecked(): + if self.record == True: + self.extra.record(True) + icon = QtGui.QIcon() + self.playButton.setText("Stop Code") + self.playButton.setStyleSheet("background-color: #ec7063") + icon.addPixmap(QtGui.QPixmap(":/images/stop.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.playButton.setIcon(icon) + self.algorithm.play() + else: + if self.record == True: + self.extra.record(False) + icon = QtGui.QIcon() + self.playButton.setStyleSheet("background-color: #7dcea0") + icon.addPixmap(QtGui.QPixmap(":/images/play.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.playButton.setIcon(icon) + self.playButton.setText("Play Code") + self.algorithm.stop() + + #def stopClicked(self): + # if self.record == True: + # self.extra.record(False) + # self.algorithm.stop() + # self.rotationDial.setValue(self.altdSlider.maximum()/2) + # self.altdSlider.setValue(self.altdSlider.maximum()/2) + # self.cmdvel.sendCMDVel(0,0,0,0,0,0) + # self.teleop.stopSIG.emit()''' + + def takeOffClicked(self): + if(self.takeoff==True): + self.takeoffButton.setText("Take Off") + self.extra.land() + self.takeoff=False + else: + self.takeoffButton.setText("Land") + self.extra.takeoff() + self.takeoff=True + + def resetClicked(self): + if self.reset == True: + self.resetButton.setText("Reset") + self.extra.reset() + self.reset=False + else: + self.resetButton.setText("Unreset") + self.extra.reset() + self.reset=True + + def showCameraWidget(self,state): + if state == Qt.Checked: + self.cameraWidget.show() + else: + self.cameraWidget.close() + + def closeCameraWidget(self): + self.cameraCheck.setChecked(False) + + def showSensorsWidget(self,state): + if state == Qt.Checked: + self.sensorsWidget.show() + else: + self.sensorsWidget.close() + + def closeSensorsWidget(self): + self.sensorsCheck.setChecked(False) + + def rotationChange(self,value): + value=(1.0/(self.rotationDial.maximum()/2))*(value - (self.rotationDial.maximum()/2)) + self.rotValue.setText('%.2f' % value) + self.cmdvel.setYaw(value) + self.cmdvel.sendVelocities() + + def altitudeChange(self,value): + value=(1.0/(self.altdSlider.maximum()/2))*(value - (self.altdSlider.maximum()/2)) + self.altdValue.setText('%.2f' % value) + self.cmdvel.setVZ(value) + self.cmdvel.sendVelocities() + + def setXYValues(self,newX,newY): + self.XValue.setText('%.2f' % newX) + self.YValue.setText('%.2f' % newY) + self.cmdvel.setVX(-newY) + self.cmdvel.setVY(-newX) + self.cmdvel.sendVelocities() + + def closeEvent(self, event): + self.algorithm.kill() + self.camera.stop() + self.navdata.stop() + self.pose.stop() + event.accept() + diff --git a/exercises/ardrone_navigation/gui/__init__.py b/exercises/ardrone_navigation/gui/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/exercises/ardrone_navigation/gui/cameraWidget.py b/exercises/ardrone_navigation/gui/cameraWidget.py new file mode 100644 index 000000000..09111dba6 --- /dev/null +++ b/exercises/ardrone_navigation/gui/cameraWidget.py @@ -0,0 +1,84 @@ +# +# Copyright (C) 1997-2015 JDE Developers Team +# +# This program 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. +# +# This program 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 this program. If not, see http://www.gnu.org/licenses/. +# Authors : +# Alberto Martin Florido +# +from PyQt5.QtCore import QSize, pyqtSignal +from PyQt5.QtGui import QImage, QPixmap +from PyQt5.QtWidgets import QPushButton,QWidget, QLabel + + +class CameraWidget(QWidget): + IMAGE_COLS_MAX=640 + IMAGE_ROWS_MAX=360 + LINX=0.3 + LINY=0.3 + LINZ=0.8 + ANGZ=1.0 + ANGY=0.0 + ANGX=0.0 + + imageUpdate=pyqtSignal() + + def __init__(self,winParent): + super(CameraWidget, self).__init__() + self.winParent=winParent + self.imageUpdate.connect(self.updateImage) + self.initUI() + + def initUI(self): + + self.setMinimumSize(680,500) + self.setMaximumSize(680,500) + + self.setWindowTitle("Camera") + changeCamButton=QPushButton("Change Camera") + changeCamButton.resize(170,40) + changeCamButton.move(245,450) + changeCamButton.setParent(self) + changeCamButton.clicked.connect(self.changeCamera) + + self.imgLabel=QLabel(self) + self.imgLabel.resize(640,360) + self.imgLabel.move(10,5) + self.imgLabel.show() + + def updateImage(self): + + img = self.winParent.getCamera().getImage().data + if img is not None: + image = QImage(img.data, img.shape[1], img.shape[0], img.shape[1]*img.shape[2], QImage.Format_RGB888); + + if img.shape[1]==self.IMAGE_COLS_MAX: + x=20 + else: + x=(self.IMAGE_COLS_MAX+20)/2-(img.shape[1]/2) + if img.shape[0]==self.IMAGE_ROWS_MAX: + y=40 + else: + y=(self.IMAGE_ROWS_MAX+40)/2-(img.shape[0]/2) + + size=QSize(img.shape[1],img.shape[0]) + self.imgLabel.move(x,y) + self.imgLabel.resize(size) + self.imgLabel.setPixmap(QPixmap.fromImage(image)) + + def closeEvent(self, event): + self.winParent.closeCameraWidget() + + def changeCamera(self): + self.winParent.getExtra().toggleCam() + diff --git a/exercises/ardrone_navigation/gui/communicator.py b/exercises/ardrone_navigation/gui/communicator.py new file mode 100644 index 000000000..ac7c1a026 --- /dev/null +++ b/exercises/ardrone_navigation/gui/communicator.py @@ -0,0 +1,24 @@ +# +# Copyright (C) 1997-2015 JDE Developers Team +# +# This program 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. +# +# This program 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 this program. If not, see http://www.gnu.org/licenses/. +# Authors : +# Alberto Martin Florido +# +#!/usr/bin/python +# -*- coding: utf-8 -*- + +from PyQt5 import QtCore +class Communicator(QtCore.QObject): + updateBW = QtCore.pyqtSignal() diff --git a/exercises/ardrone_navigation/gui/logoWidget.py b/exercises/ardrone_navigation/gui/logoWidget.py new file mode 100644 index 000000000..59b6498d8 --- /dev/null +++ b/exercises/ardrone_navigation/gui/logoWidget.py @@ -0,0 +1,44 @@ +# +# Copyright (C) 1997-2015 JDE Developers Team +# +# This program 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. +# +# This program 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 this program. If not, see http://www.gnu.org/licenses/. +# Authors : +# Alberto Martin Florido +# +import resources_rc +from PyQt5 import QtGui +from PyQt5.QtCore import pyqtSignal, QPointF, Qt, QPoint +from PyQt5.QtWidgets import QWidget, QGridLayout + +class LogoWidget(QWidget): + + def __init__(self,winParent, width=0, height=0): + super(LogoWidget, self).__init__() + self.winParent=winParent + qimage=QtGui.QImage() + qimage.load(':images/jderobot.png') + if (width != 0 and height != 0): + self.qimage = qimage.scaled(0.8*width, 0.8*height, Qt.KeepAspectRatio) + #self.qimage = qimage.scaled(0.8*width, 0.8*height) + self.resize(width, height) + else: + self.qimage = qimage + + + def paintEvent(self, e): + + painter=QtGui.QPainter(self) + painter.drawImage(self.width()/2-self.qimage.width()/2, self.height()/2-self.qimage.height()/2, self.qimage) + + diff --git a/exercises/ardrone_navigation/gui/sensorsWidget.py b/exercises/ardrone_navigation/gui/sensorsWidget.py new file mode 100644 index 000000000..ed043f3ee --- /dev/null +++ b/exercises/ardrone_navigation/gui/sensorsWidget.py @@ -0,0 +1,254 @@ +# +# Copyright (C) 1997-2015 JDE Developers Team +# +# This program 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. +# +# This program 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 this program. If not, see http://www.gnu.org/licenses/. +# Authors : +# Alberto Martin Florido +# +from PyQt5.QtWidgets import QWidget, QLabel, QGridLayout, QVBoxLayout, QSpacerItem, QSizePolicy +from PyQt5 import QtCore +from PyQt5.QtCore import pyqtSignal, Qt +# from gui.speedoMeter import SpeedoMeter +# from gui.attitudeIndicator import AttitudeIndicator +from qfi import qfi_ADI, qfi_ALT, qfi_SI, qfi_HSI +import math + + +class SensorsWidget(QWidget): + sensorsUpdate = pyqtSignal() + + def __init__(self, winParent): + super(SensorsWidget, self).__init__() + self.winParent = winParent + self.sensorsUpdate.connect(self.updateSensors) + self.initUI() + + def initUI(self): + + self.mainLayout = QGridLayout() + self.horizonLayout = QVBoxLayout() + self.horizonData = QGridLayout() + self.compassLayout = QVBoxLayout() + self.compassData = QGridLayout() + self.altLayout = QVBoxLayout() + self.altData = QGridLayout() + + self.setMinimumSize(660, 450) + self.setMaximumSize(660, 450) + + self.setWindowTitle("Sensors") + + self.pitchLabel = QLabel('Pitch:', self) + self.pitchValueLabel = QLabel('0', self) + self.pitchValueLabel.setAlignment(Qt.AlignRight | Qt.AlignTrailing | Qt.AlignVCenter) + + self.rollLabel = QLabel('Roll:', self) + self.rollValueLabel = QLabel('0', self) + self.rollValueLabel.setAlignment(Qt.AlignRight | Qt.AlignTrailing | Qt.AlignVCenter) + + self.yawLabel = QLabel('Yaw:', self) + self.yawValueLabel = QLabel('0', self) + self.yawValueLabel.setAlignment(Qt.AlignRight | Qt.AlignTrailing | Qt.AlignVCenter) + + self.altLabel = QLabel('Alt:', self) + self.altValueLabel = QLabel('0', self) + self.altValueLabel.setAlignment(Qt.AlignRight | Qt.AlignTrailing | Qt.AlignVCenter) + + self.pitchgLabel = QLabel("\272", self) + self.rollgLabel = QLabel("\272", self) + self.yawgLabel = QLabel("\272", self) + self.altmLabel = QLabel('m', self) + + hSpacer = QSpacerItem(100, 30, QSizePolicy.Ignored, QSizePolicy.Ignored) + + self.horizonData.addItem(hSpacer, 0, 0, 1, 1, Qt.AlignLeft) + self.horizonData.addWidget(self.pitchLabel, 0, 1, Qt.AlignCenter) + self.horizonData.addWidget(self.pitchValueLabel, 0, 2, Qt.AlignCenter) + self.horizonData.addWidget(self.pitchgLabel, 0, 3, Qt.AlignCenter) + self.horizonData.addWidget(self.rollLabel, 0, 4, Qt.AlignCenter) + self.horizonData.addWidget(self.rollValueLabel, 0, 5, Qt.AlignCenter) + self.horizonData.addWidget(self.rollgLabel, 0, 6, Qt.AlignCenter) + self.horizonData.addItem(hSpacer, 0, 7, 1, 1, Qt.AlignRight) + + self.compassData.addItem(hSpacer, 0, 0, 1, 1, Qt.AlignLeft) + self.compassData.addWidget(self.yawLabel, 0, 1, Qt.AlignCenter) + self.compassData.addWidget(self.yawValueLabel, 0, 2, Qt.AlignCenter) + self.compassData.addWidget(self.yawgLabel, 0, 3, Qt.AlignCenter) + self.compassData.addItem(hSpacer, 0, 4, 1, 1, Qt.AlignRight) + + self.altData.addItem(hSpacer, 0, 0, 1, 1, Qt.AlignLeft) + self.altData.addWidget(self.altLabel, 0, 1, Qt.AlignCenter) + self.altData.addWidget(self.altValueLabel, 0, 2, Qt.AlignCenter) + self.altData.addWidget(self.altmLabel, 0, 3, Qt.AlignCenter) + self.altData.addItem(hSpacer, 0, 4, 1, 1, Qt.AlignLeft) + + self.altd = qfi_ALT.qfi_ALT(self) + + self.altd.setFixedSize(QtCore.QSize(200,200)) + self.altLayout.addWidget(self.altd) + self.altLayout.addLayout(self.altData) + # self.altd.move(420,50) + + self.compass = qfi_HSI.qfi_HSI(self) + self.compass.setFixedSize(QtCore.QSize(200, 200)) + self.compassLayout.addWidget(self.compass) + self.compassLayout.addLayout(self.compassData) + + self.horizon = qfi_ADI.qfi_ADI(self) + self.horizon.setFixedSize(QtCore.QSize(200, 200)) + self.horizonLayout.addWidget(self.horizon) + self.horizonLayout.addLayout(self.horizonData) + + # self.battery=Qwt.QwtThermo(self) + # self.battery.setMaxValue(100.0) + # self.battery.setMinValue(0.0) + # self.battery.setPipeWidth(10) + + # self.battery.move(580,10) + # self.battery.resize(56,241) + # self.batteryLabel=QLabel('Battery (%)',self) + # self.batteryLabel.move(580,251) + + self.velLinX = qfi_SI.qfi_SI(self) + self.velLinX.setFixedSize(QtCore.QSize(150, 150)) + # self.velLinX.move(60,270) + self.velXLabel = QLabel('Linear X (m/s)', self) + # self.velXLabel.move(95,420) + + + self.velLinY = qfi_SI.qfi_SI(self) + self.velLinY.setFixedSize(QtCore.QSize(150, 150)) + # self.velLinY.move(240,270) + self.velYLabel = QLabel('Linear Y (m/s)', self) + # self.velYLabel.move(275,420) + + self.velLinZ = qfi_SI.qfi_SI(self) + self.velLinZ.setFixedSize(QtCore.QSize(150, 150)) + # self.velLinZ.setLabel("8 m/s") + # self.velLinZ.move(420,270) + self.velZLabel = QLabel('Linear Z (m/s)', self) + # self.velZLabel.move(455,420) + + self.mainLayout.addLayout(self.horizonLayout, 0, 0, Qt.AlignCenter) + self.mainLayout.addLayout(self.compassLayout, 0, 1, Qt.AlignCenter) + self.mainLayout.addLayout(self.altLayout, 0, 2, Qt.AlignCenter) + self.mainLayout.addWidget(self.velLinX, 1, 0, Qt.AlignCenter) + self.mainLayout.addWidget(self.velLinY, 1, 1, Qt.AlignCenter) + self.mainLayout.addWidget(self.velLinZ, 1, 2, Qt.AlignCenter) + self.mainLayout.addWidget(self.velXLabel, 2, 0, Qt.AlignCenter) + self.mainLayout.addWidget(self.velYLabel, 2, 1, Qt.AlignCenter) + self.mainLayout.addWidget(self.velZLabel, 2, 2, Qt.AlignCenter) + self.setLayout(self.mainLayout); + + def updateSensors(self): + pose = self.winParent.getPose3D().getPose3d() + + if pose != None: + qw = pose.q[0] + qx = pose.q[1] + qy = pose.q[2] + qz = pose.q[3] + self.drawAltd(pose.z) + self.drawYawValues(self.quatToYaw(qw, qx, qy, qz) * 180 / math.pi) + self.drawPitchRollValues(self.quatToPitch(qw, qx, qy, qz) * 180 / math.pi, + self.quatToRoll(qw, qx, qy, qz) * 180 / math.pi) + + navdata = self.winParent.getNavData().getNavData() + + if navdata != None: + # self.battery.setValue(navdata.batteryPercent) + self.drawVelocities(navdata.vx, navdata.vy, navdata.vz) + + def drawYawValues(self, degress): + value = "{0:.2f}".format(degress) + self.yawValueLabel.setText(value) + self.compass.setHeading(degress) + self.compass.viewUpdate.emit() + + def drawAltd(self, meters): + + self.altd.setAltitude(meters * 10) + self.altd.viewUpdate.emit() + + value = "{0:.0f}".format(meters) + self.altValueLabel.setText(value) + + def drawPitchRollValues(self, pitch, roll): + if (pitch > 0 and pitch <= 90): + result = pitch / 90 + result = -result + elif (pitch < 0 and pitch >= -90): + result = pitch / -90 + else: + result = 0.0 + + self.horizon.setPitch(pitch) + self.horizon.setRoll(-roll) + self.horizon.viewUpdate.emit() + pitchValue = "{0:.2f}".format(pitch) + rollValue = "{0:.2f}".format(roll) + self.pitchValueLabel.setText(pitchValue) + self.rollValueLabel.setText(rollValue) + + def drawVelocities(self, vx, vy, vz): + + vx = math.fabs(vx) + vx /= 1000.0 + self.velLinX.setSpeed(vx) + self.velLinX.viewUpdate.emit() + vx = math.fabs(vx) + + vy = math.fabs(vy) + vy /= 1000.0 + self.velLinY.setSpeed(vy) + self.velLinY.viewUpdate.emit() + + vz = math.fabs(vz) + vz /= 1000.0 + self.velLinZ.setSpeed(vz) + self.velLinZ.viewUpdate.emit() + + def quatToRoll(self, qw, qx, qy, qz): + rotateXa0 = 2.0 * (qy * qz + qw * qx) + rotateXa1 = qw * qw - qx * qx - qy * qy + qz * qz + rotateX = 0.0 + + if (rotateXa0 != 0.0 and rotateXa1 != 0.0): + rotateX = math.atan2(rotateXa0, rotateXa1) + + return rotateX + + def quatToPitch(self, qw, qx, qy, qz): + rotateYa0 = -2.0 * (qx * qz - qw * qy) + rotateY = 0.0 + if (rotateYa0 >= 1.0): + rotateY = math.pi / 2.0 + elif (rotateYa0 <= -1.0): + rotateY = -math.pi / 2.0 + else: + rotateY = math.asin(rotateYa0) + + return rotateY + + def quatToYaw(self, qw, qx, qy, qz): + rotateZa0 = 2.0 * (qx * qy + qw * qz) + rotateZa1 = qw * qw + qx * qx - qy * qy - qz * qz + rotateZ = 0.0 + if (rotateZa0 != 0.0 and rotateZa1 != 0.0): + rotateZ = math.atan2(rotateZa0, rotateZa1) + + return rotateZ + + def closeEvent(self, event): + self.winParent.closeSensorsWidget() diff --git a/exercises/ardrone_navigation/gui/teleopWidget.py b/exercises/ardrone_navigation/gui/teleopWidget.py new file mode 100644 index 000000000..02a067fa7 --- /dev/null +++ b/exercises/ardrone_navigation/gui/teleopWidget.py @@ -0,0 +1,113 @@ +# +# Copyright (C) 1997-2015 JDE Developers Team +# +# This program 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. +# +# This program 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 this program. If not, see http://www.gnu.org/licenses/. +# Authors : +# Alberto Martin Florido +# +import resources_rc +from PyQt5.QtGui import QImage, QPainter, QPen +from PyQt5.QtCore import pyqtSignal, QPointF, Qt, QPoint +from PyQt5.QtWidgets import QWidget, QGridLayout + +class TeleopWidget(QWidget): + + stopSIG=pyqtSignal() + + def __init__(self,winParent): + super(TeleopWidget, self).__init__() + self.winParent=winParent + self.line = QPointF(0, 0); + self.qimage=QImage() + self.qimage.load(':images/ball.png') + self.stopSIG.connect(self.stop) + self.initUI() + + def initUI(self): + layout=QGridLayout() + self.setLayout(layout) + self.setAutoFillBackground(True) + p = self.palette() + p.setColor(self.backgroundRole(), Qt.black) + self.setPalette(p) + self.resize(300,300) + self.setMinimumSize(300,300) + + def stop(self): + self.line = QPointF(0, 0); + self.repaint(); + + def mouseMoveEvent(self,e): + if e.buttons() == Qt.LeftButton: + x = e.x()-self.width()/2 + y = e.y()-self.height()/2 + self.line = QPointF(x, y) + self.repaint() + + def paintEvent(self, e): + _width = self.width() + _height = self.height() + + + width = 2 + + painter=QPainter(self) + + pen = QPen(Qt.blue, width) + painter.setPen(pen) + + #Centro del widget + painter.translate(QPoint(_width/2, _height/2)) + + #eje + painter.drawLine(QPointF(-_width, 0), + QPointF( _width, 0)) + + painter.drawLine(QPointF(0, -_height), + QPointF(0, _height)) + + #con el raton + pen = QPen(Qt.red, width) + painter.setPen(pen) + + #Comprobamos que el raton este dentro de los limites + if abs(self.line.x()*2) >= self.size().width(): + if self.line.x()>=0: + self.line.setX(self.size().width()/2) + elif self.line.x()<0: + self.line.setX((-self.size().width()/2)+1) + + if abs(self.line.y()*2) >= self.size().height(): + if self.line.y()>=0: + self.line.setY(self.size().height()/2) + elif self.line.y()<0: + self.line.setY((-self.size().height()/2)+1) + + painter.drawLine(QPointF(self.line.x(), -_height), + QPointF(self.line.x(), _height)) + + painter.drawLine(QPointF(-_width, self.line.y()), + QPointF( _width, self.line.y())) + + #print "x: %f y: %f" % (self.line.x(), self.line.y()) + + v_normalized = (1.0/(self.size().height()/2)) * self.line.y() + v_normalized = float("{0:.2f}".format(v_normalized)) + w_normalized = (1.0/(self.size().width()/2)) * self.line.x() + w_normalized = float("{0:.2f}".format(w_normalized)) + + #print "v: %f w: %f" % (v_normalized,w_normalized) + self.winParent.setXYValues(w_normalized,v_normalized) + painter.drawImage(self.line.x()-self.qimage.width()/2, self.line.y()-self.qimage.height()/2, self.qimage); + diff --git a/exercises/ardrone_navigation/gui/threadGUI.py b/exercises/ardrone_navigation/gui/threadGUI.py new file mode 100644 index 000000000..1c6e80781 --- /dev/null +++ b/exercises/ardrone_navigation/gui/threadGUI.py @@ -0,0 +1,43 @@ +# +# Copyright (C) 1997-2015 JDE Developers Team +# +# This program 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. +# +# This program 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 this program. If not, see http://www.gnu.org/licenses/. +# Authors : +# Alberto Martin Florido +# +import threading, time +from datetime import datetime + +time_cycle = 50; + +class ThreadGUI(threading.Thread): + def __init__(self, gui): + self.gui = gui + + threading.Thread.__init__(self) + + def run(self): + + while(True): + + start_time = datetime.now() + self.gui.updGUI.emit() + + finish_Time = datetime.now() + + dt = finish_Time - start_time + ms = (dt.days * 24 * 60 * 60 + dt.seconds) * 1000 + dt.microseconds / 1000.0 + + if(ms < time_cycle): + time.sleep((time_cycle-ms) / 1000.0); \ No newline at end of file diff --git a/exercises/ardrone_navigation/gui/ui_gui.py b/exercises/ardrone_navigation/gui/ui_gui.py new file mode 100644 index 000000000..bff2a6c93 --- /dev/null +++ b/exercises/ardrone_navigation/gui/ui_gui.py @@ -0,0 +1,120 @@ +# -*- coding: utf-8 -*- + +# Form implementation generated from reading ui file 'ui_gui.ui' +# +# Created by: PyQt5 UI code generator 5.5.1 +# +# WARNING! All changes made in this file will be lost! + +from PyQt5 import QtCore, QtGui, QtWidgets + +class Ui_MainWindow(object): + def setupUi(self, MainWindow): + MainWindow.setObjectName("MainWindow") + MainWindow.resize(660, 370) + MainWindow.setMinimumSize(QtCore.QSize(660, 370)) + MainWindow.setMaximumSize(QtCore.QSize(660, 370)) + self.centralwidget = QtWidgets.QWidget(MainWindow) + self.centralwidget.setObjectName("centralwidget") + self.takeoffButton = QtWidgets.QPushButton(self.centralwidget) + self.takeoffButton.setGeometry(QtCore.QRect(470, 30, 161, 41)) + self.takeoffButton.setObjectName("takeoffButton") + self.altdSlider = QtWidgets.QSlider(self.centralwidget) + self.altdSlider.setGeometry(QtCore.QRect(400, 30, 19, 311)) + self.altdSlider.setMaximum(100) + self.altdSlider.setProperty("value", 49) + self.altdSlider.setOrientation(QtCore.Qt.Vertical) + self.altdSlider.setObjectName("altdSlider") + self.playButton = QtWidgets.QPushButton(self.centralwidget) + self.playButton.setGeometry(QtCore.QRect(470, 80, 161, 51)) + icon = QtGui.QIcon() + icon.addPixmap(QtGui.QPixmap(":/images/play.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.playButton.setIcon(icon) + self.playButton.setObjectName("playButton") + #self.stopButton = QtWidgets.QPushButton(self.centralwidget) + #self.stopButton.setGeometry(QtCore.QRect(560, 80, 71, 51)) + #icon1 = QtGui.QIcon() + #icon1.addPixmap(QtGui.QPixmap(":/images/stop.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + #self.stopButton.setIcon(icon1) + #self.stopButton.setObjectName("stopButton") + self.windowsLabel = QtWidgets.QLabel(self.centralwidget) + self.windowsLabel.setGeometry(QtCore.QRect(540, 190, 71, 21)) + self.windowsLabel.setObjectName("windowsLabel") + self.cameraCheck = QtWidgets.QCheckBox(self.centralwidget) + self.cameraCheck.setGeometry(QtCore.QRect(540, 220, 94, 26)) + self.cameraCheck.setObjectName("cameraCheck") + self.sensorsCheck = QtWidgets.QCheckBox(self.centralwidget) + self.sensorsCheck.setGeometry(QtCore.QRect(540, 250, 94, 26)) + self.sensorsCheck.setObjectName("sensorsCheck") + self.altdLabel = QtWidgets.QLabel(self.centralwidget) + self.altdLabel.setGeometry(QtCore.QRect(390, 340, 51, 21)) + self.altdLabel.setObjectName("altdLabel") + self.verticalLayoutWidget = QtWidgets.QWidget(self.centralwidget) + self.verticalLayoutWidget.setGeometry(QtCore.QRect(20, 30, 361, 301)) + self.verticalLayoutWidget.setObjectName("verticalLayoutWidget") + self.tlLayout = QtWidgets.QVBoxLayout(self.verticalLayoutWidget) + self.tlLayout.setObjectName("tlLayout") + self.rotationDial = QtWidgets.QDial(self.centralwidget) + self.rotationDial.setGeometry(QtCore.QRect(440, 220, 50, 64)) + self.rotationDial.setMaximum(100) + self.rotationDial.setProperty("value", 49) + self.rotationDial.setObjectName("rotationDial") + self.rotationLabel = QtWidgets.QLabel(self.centralwidget) + self.rotationLabel.setGeometry(QtCore.QRect(440, 280, 65, 21)) + self.rotationLabel.setObjectName("rotationLabel") + self.XLabel = QtWidgets.QLabel(self.centralwidget) + self.XLabel.setGeometry(QtCore.QRect(20, 340, 21, 21)) + self.XLabel.setObjectName("XLabel") + self.YLabel = QtWidgets.QLabel(self.centralwidget) + self.YLabel.setGeometry(QtCore.QRect(130, 340, 21, 21)) + self.YLabel.setObjectName("YLabel") + self.XValue = QtWidgets.QLabel(self.centralwidget) + self.XValue.setGeometry(QtCore.QRect(40, 340, 41, 21)) + self.XValue.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) + self.XValue.setObjectName("XValue") + self.YValue = QtWidgets.QLabel(self.centralwidget) + self.YValue.setGeometry(QtCore.QRect(150, 340, 41, 21)) + self.YValue.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) + self.YValue.setObjectName("YValue") + self.altdValue = QtWidgets.QLabel(self.centralwidget) + self.altdValue.setGeometry(QtCore.QRect(390, 10, 41, 21)) + self.altdValue.setAlignment(QtCore.Qt.AlignCenter) + self.altdValue.setObjectName("altdValue") + self.rotValue = QtWidgets.QLabel(self.centralwidget) + self.rotValue.setGeometry(QtCore.QRect(445, 200, 41, 21)) + self.rotValue.setAlignment(QtCore.Qt.AlignCenter) + self.rotValue.setObjectName("rotValue") + self.resetButton = QtWidgets.QPushButton(self.centralwidget) + self.resetButton.setGeometry(QtCore.QRect(470, 140, 161, 41)) + self.resetButton.setObjectName("resetButton") + self.verticalLayoutWidget_2 = QtWidgets.QWidget(self.centralwidget) + self.verticalLayoutWidget_2.setGeometry(QtCore.QRect(570, 280, 81, 80)) + self.verticalLayoutWidget_2.setObjectName("verticalLayoutWidget_2") + self.logoLayout = QtWidgets.QVBoxLayout(self.verticalLayoutWidget_2) + self.logoLayout.setSpacing(0) + self.logoLayout.setObjectName("logoLayout") + MainWindow.setCentralWidget(self.centralwidget) + + self.retranslateUi(MainWindow) + QtCore.QMetaObject.connectSlotsByName(MainWindow) + + def retranslateUi(self, MainWindow): + _translate = QtCore.QCoreApplication.translate + MainWindow.setWindowTitle(_translate("MainWindow", "Position Control")) + self.takeoffButton.setText(_translate("MainWindow", "Take off")) + self.playButton.setText(_translate("MainWindow", "Play Code")) + #self.stopButton.setText(_translate("MainWindow", "Stop Code")) + self.windowsLabel.setText(_translate("MainWindow", "Windows:")) + self.cameraCheck.setText(_translate("MainWindow", "Camera")) + self.sensorsCheck.setText(_translate("MainWindow", "Sensors")) + self.altdLabel.setText(_translate("MainWindow", "Altitude")) + self.rotationLabel.setText(_translate("MainWindow", "Rotation")) + self.XLabel.setText(_translate("MainWindow", "X:")) + self.YLabel.setText(_translate("MainWindow", "Y:")) + self.XValue.setText(_translate("MainWindow", "0")) + self.YValue.setText(_translate("MainWindow", "0")) + self.altdValue.setText(_translate("MainWindow", "0")) + self.rotValue.setText(_translate("MainWindow", "0")) + self.resetButton.setText(_translate("MainWindow", "Reset")) + +import resources_rc diff --git a/exercises/ardrone_navigation/gui/ui_gui.ui b/exercises/ardrone_navigation/gui/ui_gui.ui new file mode 100644 index 000000000..3ec5a3b61 --- /dev/null +++ b/exercises/ardrone_navigation/gui/ui_gui.ui @@ -0,0 +1,311 @@ + + + MainWindow + + + + 0 + 0 + 660 + 370 + + + + + 660 + 370 + + + + + 660 + 370 + + + + Position Control + + + + + + 470 + 30 + 161 + 41 + + + + Take off + + + + + + 400 + 30 + 19 + 311 + + + + 100 + + + 49 + + + Qt::Vertical + + + + + + 470 + 80 + 71 + 51 + + + + Play + + + + :/images/play.png:/images/play.png + + + + + + 560 + 80 + 71 + 51 + + + + Stop + + + + :/images/stop.png:/images/stop.png + + + + + + 540 + 190 + 71 + 21 + + + + Windows: + + + + + + 540 + 220 + 94 + 26 + + + + Camera + + + + + + 540 + 250 + 94 + 26 + + + + Sensors + + + + + + 390 + 340 + 51 + 21 + + + + Altitude + + + + + + 20 + 30 + 361 + 301 + + + + + + + + 440 + 220 + 50 + 64 + + + + 100 + + + 49 + + + + + + 440 + 280 + 65 + 21 + + + + Rotation + + + + + + 20 + 340 + 21 + 21 + + + + X: + + + + + + 130 + 340 + 21 + 21 + + + + Y: + + + + + + 40 + 340 + 41 + 21 + + + + 0 + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + 150 + 340 + 41 + 21 + + + + 0 + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + 390 + 10 + 41 + 21 + + + + 0 + + + Qt::AlignCenter + + + + + + 445 + 200 + 41 + 21 + + + + 0 + + + Qt::AlignCenter + + + + + + 470 + 140 + 161 + 41 + + + + Reset + + + + + + 570 + 280 + 81 + 80 + + + + + 0 + + + + + + + + + + diff --git a/exercises/ardrone_navigation/map_image.png b/exercises/ardrone_navigation/map_image.png new file mode 100644 index 0000000000000000000000000000000000000000..b1bfed830864ae67a4ea83cfc1ee4bcac9d0e867 GIT binary patch literal 168570 zcmeEt_d8r)^se5^DAA2xqKnSxA|fIpy3s`lkp9AdIc4v)*1Okw*Sq5k^zTuUv5?{5;85Pz(K5ur!RN%m!80Qv z0^Z5=jer5a2)s1z8<7BCK_rd{;58{s$J`4ChuZGu8~3Yfp$iTUC(eB>btC`mt!5WD z=ms2fULK4Qd-G8+j{dIUrc+(Rv@V-~N0?r=t2}uGZw#el80Qs;$L0Indm4|G{%JZ=N8U&(%u&1 ze(>q&8ds~bXc&$PoO|M^e0}J~HGdKe(jeiA0%_=wlg#gs{_ht%U_rHi?~-tdM&beg zy?OET&i}j#OvL|8$p1LQ|KC9PSQmH`lQ{Dytg4GJrhJ2S-$!vkj84(sJlBfslXCPT z9QHG=sF$l^PQrD~d`KSL59K5tI z|8Z0hI64+#n&}4bZZ7I#5C)(_{C+S;jRj@5Jx!`EVTK-2z5&T13`rC5)IT@YqBJI; zIq7-LE8r?1c7Cmh+&4PB8jpc{I9h7n+)A|LdtOs}=Wx zl`J}qa=u|o67Z=M6ODf#Zi@mwhQH9Iv3KcNPXP>a$&T-l_F+pH`TU(n_iHSoch3`F zMCQJ+J!NR-1Lnbkk`OrCjce`m*YW3aMo6+~jlE!w`1p9iR9#A%$E7B$yVJEb7Ns;_ zYS?OX;P7zeqY^7LEw+9m{qtLGL`v{)698GvPS+0 zNuDk*Eu!0=GT#o)@y9tG=B%$}Q|wH_-+;y*_js{JegvMO&*a}gvk=UG81M*ZnC`5? z!LPdQ3(kx!Em=Q~I(I&yJ@Wj8^`VakY!S!T!7B*H5uu@AB`qggOC=yoPVBB~11=*= z9%D(cBa_1qsfQ#Rev9&~Pk_lMXmN)Fe>i&|hj#PhxlMmCBjNvUdCCyRXKyF- zNPDk&_08f*T80(fo4+22%cXadbZ<9YaGFouXg(T8G!*n-NPK1}ifY$@OH& z%Y}hY{n7oA2UVHQX1Ax51$Cd-umFdBrI}_zg()duk}UNbldyKNkY?_giJ=M%zkM(g z%C|1(d9A~ls{6v_{>-ZGj)lrA(zmNx_p2q$iv>xeH4<0;*qYn^5xRL2T6#|4r@qMp z3F5Rhc$dB8if29Fts;xK=m&(Ad!@Aw%y3_4-ED2paEQ;B(}59@ec45tg_CL3Y>o2LtIbV4n8_ovk0;b zef(tjO?u7Nq3XuOGb%Tu_fvMgFCqSZ0GMnbcD@;r^*efwC7MWO?mDK?@wH9J8c3sb z~TM%qm?AGCR_xQ4;7!oeHfnU_MfVTrN^&|<;L?X2EsH=lP= z25ghkNJWlYr9$6!a0vmB2}e?A`uA^l=e2vaX*JRY-^rjoo>sFjWv_-h>L!bUNT?+W z$)eTRVM%l4opggtV13@{^1T~a?ZMVV-5*d4)~tk$R9O8OVi7F+H9lKgxt4+@ov#YU z{b!P)2ml^EO)JtM)PUQC83_^M0OOkONNF1L(sYj=Z)nI8D6SXT^X%UdPNe?l=xz)* z&A1$yW&eR5b~kY!(q06+~0J0qcJn0(VGG{KZP!QHsj2T8rl1N@D|C!{eiOYgq> zcXkN}8DI(aeoxZ1A-@mGD9uW+sa?IKPo!adQ2M-Zh0SH-X=5`bdoCmnN98<963xc_gU^QR7_LSQN=I>^7O-~ji zBW`ZG;vRD|%HE0z2=xF~r*KS%msWbKkJg=Ex3(z!<1J-dVD9^7aRBdd>~(;HCj>q! z==JQ}d9VAK^#D>!C(U0MGqfl1LL0xkhWF$A^Kx1M2mAEHfQwc+n3ulh*EpQ>$$Bt~aezaWWd_0Lnlk;({e9B@bL#($Kkr1lsX8CiSAJrfBYd^RjN(G+{H zDZQQ-GnX#`1B15ELwlgx6SfcXZ%Y zO^Gb+`!5I2pZpideGuxZ7iOi!fu9Y-0pQQ&!%6eknfR$j^KX2^|DKn}OspM(SOo&* z?rBT1Si`Jywv(+-1 z(-ThURG1WVfs=VsbxRdckty3+2Jg)JY%7Y-bt&o5zX3nHFq8zyZAS z*Yg_Dp~Fcb>b{%>yL_64!at3KMlX4YWX zyLj@s!@7PSE?1p?arw1&W5YBiwOJQPAjkmU`m|aifGU8B@vu?xWDRjM#EVXSsb^u} z5?1H&^)bP2x<@$HTl2YoOX>!01-hvvei{Ps)^-qA2EbI}6~RBOIjCh~J2FUV%(2D4 zOa4u_aO^=xoyn$g75MSZ6V6!O0FFgOcf`C%`^*^}SVwtjNdXWueh_QO=0@$$z5_N9 zCJ!p6q~B)RG6pb_{Ko4am_JaZMjaXeU=9b{S29&ixeXjZ+~w41o@%pc>h0+JcF445 z2rN5M(!ZzUtNW=o>de5`ZrBy46#)!y>dOrK{9aT_nnO>laVu^b>hK2Hjf4^*$^MX* zH3zpE;!mkuW~sWL7cX(sOt>rt*mOVBwj89EFKiShJ9t#aK!8R@u#Y-_=b}w3)-V!Ebw^rmovVODFFb-Smh;*-#FJZn`};IAfxFF+97dnkc{+^}&0VF(A(Y zGt!6Tfo418zi9_uZ}Jo-&-xi!k186A)Tow>UI`i#5e8P*TgkmN$-S(Lrs@&kPdDJ7 zo|V-ppt67e;&NU!slQ($C=|DiJB%OotTa~T`~F7BteZPJ^^cE7OC7U1}T3j|TQ&|jQ2oE4m?BR@JWnc!etZjC=`P29`~&dPaVSi}l0 zYXoK}-{7l*(-%4c{p_x~*5F(HD|N4R;p^+`ZQ=M<^4`v%lUYBGv$Hb^S=pHI@bDok zd5sA*FMQxpy0veW*yg=ixPG;kO$)5*pK5L}VB&7Y>jm9Qh=@s};Wy3Q9?Md-D zOBw~%`?W6_IL5}tPCHdEcrLK5A=K!yj7c3F=SY$yHr`zaFtUyK{K6LCh!q&<6d0`c93Amx%hU9rTSLH=lXWS&e3h=I zO!)ZC{tMxc+2kvzPv%2aweQ_~tzWqNgDo$yg6>M&w}UPGGbMfEn%2pxR=)+S;O~E- zm3XtznA0(Y*xLLa@?#JG3%XYw z(2L~O?r3tEU(fG~9J`-_9>v6^5NK&_;Z-oIF={!oMfz)OHFIl4X>l%^&!(i5s^IXk z%TdzYabn5|BY6YIL33KCKGG85k! zOT0g#No{w1b$Jtk0RaKd-h`8+)=fD*o_Xb|z$h9TUVaL<-(wnT){Wq6DHs(5YQ(_Q zlt!`}Cx2-EfTtZO|A1r(DE|OIcax0|QVBJk`%6`JSDMAj@5$Zyll_&qkCv#f_a1p{ zO-lb*Rm}D)0G-Z47^d1>*N)dmPRekRc(w%G7K)tUWU<*oa&8?(&fH|hF!1b49BzZE zl=4?_#KLVnBn~IhLRAYIcR@ZVRJ>^XPSH5+t8Om6M|qubbShWNs%6|P!FU;ZtiIc? zY+)6w_mXBJ^YfL{)6?4)J7ZZ%|2Upk8HUGg_XH5kY6GKk!oS;Sw+sjW-@#Os)V#L+ zW~5OM)s|;-Ux5?!PHvRQ5DffS6%!Ma%@pdfuk>N2#Gvwe@2MP|pZ-SYcFsVVmZEhu zA#7+aa3UT6_-tCz6zWajOHg??Mc93uC!j?S`4T`Y%W6k99WY3Bu&VOnvmpznTOa6?Y1z>N^B9P?tB$M!pPMmh4)-|H%;U z?fu=((Ge$>igKy%^|>SvH0|x`-abB~>)fnMCMt5QFFqixAKOoQ0Ej;G@Frl~Inyz? zGycS=OpJNxDZ@tqJMp zD8|1Gav&RBZ{>O0Z-*|UkBL$Am=BJH?JeLYBqrK6riJ zF&^MHywnBk-25UoowWU9Ajwfu(&FFbjCelaHmstjZrv2&eL>X^=XW_tHwh6;ND>YYLSJddkTJ;J&?q(88j5o=D;L!nOKoRwj~5vk z36!_RA#1)J&paDZ`@Zp)+dVhc&BS=HjO5Rt1Wq7J*dfqRxh?>CqQ|l^b~Xv^;vztH zBb3TgPr2cJ_NAV(y1F`%%*--6wRU@b-7I*S@{XKbth4khJ^nHOQoz=VIo5tJ-GO;XhbV&x){{hjuXrkKx$qOR0(&qh{Wah?@p~q%r^@53gyIuJt!4{Z?&dyFI zFjzYonix#&>W1;7hSKZv)uls51ypCk_IL|&M+^5S)I<)*?k!B}GXDOB z2OSk~bn_%%usS*E`FAZAnLXv~@dMj`eb3nw%%Md>p_XvQtaIly!o87osUD$5aRSjY zpy&K(PsFV@jEiLaiN6Bk6>;TA3lP#ssSqOnn>pdYaNPVI>`=E;XHz%z`7p5^Zu#2> zyymenTCa5sd6+V|sWNhDs2JNYTueqIP>=#EQx8^&H*jX;e(ln=%=KwZa_jf4?v~)d zy@f~zCT4}ONMdSP+S$5-c7RX<3ar~|vT->Qj~X|skw}EIbU-8wr@~FYKR~s$9aLu9 zt(Hc}U&jg{^Nt2kI3uCCy-Q2>1??txGN7jJoyS--tKEIeXue=SU*EEpeWHx4EKPtH zgn(_Hn9rsHw8d#}=*2x?RzhQ*6u$a8u50$Y8$yQuJTp;3u*AdCMB+Fi}^s21ts8&R0?!2F<>6xokmen9v#y(pH&_?> zI7(nINxktM@vE|O7RVS>lfcTa78mWdrmCMk=*C)1-N)kvijLdTmF!cbcm%NAawDPf zK2h_s;&W|Q!7`wNBTyEg=lhfjpoHi9$~5hW36w)Yk1XsNDM$)$kd9VQ%Zhyd`s!qV zk@_09<$Tubt5FANxq;Fi`F=4IKV~5y@DnGOek3e6@eVJWi}ndhBjH$9C#3tSj#;Yl z@Fu|?8MM!sjGICTv>a@bY&wEr_*p}|R4Pxg|Bfx@@SWbqBq0JBmNZ87e#AW=nw!KL z8)eJKsnLh7f9KQ4oxNU>B1*^0-#7Tgaft3kj)-@0(XQXRV#0>v_)(BPuCu1d8W&5> zJ;m9Wt1E&(ySwd?)N`@<`9SKN-ZnXDHlSJK`wFKYa|i(6v~}3lJ9wGQQJF$v*OebD z7FZ2_N@j*;4&dD@cnsuFSwujN??*bJBaw%a+gX{S@ekhtqNTMAt|xB3l~TKX1D+uaCILL&p?IBZlcF&>@C)G?LN| zB=@fes;k@EIl0JF8)%@{6?wf~4ST03_m7Ua*vdHRGc~ziYx6(RdiG4Ir{~Y|NWMB` zuMHo0G8Z^HJss~R+xV1-k+5&A>d6{h7#&=n{bmhRd@OAM-C%0Z^Q;ew1jPYFBjyl@ zh4tNC8}#YQPKc#acrQ5^t4{KEof9^Ks9s`Bp=|Jnn zgNv!WU@)V2z}_WymtZYS7;EpIcy$LBH;tb%^3@u_1ZTEKnsO2ygJx#0AAt6k)DM(A z75`8`$2z2WJ(>viDF)l(BEYjwTJx->$JwMFNa>@8Mv%$k0-h09MU&?Q+fLd z4y?i_8TBQ01rF+=!je6yzY1l>YUg3@CCnG0Y`FhXjy_> zavh~Xaaaw)wNE4$4JvwMGhOJAw6TB2sqrJ>>b{OY5PKx`Wc!)F7IaD8dnY|+SN+zT zgf&~65?rt4fBq9B{*VzGaqbhWQh>tvfS+0w@Io0+To5=y?WyFszZBF_%HJ?D#mhNF zDUrJ;1s2ND&wVI9|GYamxU&C4_%Y zIN>4gKl!Q-VP_DNA;jEfp1`E-H9zX@*C6Qr?jC52@iWG-$M|zC!QecPt+L)-X)XTv z>#NbE>5&t9A0ti2R7cG%NQJ!+RsBr7gxkMaIgpYfi+yZJ(j>=@WO&k$gkI3)ZvBG& zNj0xZjA~@e^&{)H#~T})^Jz1D7cV^%00H`y9j z%s$S_yf=+K=OYb`^gkL2K8I}C5&jBioIN$E70S`9tmI;C#`P@WOf$sZ*>{!a5{7q0 zx&&46X>i|y3Y3e_w`2XO&wP^37j%j4`Q%AMcV7!DG9GRkd7s}!tV^`Z6!s#^mOm|O zq2W1W*nq)!#X{S%eqJqaE3`)u?4^Lt!2i(#{N=oBb@)KhhWz9e7H%n~FImArczFq* zmBm{yKD{D%Uh`Hk)l)FFK|;od1%Ok)^;{9!tqITOeF|p?!N9!~cl-mH~6br;| z!*bV^zJoxNEnzcRsiUaX?0eS_Bz=CdcJtyuV*yAuOO`>*+w$l=j_ywCP`2{c+K>W5$^QYDPb2IU+ZUG{My@ z6W@NkLa2f27Qz`xMB_@R?4CE=%1WzpYwTD^Evhr2Y*i+wHI>Ku*RmO5DOkBqx#6W! zw%n(C7|TfV{VHowR43m;E7^Wewnoiwpi{~5`KQa^;P>Tj#z%{lz1G?yX=-Ao>d&6B z?$keHa&QRTkCAJ_b#QP1a&k+H3cKpbotR7C){W6YUvpHJkB?k(1AH7Hpjytx%a`yu zPH~dh6e57)3gCLi3LiN*JFlk=Zvbo;f5=&3Xu#B8<>OyZPDeQ|M|)i7{|@`@HMX8N z`u~}FHaam84B`uUPoSxH%HagCAcS9JZq@+%>TK}?o z`}!{B$AUXFsgLkGxGjsP_myZ@4~5pl(J!^<|fk)3Eip3?4{K;5uVI=QL9{T zJX-FECO_>GPzeV(tc7Bm%T3W7ITuQxS+u3LHT=#8=xV;?b%ujmcI6u0FaphNl1LLE z>XbQ)M`uQZm7n^&xB%*mi*}9++?LbsUI{tIXr_-e@~`bK!SwMM*tP|@yot8GAUTAl(u}qqO48iy zl8{L0V6UNA|42{IcD^N8sRP+!<>pt=l9A&n8YzeDSd;)3N(o6xT%6Nxn~S>_zsoOK z{_M=gKg)#^p&*A8SBo51xStJOI`LKnLJtJ?gjfa@n4lM`Vu}6lk~uX_$H1L##u=58@2DbKK8BI!fz8ID%}-0!P&20KJ|MGBRy|iXyO*&>R6H1 z0u6SM(hay~L@-@G)e&SkLf%HGNr>w}Pm$(XKVWMcIY(P~zva zH5COAl<$XbJnMfTmmudjvo#^QzNhvX~pw>v58_jHby=-mqAEc#xU*YfHaj#dh6KU z$phrXxI=CX(_fRvMRa14O$sZ|S?s&sFEnIWn$N^5&cQ{tmF&l>8oXP!L6I|_Y9+L3 zi7tEr&lVCwrADd)s2Hf6Qk%{e2}l=y5ZR0b>u*+ycgDrL%%X&B&37KMl;JleGE$%$ zNYDfa*<}xjvvsf|HD&Eq=kzO?SptrA!*vgPYcJB8uH5@~`JF^Yl_?i)onF!S&)xR7 zCi?snhhM;^v$#BsCcZhrY-=|~A0r{3-n|Fom^h%Du5HHRRDVYFR8?TINllXRisXW3se}AOgOz8&&kd0IiJ7A4O$hR z*Ukq;lJis%MKB{?1GvVAYzm(RMJnJ+YN0d}7U03+gqUyggy=kOVs!rnSr?9e`|(+Y z)}|NGR!y!Z=N1{(NLU6zr8=cxT&5<^y%@w4W;M!6ex&kcT9zj=>&TB$Xr-qMw8fJD ztpanuUu@zTnJ+#=e(lv=N8U$Ph^+fdB4}E_VpNM=?N@_ETbHYH-yREArWAjWs4#5f z*DF(t%3kf9^!c94yA9(qra?BPQhZWVBZk%;vq6`dj}vPQZ!STA;x^9F^HE2Y+ffP1IsuY50NP?u}~ zcfNNpAin%I?}B&kVoh%OTVZa;Yo2?szXwmFG`P0rn$Ke{{dVUP1@eMCpR8%rGThkY zwwx<3(~MK|;@#4QWnU!-C%{{p(%BC@{${jSj|!#=JuAE>H!VwlZ56?ka|o*^h090! zfbD~*)F3R%4VUpChnYx3ga=YP%A~spXFtu6^ZJc&O5uFT&7(qy&{yJJ%>xf%it+fuKE_{w?pi6-Q1uOh>x{bpf;HufI!R< zg>AIWL(8`Lnl6`>RgFIKN5CI1-lH8zfTNWc(`_!Hh;LDNN8lQMofY)1@?11!tbU4)wO2GdjIFlK_ft0?@| z0__?dsL~_~!Hy-i|2Uvgt0!%q{3gB3gI3J#IfGbK>8E9%O-a$#PKZcOLoj=pR3On( z>Qn*7D(1+iz6j&gzvF>ixo$|Z`E@vqos^udH7$4GfYv({lckM$*o-*@;^dAVHc@a= z%&R6?Y{TE_5*rAK+6M%XiMOmT57>k-?E8~Uxse6_aRgd;QN-Z%w)J&EDt5)F=D?FO zXQr2ak$-)UCnia-q8uSg<}PI|y9EYuV2$#{1m^WZMj$#mvjG)t5y$fv6}`u z5o?;;Ab`A`(gx&MKA&Hd4N&3aV)D&IWPbon=P5wCGU1tB`XyDP_bkipd*xES7iLeX z_0VR&iw^a{=+$D6@VIxrR+`0R zPIzOG`{nJ?0ah_|8C)U{gX*wT<>%&Q@jl+D4JQ5{UycHdjxtvkUJ+^?HN6FS98y3J zwbjnvu|;AM+YWUF8JHVT|5Ehqmc{%LLGLBU-T%EO!@4 zBG%(i+wALB63G-?$;n18%7c>Rds(?Q;9o z5N1dc)44i4%+#w*w$hgaV#;!K%o9-~8edAFdp^p^v!MNyk;5RurJOjd8H0OT6!f|x zH*d9JGV@VKT>JoNH#Yj+!;Qbw^31b-$+;LX@=C<2!J5D5i%$z5&LgZs&cul}C;M#d zBHYJ+?iPJ~0W#<>n>BpZMXR6}6n{wxDDsGi0|DAd>;|{GfF$5XV(<)=+gRH$=Tz|) zGAcYIlZOJDWF_bPlK~3w171L%2#BQs5CkP^?{{)sfnJ85PqR(~+8qc`z4&94=IcLu zab@8H$#5Obz#%|UngJw=P-;UvqP8gp2sIov#T;$!*OL{GTSs>mS(S&K#W@7t3bNM0aj^PC~v;MpIT+@J8H4f*@CJMvJH6!>FWkfsl)X>3{}f3}$(Lm4@Vl<}7Ji2hWDaFWB|Qoih7u-vqWvOt3r zind@Xd(i%0p=?u!x)yi#-*f>(RRPw%Wj^cfN_fVY8@2y3Uj14=V__q(9C>P^EK$lie<^h;(EAKkm0*)mb!z*zbHTIZfEWr3n~UGA`pe z8#ypuxoN%TQ;e0BQXE!jF2gKB1Z7dTtuVIFd=Gi8Vh7kO?FmhiCY$8AkVSSFK%)s=xY<0&umk)W9%6%KT8NM``}A{wKrR7pKYB@&SQ?`#&FuFP2|zmZuCk z?e8atHdW4YT<>!%3xq5Q>^)IEe&T=9IG=dK=RV0hqn|sQs9Sm#`sbP43l&G~LMVZT zMFMHOZA5*v8+ZwCf+BvL-^*kf859;CaYc7?v zR?;f@#6^^!>hn-zC&E&{*9Rd<72HfIcDuP`cRLna>rUj%*df?TxfOT+}_Tr9b8buOzNa!;`Q3*V^V-zeCtXExQ_$jogbybJ6TJBQM&|Hk-W4y~hn!;pbIJ|5 z&lXf{-Y>ivS6Ylp=c_|?u2xcP%xEtjSaQSWZWoI<<;*ZHdWMT=jnMuUecejDQ_VJ7 z@3*;MQrv7zE>-Db=TC~1K(g^B44k2x@a98ta{Y*M8PzN0ql8VZyW_=&d1(J~*;lq| z@W1VxYt8{AdR?sZr`l`~e-JSh8=y=Jtlhds>F)_`Nk1CWz3wd~*mcV3y%A|GzrF`< zFI2sB8APER&}CNs=hO2^$w4bT@~{OphsGNUJ6ZKgehz3TF8hUG{89#1_W=4S>mS2+ z`;gP}QQy1^ZFUO#kD&ud(0uRKmdKlaM;=~4@5z~?U)~tn{T#q=5fPCO&CS6Nmg|I5 z$qxQP0I`nyw-Z1kR2FSP*gXWCbSGo5ijNtWMm+C)2D1myBcaPbKi|>9Fk6la{ z5$rOkb&6~TN!r>yltCZ_JGz%F?T=lME4yr6&av*65&o2hpn?$==HFJHZL499T-+D} zsERI&*z0rNE~MUuhv?`f9`tDuqGgWi_V{n=XKI-r_7@>UCZ)w6y{fbh?U0c8Vi@~0 zouZtuxZ7V^Q}3Y?$MV(S$>uWug%jQ`XIYfC;GT0TvZQ`Fp$r4pVLW*Pq8>Fz{_L=V zjaTWB)Dq5B*JMPwGRPOS3M@2%xs>a&RVKW*i5CLGjW`Jj8IGEU25yBv*m@buw88&; zi^=4RJe}D!7FN#@%Za`UK}fKpU2X9O%W{H=oEC%}rp!!~U5(J2Av%UlrBm*+t>EW#T&V&A>m6w~XY;7gUfR31T@F^cA8yJ)$~ILCMcXrkK@VzN7;5Lh<|i z_{d$A+vGFj%%O6TjxnflU@b;7X}^ZUU&S@r7kbq2xo0&xBLQt+tu|^_z1~E=(cU z7li)S1P|W!Z{)2TS z7Gt*>Es9=%XsJPe?bE>&IN^wV%U?A`8-Js{-T7BM%`(Y<=J(!F$6!55OWcUWdfPK` zEeO96XjP;hK?V!F?xKG*$zkv@vIJ;MXA@4Eqx&2Q^#jf3JFwge>t>Y~Fsl{R<_z&- zCT(WnHxkoZ18CQ`qw{0ohu=~8#`9HgP|uG3-beBV(LH_=P+GE^5bd4eT$8lJzcs8b zk84WOUbg)d;h%+u8+kYJLiwbG9kw=VDpdF;9D2HXeUSXGP}tg>6vJN*I1G==Q6H>T z$xupw>Wq?6oPTe|>k%g{oNV8prXEUxkAIn-&eYS@Wd}JL(PUy})wHynZg}}_>(h0X z($g}z2O9I6WM(VFfQCEeW?NDW*Na4gEMd#PE%WO(xE~&y_z53E-0w2EMID0+{}Jvp zhNn|2E4<*4Q@fyz2alZ1t%{Qzt*NnnFLJ4{hkz`Ij2+ZD9yy~Rwi%*;7+u3Yl5lUK zvrjj7yY@gP{my}Xc6+p5KrA^kWYA2&_{!0^cCbQJBf;f?WAet86o`in)G19$V{i!L z?p4gq+P5CLEYW?(yv@i7rp6K*j=-Z90}y6+za7GErIEo>zcn1QxwYcvxR!(Gg>1}u zT5ro!lX=gjjg6FJW{an@-v3DOlX`>Nm`sbcThw59K3<5HAY>eVO3AyODb^9qWkn5T zrK7!tXb-WflR~}samkAqKvhVTDp>6n4RgT$N`x!`bbZf(Y)L?Rw2f6vF?1FC_m)p9 zE%g0gL1^!hNOpx_`z;D20os{XF>Q6nO!b|D{9~q{@Zn7b^E_JYL<_5!^kAgxg*>G| zUvg($!)_ZNCNE+)o^!v+mp;DH<9O2*XnmM#6+GesU+48XR!z3HvMw(N?mMXdabRL+ z?*`PhXXodp(ldU#2EFgCdQg23FB&7S|7IIcGi$|QqOX(V9m5cv(3b>$s=wppfph3DzGZdmIGI+lHlcL%{0R*J8csZ& z_4NJ-0?O|)`*E4eW_&R=Y(&|8Lz`+9&I#4-H$$S*SjFrQOe4Xi-UlhU9Nt#q4lV*f z=NcyQ((p05qy%Hz;i=6b46}Y457QXZ*uT?|NIIq8EY`UAgTQ9&GDz8%c%@q6!>$k3 zCntI!zY_B0K|ta%NZo!KjK2p81NINz_n(u0)%{*V#<)!i)%2?M`uwF_N8Q=k@4XSt zljVhWu7A3U-IjA%8JdRFEduJkp#$R*G6pVeG!R-3|JyNEmIrL*(*&6J75Iv$0iEr$9JxTn7cK?yLLYZu(bCP#R7kB;iU)gV!gY38WL!)j zSKeE$W)otcVH1jcJjMv)pKz-Qj8s6vtYYgqXUCLA#~s+`x7#ckVJ~72Grc!{i)*N$ z-@@c@LU7P0Yk?^^ue!m|CZ_gkR0!oFjI4j<+3TSmp!Nsr=}|0> zdP~vT5iyw^`vphLE%-v$XnoK(gqCE?<)A5~q0s9;| zy^!Em>^)I-SgNtpXW3`?9^ClF)i#mgo=>iEa4Amx98W+?blZ#`n}trUu|@SKYmPEM zT{im}#-nVJPg`WrZ;k^`Hzp)SrAw*a4@j1}`jC}rSQzkv$r7wa;h%j}(n9HC=)y`T z#{Z8Nfb2G+tw=+K&Ln7oR?BU0Q|5aRtuSIFAN`SWIMV59RH=7$^bTQXUD8IS6iy2z z>C3mM>RpbzrGF^MqTT7qW|0BMJ{NnjQV6Yd;5%k!w0l$4bDb#;u> zaXCA+GZWd8bO1;oSaJZ&)(eFt6&1f?wbgkXsqsdUPaDB#z%O5;* z^Vwry|L9&|>q+4045lS2I+~Sxmvtio>>G{c|_L#oQ zU^ABKoLViREOWdXURhZXQa%dSNB1ThN~#_^?fY$=m@aDn2C0*ZHd*Q!_e47}iZ zM;GSTxjlRa&Z%CdCKwp8SWj23>zxI4e&-NN_~FT0Qe`IIwTDtaGoH*uFVHTOdcIq2 zG&6g5i)%faM3Sz=$&8WH??jYz+EcvbL)fuZJt#etR^PxnMwlOVON?~3V^9zJ`3R!q z9r8z^rb3C1q>!uU0*(~2^MWbxN(Rqo+GQdoAHNGDPgW!}jhXtPYDqQ7mRrE<` zY`eR0UBCjw-_&Q=H_4=@flNTxcO#^6L8vKXoh1xQ{UlbHC&;+`8Clsk2U>2Ofa7LH z+zY`l%;Vx!K&|!BEQs{&TaTdYbJXJNdo4-+kjR}KlCv{K*wH#ZpgVf*uj_uVyJQ=N z?<@sq*W~^87PfPVrosWcR;f>9VeAqQO?1Dyr{dJ-_{{)`se2ds%DKwFtxAA+K{;F5 zmGW{+zmad(0+A?#mpyiJvMERoTJBC5VS(PdEiCu^R_Inz)$!XtwF6DXBMdk1_iozi zz=h1-RW9v$=}(3xx9p94h(h?ew4r$Rr<9$?Zz>uuRkYZS))jKb6igSo9#YyLSx|!> z1gRFETRgq}Kxp@;Lg50%=_8fr~W<+x5XwBb4dc( zHK&iCl@A5`pKX+Lv*~kZi11`3I$U3=-(W8w>j#uPkM;EQzHeQx=%0j15X8ID1C64I zxK+4+E*bg1glDYX@NG-)jF;6XP0m3`c+ z7ILfct;*-#s+8gfu)j3zv=z#*RVQpf(!?y;__A;^j%0^YpN057wB96D!4Z}1Bkn7tTw zD}qj9f&fNlf3X`)PR=@lohj_C9^M1U-1q3qwc~vj=q^KXl#d!muXpQgfIdTyEVG&G zI@@vz$~#%bqMT+Riy2)>x^vTw)h@kaBO6xbN zvx}zTv5teD#Y~wrpKGQSDf`R&XWkdzhex%(rJ4Q9g*Pn*y2=^jbgK*kQ%y^ouQ@$a4nLOBoZfk3X_h#fQ{G{0RkU$|%c$~n z(MbjR@|6(p{rZPCby$ONI_ATMoM5pfkmfW&q%k~zsBEU3xx^#{PNcuWhmYROOlo8t z?rSA`e=b3W*+7=`))+S0$ybu~VaB!I-_p`i({d%W|86;c+fvna(D@;8esUVV{X_7B zvpK>(3|d9AaY5^2^h^e4Mb_WkS|3%eVhDFtW;hxTsPHCh;QY559 z>5}e78bP{|2I;N=rMnx3k`zWky32XVA%p>jINSfZ&WCed^J%{By`N{Td)@bMg$Rl3 zn^?Nkq}eadRwqVXeWd3l%(<@50B^HR^jLL^>%L*&n-kSGS4Qb?b{cb(Z!8U{;KC)t04h z+3R{bm2vlzk2SY(0qNf6?sLB&GaDdJ8?=pYF#vyh1aBSc5%TIzPWDi2Ra2qX=UQS} z(*ljd9?3}Y#IT;uq&#RJ&$M$M#q{PKZhcA7dR%+Y#Oq|UfL=@Nt5+j9c~MlFda-0{ZkfwKL8n z_1fF7Sk}fC>;RWmI}VIfP}Ux8hs2cp??kEZi1ilkaQ1%-^U$&A!J~XGOi9I!%Kk~6HfkF~nPJW0kk<5t`V>6pmrcx7 zg+;R6(7eR^bdQY`f=+@fHmO>z0g1g9M|w(pZlPcE^)_TzeryZY9^``(eH7WBB*##L zsIH~UbmL1*d3T*H0?{DnG>~xH*}PykUJ-of6}Q?~2Ac>@nepg<(mET)r9{|14K9{^ z0!bVOKy8Rtk!}~n4dzZJC$mBMXO^F~UwlwRerC8wfjq3jhcBUE`98O8eaB7~GiSyR z6Qfx!Jv7!^rHoh(&vv3J2d>b>o(V`fY^mg%$56e{H~#dNp+P2bOEGrPdUTh*lf`?g zr;*AVpGhD&aXq)A=WoLR@RgwQvamfPrf!fwRWT{6%{hIdEkON1pUf0nB22hg(nCOr z_V>Ahp=r#Kq1!Fd+?}6(@<-hI_$4yaQzb}$KPB%EEDzs|I)-t(&JiE(nx5jtuT*|@ zG`v2WlXH2L#ih=Y_y%15Ixv^r>@Ck=JVRp*5dzrNxSSOFf_z!Iv-`y9bqKoxzu^Z{ zIcS@~;)wWA1=Cx%j+t`|Q%Y~uMg$|1|H7MZ1h+wkIl5j1N_tTCua_OXyXYPz25f3- z5=I1y6;gR&uLi_$e$;UN>-+cb{Q!?s!aIfsib=a3 zXJ=pj>72GV8w=@!T;fLcF@(PCw??ts)Sr7Ujpt6FvX9UDQ3P}}W>vbEc~28fAG9w_1nR@=Bzcc#dRafkYyl1% z?1aqCK0L|>hDBG&K8=MV7EIrF61bE}cI17{hKH>D+V<)Q9zgc`05*s7DA4YsN&rr_ z6{cuU6$@}ojj{a~=Hw%B6L=PHVT)d8bJBlo5X)N zs7&=5F$##9H<%Y_$_RObPHDp$Wbl*~wvqm1c+vx>>tX-^VDt;w)KvnIMBj8*lmR5Z ztD4MhS9f>Fwzr%PSM3V^ER$Y>L@=cpG*bfF8F)*fa?h4rEc zkbz^4zVl=II)pHcST>iQYIMGt(I=nWdF=TM0@6?;Y2CvfOtx8-`Sb5;;=Sp3Wj9Ec zV;JcR<;BaR%1nPXQ8Grb{pTuL;nkP~vtMHPec3^M zy2>f#|!G+s`q54p&+a?hLL(frEK>W$1G~5cbFysnvwpGKD&z zIXBbPpPLcy5T7$*IjW2`so3bpchFo==gu!4yY;b}t^yh^7rOXUi^nsxX?d;aAPt^Y zc669`)qy5jJ@cX?T_8#Q%95ibqS?)6ql-ns#fXpea>ST-gt}9rn6ncDfxPOo>@-2E z-!98EZXZqYjx_|w;O`0%M#kaPp!^vQ<|VoNupI#No?-uM%0pJG30;o-Kzq?uIlLa* zNSm2(*S#LqK_tj+=2~K6)k({EwDZWkXZ+M+9utD{gu3}eaeWeq@7YBQkY#|MQ=9Sb zo54;!?dG|Dp}Zy{9=4M7f8`SnKook#o|9j)VJi2!P=oJ?dYR*m0ETm0bGNhP45_9> zIHDK!Ao|f7ub&~ByTvUI{VHci+Z+ z{`~7S1@d(@Use)>nB|-Jqqh?7Y~S6T(fUSSuQ!%?PvuzCp$-L18fP)1sS^6~cxtGH zhjV3eDtbHZYIVC;?@zAs5H883Qq(sGLZ=eYtQO{az^ZX1kz=*8*lDQ!krL3&aZ$JQYYJQIj2O zIs3NUY3-6+ev*yc#N%Uau?)6CN{;V%z>p!e9lk&de?4#CLZQco3_fNHG~FMgPPgeQ z^Z;lkeAE_Kq}=sq{mo&nfJB}?hk6_O@!3U(cJ%#;G|CpS;lzJQNeshL_=^CM3W!?$ z`z3rw>4e9dkh8w|g+BXZXNMU`zm&-*}x+KZaI zMUY=5z5akgkR4B&LAx}X!?gt)2i#W2ygV zO~+0wSN&fT&!=-4IftkX=uO3z78_e9p*<+~4#9A)k;c3585_f~g9xgf#qB%ay)E%- z6XhkAkyzTu8Z`SaBz0Y2>S$NPm2-&-&ANGn(eOV%HD`+3mk$`(#J8yYLRHY8m=FGz7*6nMh~PUQ~x;_0!`>KMCCkMH8_ z>=bt)?~(cIpGkJ8wD!0uDrNqY3BJi;)KQ;c6-*}@CmBOwt5+6Q7#^f=5q1g6$^KS4 zmhj*6+ir@iP?F=9)>d}uKwWPC!h^mN@JbMx?sKZ%wKw*;bz(5GWX8>Y7(d4~R?=@XUiZLVZkts>_2i~c zu{@n}a&Ddld}gfoKwg4a3c37585NOf(e~!iUevHs5$#l?>HC_m45$TX!^cZ~slS-9OqUV#b8@nWM4vr%E#6>I!rhwwqnIQO$CqqM`sI_o zI$3Fq-liHrKlsNbxI4MRD=&g-E{mHaid&$EifZaq*HiKt_jzx!UI=QTZ}0D} z9mTbTwNjk#qMFLA7;UeZJ3sydOStrr>Z35t-~tb88i^g102$}oTBYeEHU%A0CQSKn zbag)x<#u9ehf9pay<756wx53})^3av;AfK`h^VLPFEKY?$>E*;l66#Q^s;-EG$S_-;Z8Yd7elCS|7J7&_Fx|`iA%O@f zRORV*YupA+CMzDDh{;iHBMmv{G2z2+ND%sBISMrP!5cJjf-TL`HO4~_MM!#i1YS^P zU%Ka-#*nDMJHPS|iKGgzoqvaNr)d4WVf#v^Hao7Pnf|LX$iukT0#Bb@Vu3VwmiJgp z#BURNbJG*|<3PZraFei#gVBf~WPYUIe9!7O~&v)4zZ%?kVt8 zxOs~qFOPO0NIB=$u2P$}uTQ;@WzuFjuqQU8`t`F`(xeq541~&Ia{n-F$S4pSp7e2{ z8qM1Vr5$xDBIChS`6hmUpuw6 z&6#({T@rO*rH`R6GQCsuIVPyGTH(VulGWGegcp9c`7lx+I0I)gGO<%x!8I}>u3W{H zO7MF=kyfLDMzwI`-Q7#*ZFpAcl8uIc`)Pb4D2Xw5icpS`fnnE5j;{K}$3ks}K+<+B zg%3sNO{D@`JoM2@h_sy(MzS2+ppIdfM6%t4J`U+PJp-%;Z zrw@7Kh7*5v&_JGnVCINn`Ns|WEnFTBlqtbpa(t!s8!zp0#|dOZV2v8L;+b+SPRxT7IWg|4ImX691L`kAE7R*TsPVan@!esyJ(_J)-P0x<(J`Vj2s7Vhc2W1;yOZdg4fb4(FU$*C29|zL9jgf5*xx=*7)epE}Co-4YU-nqSfA zGR1PnkM%==$X(gggs}lSCL(jp^CUe0IosXM!T*bgg~JMYvwg{RYxa?K@1SWNdSdkw zTj8y@5kr3Bd1;=oB8oaDAy-_wS5L$%F&8LF5j|I*5gcCP`7*QA8`(RaU|ZnPx2bVl ztVwcI3569OKbT)!t_BC~jQqF}6IM7A*n?|x7Al>Dt`n{G^7FiLi785}ZPBJ`zK<4D^%*VN|^61+C zJ|b;uy@h$u#F54^6Qy)^8juC`_V%`ISyr9>(^0)1hdLf$v>qKD zeN=6sQKkbpZZ1tAS0?u-n<1H*gdTY!t^D92QlpItUJEt`o z_8kwac7$_D`8QOvQNQ|%AFDRWdcZE8e@8XTR5A<`2SHM-R;I7a7#gm%aUdhZCh@8? ztdwVzM>~&s=VVG@8OnvFk9kTF>yi-B!p2)m$OH zt(>j!DT)h+7vx6AM7E$8panrg8Rof(-N;#8k?oa`#PRO7n@iV$h1iT?vSa(kE!V^| zOk$W_;+EvvuFc8iN3N9KtzD(bwfdVJ?1ww$sD?erd(+7{k`DPlIXZi3#3h1OyOBNj zEx`3V@ZRutt`)KWE;dYI-vobS>?0(r*-Y?`453h7s2q-l`PwMLB<<2a_X_5Nio8D% z(oM$1HAc^V|HYY|tUhBoW%!nuK@(j&3G=!u@1pDAvI|MP6%*z(d+@4kR)mAh)-Aw! z)!9zPoSQ?rVlLaIbp6;zm_SKYRh1Q8$5G@r7phWOU&*R7HSUmc3Xs}TmzN*@&i8q> zP7Sj|lWyM$(Un$vdey2^HL`g>y?KU&Ji&7cbbqF@I(6-T;I-})Ww32avy*R-)z z+=B6f!ltcJo_MxRdBeoxF^Rakx}eErB=D2b0S8HnoITvGSutq`8mz=q<{#V5A%vKE zH%&$no!pzK{((G}gVR)_W1>-+=B!=Bcvxxm`_!P~xMCA|qF;P|Pm0x#c095E1n6cE zjQe-enx$KGk=h%*PobFTHhPhc`7F^f59Xd0@QW_u2E{ikCVdTQ1A2^NayfUn{9ON$ zjgzN}4jbz_P^vm{Lh4Mr#tU=8%BdPBb-yTf=C%~tY31>eQSHQ z-@s=H{wxMQQXYD;XSeF+q+Y+;cMy&wRO^7We}^66Fy;Mxt7M#@SxQnTehi=NX`GH` z{=TD4K?#}qmfR0ES*{e10^F4FYuiAdwVYD+_{YW>ps@3M6tXUi86G0yem5T zX}upZq0_-q*p`E_1+`FQ+lVT$m?3K1o&jdY{$3rU)*Hl&NYrlDuM5s?o*5RiqM}eUBoPN;-?}TIs~8n+rKze|+kr$_YV1??Cbw=dj^4xQ-h7?z{5T7JiEKH#3e>O+GcG-s0uKwgOz+{esBm%~+)E8 z7!+xtzE@69eBq2U+o`g-*D5qCC$T>~$a%lqk5f_q9odN-^t*T2(8sJME2XG-6uOK%H-Z)qADB?)DL}ftDxzwe38<}Jk@9}@ z=rm&w-PpKB^F|1W94fmSZdGo*?TxCF z`okh-`Zf~wz&>nTKc;3~#4^7o&+!5G)zr~9Jd+nX>rQO3R}FVpBQJ=u-$HAqV9r## zTvebZRFn6UY8;Izav6S7{y;GGNSf<}gxGxFmzBAOl~-pHZon3SiV`KEx819`i=}jbDm6V_X~yLYW$Rt zrG+9*5u`(y@*^vUBSnxP9g2I2R5)uu2touF5D?p+)m)|Q>v=+TOx0VTkg zPi{`!sx_wq#*R%G?369cx&zWd?$uU*jd)2ae0V~IvKEh;6C&rR6t5BBmZqUsHDAiG zdhi5+S_G0}4bcBZa&u_4E0*aGOU0}-WhQ^ell5R=5hZ+@94kqj(L{OLfK?ARK&Hn8 zWKiCJq{CBCPPmt0aebrmD%>Spk(#s`q?B~x>E}KY2EKp)CvKKo`NyOZe|pYp1=2(_Y2?^fi9^$E*oiXY--xOt1{2!dilT0IKSO2A=NSsmO5cupRX3&GmYy0 zE}alhhBv#%%4ogC0eS%>=tD2|(y=)x+K^`QEToWA|DX)EVni8|JXEDLqlfYJfMe_lcMJh8kW&Td%qS zd3EjZN_{+|nnx{sh$=NVMdFBOd$B~YQIg6K=uK67(>;FabEbpfDj1ZC1ra_GF&FPK z_~d9)d>~+3yPAhSE>co$b4K0zRXmEhNcJd|dfgC~QyDDucIB_F5)30o%A{^^%%N4Q zgiXzhs{Rp(3^*wg0N};PtpQYe|2<-Wu})1z)yevR z{97SC4FGL%>HW4)fv{!kpSB8cNZ#gc;pRV(XKQQg*#@FN8ww=xUcG*8zU`Fh!`}X{ zT+6S=mEk_?EC7!M%=OZ+zTZ4;t~8kzQ?`#lF*Y%2KqkD-pS}QUaOC|2_#R^0;s{b3 z=d+2bG|LXp$%ua5#D_#3iquH!{H*ZuPBKz(z8az*b7C(iD#XB=_~+U>Og`FXii)Z` z28T>#iQu>yTtSjX9r{EKBI}FCWTWKQGQDtCwr~H!zn3nTzNe|U#%npC&1yVJpiEpt ziv9~PLl>)kYccOKkdLT-H?6ns+IeEiwgY;Qk-QHj#=SeHOp28TZRa^MU5)Fq~Q6{7rNQ%{GXE ziq*7XV~&IC?E-D;^n0S0)c^T^T~n)eocvTvV#Fhs!Hv&Vl&o|%W%%}eE@|cgtB~|g z4@(!l;*axSVq}nVo9oPG%|~u6v9~EptFh2SOqad|bhxs_E}5{f^gH?CsRGfD5gonz zFW2IIdW0AkSJJ2ng*ln+1*rkN{^rA}@9p?IDKvnYM8nsA!wf_b0A!Gj#NB`2{slSx zOaap3KPt(f9eFzet>{3{=7Zby$?BMcG}34WEDdD<;2VOXqT>dDOYGTt$E=&XYUXAv ziFD?5vWG`Y2^hD!zMd_AdUj#K z$E=^;;(mwn_GI(X?Cm?s*yk4k#|air>fDjkg}nWa{a($szS2|JKRp&(CU5HX+Or)#(Ix&)#gHM4yE{Y&?4m zs6d#MS$Ic^M0Zl-k&&GtZr}mTVVP92G*-viF9Zz(@97C`WH0-q*tuMF#In#*apl;2 z^EiaLNsL~_5pn#d{fAwQK{=HCb{vFseuy00#wqcbKV4L{0t;uV`;badf6QBXpqD0) zh!3Wy9=D}f?!J<~IM9T|0MT7AO#fd?esLiP=rJApfp#y{QI&#&AHGjl1L(zT_$HLH z{|wXK!GW5F<{uyj1t5!`{YnnWNDe->5eb4?0Ld6zC#QJ1l7Zj9f9HY5HGvee*MC2m z0A^8$!0tH=n7m2=v3h`9Z2hktB^H0u-9xKa&1`)z#-LHSJU4~yZ&EHP*gY&`Q*(m_K@x;P ze7e6PpnxHFP;4q*GF4fiUa~>V!4Px!uC#pRaNJqw!r1TQ z;4J3cf_XbGjsxpVMHM%HO#IgGnLh&-{=~c{pSkmr46n1nY}HfvS#afSKs*xg@ttl~ z4&tbiwn53(R4~%1U}4)_ZrmzXi~Rkld(KV4Yu2L6TIic42Pbx>*K@FG3N%p}xb2y4Kar z)8n5#vs}-3&o9H;2E?j%GA3+4I5~AY_NX+x!(EvEfHw~C`0VVw8?K}Pi^%;S_eZu+ zWQT>7x3K*9VRb=y&W*1yR!-|MXL>v8(7-7V7Qu6Y;)P_?`0O|xx<(n<@~ElD?u;a? zMwDF%EDVDOm^o-X^8seWEc8ohd`ZJ*U=M`#m~69UvxGEZ)Qv2~nhy&vbb=tbsTw&+ zk`7iLvQ~YQ7%@9;s2G3y>o)K%n<=BhS${0qUKpr%Dow#`iSd&ZxZIfmj^AY{<$cJc zlr8SEY3kHe$QOT5j`E3W5)4zi9;8Qy6{KhU?JoGFpk_@2!x@uFIHXS>?*4*;afUQr z3qcx*G=i(*;iKr5ztTLzIb~~>ypLqXd*S_XW}&T z_uw!QnldJmI$T_i#v~=YSp!7v_+tWX6;|^4xyKbp_;Bk}y-i)m8bp3C*P)y^G^$eh|2*7y${9sS=8$KebtNw_(Sk^X@#b%qMCKOR-hrGswxqvXQh zF$8~_K0iEkq7V3w2{_C`hY|I_8}d)8Tt8QrNUpA~P6!3D_T=yRzY-SKHZ$w1^2uE2 z$R9-QqA*%t#H$7NJcOQ8h7Q6XPoKX3{gkvNTbX52%r&)g_eg@JgMo(?>lw=hdgbz6 z<^wT$X$(JqJ>6y?PD2DMh-YHjxZ2PGmoZ~B%o*E?N>u$=D?4@_vAY4b32{X=yZx~C zIb13ex2oJCC`%Fa%AuD~&k*I~TBvl<&C}*h=XZAP<=NU~BpKF>#dRPqtrN_cq%v*u zgvr+K{38!(&zTLCD(lav_Qy`~G{J7+F>A;>%__30)kmQpJj^I4sgbc>Who8ix&adqfj&g@-G4o$+P zT`Rl0RKsY!T=1G^dZ9

R7=B4KVT>20xs!_q9TFwr4hqmf*OSmbofMQrbl(bB@ZG zaigVNK^9qiQ9Fq(g6v#u>CYm~SVXp0h}xtpmv{9GJo>%7jY!N{Rvs$KZ4>Yb$@aX! zsf}(IV~Coa>!dANBLp`+M6yadPOE6|iQ_%jvK6Lx@MB_Wd_|fNCO>xYDHhH*#oKOO zWbc3O->o3-$xphZlRkJ(A-@_vKJU!GyF+O4K1sr63U^O?;D7X6r!!Ey3I%%su1@GA z01vsj2KEvxFTQ*CPPt6y@70yrg)xxV2XgZWH)9K~lZF4y6n%qKE9NMb=k8wQOzj(* znXzW7{QCJ*#?|%Y<*iMBuF`);=*?4NJq_;JJ&Op)Q?S&{ARzzJsmM4j zCr^|Z3g)6I&KvTd@nFeSH<~@H8`_tIp<+~#>XDk`^W7X_LN*9MwiJzYdJ~ZgBm?_I zsGhpfdP8VboEtb{Q|V|eR0P#i zG(NvGXsk_GThybUQy^N1xtD_BiY;+1ko?@ooISpLp{zsv<%tgU)HCFP7}N^AUBeY+ ze}<(OA*9ji*m1k|g@iVmwrP&1tiIH$Aa;riwsN>g3ENX1qS9>czFIQ=MbJf7me-`i zm-$zDqLSQ1nwdMHaZT1k{!{OKGWc&3i(Md%z*b1e`W2HJ*->y2xnE^QUHo|YgaV)P zz+yKswZ+H-tK4};Wzx{(p|=mAv%+?L_DT(+miT?yH4^jo&bg;!zs+= zonWoEZ%G!?Bdc>qtEZ#Pq$qa;*HvxB_QbwIh<6{&IWsstv?-MGDLs|o%u-LE;{>km zgr^iNi^1LoZR4zAkQKI9*9spq*9}i2%aEp=m^JLwup_a~Q z*o=>KNXps9l8R`obG9)_)yiPD@%yB+mT?j`H?Hsg8C#!USqZ2a3K($e!_9%}NVHfg%@j3z=K7^{KhUiqWpzh-qW7>gjurB_+$Qn(p% zVQO|j;q$i$h`Z=YgU-=zyYWU`D>LJq4h6+l$xj6r{p+&N5f1Qcq1%?qe#e76V7GrBdLA~HK0Yk;)yg6GW5!U?@ zCPX71N3bm3Cmcs24@Z$d_-*^0^IiUX`Ll$PxVz`=oM#+oc(?ZkRn>WW0O)H^zfD91 z%ht+jyWCFZA7No`d3mDYVWq+Byx=YXnF#ajQZ+X*GU~bsD05Bvr=PmMZ?!cdUSjyV zdNuY(Uv<}Q&s^DP{RnqFEnH6u47&tBNj&}83`+X`Jq^snn7MXf&K!|anhV4%>Io{kfyMXnO(I@sjg-TBq-sy%e?yu~?Q;Hz1>L2zX{6_jSF$tNR?{O_J_+qpfraB>6%qseSa4-ez z!fk0QFTS!sUQ~Jf-foyUJHZ@U{JH__i`q(4`r`-eH~I}zU^VUuA}D{Lu2ygcOM|;c z&dDf>A9R#z76o!OC&d+qZAorZzyOT4&PX z*2$OYgEanUT6oiM*J1=v`+?EuD-jV3-NA0N6Ah5eYp2G_>pJ(~gw2~Tn}2@fDo2nq zCgYsgQsBznaADz;RzcGF4{Ol-)kSSYtluOgP;KQ8h7It77^^@#VsZ~}s}1mYW~?oB zLqK&@|B0#`pS`w5X@1TzZ9y;HD)|#P!y+M)>Y8PAUfnOQ_b@oyr7|qTHlWOs)w2l={yg+GP%K`sZmB++ zZp5QLHIPUo5ik#Hv*Daxpi^INzPp!9d#S&iC`dOErXZ`73Gu}r)Kf;+B*N2FkHjq# zB!Bi8dYeJZta4)<#IwV#lxIX;ZV!fZjd-n`?mlNBD@hyu#LT)#yxikvoo(u@BKw=H zy}vE62;o29ZOrH=hPQ^V@K&kVWOh7w!{B3x!q&$2gH&9w{#l^Q@Ipb5%m}yR<&nA- zn45SKaVxrKh3<^IFG%GwTI5Aej59;x%yh8KED%E-s$TjOa_q&5?dKo#p^!~Tt|hX$ za0ExgA3PO#?;(9Hf!zcl<(jKuilm%WaMaVukXa^@{A|z*XFi8L6p_694{HN#1sWlc@sNxH?iXM}|Bd9!c-6`U~u%Tm>RvoBDD z0nlJ>epg5-vZZfFQJQa2hHO*v_7_26Vjf#4|KORg*RoZ5g8!$}({1i0lr(=`r6w9E zBoE7&vIE#o+}zxN$(iQ0A?@?$JK3SP78l1YeDAsDtr?Yca#n6N(;UhoUdVmOXMkfD z0pt2slb(3gI$A6aAu7ft*jNm|$6t`u2mR#mcXouOdo7gc#&c9>OUbBYnNn9baP-In zQ)#U6y-&g6OLX*CPwZ2&EKFPjEX6OQ%4(Cl^7*}wh9WAI88%JALJ5O5HaBBX2Whd^ zt}SzmkOD+32=DVxrH5YODZS*Bie;}{AA!3qTV;+I@=mxN`NR;$cN3VIo0ibwlSFwh zrPHOu|1pxoSDUdo@#me|J7V;(ou|J@(?sIE6A}uZaVwfF&QN#CZN{P@wLf?pe85*s zy01U2aI5o@Vr0C`q#!jQLTGjeR#Tu^@B)+aWn{@#DWW1u%TZjF>Y2|Cl69ZUo9*1} z?=7)_bfIX+=akTCKZxE+h4;MdS> zvYAspl^JKBYyA$`yZCm;%+vZR}8peLJI&6*CT)q%31I6otvE<`X|Ia0zB0`z?V)+S>^{Y zXP(l^%2@y5RsX+}rEb%%YaJD*_mMVb5t!pFv^O^ou}j}7_n$>Un6uwkWrg`9aYUuk z)`#g!YKzatqh_FjlqQzyu(;7Vtu3zS3>kkZoXPl*lH~b=OeK`UUUS06p8*vJ(F%DM z^CEg=x;FuOG$%;6a?L4=KbsVLj|{b!GA>PU0w-a>SRRAr3l$38K-gR?bnLy<%xgIO z>2!=8SLfkLHsiE_>F@v;0Y68VR@P}CtM-&?*o|BjdNkQv4*5Z7Q{>VA=zvjqeE#_4 zEdBbA4YmBJ^J_vKFiC|&6eCAM{x|PbHWmX#!R#^BH|=Z`I9{DSLA1jPY--_Yow#0x zjXGa2o$SyC7_LL{6RQi9ph4VCkIjaTTB(d*RP%015ssAFY_E=_xeZfTM3CCxCq0*! zFKuYQBtCJxU?C)(PRGDhh|aVs?&O%-fTIoOJDo{Mz3ZG_L>! zbWY!xCf)>-=Wqc2PRt1cP0PWD6zK$ihR`n%w)&K{A5db9OJzA`F{88!OEoYnrllZG zhSPcr223faK;qip@UK`(^O7?26pJ>RT|Zk>Tg`x5Fr?MU^Ev);K5o@6HTYYD&ES~X zfEO8_#kab;$1IgIbKKvXRVPmO$@v0F0IVFHd zI_-Pr@&zBL-rqHZCZ3;TO6RfC*0fFaPa0?Dk!Vl;jt^E=6*xpe{ZJziwh?z?oh|UQ zInyKZbZ}r1N4BvK8=d0C278rBo3%_CHu`f|oM574q^To4I$d4ltPVOBqb7-ywh`R@ znKNB?x;eS3f@4mzj2TAka`d^#o+hJU5GwM00NITrXe(Bt4x~VdHL!1$G8*QYm4bP4 zEj2Dumdt3u{-D0MS9ICMdE-@e5mJB>LWY0cY#7a@dM1D|ZW3H1Xvn5Agoej6DW$K> z$3A$4SHd$dlkrmW`wHE?>r&p5W=4x};N~7Xvt|MT=-7e3G53i<7^w zSK+8CSH3X_Stq?o&-Hx-HIquH4iyog`{|I@Tm>?0&!$OWTI$54OUM3EvvssXReWxM zJKq||-hkkjFLh)I^CFp+eYvZd z{m$;~81T%MJNQTpLr9K$$Yp-eu?@rSH--m}>FMd9Ep&!0V2Js%W{ z21zYVDlh_m0|=%7Tl*=V(n3O`izkZa#9Gx_q)P1t6S1A>$%R9o0LnvYMu0gxvWbTRk<%BB=ULPWo^ z^b;*s{mhN`Xwb2-7j?vwj%vy|rr-V~FJEMXrR+~a|IPl0aEV*>TFU{|V!zBSuF9m4 z+{!0|x0O-?nv*!nbv2Q3gtbJxhbzk=c^qJ;>&2tSFpv3$)u_4zW1jK!ICJc*CatiF zt7^-#1(-3kjU2sNwD;1kZ_?90x$G-lb%A3s9P!R zv=^a~AJLRD8a->oL7(;nCf2!z3fu_WX}EGXBq;WNTW^)foMEiQrp1$k)a+slf7ym& z{vB~9ik)oMKTrX*^H5D>&b5t5Q_S^0=wmPd1og3%N#zJFJOtZ z4>coLs|j(>rBGSnSukylM}{XScZo2hmm2kBxADbZ0WqM4hz5P z1=k3I1&M!iS(2w(ggX;5J#ls1u*&rtot_)NnLJa9FhuPURAesbKpvSqJP1ycKFRG% zW8;K}+cW3*4ZaZ z6>*k|wzsAE{*tKz>S<8LcK8*Npyf4^_47Mw4V6YYClwe@b07q|p29#1dwaA+o2q@n zPWCH9oBXMKJy|(A0K4S14=u9Cc6I@nivrtTOL}_pReC)0!QcklST0xj|Lgs5?hs=9 z`QT=`4iT#`Um~O+ z)LQ141FLb5?kBJ?u?7pt)4sa2)1WH&mld)|ghksTZ?iHk#{G zSg?DJPW+`7vvZQ7jwmdkFF9t^JSsec0Ng;g0nuI%Pf}g3pk6&+gwTY6nIvj~eboO5YQMkb*AA2zUvZ4F6(xg?WnX#5i zI>S5tO5L3cFoR;6g?K`anqP65In#GbhmErgAC#&6aWrF1SKGg({3XN$+2f{{1WA zBVQe{xboTrYtN?>h&~XS*I+PawjZp}G+}Ua*5LpA{aZm@ecHMA=)%>}F^(|gOoW;~ z3o%TBmBM0W{Cy?Nw(lZ+GRgcRwB}yX_P`#*1Cgr~dTBLq?TB^;i!%=8FFprp*EgAa z`cj}ra-BFb-JrLO@HYF*DT>muPnzkvl@&UtW~HYMarKc5q?3AgKz~ty{?f5cwq09W zo{$eAi(^qcY7tk8iIJO8;q99HB_VJvkBj6-uy<2K8qK?@_9ABuI??U(&?QU zb&dn>XIZ-m+xFZapFTWN;9NCGF>BE>tu6Qx8=>XOtFXwH9$?D-XVn8;CDLBWViRRl z9t*^M==6gr*2yi={OrOg3gULDc<2ANpfNxAix;QPoVK|JsNrrFw#Z>H5qMNemH7tQ&VKA|$(1Ib;AWh2i?Kj~d^loj7v+ zwVe5tgls_;O?L!A<^vAxB5)WRrm$yU>TUH%StR>QKa!xAGv{(|{hnz%Wzt4N4Hc`Y z2{A7|>4hTu9zmrye}lnC=FFswG46JRK%k^inB`Hv2davT?=!W*Cz;=ythn4x4>t(M z+57Z}($1C~5K8lKC-_NXZ+9-~&Iknb&>StY#8sK(zh@wS8irv;rU}IG3Yjwv_*jaC zL7Cm82d!P*rZNQb*eq@Gv2i59FPF$v%TYTIr+fr;IjOFr=l{@jmQhi?{nsZ3q@)|9 z8>B@L7`nT=yQO;wk?!sRq#NlD>6VrlkZ_a^$swNccmJOkyclL3)?&?>bA4m)&*m}b za!1X)W|P5$$vu_&}}iZ(a1jD=ucZ30jaxsh0Jw%c4@s7RGI#W`C| z&31)0o6GOB^X2snKO$et?gd}7&!uJ#|5N4)m;{Pi`?>jar`EbQ>08^_*huYBP*XE! zq+;2L-h3|DoH?QIAvi?aty4?^^zFaUl6>>>qWp=wa!An;aF_9shb1k2%R-Io%Zp202&ub6k7WbWo8k=k5 zckxBE)Q4IiB4_cfkyl>1FIggZ5@TxWoPMdD#6s1Uadu((2i`JdI}v`X^vhHiRqPx<6EuCTH_K8x83zrTe~mAd{&@xqp`bH_Q8s^ z=F70A5pZ#MCJ`ONhH7Q6!l6+i$6FxN{XI*zAzkz@lQpPvI8p%9W%zg0G3VV)@^Gn= zc!xZ2AURCgo1s2%a_o_qMx|3^Xmc~|c%%I)6NNgj%4CMy-NGU)Gc$8zt4uKv`M=&R zuY~ba;wn3`N3-VTdnw&p$CJXqQ`(H&GWBHGM{3m4jhF!|s*TP!x2sChE8-Ah_F9xT z6UP$o@S@Wndln-Uwink>dswk56+#ytQ=N-~lwdc3{J zYj&>egL#^vx#<+dt5QGOna-vV+6rwrsu7eKf>6v;ck8aMq%XJstma&U>hUjEA7B@$ zw>m?J58VQ1tEab~n9@vg;eZ)wGTp(slZzv&YaY-d^efpNXVsF%2VwYLq3yk-|LVem z=16VRg1~>K7?-YcvCa0Qq`R3eIZfU71*&Gcy6=VBC|ukEH#S(ZplBtn1Myg!U5)0& zuQm`jKLwMW4T_876o)R$3Nky9XfuJ>2vUa<5fVj^Wn-|WJpo~V0jNth_#Gr0tuuIx z99`vpth*9_Ef5o?mxs-8g(&H0suc6JK82;CV*RcBTTYG{&)|!JiWSux(n_lDKL9=H z0MmPO_;Y$$_C(7y7MP(>TLE%(u{Y^~;0m-k%nLpSy&7kE70Y7mHY_z1?IH zJmBFayg9m>RY(UOF*ME9%1{d} z)@(u7hdVJyZ>)++DUd5}&1vk>{j#~)wZyalaPMd)LyBn`-KD+yR^RCAL9ui47Cq@OQ)^!KQs#`@A`E-UTGxienvr%YZ_oZ2<}O$ zTwQ%&wX)G?r27~|jvQ(f6F-dsUnLK4ES%$o10a$gSrhSiC*D9Rgu<8dMSyB(IOE__ z983TM;->tatNjd1r3#yY!;E#brbx!8cUA*Fg*(LQG3Jg$MtLY;t^2HWbPuofvHryd z_CIFsge~7fLe=%s0{xJ0gmP5!nSbnS0o~ZT*ZO3mWIfHLG=a8&m+doybqaUhE#5do#B!{fqpJ+{i z&;9p8FDqt&?5N|Yy7Fuwn$^5yPX93hxrKQ?b}{z5 zvE%)gx2d5(o@d0GRy5VI5wt_!>lDEHtrVl|dgKdhHM6Mm|H4brEN#vd?mY+-JiNu_ zCLHu9#q>kzLFr2y%Zsj^dbnaFFFJ~=;Yp}7Lm|e{#jHITlP@vY(Gq^R*8j*Khk`h% z$YRwJUWXiQ?#TXgzi!V35CTWGg}lNuCq!d4z5iT_=FvfUCLHtiUGVH-qd3Eep_W93 zmLhqVc#!K~gO!Jjp$uvI;cI(FWlF{+?o+7iRa3RL22%w-Q6+6Wug5YmGLY~&&{TW-87aamH00Qr=fj85hjkPnscx1sf_Mz!MRR>!%GG!bV;uV@CIh_s_ z&t(1O;mvH}=3CP8Bxekh>52DO@ zdNG-bm*nW!5})R>5`76KH3-(oUC)7;F*KUCe%+6S24cV&!!(fujj#>Q{xOFTm9RV{ zWE<+NphtSrJ0NtA*v>voot0o!is7-PpQ-bJ_$-8kczC9gDpl(Bh#e$XpeVL8^?Qd1Sr-mS$?ISR-))?vN$iNka@7}#zeYEl9 z8x`Q@7I$*W^@Ju`nk`f`azRCIK0`hSBffBFKKZe?5p*Qcq)C!OR4jI6XE*5-=d6l$ zbg&u|tU@j4L)0wI-^!fJr-llvr6UocBXuG)BgHwcbg4Gr4|}*9mwJ`q6ZoeoTOcsV zrRRzqujXIyP%`-FIQ5>5@u+K_JavNAw{k~(dJeQSKO^S3J>;`h`r{!Bb@z%P`L1UG zlfuRK#tUqWgfgyH9T8aooCQJ+jxpG8EeM6H_Q_fjwseCOVUVVS##U1 zTxq{`08J0V?w1oxShaAf#=zVM+%XDuE3)vT15W82FD?RInB(D&Bp$M>Ygo_gKxzzN zL-4c6EaOA_8>7Gm+^`zO0*~QbjT>Cuf(s2Buiwcb1XTIIp#>_fZ*kqXwp$JUraFrf(JXCaMB%)l+Qs-2O%Ha{{CXPI)m=@g5qW6bDh-N``8vi6dR1k>_-n`> z#iF*3j!SIlPoGwgPGi==>X!czCGzUGP7(GEbFICzva(pSrbukO-S(B7zOb3W(~3Db zbyFt1v{>vbdZ{*=Q0a~9%|oR{LOD>oPFkV0p_$eG0mhp+y zT=27vkyo9?u~Pr~_J-;)P|vDJ6*UrN1(g9QKF51(`PMKT(4UsPw+Xev^`(zPL7gnu zwh*rLh3V&VC%>2cDE`1b|IAT*x)g_Ko>5vkedw8uGmfJC*%1#}`kOwkCq~VCFs*nu z+FW%-7J0hgyJphpDGPIlwK2FGZ)&55It`MQZ>F}RMM)NB&WbXFOJh>Vk}8quIjuGD{$lbNEd1Bs5hc)B5aDm9K`PovdA++qAP6K9}-UZ#OdD^5~s^4-Rs+ z9sa5U-hs+4`h=Q{Q(a_hS*Rw8)Aj1Z)Ab&xAilpXibjAPb3P2kt_C7juq~TD)T?-x z&T>~dB!0%GakzXM98mc29m%Q`nS#Pab&G1X<&`wCj6qD)fHe{Sz`|%H!J!xKmkB4K1Agai zLmf^!ytWJ4Y$z75{`MkvvUkjs-t0rmRiMqL#(BH#;0lqg(k@ZQt#}0&ey#u*C|)ZH zNQry4V#${QY8C|6|LHyc&aRANI-dS)1Nt+sr{?OX>;%>`0+JqX!@5R64YPA zeB@Vs-uG)Bz&<{G?xnrGl%_oeF^CF)@tuAV@1d20e2?Ay+4lozoH>Wb4I5%DBhTA` zx0n90EM$rnP9KSpr|BBsKTvYuiv!63Z_S<9PoQl9U}AI$gq#o6#|j62!4Z{1_!4W| zN&s~>H6>W{$<2MTW|{~n{Tlz(K1cD#GVcQIht!`XNRdof>FoMSR!-7Ch)@#o8(0Ef z+VAh}VwB4_rx(}OCkP-+>!E@_S0V5QjlP4E?b>N%<(E>#r2$3LeQ`gnDCCw>S2Fk3 zsSVa)gIXnYc76*)5y5!m5>J%;+5ffYQ6x$9X3pN;9-g(X#g@U6p-31vGKI=G5xQ>} zo+|9H=S=V7?%wpz_5S+*I&+iLq=%z7p~i8wr$^s?j@RJP(W1~fC1PzxLbq>v!uiZY1HL5}=VZiW?EnrI3g>!rHjMNpL;5Z30v~U`N?XO?4eQs8=mmEFA%6V9@A3UW3F{%$ z#~yO<9{it|-=m^e{4*S)NT3Rt+#&4c+KV_`#0K9gfRXX;4<3Wpp)?0q5VdSE)qoe7 zL*jOsN!Mk4CWSsKj5fm1YD)F|EP?R%p5f;OVcZ8I2f=!%r3vt5n~G9crZn3uK0Mn1 zRKK*&3y2BMMk~p9J$dIzcGa|%5G=6&sbmJzKVJFH+wpNzpae^)hdU$2VbKD|mhvHd z?mJm-UJVx;ak(Zd9B1*z>tT1>2L=AAGu&?_V=2Oa1!tgF$42>jbZerP>T~G+6MugE z-<&=7xv1Y1ar}Dx5c2y;fVXXZ-4GRZ9j@x3=HV_`DCP-QYKljUh1hH#g0-jwdCkItL~y#+z723FI&IPz~97c_U&YR&X}+WV4)3`jvsb| zdZevQ_W4a1Hn(*g8H?D)PEudY5*IK4wvVP)5q~z&P)o>Tj=vWrDaD#l#WIa+8$N+ztCU$5D{-4^8H9eVZUCB9OTt#TM4gAcmhz%{K!EWdXWaFVM>Q` za%Mf0b+X|d=!&jA1f?WT=@@M2hJd=tAKx23-cPK`Lf8$Cue#;Ox7r@`54OK7RH}09 zkX>E)5)8t^#TqibS8*E@da&=mao`pTCPZW@dhTwb^smN(VFZw59%|#^A$uqdmy$e|!l0UVf zEv>tAnj0Xn9*qwMk)_SPx!LBj-0>I+%pOIEEH#gM>aC{EQo7acf2NN}8pR8z$sY{; z9Q1bB|3|$c-v8GEBg7K;cjT^&b4J)N@}{2_u`txeAGMd02m{fSb6q8;{bKcJTclkSujsly;V zi5@$8sS*&58U4}ejEq50(CAU<&9Hc3{@QkYn4r7(>_gzqUC^&!PPi8cx4mp+jQ!q< zjM&F2tc+Lki0}QRc;#mGSW*(xtl5Pfoi~?|J-cu@FMY~fnoZDp@%G`It(eA#rQNxT z2t+HCfL$-^CT|U<@W?IT8FM33`&|Kq1n)d;-=)(ht4Hi4!f?}~|CuD0$~3$xR=(slju zoBNl+h1^umLw$c`NZriqFz@5RSCpuz;PbsPbaRH7ADE;pT3eu$wU|2EQf}}N!t52O zP9faBFB<bFKa=J4u2@Ng~}3Nej4AY8G9uL#MnFD{T@;h3Un>_m~tRb<17u<&A(G zBh3AgV{y2mDYS-+-t9e1FgM7w45=*|QJJhQ3{fZy6pf#*;~eFixf5Rd@Nza9`iB8k z*NG?e!>?c$MnB%WJBFyBX&6nar&wldillmPKSb)Ic)DoE#`c%4kc^y|Ji6;jGBL>B zl@^m?bQ@k|rVc3$cqp_o$fg<%fkO=~0}Wl7nP`bHnFHpgAc{Hd3J-h;eWs&jH9DA+ z%*R$(7Pt$v>pj~|=7(J*uH*fii2UnD>g~PTe1>P!O01ees3V%uRp2AJrka**{S^2o zfNA`K=3HxlbYwS#IZGHGa}S)9%@d^k_q5n$(glM1S&U1{rzZ|&Q{RjxrQtBOL3aejuC36VM)O`Mp5F`r4 z%agRj;QC^Yd9BA!U!8or z4=ucf^3?b4i7rfpcMtjNz=YN@P}KU7yqFa6a&mTFG$ zl*D_6s#{So^RlM)Qr&beljil#rPaPBWfW-&zu0|dF4xau%rGpeaiW0Efh+p^!FfNl zKjf=cz>`y!;4Y>kj{_se_eTClvasI1w;UYst-DuFGke`aeeZuyg7g#J7d%t0nmom_ z%KG|pxR)doLgMUPrunB;tw)i9bWO7OT#Q?tora_0$^c27m}kD0M)G>wCghCc=dXe> zgouIEQZ5_|gG}b9TM(9cBHlYFlIEoVu6qz3auw336=_k(V8|5I^2fGO6bjY4g0BA> zasMw9(vL^CPSx>8oG<;#N0)#mlSlL_V^$iCx}K#0fJY5Fz86tbTp#8X&8Cc>m`FIH zM}nC|+W6^PP!iP;t_ROWh{DhfY|{((F|o(&%yfvJG0p~`saNe%(|~EIdYvcvo~4+Q z5qliS(fC1%1tX&=m7f*+EsReWJ(U-*xJIt!uf~V^RSqp}%8*XW1Al4={w4J_H^0>h zY0DYz@v+L`L9x^V1z!ZEAB2y`vuJ5RS6zmYNa0RYx-@!sK+3g_E90JMRr()3UX@z< z1iKU#I}ZE-6G{}kCE=!bTs(;GI_poOtdiFX3+=C^JF_Xr$DZT{GsCeaIBWRpj;-o$ zk9;jCaM?_%zpdzq5l2iesZ*u_L+VM;+;Bykn}Ko|=c&6GGDBHkmK%D-(?Nee@CqI? z`7v05>VMV5?Tk*tIt!#TvjB(92su&wpOi;jE!rp;l7Y>|0@#g+nWJL9$&lQJhX<^k7xboURCRHU^0Z z+X7+HRQFUW3tBn7e_=pfIo~$o9=Jej!+fc{HQu2({ncga);9MvjA59(TCmj{EH@deTmS*v>#FooFgdr4kUPDf$wNC z)M?(upP2i?I;^w(hRo6|EZ8k7eZ$nx94|tsM^~~H{iJ<>(f}jPf@(SuwP9E#^U~&r zm`nPM1%-e%$XFgNuh1m_@g+}8cQIB{1PN*k$EWn+UrpZi%7*Y2!ws8;?$WO(Ma7HB zGbzLeg|jox7n5_InY)*E3&gOcqZZYDH#!4T6x!Fdj7efrxSKjd%PP3@jGtlD)yuWSKLqA@>OlN5 z#}B<8IwvkUS&v^^+D3GT5@lwosk0atH8do_^%ww%TiNBxb(=?LkU?P-yep{rKN?!z z*R6Pt!3Upr23s35LO*`@!fhhn-Y|V*k+2No-cYi!f|1aw$#8P?=K;au(WNL9v~HfF zE(+M!?Y);XhH)sHO!eqwsk9~+ewf6&;#&H96O{LCqU3`EqiLYoMGy`v+C+B|2{M5Z zw|QxoZ^kA(4e>2<(Aai2H8Kyqw61Ip!O2~D497Ue&s|Qc7{x{H!i>LpmGnWJ)sIi9 zOhhCl8%bd(T3ooo3m&>8?uL^_pCDZKF8O~~fAyFJ2G>q#7HmZ<*<%hOYRrVhu-Iq_ z>5M{m{UvSWX?kbCH9>?Qp8Dv%vR{2gMlM-T$;3}(@FGE{A4b9Q62O;Ou%L$B3}1~C zbU(iaJ&Uwq&k-ES<2PUC;SPpmR%zWedXAB(}1rc|ir z@jb+D0#WI(bqv=02KhrbT3MYVQc+F1)ItY_YUwz`2*ruA*?_n<;>PFt3m`Uf5}x^; z9CuM=mp3aW5t>_6)}!Go_`ZEOkxx1%+1iYVCjZjTdlVWnFsA<*F<59KZmnPtX{aQr z|NAz5E7QH&-#gD9alg8qF;ut*y(qm&d6XE-yk?r~vrY7R(6m;J5Cm$am}_%rh(X$Z z*L#q4(E}Eas~hQsuU*AndgiitDfl9o<%Cmyj9Y-C9Kcg95r0p(i*Z}Gl8y{9*SaXr z-N#=f50#OJ`DXmLhsiYNb4FGqTIVb@ZAm3LB#kTK%f-WM&kV-o>J3xsu^PZF)4}bV z0pRwo!}wqym^-;vX;bP*>oMU3!i6%iTNKmpz6q6$wVL#L{ot8G=uTu2kWHt(kc-xz ztNK!P^}=O-q^;Sr9Dy8&BLSqQB^&~w-3g2$8Q61E+(iQ8$wX~)GtM^ZIGt6V47lvQE+BbZyzuX?4Y)MuzpcH;u%etd2*{}X> z=QyV6QKnFFMR+q(pZk|iLy;#eIOhG`SpjHkj62zngtpsACY_k^O?(=V)&Q%g=Wx|; zG^9TqG1Z`CN&2$0+AVAKbB~a!CCyPc>A+eNt6c#jLLS=mZZle?Vu=?KvWXnefIqR; zlu&$Fov)Ss_fnTFt-G1cYeEJQwMsA1uoLMx+$YtLHD&qJXlQrm?aL`0~-^Y0zI^AdF~C<__a|G zb`tJ0HyxYr!-atuWdCaWaLn`Bjn#nls73MPyRPVCgSu_FzW!QBOs@U_KA)5twkugo z;v#saVZ@q0qceth_uv@4cuKRH5IF@)F>D#h0oV;BOC=5rPC$b#59sSvVGMC`j%9iZAPrfu9OHvdYugLiIMMD{6+7apIHL5HrQf35q zpgwqH0yY?)5jju{jmc$kYi(=$+*|*x-Bn>Jz_g~f5!2URwja6QRw{l4VrIrDTS#cI zzkZ#t@9}cKYr9lu-+mdmuQ+Kmaanhy1L;b|a{IRkO86f>shlzFURELiF23^j&w+!u z;9Y;-TLdA$bF|m5lX%5K;t_QD)<_x@NdOqFSCwxz+)EfrrsnjLX|4;NNC*64S!y)Y zn_>~O+`~CGR1k*ge$u`EnfaV*CIupN{LR*s5 z4lLoR2VK~EcnU;H!(KgH3=i##P-wImAEKAd5W|R(9PUX)|qZOBb6i ztSK3bQcBBIU*+CHpqY5Hdze@yDH800}UNwL@xv8xr%0 zVb)GLQ=!MJo5Z`ltM!bKQ4^Cj)~QKfr#4q?*58YBId#)f!UxurSyKQwMO}^p;$MNpFWR8r4FsB)q z!){JCXFM7PHWZS(LEHQ3J!HS$76b-Y$%s4J&?~CQl*Vltb4yzrL`Nm5-SBi6U7@U~*dkiHI=albjQeo&!|~XMPWt|crvWZ=)ybZadao+{ z8)TE=cr*87cAe^ikG6neU?N#f$(9bNcVJd=Y9717OLCou+nJoN2%wo2E(l7<&IEL1 z4j7>F872|PR?xk7%Kllu^hRS-fcbDTz-&Dj*u@Q1l>NB8?X=qRJ!wKc6?YE-Pv#HJ zFro|*$E)Im`7qoaQZchzSwplvh}}>A8*^0c_qJR5Gjr)Ky9%N^v?*7~t zP7aTc8MqY`^wF4vCO^W<)X|Z7;VR1lP~QS{_U>XUJB)35kQThL1yqZ5?EPg$^(}tF zdAEb~Tl#4G@)$d#+sPxQ$P>*Vi_TzveEeL_NAf9P7Lt$e-8R21w_R{8QX+Val8rUW z_udZ5J=RFXR}$Fs?9P!|%-=vo7ktcNE4>|6_s*eb+`x#QbbOzNy-qDWHum8l2-xy9-l7Ij^a5j-O0|jPvvGs9M`xTjQ8e@V=%OB zY))rVHziuyUX<$Ue(Q9sthZTQFv=wR3w_WGLh+eLVG$Db#X}OVG+OLelk=vY@U0`I z0XVAUMZ(GH^Q>wauWL}}>VwWIi4cROx3i;TNoQxy=%_5*3ksK=bLsRIMM{p_G)QueM--S82@{zCicWH?0Z@YzY+p5{fDBs>^%!Udkv)$4fqex z1iyV2wzg&}fY+?HwzdML)U6(8Q^IqH)ftBV1P4C8$@LY>GY1}UbX?z`fKzY4zUmY_ zg%0@qIQJ3?_e?FX*D2!(-v$E&b%ddMJYS$R#Vh$^Sc6D6^nDRB_@JEzF005Tq3Llv zpwu4)g`yjLTT*39ECUiAs|!X+HL0cM$t^Gwoq(kkzCLJatbGF(AZGjVnAXR^2JM|? zq+BGfg)x(+kdMXa1r_yTcjMNY3kX74T&1ZNy`#V-J#ej+EfGi_m)Uddod~-|!g2By zRN8N19@$%pOs_*3)+3Z5jrRo20p#Zp)$Z~q(S#Kc8kD4C^;5T0zJECZe99Kzagelj zV{cQ1e}FU>EMWw=I{_rNG_j6EFU}-wnW}io@(HGL1QI43ng|;FXY@;J2emPYzPNni zzUH^udntEcxM@hDmwH{1kiSYt+qn*&!t$xXmmqA0#3>828zGJA9U`}Oap~8@Juf!S zby${83}?5p0c>vp8et`gNNZDJ%CQcU2yWLxk@9@&2YbS@>H-x(pogZ`KKbbPu``Eg zH^(fwUj$U~*se`LD!JFW%tq+T7mP!+}TSp9Z_HjTxWFYkdPinGN&8qJ_ z)P>a3D>nOz&e5$bC??1CwUJ&hB=X#BYk(^-_yxn--Yq0u`;aXyEe#6?$FqG>IHB)G zcG@UI@iY7&bMbWD?h6ijz5>6(Viw}&9)NE*)kH1;YQTF2_>$Yr!5D6Lw}Fqk)j}Q? z;YCsE9Q1yp|H%k=5u|@-8KVgjCZ}>WVoJoR_Jd;g_^9%U0Ex!Kp z9xv+OdCH~H99GOu@j-}tcnn2|Rr-QVms-5}lJ)rHPNxP;I`Lo+J^~D$(+`R@y)Q#2 zUq|V88%h}$N?~iuP8xRgj$dO8$t*f8002YFx?0UOQiF$2Z%ei*xUJk0Ki1ac3eQNyS3QRO#2~C&3nwdVz}w9_k(O3QEkAs7>8{cUM+t_h>#Z6$d<8zq z2M}-c1Y3xg;_?1kO@d|ffu10MN=}l?4MN6(t;S>B4oK&yg0XY8lt0FMfows<9@p{@8Bdcbknt}rshk1eWDuZGA-!t8}+XY zJ=32_FLy~f1qBCi>}-SouN*ss-+%u6oHBdn{pX8zt$9ugdE5(n+@m)0J^qK#NCG~v zn7ur#HVDy}-B0T28yNhS)6=+f;qT?Nd9;s+iqH;TS*AFdjbxeLuh8_7^fBoH`)w0IM``g)5Q1!lz2`q>l&a+(YXe-i`)h3nPNw2o_11 zJnKRE{*&-em*;46drgc*4BK(}N_B*vvf&n$v^+SP##znYvDa0~Thi+pSjLD|HfXH< zFDPPq9jWfq*@jm~>DXlfSHRVwH}+Bb0Sv4SX47&W=ZEO=3DOKBH|ayaqswA02sktu z-lpLM7Q|Z1w;He0*@*eX%)>}WOFqzX3Z9O&;|V5X5sT)~`V4e8Uef)%!&o!E+*67O zTRXB+=if5{1v5*$HD;t7XBf2^fHrPHp<`?0$=QW1?4~zIHuISF^-E$iQ_*vS8fTw; zHe}QS^AXxHk43{|2({anrso1gOFoM#NwY-!z*~`WVU1<2;EJo;BhklUaieF+fcA>U zym(tVT~veV!$wi*=y=0LnOXbgG#$5Ng`AauoHA2w(lUY>^pf?2Kp)%P*M?X9rRIuOn*0pXYlWJL*B9#y4lWMCWw zw??sh3udC#`$c7youiIb8P6yec1oz?$n!^FhY;dem?zsp|6p}TX zWPUhD8MD*_px;W_-^=((%9Kn-L3euGQqFjMKvhezyRqIx^r51{Z&*_V(tQCLA2}mb z<~iGE!Gl=18ffpWMpgJyfPdM3BA`)xmuLB8cHLxpb1Orwg(DHnPQK$#7hJVE)>BZ_ zAsR~twdAZGi)R+{CmV85kNs{iy@UVMAbuOw`?{=opIb^zkko-no{&d{o8IPe6RWt2 zmgXKLW{038_UHOV%^a^%33Qq~7qd{Y^7ui>7C(yvmi;Q2Lo>GFA=s5}_%GnOf4jlt za&HI|eeUb7XJC$c4avd96NoxJs!(y-X=I~zEPggMsXYu=EuFsJn|J?7Cl?}wRLc>Z z&}?dk)An3#T7hs^59qW_oGW?~%<&jNa08Cv*4{xB0$FHVBOF!~$NBU14)ZE7gx&wK$3*?!W$aB;uu z8jOS7L`cR%QH>ydl$VzoEg{c!C`V*}?DGq5n{2%2lc{fxJTsAJW{W~8$=`Nho1T0H z&iumVj>%JEe^x)OZw&(v4V=;Nv#O$S7yi@r<-^sz2;5QiFKy&D1++6DUx6KDv!*y?{ACj&2aYH0X)9Sob-*ot{2XdK>fk!(MO}9y8SDm2Hq{IGN%t z9%@+$Q5>MMSMqpx4)>4L5J~zY73+awt-NJcHFNwy03F->aKK;}n?$D(!-8JDdH0e_ zcC*02t<}&*@DhwG^rG}dg&-vp#o5=y!TojiV&_Pb2?OQws~%GsUE9YeWPsL&4N{F4 zIE3@VZ+E0*le0ZMA92#CMMwo%!<9sTQtY$JL%yiIy}j{h{ISG}u)Nmu542zQWoYYL zLesw?866=cy?uD$3%)}QRd*ub^AF8{G(BYZB=4ks1m|ysIZ81?`rlUivO7xM=tiq7 z&bk{}StT!?OK){TFI}l`d8o6rm%7oN7ab&zzkZ5VH6C6Wp|V64S5>Q6-H5KMV+Yl* zwf%~=-{3Bm@=QknvwJWMeoIXX98M~r$-k#{b0YU_Jbc`wJ*yx*YDap(yrF!?4TV6| zJgyxx;AhLJ?GG&M@dP@W4}EaxvIUJ-1{dfe?(fYFuYka4R^MMn;QfT!<%}DMAQY2C z`SDT~;~I`9oSvOET@F8T({ECo$u~(3kFdEeo1yDe0u)jkHkzHdg#8>3xU3BeB&&HZ$sUW&{$m~ z9||r@x+JwN5;^u8pohDBUTEx=$nYW{3TKcru2c& zQEPu(3NAbA$4+6Y&L*mU;D|qKvjI&bsMK>Twk=7`g(be@P*;)fSwhZF7)Khk`hPGY zK2`52UNxHpPj+Sw?%RH#p{R>^?(TO!iKOFCJI<0yea+0H(~-rjIksttm6BQ=!H7B@yz)&XBBTcSC<%|0j@cfowZ8au+L=O$`-O0M5mh@9+I2m4v{8J3ISo)oDWTnT&g4lw%8AG$B#Jai{mrLk&O9YnkNcYnHh5tr!q?#Pf(tM4 zOTIihW$aJ+vnf-zg4;hY*PYYmkz22LzW9)NQsP)0Ifl&#v)(}$0`NkO#DCup2Ol#w zDy>hbTB*)E&|NYzam4H@V3sTI;FAaUfs`u~rtD2ePa8&;cdtUoJo6c>*CK@^39`Sl zV`;t$lM_@!I4>g)!RQ_ng^ZHX%_!dTs0dQ2Jc~u78Ab_ZH&~*2rJ2z`Y-OQCbyQRI z2O^GA1(Y${{Auh;z8!JgZ)P?99xa-yu=iUug+_1VIC!Jk*9WKu!Zy%pryc0%n0{4T znYMGYn>^nO7Ax@|yC~!5k(U5xEstEO;HO(7^r&YJO!-YWe5OHuM{yWmOMCYV8kC2FAvvy}iLw8yj$pRJSk1&ABfK>U(!!_H6TC&-HjU zx435iXV^nNRiQUk6cWYY za+`X#DhgVcz=D(Ix2prpN~<~LJT{^GLT+a)VH+CS66@@-ilvXuZ-K9nG^S`W&247k zSh5t3lNF_y?m~I+q(${r9Pl(aJLUba>yr^IIhk%%TnF{NXWx}G4d%8s!rsUQUQIULuf`o8!|Lnn4gXpZWac7kvgg&q*n|Z4&_Egcxii_7cFx2VOsFJYh^r#40j8Rg}yV{-j2+Z>QcX@ zES5}sA!Yo|4rvrq?yL0t#w6~9l`M>{4HHZ|j8i7q7f;scKixP%VD5sLa&~o9NHb7l zX`D(x3zZnqa}eW)LFuz4o;gGM^P**!?gh&_FUjBQnP&B&09R^PX$^H)HJtRGL&1SV zW+abY)pOs;Z3Q_kBaP7cDvi*iq*xWn)RgVQ872SUh6-0DZByw?hCJTL7RypfYkGwpVBY=f%ZI`PCE=G znK0!?ibjA&Q5u{GJ!>&QPcqJLU2lP$4?K0jJfkKIAB89E3;UY+!9wAEdX~Xj(!-Wf zw_-V|EeZ07Mt|F6oP>8z=9o^kz#%@vV@883swBh*LdV{%0cQLA3a_NIDa+DKE`3I5 zR>Vt%-&xLeio;@46CTVW%{$<-YOR)ySB>w=(Bvke7pX~B-PGWL)7IZnu;9K~f6;{$ zhl{Iy7E!cEj9-Lxsj6ZUX4{MTV${=06eg~=OK#=xu}zgW8%1Hbcd*9#^5dfj9Bv4& zR=&Dz_r1@*g^y@1PksAg^*3d@r>g&*Kn|1FEnQXGu@4Uq?b0=Y-3JA~LhU*KM>K@b z9KUaFDt~ZE@+wv@s#ir=r<3sar{XJ}%>GVOu?L#2#KV0AO1>&W<{QXuqCV%(l+?I* zSODyFhDA62#m5WPMZ$vT+2E%!Uru3WhpniE#^`|UE-v0&9A*4kl#wQ& zp=8uZEa%yiE{Ssj0C(_@Bz86)?@ot<_ya^4>AV>d%3LzgWZ*gc&i#sE5m?c9k!Mr*_r$B?#9D(<)-TaKR-4=es^pOjUqc z#2H>-8lA}cG6~~3?U2OXOZ4x7Bhse^ZcJ*rkBTzWexgZaLvl|LG^+)b#u(Pg^1%Ms zx?Vf2P7JxT%(pk+yR5d{byzNiW@dm1`SJsjqavc>x^j{P98rduzUOm^Yig7Pn7; zo9Qzq{@CFz<{8qTAEcRz?3W2?1=pc_?07{tc+R5J4?KYs9GVR@;uzqdPr zN6M5|RXKd)&RkqvbQ9EtBMqCyV0`2h6#hGi8#H&VX0UgCx4maPLPGv0J@B?DWbf`` zPvq(EBE>?MVP|_;Bw|&DiWZ!Z4Ocml*PRXx4T<8|Z7aR|YovkIP+Oa5^DH-4fPli{uqDIQYpVpaGu31_8Z|u3A_)9C`oSOw}*is}{8K?;5F4-|y2g5V<O*EN=mqFe$6E;VY^Q0miOzD_f!2VJ}IjbgLY{_yg1%Y=kZx7g#wnC z3|TMsZUwLcu@rnGsBN!3bg1TjzLu?vylrUAhphce;mz(&khs?Uwa@c1D0Rc2_&O@@ zdQVT(laLtZGzuE`^n{%Np7|%IK?7%5dxtJs{&QdIhA#QuI+tQU_n_&TUtzE|nbyg( ze?smdmEUDe@lX4`Lo(Myg(yEpi5 z^HZIjo%b{`7v&2V{34m8AwYm(+dsJOWnqeIDwT{`+?urTje75k*Z4Om9KpF2(3MEX9Z zeTVZo)#3-KrH$ZW2H2C)PEsm-2O&Upeqe8V$6R(yEgV)X8+F!9KKm;KH2Z6Je~V4p zxHXQBIGu)wPfn0_=QUm|5St58Z_hYlsdyEp>L{f=oD!2{@c6rmuz8<)#(~!xq4!fl ztb9O3QfbDK@qWX2QPLFqep43Bk|0mUh~NC)58>P@kA7LRAEX%d>8H}6FqOALc0oz6 z#A&I5^*^+;glLWu3)(2J;^J9bvEwZCPURjn)Es>^C~*B_UT(LMoFcTWW1D%qbUS2A zcyOnJTDqb-GX&cZd39tcpJ+yiiCcdv&1u{B#TFaH8AhCl=r!tpm2Oksl3=^#6E3ux zn&T4}MZ~>1wjP|+{^{tA2c<$*)(p?-)lM7fUp(-$nLoi#H5NaOX8dT$R&A$zY=)i+ z34KjVFulhK<$)8p9$orOC#aj~Y7IQw8#2n>QwoP@YjrV$-;B-}yjV>dLJ=_)aC!-Q zBuF(g>4TE9#I@O?GFdXJ=qvY-&*U{kHC+*60ZgV9H9q~_0HjQ=eeoo{7|oPdbMiz~ zw1LhK?HWn+b3m^`@kgVs-Rp~uq6Ke~iEHeF71hHl2u}@>YPsu-C;H+0!1K?EfC|@u zXDY>}@2?1ui3qTuEhWFr@b@PYwfaAJ2V2t=woS}c=;0HDWhZ%|abH9@k zO5wxJwf+mpz2YMn?%4kOwmKxiqpu_muZi`CRflmUYzdtwx415+tEntFI&KwVv#N^X ziu^oDINWF^_ToTxovF3pUw=;)_D=So`42Ta1R7U;-kp57i&rOTJrW*{L347w9-HU1 z;T+8vGwqzzkBN#$2`T9QiR#PH%{tlYEKJqN-rT?8lE(pan;c;CVL4b~u0LZ0K|^b= zaJ*&^gSSK0gUVEic;d0J02wIMb*580X4YOvA%Hqixd5EZ-a1=EJeST;yQ@V}-QXDn z2eG?GzS8nklp1CTqZ%=#ZzpwnO71h=JO@laF;Wc^YuBMnXwJ!VoJiJ&KYNd_YN))b z(biaVoSpmx%@XUFO$!c&T}F3z0bxg5{bcFBE$R6=X-9j!j9m77TFwg1(5!`Se(55| zAfW78zAeee1QTl(W4LW8Hz zzGVVT054G7;+KtEYINh3^53LW+w_p^XY&iRX*po%0ph$_03cRmZd3U$P+3&=b#i3p z;=Y{vFD>gwWOiM>RT+sMpi34yg_O}MMyQh;LBLC9A$E}qI$?aLA466hapaI5UX$mz z{jD!fq(E9jw#vpdvp!v0PF-;^EaC8~!7V3fMnGZooSBU|4-$dgCv}#QS@Us-ZaU5n z@$F5(>|RT5qN}7&dGfX@-U+=x(yX^LRMGbPw_2qtPim|z8VlLM5|lza@CvCb3J_g9 zOK1F4^*8=#7cwt~P6oDiI;J`yX3wO8M$Z_W_`CP#HkUUKwg@P`Qj$#LeGsGozaeo@ z{dy)@Sefz-n%8T2AcZ=BvKqcD#pE;4&Zfr&mM4nI-Oj`-l?&kPapqg?9UKt`^YyHc zr40Ru1^sM^)zOvNI2z7AdknAOdPM0xF-ku&&75xm-$-n=iEI^mc<53?3q*2_AqHu4 z>sZ_`p`edLL+rM5q?FKrw=?>Au^a!Nayt<-7VN^;FZU|be3~|d# z0b+o+zhE1^wv=}%HN6JAxO77p=X>V_WbOhaO-brw{zrHIo%{7Sefy3hxX;YY>^wYx zoi5LlnqOZ24lv)$-u&3y-Q`GEudL_$}EGjkc*g;Nanos}B2*%S5dr|FpA zWnrFF4Q*4(J*UTYqmj84aX9|T0wz>swnqu^pD$En1}|$g5j(}05DABhC!5#1npvy^ zL_43>qKmsp96k|EeG9iCrM7Tl&*z7SJ!4)EMU@UOo$N6yo*5C*TPwZq1?_ArO%}aO zy>`EwIA0x#f)f|;Im*Xra9)Wjfh@685Zi8PB}K(e%9E%(*PdIYHwXXf=GmU&S|#eY-Gb+b&sO+&hgt6? z9bS3t<$C)x1Tks_&c$f01nml*Gk6CujZI&t%jO&O-Uabv?}vSaaOxEjHVyQFSbuBn zdCjj`Bua4q2398?t=9tekH`u2^-4axy_1WZ`)9198)^Z8OlIBRsz>arlw{oqJanDO6;JQjFWG(>$#{161()zbFV+1;5;c;F^ zv1nDfVL%i!q;T`0U@kRM1uVL$Si{rq2LNP4wq3vEFo-U6qSP6egXGgcc1(weM-+|w zaK``|QBVR!{b3)nt0&m9n|Y~N?e6oDEgG*W@+KJ-;#W2Kije+ks%0HIxn#aL6)Fd2 z9{EpVpR|~;jFTz_jBE^4DBkFMTUk>_4VXd7FMrghcfBXjsY|)g(b$IQ6iE3)VXz_9 zl=@@4I}T6w*br$TJ$L3T$&LPXX|8g$#C(#a`_bV6nA|DitA}|JMdpShWb?ouy<}O`|nmOkx52_MX@hF>BuV$rUn=R|uKHS-rF3IyJu7NXcsDJ|Li$rFcZ^X+;Ki)7>s%l1N30h+*B1n@g)a9C{P_iA zs-xd3lEwgEBMkM~<*OvSyuPi<7#mloaLhV^jSxl=iN{ywSl0dNP78L8klyXHENb0~ z$e=pB+sf(WbmneQ+Dsh_jr$(>-%w!yRC>K21Fmn?|A2@R_+jj&BRd{&kfSTjxaT&t zYBYILNn&H!H@EaNY&<}s@ErD(ymTQuxp0ICyc3%8Bkq1!B=0o&HPqpK|GCSDR=*VC zh&&BNcHe+&3LHO(6n98DMZY)=-VigvHf|gD1{-qJazDVTl@zTH3T>3%9t=OyH~?sT^IcSC9@(E9S;1$A>~?@e$-q%phXXGCB)`fW*U#+-}m_&j_s_ zmpL3_k5Xb~;F4Tz6-0}rMDe7DlzHA(9SrxC=8(i2>ist4VKrP-wgH|EDTC}9YCYXN z%HKXC2Kx|u^RX~CrGDlCAcm1q{zy?bsUVq^Q9j zVs`)iT?9tY(l~Z#ix{su+x{uxif2AQ3Xf+(4dau?q^#k4i{`8EeU?=Xx zymBqdyA-I_aQU~z8^&s5I-$m2`9^-7-$g30R93pRS<`+uY3y($s&V)mp}t{mt&ba9 zqYyHRhQSwAkL@sV5CebtXIBU)ieqi6K*-LKK$Kb?Ir)>s;kXRE!9z|(2B%A0&K8@e zsqC@t(k9RIde32!nDQ<0^rDc6euM{nj{>3axbbXStS}3UdOO`?bTPsQt^|vYierqp ztrIAzO9qg-ru#5{s+l{+SHZk;P@%P>d;^%0>AvxX<4+32pp`bo2sw=DAyT9!BMpfQ zwk+I6<3;tU?O+e~I^u?!5lxvCf07k^N7k0v-L3+NY6P)fF=Y&*x7Zfa3cvo~Dr7(hoNx<+J1fik$ zH;6kd%#v@O>hDaOQZyfX^uOP0-}OtzZgI)EG>VR>Y&gbIa)%$48S5-)=`5}icRLeZ z3f$*p_++`a0FfVZr2FCR8#c`POU_k972Nzbs_WtYsUlUG_csEJ%(e8ObH5zb;FpA9 zoE{*G!P$iK`unO(W+Y10hAAwf9`EYGYFsnC=0c_zZS@J@7EJ5?|J%B{sMXN%-a?)! z6@9By9RdR+k-lwTh<*9!|LT20G1;oGKkXnyqzA&y^ zxz>lzQaU{0&ellz48v0_GryAJd!f&!XDLu&nP;w{HdA8sA)9nteDbjxrKIbD^sdIS z((;$rRyCIP+g1svc{CP^<0(bEd9a;@Xye^r}OD}tKP&q^J`v03hS}kP}v>F;IGs>{pXclmTQcz<-lMdPBHRc zWL1$97i*XsvXlivU}P0dWg^QPF?ViKU40)seX8xbnIh&Z-r}Uck$p4kQ;L{m07Npy z|J}%AX#@9h>vbC@-c6SS1hxTBCqB8WqyM>;(C1|w+||hhPh3&Nq)@jaA@XoXA8|wC zFzj@PX1m++@xbi$@8%4ast9Y3&CaYSWxhG~Hz;7L!q;f*5~*enm{cx^6|)&sz^`?| zM545R;;7^GsWi{+%+qmm-c`+N^G)J4kxKMcI07D5p+f_J;to^nr9VFvJ3n|N9cg?; zujElMS*9AjYFJ`l-{CJ9m@NZ%vQ~Ts&ud`ey|u|W6Fm9D2@qdRQ1W!Om06oFQfZdC}amXzXI8k*}{BHY*yd+Iq$@uwe!Slcqwfpbke*$13f!9abph)>L zJ>x6sCwqG#E4jS)%Hqn?#qzXS2n%fR>{8&rWo(qi&=iUHV2>$GA9z}OlT9~AeCn9+ zXEjk&(sGQnHVp(Kz47f+qFlM{1-6caG>74L?^ffB_M=}vgrNP#*A$R=`TmS-qg5ZS z5Iz*!&(eux19%g=zPAQ=w8u{SMONP%`JWabwD>g;ATY{F!{U5|U^1e1M z`x3D$hc+X#BR?gLu7^9D&}`S6Fe~0;7vssAiJJ20nH@Sip=3|d(s)p-MPIRpxh1(0 zFB!j8%&Yj8_drg9XU}_$^_koFKg@>yp8NmqW#>KzPuZ{9buZlAG3<>%HsxbFK}+`Z zEsj?N<)HZ`n(%PLMQ2n!7r)y0N0E#p#JhZOk#M^$Sd@0=>ihV&T;|cj=ZX?`k?Qd{ zY3it;93AfTjBEyC=a^$_!k>ndW5k%%&(g})_p0^z?}3G%pHPUkT1@XclZn#Xd#5*9 z#S{<`&1p%h<>T(Qk>>gCseFi1b#gS`T%mB*gcDHR+xRfW+uzL&?NqC-j*vDM`Q3@< z%VeXWj<3|X)|QmT=6ms`Bm?WhPS4pC*;Qun4gcoXU1^nWV&x*OTdS^3fk``A>S%5% zdWP=n6Sg1UIE>1mxaL~Qf^5=7L~m6IQcT(3a{cvcSP#wsP@*NZ6~*d{r{=7B`^@=Z zA0%`Bs-{-dOutRn8Brp~@sq?u&MY0e#0w76_fTo{1u#wAXl?(JRKn>tYjSgT{Q1MF zGj0QaI$(oCm0?Ec^ED4k85e24^WLr2E!A>XzD562IckmDJLD+{Kl)wToW-3GH4oNLb~1`b#d5GDq=sMGUx*XN2#V=rTt0u2v4j zS#Afu;4CdwF?k02(M~SY4_wCt_@2O~h61vK{~g#8@E-x3kC`RZ>n@y`kKVnKEi3}k z5H9`oN=VWy3!XTk!m6#iH1HjWj4HF-cZ}}t^S)VO+`eP>q7xnh+vb-}fIN!fB9ah} zg2X0RNA?FJV;V+Z7!jjZl=|0ApmEkHpC>D8(pvZT_z`7^I&`@#6;u@A;c#9ayVt}* zZ%aC7H;<(}LHy-JI3smMk_jFMNuJRptZ=l{qqSiD zFGGAR-_qh3i4{Z@qe#Lb)^PteE61y8FN8Z17p7%>zo4Gw*{oY&>bx>9n7`CD&M^&_me8ETsQ*->gja`#g|gbGJOgu zK7PxEiXru`ErVOJE~D^fb!)F{@0hR`ZQULpKBc8qO~*lIpY1hnQW<)P))KC)TXp3q zaObO=?~^+GCbu5Uxp+llhCkb6p{wZ99YXE3;&`?9#Y$pvDvKlQC2XCNNnZT+IqdFcxk=o`!zi&-Xlxn0BAZ7&eFf+m?&{oH`=vxTCXvRpP`62tZ zN>dcX)hFYC#eUh>mhKwlAjECIY6x8mPPJB?**sA-(2O&@7wv0xB5uXXK(dn}Xh#?) z#LV4)mYPE}KhHkNn`L!?#d~%4)8&1zrt0N1J<%Qc#;=qTycD`PsGBFM)^aIh0@i+E zC;cXZm(3L7?3wdyFTt=EaMon5-`r^ITgz_gkf%cz#hTeeF7%?CY#hH7Ck0k8U)sD9?d3_l`EVfg$v1>EN2u45u1tln|}UydaE~8`Ba6Ijx#D z6##$Wr`S+|(==@7T5#wUDegT{c5*6~F4tbtDcE3Hdx$ohPS^n@Pf^;0CRoS&(==jU z2fm!ALp^$Q`THw)+f&rCn^A`2gC#0$r!8xl0I#m<&8PL|@Hx>XL?rR%BG2k`?~6xw zgd@zuWAn?Uy8v8W%6%&wt5Tw?E{#~qqqg}TK6e|3HD27iJHN+~+bED*q?Y^Or6=E+ z32hVk=Xorvw2B2%by+Mi@)3MkkLd_O4(=dY20fTUTQA6+KB=v_Hs0J)0d$J_CVYBa z)Fzge4uDThK2H`9BmzRC_?Bb|b&%rUi-j*QYE0FCd`Z2>`yYDp(!WTGx$dKzPigIL z0U(u)O--e%V6s7(_n1&r1hft@vik0`k8{W#viA>5zJ-ALGK33Kcy}~p;vvN3=O>!L zkVSKQOX~2Ri^hrMl0+{csp1}-SB+jt)N-m1)EfLqIL9Av?>%&Uu(8Pvf`ZBOpT9+v zr=+nPO|P!S2bg7BzIC#!QRg>Ze$@XfN!2t_^=N~W0bEJ@muSD?aB(X-ssk78m3DG= zABB1 zxhbB96fmWun`fPuRxElwj9rsydb`YAnSVpCIxbJ`Zp}x?xWDk~C3iD~lGspi<`%`y zCj?7Jj!Ey@Lv8bj?4Q=2DqZqUvJ15bWPR$S@j1xA8XmX$ATt(XqhW)TktZI|@NwY4 z$-yo=GcEh_K|e*k8O5tR4>D&vc95!AyY!Ly$|YMGf&k6F0w|T4y%DxBD?Ms;vN|!z zi|eM(AQwY-TLdbqrF-^qZie590vKI7fBVw%7F2nf8tIeziI?$Yd8)|f9@tcplHGuQ z5(b^&P_8ST$K2vwiIkQ)f0?ik2nhHm=_x6Rb`@6#IspLfyKUvq-d>WvzP^#T928$VK5@#LEU7vhaEy+n6z1WbcI|M1Ia3 zO}T2;6-Q?Cu=oCC9@*@GVT`_t?GyU9CXH!P-tr(0dMd!xF%QOBHq8!k8C- zf}$zOs9fcnJHD1pmx!s@$|iY%927}Hv)f3~WVdCpt!Q)hQRgqxtJ3mVyI*UP2|M#s zRL;h>7d)J+%AqzBCAGf(*S0-LuRe#)@Mj!Mg~Hej`=d3$WMr1wKQ1{u@oZ!jT`N|m zT&}a3Ne`cdu}{lOTH(E}3ovWI#v&}jqS{p59U6y*82dx#<$kM&jig3CTDpD=sBVFB zm{SU9X6y~(&B^1v;EX&V?7y=Qp@qTfm&ki&rNk7Z)CMD0wa8)xR0*D_M`@ASG@dvT zk4SI)Vdx^gLiBq$ox&b3d9^(Bii<(cza6bo7QS-?Jz&5F$Y`BksSY$x%|1~OGEj?f z=-NsYS}(>feX9Glu_keONhN(_A?zgP zPzW_#o6_h{HFjCdL9C1-)R{XLjiY^WUpYD@lMv6bG87^ffYFc?hyguWeJQ;%#iREv zE8`rZ>Y?Sl(tFNrYWnogGCw67HJFi@ zNqDkt2}PLMO#-bf_20=wySA=sqF{WpL@FkO9oNXJ`bZerOOs8@$*u3gKJmrGnY`D3(!GU-z%n8C}%RlU9odkQ|B1&S3qM{6oZVRd_A$I zGBiLdzv77*y7jfymy0unBF&m_M{tY!&irXd?0l z?cS}V9Ouni>n+(;?)a4U$cS*qarOO`$#h^@a3tpe%fehh#mh?+SQaauFnW4di}XHw zk~eKqMJpHM<^O&^@PPa8QDCu)qQ7{@kn>KpuEEgEPmj_6xgam^oe~2hq8;4mgB=dN zfD}rQ!y@;>6&^U7!pUNTpIu(kT`;fp_q*%;I?4^m+p8uIyx+VNjyo^>z$IFMQk1p! zF=<*(^|lWm6%LjKst5fHmv^6elXdKC#V&hDu)XC@Eb5k4sjB(rr$5~QKWNyrMC2{; z%lA)MBEQdWAwpHN^l7kdPsMz=yOLcywCfCXU_K@21Vh*Mb>t)%Ua6|90;6>#g-rk*G;70pl ztaybk7;QC4fWi^TZ!~y@(K^zDbJd($*pYc_XL*lKc)4L&Ww&O z*i!0Ry5>@N#MqB=q+=>5P9bBALu!LpTJRdF!1^*1I8(REhTctAc+Q7xWNaUMWV64L z2yPX$DG7Pny6o*8^ue<}@!Vhc-R*G_z=Z<7EI^(Nm`gu=_~7*71vI@+*VtI4#?&Ic zp{BOB3$Pjk5}x=2HovOh{m&?A3u%FO;yW(C^!)~-7xoc-%3JnLskNmOJQFWcQl-jwiAH;7OYLa=BoAj{RUzWzy0uobXFZimB=MrXV;i*H$NrZAG*;MmrKy^BpC)VLchL6 zWhRPw@`9q8WD9%%#&ey`VdcrzREx$+GMaKa!Yw@dF7yxc`ykQ6#q2JIFleG)^|sff zn&jgXkWZ<>_eSF38{YZ&e1O;9SnrgmfbKy<-UECQ|&8YhNbkrZek0AohG$T*%;(LBjee2S(d-*zFlbDFiZfjL! zl|kJ9!Hy!y_@|IsMV(jaO@p}o{)S@e^z^LrZ@Jra$CZCTa*bgymyG03HG;ZrJgWs| zlqK0QWI533ih!nv#P@1n&z;OmEVTlJs&~OeLhOVFe2>*~jq=AXCoSd?(-iBC#L(3h z?{g9Vi;IQ5ue@%79_G|@Dr&3CBgNZ_rC z6iqKA7wUOIa6waRL$JJQ>@9Eoiu=37+3V+JB-+FB+ZoH(#R1ur_FJ8CUyb^ICer@+ zFlDH{t|x0vd`hvw(Z=s>)208=;nSCRgM2dGYMzr)ctP#N`4hp*Yg4H2q>>>Y$G2?7 zaR+YUZdkazYv!k4<<^9C_nSerbkaC&Leyrd3@Bgd2NY4LlYu(O9xcR-1y|R%4HDsh zQ`9pn7(I7yz?@ zBBaSFt$xFw;HL|53a(w#1kU@1qkz2ntVVcogF=A~w?!$jS7e8z_@ChK>t6bAgA-mJ zY!z0#yW`wzjxq>HJ3XbGo11feu2$%V(&Gm9JbFnDa65fnQcGz7y0g(Jw>f^E#att| z$yLBg3}hBaT$LHU`0K0wzl#MQYHgFJGd=8GYL@yKG7FqGzqXk-ykmx`?sFgqTH~5w z{W{+j7dcjmo^%-X56{Lz$jQO7J}C1VDrUEx)tuuC<7+9%_qq8N`J)_Krx)HzQyid1ry%ojjrEj z7IMCGRfS+u;SKXI2{5w_qL5jirmKP zH}!g7t1m5Wn+sKwLkg=uktVjGq*Wt6!?!Qu&uYwwQnv*9r+-pb7rqr*c8?Uw;6*M8 zt|^llrA4_uS-TFN>t@0KIx$NX8u)RF{T{inubj?G%gVA2NQGPW^UXO~|Gq0{+`M61 zg}RMcD}ihJGiL{aw&6XsV+EdH&oEYN!Etchz|>(}HGifK*+&{UVKomH)Q6dP^}hYN zr6YcH!UFwJBfB07cnNJkNhRMmro_+p<|wIlB*J-3i9f2XNY^``!}SKW=kb(KafAZq za+XEYoZxs}RTVA#VrS>z$T!d|kk~^7rUq~PxQ|d165a`Etn>+fBQd&8f}iiG0uHr9 z+L>xw#}A&H3xX5F2+&r}n&k+lqKcZLzzH>-gP3_Kh}!G9p}p1|Fs%435zqWX#GGMU z5BtE_pp7cq)SF|D4p zVmfj0rD{Bcd%S@9Hgpr0M@gZzy}!krav7p%M4wd8Uh*V{2s&^tF^?h-a^ohwFKX_B zqUvx(HA+Em_L{IV3I#pKHjzoy_w_P_#g^6>4(y7evR`eas|Lz{SqyB_+A&2_d|4m$ zf5tHy2hU)sjoYaev`x&VUMiMVEbWLp2_Nc~#Qi2<-Ias0;K`@xptEO3vz4Y7D8@;X zi=W8C<|Erh)U9li^(^Ez9Fu+;v#J*ABK$zDjG_%l50>$3lX=Lp&r%A~m1Ww>CdHjX zH3=v*)0z3T?W>mlmFCl9H!7BbitMxQj zn%-8{%&D)hi&<~L?}9!tY~QHcJ%1bGn@B<5Z|hRE&@Cb76ieiU=tV==pk>L({esX< z-i&2|pm(d-Z*}QM2d6e*lHdiW2u0Z=7?ZKcJ@w;9yot|6x&HNAK9?2#~s(j%mE zk6pYfSX3OIu{5`M?aT=k?~Tpn2wm}+Hx}0KkxM!d2#SM}QO-90@yeTX$IV17C3Z8^ zP&!ofet&s*u)Nmqe|e>gOF-#H?e=1urK8 ztqP7jl1?O}t2zSUBBkYLui>Ct)I6G|Z;C#zo`uE(^(y-Nr`%zjL^4&;N|*WU?^dy8 z-Jx7Qu8`OybC&TP6hGb)bJo6i$_L*xX2$#M`RwW@Gx4pfZJfvFS9hNW|GpH{5Xw|T z>LeB$;W-{A)nSrV)v|t&Zu`pQ9AP~88km*5VRJ>z13RFB<0lS_ci%siCj#oCR6IgI z{Z8cxmZ)Xy$a%ExBXQoOP3huzrⅆm09UnKtA!w+wug)$=HX!)$ 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 Mqq`)z>=C8@rYokB!+ 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

Ih|dgNwpF) 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�VlVW;^FFx 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 + + + inHouse + 1.3 + model.sdf + + + Hanqing Xie + + + + + inHouse jderobto teaching + + + + + diff --git a/exercises/ardrone_navigation/models/inHouse/model.sdf b/exercises/ardrone_navigation/models/inHouse/model.sdf new file mode 100644 index 000000000..a7ef44189 --- /dev/null +++ b/exercises/ardrone_navigation/models/inHouse/model.sdf @@ -0,0 +1,877 @@ + + + + true + + + + + 0.2 0.2 4.0 + + + 0 0 2.0 0 0 0 + + + 0 0 2.0 0 0 0 + + + 0.2 0.2 4.0 + + + + + + + + 0 + 0 + + 10 10 0 0 0 0 + + + + + + 0.2 0.2 4.0 + + + 0 0 2.0 0 0 0 + + + 0 0 2.0 0 0 0 + + + 0.2 0.2 4.0 + + + + + + + + 0 + 0 + + 10 0 0 0 0 0 + + + + + + 0.2 0.2 4.0 + + + 0 0 2.0 0 0 0 + + + 0 0 2.0 0 0 0 + + + 0.2 0.2 4.0 + + + + + + + + 0 + 0 + + 10 -10 0 0 0 0 + + + + + + 0.2 0.2 4.0 + + + 0 0 2.0 0 0 0 + + + 0 0 2.0 0 0 0 + + + 0.2 0.2 4.0 + + + + + + + + 0 + 0 + + 5 10 0 0 0 0 + + + + + + 0.2 0.2 4.0 + + + 0 0 2.0 0 0 0 + + + 0 0 2.0 0 0 0 + + + 0.2 0.2 4.0 + + + + + + + + 0 + 0 + + 5 5 0 0 0 0 + + + + + + 0.2 0.2 4.0 + + + 0 0 2.0 0 0 0 + + + 0 0 2.0 0 0 0 + + + 0.2 0.2 4.0 + + + + + + + + 0 + 0 + + 5 0 0 0 0 0 + + + + + + 0.2 0.2 4.0 + + + 0 0 2.0 0 0 0 + + + 0 0 2.0 0 0 0 + + + 0.2 0.2 4.0 + + + + + + + + 0 + 0 + + 0 10 0 0 0 0 + + + + + + 0.2 0.2 4.0 + + + 0 0 2.0 0 0 0 + + + 0 0 2.0 0 0 0 + + + 0.2 0.2 4.0 + + + + + + + + 0 + 0 + + 0 5 0 0 0 0 + + + + + + 0.2 0.2 4.0 + + + 0 0 2.0 0 0 0 + + + 0 0 2.0 0 0 0 + + + 0.2 0.2 4.0 + + + + + + + + 0 + 0 + + 0 0 0 0 0 0 + + + + + + 0.2 0.2 4.0 + + + 0 0 2.0 0 0 0 + + + 0 0 2.0 0 0 0 + + + 0.2 0.2 4.0 + + + + + + + + 0 + 0 + + -5 0 0 0 0 0 + + + + + + 0.2 0.2 4.0 + + + 0 0 2.0 0 0 0 + + + 0 0 2.0 0 0 0 + + + 0.2 0.2 4.0 + + + + + + + + 0 + 0 + + -5 -5 0 0 0 0 + + + + + + 0.2 0.2 4.0 + + + 0 0 2.0 0 0 0 + + + 0 0 2.0 0 0 0 + + + 0.2 0.2 4.0 + + + + + + + + 0 + 0 + + -5 -10 0 0 0 0 + + + + + + 0.2 0.2 4.0 + + + 0 0 2.0 0 0 0 + + + 0 0 2.0 0 0 0 + + + 0.2 0.2 4.0 + + + + + + + + 0 + 0 + + -10 10 0 0 0 0 + + + + + + 0.2 0.2 4.0 + + + 0 0 2.0 0 0 0 + + + 0 0 2.0 0 0 0 + + + 0.2 0.2 4.0 + + + + + + + + 0 + 0 + + -10 0 0 0 0 0 + + + + + + 0.2 0.2 4.0 + + + 0 0 2.0 0 0 0 + + + 0 0 2.0 0 0 0 + + + 0.2 0.2 4.0 + + + + + + + + 0 + 0 + + -10 -5 0 0 0 0 + + + + + + 0.2 0.2 4.0 + + + 0 0 2.0 0 0 0 + + + 0 0 2.0 0 0 0 + + + 0.2 0.2 4.0 + + + + + + + + 0 + 0 + + -10 -10 0 0 0 0 + + + + + + + 5 10 0.05 + + + 0 0 0.025 0 0 0 + + + 0 0 0.025 0 0 0 + + + 5 10 0.05 + + + + + + + + 0 + 0 + + 7.5 5 4.0 0 0 0 + + + + + + 5 5 0.05 + + + 0 0 0.025 0 0 0 + + + 0 0 0.025 0 0 0 + + + 5 5 0.05 + + + + + + + + 0 + 0 + + 2.5 7.5 4.0 0 0 0 + + + + + + 10 10 0.05 + + + 0 0 0.025 0 0 0 + + + 0 0 0.025 0 0 0 + + + 10 10 0.05 + + + + + + + + 0 + 0 + + -5 5 4.0 0 0 0 + + + + + + 15 10 0.05 + + + 0 0 0.025 0 0 0 + + + 0 0 0.025 0 0 0 + + + 15 10 0.05 + + + + + + + + 0 + 0 + + 2.5 -5 4.0 0 0 0 + + + + + + 5 5 0.05 + + + 0 0 0.025 0 0 0 + + + 0 0 0.025 0 0 0 + + + 5 5 0.05 + + + + + + + + 0 + 0 + + -7.5 -7.5 4.0 0 0 0 + + + + + + 0.2 20 4.0 + + + 0 0 2.0 0 0 0 + + + 0 0 2.0 0 0 0 + + + 0.2 20 4.0 + + + + + + + + 0 + 0 + + 10 0 4.0 0 0 0 + + + + + + 20 0.2 4.0 + + + 0 0 2.0 0 0 0 + + + 0 0 2.0 0 0 0 + + + 20 0.2 4.0 + + + + + + + + 0 + 0 + + 0 10 4.0 0 0 0 + + + + + + 0.2 20 4.0 + + + 0 0 2.0 0 0 0 + + + 0 0 2.0 0 0 0 + + + 0.2 20 4.0 + + + + + + + + 0 + 0 + + -10 0 4.0 0 0 0 + + + + + + 20 0.2 4.0 + + + 0 0 2.0 0 0 0 + + + 0 0 2.0 0 0 0 + + + 20 0.2 4.0 + + + + + + + + 0 + 0 + + 0 -10 4.0 0 0 0 + + + + + + 5 0.2 4.0 + + + 0 0 2.0 0 0 0 + + + 0 0 2.0 0 0 0 + + + 5 0.2 4.0 + + + + + + + + 0 + 0 + + 7.5 -5 4.0 0 0 0 + + + + + + 0.2 10 4.0 + + + 0 0 2.0 0 0 0 + + + 0 0 2.0 0 0 0 + + + 0.2 10 4.0 + + + + + + + + 0 + 0 + + 5 0 4.0 0 0 0 + + + + + + 0.2 15 4.0 + + + 0 0 2.0 0 0 0 + + + 0 0 2.0 0 0 0 + + + 0.2 15 4.0 + + + + + + + + 0 + 0 + + 0 2.5 4.0 0 0 0 + + + + + + 5 0.2 4.0 + + + 0 0 2.0 0 0 0 + + + 0 0 2.0 0 0 0 + + + 5 0.2 4.0 + + + + + + + + 0 + 0 + + -7.5 0 4.0 0 0 0 + + + + + + 0.2 5 4.0 + + + 0 0 2.0 0 0 0 + + + 0 0 2.0 0 0 0 + + + 0.2 5 4.0 + + + + + + + + 0 + 0 + + -5 -7.5 4.0 0 0 0 + + + \ No newline at end of file diff --git a/exercises/ardrone_navigation/navigation/__init__.py b/exercises/ardrone_navigation/navigation/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/exercises/ardrone_navigation/navigation/control/__init__.py b/exercises/ardrone_navigation/navigation/control/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/exercises/ardrone_navigation/navigation/control/ardroneControl.py b/exercises/ardrone_navigation/navigation/control/ardroneControl.py new file mode 100644 index 000000000..3c014218c --- /dev/null +++ b/exercises/ardrone_navigation/navigation/control/ardroneControl.py @@ -0,0 +1,88 @@ +import numpy as np +import time +import math + +class ardroneControl: + def __init__(self, show = False): + #self.pathlist = pathlist + self.getTarget = False + self.controlYaw = False + self.getFinal = False + self.show = show + self.PathId = 0 + + def restart(self): + self.getTarget = False + self.controlYaw = False + self.getFinal = False + self.PathId = 0 + + def setPath(self, pathlist): + self.pathlist = pathlist + + def getPath(self): + return self.pathlist + + def setControlYaw(self, controlYaw): + #self.controlYaw = False + self.controlYaw = controlYaw + + def findTarget(self,Pose): + X = Pose[0] + Y = Pose[1] + Z = Pose[2] + num = len(self.pathlist[0])-1 + TargetNowX = self.pathlist[0][self.PathId] + TargetNowY = self.pathlist[1][self.PathId] + TargetNowZ = self.pathlist[2][self.PathId] + + dis = math.sqrt(pow(X-TargetNowX,2)+pow(Y-TargetNowY,2)+pow(Z-TargetNowZ,2)) + if dis < 1: + if num == self.PathId: + if dis < 0.2: + self.getTarget = True + else: + self.PathId += 1 + #elif(math.fabs(self.e[0]) < 0.01 and math.fabs(self.e[1])<1.5 and math.fabs(self.e[2])<0.01): + # self.PathId += 1 + print ("path",self.PathId) + target = [0, 0, 0] + target[0] = self.pathlist[0][self.PathId] + target[1] = self.pathlist[1][self.PathId] + target[2] = self.pathlist[2][self.PathId] + return target + + def isGetTarget(self): + return self.getTarget + + def control(self, Pose): + # id = 1, pid + # id = 2, direct + TargetPose = self.findTarget(Pose) + if self.show: + print ("pose", Pose) + print ("target", TargetPose) + + vx = TargetPose[0] - Pose[0] + vy = TargetPose[1] - Pose[1] + vz = TargetPose[2] - Pose[2] + az = 0 - Pose[3] + + vx = self.limiting(vx, 1, -1) + vy = self.limiting(vy, 1, -1) + vz = self.limiting(vz, 1, -1) + az = self.limiting(az, 1, -1) + + if self.show: + print("v", vx,vy,vz,az) + + return [vx,vy,vz,az] + + def limiting(self, num, max, min): + if num > max: + num = max + if num < min: + num = min + + return num + diff --git a/exercises/ardrone_navigation/navigation/control/noHolomonicControl.py b/exercises/ardrone_navigation/navigation/control/noHolomonicControl.py new file mode 100644 index 000000000..de1c4104e --- /dev/null +++ b/exercises/ardrone_navigation/navigation/control/noHolomonicControl.py @@ -0,0 +1,159 @@ +import numpy as np +import time +import math + +class noHolomonicControl: + def __init__(self, show = False): + #self.pathlist = pathlist + self.find_path = False + self.PathId = 0 + self.getTarget = False + self.v_old = 0 + self.w_old = 0 + self.v = 0 + self.w = 0 + self.e_old = [0,0,0] + self.e = [0,0,0] + self.controlYaw = True + self.getFinal = False + self.show = show + + def restart(self): + #self.pathlist = pathlist + self.find_path = False + self.PathId = 0 + self.getTarget = False + self.v_old = 0 + self.w_old = 0 + self.v = 0 + self.w = 0 + self.e_old = [0,0,0] + self.e = [0,0,0] + self.controlYaw = True + self.getFinal = False + self.show = show + + def setPath(self, pathlist): + self.pathlist = pathlist + + def getPath(self): + return self.pathlist + + def setControlYaw(self, controlYaw): + #self.controlYaw = False + self.controlYaw = controlYaw + + def findTarget(self,Pose,id): + X = Pose[0] + Y = Pose[1] + Yaw = Pose[2] + num = len(self.pathlist[0])-1 + TargetNowX = self.pathlist[0][self.PathId] + TargetNowY = self.pathlist[1][self.PathId] + dis = math.sqrt(pow(X-TargetNowX,2)+pow(Y-TargetNowY,2)) + if dis < 1: + if num == self.PathId: + if id==1: + if math.fabs(self.e[0]) < 0.1 and math.fabs(self.e[1])<0.5 and math.fabs(self.e[2])<0.1: + self.getTarget = True + else: + self.PathId += 1 + #elif(math.fabs(self.e[0]) < 0.01 and math.fabs(self.e[1])<1.5 and math.fabs(self.e[2])<0.01): + # self.PathId += 1 + + print ("path",self.PathId) + target = [X,Y,Yaw] + target[0] = self.pathlist[0][self.PathId] + target[1] = self.pathlist[1][self.PathId] + target[2] = self.pathlist[2][self.PathId] + return target + + def isGetTarget(self): + return self.getTarget + + def control(self, Pose, id): + # id = 1, pid + # id = 2, direct + TargetPose = self.findTarget(Pose,id) + num = len(self.pathlist[0])-1 + if self.show: + print ("pose", Pose) + print ("target", TargetPose) + if id==1: + self.e[0] = math.cos(Pose[2])*(TargetPose[0] - Pose[0]) + math.sin(Pose[2])*(TargetPose[1] - Pose[1]) + self.e[1] = -math.sin(Pose[2])*(TargetPose[0] - Pose[0]) + math.cos(Pose[2])*(TargetPose[1] - Pose[1]) + self.e[2] = TargetPose[2] - Pose[2] + #print ("e:",self.e) + #print ("e_old:",self.e_old) + self.v = self.v_old + 0.2*(self.e[0] - self.e_old[0]) + 0.01*self.e[0] + self.w = self.w_old + 0.5*self.v*(self.e[1] - self.e_old[1]) + 0.001*self.e[1] + 0.2*(self.e[2] - self.e_old[2]) + 0.01*self.e[2] + #self.pathlist + #print ("v w: ",self.v,self.w) + #print ("v w old: ",self.v_old,self.w_old) + if self.v > 0.5: + self.w = self.w*0.5/self.v + self.v = 0.5 + if self.v < -0.5: + self.w = self.w*-0.5/self.v + self.v = -0.5 + self.v_old = self.v + self.w_old = self.w + self.e_old[0] = self.e[0] + self.e_old[1] = self.e[1] + self.e_old[2] = self.e[2] + else: + if self.controlYaw: + theta = math.atan2((TargetPose[1] - Pose[1]),(TargetPose[0] - Pose[0])) + theta_01 = theta - Pose[2] + theta_21 = TargetPose[2] - theta + theta_20 = TargetPose[2] - Pose[2] + dis = math.sqrt(math.pow((TargetPose[1] - Pose[1]),2)+math.pow((TargetPose[0] - Pose[0]),2)) + if self.show: + + print ("theta_01 ",theta_01) + print ("theta_21 ",theta_21) + print ("dis ",dis) + if math.fabs(theta_01) > 0.2 and dis > 0.1 and ~self.getFinal: + self.v = 0 + if theta_01 > 0: + self.w = max(0.2, theta_01) + else: + self.w = min(-0.2, theta_01) + elif dis > 0.1 and ~self.getFinal: + self.v = max(0.2, dis) + self.w = 0 + elif math.fabs(theta_20) > 0.2 and num == self.PathId: + self.v = 0 + if theta_21 > 0: + self.w = max(0.2, theta_20) + else: + self.w = min(-0.2, theta_20) + self.getFinal = True + # elif math.fabs(theta_21) > 0.2: + # self.v = 0 + # if theta_21 > 0: + # self.w = max(0.2, theta_21) + # else: + # self.w = min(-0.2, theta_21) + else: + print ("get target") + self.v = 0 + self.w = 0 + else: + theta = math.atan2((TargetPose[1] - Pose[1]),(TargetPose[0] - Pose[0])) + theta_01 = theta - Pose[2] + dis = math.sqrt(math.pow((TargetPose[1] - Pose[1]),2)+math.pow((TargetPose[0] - Pose[0]),2)) + if math.fabs(theta_01) > 0.05: + self.v = 0 + self.w = 0.5*theta_01 + elif dis > 0.1: + self.v = max(0.1, 0.5*dis) + self.w = 0 + else: + print ("get target") + self.v = 0 + self.w = 0 + if self.show: + print ('v, w ', self.v, self.w) + + return [self.v, self.w] diff --git a/exercises/ardrone_navigation/navigation/laser/__init__.py b/exercises/ardrone_navigation/navigation/laser/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/exercises/ardrone_navigation/navigation/laser/laser_handle.py b/exercises/ardrone_navigation/navigation/laser/laser_handle.py new file mode 100644 index 000000000..6ff1b2d7d --- /dev/null +++ b/exercises/ardrone_navigation/navigation/laser/laser_handle.py @@ -0,0 +1,59 @@ +import numpy as np +import math +from math import cos,sin,tan,pow,pi,sqrt + +class laser_handle: + def autopark_place(self, laser_data, pose): + #assert len(laser_data) == len(laser_angle) + assert len(pose) == 3 + car_x = pose[0] + car_y = pose[1] + car_yaw = pose[2] + num = len(laser_data) + tmp = 0 + right_angle = 90 + left_angle = 90 + for i in range(num/2): + if laser_data[num/2 - i][0] == 10: + tmp += 1 + right_angle = 90 - i + else: + # print ("laser_data",laser_data[num/2 - i]) + break + + + for i in range(num/2): + if laser_data[num/2 + i][0] == 10: + tmp += 1 + left_angle = 90 + i + else: + # print ("laser_data",laser_data[num/2 - i]) + break + if right_angle == left_angle: + print ("not free place") + return None + else: + print ("left_angle",left_angle,"right_angle",right_angle) + len_angle = left_angle - right_angle + print ("len_angle ",len_angle, ", tmp_length = ",tmp) + max_length = sqrt(pow(10,2)-pow(5,2)) + left_park_len = min(tan((left_angle - 90)*pi/180) * 5.5, max_length) + right_park_len = min(tan((90 - right_angle)*pi/180) * 5.5, max_length) + park_len = left_park_len + right_park_len + print ("park_len",park_len) + + if park_len > 8: + print ("find park place") + car_head_len = (left_park_len-right_park_len)/2 + print ("car_head_len",car_head_len) + print ("pose",pose) + x_tmp = 5.5*sin(car_yaw) + car_head_len * cos(car_yaw) + y_tmp = - 5.5*cos(car_yaw) + car_head_len * sin(car_yaw) + + park_x = x_tmp + car_x + park_y = y_tmp + car_y + park_yaw = car_yaw + print ("park place = ", park_x, park_y, park_yaw) + return [park_x, park_y, park_yaw] + else: + return None diff --git a/exercises/ardrone_navigation/navigation/map/__init__.py b/exercises/ardrone_navigation/navigation/map/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/exercises/ardrone_navigation/navigation/map/costmap_2d.py b/exercises/ardrone_navigation/navigation/map/costmap_2d.py new file mode 100644 index 000000000..648057727 --- /dev/null +++ b/exercises/ardrone_navigation/navigation/map/costmap_2d.py @@ -0,0 +1,322 @@ +import numpy as np +import math +from math import cos,sin,tan,pow,pi +import cv2 + +class costmap_2d: + def __init__(self,cells_size_x, cells_size_y, resolution, origin_x = 0, origin_y = 0, default_value = 0): + self.size_x_ = cells_size_x + self.size_y_ = cells_size_y + self.resolution_ = resolution # grid length + self.origin_x_ = origin_x + self.origin_y_ = origin_y + self.default_value_ = default_value + + + self.initMaps(self.size_x_, self.size_y_,self.default_value_) + + def initMaps(self, size_x, size_y,default_value): + if (size_x>0 and size_y>0): + self.costmap_ = np.full((size_x, size_y), default_value, int) + else: + self.costmap_ = None + print ("map's size error") + + def reinitMap(self, size_x, size_y, resolution, origin_x, origin_y): + self.size_x_ = size_x + self.size_y_ = size_y + self.resolution_ = resolution + self.origin_x_ = origin_x + self.origin_y_ = origin_y + + self.initMaps(self.size_x_, self.size_y_,self.default_value_) + + def resetMap(self, x0, y0, xn, yn): + if xn > self.size_x_: + xn = self.size_x_ + if yn > self.size_y_: + yn = self.size_y_ + lenx = xn - x0 + leny = yn - y0 + for i in range(lenx): + for j in range(leny): + self.costmap_[i+x0,j+y0] = self.default_value_ + + def cellDistance(self, world_dist): + cells_dist = max(0.0, math.ceil(world_dist / self.resolution_)) + return cells_dist + + def getMap(self): + return self.costmap_ + + def getCost(self, mx, my): + if mx < self.size_x_ and my < self.size_y_: + return self.costmap_[mx, my] + else: + return None + + def setCost(self, mx, my, cost): + if mx < self.size_x_ and my < self.size_y_: + self.costmap_[mx, my] = cost + else: + print("Over the border") + + def getSizeInCellsX(self): + return self.size_x_ + + def getSizeInCellsY(self): + return self.size_y_ + + def getSizeInMetersX(self): + return (self.size_x_ - 1 + 0.5) * self.resolution_ + + def getSizeInMetersY(self): + return (self.size_y_ - 1 + 0.5) * self.resolution_ + + def getOriginX(self): + return self.origin_x_ + + def getOriginY(self): + return self.origin_y_ + + def getResolution(self): + return self.resolution_ + + def getSize(self, min_x, min_y, max_x, max_y): + cell_size_x = (int)((max_x - min_x) / self.resolution_) + cell_size_y = (int)((max_y - min_y) / self.resolution_) + return [cell_size_x, cell_size_y] + + def mapToWorld(self, mx, my): + wx = self.origin_x_ + (mx + 0.5) * self.resolution_ + wy = self.origin_y_ + (my + 0.5) * self.resolution_ + return [wx, wy] + + + def worldToMap(self, wx, wy): + if (wx < self.origin_x_ or wy < self.origin_y_): + return False + + mx = (int)((wx - self.origin_x_) / self.resolution_) + my = (int)((wy - self.origin_y_) / self.resolution_) + + if (mx < self.size_x_ and my < self.size_y_): + return [mx, my] + else: + return False + + def worldToMapNoBounds(self, wx, wy): + mx = (int)((wx - self.origin_x_) / self.resolution_) + my = (int)((wy - self.origin_y_) / self.resolution_) + return [mx, my] + + def worldToMapEnforceBounds(self, wx, wy): + # Here we avoid doing any math to wx,wy before comparing them to + # the bounds, so their values can go out to the max and min values + # of double floating point. + if (wx < self.origin_x_): + mx = 0 + elif (wx > self.resolution_ * self.size_x_ + self.origin_x_): + mx = self.size_x_ - 1 + else: + mx = (int)((wx - self.origin_x_) / self.resolution_) + + if (wy < self.origin_y_): + my = 0 + elif (wy > self.resolution_ * self.size_y_ + self.origin_y_): + my = self.size_y_ - 1 + else: + my = (int)((wy - self.origin_y_) / self.resolution_) + return [mx, my] + + + def updateOrigin(self, new_origin_x, new_origin_y): + + cell_ox = int((new_origin_x - self.origin_x_) / self.resolution_) + cell_oy = int((new_origin_y - self.origin_y_) / self.resolution_) + + new_grid_ox = self.origin_x_ + cell_ox * self.resolution_ + new_grid_oy = self.origin_y_ + cell_oy * self.resolution_ + + size_x = self.size_x_ + size_y = self.size_y_ + + lower_left_x = min(max(cell_ox, 0), size_x) + lower_left_y = min(max(cell_oy, 0), size_y) + upper_right_x = min(max(cell_ox + size_x, 0), size_x) + upper_right_y = min(max(cell_oy + size_y, 0), size_y) + + cell_size_x = upper_right_x - lower_left_x + cell_size_y = upper_right_y - lower_left_y + + new_costmap = np.full((self.size_x_,self.size_y_), self.default_value_, int) + + for i in range(cell_size_x): + for j in range(cell_size_y): + tmp_x1 = lower_left_x - cell_ox + i + tmp_y1 = lower_left_y - cell_oy + j + tmp_x2 = lower_left_x + i + tmp_y2 = lower_left_y + j + new_costmap[tmp_x1, tmp_y1] = self.costmap_[tmp_x2, tmp_y2] + + self.costmap_ = new_costmap + self.origin_x_ = new_grid_ox + self.origin_y_ = new_grid_oy + + def updateSize(self, new_size_x, new_size_y): + new_costmap = np.full((new_size_x, new_size_y), self.default_value_, int) + + cell_size_x = min(new_size_x, self.size_x_) + cell_size_y = min(new_size_y, self.size_y_) + + for i in range(cell_size_x): + for j in range(cell_size_y): + new_costmap[i,j] = self.costmap_[i,j] + + self.costmap_ = new_costmap + self.size_x_ = new_size_x + self.size_y_ = new_size_y + + def resizeMap(self, minP, maxP): + # max_cell_size = self.getSize(minP[0], minP[1], maxP[0], maxP[1]) + iMinP = self.worldToMapNoBounds(minP[0], minP[1]) + iMaxP = self.worldToMapNoBounds(maxP[0], maxP[1]) + + if iMinP[0] > 0 or iMinP[1] > 0 or iMaxP[0] < self.size_x_ - 1 or iMaxP[1] < self.size_y_ - 1: + print ("reszie map error") + + new_grid_ox = self.origin_x_ + iMinP[0] * self.resolution_ + new_grid_oy = self.origin_y_ + iMinP[1] * self.resolution_ + + new_size_x = iMaxP[0] - iMinP[0] + new_size_y = iMaxP[1] - iMinP[1] + + self.updateSize(new_size_x,new_size_y) + self.updateOrigin(new_grid_ox, new_grid_oy) + + def resizeMapCheckPoint(self,world_pose): + map_pose = self.worldToMap(world_pose[0], world_pose[1]) + if not map_pose: + minP = self.mapToWorld(0,0) + maxP = self.mapToWorld(self.size_x_ - 1, self.size_y_ - 1) + minP = self.min_pose(world_pose, minP) + maxP = self.max_pose(world_pose, maxP) + self.resizeMap(minP, maxP) + + + def updateWeight(self, lo_max, lo_min): + for i in range(self.size_x_): + for j in range(self.size_y_): + if self.costmap_[i,j] > lo_max: + self.costmap_[i,j] = lo_max + if self.costmap_[i,j] < lo_min: + self.costmap_[i,j] = lo_min + + def convexFillCells(self, lineList): + lenLine = len(lineList) + if (lenLine < 2): + return + + for i in range(lenLine-1): + iLine = self.drawBresenham(lineList[i][0], lineList[i][1], lineList[i+1][0], lineList[i+1][1]) + iLenLine = len(iLine) + for j in range(iLenLine): + x = iLine[j][0] + y = iLine[j][1] + self.costmap_[x,y] += 1 + #def raytraceLine(self, x0, y0, x1, y1): + + def drawBresenham(self, x0, y0, x1, y1): + # x is main axes + if x0>x1: + x0,x1=x1,x0 + y0,y1=y1,y0 + + delta_x = x1 - x0 + delta_y = y1 - y0 + d = 0 + if delta_x == 0: + k = 999999999 + else: + k = float(delta_y) / delta_x + + + x = x0 + y = y0 + line = [] + if k > 1: + while True: + if y > y1: + break + line.append([x,y]) + #self.costmap_[x,y] += 1 + y = y + 1 + d = d + 1 / k + if d > 0.5: + x = x + 1 + d = d - 1 + elif k > 0: + while True: + if x > x1: + break + #self.costmap_[x,y] += 1 + line.append([x,y]) + x = x + 1 + d = d + k + if d > 0.5: + y = y + 1 + d = d - 1 + elif k > -1: + while True: + if x > x1: + break + line.append([x,y]) + #self.costmap_[x,y] += 1 + x = x + 1 + d = d - k + if d > 0.5: + y = y - 1 + d = d - 1 + else: + while True: + if y < y1: + break + line.append([x,y]) + #self.costmap_[x,y] += 1 + y = y - 1 + d = d - 1 / k + if d > 0.5: + x = x + 1 + d = d - 1 + return line + + def min_pose(self, p1, p2): + if p1[0] < p2[0]: + p2[0] = p1[0] + if p1[1] < p2[1]: + p2[1] = p1[1] + return p2 + + def max_pose(self, p1, p2): + if p1[0] > p2[0]: + p2[0] = p1[0] + if p1[1] > p2[1]: + p2[1] = p1[1] + return p2 + + def drawMap(self): + h = self.size_x_ + w = self.size_y_ + mapppm = np.zeros((h,w,3)) + + for i in range(h): + for j in range(w): + mapppm[i,j,0] = (self.costmap_[i,j]+ 15)*255/30 + mapppm[i,j,1] = (self.costmap_[i,j]+ 15)*255/30 + mapppm[i,j,2] = (self.costmap_[i,j]+ 15)*255/30 + # if self.map.costmap_[i,j] != 0: + # print self.map.costmap_[i,j] + #print self.map.costmap_ + cv2.imwrite('map_image.ppm',mapppm) + + diff --git a/exercises/ardrone_navigation/navigation/map/costmap_3d.py b/exercises/ardrone_navigation/navigation/map/costmap_3d.py new file mode 100644 index 000000000..82ed5bdca --- /dev/null +++ b/exercises/ardrone_navigation/navigation/map/costmap_3d.py @@ -0,0 +1,298 @@ +import numpy as np +import math +from math import cos,sin,tan,pow,pi +import cv2 +import matplotlib.pyplot as plt +from mpl_toolkits.mplot3d import Axes3D + +class costmap_3d: + def __init__(self,cells_size_x, cells_size_y, cells_sise_z, resolution, origin_x = 0, origin_y = 0,origin_z = 0, default_value = 0): + self.size_x_ = cells_size_x + self.size_y_ = cells_size_y + self.size_z_ = cells_sise_z + self.resolution_ = resolution # grid length + self.origin_x_ = origin_x + self.origin_y_ = origin_y + self.origin_z_ = origin_z + self.default_value_ = default_value + + self.initMaps(self.size_x_, self.size_y_, self.size_z_, self.default_value_) + + def initMaps(self, size_x, size_y, size_z, default_value): + if (size_x>0 and size_y>0 and size_z>0): + self.costmap_ = np.full((size_x, size_y, size_z), default_value, int) + else: + self.costmap_ = None + print ("map's size error") + + def reinitMap(self, size_x, size_y, size_z, resolution, origin_x, origin_y, origin_z): + self.size_x_ = size_x + self.size_y_ = size_y + self.size_z_ = size_z + self.resolution_ = resolution + self.origin_x_ = origin_x + self.origin_y_ = origin_y + self.origin_z_ = origin_z + + self.initMaps(self.size_x_, self.size_y_, self.size_z_, self.default_value_) + + def resetMap(self, x0, y0, z0, xn, yn, zn): + if xn > self.size_x_: + xn = self.size_x_ + if yn > self.size_y_: + yn = self.size_y_ + if zn > self.size_z_: + zn = self.size_z_ + lenx = xn - x0 + leny = yn - y0 + lenz = yn - z0 + for i in range(lenx): + for j in range(leny): + for k in range(lenz): + self.costmap_[i+x0,j+y0,k+z0] = self.default_value_ + + def cellDistance(self, world_dist): + cells_dist = max(0.0, math.ceil(world_dist / self.resolution_)) + return cells_dist + + def getMap(self): + return self.costmap_ + + def getCost(self, mx, my, mz): + if mx < self.size_x_ and my < self.size_y_ and mz < self.size_z_: + return self.costmap_[mx, my, mz] + else: + return None + + def setCost(self, mx, my, mz, cost): + if mx < self.size_x_ and my < self.size_y_ and mz < self.size_z_: + self.costmap_[mx, my, mz] = cost + else: + print("Over the border") + + def getSizeInCellsX(self): + return self.size_x_ + + def getSizeInCellsY(self): + return self.size_y_ + + def getSizeInCellsZ(self): + return self.size_z_ + + def getSizeInMetersX(self): + return (self.size_x_ - 1 + 0.5) * self.resolution_ + + def getSizeInMetersY(self): + return (self.size_y_ - 1 + 0.5) * self.resolution_ + + def getSizeInMetersZ(self): + return (self.size_z_ - 1 + 0.5) * self.resolution_ + + def getOriginX(self): + return self.origin_x_ + + def getOriginY(self): + return self.origin_y_ + + def getOriginZ(self): + return self.origin_z_ + + def getResolution(self): + return self.resolution_ + + def getSize(self, min_x, min_y, min_z, max_x, max_y, max_z): + cell_size_x = (int)((max_x - min_x) / self.resolution_) + cell_size_y = (int)((max_y - min_y) / self.resolution_) + cell_size_z = (int)((max_z - min_z) / self.resolution_) + return [cell_size_x, cell_size_y, cell_size_z] + + def mapToWorld(self, mx, my, mz): + wx = self.origin_x_ + (mx + 0.5) * self.resolution_ + wy = self.origin_y_ + (my + 0.5) * self.resolution_ + wz = self.origin_z_ + (mz + 0.5) * self.resolution_ + return [wx, wy, wz] + + + def worldToMap(self, wx, wy, wz): + if (wx < self.origin_x_ or wy < self.origin_y_ or wz < self.origin_z_): + return False + + mx = (int)((wx - self.origin_x_) / self.resolution_) + my = (int)((wy - self.origin_y_) / self.resolution_) + mz = (int)((wz - self.origin_z_) / self.resolution_) + + if (mx < self.size_x_ and my < self.size_y_ and mz < self.size_z_): + return [mx, my, mz] + else: + return False + + def worldToMapNoBounds(self, wx, wy, wz): + mx = (int)((wx - self.origin_x_) / self.resolution_) + my = (int)((wy - self.origin_y_) / self.resolution_) + mz = (int)((wz - self.origin_z_) / self.resolution_) + return [mx, my, mz] + + def worldToMapEnforceBounds(self, wx, wy, wz): + # Here we avoid doing any math to wx,wy before comparing them to + # the bounds, so their values can go out to the max and min values + # of double floating point. + if (wx < self.origin_x_): + mx = 0 + elif (wx > self.resolution_ * self.size_x_ + self.origin_x_): + mx = self.size_x_ - 1 + else: + mx = (int)((wx - self.origin_x_) / self.resolution_) + + if (wy < self.origin_y_): + my = 0 + elif (wy > self.resolution_ * self.size_y_ + self.origin_y_): + my = self.size_y_ - 1 + else: + my = (int)((wy - self.origin_y_) / self.resolution_) + + if (wz < self.origin_z_): + mz = 0 + elif (wz > self.resolution_ * self.size_z_ + self.origin_z_): + mz = self.size_z_ - 1 + else: + mz = (int)((wz - self.origin_z_) / self.resolution_) + return [mx, my, mz] + + + def updateOrigin(self, new_origin_x, new_origin_y, new_origin_z): + + cell_ox = int((new_origin_x - self.origin_x_) / self.resolution_) + cell_oy = int((new_origin_y - self.origin_y_) / self.resolution_) + cell_oz = int((new_origin_z - self.origin_z_) / self.resolution_) + + new_grid_ox = self.origin_x_ + cell_ox * self.resolution_ + new_grid_oy = self.origin_y_ + cell_oy * self.resolution_ + new_grid_oz = self.origin_z_ + cell_oz * self.resolution_ + + size_x = self.size_x_ + size_y = self.size_y_ + size_z = self.size_z_ + + lower_left_x = min(max(cell_ox, 0), size_x) + lower_left_y = min(max(cell_oy, 0), size_y) + lower_left_z = min(max(cell_oz, 0), size_z) + upper_right_x = min(max(cell_ox + size_x, 0), size_x) + upper_right_y = min(max(cell_oy + size_y, 0), size_y) + upper_right_z = min(max(cell_oz + size_z, 0), size_z) + + cell_size_x = upper_right_x - lower_left_x + cell_size_y = upper_right_y - lower_left_y + cell_size_z = upper_right_z - lower_left_z + + new_costmap = np.full((self.size_x_,self.size_y_,self.size_z_), self.default_value_, int) + + for i in range(cell_size_x): + for j in range(cell_size_y): + for k in range(cell_size_z): + tmp_x1 = lower_left_x - cell_ox + i + tmp_y1 = lower_left_y - cell_oy + j + tmp_z1 = lower_left_z - cell_oz + k + tmp_x2 = lower_left_x + i + tmp_y2 = lower_left_y + j + tmp_z2 = lower_left_z + k + new_costmap[tmp_x1, tmp_y1, tmp_z1] = self.costmap_[tmp_x2, tmp_y2, tmp_z2] + + self.costmap_ = new_costmap + self.origin_x_ = new_grid_ox + self.origin_y_ = new_grid_oy + self.origin_z_ = new_grid_oz + + def updateSize(self, new_size_x, new_size_y, new_size_z): + new_costmap = np.full((new_size_x, new_size_y, new_size_z), self.default_value_, int) + + cell_size_x = min(new_size_x, self.size_x_) + cell_size_y = min(new_size_y, self.size_y_) + cell_size_z = min(new_size_z, self.size_z_) + + for i in range(cell_size_x): + for j in range(cell_size_y): + for k in range(cell_size_z): + new_costmap[i,j,k] = self.costmap_[i,j,k] + + self.costmap_ = new_costmap + self.size_x_ = new_size_x + self.size_y_ = new_size_y + self.size_z_ = new_size_z + + def resizeMap(self, minP, maxP): + # max_cell_size = self.getSize(minP[0], minP[1], maxP[0], maxP[1]) + iMinP = self.worldToMapNoBounds(minP[0], minP[1], minP[2]) + iMaxP = self.worldToMapNoBounds(maxP[0], maxP[1], maxP[2]) + + if iMinP[0] > 0 or iMinP[1] > 0 or iMinP[2] > 0 or iMaxP[0] < self.size_x_ - 1 or iMaxP[1] < self.size_y_ - 1 or iMaxP[2] < self.size_z_ - 1: + print ("reszie map error") + + new_grid_ox = self.origin_x_ + iMinP[0] * self.resolution_ + new_grid_oy = self.origin_y_ + iMinP[1] * self.resolution_ + new_grid_oz = self.origin_z_ + iMinP[2] * self.resolution_ + + new_size_x = iMaxP[0] - iMinP[0] + new_size_y = iMaxP[1] - iMinP[1] + new_size_z = iMaxP[2] - iMinP[2] + + self.updateSize(new_size_x,new_size_y,new_size_z) + self.updateOrigin(new_grid_ox, new_grid_oy,new_grid_oz) + + def resizeMapCheckPoint(self,world_pose): + map_pose = self.worldToMap(world_pose[0], world_pose[1], world_pose[2]) + if not map_pose: + minP = self.mapToWorld(0,0,0) + maxP = self.mapToWorld(self.size_x_ - 1, self.size_y_ - 1, self.size_z_ - 1) + minP = self.min_pose(world_pose, minP) + maxP = self.max_pose(world_pose, maxP) + self.resizeMap(minP, maxP) + + def updateWeight(self, lo_max, lo_min): + for i in range(self.size_x_): + for j in range(self.size_y_): + for k in range(self.size_z_): + if self.costmap_[i,j,k] > lo_max: + self.costmap_[i,j,k] = lo_max + if self.costmap_[i,j,k] < lo_min: + self.costmap_[i,j,k] = lo_min + + def min_pose(self, p1, p2): + if p1[0] < p2[0]: + p2[0] = p1[0] + if p1[1] < p2[1]: + p2[1] = p1[1] + if p1[2] < p2[2]: + p2[2] = p1[2] + return p2 + + def max_pose(self, p1, p2): + if p1[0] > p2[0]: + p2[0] = p1[0] + if p1[1] > p2[1]: + p2[1] = p1[1] + if p1[2] > p2[2]: + p2[2] = p1[2] + return p2 + + def drawMap(self): + lx = self.size_x_ + ly = self.size_y_ + lz = self.size_z_ + + x = np.array([]) + y = np.array([]) + z = np.array([]) + + for i in range(self.size_x_): + for j in range(self.size_y_): + for k in range(self.size_z_): + if self.costmap_[i,j,k] < -5: + x = np.append(x,i) + y = np.append(y,j) + z = np.append(z,k) + + ax = plt.subplot(111, projection='3d') + ax.scatter(x, y, z, c='b') + plt.savefig('map_image.png') + #plt.show() + diff --git a/exercises/ardrone_navigation/navigation/map/occGridMap.py b/exercises/ardrone_navigation/navigation/map/occGridMap.py new file mode 100644 index 000000000..41ae88267 --- /dev/null +++ b/exercises/ardrone_navigation/navigation/map/occGridMap.py @@ -0,0 +1,135 @@ +import numpy as np +import math +from math import cos,sin,tan,pow,pi +from costmap_2d import costmap_2d +from costmap_3d import costmap_3d +import matplotlib.pyplot as plt +from mpl_toolkits.mplot3d import Axes3D +import cv2 + +class occGridMap: + def __init__(self, lo_occ = -10, lo_free = 5,lo_max = 15,lo_min = -15, dimension = 2): + self.dimension = dimension + self.lo_occ = lo_occ + self.lo_free = lo_free + self.lo_max = lo_max + self.lo_min = lo_min + if self.dimension == 2: + self.map = costmap_2d(100, 100, 0.2) + self.m_laserpose = [] + self.m_laserpose.append( [2,0,0] ) + self.m_laserpose.append( [-1.9,0,pi] ) + self.m_laserpose.append( [0.3,-1,-pi/2] ) + self.m_laserMaxRange = 11 + self.m_usableRange = 9 + elif self.dimension == 3: + self.map = costmap_3d(100, 100, 100, 0.2, origin_x = -10, origin_y = -10, origin_z = 0) + #cells_size_x, cells_size_y, cells_sise_z, resolution, origin_x = 0, origin_y = 0,origin_z = 0, default_value = 0 + + def registerScan(self, pose, laserNum, laserData): + laserPose = [0,0,0] + laserPose[0] = pose[0] + cos(pose[2]) * self.m_laserpose[laserNum][0] - sin(pose[2]) * self.m_laserpose[laserNum][1] + laserPose[1] = pose[1] + sin(pose[2]) * self.m_laserpose[laserNum][0] + cos(pose[2]) * self.m_laserpose[laserNum][1] + laserPose[2] = pose[2] + self.m_laserpose[laserNum][2] + + self.computerActiveArea(laserPose, laserData) + + P0 = self.map.worldToMapEnforceBounds(laserPose[0],laserPose[1]) + #print ("P0 = ", P0, laserPose) + + for i in range(180): + r = laserData[i][0] + if r > self.m_laserMaxRange: + continue + if r > self.m_usableRange: + r = self.m_usableRange + phit = [0,0] + angle = math.radians(180 - i) - pi/2 + phit[0] = laserPose[0] + r * cos(laserPose[2] - angle) + phit[1] = laserPose[1] + r * sin(laserPose[2] - angle) + P1 = self.map.worldToMapEnforceBounds(phit[0],phit[1]) + #print ("P1 = ", P1, phit, i, "P0 = ", P0, laserPose) + line = self.map.drawBresenham(P0[0],P0[1],P1[0],P1[1]) + #print ("line = " , line ) + lenLine = len(line) + for i in range(lenLine): + x = line[i][0] + y = line[i][1] + self.map.costmap_[x,y] += self.lo_free + + if r < self.m_usableRange: + self.map.costmap_[P1[0],P1[1]] += self.lo_occ + + self.map.updateWeight(self.lo_max, self.lo_min) + + + def computerActiveArea(self, laserPose, laserData): + #P0 = self.worldToMapNoBounds(laserPose[0], laserPose[1]) + + minP = self.map.mapToWorld(0,0) + maxP = self.map.mapToWorld(self.map.size_x_ - 1, self.map.size_y_ - 1) + + minP = self.map.min_pose(laserPose, minP) + maxP = self.map.max_pose(laserPose, maxP) + + # laser angle 0~180 + for i in range(180): + r = laserData[i][0] + if r > self.m_laserMaxRange: + continue + if r > self.m_usableRange: + r = self.m_usableRange + phit = [0,0] + angle = math.radians(i) - pi/2 + phit[0] = laserPose[0] + r * cos(laserPose[2] - angle) + phit[1] = laserPose[1] + r * sin(laserPose[2] - angle) + #print ("P1 = ", phit, i) + #P1 = self.worldToMapNoBounds(phit[0],phit[1]) + minP = self.map.min_pose(phit, minP) + maxP = self.map.max_pose(phit, maxP) + + + iMinP = self.map.worldToMapNoBounds(minP[0], minP[1]) + iMaxP = self.map.worldToMapNoBounds(maxP[0], maxP[1]) + if iMinP != [0, 0] or iMaxP != [self.map.size_x_ - 1, self.map.size_y_ - 1]: + minP = [minP[0] - 1,minP[1] - 1] + maxP = [maxP[0] + 1,maxP[1] + 1] + self.map.resizeMap(minP, maxP) + #print ("minp = ",minP, "maxP = ", maxP ) + + + def drawMap(self): + if self.dimension == 2: + h = self.map.size_x_ + w = self.map.size_y_ + mapppm = np.zeros((h,w,3)) + + for i in range(h): + for j in range(w): + mapppm[i,j,0] = (self.map.costmap_[i,j]+ 15)*255/30 + mapppm[i,j,1] = (self.map.costmap_[i,j]+ 15)*255/30 + mapppm[i,j,2] = (self.map.costmap_[i,j]+ 15)*255/30 + # if self.map.costmap_[i,j] != 0: + # print self.map.costmap_[i,j] + #print self.map.costmap_ + cv2.imwrite('map_image.ppm',mapppm) + elif self.dimension == 3: + lx = self.map.size_x_ + ly = self.map.size_y_ + lz = self.map.size_z_ + + x = np.array([]) + y = np.array([]) + z = np.array([]) + + for i in range(lx): + for j in range(ly): + for k in range(lz): + if self.map.costmap_[i,j,k] > 0: + x = np.append(x,i) + y = np.append(y,j) + z = np.append(z,k) + + ax = plt.subplot(111, projection='3d') + ax.scatter(x, y, z, c='b') + plt.savefig('map_image.png') diff --git a/exercises/ardrone_navigation/navigation/navigation.py b/exercises/ardrone_navigation/navigation/navigation.py new file mode 100644 index 000000000..eae323270 --- /dev/null +++ b/exercises/ardrone_navigation/navigation/navigation.py @@ -0,0 +1,341 @@ + +from ompl_planner.ompl_planner import ompl_planner +from control.noHolomonicControl import noHolomonicControl +from control.ardroneControl import ardroneControl +from path_processing.smoothPath import smooth +from map.occGridMap import occGridMap,costmap_2d,costmap_3d + +import cv2 +import numpy as np +import matplotlib.pyplot as plt +import math +from math import cos,sin,tan,pow,pi,sqrt +import matplotlib.pyplot as plt +from mpl_toolkits.mplot3d import Axes3D + +class navigation: + def __init__(self, files, origin, resolution, dimension = 2): + + self.smooth = smooth() + #self.occGridMap = occGridMap() + + self.dimension = dimension + origin_x = origin[0] + origin_y = origin[1] + if dimension == 3: + origin_z = origin[2] + + if dimension == 2: + self.control = noHolomonicControl() + if files: + self.have_map = True + self.init_img = cv2.imread(files) + size = self.init_img.shape + self.gridMap = costmap_2d(size[0],size[1],resolution, origin_x, origin_y) + for i in range(size[0]): + for j in range(size[1]): + data = (np.sum(self.init_img[i,j]))/3 + self.gridMap.setCost(i,j,data) + else: + self.have_map = False + self.gridMap = costmap_2d(100,100,resolution, origin_x, origin_y) + + + elif dimension == 3: + self.control = ardroneControl() + self.have_map = False + self.gridMap = costmap_3d(100, 100, 50, 0.2, origin_x = -10, origin_y = -10, origin_z = 0) + #cells_size_x, cells_size_y, cells_sise_z, resolution, origin_x = 0, origin_y = 0,origin_z = 0, default_value = 0 + + self.getTarget = False + + def update_map_autopark(self,car_pose,target_pose): + #car [6,3] + #target [8,5] + p1 = self.move_pose(car_pose,[12,-3]) + p2 = self.move_pose(car_pose,[-12,-3]) + p3 = self.move_pose(car_pose,[12,3]) + p4 = self.move_pose(car_pose,[-12,3]) + p5 = self.move_pose(target_pose,[4,2.5]) + p6 = self.move_pose(target_pose,[-4,2.5]) + + self.gridMap.resizeMapCheckPoint(p1) + self.gridMap.resizeMapCheckPoint(p2) + self.gridMap.resizeMapCheckPoint(p3) + self.gridMap.resizeMapCheckPoint(p4) + self.gridMap.resizeMapCheckPoint(p5) + self.gridMap.resizeMapCheckPoint(p6) + + m_p1 = self.gridMap.worldToMapEnforceBounds(p1[0],p1[1]) + m_p2 = self.gridMap.worldToMapEnforceBounds(p2[0],p2[1]) + m_p3 = self.gridMap.worldToMapEnforceBounds(p3[0],p3[1]) + m_p4 = self.gridMap.worldToMapEnforceBounds(p4[0],p4[1]) + m_p5 = self.gridMap.worldToMapEnforceBounds(p5[0],p5[1]) + m_p6 = self.gridMap.worldToMapEnforceBounds(p6[0],p6[1]) + + print ("getSizeInCellsX",self.gridMap.getSizeInCellsX()) + print ("getSizeInCellsY",self.gridMap.getSizeInCellsY()) + print ("getOriginX",self.gridMap.getOriginX()) + print ("getOriginY",self.gridMap.getOriginY()) + print ("p1",p1,m_p1) + print ("p2",p2,m_p2) + print ("p3",p3,m_p3) + print ("p4",p4,m_p4) + print ("p5",p5,m_p5) + print ("p6",p6,m_p6) + h = self.gridMap.size_x_ + w = self.gridMap.size_y_ + for i in range(h): + for j in range(w): + self.gridMap.setCost(i,j,-10) + + if m_p1[1] == m_p2[1]: + #scene like autopark.world + print ("update map") + num_x_1 = m_p1[0] - m_p2[0] + num_y_1 = m_p1[1] - m_p3[1] + for i in range(num_x_1): + for j in range(num_y_1): + self.gridMap.setCost(i+m_p4[0],j+m_p4[1],15) + num_x_2 = m_p5[0] - m_p6[0] + num_y_2 = m_p3[1] - m_p5[1] + for i in range(num_x_2): + for j in range(num_y_2): + self.gridMap.setCost(i+m_p6[0],j+m_p6[1],15) + self.gridMap.drawMap() + + def move_pose(self,pose,move): + assert len(pose) == 3 + assert len(move) == 2 + dx = move[1]*sin(pose[2]) + move[0]*cos(pose[2]) + dy = -move[1]*cos(pose[2]) + move[0]*sin(pose[2]) + x = pose[0] + dx + y = pose[1] + dy + return [x,y] + + def path_planning_2d(self,current_pose,goal_pose): + startX = current_pose[0] + startY = current_pose[1] + startYaw = current_pose[2] + goalX = goal_pose[0] + goalY = goal_pose[1] + goalYaw = goal_pose[2] + print ("pose",startX, startY, startYaw, goalX, goalY, goalYaw) + + start_pose_map = self.gridMap.worldToMap(startX,startY) + goal_pose_map = self.gridMap.worldToMap(goalX, goalY) + + ompl_control = True + ompl_sol = ompl_planner(self.gridMap, startX, startY, startYaw, goalX, goalY, goalYaw, "rrt", ompl_control, False) + path_list = ompl_sol.omplRunOnce(50) + #print self.pathlist + if path_list : + if ompl_control: + path_list = self.path_smooth(path_list,current_pose) + + self.control.setPath(path_list) + return True + else: + self.control.setControlYaw(False) + self.control.setPath(path_list) + return True + else: + return False + + def path_smooth_2d(self,path_list,current_pose): + plt.figure(1) + plt.plot(path_list[0], path_list[1]) + num = len(path_list[0]) + for i in range(num): + x1 = path_list[0][i] + y1 = path_list[1][i] + yaw = path_list[2][i] + x2 = x1 + math.cos(yaw) + y2 = y1 + math.sin(yaw) + plt.plot([x1,x2],[y1,y2]) + plt.savefig("figure1.jpg") + self.smooth.setPath(path_list) + path_list = self.smooth.Floyd(path_list) + path_list = self.smooth.checkYaw(path_list, current_pose) + + plt.figure(2) + plt.plot(path_list[0], path_list[1]) + + num = len(path_list[0]) + for i in range(num): + x1 = path_list[0][i] + y1 = path_list[1][i] + yaw = path_list[2][i] + x2 = x1 + math.cos(yaw) + y2 = y1 + math.sin(yaw) + plt.plot([x1,x2],[y1,y2]) + plt.savefig("figure2.jpg") + + return path_list + + def path_following_2d(self, current_pose): + if self.getTarget: + print "get Target" + print self.control.getPath() + return [0,0] + + Pose = [0,0,0] + Pose[0] = current_pose[0] + Pose[1] = current_pose[1] + Pose[2] = current_pose[2] + + data = self.control.control(Pose,2) + self.getTarget = self.control.isGetTarget() + return data + + def path_check(self,path_smooth, costmap): + print ('todo') + + def update_map_laser_2d(self, laser, id, pose): + self.occGridMap.registerScan(pose,id,laser) + self.occGridMap.drawMap() + + def update_map_3d(self): + lx = self.gridMap.size_x_ + ly = self.gridMap.size_y_ + lz = self.gridMap.size_z_ + for i in range(lx): + for j in range(ly): + for k in range(lz): + self.gridMap.setCost(i,j,k,15) + + pillar_box = [0.2, 0.2, 4.0] + pillar_swell = [1, 1, 0] + + floor_box = [5,5,0.2] + floor_swell = [1,1,1] + + wall1_box = [5,0.2,5] + wall2_box = [0.2,5,5] + wall_swell = [1,1,0] + + # pillar_pose = [] + # floor_pose = [] + # wall1_pose = [] + # wall2_pose = [] + + for i in range(5): + for j in range(5): + x = i*5-10 + y = j*5-10 + z = 2 + #pillar_pose.append([x,y,z]) + self.add_box_map(pillar_box,[x,y,z],pillar_swell) + + for i in range(4): + for j in range(4): + x = i*5-7.5 + y = j*5-7.5 + z = 4 + if (x==2.5 and y==2.5) or (x==-7.5 and y==-2.5): + print ('no floor') + else: + #floor_pose.append([x,y,z]) + self.add_box_map(floor_box,[x,y,z],floor_swell) + + for i in range(4): + for j in range(2): + x = i*5-7.5 + y = j*20-10 + z = 6.5 + self.add_box_map(wall1_box,[x,y,z],wall_swell) + + for i in range(4): + for j in range(2): + y = i*5-7.5 + x = j*20-10 + z = 6.5 + self.add_box_map(wall2_box,[x,y,z],wall_swell) + + wall1_pose = [[7.5,5,6.5],[-7.5,0,6.5]] + wall2_pose = [[5,2.5,6.5],[5,-2.5,6.5],[0,7.5,6.5],[0,2.5,6.5],[0,-2.5,6.5],[-5,-7.5,6.5]] + + l1 = len(wall1_pose) + l2 = len(wall2_pose) + for i in range(l1): + self.add_box_map(wall1_box,wall1_pose[i],wall_swell) + for i in range(l2): + self.add_box_map(wall2_box,wall2_pose[i],wall_swell) + + self.gridMap.drawMap() + + def add_box_map(self, box, pose, swell): + box_sum = [] + for i,j in zip(box, swell): + summ = i+j + box_sum.append(summ) + + start = [] + for i,j in zip(box_sum, pose): + tmp = j - i/2 + start.append(tmp) + + cell_size = self.gridMap.getSize(0,0,0,box_sum[0],box_sum[1],box_sum[2]) + cell_start = self.gridMap.worldToMapEnforceBounds(start[0],start[1],start[2]) + + for i in range(cell_size[0]): + for j in range(cell_size[1]): + for k in range(cell_size[2]): + self.gridMap.setCost(i+cell_start[0], j+cell_start[1], k+cell_start[2], -10) + + + def path_planning_3d(self,current_pose,goal_pose): + startX = current_pose[0] + startY = current_pose[1] + startZ = current_pose[2] + goalX = goal_pose[0] + goalY = goal_pose[1] + goalZ = goal_pose[2] + print ("pose",startX, startY, startZ, goalX, goalY, goalZ) + #bitstar rrtstar fmtstar + ompl_sol = ompl_planner(self.gridMap, 3, current_pose, 0, goal_pose, 0, "fmtstar", False, False) + path_list = ompl_sol.omplRunOnce(10) + #print path_list + if path_list : + self.control.restart() + self.control.setPath(path_list) + tmpmap = self.gridMap.getMap() + lx = self.gridMap.size_x_ + ly = self.gridMap.size_y_ + lz = self.gridMap.size_z_ + + x = [] + y = [] + z = [] + d = 0 + for i in range(lx): + for j in range(ly): + for k in range(lz): + if tmpmap[i,j,k] < -5: + tmp = self.gridMap.mapToWorld(i,j,k) + d = d+1 + if ( d ==10 ): + x.append(tmp[0]) + y.append(tmp[1]) + z.append(tmp[2]) + d = 0 + + x2 = path_list[0] + y2 = path_list[1] + z2 = path_list[2] + ax = plt.subplot(111, projection='3d') + ax.scatter(x, y, z, c='b') + ax.scatter(x2, y2, z2, c='r') + plt.show() + return True + else: + return False + + def path_following_3d(self, current_pose): + if self.getTarget: + print "get Target" + print self.control.getPath() + + data = self.control.control(current_pose) + self.getTarget = self.control.isGetTarget() + return [self.getTarget,data] diff --git a/exercises/ardrone_navigation/navigation/ompl_planner/Point2DPlanning.py b/exercises/ardrone_navigation/navigation/ompl_planner/Point2DPlanning.py new file mode 100755 index 000000000..083827ba6 --- /dev/null +++ b/exercises/ardrone_navigation/navigation/ompl_planner/Point2DPlanning.py @@ -0,0 +1,161 @@ +#!/usr/bin/env python + +###################################################################### +# Software License Agreement (BSD License) +# +# Copyright (c) 2010, Rice University +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided +# with the distribution. +# * Neither the name of the Rice University nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +###################################################################### + +# Author: Ioan Sucan, Mark Moll + + +from ompl import util as ou +from ompl import base as ob +from ompl import geometric as og +from os.path import abspath, dirname, join +import sys +from functools import partial + +class Plane2DEnvironment: + def __init__(self, ppm_file): + self.ppm_ = ou.PPM() + self.ppm_.loadFile(ppm_file) + self.space = ob.RealVectorStateSpace() + self.space.addDimension(0.0, self.ppm_.getWidth()) + self.space.addDimension(0.0, self.ppm_.getHeight()) + self.maxWidth_ = self.ppm_.getWidth() - 1 + self.maxHeight_ = self.ppm_.getHeight() - 1 + + self.si = ob.SpaceInformation(self.space) + + # set state validity checking for this space + self.si.setStateValidityChecker(ob.StateValidityCheckerFn( + partial(Plane2DEnvironment.isStateValid, self))) + self.si.setup() + self.si.setStateValidityCheckingResolution(1.0 / self.space.getMaximumExtent()) + #self.ss_.setPlanner(og.RRTConnect(self.ss_.getSpaceInformation())) + + def plan(self, start_row, start_col, goal_row, goal_col,plannerType,runtime): + if not self.si: + return False + start = ob.State(self.space) + start()[0] = start_row + start()[1] = start_col + goal = ob.State(self.space) + goal()[0] = goal_row + goal()[1] = goal_col + # Create a problem instance + + self.pdef = ob.ProblemDefinition(self.si) + # Set the start and goal states + self.pdef.setStartAndGoalStates(start, goal) + + self.planner = self.allocatePlanner(self.si, plannerType) + self.planner.setProblemDefinition(self.pdef) + # perform setup steps for the planner + self.planner.setup() + # print the settings for this space + print(self.si.settings()) + # generate a few solutions; all will be added to the goal + for i in range(5): + self.solved = self.planner.solve(runtime) + p = self.pdef.getSolutionPath() + print("Found solution:\n%s" % p) + ns = self.planner.getProblemDefinition().getSolutionCount() + print("Found %d solutions" % ns) + + if self.solved: + # get the goal representation from the problem definition (not the same as the goal state) + # and inquire about the found path + path = self.pdef.getSolutionPath() + print("Found solution:\n%s" % path) + return True + else: + print("No solution found") + return False + + + def allocatePlanner(self,si,plannerType): + if plannerType.lower() == "bitstar": + return og.BITstar(si) + elif plannerType.lower() == "fmtstar": + return og.FMT(si) + elif plannerType.lower() == "informedrrtstar": + return og.InformedRRTstar(si) + elif plannerType.lower() == "prmstar": + return og.PRMstar(si) + elif plannerType.lower() == "rrtstar": + return og.RRTstar(si) + elif plannerType.lower() == "sorrtstar": + return og.SORRTstar(si) + else: + OMPL_ERROR("Planner-type is not implemented in allocation function."); + + + def recordSolution(self): + if not self.si: + return + p = self.pdef.getSolutionPath() + print("Found solution:\n%s" % p) + p.interpolate() + for i in range(p.getStateCount()): + w = min(self.maxWidth_, int(p.getState(i)[0])) + h = min(self.maxHeight_, int(p.getState(i)[1])) + c = self.ppm_.getPixel(h, w) + c.red = 255 + c.green = 0 + c.blue = 0 + + def getPath(self): + # if not self.si: + # return + p = self.pdef.getSolutionPath() + p.interpolate() + pathlist = [[] for i in range(2)] + for i in range(p.getStateCount()): + w = min(self.maxWidth_, int(p.getState(i)[0])) + h = min(self.maxHeight_, int(p.getState(i)[1])) + pathlist[0].append(w) + pathlist[1].append(h) + return pathlist + + def save(self, filename): + if not self.si: + return + self.ppm_.saveFile(filename) + + def isStateValid(self, state): + w = min(int(state[0]), self.maxWidth_) + h = min(int(state[1]), self.maxHeight_) + + c = self.ppm_.getPixel(h, w) + return c.red > 127 and c.green > 127 and c.blue > 127 + diff --git a/exercises/ardrone_navigation/navigation/ompl_planner/RigidBodyPlanning.py b/exercises/ardrone_navigation/navigation/ompl_planner/RigidBodyPlanning.py new file mode 100755 index 000000000..b0233e516 --- /dev/null +++ b/exercises/ardrone_navigation/navigation/ompl_planner/RigidBodyPlanning.py @@ -0,0 +1,308 @@ +#!/usr/bin/env python + +###################################################################### +# Software License Agreement (BSD License) +# +# Copyright (c) 2010, Rice University +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided +# with the distribution. +# * Neither the name of the Rice University nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +###################################################################### + +# Author: Mark Moll + +from math import sin, cos +from functools import partial + +from ompl import util as ou +from ompl import base as ob +from ompl import control as oc +from ompl import geometric as og + +from os.path import abspath, dirname, join +import numpy as np +import math + +import matplotlib.pyplot as plt +## @cond IGNORE +# a decomposition is only needed for SyclopRRT and SyclopEST +class RigidBodyPlanning: + def __init__(self, costMap, dimension, start, start_yaw, goal, goal_yaw, plannerType): + self.costMap = costMap + self.dimension = dimension + self.start_x = start[0] + self.start_y = start[1] + self.start_yaw = start_yaw + self.goal_x = goal[0] + self.goal_y = goal[1] + self.goal_yaw = goal_yaw + if self.dimension == 3: + self.start_z = start[2] + self.goal_z = goal[2] + self.plannerType = plannerType + self.simpleSetupControl() + + + def isStateValid(self, state): + # perform collision checking or check if other constraints are + # satisfied + if not self.costMap: + wx = state.getX() + wy = state.getY() + tmp = True + if wy < -4 and wy > 5: + tmp = False + elif wy < -0.5: + if wx > 12 or wx < 5.5: + tmp = False + return tmp + + if self.dimension == 2: + wx = state.getX() + wy = state.getY() + + mPoint = self.costMap.worldToMapEnforceBounds(wx, wy) + cost = self.costMap.getCost(mPoint[0],mPoint[1]) + if cost < -5: + tmp = False + else: + tmp = True + + elif self.dimension == 3: + wx = state.getX() + wy = state.getY() + wz = state.getZ() + mPoint = self.costMap.worldToMapEnforceBounds(wx, wy, wz) + cost = self.costMap.getCost(mPoint[0],mPoint[1],mPoint[2]) + if cost < -5: + tmp = False + else: + tmp = True + return tmp + + def simpleSetupControl(self): + if self.dimension == 2: + self.setSpace_2d() + self.setProblem_2d() + self.setPlanner_2d() + elif self.dimension == 3: + self.setSpace_3d() + self.setProblem_3d() + self.setPlanner_3d() + + def setSpace_2d(self): + # construct the state space we are planning in + self.space = ob.SE2StateSpace() + + # set the bounds for the R^2 part of SE(2) + self.bounds = ob.RealVectorBounds(2) + if not self.costMap: + self.bounds.setLow(-8) + self.bounds.setHigh(20) + else: + ox = self.costMap.getOriginX() + oy = self.costMap.getOriginY() + size_x = self.costMap.getSizeInMetersX() + size_y = self.costMap.getSizeInMetersY() + low = min(ox, oy) + high = max(ox+size_x, oy+size_y) + print ("low",low) + print ("high",high) + self.bounds.setLow(low) + self.bounds.setHigh(high) + self.space.setBounds(self.bounds) + + # define a simple setup class + self.ss = og.SimpleSetup(self.space) + self.ss.setStateValidityChecker(ob.StateValidityCheckerFn(self.isStateValid)) + + def setProblem_2d(self): + # create a start state + start = ob.State(self.space) + start().setX(self.start_x) + start().setY(self.start_y) + start().setYaw(self.start_yaw) + + # create a goal state + goal = ob.State(self.space) + goal().setX(self.goal_x) + goal().setY(self.goal_y) + goal().setYaw(self.goal_yaw) + + # set the start and goal states + self.ss.setStartAndGoalStates(start, goal, 0.05) + + def setPlanner_2d(self): + self.si = self.ss.getSpaceInformation() + if self.plannerType.lower() == "bitstar": + planner = og.BITstar(self.si) + elif self.plannerType.lower() == "fmtstar": + planner = og.FMT(self.si) + elif self.plannerType.lower() == "informedrrtstar": + planner = og.InformedRRTstar(self.si) + elif self.plannerType.lower() == "prmstar": + planner = og.PRMstar(self.si) + elif self.plannerType.lower() == "rrtstar": + planner = og.RRTstar(self.si) + elif self.plannerType.lower() == "sorrtstar": + planner = og.SORRTstar(self.si) + else: + OMPL_ERROR("Planner-type is not implemented in allocation function.") + planner = og.RRTstar(self.si) + self.ss.setPlanner(planner) + + def setSpace_3d(self): + # construct the state space we are planning in + self.space = ob.SE3StateSpace() + + # set the bounds for the R^2 part of SE(2) + self.bounds = ob.RealVectorBounds(3) + if not self.costMap: + self.bounds.setLow(0) + self.bounds.setHigh(20) + else: + ox = self.costMap.getOriginX() + oy = self.costMap.getOriginY() + oz = self.costMap.getOriginZ() + size_x = self.costMap.getSizeInMetersX() + size_y = self.costMap.getSizeInMetersY() + size_z = self.costMap.getSizeInMetersZ() + print ('o',ox,oy,oz) + print ('size',size_x,size_y,size_z) + low = min(ox, oy, oz) + high = max(ox+size_x, oy+size_y, oz+size_z) + print ("low",low) + print ("high",high) + self.bounds.setLow(0,ox) + self.bounds.setLow(1,oy) + self.bounds.setLow(2,oz) + self.bounds.setHigh(0,ox+size_x) + self.bounds.setHigh(1,oy+size_y) + self.bounds.setHigh(2,oz+size_z) + self.space.setBounds(self.bounds) + + # define a simple setup class + self.ss = og.SimpleSetup(self.space) + self.ss.setStateValidityChecker(ob.StateValidityCheckerFn(self.isStateValid)) + + def setProblem_3d(self): + # create a start state + start = ob.State(self.space) + start().setX(self.start_x) + start().setY(self.start_y) + start().setZ(self.start_z) + #start().setYaw(self.start_yaw) + start().rotation().setIdentity() + + # create a goal state + goal = ob.State(self.space) + goal().setX(self.goal_x) + goal().setY(self.goal_y) + goal().setZ(self.goal_z) + #goal().setYaw(self.goal_yaw) + goal().rotation().setIdentity() + + # set the start and goal states + self.ss.setStartAndGoalStates(start, goal, 0.05) + + def setPlanner_3d(self): + self.si = self.ss.getSpaceInformation() + if self.plannerType.lower() == "bitstar": + planner = og.BITstar(self.si) + elif self.plannerType.lower() == "fmtstar": + planner = og.FMT(self.si) + elif self.plannerType.lower() == "informedrrtstar": + planner = og.InformedRRTstar(self.si) + elif self.plannerType.lower() == "prmstar": + planner = og.PRMstar(self.si) + elif self.plannerType.lower() == "rrtstar": + planner = og.RRTstar(self.si) + elif self.plannerType.lower() == "sorrtstar": + planner = og.SORRTstar(self.si) + else: + print("Planner-type is not implemented in allocation function.") + planner = og.RRTstar(self.si) + self.ss.setPlanner(planner) + + def solve(self, runtime = None ): + if not runtime: + if self.dimension == 2: + runtime = 1 + elif self.dimension == 3: + runtime = 5 + # attempt to solve the problem + solved = self.ss.solve(runtime) + + if solved: + self.ss.simplifySolution() + # print the path to screen + p = self.ss.getSolutionPath() + print("Found solution:\n%s" % self.ss.getSolutionPath().printAsMatrix()) + #p.interpolate() + if self.dimension == 2: + pathlist = [[] for i in range(3)] + + for i in range(p.getStateCount()): + x = p.getState(i).getX() + y = p.getState(i).getY() + yaw = p.getState(i).getYaw() + pathlist[0].append(x) + pathlist[1].append(y) + pathlist[2].append(yaw) + return pathlist + elif self.dimension == 3: + pathlist = [[] for i in range(3)] + for i in range(p.getStateCount()): + x = p.getState(i).getX() + y = p.getState(i).getY() + z = p.getState(i).getZ() + pathlist[0].append(x) + pathlist[1].append(y) + pathlist[2].append(z) + return pathlist + else: + print ("can't find path") + return 0 + + +if __name__ == "__main__": + + planner = RigidBodyPlanning(None, 3, [ 12.40232229232788, 4.982222080230713, 6.667441368103027],0,[ 10, 10, 1],0, 'rrtstar') + pathlist = planner.solve() + + plt.figure(1) + plt.plot(pathlist[0], pathlist[1]) + num = len(pathlist[0]) + for i in range(num): + x1 = pathlist[0][i] + y1 = pathlist[1][i] + yaw = pathlist[2][i] + x2 = x1 + 0.5*cos(yaw) + y2 = y1 + 0.5*sin(yaw) + plt.plot([x1,x2],[y1,y2]) + plt.show() \ No newline at end of file diff --git a/exercises/ardrone_navigation/navigation/ompl_planner/RigidBodyPlanningWithControls.py b/exercises/ardrone_navigation/navigation/ompl_planner/RigidBodyPlanningWithControls.py new file mode 100755 index 000000000..926609b97 --- /dev/null +++ b/exercises/ardrone_navigation/navigation/ompl_planner/RigidBodyPlanningWithControls.py @@ -0,0 +1,256 @@ +#!/usr/bin/env python + +###################################################################### +# Software License Agreement (BSD License) +# +# Copyright (c) 2010, Rice University +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided +# with the distribution. +# * Neither the name of the Rice University nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +###################################################################### + +# Author: Mark Moll + +from math import sin, cos +from functools import partial + +from ompl import util as ou +from ompl import base as ob +from ompl import control as oc +from ompl import geometric as og + +from os.path import abspath, dirname, join +import numpy as np +import math + +import matplotlib.pyplot as plt +## @cond IGNORE +# a decomposition is only needed for SyclopRRT and SyclopEST +class RigidBodyPlanningWithControls: + def __init__(self, costMap, dimension, start, start_yaw, goal, goal_yaw, plannerType): + self.costMap = costMap + self.start_x = start[0] + self.start_y = start[1] + self.start_yaw = start_yaw + self.goal_x = goal[0] + self.goal_y = goal[1] + self.goal_yaw = goal_yaw + self.plannerType = plannerType + self.simpleSetupControl() + + def propagate(self, start, control, duration, state): + state.setX( start.getX() + control[0] * duration * math.cos(start.getYaw()) ) + state.setY( start.getY() + control[0] * duration * math.sin(start.getYaw()) ) + state.setYaw(start.getYaw() + control[1] * duration) + + def isStateValid(self, spaceInformation, state): + # perform collision checking or check if other constraints are + # satisfied + wx = state.getX() + wy = state.getY() + if not self.costMap: + tmp = True + if wy < -4 and wy > 5: + tmp = False + elif wy < -0.5: + if wx > 12 or wx < 5.5: + tmp = False + else: + mPoint = self.costMap.worldToMap(wx, wy) + cost = self.costMap.getCost(mPoint[0],mPoint[1]) + if cost < -5: + tmp = False + else: + tmp = True + return spaceInformation.satisfiesBounds(state) and tmp + + def simpleSetupControl(self): + self.setSpace() + self.setProblem() + self.setPlanner() + + def setSpace(self): + # construct the state space we are planning in + self.space = ob.SE2StateSpace() + # set the bounds for the R^2 part of SE(2) + self.bounds = ob.RealVectorBounds(2) + if not self.costMap: + self.bounds.setLow(-8) + self.bounds.setHigh(20) + else: + ox = self.costMap.getOriginX() + oy = self.costMap.getOriginY() + size_x = self.costMap.getSizeInMetersX() + size_y = self.costMap.getSizeInMetersY() + low = min(ox, oy) + high = max(ox+size_x, oy+size_y) + print ("low",low) + print ("high",high) + #self.bounds.setLow(low) + #self.bounds.setHigh(high) + self.bounds.setLow(0,ox) + self.bounds.setHigh(0,ox+size_x) + self.bounds.setLow(1,oy) + self.bounds.setHigh(1,oy+size_y) + + self.space.setBounds(self.bounds) + + # create a control space + self.cspace = oc.RealVectorControlSpace(self.space, 2) + # set the bounds for the control space + cbounds = ob.RealVectorBounds(2) + cbounds.setLow(0,-1.5) + cbounds.setHigh(0,1.5) + cbounds.setLow(1,-3) + cbounds.setHigh(1,3) + self.cspace.setBounds(cbounds) + + # define a simple setup class + self.ss = oc.SimpleSetup(self.cspace) + self.ss.setStateValidityChecker(ob.StateValidityCheckerFn(partial(self.isStateValid, self.ss.getSpaceInformation()))) + self.ss.setStatePropagator(oc.StatePropagatorFn(self.propagate)) + + def setProblem(self): + # create a start state + start = ob.State(self.space) + start().setX(self.start_x) + start().setY(self.start_y) + start().setYaw(self.start_yaw) + # create a goal state + goal = ob.State(self.space) + goal().setX(self.goal_x) + goal().setY(self.goal_y) + goal().setYaw(self.goal_yaw) + + # set the start and goal states + self.ss.setStartAndGoalStates(start, goal, 0.05) + + def setPlanner(self): + # (optionally) set planner + self.si = self.ss.getSpaceInformation() + + if self.plannerType.lower() == "rrt": + planner = oc.RRT(self.si) + elif self.plannerType.lower() == "est": + planner = oc.EST(self.si) + elif self.plannerType.lower() == "kpiece1": + planner = oc.KPIECE1(self.si) + elif self.plannerType.lower() == "syclopest": + # SyclopEST and SyclopRRT require a decomposition to guide the search + decomp = MyDecomposition(32, self.bounds) + planner = oc.SyclopEST(self.si, decomp) + elif self.plannerType.lower() == "sycloprrt": + # SyclopEST and SyclopRRT require a decomposition to guide the search + decomp = MyDecomposition(32, self.bounds) + planner = oc.SyclopRRT(self.si, decomp) + else: + OMPL_ERROR("Planner-type is not implemented in ompl control function.") + decomp = MyDecomposition(32, self.bounds) + planner = oc.SyclopEST(self.si, decomp) + + self.ss.setPlanner(planner) + # (optionally) set propagation step size + self.si.setPropagationStepSize(.05) + + def solve(self, runtime=None): + if not runtime: + runtime = 10 + # attempt to solve the problem + solved = self.ss.solve(runtime) + + if solved: + # print the path to screen + p = self.ss.getSolutionPath() + print("Found solution:\n%s" % self.ss.getSolutionPath().printAsMatrix()) + #p.interpolate() + pathlist = [[] for i in range(6)] + #print p.getControlCount() + for i in range(p.getControlCount()): + x = p.getState(i+1).getX() + y = p.getState(i+1).getY() + yaw = p.getState(i+1).getYaw() + pathlist[0].append(x) + pathlist[1].append(y) + pathlist[2].append(yaw) + # print ('x = %f, y = %f, yaw = %f ' %(x,y,yaw)) + v = p.getControl(i)[0] + w = p.getControl(i)[1] + t = p.getControlDuration(i) + pathlist[3].append(v) + pathlist[4].append(w) + pathlist[5].append(t) + # print ('v = %f, w = %f, t = %f ' %(v,w,x)) + return pathlist + else: + print ("can't find path") + return False + + def updateMap(self, costMap): + self.costMap = costMap + + def updatePose(self,start_x, start_y, start_yaw, goal_x, goal_y, goal_yaw): + self.start_x = start_x + self.start_y = start_y + self.start_yaw = start_yaw + self.goal_x = goal_x + self.goal_y = goal_y + self.goal_yaw = goal_yaw + + def updatePlannerType(self, plannerType): + self.plannerType = plannerType + +class MyDecomposition(oc.GridDecomposition): + def __init__(self, length, bounds): + super(MyDecomposition, self).__init__(length, 2, bounds) + def project(self, s, coord): + coord[0] = s.getX() + coord[1] = s.getY() + def sampleFullState(self, sampler, coord, s): + sampler.sampleUniform(s) + s.setXY(coord[0], coord[1]) +## @endcond + + +if __name__ == "__main__": + + + planner = RigidBodyPlanningWithControls(None, 6,3,0,7.25,-3,0, 'syclopest') + + pathlist = planner.solve() + print pathlist + plt.figure(1) + plt.plot(pathlist[0], pathlist[1]) + num = len(pathlist[0]) + for i in range(num): + x1 = pathlist[0][i] + y1 = pathlist[1][i] + yaw = pathlist[2][i] + x2 = x1 + 0.5*cos(yaw) + y2 = y1 + 0.5*sin(yaw) + plt.plot([x1,x2],[y1,y2]) + plt.show() \ No newline at end of file diff --git a/exercises/ardrone_navigation/navigation/ompl_planner/RigidBodyPlanningWithODESolverAndControls.py b/exercises/ardrone_navigation/navigation/ompl_planner/RigidBodyPlanningWithODESolverAndControls.py new file mode 100755 index 000000000..a8e1b631f --- /dev/null +++ b/exercises/ardrone_navigation/navigation/ompl_planner/RigidBodyPlanningWithODESolverAndControls.py @@ -0,0 +1,257 @@ +#!/usr/bin/env python + +###################################################################### +# Software License Agreement (BSD License) +# +# Copyright (c) 2010, Rice University +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided +# with the distribution. +# * Neither the name of the Rice University nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +###################################################################### + +# Author: Mark Moll + +from math import sin, cos, tan +from functools import partial + +from ompl import util as ou +from ompl import base as ob +from ompl import control as oc +from ompl import geometric as og + +from os.path import abspath, dirname, join +import numpy as np +import math + +import matplotlib.pyplot as plt +## @cond IGNORE +# a decomposition is only needed for SyclopRRT and SyclopEST +class RigidBodyPlanningWithODESolverAndControls: + def __init__(self, costMap, dimension, start, start_yaw, goal, goal_yaw, plannerType): + self.costMap = costMap + self.start_x = start[0] + self.start_y = start[1] + self.start_yaw = start_yaw + self.goal_x = goal[0] + self.goal_y = goal[1] + self.goal_yaw = goal_yaw + self.plannerType = plannerType + self.simpleSetupControl() + + def kinematicCarODE(self, q, u, qdot): + theta = q[2] + carLength = 5 + qdot[0] = u[0] * cos(theta) + qdot[1] = u[0] * sin(theta) + qdot[2] = u[0] * tan(u[1]) / carLength + + def isStateValid(self, spaceInformation, state): + # perform collision checking or check if other constraints are + # satisfied + wx = state.getX() + wy = state.getY() + if not self.costMap: + tmp = True + if wy < -4 and wy > 5: + tmp = False + elif wy < -0.5: + if wx > 12 or wx < 5.5: + tmp = False + else: + mPoint = self.costMap.worldToMap(wx, wy) + cost = self.costMap.getCost(mPoint[0],mPoint[1]) + if cost >= 1: + tmp = False + else: + tmp = True + return spaceInformation.satisfiesBounds(state) and tmp + + def simpleSetupControl(self): + self.setSpace() + self.setProblem() + self.setPlanner() + + def setSpace(self): + self.space = ob.SE2StateSpace() + + # set the bounds for the R^2 part of SE(2) + self.bounds = ob.RealVectorBounds(2) + if not self.costMap: + self.bounds.setLow(-8) + self.bounds.setHigh(20) + else: + ox = self.costMap.getOriginX() + oy = self.costMap.getOriginY() + size_x = self.costMap.getSizeInMetersX() + size_y = self.costMap.getSizeInMetersY() + low = min(ox, oy) + high = max(ox+size_x, oy+size_y) + print ("low",low) + print ("high",high) + self.bounds.setLow(low) + self.bounds.setHigh(high) + + self.space.setBounds(self.bounds) + + # create a control space + self.cspace = oc.RealVectorControlSpace(self.space, 2) + + # set the bounds for the control space + cbounds = ob.RealVectorBounds(2) + cbounds.setLow(0,-1.5) + cbounds.setHigh(0,1.5) + cbounds.setLow(1,-3) + cbounds.setHigh(1,3) + self.cspace.setBounds(cbounds) + + # define a simple setup class + self.ss = oc.SimpleSetup(self.cspace) + validityChecker = ob.StateValidityCheckerFn(partial(self.isStateValid, self.ss.getSpaceInformation())) + self.ss.setStateValidityChecker(validityChecker) + ode = oc.ODE(self.kinematicCarODE) + odeSolver = oc.ODEBasicSolver(self.ss.getSpaceInformation(), ode) + propagator = oc.ODESolver.getStatePropagator(odeSolver) + self.ss.setStatePropagator(propagator) + + def setProblem(self): + # create a start state + start = ob.State(self.space) + start().setX(self.start_x) + start().setY(self.start_y) + start().setYaw(self.start_yaw) + # create a goal state + goal = ob.State(self.space) + goal().setX(self.goal_x) + goal().setY(self.goal_y) + goal().setYaw(self.goal_yaw) + + # set the start and goal states + self.ss.setStartAndGoalStates(start, goal, 0.05) + + def setPlanner(self): + # (optionally) set planner + self.si = self.ss.getSpaceInformation() + + if self.plannerType.lower() == "rrt": + planner = oc.RRT(self.si) + elif self.plannerType.lower() == "est": + planner = oc.EST(self.si) + elif self.plannerType.lower() == "kpiece1": + planner = oc.KPIECE1(self.si) + elif self.plannerType.lower() == "syclopest": + # SyclopEST and SyclopRRT require a decomposition to guide the search + decomp = MyDecomposition(32, self.bounds) + planner = oc.SyclopEST(self.si, decomp) + elif self.plannerType.lower() == "sycloprrt": + # SyclopEST and SyclopRRT require a decomposition to guide the search + decomp = MyDecomposition(32, self.bounds) + planner = oc.SyclopRRT(self.si, decomp) + else: + OMPL_ERROR("Planner-type is not implemented in ompl control function.") + decomp = MyDecomposition(32, self.bounds) + planner = oc.SyclopEST(self.si, decomp) + + self.ss.setPlanner(planner) + # (optionally) set propagation step size + self.si.setPropagationStepSize(.05) + + def solve(self, runtime=None): + if not runtime: + runtime = 100 + # attempt to solve the problem + solved = self.ss.solve(runtime) + + if solved: + # print the path to screen + p = self.ss.getSolutionPath() + print("Found solution:\n%s" % self.ss.getSolutionPath().printAsMatrix()) + #p.interpolate() + pathlist = [[] for i in range(6)] + #print p.getControlCount() + for i in range(p.getControlCount()): + x = p.getState(i+1).getX() + y = p.getState(i+1).getY() + yaw = p.getState(i+1).getYaw() + pathlist[0].append(x) + pathlist[1].append(y) + pathlist[2].append(yaw) + # print ('x = %f, y = %f, yaw = %f ' %(x,y,yaw)) + v = p.getControl(i)[0] + w = p.getControl(i)[1] + t = p.getControlDuration(i) + pathlist[3].append(v) + pathlist[4].append(w) + pathlist[5].append(t) + # print ('v = %f, w = %f, t = %f ' %(v,w,x)) + return pathlist + else: + print ("can't find path") + return 0 + + def updateMap(self, costMap): + self.costMap = costMap + + def updatePose(self,start_x, start_y, start_yaw, goal_x, goal_y, goal_yaw): + self.start_x = start_x + self.start_y = start_y + self.start_yaw = start_yaw + self.goal_x = goal_x + self.goal_y = goal_y + self.goal_yaw = goal_yaw + + def updatePlannerType(self, plannerType): + self.plannerType = plannerType + + +class MyDecomposition(oc.GridDecomposition): + def __init__(self, length, bounds): + super(MyDecomposition, self).__init__(length, 2, bounds) + def project(self, s, coord): + coord[0] = s.getX() + coord[1] = s.getY() + def sampleFullState(self, sampler, coord, s): + sampler.sampleUniform(s) + s.setXY(coord[0], coord[1]) +## @endcond + +if __name__ == "__main__": + + planner = RigidBodyPlanningWithODESolverAndControls(None, 6,3,0,7.25,-3,0, 'kpiece1') + pathlist = planner.solve() + + plt.figure(1) + plt.plot(pathlist[0], pathlist[1]) + num = len(pathlist[0]) + for i in range(num): + x1 = pathlist[0][i] + y1 = pathlist[1][i] + yaw = pathlist[2][i] + x2 = x1 + 0.5*cos(yaw) + y2 = y1 + 0.5*sin(yaw) + plt.plot([x1,x2],[y1,y2]) + plt.show() diff --git a/exercises/ardrone_navigation/navigation/ompl_planner/__init__.py b/exercises/ardrone_navigation/navigation/ompl_planner/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/exercises/ardrone_navigation/navigation/ompl_planner/ompl_planner.py b/exercises/ardrone_navigation/navigation/ompl_planner/ompl_planner.py new file mode 100644 index 000000000..3294fa493 --- /dev/null +++ b/exercises/ardrone_navigation/navigation/ompl_planner/ompl_planner.py @@ -0,0 +1,69 @@ +#!/usr/bin/env python +from ompl import util as ou +from ompl import base as ob +from ompl import control as oc +from ompl import geometric as og +from os.path import abspath, dirname, join +import sys +from functools import partial + +import numpy as np +from math import sin, cos, tan +import matplotlib.pyplot as plt + +from RigidBodyPlanning import RigidBodyPlanning +from RigidBodyPlanningWithControls import RigidBodyPlanningWithControls +from RigidBodyPlanningWithODESolverAndControls import RigidBodyPlanningWithODESolverAndControls + + +class ompl_planner: + def __init__(self, costMap, dimension, start, start_yaw, goal, goal_yaw, plannerType, control = True, ODESolver = False): + self.costMap = costMap + self.dimension = dimension + + self.start = start + self.start_yaw = start_yaw + self.goal = goal + self.goal_yaw = goal_yaw + + self.plannerType = plannerType + self.control = control + self.ODESolver = ODESolver + + def setMap(self, costMap): + self.costMap = costMap + + def setPose(self,start, start_yaw, goal, goal_yaw): + self.start = start + self.start_yaw = start_yaw + self.goal = goal + self.goal_yaw = goal_yaw + + def setPlannerType(self, plannerType): + self.plannerType = plannerType + + def setControl(self, control): + self.control = control + + def setODESolver(self, ODESolver): + self.ODESolver = ODESolver + + def omplRunOnce(self, runtime=None): + if self.dimension == 3: + planner = RigidBodyPlanning(self.costMap,self.dimension, self.start, self.start_yaw, self.goal, self.goal_yaw, self.plannerType) + elif self.dimension == 2: + if self.control : + if self.ODESolver: + planner = RigidBodyPlanningWithODESolverAndControls(self.costMap, self.dimension, self.start, self.start_yaw, self.goal, self.goal_yaw, self.plannerType) + else: + planner = RigidBodyPlanningWithControls(self.costMap, self.dimension, self.start, self.start_yaw, self.goal, self.goal_yaw, self.plannerType) + else: + planner = RigidBodyPlanning(self.costMap,self.dimension, self.start, self.start_yaw, self.goal, self.goal_yaw, self.plannerType) + #costMap, dimension, start, start_yaw, goal, goal_yaw, plannerType + if not runtime: + pathlist = planner.solve() + else: + pathlist = planner.solve(runtime) + + return pathlist + \ No newline at end of file diff --git a/exercises/ardrone_navigation/navigation/path_processing/__init__.py b/exercises/ardrone_navigation/navigation/path_processing/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/exercises/ardrone_navigation/navigation/path_processing/smoothPath.py b/exercises/ardrone_navigation/navigation/path_processing/smoothPath.py new file mode 100644 index 000000000..da0407afd --- /dev/null +++ b/exercises/ardrone_navigation/navigation/path_processing/smoothPath.py @@ -0,0 +1,120 @@ +import numpy as np +import time +import math +from scipy import interpolate +import matplotlib.pyplot as plt + +class smooth: + #def __init__(self): + + def setPath(self, pathlist): + self.pathlist = pathlist + + def getPath(self): + return self.pathlist + + def inter(self,pathlist): + self.pathlist = pathlist + x = self.pathlist[0] + y = self.pathlist[1] + xnew=np.linspace(min(x),max(x),201) + #plt.figure(3) + #plt.plot(x,y,"ro") + + for kind in ["nearest","zero","slinear","quadratic","cubic"]: + #"nearest","zero" + #slinear + #"quadratic","cubic" + f=interpolate.interp1d(x,y,kind=kind) + ynew=f(xnew) + #plt.plot(xnew,ynew,label=str(kind)) + #plt.savefig("figure3.jpg") + + def Floyd(self,pathlist): + self.pathlist = pathlist + numLen = len(self.pathlist[0]) + if (numLen > 2): + + vector = [self.pathlist[0][numLen -1] - self.pathlist[0][numLen - 2], self.pathlist[1][numLen -1] - self.pathlist[1][numLen - 2],self.pathlist[2][numLen -1] - self.pathlist[2][numLen - 2]] + tempvector = [0,0,0] + #for (int i = numLen - 3; i>= 0; i--) + i = numLen - 3 + while (i >= 0): + tempvector = [self.pathlist[0][i+1] - self.pathlist[0][i], self.pathlist[1][i+1] - self.pathlist[1][i],self.pathlist[2][i+1] - self.pathlist[2][i]] + if math.fabs(math.atan2(vector[1],vector[0])-math.atan2(tempvector[1],tempvector[0])) < 0.001: + del self.pathlist[0][i+1] + del self.pathlist[1][i+1] + del self.pathlist[2][i+1] + else: + vector = tempvector + i -= 1 + numLen = len(self.pathlist[0]) + #for (int i = numLen-1; i >= 0; i--) + i = numLen-1 + while(i>=0): + j = 0 + while (j<=i-1): + #for (int j = 0; j<= i-1; j++) + + if (self.CheckCrossNoteWalkable(i,j)): + k = i-1 + while(k>=j): + #for (int k = i-1; k>=j; k--): + del self.pathlist[0][k] + del self.pathlist[1][k] + del self.pathlist[2][k] + k -= 1 + i=j + break + j += 1 + i -= 1 + + return self.pathlist + + def CheckCrossNoteWalkable(self, i ,j): + x1 = self.pathlist[0][i] + y1 = self.pathlist[1][i] + x2 = self.pathlist[0][j] + y2 = self.pathlist[1][j] + dis = math.sqrt(pow(x1-x2,2)+pow(y1-y2,2)) + return dis < 1 + + def checkYaw(self,pathlist, start): + self.pathlist = pathlist + num = len(self.pathlist[0]) + for i in range(num-1): + if (i == 0): + x0 = start[0] + y0 = start[1] + else: + x0 = self.pathlist[0][i-1] + y0 = self.pathlist[1][i-1] + x1 = self.pathlist[0][i] + y1 = self.pathlist[1][i] + x2 = self.pathlist[0][i+1] + y2 = self.pathlist[1][i+1] + vector1 = [x1-x0,y1-y0] + vector2 = [x2-x1,y2-y1] + yaw1 = math.atan2(vector1[1],vector1[0]) + yaw2 = math.atan2(vector2[1],vector2[0]) + # print ("yaw1") + # print (yaw1) + # print ("yaw2") + # print (yaw2) + yawtmp = (yaw2+yaw1)/2 + if (i==0): + yawtmp = self.setYaw(yawtmp,start[2]) + else: + yawtmp = self.setYaw(yawtmp,self.pathlist[2][i-1]) + print ("yawtmp",yawtmp) + self.pathlist[2][i] = yawtmp + return self.pathlist + + def setYaw(self, newYaw, lastYaw): + dis = newYaw - lastYaw + if math.fabs(dis) > 1.57: + if newYaw >= 0: + newYaw = -(3.14 - newYaw) + else: + newYaw = 3.147 + newYaw + return newYaw \ No newline at end of file diff --git a/exercises/ardrone_navigation/resources/__init__.py b/exercises/ardrone_navigation/resources/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/exercises/ardrone_navigation/resources/ball.png b/exercises/ardrone_navigation/resources/ball.png new file mode 100644 index 0000000000000000000000000000000000000000..7fbb31af939a79be26b875073570e302e17909d8 GIT binary patch literal 1869 zcmV-T2eSByP)P000~a1^@s6lq3|;00001b5ch_0Itp) z=>Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2iyV) z5i|#uO2rKT00zxTL_t(Y$DLN)Yh1?_|DBn;yLUen~+e~G&ZRUs*^%m z9NMNPghCrI{lG2GgWvi$G_N60KlGsnV;fpyD19l$RO*B4rqpt>Q7l<@B(MB!ue7?W z`!zG?^kG*@+`1(6z;Kzl!}-l`&Y9miz*8&?L;OG1>2#jDuAa6iB2V3X`}S=w48uG# z`$>|}7X^P}V-!UoBKW=!0BAOwsMTr}05t$*00Tti17H#9h{$8-++*k5dVha^&^rjm z$HyTeAfhKh!Z1XkQ1~o$O+;f_>(_{AnusO<^a02+vk+0p%q2^>9o6y7!^`jk|XBsCFvO+4B@-nV|Fo)Bp&tPh5 z3dfHhr+xeOAqWE3Zntkec<|t=2LL8D&p@OO0^%gZ%*?Db8uN7a>{-0{;@7cv?+X|k zABA%cCbh8E!8ryIVPm6>^|dwlzK>F=gi57?p`jrhIB?)O&+~RS8V&M#88X5mW6n7U z3zBpL>+KEPxx0$ejsk{;2ca|64Lk!7f@22)L4;AQ4$8#DI9jb1UAb}v zhX5!~MDWbS#EvA12cjr~wFb^v%H?wMrO8Q*j*L(ylR*#!AYl8=SnCjM1jzY52Fm?Z zsZ?^!2 zgzsko2($*1&0nDBG1IY$na2X z%s^DDRglCHjIkgh$`|q|6$@a;NRt#S0^t}+X=E~4m^8&|vx&9!4axcil*@ypl%i&{ ziF&<`TrMZ|dRQl9Y!rioW%zy$BHRtObx2c-IEoQP z37mD+AslGp{=5mYEsdVM!qHJP&|Cgt4Wc zA_V6IL9mI9jSg4@#XT5wmp#~Fbga@6pDFFPVU6m*eEi75zIiTC${-~e$F}9Afj$%eEF3hKQzwG8*4sJ zjY*BMFsYFwiP0MpZ@=;gaU8>>21+RuibeDf3`j1UlWaCazVD-0EXG>v_lfA^ZS8vX zyI+%E{qh&rnE5Z(m~mz|ChW*LCjdjhB;p`U5CND4U0o!Ih%i794bmFgqa;n!1~dP2 zYHI4%6P5AGzwYDRcYnPz41?b~YrkWyRpMM%-oYfoJ?GMk8$po*h(rJqXk@clWU`rM z&-32VTAw>}=FEKn@c#IRcd&o|C)hbT3;?&@c;ofogkjW9)AUEq7!q`)SJ;7_fSH9w z08vC;5CRCKHLSG@zW8UYwSTtOJ^+AY$Bsc4^5eL8(ctRUs{nxc`D-h~!^16WZ8=V& zY@8&SB#Lz$#VU>yL{Us}oRBdVX<}efn;L7|B3QC)&lqD)U$}7L=F-v<4j(>@lP6Dp zdR^a|p2p1V>~@uU_Srp^Y$o&bB#I6iW4p85a^r%BP} z>({TZ0l@V1G-iAL{;QGr)A{%Cz1NT7+O@^UFFgO;@6*&=w8l(|@Pu>R&n!7WF}pM( z(hrr=%OJfJNAdl;_wH_fZr`Ae#SBmwr5B|zHlP4;dAyScf^-kSm`dlMkp z7y?U3p%{qiMN}KxxXZR=Te2jpca6#vO*`kgf4n1&Y)r8|vW?@%pYceenRDj6@9#Xl zz*#y=XXz}RD5<~4ix$Hup|i7&KmdY42!$XV2Gax){U(b=KkRn>a5x|#p-<)HWSj-b znFa`#YdgKY3+V5M!9nQmhK>&C>H?Qbzvl74(2xd;VSrL#vuR*Bosg6Ssi}~a1$lXp zn+q8k`nA+l)PxD@EJ!|Yz^JVidU~L_8ER^xkLG3=7|`5fG}jn@9{yP!u`EhOA66?Q zBtUjH6c@wf$uMOKl$L4$WoM%b3)NYWoQ}Y#s}ovUpsEVCZ--sGprHYLKFuA5K`|N- z!vL^aHRrHc^zXy(Gfgdq4C5#(r=lXsX49Ny+B8_W5a!Q^k`gE?LgnSDvmiOuqNJf= z0aaD-{`>I32bx<10(yf@Gy3MZI7m*`52q6n6E#POi;Mnjw`;(NXmA9B8e~I5;P>li zpHGV?w_86C186j8l9OTPOgR61Sh564OHos%sIwqBnc!$`73%6><3@Pn4cN0sZ(2AU zY8N6ZFp!(Lh1L#meD$>r=tl)wO`GE@*GpKs6SOM2u1B(}< z=FU}TK@#iD*uK4-S6_v{{SE5tqasBq%`J+Gv;w(o87yB83l@|rtF`{5e$SQ`p`rpd zY=E6RHMePN(_%_QgT?8DRjc5}8)4~E)YPeemu`sIF8scIA`d(uvSf*f(zdP~IXz3+5Vq~-C)MZWxHkvVfjQc|Md-D(xd%oMr(c99of6lrgl zvpan}i`>y6^76|fH{GNcbo2rj7K&VWp~!vriR|8eS}(Bp{ddVLuZVo@Ya;XKYp^J# zA2yrF{Zf{Y+%qG!cg*x~N%MBCA&EzwO`uxm#3u{dJK$?-ZFa zLnI+VgI+0-#6*#6t`T|eIgzd|*I5L7+CskjuE>|ZbU61&ND!GdOXR!X71_D-bA88& zVU)orGQx>O`7NnfkQB9fji@{MnZY~FkpD2FXrQlRMgHlZL~?TU9Rwn|xg!7Y41hv_P0f{v!ei+JXz$y2haKlMz2#vg~+Y9YHMd0D0AnEJoC(%eG`ex zwOxMrLv7X$>mX%iBES2ch|9J8EHsW-ugXf1+iuewWmrd9vEt0OdgP&pL`q7c0GTjB zS{Uoun?aOj&!i6fISToi!N5yPtIC9 zS)?wzOv&Sq3w?c1U9A(k|L_MWD@z~qbB{Sle*0VD=Rb$0CjG*aC2;@!sB_Pa$+^W{ zFZesba|i~j^}}5QzGg7IU>XrnL>@Bw%GIb}EMT!gToyQsA!Rb8Ox4c`g%F>MO3Xjy z2}1It9|>#L=64|_2_?LgtDSaY3d+eLvJYj%{uT}6i!$1{O z-3Pr}z-H3|CPu)bKnySf5cWW@4+1?f)CQgg@YHK?1be~Xt@RrT>*Z1weZKU0P*}_vuhz9E z$Fxa%gxWtq!;|1SsPB42-^B?9kUR;pS3>T2kh$n?WsQG0rYtsnmO6}+xwb*a+t615 zo;uBaj3MaV1pT`q|6-(Zt&qI}l~^$5U{QsIN?v?XsH)N;eQ*$-d=lBUOVqr1>a+%l z)Yl7l-U*$Z`h|%TVfAWMNy%sW;u0KuhOYNlv*&+nJs>oo#frrNiA9h$2MR8SybDpu zr6=XiRmo9PUAjIHT3>A1JL+1_&YTBuq8tBR4BRzCftllop#z3GO6rk z8Z0d@3k^>~_j;`}8D4049(+wm{U3#b%llOP*!46~=bWSDJKxdPPG6q}%#IzWF()Z6 z*DW4GAziI@_0_sH!>0kHXRA<(1HEdk>5v z+azNpCe=CTD7o`ap|(~R&aYpuUH35}OUy*gh7DTN9vadwTzDZ&n)Jz^>E0|<{S-Ri z(#oH*Lh4MI`Y$l$+n)=79Pa44MltJ0FzyD3&(VM%YJ=)u!=b+*-Z9L&yXq?4!2q-p z``&w6{GU!Bp}aiGMVwB!=%P=wXvDQk*mpm4y{G*S%BFoCGyVXpNe56x;O)0{IOKEziFm#Dv1N-!Fu;r% zpJ>s@V6AZA*UG#6evR-Q1c+PytrU& z&Y5x(D_81{OQ0>x7>S(NaFR`%?xCqkE38WC4nKK$M+Zp2wVm2Wp#3%N?p78^n+?rmBb!^1WdFSbMS4wwj+PD#EYLe3hB)Zej zu&7zGgpZ9#HmzMi^I9DfK_F=oO#7c_6hKCRk{AI>;Wgk$)7RTq0S6yOT(PfdR!f&E zii@?#0qwT$?mkU8NkxS=zd$#VDK0)b7wKFt)crSjo1$j!q_0Ea)oVFJNhM8GO!+6s zUZn$!Vrnkh^sG*Kh&3%*1Zipd=i!c%ry3+uRV6exYxXw$B{MTW`aWNqQ1=I&10rF) zU=wbEao3|9DK~KjlS-ehnDlj>mtwK&)P(vcw5dE6AQ{$6yk4lRLiSKo?>D7!z?3$R zDTXPhMKO~qyi76SOIndd!B8vIKY_R^V#`ft&C*SYEs?F=yBE5;R-amDT2)n4)FdZE zS=mRqNPm^E?VH+XfarXWvb#{3^N$N4&4Z@&`URICI=sSfE5h{p!5tKWhE9@+vq6eO zk(EH*vo&F|6BHToimU|4PqLgE{io#i9rW#5K=bq3FVyu8bi9Rx{dXx_Y@*mIFHgy> zw+cIU=ysy}_C@#Sl!4^nL7hei8X!jzH8eh>Bc^6ZM{_6K3|Y&L=`=3CxxHaP7F72M z59~77-D7gFPv{ST5$Vl)`$! z169zsOQ%~lJ_T9J)&Q|fb(l6y->^QPZX-HuRGbQsNHBO2mtLw<=s?G5l9P{cnpFr} zzpgXC08*zz(e-@RSVou6bXB_~{oy?ZZ#9`z_6Xh(n4%wY1Sk$W_h_SPP-t-r?=(Yd z94yMU@Vi|hS(s;GL9V6LYEdVpQ=`f|Pf5l11#g31lfDY*d>0A0wyU^|g)uqF@IG{Q zYQ3bb?G$p7nwoM32BMvpi;J}hJepcw0B@a67qrI1_*)=t)@QP2c61o>=w5@r)tc1y z3w}cb1Hfj1^f*Y3QzY9V(FP8y!e-InFiib^Ul`m0=nH6Yg-qxjf;SsY-fw{gxh6ME zves4g7|h5zNt|WE&Cs9dvW`Jqng4s=2P9_ZZ~yAzBb-$!C%v>vCm75e*EpBhfm(V;`rM1=p5 zUe=Cxbg6@cwd=j`YDk*c_eqD`-)qPtdkmhc7FxYR*wo<2b3$3FVrjmGRYi)qIhMYh zM9VSdX*3O*vZK@Fy%v*QUBbbBVITklL3pm#q^ehF7?d@QgXWqEDPyeM7G4W&ujv9Z zGYIW(AOrg)7CAF2CnrNLy;QJTH76OpPbVED-Q7`rBq1T{#2Ha2-i>%x(Fx1htN5e@ zob4Tk{9?Po>kTG-el0dq955|Iap^=0SC?2$NZA}eYU4j>HROd_gY~V#!9KwkhJ7yK zXIl+Ayin~jWlF}_0YzobSF-i%!r%cNp6LA$+;y=#K~|RbybcZNeL7_z>F?L^8qfoR z;^GchSU>znoh%HHvkHY&Yb-Z3geRn0SJkF^x-7Trgg7`!p-d3$5zkW4K8x z3pLnYtTp7X2Mjj0ne_UhX9)glw}Ce#?CUjTdgd7FDR~z|$6FdCfnIQJkDUUWo*o6s z=zTirAQ>Eto;)Qb8Z!W`lm#O38H)pQR-uxQo;t9j(~zHSHF&q##2eB|>C|+^ZPP8> zFv-feuPL*t8kZ@LRfbttXVT&UcL*NeXQC7w>@#Ij`WT%*YpM1bc_JdYXA_LS zDK-jhN{Y@gK60NC`EU!_>Q7mmL5k6lv4oGit67h1V#icOs?-vxlvVY&BT7uwYwGc6RSD69$wgYa0TL3yJ&b|=nUsOybE zTRNT?U9!Uwbb(Wif%6p$E3wD8($jn1wcJeW?<6)nR|p9683A4%a}TN5sSp-$+D zs4Zn_if_-ia&4LIl%);)kNI}>rD+!Oo!Y8u_rjx<2J4%{W0M1tHaiLuF<@Z74u+m6 z*=*7M5IKb)89mT&bd|pQpf0=yU8J2dnIo04eZ7YKd7nv>TM!^80q&e-;ffN={hVUD zd$x7QmBkiP9ooX$*(E%C(4=7?_F_k+&sI3HwTobQ?ZWO8Kk(6J4W1H^j7DoH6cs(5 z2A#JGI@u;Z|05rL`hdyaULju$h8n>)v=)$ z>P)tGjFH$$EY#j;0B-|?yeEFl(fctxO!Aa~BrYzh9E~1xHq@$vR{#ll;7B>#n&~j) zttJy!@|1wY>5L-OK92|Cz!0-WT;{3NNVQCoa?DD&`z zO5_X1WoSQ1gzEcExwAY(UxbUy%2b?NWZ~1|CEl?2zGi>(Jp{O7oy8<6`NycZ^yi!>>z4Pn1SG&_?@A zPC7_3G7fjx_Xy{g2*sIF(gd$+TxOA$|h0lV|gM=kqevIhE;2(_^xUmlszBDz)633pb(;BtL8P(@bgj}xZO*`y;JtZK?&DCR8hZUus9vEn=i?*f$EOC(* zRGK!one-0nMb1rxr3Dt8j*lvwq>d)DLK`5>q;2jWB~Je$3w9 zC`j`1qWg5xL2~5D+uC60h`V5!fADqWKAN@5{FWXkQu^jc<~q>LK4Cia`8&fD{L9q!s^`B>=@V~3sO(4 zGvV?A3xD2k((cvP*sgA&)on^)th~X>%3&!}a$J76~5?3}rpB<25wsjI?3@NKp~w<&~Zikf`EfC0AUb z^A^Hk-8yn8bofOMcSi(j`vrGIm$F-6O1e5sVQm>|kq4U|R6 zHra(>K#QD#VC*oWBjuw!qSp8mJ6>H~6eKnqj2p*^4AMUFMCftjbS{b89Z8DzYnxwL zH5UmDjC3q&b_+ux%|H~qZP`vw{I3~IbJGTAx#xPVfUs&LvB{Q(_D7zo5r zFB!E)D2wh=eIis{t@mTtxcHco+mqub0iBth5^^J|AgB|O0n*}h;_}Gd-}xl<{(4MIQf;Z+mUkPgb61aWh-0N| zbFNi)7T!roLJ3x7#cjBGvg1j14(=>Jp5vv*D;%R%YcQ7L#@`-I&T?dEx54qMH`Ucb zTU*peGIM71CO%~#nKI?@im~n6!S6iW%^cJ%DMDscX-%-{a6G`m<=%zQ@S#wg zpME|!Q*&HbB?i<`vTV06$R5RM^8yvQi*PNe!R4G zYZN4znJ{(giMw%Pa;1wGN9A5~Gqi*uWR04@gZnYPZQ`)#91Q?}SjTQ!hgzk|TZPBa z@ruPMi<$16960QnVsE-tfPEQ1__yr81##cjM zxNpQ+wlr-i6A~uGG}yN^btxHkouuC%?x(@uaLmW(56Fl<1v-mACe*hpn&xSV({0a> zHxYWv7Tv1dF!WxmSOIx?J5CFbsJU|$lP7Ca7+}k87)S;c`F&t7_*%)1H;9igm`*N9p?-p9@R&|?Xh85q42G-(-Lc?= z^^w79VQ_ym7dT_VQFDPmvsJTgn+~*s?o%>xVocUAb|5+bd>B8Tk*cw6J>W7KnJv@# z9%b=eB-v%e(y8tf`U0VmV4X!-u&LO0GLN^$KWrIwsUZnra%}ODx_-eMv1p1@6{&GC z(5pM&*X=mO)S@|SIV2SypZomHH}!Nf)6`zpOD=)3vWrhEkf_O%6^j=~ZSu+vXjBl4 z$d}#^nQW~g!x0=&UUzf~{!l#Dh#Nm_hDTBWVo4qT4hc)tW{tN*&-qzscb6e89tcGo zfip7|sj(0@o$qKz+OVj}n1`|)yZc<)+l4pYi1tUDFaee>MLA;A#nKYf-MH#1?WR`> ze16y-0$0TL4Y zXHK}@*k;l_q%Xr^fw?(~q7#e&OIx`f^kn!nx6M5loa2x0U~j*zo7DsYdhM^gGG?MC z<{(iQT%edYFWUBCuOAMX5Y%20TgPiGPpTo)p@n;kN9Y}#h+#NK#K3;f{$o}uKaKW! z_A(IG-F|G!#`yU0IBlP8ELzZzi#D{H^!l|(oR9)j(qph}Eq?=Fq#3V01@bOIB^~>8 zQ|an*v2Go-x9b;ja^TWSQAM%qWp9c3ZF~LoQEuWkV5>lHgqwI8Si$Rzvv*@v+WT75 zSxms6jRnpAHx6v1EzlNI?RE3OW?UhSyF5o86B0hV1ySB;lvnj?KZycX6>?{3NF7~$J1g+F$amd?mESyMNw{2DNqggwEYa_=x)Wp@cXDGHcVO1tU(}KJ20>XhJz=F^8@l5A4Z0y^3 z!m;>vf4lsp{Vz28n-PR$YcdyRTu4dcXIsQ|wMO}-cEJ;ha*;ysLk^Y;Jq1hk~!V+d}=UV>Q_D3oP zkG;=n>rksa(e}hWkGDTQlAlr(SHzd|zs$6xPwynu!S_vyMYZrn-)7W)J|4Ao@Si21q&wlXF}gCNMI6(4X@$l@wUf# zpy>f#>wZnz{GVP#ZJ0*Mj)5JrrgaU!Zu&J1Lk*aiWY{yfE&n!_r!MEyk0B*bA28Bi ztkZQiicuAYX(wRzHvDxBEq~KVSfKmIO}y=6d$xQcJ^Sne-h5NnIw_?Ctk+$~=WfC{ z0AKu~&Tn|>C7pA*8z=-)z>;qA1;<(rgs1SN`6YTK0~x#;Y`u{2CzPWepXuYduIJd} z*~67tSMpl-Yf=N@t?eiK|?P3(>|m8kxG*mkM;xSJK?LdtejJ1IqpPaX?s)H{lCB)QC8z} zp!812UVh9TQCYuU_}=$)v|U7J!ryTRYT?2$E0G;jkf@>}C2zhd96AI$cj|1Z^*}bT z7*d^s+!?=yPBVwsLRa9CB&6>)5G$muMaAzYZ1Gd=t)|vj%hPdBGu=6z;)G(-ZRzP2 zWm(`gytMdRsPb0P5taE*&rbhU|51_JAi%eC3F*5X!Ud?b zXR#*k#I$u2k`OKa7TN-BsK~#O#X~T~#Kc6QNVlakDRB}v=HAG4IoEw^>1U10kjM8K zJYJ<+VVDA`4!E||!aq#6a=ay-(z{)ckL%f_GtVq`$T=U%zIx1#GwJ9Me)TII>GgQD zHM3#`+;$sk!UXlX1_=PS-r7f9T{=%bsk5dU(Liy6rBqmC-^M@l05Sw0gjZ>f;=UXS zOvj%78gVHbh)ZZ7pnC8bUcwSStb)&?EF@SHNVlg`;3!~G$|A1Mxt?X|%hWgd=Er`m z!GQV9j!s$q^KAyNH|oZEB9I;rS4_0Z)p8wFOUQ{z&g(49Qy~@ z2;ubmcsFzbeUgqDD8b)#7g?dJD9YK6-MJe{XdrCqCoHcpyiQqWklGSVW7(9E>q`FT=a#r)5aCM1=ug|eE?KnFxO6Pi^^2ehllv*Rx z!M=yezwp?P`Io=cv1X0t#KU^X*S-dqTyjzx{+!f)AW~i~{NyKkz_H&Cc3>rNiB@P0 zi-iNmH2&bfgExYg(_$78!j4jc({8XRS6E);Wn6kcDe+#?;!U#R63McsQ;<;bsY+?% zpp>_`g{_??uOBk0?2Y!RcUX08%}tXme0iFM3CGbZMZ*5OXn%bT2Yv&+TXgVF+4P*7 z>Gz^0-g4~6eC8S9r$3GAAsHEP#~tvkZ;iQ_IVB(gkf)y(e)&t-y<3-HIDm733xEPJ zZ5F!CEY=2Y;)US#R2s7wG?OvW+4XTYC`%I-;#yTt+;KPm8(mSuWh)v>xI@=pzaYEJfMrdEe=SV zqsPY`4?v!KPWai+^jd{Ny3fe!)$qOV5v#$_rzJ=L`Lw$HWZo@IMu@(h3r6*40B`R{9km`u`mLCf1JlYPg(B;z{BP`Le zCl&=6@w$BTqVX1Pm}EKb0R+;uQE2#!9wgxJ&`Cr#XH*9nckQw3AZypw@rz%;o;`Yv zoKBqr@NfTynm6y{j$q*wkv#N}@cZBEYO_#Ci=M>_E>ut~;1Eo#cr2NGXt|14!`D&~ znnO!Cn_j<_fT07+!!vDEFA3=-(YFLK8i`Gv{2G^3SN;RmTR9t}YiZzL?Gf4@7UG$ed-q|9#Zr#V38wPHB%a zdFUbG;fJG(VFPB_;35l5F(E;xf*IHtN+_c$@p?9!=d;s4g{EK@u7H)nAo#*sd>EsO zzbr~;=EqsVY1bo9GUA{#O|dZ7!ioaL%;W4+F9X#=-!5o=4m#gMmqZv^|41s<(>qJ= zK&2g9BOR%)*YTJ~9)UxLbQ)-KvIfXK_vmSzr+`j*BRg5MMtI~AJu5B{(1C)|1h^y) zW&=(ys1RVmRCbJ%>3DOlrXg+x2SUXh3gpq|kE1W3@P%}12!&QeopxQAUYMjPOH)kE zP{(hvguM6Rsk?{%N@#mU555j`gBjM`!;zsanBv>Dqb%Xr%2Q?6F5$7q;Mr&OFeB5{ zwM>^@3jg}AC&kn~Z9xJcwY9>Z{sfOd9<3XRcfiDCxU3i!1i^j?OmC!!N$FPG3HjhG zM$%>@8A~wJ=3-APi^(;K5xj`LigJ3lfomHKR_jrU_;$Q0JrM zXTX9YxUvj#njm;UH=Ywy-@(cTj#M2FiO+!>?WZNiJ~lwF`kBow@F9 z)D3stwfc8|rv}EbzM(AI?-5@B`IkW96_7svn5U9TM~Cq2vpOea=gw#;L}DV`a07hh zD>@zYG@#RJs<#+M39r4T9Y^ce>xC162KyUfEr?f43NAUFWw!Kf%fX<%6FkUR3K6s;NpdF&SbDRLjP9iuhg3=CSV?==h*Nj+oC<(#EbxJG{{E3-x7JRMRTN#MUZn2 zq|St-Vw7{-v4dmNCgJ(#b^N8eI@*oMZinT|;VWN(3ondm67v}g5&&s#7B+2y|Nd{- zyjd4Po2FjSq9T|v6)so|t7b#GfUgDy_rbtE-Fn;bMK@n$6bOzs|5HWUpGUwHm!%80 zQ>W|U2}z}pkdI0|J_nYpUoX7;GHl!k2M$1ApS~8W6$%P;B=@@OQ0JU;TJFbb3zCrq z-nmnFws8gG$A-4vO8H(r@ zR$ZWNb87JvmlaWj$7w%@BOM$W$2|dE+S`RKTXbdJjve~D_xEe1+-lXj#|0O_%{Rk} z6(`Nbenx_1M9HhHgcn|bmtWQ*C=}9QvDtL3Nl_8ZovYdQlEsjh0k*JC6_jA4!(sSv z{bg9susC!xyZG$8)d>`ue_q~&|$C)H`-50(SRA|F!ScYtXWW2HfE`PQd1*TSL>|xJ$rO} z%=UI2(ho+42@Zo}-aNSWT3ERf7A&|pR%MuHLGp2)$?9rd-uUL5P+zZ$lT1@{7o{L6 zNq73m%!KT0UG`W~qMyf&)48|#`FE@M&&D~VqeF1Hbd5_>lLo`VgZlU0Ufm75uTKL* zL<7TW)xce}2(Gw7KhK_hzj8Y7{XDJr=Mp4`DYp#`TIR1`ulu!ibw#<$=&4>#r*?5C zCu;yDCh84LO45HzNQjR75YZM@AfUnL_3ET!uUF5rbG!9pa4@PPfdWaEqvrf5F?C%mJBVY*zFXG_Aa+;c;q5`&U)8X}wj_8~~k?3QXtX8el3_mQE=#VGV zjLxnLhod^s$jYgxD6v>H*h)*aNLjK(HykV~LQS0bxnJWm0wjk4W^nKsnwwYC(V>H6 zH8nbbSX-;PjMsbk?7$;|GyL-~SVlh&KO7Ftg^G$a7^Y6uI#5v&a!Jr1muxJ^rUs$agEOxu*9tjDMnfbX>JkQcuI!kBi zES;sZbe7K2NubsakL<7Qm8zN^@%lpdozCCY9|+%7UGI{rnjUHGaLbvp2ey-WvJG2X zKCssgi?x` zQ?k?<2a@`x0bY9h5UuSVW=_rInu|(4(`UBr?!@B@as6dw7=~G5wOWqZz5UL{X39Tk zA{a6wJ~3T#Zm}qwiFU>pB=OQ)^>Xz^$L|R1?D4U)vYXZp4^v9g2?mXruiY$7M!2k}sh4F>SESsM{A_A-HT(YCG zi~D}3*CTA0y#HYfk3CZ*R;yz9f_x@TOjl<#NS=6ZpZuTSY@xPcfUseblH}n1Ev?iw z4PYFFj-Oc=uEUTu`rqEUppu_ zTs}!v?Ca*yr>kgc^XMS8Ve;H-wY>ITBMSJx|GMnV<|Ny8cgVm0ViO$^YQz;mzFGUy4id_g|*XXMb*;pUx9Exi7IBc>@FsO^=h zWtkt@K#wm(XSa`<`ab#NbNd)@2e4Qa^JnIeHmYg-&dN^t(XY3#eoHHcVX|OWE)xoq zsXExp!9)EtwYo73lbU*$Og=*Ct=QK?L9UZY#c8A@`KYY!WzYT|9(rOAlS|U?0q%7T z__zQ2{To^LW*r0WAX7`ynK>ns_AW0K`+I0?anm;tV8gZ+>FOCGJv9!y-NKL`6pGa3 zIMPxaD5Z#xvz^%>d3$3M2WnjeLk24s74YqUoJZNjG%Blm`0;~V=^qRn`TjKLyDr++ZO#G(}b=UwwzI6D`&C&-T< z+`@YwwzB6y5ASSj=Ef@~aqrg`(%Ru+S5-H5yM;S$p23p2dDv`JpV&%8NzNg*p17VMZ-mZ4T(Ia!rXNX2eD{A=#s-$QSo9}yTD3iH+nO+5N^B~QOp zLr1p{5y9gPv9H!eN4NJQvZ}4q!_K{(Y~9^STbCDett?U{(%G=jAiCG(#<#lc!cJ)b9@)!k+y-*zHyp&dyWGNxE*x zVo{V%NM-TdJT-r2t{N6!XDlZf^aKekyVmTeBg5IFj;VWzZcY6#nR3n+ui|an9mGn%dmF^7bLR`~1AKshK5n^Nv&~ z2^;2!9y59)Mzj9@!2m-;p#`Hqo1KwBLVR?2b21Z&i}b}9bO#BCP5KA5E;ao3__*kJ zmF!XP?{^0Yg^sw6ljy2DEkKf(V8d!vgblsmUSIG&XQJa?0Jkql*gSlJk`nFMBHU%( zjBGAlHG%ZhIFwQ#x|^C|2-C{a8JGW2XWg_(ncQ$$8CI)8DOmeTEe$Ph{`lN}D)x2B z-03+Az?opjYEhV`&^Hhu7&0z$*sX5?3`8~>Aj#>#ZnO3QBZCwA-NBCldl;~Zi8ib@ z3rSAL$aRbcK0yCK^!-UrJ2sm-0wZ$LTnHyw+#Hbw7NN<-=_EV#xbgQsY+=uVo_he^ z+|VfXO@o9&hks_~luXi6w1VmO1erM{lWQ+7?Ys5*sdwLS#iSi878GE$D)MuVK3~^i zw~(CVpk#cS`qH&inOKy9MJaYwb+dj;E5VR)5y14a3{sLEBhI*rs;=^3Q~f}l3xB{M z&S7P0X*xMsiHFImJ^Opuy{{X8AbeMSQ@?DE=pIVJloEZtGbd+^T*ubkomAEIjCfTy z@6_+ND8SwWTLx)r zb2IG-9ph-Ud_jTw*PpDH#@0dl1_C_)dM&G#6qW<(>WfR*xTBrzdph}WR|o&=mz%l$ zJ8#Ip|6(IG_5COX7c4Jg_SCG89Jg=4Í#f#;*c2;&12pSaRCb4pHA^AB; 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 QgZ~4K4J`EU>AFS#2gGzHq5uE@ literal 0 HcmV?d00001 diff --git a/exercises/ardrone_navigation/resources/resources.qrc b/exercises/ardrone_navigation/resources/resources.qrc new file mode 100644 index 000000000..1f733f475 --- /dev/null +++ b/exercises/ardrone_navigation/resources/resources.qrc @@ -0,0 +1,8 @@ + + + ball.png + play.png + stop.png + jderobot.png + + diff --git a/exercises/ardrone_navigation/resources/stop.png b/exercises/ardrone_navigation/resources/stop.png new file mode 100644 index 0000000000000000000000000000000000000000..2ac1754ebfb30e1092b98f73645e70923f85f8ce GIT binary patch literal 4817 zcmb7Ic{r5q+aAUqg^=xKOtuO$mdG&H(V{3zmXge73}!JiGReLxC8bD0c10*ji6oIF zdy9PuV_(aX?KgPq_rCAr)V0}FvT{?^jK)}+FAUeUF zNQKei;B8zO^L^_x1Ps~^p}E1qhFgRnJF_z&V-f`m(oj)VMnl!LKpL7VYDd&GwKbJM zW+WoWkW2wV)j(=cs1``|2uxiQc32bi=LOc|0qIgO&M-^F(LWiPBRJTFMkB)@5HBw; z6)$xa5(NiQ)z;RAK-C~>YRb$AW$I-j4M|rfQul8`>|h|UR5S%ors0{STbM``$&&^L zgPHT7U&}#~E%7)P8ud2?k@}}`Oba1&BpITr0)`&LMupT?39TVO0dZ*p-m#`F&wHK0tA$d{V@VbyKJDD>(S=*yuX}XYqpl-2X{#%K^ zFd4ej{?4>j6qq}bh=YUa${4IO($k#=)|*Lt12RKmupnj`k<2%=jQoDOq0aUVOGmqV zQkn8V&Lj%Rih!iL;E6b95Dw{$Cu09i=r7i7q5l-NC6?-ma>b(ml(xMYjgYv%3)RD; zNkr=3(SO_J?2f!d@}&GPv(#T23ID3`H_ERngJ>ksPM!Y_xHI~tYu_p=P?tgfqx7JEQ}H)T|I4})`j=D4l(O@UdAgas1@gPOFbBWe6PC#ANfc)D znP#(SWi~D`nu#6FisVdVZa*wwr^lRNnuoQ-A~BaeYO%V^hM0T;VQ5Y7M6GRZ>H{?^ zSkc`-D4R`Hm6fwIP0}301RXIkLzqF(WDqNFgQO&{zyF%k^2!DfxcB&v{4e=p<*)r~ z`28kj-(3Xp4F<~@blSh!bh67;te@pP9=sd{t>Hz=N~Bm3J{%pHC}wb&ENUb z?t+Txe0}|lnXdXKp}XkzgSJ8-Lhi zgG(Ixuf<%eDl5uG{eHHSgJo>5ilazz1B=m{ocRnO5FE*p&Jq>~5SsLMUrRmN!7fL9Hpk-$Tyg#2 z3SezE+fd;HT1fiW4u>1Io0_l-TO8BjRt`FT;17p2V4{Pog2ICb-ICqxS~b%|!E0_j zyWi&!!Bes(*|)kOeLZ*5IK+?Kx}rbdTh_Fc`SkuG{j%{wL9hO<;a13_ksy6IhgEY!i!6>@ z`i#9Y>kMDVYr=s)cmwupZX3NK|@p0Cab{Vs?-G1

p9juth-(W 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 kxm|0<_j{Xpx$}FjU5=gQ)pZWr`X}myi8