This commit is contained in:
Dylan Thrush
2018-07-28 22:24:46 -07:00
27 changed files with 5306 additions and 598 deletions

8
ground_station_git_reset.sh Executable file
View File

@@ -0,0 +1,8 @@
#!/bin/bash
test -d ~/Maps_Backup || mkdir -p ~/Maps_Backup
cp -r software/ros_packages/ground_station/src/Resources/Maps/* ~/Maps_Backup
git reset --hard
git clean -fdx
git pull
test -d ~/software/ros_packages/ground_station/src/Resources/Maps || mkdir -p software/ros_packages/ground_station/src/Resources/Maps
cp -r ~/Maps_Backup/* software/ros_packages/ground_station/src/Resources/Maps/

5
rover_git_reset.sh Executable file
View File

@@ -0,0 +1,5 @@
#!/bin/bash
git reset --hard
git clean -fdx
git pull

View File

@@ -2,6 +2,8 @@
#include <ModbusRtu.h> #include <ModbusRtu.h>
#include <Adafruit_BNO055_t3.h> #include <Adafruit_BNO055_t3.h>
#include <ArduinoJson.h> #include <ArduinoJson.h>
#include <utility/imumaths.h>
#include <EEPROM.h>
/* /*
Imu/data (Imu) Imu/data (Imu)
@@ -113,16 +115,7 @@ void setup() {
GPS_IMU_STREAMING_PORT.begin(115200); GPS_IMU_STREAMING_PORT.begin(115200);
GPS_IMU_STREAMING_PORT.transmitterEnable(HARDWARE::GPS_IMU_RS485_EN); GPS_IMU_STREAMING_PORT.transmitterEnable(HARDWARE::GPS_IMU_RS485_EN);
// IMU Setup setup_imu();
Serial.println("Setting up IMU");
if (!bno.begin()) {
/* There was a problem detecting the BNO055 ... check your connections */
Serial.print("Ooops, no BNO055 detected ... Check your wiring or I2C ADDR!");
while (1);
}
Serial.println("IMU Online. Setting to external crystal.");
bno.setExtCrystalUse(true);
Serial.println("IMU Configured.");
// GPS Setup // GPS Setup
GPS_SERIAL_PORT.begin(9600); GPS_SERIAL_PORT.begin(9600);
@@ -170,7 +163,7 @@ void send_imu_stream_line(JsonObject &root) {
JsonObject& imu_object = root.createNestedObject("imu"); JsonObject& imu_object = root.createNestedObject("imu");
quat = bno.getQuat(); quat = bno.getQuat();
linear_accel = bno.getVector(Adafruit_BNO055::VECTOR_LINEARACCEL); linear_accel = bno.getVector(Adafruit_BNO055::VECTOR_ACCELEROMETER);
angular_vel = bno.getVector(Adafruit_BNO055::VECTOR_GYROSCOPE); angular_vel = bno.getVector(Adafruit_BNO055::VECTOR_GYROSCOPE);
imu_object["ox"] = quat.x(); imu_object["ox"] = quat.x();
@@ -186,18 +179,73 @@ void send_imu_stream_line(JsonObject &root) {
imu_object["avy"] = angular_vel.y(); imu_object["avy"] = angular_vel.y();
imu_object["avz"] = angular_vel.z(); imu_object["avz"] = angular_vel.z();
// }
// /* Display calibration status for each sensor. */
// uint8_t system, gyro, accel, mag = 0; void setup_imu(){
// bno.getCalibration(&system, &gyro, &accel, &mag);
// Serial.print("CALIBRATION: Sys="); // IMU Setup
// Serial.print(system, DEC); Serial.println("Setting up IMU");
// Serial.print(" Gyro="); if (!bno.begin()) {
// Serial.print(gyro, DEC); /* There was a problem detecting the BNO055 ... check your connections */
// Serial.print(" Accel="); Serial.print("Ooops, no BNO055 detected ... Check your wiring or I2C ADDR!");
// Serial.print(accel, DEC); while (1);
// Serial.print(" Mag="); }
// Serial.print(mag, DEC); Serial.println("IMU Online. Setting to external crystal.");
int eeAddress = 0;
long bnoID;
bool foundCalib = false;
EEPROM.get(eeAddress, bnoID);
adafruit_bno055_offsets_t calibrationData;
sensor_t sensor;
/*
* Look for the sensor's unique ID at the beginning oF EEPROM.
* This isn't foolproof, but it's better than nothing.
*/
bno.getSensor(&sensor);
eeAddress += sizeof(long);
EEPROM.get(eeAddress, calibrationData);
bno.setSensorOffsets(calibrationData);
bno.setAxisConfig(Adafruit_BNO055::REMAP_CONFIG_P6);
bno.setAxisSign(Adafruit_BNO055::REMAP_SIGN_P6);
bno.setMode(Adafruit_BNO055::OPERATION_MODE_IMUPLUS);
delay(300);
bno.setExtCrystalUse(true);
Serial.println("IMU Configured");
// while(1){
// display_cal_status();
// }
}
void display_cal_status(void)
{
/* Get the four calibration values (0..3) */
/* Any sensor data reporting 0 should be ignored, */
/* 3 means 'fully calibrated" */
uint8_t system, gyro, accel, mag;
system = gyro = accel = mag = 0;
bno.getCalibration(&system, &gyro, &accel, &mag);
/* The data should be ignored until the system calibration is > 0 */
Serial.print("\t");
if (!system)
{
Serial.print("! ");
}
/* Display the individual values */
Serial.print("Sys:");
Serial.print(system, DEC);
Serial.print(" G:");
Serial.print(gyro, DEC);
Serial.print(" A:");
Serial.print(accel, DEC);
Serial.print(" M:");
Serial.println(mag, DEC);
} }
void process_gps_and_send_if_ready(JsonObject &root) { void process_gps_and_send_if_ready(JsonObject &root) {

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 326 KiB

View File

@@ -0,0 +1,73 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="1000"
height="1000"
viewBox="0 0 1000 1000"
id="svg2"
version="1.1"
inkscape:version="0.91 r13725"
sodipodi:docname="rover_icon.svg"
inkscape:export-filename="/home/caperren/Github/Rover_2017_2018/software/ros_packages/ground_station/src/Resources/Images/rover.png"
inkscape:export-xdpi="400"
inkscape:export-ydpi="400">
<defs
id="defs4" />
<sodipodi:namedview
id="base"
pagecolor="#323232"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:zoom="0.35"
inkscape:cx="338.99045"
inkscape:cy="775.33545"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="2556"
inkscape:window-height="1401"
inkscape:window-x="1080"
inkscape:window-y="18"
inkscape:window-maximized="0"
units="px" />
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-52.362161)">
<path
style="fill:#1974df;fill-opacity:1;stroke:#ffffff;stroke-width:26;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 499.63597,74.698266 319.16703,356.39733 c 37.73561,-64.04828 106.4946,-103.40171 180.8327,-103.49752 74.34377,0.10172 143.10405,39.46639 180.8327,103.52539 L 499.63487,74.698266 Z"
id="rect4196"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccccc" />
<circle
style="fill:#1974df;fill-opacity:1;stroke:#ffffff;stroke-width:30;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path4157-6"
cx="500"
cy="552.36218"
r="229.86818" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

@@ -26,8 +26,12 @@ from io import StringIO, BytesIO
import os import os
import time import time
import PIL.ImageDraw import PIL.ImageDraw
import PIL.Image
import PIL.ImageFont
import signing import signing
import RoverMapHelper as MapHelper import RoverMapHelper as MapHelper
import cv2
import numpy as np
##################################### #####################################
# Constants # Constants
@@ -36,7 +40,8 @@ _KEYS = []
# Number of pixels in half the earth's circumference at zoom = 21 # Number of pixels in half the earth's circumference at zoom = 21
_EARTHPIX = 268435456 _EARTHPIX = 268435456
# Number of decimal places for rounding coordinates # Number of decimal places for rounding coordinates
_DEGREE_PRECISION = 4 _DEGREE_PRECISION = 6
_PRECISION_FORMAT = '%.' + str(_DEGREE_PRECISION) + 'f'
# Larget tile we can grab without paying # Larget tile we can grab without paying
_TILESIZE = 640 _TILESIZE = 640
# Fastest rate at which we can download tiles without paying # Fastest rate at which we can download tiles without paying
@@ -113,14 +118,14 @@ class GMapsStitcher(object):
# Make the url string for polling # Make the url string for polling
# GET request header gets appended to the string # GET request header gets appended to the string
urlbase = 'https://maps.googleapis.com/maps/api/staticmap?' urlbase = 'https://maps.googleapis.com/maps/api/staticmap?'
urlbase += 'center=%.4f,%.4f&zoom=%d&maptype=%s' urlbase += 'center=' + _PRECISION_FORMAT + ',' + _PRECISION_FORMAT + '&zoom=%d&maptype=%s'
urlbase += '&size=%dx%d&format=png&key=%s' urlbase += '&size=%dx%d&format=png&key=%s'
# Fill the formatting # Fill the formatting
specs = (self.helper.fast_round(latitude, _DEGREE_PRECISION), specs = (self.helper.fast_round(latitude, _DEGREE_PRECISION),
self.helper.fast_round(longitude, _DEGREE_PRECISION), self.helper.fast_round(longitude, _DEGREE_PRECISION),
self.zoom, self.maptype, _TILESIZE, _TILESIZE, _KEYS[0]) self.zoom, self.maptype, _TILESIZE, _TILESIZE, _KEYS[0])
filename = 'Resources/Maps/' + ('%.4f_%.4f_%d_%s_%d_%d_%s' % specs) filename = 'Resources/Maps/' + ((_PRECISION_FORMAT + '_' + _PRECISION_FORMAT + '_%d_%s_%d_%d_%s') % specs)
filename += '.png' filename += '.png'
# Tile Image object # Tile Image object
@@ -134,7 +139,13 @@ class GMapsStitcher(object):
# make the url # make the url
url = urlbase % specs url = urlbase % specs
url = signing.sign_url(url, _KEYS[1]) url = signing.sign_url(url, _KEYS[1])
try:
result = urllib2.urlopen(urllib2.Request(url)).read() result = urllib2.urlopen(urllib2.Request(url)).read()
except urllib2.HTTPError, e:
print "Error accessing url for reason:", e
print url
return
tile_object = PIL.Image.open(BytesIO(result)) tile_object = PIL.Image.open(BytesIO(result))
if not os.path.exists('Resources/Maps'): if not os.path.exists('Resources/Maps'):
os.mkdir('Resources/Maps') os.mkdir('Resources/Maps')
@@ -348,7 +359,9 @@ class OverlayImage(object):
self.width = width self.width = width
self.height = height self.height = height
self.big_image = None self.big_image = None
self.big_image_copy = None
self.display_image = None self.display_image = None
self.display_image_copy = None
self.indicator = None self.indicator = None
self.helper = MapHelper.MapHelper() self.helper = MapHelper.MapHelper()
@@ -362,6 +375,13 @@ class OverlayImage(object):
self.generate_image_files() self.generate_image_files()
self.write_once = True self.write_once = True
# Text Drawing Variables
self.font = cv2.FONT_HERSHEY_TRIPLEX
self.font_thickness = 1
self.font_baseline = 0
self.nav_coordinates_text_image = None
def generate_image_files(self): def generate_image_files(self):
""" """
Creates big_image and display image sizes Creates big_image and display image sizes
@@ -370,9 +390,15 @@ class OverlayImage(object):
""" """
self.big_image = self.helper.new_image(self.big_width, self.big_height, self.big_image = self.helper.new_image(self.big_width, self.big_height,
True) True)
self.big_image_copy = self.big_image.copy()
self.display_image = self.helper.new_image(self.width, self.height, self.display_image = self.helper.new_image(self.width, self.height,
True) True)
self.generate_dot_and_hat()
self.display_image_copy = self.display_image.copy()
self.load_rover_icon()
self.indicator.save("location.png") self.indicator.save("location.png")
def _get_cartesian(self, lat, lon): def _get_cartesian(self, lat, lon):
@@ -406,43 +432,49 @@ class OverlayImage(object):
def update_new_location(self, latitude, longitude, def update_new_location(self, latitude, longitude,
compass, navigation_list, landmark_list): compass, navigation_list, landmark_list):
self.big_image = self.big_image_copy.copy()
self.display_image = self.display_image_copy.copy()
size = 5 size = 5
draw = PIL.ImageDraw.Draw(self.big_image) draw = PIL.ImageDraw.Draw(self.big_image)
for element in navigation_list: for element in navigation_list:
x, y = self._get_cartesian(float(element[2]), float(element[1])) x, y = self._get_cartesian(float(element[1]), float(element[2]))
draw.ellipse((x-size, y-size, x+size, y+size), fill="red") draw.text((x + 10, y - 5), str(element[0]))
# for element in landmark_list: draw.ellipse((x - size, y - size, x + size, y + size), fill=(element[3].red(), element[3].green(), element[3].blue()))
# x, y = self._get_cartesian(element[1], element[2])
# draw.ellipsis((x-size, y-size, x+size, y+size), fill="blue") for element in landmark_list:
self._draw_rover(longitude, latitude, compass) x, y = self._get_cartesian(element[1], element[2])
self.update() draw.text((x + 10, y - 5), str(element[0]))
draw.ellipse((x - size, y - size, x + size, y + size), fill=(element[3].red(), element[3].green(), element[3].blue()))
self._draw_rover(latitude, longitude, compass)
self.update(latitude, longitude)
return self.display_image return self.display_image
def generate_dot_and_hat(self): def load_rover_icon(self):
self.indicator = self.helper.new_image(100, 100, True) self.indicator = PIL.Image.open("Resources/Images/rover.png").resize((40, 40))
draw = PIL.ImageDraw.Draw(self.indicator)
draw.ellipse((50-12, 50-12, 50+12, 50+12), fill="red")
draw.line((25, 40, 50, 12), fill="red", width=7)
draw.line((50, 12, 75, 40), fill="red", width=7)
def _draw_rover(self, lat, lon, angle=0): def _draw_rover(self, lat, lon, angle=0):
x, y = self._get_cartesian(lat, lon) x, y = self._get_cartesian(lat, lon)
# print x,y
# Center of the circle on the indicator is (12.5, 37.5) x -= 25 # Half the height of the icon
x = x - 50 y -= 25
y = y - 50
rotated = self.indicator.copy() rotated = self.indicator.copy()
rotated = rotated.rotate(angle, expand=True) rotated = rotated.rotate(-angle, resample=PIL.Image.BICUBIC)
rotated.save("rotated.png") # rotated.save("rotated.png")
self.big_image.paste(rotated, (x, y), rotated) self.big_image.paste(rotated, (x, y), rotated)
if self.write_once: if self.write_once:
self.display_image.save("Something.png") # self.display_image.save("Something.png")
self.write_once = False self.write_once = False
def update(self): def update(self, latitude, longitude):
# self.left_x -= 50
# self.upper_y -= 50
self.display_image.paste(self.big_image, (-self.left_x, -self.upper_y)) self.display_image.paste(self.big_image, (-self.left_x, -self.upper_y))
# self._draw_coordinate_text(latitude, longitude)
def connect_signals_and_slots(self): def connect_signals_and_slots(self):
pass pass

View File

@@ -5,14 +5,22 @@
from PyQt5 import QtCore, QtWidgets, QtGui from PyQt5 import QtCore, QtWidgets, QtGui
from PIL.ImageQt import ImageQt from PIL.ImageQt import ImageQt
from PIL import Image from PIL import Image
import PIL.ImageDraw
import PIL.Image
import PIL.ImageFont
import numpy import numpy
import logging import logging
import rospy import rospy
from tf import transformations
from scipy.interpolate import interp1d
import math
from sensor_msgs.msg import Imu
# Custom Imports # Custom Imports
import RoverMap import RoverMap
from Resources.Settings import Mapping as MappingSettings
from sensor_msgs.msg import NavSatFix from sensor_msgs.msg import NavSatFix
##################################### #####################################
@@ -21,6 +29,12 @@ from sensor_msgs.msg import NavSatFix
# put some stuff here later so you can remember # put some stuff here later so you can remember
GPS_POSITION_TOPIC = "/rover_odometry/fix" GPS_POSITION_TOPIC = "/rover_odometry/fix"
IMU_DATA_TOPIC = "/rover_odometry/imu/data"
MAP_WIDGET_WIDTH = float(1280)
MAP_WIDGET_HEIGHT = float(720)
MAP_WIDGET_RATIO = MAP_WIDGET_WIDTH / MAP_WIDGET_HEIGHT
class RoverMapCoordinator(QtCore.QThread): class RoverMapCoordinator(QtCore.QThread):
@@ -32,7 +46,7 @@ class RoverMapCoordinator(QtCore.QThread):
self.shared_objects = shared_objects self.shared_objects = shared_objects
self.left_screen = self.shared_objects["screens"]["left_screen"] self.left_screen = self.shared_objects["screens"]["left_screen"]
self.mapping_label = self.left_screen.mapping_label self.mapping_label = self.left_screen.mapping_label # type:QtWidgets.QLabel
self.navigation_label = self.left_screen.navigation_waypoints_table_widget self.navigation_label = self.left_screen.navigation_waypoints_table_widget
self.landmark_label = self.left_screen.landmark_waypoints_table_widget self.landmark_label = self.left_screen.landmark_waypoints_table_widget
@@ -47,43 +61,65 @@ class RoverMapCoordinator(QtCore.QThread):
self.google_maps_object = None self.google_maps_object = None
self.map_image = None self.map_image = None
self.map_image_copy = None
self.overlay_image = None self.overlay_image = None
self.overlay_image_object = None self.overlay_image_object = None
self.map_pixmap = None self.map_pixmap = QtGui.QPixmap.fromImage(ImageQt(Image.open("Resources/Images/maps_loading.png").resize((1280, 720), Image.BICUBIC)))
self.last_map_pixmap_cache_key = None self.last_map_pixmap_cache_key = None
self.longitude = None self.longitude = 0
self.latitude = None self.latitude = 0
self.last_heading = 0
self.imu_data = None
self.new_imu_data = False
self.yaw = None
self.pitch = None
self.roll = None
self.euler_interpolator = interp1d([math.pi, -math.pi], [-180, 180])
self.imu_data_subscriber = rospy.Subscriber(IMU_DATA_TOPIC, Imu, self.on_imu_data_received)
self.zoom_pan_x_location = 0
self.zoom_pan_y_location = 0
self.zoom_subtraction = 0
self.x_drag = 0
self.y_drag = 0
self.x_drag_start = -1
self.y_drag_start = -1
self.x_drag_end = -1
self.y_drag_end = -1
def run(self): def run(self):
self.logger.debug("Starting Map Coordinator Thread") self.logger.debug("Starting Map Coordinator Thread")
self.pixmap_ready_signal.emit() # This gets us the loading map
while self.run_thread_flag: while self.run_thread_flag:
if self.setup_map_flag: if self.setup_map_flag:
self._map_setup() self._map_setup()
self.setup_map_flag = False self.setup_map_flag = False
else: else:
if self.new_imu_data:
self.calculate_euler_from_imu()
self.new_imu_data = False
self._get_map_image() self._get_map_image()
self.msleep(30) self.msleep(10)
self.logger.debug("Stopping Map Coordinator Thread") self.logger.debug("Stopping Map Coordinator Thread")
# def _setup_map_threads(self):
# self.google_maps_object = RoverMap.GMapsStitcher(1280,
# 720,
# 44.567161,
# -123.278432,
# 18,
# 'satellite',
# None, 20)
def _map_setup(self): def _map_setup(self):
self.google_maps_object = RoverMap.GMapsStitcher(1280, self.google_maps_object = RoverMap.GMapsStitcher(1280,
720, 720,
44.5675721667, 44.5675721667,
-123.2750535, -123.2750535,
20, # FIXME: Used to be 18 18, # FIXME: Used to be 18
'satellite', 'satellite',
None, 20) None, 20)
self.overlay_image_object = ( self.overlay_image_object = (
@@ -97,30 +133,65 @@ class RoverMapCoordinator(QtCore.QThread):
def _get_map_image(self): def _get_map_image(self):
while self.map_image is None: while self.map_image is None:
self.map_image = self.google_maps_object.display_image self.map_image = self.google_maps_object.display_image
# self.overlay_image_object.update_new_location(44.567161,
# -123.278432, if self.map_image:
# .7, self.map_image_copy = self.map_image.copy()
# [],
# [])
self.update_overlay() self.update_overlay()
self.map_image = self.map_image_copy.copy()
self.map_image.paste(self.overlay_image_object.display_image, self.map_image.paste(self.overlay_image_object.display_image,
(0, 0), (0, 0),
self.overlay_image_object.display_image) self.overlay_image_object.display_image)
# self.map_image = Image.alpha_composite(
# self.google_maps_object.display_image, # ##### Zoom testing! #####
# self.overlay_image_object.display_image) if self.zoom_subtraction:
# get overlay here self.zoom_subtraction = self.constrain(self.zoom_subtraction, 0, 520)
crop_x_start = ((self.zoom_subtraction * MAP_WIDGET_RATIO) / 2) - self.x_drag - self.x_drag_end
crop_y_start = (self.zoom_subtraction / 2) - self.y_drag - self.y_drag_end
crop_x_end = (MAP_WIDGET_WIDTH - ((self.zoom_subtraction * MAP_WIDGET_RATIO) / 2)) - self.x_drag - self.x_drag_end
crop_y_end = (MAP_WIDGET_HEIGHT - (self.zoom_subtraction / 2)) - self.y_drag - self.y_drag_end
crop_box = (int(crop_x_start), int(crop_y_start), int(crop_x_end), int(crop_y_end))
self.map_image = self.map_image.crop(crop_box)
self.map_image = self.map_image.resize((1280, 720), resample=PIL.Image.BICUBIC)
# ##### Draw coordinates #####
self._draw_coordinate_text(self.latitude, self.longitude)
qim = ImageQt(self.map_image) qim = ImageQt(self.map_image)
self.map_pixmap = QtGui.QPixmap.fromImage(qim) self.map_pixmap = QtGui.QPixmap.fromImage(qim)
if self.map_pixmap.cacheKey() != self.last_map_pixmap_cache_key: # if self.map_pixmap.cacheKey() != self.last_map_pixmap_cache_key:
self.last_map_pixmap_cache_key = self.map_pixmap.cacheKey() # self.last_map_pixmap_cache_key = self.map_pixmap.cacheKey()
self.pixmap_ready_signal.emit() self.pixmap_ready_signal.emit()
def _draw_coordinate_text(self, latitude, longitude):
if latitude == 0 and longitude == 0:
location_text = "LAT: NO FIX\nLON: NO FIX"
else:
location_text = "LAT: %+014.9f\nLON: %+014.9f" % (latitude, longitude)
font = PIL.ImageFont.truetype("UbuntuMono-R", size=20)
new_image = PIL.Image.new('RGBA', (200, 45), "black")
draw = PIL.ImageDraw.Draw(new_image)
draw.multiline_text((5, 0), location_text, font=font)
self.map_image.paste(new_image, (0, 0))
def connect_signals_and_slots(self): def connect_signals_and_slots(self):
self.pixmap_ready_signal.connect(self.pixmap_ready__slot) self.pixmap_ready_signal.connect(self.pixmap_ready__slot)
self.change_waypoint_signal.connect(self.update_overlay) self.change_waypoint_signal.connect(self.update_overlay)
self.mapping_label.mouseReleaseEvent = self.__mouse_released_event
self.mapping_label.wheelEvent = self.__mouse_wheel_event
self.mapping_label.mouseMoveEvent = self.__mouse_move_event
def on_kill_threads_requested_slot(self): def on_kill_threads_requested_slot(self):
self.run_thread_flag = False self.run_thread_flag = False
@@ -137,31 +208,79 @@ class RoverMapCoordinator(QtCore.QThread):
temp_list = [] temp_list = []
count = UI_element.rowCount() count = UI_element.rowCount()
for row in range(0, count): for row in range(0, count):
num = UI_element.item(row, 0).text() name = UI_element.item(row, 0).text()
lat = float(UI_element.item(row, 1).text()) lat = float(UI_element.item(row, 1).text())
lng = float(UI_element.item(row, 2).text()) lng = float(UI_element.item(row, 2).text())
temp_tuple = (num, lat, lng) color = UI_element.item(row, 3).background().color()
temp_tuple = (name, lat, lng, color)
temp_list.append(temp_tuple) temp_list.append(temp_tuple)
return temp_list return temp_list
def update_overlay(self): def update_overlay(self):
if self.latitude and self.longitude:
if not numpy.isnan(self.latitude) and not numpy.isnan(self.longitude): if not numpy.isnan(self.latitude) and not numpy.isnan(self.longitude):
latitude = float(self.latitude)
longitude = self.latitude longitude = float(self.longitude)
latitude = self.longitude
navigation_list = self._get_table_elements(self.navigation_label) navigation_list = self._get_table_elements(self.navigation_label)
# landmark_list = self._get_table_elements(self.landmark_label) landmark_list = self._get_table_elements(self.landmark_label)
landmark_list = []
self.overlay_image = self.overlay_image_object.update_new_location( self.overlay_image = self.overlay_image_object.update_new_location(
latitude, latitude,
longitude, longitude,
70, self.last_heading,
navigation_list, navigation_list,
landmark_list) landmark_list)
# self.last_heading = (self.last_heading + 5) % 360
# self.overlay_image.save("something.png") # self.overlay_image.save("something.png")
def gps_position_updated_callback(self, data): def gps_position_updated_callback(self, data):
self.latitude = data.latitude self.latitude = data.latitude
self.longitude = data.longitude self.longitude = data.longitude
def on_imu_data_received(self, data):
self.imu_data = data
self.new_imu_data = True
def calculate_euler_from_imu(self):
quat = (
self.imu_data.orientation.x,
self.imu_data.orientation.y,
self.imu_data.orientation.z,
self.imu_data.orientation.w,
)
self.roll, self.pitch, self.yaw = transformations.euler_from_quaternion(quat)
self.last_heading = (self.euler_interpolator(self.yaw) + MappingSettings.DECLINATION_OFFSET) % 360
def __mouse_released_event(self, _):
self.x_drag_start = -1
self.y_drag_start = -1
self.x_drag_end += self.x_drag
self.y_drag_end += self.y_drag
self.x_drag = 0
self.x_drag = 0
def __mouse_wheel_event(self, event):
# print "wheel:", event.angleDelta()
self.zoom_subtraction += event.angleDelta().y() / 12
def __mouse_move_event(self, event):
if self.x_drag_start != -1 and self.x_drag_start != -1:
buttons = event.buttons()
if buttons == QtCore.Qt.LeftButton:
x_pos = event.pos().x()
y_pos = event.pos().y()
dx = x_pos - self.x_drag_start
dy = y_pos - self.y_drag_start
self.x_drag = dx
self.y_drag = dy
else:
self.x_drag_start = event.pos().x()
self.y_drag_start = event.pos().y()
@staticmethod
def constrain(val, min_val, max_val):
return min(max_val, max(min_val, val))

View File

@@ -0,0 +1,47 @@
# coding=utf-8
#####################################
# Imports
#####################################
# Python native imports
from PyQt5 import QtCore, QtWidgets
import logging
from time import time
import paramiko
#####################################
# Global Variables
#####################################
ACCESS_POINT_IP = "192.168.1.20" # The channel only has to be set on the access point. The staion will adjust.
ACCESS_POINT_USER = "ubnt"
ACCESS_POINT_PASSWORD = "rover4lyfe^" # We don't care about this password, don't freak out. Wifi is open anyways...
#####################################
# UbiquitiRadioSettings Class Definition
#####################################
class SSHConsole(QtCore.QThread):
def __init__(self, shared_objects):
super(SSHConsole, self).__init__()
# ########## Reference to class init variables ##########
self.shared_objects = shared_objects
self.left_screen = self.shared_objects["screens"]["left_screen"]
self.ubiquiti_channel_spin_box = self.left_screen.ssh_console_widget # type: QtWidgets.QSpinBox
self.ubiquiti_channel_apply_button = self.left_screen.ubiquiti_channel_apply_button # type: QtWidgets.QPushButton
# ########## Get the settings instance ##########
self.settings = QtCore.QSettings()
# ########## Get the Pick And Plate instance of the logger ##########
self.logger = logging.getLogger("groundstation")
# ########## Thread Flags ##########
self.run_thread_flag = True
# ########## Class Variables ##########
self.connect_signals_and_slots()
def connect_signals_and_slots(self):
pass

View File

@@ -9,6 +9,12 @@ from time import time
import PIL.Image import PIL.Image
from PIL.ImageQt import ImageQt from PIL.ImageQt import ImageQt
from random import random from random import random
import rospy
from tf import transformations
from scipy.interpolate import interp1d
import math
from sensor_msgs.msg import Imu
from Resources.Settings import Mapping as MappingSettings
##################################### #####################################
# Global Variables # Global Variables
@@ -17,6 +23,8 @@ THREAD_HERTZ = 20
ROTATION_SPEED_MODIFIER = 2.5 ROTATION_SPEED_MODIFIER = 2.5
IMU_DATA_TOPIC = "/rover_odometry/imu/data"
##################################### #####################################
# Controller Class Definition # Controller Class Definition
@@ -58,15 +66,30 @@ class SpeedAndHeadingIndication(QtCore.QThread):
self.shown_heading = (self.current_heading + (1.5 * ROTATION_SPEED_MODIFIER)) % 360 self.shown_heading = (self.current_heading + (1.5 * ROTATION_SPEED_MODIFIER)) % 360
self.current_heading_shown_rotation_angle = 0 self.current_heading_shown_rotation_angle = 0
self.last_current_heading_shown = 0 self.last_current_heading_shown = -1000
self.rotation_direction = 1 self.rotation_direction = 1
self.imu_data = None
self.new_imu_data = False
self.yaw = None
self.pitch = None
self.roll = None
self.euler_interpolator = interp1d([math.pi, -math.pi], [-180, 180])
self.imu_data_subscriber = rospy.Subscriber(IMU_DATA_TOPIC, Imu, self.on_imu_data_received)
def run(self): def run(self):
self.on_heading_changed__slot(self.current_heading) self.on_heading_changed__slot(self.current_heading)
while self.run_thread_flag: while self.run_thread_flag:
start_time = time() start_time = time()
if self.new_imu_data:
self.calculate_euler_from_imu()
self.new_imu_data = False
if self.current_heading_changed: if self.current_heading_changed:
self.update_heading_movement() self.update_heading_movement()
self.current_heading_changed = False self.current_heading_changed = False
@@ -77,20 +100,19 @@ class SpeedAndHeadingIndication(QtCore.QThread):
self.msleep(max(int(self.wait_time - time_diff), 0)) self.msleep(max(int(self.wait_time - time_diff), 0))
def calculate_euler_from_imu(self):
quat = (
self.imu_data.orientation.x,
self.imu_data.orientation.y,
self.imu_data.orientation.z,
self.imu_data.orientation.w,
)
self.roll, self.pitch, self.yaw = transformations.euler_from_quaternion(quat)
self.current_heading = (self.euler_interpolator(self.yaw) + MappingSettings.DECLINATION_OFFSET) % 360
def rotate_compass_if_needed(self): def rotate_compass_if_needed(self):
heading_difference = abs(int(self.shown_heading) - self.current_heading)
should_update = False
if heading_difference > ROTATION_SPEED_MODIFIER: self.current_heading_shown_rotation_angle = int(self.current_heading)
self.shown_heading += self.rotation_direction * ROTATION_SPEED_MODIFIER
self.shown_heading %= 360
should_update = True
elif heading_difference != 0:
self.shown_heading = self.current_heading
should_update = True
if should_update:
self.current_heading_shown_rotation_angle = int(self.shown_heading)
if self.current_heading_shown_rotation_angle != self.last_current_heading_shown: if self.current_heading_shown_rotation_angle != self.last_current_heading_shown:
new_compass_image = self.main_compass_image.rotate(self.current_heading_shown_rotation_angle, resample=PIL.Image.BICUBIC) new_compass_image = self.main_compass_image.rotate(self.current_heading_shown_rotation_angle, resample=PIL.Image.BICUBIC)
@@ -98,6 +120,7 @@ class SpeedAndHeadingIndication(QtCore.QThread):
self.compass_pixmap = QtGui.QPixmap.fromImage(ImageQt(new_compass_image)) self.compass_pixmap = QtGui.QPixmap.fromImage(ImageQt(new_compass_image))
self.show_compass_image__signal.emit() self.show_compass_image__signal.emit()
self.heading_text_update_ready__signal.emit(str(self.current_heading_shown_rotation_angle) + "°")
def update_heading_movement(self): def update_heading_movement(self):
current_minus_shown = (self.current_heading - self.shown_heading) % 360 current_minus_shown = (self.current_heading - self.shown_heading) % 360
@@ -126,6 +149,10 @@ class SpeedAndHeadingIndication(QtCore.QThread):
def on_new_compass_image_ready__slot(self): def on_new_compass_image_ready__slot(self):
self.heading_compass_label.setPixmap(self.compass_pixmap) self.heading_compass_label.setPixmap(self.compass_pixmap)
def on_imu_data_received(self, data):
self.imu_data = data
self.new_imu_data = True
def connect_signals_and_slots(self): def connect_signals_and_slots(self):
self.show_compass_image__signal.connect(self.on_new_compass_image_ready__slot) self.show_compass_image__signal.connect(self.on_new_compass_image_ready__slot)
self.heading_text_update_ready__signal.connect(self.heading_text_label.setText) self.heading_text_update_ready__signal.connect(self.heading_text_label.setText)

View File

@@ -2,6 +2,10 @@ from PyQt5 import QtCore, QtWidgets, QtGui
import logging import logging
import rospy import rospy
from sensor_msgs.msg import NavSatFix
GPS_POSITION_TOPIC = "/rover_odometry/fix"
class WaypointsCoordinator(QtCore.QThread): class WaypointsCoordinator(QtCore.QThread):
new_manual_waypoint_entry = QtCore.pyqtSignal(str, float, float, int) new_manual_waypoint_entry = QtCore.pyqtSignal(str, float, float, int)
@@ -20,12 +24,9 @@ class WaypointsCoordinator(QtCore.QThread):
navigation_waypoints_table_widget) navigation_waypoints_table_widget)
self.landmark_label = self.left_screen.landmark_waypoints_table_widget self.landmark_label = self.left_screen.landmark_waypoints_table_widget
self.name_edit_label = (self.left_screen. self.name_edit_label = self.left_screen.manual_waypoint_landmark_name_line_edit
manual_waypoint_landmark_name_line_edit) self.latitude_label = self.left_screen.manual_waypoint_decimal_lattitude_spin_box
self.latitude_label = (self.left_screen. self.longitude_label = self.left_screen.manual_waypoint_decimal_longitude_spin_box
manual_waypoint_decimal_lattitude_spin_box)
self.longitude_label = (self.left_screen.
manual_waypoint_decimal_longitude_spin_box)
self.latitude_degree_label = self.left_screen.manual_waypoint_degrees_lattitude_spin_box self.latitude_degree_label = self.left_screen.manual_waypoint_degrees_lattitude_spin_box
@@ -43,6 +44,13 @@ class WaypointsCoordinator(QtCore.QThread):
self.longitude_card_label = self.left_screen.manual_waypoint_cardinal_longitude_combo_box self.longitude_card_label = self.left_screen.manual_waypoint_cardinal_longitude_combo_box
# Color Labels and Buttons
self.nav_color_label = self.left_screen.manual_waypoint_navigation_color_label
self.nav_color_set_button = self.left_screen.manual_waypoint_navigation_color_set_button
self.landmark_color_label = self.left_screen.manual_waypoint_landmark_color_label
self.landmark_color_set_button = self.left_screen.manual_waypoint_landmark_color_set_button
# Nav Table Buttons # Nav Table Buttons
self.nav_set_button_label = (self.left_screen. self.nav_set_button_label = (self.left_screen.
navigation_waypoints_set_button) navigation_waypoints_set_button)
@@ -70,6 +78,23 @@ class WaypointsCoordinator(QtCore.QThread):
self.logger = logging.getLogger("groundstation") self.logger = logging.getLogger("groundstation")
self.gps_position_subscriber = rospy.Subscriber(GPS_POSITION_TOPIC, NavSatFix,
self.gps_position_updated_callback)
self.longitude = None
self.latitude = None
self.nav_color = QtCore.Qt.yellow
self.landmark_color = QtCore.Qt.blue
self.nav_color_dialog = QtWidgets.QColorDialog()
self.nav_color_dialog.setWindowFlags(
QtCore.Qt.WindowStaysOnTopHint | QtCore.Qt.X11BypassWindowManagerHint | QtCore.Qt.WindowTitleHint | QtCore.Qt.WindowCloseButtonHint)
self.landmark_color_dialog = QtWidgets.QColorDialog()
self.landmark_color_dialog.setWindowFlags(
QtCore.Qt.WindowStaysOnTopHint | QtCore.Qt.X11BypassWindowManagerHint | QtCore.Qt.WindowTitleHint | QtCore.Qt.WindowCloseButtonHint)
def run(self): def run(self):
while self.run_thread_flag: while self.run_thread_flag:
self.msleep(3) self.msleep(3)
@@ -93,13 +118,22 @@ class WaypointsCoordinator(QtCore.QThread):
self.navigation_label.cellClicked.connect(self._on_nav_clicked) self.navigation_label.cellClicked.connect(self._on_nav_clicked)
self.landmark_label.cellClicked.connect(self._on_land_clicked) self.landmark_label.cellClicked.connect(self._on_land_clicked)
def _add_to_table(self, name, lat, lng, dist, table): self.nav_color_set_button.clicked.connect(self.nav_color_dialog.show)
self.landmark_color_set_button.clicked.connect(self.landmark_color_dialog.show)
self.nav_color_dialog.currentColorChanged.connect(self.__on_new_nav_color_selected)
self.landmark_color_dialog.currentColorChanged.connect(self.__on_new_landmark_color_selected)
def _add_to_table(self, name, lat, lng, color, table):
color_table_item = QtWidgets.QTableWidgetItem()
color_table_item.setBackground(color)
count = table.rowCount() count = table.rowCount()
table.insertRow(count) table.insertRow(count)
table.setItem(count, 0, QtWidgets.QTableWidgetItem(name)) table.setItem(count, 0, QtWidgets.QTableWidgetItem(name))
table.setItem(count, 1, QtWidgets.QTableWidgetItem(lat)) table.setItem(count, 1, QtWidgets.QTableWidgetItem(lat))
table.setItem(count, 2, QtWidgets.QTableWidgetItem(lng)) table.setItem(count, 2, QtWidgets.QTableWidgetItem(lng))
table.setItem(count, 3, QtWidgets.QTableWidgetItem(dist)) table.setItem(count, 3, color_table_item)
def _clear_inputs(self): def _clear_inputs(self):
self.name_edit_label.clear() self.name_edit_label.clear()
@@ -107,50 +141,54 @@ class WaypointsCoordinator(QtCore.QThread):
self.longitude_label.clear() self.longitude_label.clear()
def _is_empty_inputs(self): def _is_empty_inputs(self):
if self.name_edit_label.text().isEmpty(): if not self.name_edit_label.text():
return True return True
if self.latitude_label.text().isEmpty(): if not self.latitude_label.text():
return True return True
if self.longitude_label.text().isEmpty(): if not self.longitude_label.text():
return True return True
return False return False
def _nav_add_gps(self): def _nav_add_gps(self):
# request GPS data if self.longitude and self.latitude:
name = self.navigation_label.rowCount() name = self.navigation_label.rowCount()
lat = 44.567200 self._add_to_table(str(name + 1), str(self.latitude),
lng = -123.27860 str(self.longitude), self.nav_color,
distance = 200
self._add_to_table(str(name+1), str(lat),
str(lng), str(distance),
self.navigation_label) self.navigation_label)
self._clear_inputs() self._clear_inputs()
def _nav_save(self): def _nav_save(self):
if not self._is_empty_inputs(): if not self._is_empty_inputs():
lat = self.latitude_label.getText() lat = self.latitude_label.text()
lng = self.longitude_label.getText() lng = self.longitude_label.text()
self.navigation_label.setItem( self.navigation_label.setItem(
self.navigation_table_cur_click, self.navigation_table_cur_click,
1, 1,
QtWidgets.QTableWidgetItem(lat)) QtWidgets.QTableWidgetItem(lat))
self.navigation_label.setItem( self.navigation_label.setItem(
self.navigation_label, self.navigation_table_cur_click,
2, 2,
QtWidgets.QTableWidgetItem(lng)) QtWidgets.QTableWidgetItem(lng))
color_table_item = QtWidgets.QTableWidgetItem()
color_table_item.setBackground(self.nav_color)
self.navigation_label.setItem(
self.navigation_table_cur_click,
3,
color_table_item)
self._clear_inputs() self._clear_inputs()
def _nav_add_manual(self): def _nav_add_manual(self):
# request GPS data # request GPS data
if not self._is_empty_inputs(): if not self._is_empty_inputs():
name = self.navigation_label.rowCount() name = self.navigation_label.rowCount()
lat = self.latitude_label.getText() lat = self.latitude_label.text()
lng = self.longitude_label.getText() lng = self.longitude_label.text()
distance = 200
self._add_to_table(str(name + 1), lat, self._add_to_table(str(name + 1), lat,
lng, str(distance), lng, self.nav_color,
self.navigation_label) self.navigation_label)
self._clear_inputs self._clear_inputs()
def _nav_del(self): def _nav_del(self):
if self.navigation_table_cur_click is not None: if self.navigation_table_cur_click is not None:
@@ -164,23 +202,18 @@ class WaypointsCoordinator(QtCore.QThread):
self._clear_inputs() self._clear_inputs()
def _land_add_gps(self): def _land_add_gps(self):
name = self.name_edit_label.getText() if self.longitude and self.latitude:
lat = 44.19223 name = self.name_edit_label.text()
lng = -123.12394 self._add_to_table(name, str(self.latitude), str(self.longitude), self.landmark_color, self.landmark_label)
distance = 200
self._add_to_table(name, str(lat),
str(lng), str(distance),
self.landmark_label)
self._clear_inputs() self._clear_inputs()
def _land_add_manual(self): def _land_add_manual(self):
if not self._is_empty_inputs(): if not self._is_empty_inputs():
name = self.name_edit_label.getText() name = self.name_edit_label.text()
lat = self.latitude_label.getText() lat = self.latitude_label.text()
lng = self.longitude_label.getText() lng = self.longitude_label.text()
distance = 200
self._add_to_table(name, lat, self._add_to_table(name, lat,
lng, str(distance), lng, self.landmark_color,
self.landmark_label) self.landmark_label)
self._clear_inputs() self._clear_inputs()
@@ -197,9 +230,9 @@ class WaypointsCoordinator(QtCore.QThread):
def _land_save(self): def _land_save(self):
if not self._is_empty_inputs(): if not self._is_empty_inputs():
name = self.name_edit_label.getText() name = self.name_edit_label.text()
lat = self.latitude_label.getText() lat = self.latitude_label.text()
lng = self.longitude_label.getText() lng = self.longitude_label.text()
self.landmark_label.setItem(self.landmark_table_cur_click, 0, self.landmark_label.setItem(self.landmark_table_cur_click, 0,
QtWidgets.QTableWidgetItem(name)) QtWidgets.QTableWidgetItem(name))
@@ -209,6 +242,12 @@ class WaypointsCoordinator(QtCore.QThread):
self.landmark_label.setItem(self.landmark_table_cur_click, 2, self.landmark_label.setItem(self.landmark_table_cur_click, 2,
QtWidgets.QTableWidgetItem(lng)) QtWidgets.QTableWidgetItem(lng))
color_table_item = QtWidgets.QTableWidgetItem()
color_table_item.setBackground(self.landmark_color)
self.landmark_label.setItem(self.landmark_table_cur_click, 3,
color_table_item)
self._clear_inputs() self._clear_inputs()
def setup_signals(self, start_signal, def setup_signals(self, start_signal,
@@ -267,3 +306,19 @@ class WaypointsCoordinator(QtCore.QThread):
float(self.landmark_label.item(row, 2).text()), float(self.landmark_label.item(row, 2).text()),
1 1
) )
def __on_new_nav_color_selected(self, color):
self.nav_color_label.setStyleSheet(
"background-color: rgb(%s, %s, %s)" % (color.red(), color.green(), color.blue()))
self.nav_color = color
def __on_new_landmark_color_selected(self, color):
self.landmark_color_label.setStyleSheet(
"background-color: rgb(%s, %s, %s)" % (color.red(), color.green(), color.blue()))
self.landmark_color = color
def gps_position_updated_callback(self, data):
self.latitude = data.latitude
self.longitude = data.longitude

View File

@@ -7,13 +7,14 @@ from PyQt5 import QtWidgets, QtCore, QtGui, uic
from std_msgs.msg import Empty from std_msgs.msg import Empty
import PIL.Image import PIL.Image
from PIL.ImageQt import ImageQt from PIL.ImageQt import ImageQt
import time
from std_msgs.msg import UInt16 from std_msgs.msg import UInt16
# import Timer # import Timer
REQUEST_UPDATE_TOPIC = "/rover_status/update_requested" REQUEST_UPDATE_TOPIC = "/rover_status/update_requested"
CAMERA_TOPIC_NAME = "/rover_status/camera_status" CAMERA_TOPIC_NAME = "/rover_status/camera_status"
BOGIE_TOPIC_NAME = "/rover_status/bogie_status" BOGIE_TOPIC_NAME = "/rover_status/bogie_status"
FRSKY_TOPIC_NAME = "/rover_status/frsky_status" FRSKY_TOPIC_NAME = "/rover_status/frsky_status"
@@ -25,8 +26,14 @@ CO2_TOPIC_NAME = "/rover_control/tower/status/co2"
COLOR_GREEN = "background-color: darkgreen; border: 1px solid black;" COLOR_GREEN = "background-color: darkgreen; border: 1px solid black;"
COLOR_ORANGE = "background-color: orange; border: 1px solid black;" COLOR_ORANGE = "background-color: orange; border: 1px solid black;"
COLOR_YELLOW = "background-color: rgb(204,204,0); border: 1px solid black; color: black;"
COLOR_RED = "background-color: darkred; border: 1px solid black;" COLOR_RED = "background-color: darkred; border: 1px solid black;"
GPS_BEST_CASE_ACCURACY = 3
LOW_BATTERY_DIALOG_TIMEOUT = 120
CRITICAL_BATTERY_DIALOG_TIMEOUT = 30
class SensorCore(QtCore.QThread): class SensorCore(QtCore.QThread):
# ########## create signals for slots ########## # ########## create signals for slots ##########
@@ -62,6 +69,9 @@ class SensorCore(QtCore.QThread):
battery_voltage_update_ready__signal = QtCore.pyqtSignal(str) battery_voltage_update_ready__signal = QtCore.pyqtSignal(str)
battery_voltage_stylesheet_change_ready__signal = QtCore.pyqtSignal(str) battery_voltage_stylesheet_change_ready__signal = QtCore.pyqtSignal(str)
show_battery_low__signal = QtCore.pyqtSignal()
show_battery_critical__signal = QtCore.pyqtSignal()
def __init__(self, shared_objects): def __init__(self, shared_objects):
super(SensorCore, self).__init__() super(SensorCore, self).__init__()
@@ -120,6 +130,28 @@ class SensorCore(QtCore.QThread):
self.osurc_logo_pixmap = QtGui.QPixmap.fromImage(ImageQt(self.osurc_logo_pil)) self.osurc_logo_pixmap = QtGui.QPixmap.fromImage(ImageQt(self.osurc_logo_pil))
self.osurc_logo_label.setPixmap(self.osurc_logo_pixmap) # Init should be in main thread, should be fine self.osurc_logo_label.setPixmap(self.osurc_logo_pixmap) # Init should be in main thread, should be fine
self.low_battery_warning_dialog = QtWidgets.QMessageBox()
self.low_battery_warning_dialog.setIcon(QtWidgets.QMessageBox.Warning)
self.low_battery_warning_dialog.setText("\n\n\n\nRover battery low!\nReturn and charge soon to avoid battery damage!\n\n\n\n")
self.low_battery_warning_dialog.setWindowTitle("Low Battery")
self.low_battery_warning_dialog.setStandardButtons(QtWidgets.QMessageBox.Ok)
self.low_battery_warning_dialog.setWindowFlags(
QtCore.Qt.WindowStaysOnTopHint | QtCore.Qt.X11BypassWindowManagerHint | QtCore.Qt.WindowTitleHint | QtCore.Qt.WindowCloseButtonHint)
self.low_battery_warning_dialog.setStyleSheet(COLOR_YELLOW)
self.critical_battery_warning_dialog = QtWidgets.QMessageBox()
self.critical_battery_warning_dialog.setIcon(QtWidgets.QMessageBox.Critical)
self.critical_battery_warning_dialog.setText(
"\n\n\n\nRover battery critical!\nPower down immediately or battery damage will occur!\n\n\n\n")
self.critical_battery_warning_dialog.setWindowTitle("Critical Battery")
self.critical_battery_warning_dialog.setStandardButtons(QtWidgets.QMessageBox.Ok)
self.critical_battery_warning_dialog.setWindowFlags(
QtCore.Qt.WindowStaysOnTopHint | QtCore.Qt.X11BypassWindowManagerHint | QtCore.Qt.WindowTitleHint | QtCore.Qt.WindowCloseButtonHint)
self.critical_battery_warning_dialog.setStyleSheet(COLOR_RED)
self.low_battery_warning_last_shown = 0
self.critical_battery_warning_last_shown = 0
def __camera_callback(self, data): def __camera_callback(self, data):
self.camera_msg.camera_zed = data.camera_zed self.camera_msg.camera_zed = data.camera_zed
self.camera_msg.camera_undercarriage = data.camera_undercarriage self.camera_msg.camera_undercarriage = data.camera_undercarriage
@@ -215,7 +247,8 @@ class SensorCore(QtCore.QThread):
self.gps_heading_valid_update_ready__signal.emit("GPS Heading Valid\nFalse") self.gps_heading_valid_update_ready__signal.emit("GPS Heading Valid\nFalse")
self.gps_num_satellites_update_ready__signal.emit("GPS Satellites\n%s" % data.num_satellites) self.gps_num_satellites_update_ready__signal.emit("GPS Satellites\n%s" % data.num_satellites)
self.gps_accuracy_update_ready__signal.emit("GPS Accuracy\n%2.2f m" % data.horizontal_dilution) self.gps_accuracy_update_ready__signal.emit(
"GPS Accuracy\n%2.2f m" % (data.horizontal_dilution * GPS_BEST_CASE_ACCURACY))
def __misc_callback(self, data): def __misc_callback(self, data):
self.misc_msg.arm_connection_status = data.arm_connection_status self.misc_msg.arm_connection_status = data.arm_connection_status
@@ -227,11 +260,22 @@ class SensorCore(QtCore.QThread):
def __battery_callback(self, data): def __battery_callback(self, data):
voltage = data.battery_voltage / 1000.0 voltage = data.battery_voltage / 1000.0
if voltage >= 20: if voltage >= 21:
self.battery_voltage_stylesheet_change_ready__signal.emit(COLOR_GREEN) self.battery_voltage_stylesheet_change_ready__signal.emit(COLOR_GREEN)
elif voltage >= 19:
self.battery_voltage_stylesheet_change_ready__signal.emit(COLOR_YELLOW)
if (time.time() - self.low_battery_warning_last_shown) > LOW_BATTERY_DIALOG_TIMEOUT:
self.show_battery_low__signal.emit()
self.low_battery_warning_last_shown = time.time()
else: else:
self.battery_voltage_stylesheet_change_ready__signal.emit(COLOR_RED) self.battery_voltage_stylesheet_change_ready__signal.emit(COLOR_RED)
if (time.time() - self.critical_battery_warning_last_shown) > CRITICAL_BATTERY_DIALOG_TIMEOUT:
self.show_battery_critical__signal.emit()
self.critical_battery_warning_last_shown = time.time()
self.battery_voltage_update_ready__signal.emit("Battery Voltage\n" + str(voltage) + " V") self.battery_voltage_update_ready__signal.emit("Battery Voltage\n" + str(voltage) + " V")
def __co2_callback(self, data): def __co2_callback(self, data):
@@ -247,7 +291,7 @@ class SensorCore(QtCore.QThread):
def run(self): def run(self):
while self.run_thread_flag: while self.run_thread_flag:
self.update_requester.publish(Empty()) # self.update_requester.publish(Empty())
self.__display_time() self.__display_time()
self.msleep(1000) self.msleep(1000)
@@ -277,6 +321,9 @@ class SensorCore(QtCore.QThread):
self.battery_voltage_update_ready__signal.connect(self.battery.setText) self.battery_voltage_update_ready__signal.connect(self.battery.setText)
self.battery_voltage_stylesheet_change_ready__signal.connect(self.battery.setStyleSheet) self.battery_voltage_stylesheet_change_ready__signal.connect(self.battery.setStyleSheet)
self.show_battery_low__signal.connect(self.low_battery_warning_dialog.show)
self.show_battery_critical__signal.connect(self.critical_battery_warning_dialog.show)
def setup_signals(self, start_signal, signals_and_slots_signal, kill_signal): def setup_signals(self, start_signal, signals_and_slots_signal, kill_signal):
start_signal.connect(self.start) start_signal.connect(self.start)
signals_and_slots_signal.connect(self.connect_signals_and_slots) signals_and_slots_signal.connect(self.connect_signals_and_slots)

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 220 KiB

View File

@@ -0,0 +1,23 @@
# Note that the lat and lon positions below correspond to the center point of the maps you want to download
# Proper zoom level selection determines total viewable area
MAPPING_LOCATIONS = {
"Graf Hall": {
"latitude": 44.5675721667,
"longitude": -123.2750535,
"default_zoom": 18,
"valid_zoom_options": [15, 16, 17, 18, 19, 20],
"pre_cache_map_zoom_levels": [18, 19, 20]
},
"Crystal Lake": {
"latitude": 44.547155,
"longitude": -123.251438,
"default_zoom": 18,
"valid_zoom_options": [15, 16, 17, 18, 19, 20],
"pre_cache_map_zoom_levels": [18, 19, 20]
}
}
# ##### This is the offset from magnetic north to true north
DECLINATION_OFFSET = 15

View File

@@ -1392,224 +1392,13 @@ N/A</string>
<property name="currentIndex"> <property name="currentIndex">
<number>0</number> <number>0</number>
</property> </property>
<widget class="QWidget" name="tab_3"> <widget class="QWidget" name="tab">
<attribute name="title"> <attribute name="title">
<string>Science Readouts</string> <string>SSH Console</string>
</attribute> </attribute>
<layout class="QHBoxLayout" name="horizontalLayout_4"> <layout class="QGridLayout" name="gridLayout_4">
<item>
<layout class="QVBoxLayout" name="verticalLayout_6">
<item>
<widget class="QLabel" name="label_15">
<property name="font">
<font>
<pointsize>12</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Soil Probe</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<layout class="QGridLayout" name="gridLayout_9">
<item row="0" column="0"> <item row="0" column="0">
<widget class="QLabel" name="label_20"> <widget class="QWidget" name="ssh_console_widget" native="true"/>
<property name="text">
<string>Soil Temperature:</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="label_23">
<property name="text">
<string>N/A</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_22">
<property name="text">
<string>Soil PH:</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="label_21">
<property name="text">
<string>N/A</string>
</property>
</widget>
</item>
<item row="0" column="2">
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<spacer name="horizontalSpacer_6">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
<widget class="QPushButton" name="pushButton_11">
<property name="text">
<string>Read Soil Probe</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_5">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer_3">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_7">
<item>
<widget class="QLabel" name="label_19">
<property name="font">
<font>
<pointsize>12</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Dust Sensor</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<layout class="QGridLayout" name="gridLayout_8">
<item row="0" column="0">
<widget class="QLabel" name="label_12">
<property name="text">
<string>Small Dust Present:</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_14">
<property name="text">
<string>Large Dust Present:</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="label_11">
<property name="text">
<string>False</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="label_13">
<property name="text">
<string>False</string>
</property>
</widget>
</item>
<item row="0" column="2">
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer_4">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</item> </item>
</layout> </layout>
</widget> </widget>
@@ -2108,6 +1897,125 @@ N/A</string>
</item> </item>
</layout> </layout>
</item> </item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<property name="spacing">
<number>6</number>
</property>
<item>
<widget class="QLabel" name="label_11">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Color</string>
</property>
</widget>
</item>
<item>
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_4">
<property name="text">
<string>Navigation</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="manual_waypoint_navigation_color_label">
<property name="minimumSize">
<size>
<width>25</width>
<height>25</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>25</width>
<height>25</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">background-color:yellow;</string>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="manual_waypoint_navigation_color_set_button">
<property name="text">
<string>Set</string>
</property>
</widget>
</item>
<item>
<widget class="Line" name="line_7">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_12">
<property name="text">
<string>Landmark</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="manual_waypoint_landmark_color_label">
<property name="minimumSize">
<size>
<width>25</width>
<height>25</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>25</width>
<height>25</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">background-color:blue;</string>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="manual_waypoint_landmark_color_set_button">
<property name="text">
<string>Set</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item> <item>
<widget class="Line" name="line_2"> <widget class="Line" name="line_2">
<property name="orientation"> <property name="orientation">
@@ -2279,22 +2187,26 @@ N/A</string>
</item> </item>
<item> <item>
<widget class="QTableWidget" name="navigation_waypoints_table_widget"> <widget class="QTableWidget" name="navigation_waypoints_table_widget">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>9999999</height>
</size>
</property>
<property name="styleSheet"> <property name="styleSheet">
<string notr="true">QTableView{ <string notr="true"/>
selection-background-color: #DE8D47;
}
QTableView QHeaderView{
background-color: #201F1D;
}
QTableView QHeaderView::section{
background-color: #201F1D;
}
QTableView QTableCornerButton::section{
background-color: #201F1D;
}</string>
</property> </property>
<property name="horizontalScrollBarPolicy"> <property name="horizontalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum> <enum>Qt::ScrollBarAlwaysOff</enum>
@@ -2332,16 +2244,6 @@ QTableView QTableCornerButton::section{
<attribute name="verticalHeaderDefaultSectionSize"> <attribute name="verticalHeaderDefaultSectionSize">
<number>30</number> <number>30</number>
</attribute> </attribute>
<row>
<property name="text">
<string>1</string>
</property>
</row>
<row>
<property name="text">
<string>2</string>
</property>
</row>
<column> <column>
<property name="text"> <property name="text">
<string>#</string> <string>#</string>
@@ -2359,49 +2261,9 @@ QTableView QTableCornerButton::section{
</column> </column>
<column> <column>
<property name="text"> <property name="text">
<string>Dist</string> <string>Color</string>
</property> </property>
</column> </column>
<item row="0" column="0">
<property name="text">
<string>1</string>
</property>
</item>
<item row="0" column="1">
<property name="text">
<string>32.1665</string>
</property>
</item>
<item row="0" column="2">
<property name="text">
<string>-112.1115</string>
</property>
</item>
<item row="0" column="3">
<property name="text">
<string>2.4 KM</string>
</property>
</item>
<item row="1" column="0">
<property name="text">
<string>2</string>
</property>
</item>
<item row="1" column="1">
<property name="text">
<string>33.1124</string>
</property>
</item>
<item row="1" column="2">
<property name="text">
<string>-111.4334</string>
</property>
</item>
<item row="1" column="3">
<property name="text">
<string>5.7 KM</string>
</property>
</item>
</widget> </widget>
</item> </item>
<item> <item>
@@ -2409,6 +2271,9 @@ QTableView QTableCornerButton::section{
<property name="spacing"> <property name="spacing">
<number>2</number> <number>2</number>
</property> </property>
<property name="bottomMargin">
<number>4</number>
</property>
<item> <item>
<widget class="QPushButton" name="navigation_waypoints_set_button"> <widget class="QPushButton" name="navigation_waypoints_set_button">
<property name="sizePolicy"> <property name="sizePolicy">
@@ -2423,11 +2288,13 @@ QTableView QTableCornerButton::section{
<height>35</height> <height>35</height>
</size> </size>
</property> </property>
<property name="font">
<font>
<pointsize>8</pointsize>
</font>
</property>
<property name="styleSheet"> <property name="styleSheet">
<string notr="true">QPushButton{ <string notr="true"/>
color: #201F1D;
background-color: #868685;
}</string>
</property> </property>
<property name="text"> <property name="text">
<string>Set</string> <string>Set</string>
@@ -2448,11 +2315,13 @@ QTableView QTableCornerButton::section{
<height>35</height> <height>35</height>
</size> </size>
</property> </property>
<property name="font">
<font>
<pointsize>8</pointsize>
</font>
</property>
<property name="styleSheet"> <property name="styleSheet">
<string notr="true">QPushButton{ <string notr="true"/>
color: #201F1D;
background-color: #868685;
}</string>
</property> </property>
<property name="text"> <property name="text">
<string>Add <string>Add
@@ -2474,11 +2343,13 @@ Manual</string>
<height>35</height> <height>35</height>
</size> </size>
</property> </property>
<property name="font">
<font>
<pointsize>8</pointsize>
</font>
</property>
<property name="styleSheet"> <property name="styleSheet">
<string notr="true">QPushButton{ <string notr="true"/>
color: #201F1D;
background-color: #868685;
}</string>
</property> </property>
<property name="text"> <property name="text">
<string>Add <string>Add
@@ -2500,11 +2371,13 @@ GPS</string>
<height>35</height> <height>35</height>
</size> </size>
</property> </property>
<property name="font">
<font>
<pointsize>8</pointsize>
</font>
</property>
<property name="styleSheet"> <property name="styleSheet">
<string notr="true">QPushButton{ <string notr="true"/>
color: #201F1D;
background-color: #868685;
}</string>
</property> </property>
<property name="text"> <property name="text">
<string>Delete</string> <string>Delete</string>
@@ -2575,22 +2448,26 @@ GPS</string>
</item> </item>
<item> <item>
<widget class="QTableWidget" name="landmark_waypoints_table_widget"> <widget class="QTableWidget" name="landmark_waypoints_table_widget">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>9999999</height>
</size>
</property>
<property name="styleSheet"> <property name="styleSheet">
<string notr="true">QTableView{ <string notr="true"/>
selection-background-color: #DE8D47;
}
QTableView QHeaderView{
background-color: #201F1D;
}
QTableView QHeaderView::section{
background-color: #201F1D;
}
QTableView QTableCornerButton::section{
background-color: #201F1D;
}</string>
</property> </property>
<property name="horizontalScrollBarPolicy"> <property name="horizontalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum> <enum>Qt::ScrollBarAlwaysOff</enum>
@@ -2631,16 +2508,6 @@ QTableView QTableCornerButton::section{
<attribute name="verticalHeaderDefaultSectionSize"> <attribute name="verticalHeaderDefaultSectionSize">
<number>30</number> <number>30</number>
</attribute> </attribute>
<row>
<property name="text">
<string>1</string>
</property>
</row>
<row>
<property name="text">
<string>2</string>
</property>
</row>
<column> <column>
<property name="text"> <property name="text">
<string>Name</string> <string>Name</string>
@@ -2658,49 +2525,9 @@ QTableView QTableCornerButton::section{
</column> </column>
<column> <column>
<property name="text"> <property name="text">
<string>Dist</string> <string>Color</string>
</property> </property>
</column> </column>
<item row="0" column="0">
<property name="text">
<string>Astronaut</string>
</property>
</item>
<item row="0" column="1">
<property name="text">
<string>32.1665</string>
</property>
</item>
<item row="0" column="2">
<property name="text">
<string>-112.1115</string>
</property>
</item>
<item row="0" column="3">
<property name="text">
<string>2.4 KM</string>
</property>
</item>
<item row="1" column="0">
<property name="text">
<string>Water</string>
</property>
</item>
<item row="1" column="1">
<property name="text">
<string>33.1124</string>
</property>
</item>
<item row="1" column="2">
<property name="text">
<string>-111.4334</string>
</property>
</item>
<item row="1" column="3">
<property name="text">
<string>5.7 KM</string>
</property>
</item>
</widget> </widget>
</item> </item>
<item> <item>
@@ -2708,6 +2535,9 @@ QTableView QTableCornerButton::section{
<property name="spacing"> <property name="spacing">
<number>2</number> <number>2</number>
</property> </property>
<property name="bottomMargin">
<number>4</number>
</property>
<item> <item>
<widget class="QPushButton" name="landmark_waypoints_set_button"> <widget class="QPushButton" name="landmark_waypoints_set_button">
<property name="sizePolicy"> <property name="sizePolicy">
@@ -2722,11 +2552,13 @@ QTableView QTableCornerButton::section{
<height>35</height> <height>35</height>
</size> </size>
</property> </property>
<property name="font">
<font>
<pointsize>8</pointsize>
</font>
</property>
<property name="styleSheet"> <property name="styleSheet">
<string notr="true">QPushButton{ <string notr="true"/>
color: #201F1D;
background-color: #868685;
}</string>
</property> </property>
<property name="text"> <property name="text">
<string>Set</string> <string>Set</string>
@@ -2747,11 +2579,13 @@ QTableView QTableCornerButton::section{
<height>35</height> <height>35</height>
</size> </size>
</property> </property>
<property name="font">
<font>
<pointsize>8</pointsize>
</font>
</property>
<property name="styleSheet"> <property name="styleSheet">
<string notr="true">QPushButton{ <string notr="true"/>
color: #201F1D;
background-color: #868685;
}</string>
</property> </property>
<property name="text"> <property name="text">
<string>Add <string>Add
@@ -2773,11 +2607,13 @@ Manual</string>
<height>35</height> <height>35</height>
</size> </size>
</property> </property>
<property name="font">
<font>
<pointsize>8</pointsize>
</font>
</property>
<property name="styleSheet"> <property name="styleSheet">
<string notr="true">QPushButton{ <string notr="true"/>
color: #201F1D;
background-color: #868685;
}</string>
</property> </property>
<property name="text"> <property name="text">
<string>Add <string>Add
@@ -2799,11 +2635,13 @@ GPS</string>
<height>35</height> <height>35</height>
</size> </size>
</property> </property>
<property name="font">
<font>
<pointsize>8</pointsize>
</font>
</property>
<property name="styleSheet"> <property name="styleSheet">
<string notr="true">QPushButton{ <string notr="true"/>
color: #201F1D;
background-color: #868685;
}</string>
</property> </property>
<property name="text"> <property name="text">
<string>Delete</string> <string>Delete</string>

View File

@@ -26,6 +26,8 @@ public:
node_handle->param("device_path", capture_device_path, std::string("/dev/video0")); node_handle->param("device_path", capture_device_path, std::string("/dev/video0"));
node_handle->param("fps", fps, 30); node_handle->param("fps", fps, 30);
node_handle->param("upside_down", upside_down, false);
node_handle->param("large_image_width", large_image_width, 1280); node_handle->param("large_image_width", large_image_width, 1280);
node_handle->param("large_image_height", large_image_height, 720); node_handle->param("large_image_height", large_image_height, 720);
node_handle->param("medium_image_width", medium_image_width, 640); node_handle->param("medium_image_width", medium_image_width, 640);
@@ -97,30 +99,22 @@ public:
} }
if(!image_large.empty()){ if(!image_large.empty()){
bool large_ready_to_broadcast = false; if(upside_down){
bool medium_ready_to_broadcast = false; cv::flip(image_large, image_large, -1);
bool small_ready_to_broadcast = false; }
if(broadcast_large_image){ if(broadcast_large_image){
large_image_message = cv_bridge::CvImage(std_msgs::Header(), "bgr8", image_large).toImageMsg(); large_image_message = cv_bridge::CvImage(std_msgs::Header(), "bgr8", image_large).toImageMsg();
large_ready_to_broadcast = true; large_image_publisher.publish(large_image_message);
} }else if(broadcast_medium_image){
if(broadcast_medium_image){
cv::resize(image_large, image_medium, cv::Size(medium_image_width, medium_image_height)); cv::resize(image_large, image_medium, cv::Size(medium_image_width, medium_image_height));
medium_image_message = cv_bridge::CvImage(std_msgs::Header(), "bgr8", image_medium).toImageMsg(); medium_image_message = cv_bridge::CvImage(std_msgs::Header(), "bgr8", image_medium).toImageMsg();
medium_ready_to_broadcast = true; medium_image_publisher.publish(medium_image_message);
} }else if(broadcast_small_image){
if(broadcast_small_image){
cv::resize(image_large, image_small, cv::Size(small_image_width, small_image_height)); cv::resize(image_large, image_small, cv::Size(small_image_width, small_image_height));
small_image_message = cv_bridge::CvImage(std_msgs::Header(), "bgr8", image_small).toImageMsg(); small_image_message = cv_bridge::CvImage(std_msgs::Header(), "bgr8", image_small).toImageMsg();
small_ready_to_broadcast = true; small_image_publisher.publish(small_image_message);
} }
if(large_ready_to_broadcast){ large_image_publisher.publish(large_image_message); };
if(medium_ready_to_broadcast){ medium_image_publisher.publish(medium_image_message); };
if(small_ready_to_broadcast){ small_image_publisher.publish(small_image_message); };
} }
ros::spinOnce(); ros::spinOnce();
@@ -152,6 +146,8 @@ private:
ros::Rate *loop_rate; ros::Rate *loop_rate;
bool upside_down;
int large_image_width; int large_image_width;
int large_image_height; int large_image_height;
int medium_image_width; int medium_image_width;

View File

@@ -83,7 +83,7 @@ class DriveCoordinator(object):
try: try:
self.process_drive_commands() self.process_drive_commands()
except Exception, error: except Exception, error:
print "Error occurred:", error print "COORDINATOR: Error occurred:", error
time_diff = time() - start_time time_diff = time() - start_time

View File

@@ -118,11 +118,9 @@ class IrisController(object):
self.broadcast_drive_if_current_mode() self.broadcast_drive_if_current_mode()
self.broadcast_arm_if_current_mode() self.broadcast_arm_if_current_mode()
self.broadcast_iris_status() self.broadcast_iris_status()
self.iris_last_seen_time = time()
except Exception, error: except Exception, error:
print "Error occurred:", error print "IRIS: Error occurred:", error
return
if (time() - self.iris_last_seen_time) > IRIS_LAST_SEEN_TIMEOUT: if (time() - self.iris_last_seen_time) > IRIS_LAST_SEEN_TIMEOUT:
print "Iris not seen for", IRIS_LAST_SEEN_TIMEOUT, "seconds. Exiting." print "Iris not seen for", IRIS_LAST_SEEN_TIMEOUT, "seconds. Exiting."
@@ -135,6 +133,7 @@ class IrisController(object):
def read_registers(self): def read_registers(self):
try: try:
self.registers = self.iris.read_registers(0, len(MODBUS_REGISTERS)) self.registers = self.iris.read_registers(0, len(MODBUS_REGISTERS))
self.iris_last_seen_time = time()
except Exception, error: except Exception, error:
self.iris_connected = False self.iris_connected = False

View File

@@ -3,6 +3,7 @@
<!-- Start Undercarriage Camera --> <!-- Start Undercarriage Camera -->
<node name="undercarriage" pkg="rover_camera" type="rover_camera" launch-prefix="taskset -c 1" respawn="true" output="screen"> <node name="undercarriage" pkg="rover_camera" type="rover_camera" launch-prefix="taskset -c 1" respawn="true" output="screen">
<param name="device_path" value="/dev/rover/camera_undercarriage"/> <param name="device_path" value="/dev/rover/camera_undercarriage"/>
<param name="upside_down" value="true"/>
</node> </node>
<!--&lt;!&ndash; Start Main Navigation Camera &ndash;&gt;--> <!--&lt;!&ndash; Start Main Navigation Camera &ndash;&gt;-->
@@ -17,7 +18,7 @@
<!--&lt;!&ndash; Start End Effector Camera &ndash;&gt;--> <!--&lt;!&ndash; Start End Effector Camera &ndash;&gt;-->
<node name="end_effector" pkg="rover_camera" type="rover_camera" launch-prefix="taskset -c 4" respawn="true" output="screen" > <node name="end_effector" pkg="rover_camera" type="rover_camera" launch-prefix="taskset -c 4" respawn="true" output="screen" >
<param name="is_rtsp_camera" value="True" /> <param name="is_rtsp_camera" value="true" />
<param name="device_path" value="rtsp://192.168.1.11" /> <param name="device_path" value="rtsp://192.168.1.11" />
</node> </node>
</group> </group>

View File

@@ -9,10 +9,8 @@
<param name="port" value="/dev/rover/ttyREAR"/> <param name="port" value="/dev/rover/ttyREAR"/>
<param name="drive_control_topic" value="drive_control/rear"/> <param name="drive_control_topic" value="drive_control/rear"/>
<param name="drive_control_status_topic" value="drive_status/rear"/> <param name="drive_control_status_topic" value="drive_status/rear"/>
<param name="first_motor_id" value="1"/> <param name="first_motor_id" value="2"/>
<param name="second_motor_id" value="2"/> <param name="second_motor_id" value="1"/>
<param name="invert_first_motor" value="True"/>
<param name="invert_second_motor" value="True"/>
</node> </node>
<node name="left_bogie" pkg="rover_control" type="drive_control.py" respawn="true" output="screen"> <node name="left_bogie" pkg="rover_control" type="drive_control.py" respawn="true" output="screen">

View File

@@ -32,7 +32,7 @@
<!--<param name="base_link_frame" value="base_link"/>--> <!--<param name="base_link_frame" value="base_link"/>-->
<!--<param name="world_frame" value="odom"/>--> <!--<param name="world_frame" value="odom"/>-->
<!--<param name="odom0" value="odometry/gps"/>--> <!--<param name="odom0" value="fix"/>-->
<!--<param name="imu0" value="imu/data"/>--> <!--<param name="imu0" value="imu/data"/>-->
<!--<rosparam param="odom0_config">[true, true, false,--> <!--<rosparam param="odom0_config">[true, true, false,-->

View File

@@ -153,7 +153,8 @@
<rosparam param="topics"> <rosparam param="topics">
[ [
{name: "/rover_status/battery_status", compress: false, rate: 1.0}, {name: "/rover_status/battery_status", compress: false, rate: 1.0},
{name: "/rover_control/tower/status/co2", compress: false, rate: 1.0} {name: "/rover_control/tower/status/co2", compress: false, rate: 1.0},
{name: "/rover_odometry/imu/data", compress: false, rate: 10.0},
] ]
</rosparam> </rosparam>
</node> </node>

View File

@@ -28,6 +28,8 @@ DEFAULT_IMU_TOPIC = "imu/data"
DEFAULT_HERTZ = 100 DEFAULT_HERTZ = 100
ODOM_LAST_SEEN_TIMEOUT = 1 # seconds
##################################### #####################################
# DriveControl Class Definition # DriveControl Class Definition
@@ -50,6 +52,8 @@ class Odometry(object):
self.sentence_publisher = rospy.Publisher(self.gps_sentence_topic, Sentence, queue_size=1) self.sentence_publisher = rospy.Publisher(self.gps_sentence_topic, Sentence, queue_size=1)
self.imu_data_publisher = rospy.Publisher(self.imu_data_topic, Imu, queue_size=1) self.imu_data_publisher = rospy.Publisher(self.imu_data_topic, Imu, queue_size=1)
self.odom_last_seen_time = time()
self.run() self.run()
def run(self): def run(self):
@@ -58,9 +62,14 @@ class Odometry(object):
try: try:
self.process_messages() self.process_messages()
self.odom_last_seen_time = time()
except Exception, error: except Exception, error:
print "Error occurred:", error pass
if (time() - self.odom_last_seen_time) > ODOM_LAST_SEEN_TIMEOUT:
print "Odometry not seen for", ODOM_LAST_SEEN_TIMEOUT, "seconds. Exiting."
return # Exit so respawn can take over
time_diff = time() - start_time time_diff = time() - start_time