Exemplo n.º 1
0
class RoboQuasar(Interface):
    def __init__(self, set_num=-1):
        self.gps = GPS()
        self.imu = IMU()
        self.turret = LidarTurret()

        self.steering = Steering()
        self.brakes = Brakes()
        self.underglow = Underglow()

        joystick = None

        if only_lidar:
            self.gps.enabled = False
            self.imu.enabled = False
            self.steering.enabled = False
            self.brakes.enabled = False
            self.underglow.enabled = False
        elif not simulated:
            joystick = WiiUJoystick()

        if args.includelidar:
            self.turret.enabled = False

        # self.imu_plot = RobotPlot("Magnetometer data (x, z vs. time)", flat_plot=True, skip_count=20,
        #                           plot_enabled=False)
        self.kalman_plots = KalmanPlots(not only_lidar, use_filter, animate)

        if animate:
            if args.includelidar:
                self.turret.point_cloud_plot.enabled = False
            self.live_plot = LivePlotter(2,
                                         self.kalman_plots.filter_comparison,
                                         self.turret.point_cloud_plot,
                                         skip_count=10)

            if self.turret.point_cloud_plot.enabled:
                self.live_plot.draw_dot(self.turret.point_cloud_plot,
                                        0,
                                        0,
                                        color='orange',
                                        markersize=5)
        elif plots_enabled:
            self.static_plot = StaticPlotter(
                1, self.kalman_plots.filter_comparison)

        self.filter = None

        # m = np.array(MapFile("buggy course checkpoints").map)
        # self.map_plot.update(m[:, 0], m[:, 1])

        file_sets = (
            ("16;49", "2017_Feb_17"),  # default, latest

            # data day 4
            # ("15", "data_days/2017_Feb_14"),  # filter explodes, LIDAR interfered by the sun
            # ("16;20", "data_days/2017_Feb_14"),  # shorten run, LIDAR collapsed
            # ("16;57", "data_days/2017_Feb_14"),  # interfered LIDAR
            # ("17;10", "data_days/2017_Feb_14"),  # all data is fine, interfered LIDAR
            # ("17;33", "data_days/2017_Feb_14"),  # data is fine, normal run

            # data day 3
            # ("16;38", "data_days/2017_Feb_08"),
            # ("17", "data_days/2017_Feb_08"),
            # ("18", "data_days/2017_Feb_08"),

            # trackfield no gyro values
            # ("15;46", "old_data/2016_Dec_02"),
            # ("15;54", "old_data/2016_Dec_02"),
            # ("16;10", "old_data/2016_Dec_02"),
            # ("16;10", "old_data/2016_Dec_02")

            # rolls day 1
            # ("07;22", "old_data/2016_Nov_06"),  # bad gyro values

            # rolls day 2
            # ("07;36;03 m", "old_data/2016_Nov_12"),
            # ("09;12", "old_data/2016_Nov_12"),  # invalid values
            # ("07;04;57", "old_data/2016_Nov_13"),  # bad gyro values

            # rolls day 3
            # ("modified 07;04", "old_data/2016_Nov_13"),
            # ("modified 07;23", "old_data/2016_Nov_13"),  # wonky value for mag.

            # rolling on the cut
            # ("16;29", "old_data/2016_Dec_09"),
            # ("16;49", "old_data/2016_Dec_09"),
            # ("16;5", "old_data/2016_Dec_09"),
            # ("17;", "old_data/2016_Dec_09"),  # nothing wrong, really short

            # bad data
            # ("16;07", "old_data/2016_Dec_09/bad_data"),  # nothing wrong, really short
            # ("16;09", "old_data/2016_Dec_09/bad_data"),  # nothing wrong, really short
            # ("18;00", "old_data/2016_Dec_09/bad_data"),  # gps spazzed out
            # ("18;02", "old_data/2016_Dec_09/bad_data"),  # gps spazzed out
            # ("18;09", "old_data/2016_Dec_09/bad_data"),  # gps spazzed out
        )

        self.checkpoint_num = 0

        self.lat1, self.long1, self.alt1 = 0, 0, 0
        self.lat2, self.long2, self.alt2 = 0, 0, 0
        self.num_recv_from_fix = None
        self.received_fix = False

        self.imu_t0 = 0
        self.gps_t0 = 0
        self.lidar_t0 = 0

        if simulated:
            super(RoboQuasar, self).__init__(
                file_sets[set_num][0],
                file_sets[set_num][1],
                self.gps,
                self.imu,
                self.turret,
                start_index=10000,
                # end_index=2000,
            )

            extension_index = self.parser.full_path.rfind(".")
            image_path = self.parser.full_path[:extension_index]
        else:
            super(RoboQuasar, self).__init__(self.imu,
                                             self.gps,
                                             self.steering,
                                             self.brakes,
                                             self.underglow,
                                             self.turret,
                                             joystick=joystick,
                                             debug_prints=args.verbose,
                                             log_data=args.log)

            extension_index = self.logger.full_path.rfind(".")
            image_path = self.logger.full_path[:extension_index]

        self.slam = SLAM(self.turret, image_path + " post map")
        self.slam_plots = SlamPlots(self.slam.map_size_pixels,
                                    self.slam.map_size_meters,
                                    args.computeslam)

    def init_filter(self, timestamp):
        self.lat2, self.long2, self.alt2 = self.gps.latitude_deg, self.gps.longitude_deg, self.gps.altitude
        roll, pitch, yaw = get_gps_orientation(self.lat1, self.long1,
                                               self.alt1, self.lat2,
                                               self.long2, self.alt2)

        yaw = math.atan2(self.lat2 - self.lat1, self.long2 - self.long1)
        print(math.degrees(roll), math.degrees(pitch), math.degrees(yaw))

        self.filter = GrovesKalmanFilter(initial_roll=roll,
                                         initial_pitch=pitch,
                                         initial_yaw=yaw,
                                         initial_lat=self.lat2,
                                         initial_long=self.long2,
                                         initial_alt=self.alt2,
                                         **constants)

        self.imu_t0 = timestamp
        self.gps_t0 = timestamp
        self.lidar_t0 = timestamp

        self.kalman_plots.update_gps(self.gps.latitude_deg,
                                     self.gps.longitude_deg, self.gps.altitude)
        self.kalman_plots.update_kalman(self.filter.get_position())

        # length = math.sqrt(lat1 ** 2 + long1 ** 2)
        lat2 = 0.0003 * math.sin(yaw) + self.lat1
        long2 = 0.0003 * math.cos(yaw) + self.long1
        self.kalman_plots.initial_compass_plot.updated([self.lat1, lat2],
                                                       [self.long1, long2])

    def gps_in_range(self):
        if not 280 < self.gps.altitude < 310:
            self.gps.altitude = 300.0
        if -80 < self.gps.longitude_deg < -79.8 and 40.4 < self.gps.latitude_deg < 41 and (
                280 < self.gps.altitude < 310 or self.gps.altitude == 0.0):
            return True
        else:
            return False

    def update_gps_plot(self):
        if self.gps_in_range():
            self.kalman_plots.update_gps(self.gps.latitude_deg,
                                         self.gps.longitude_deg,
                                         self.gps.altitude)
            return True
        else:
            return False

    def update_epoch(self, timestamp):
        if use_filter:
            if self.gps.fix and self.gps.latitude is not None and self.received_fix != self.gps.fix:
                self.received_fix = True
                self.num_recv_from_fix = self.num_received(self.gps)

            if self.num_recv_from_fix is not None and self.num_received(
                    self.gps) - self.num_recv_from_fix == 245:
                self.lat1, self.long1, self.alt1 = self.gps.latitude_deg, self.gps.longitude_deg, self.gps.altitude

            elif self.filter is None and self.num_recv_from_fix is not None and \
                    self.gps_in_range() and self.num_received(self.gps) - self.num_recv_from_fix >= 250:
                if use_filter:
                    self.init_filter(timestamp)

            elif self.filter is not None and self.num_recv_from_fix is not None:
                # if self.num_received(self.gps) % 5 == 0:
                if self.update_gps_plot():
                    if use_filter:
                        self.filter.gps_updated(timestamp - self.gps_t0,
                                                self.gps.latitude_deg,
                                                self.gps.longitude_deg,
                                                self.gps.altitude)
                        self.kalman_plots.update_kalman(
                            self.filter.get_position())
                        self.gps_t0 = timestamp

                        lat1, long1, alt1 = self.filter.get_position()
                        roll, pitch, yaw = self.filter.get_orientation()

                        yaw = -self.imu.euler.x
                        # declination = 9, 19
                        #
                        # self.declination = \
                        #     (declination[0] + declination[1] / 60) * math.pi / 180
                        #
                        # yaw = math.atan2(self.imu.mag.y, self.imu.mag.x)
                        # yaw += self.declination
                        # # Correct for reversed heading
                        # if yaw < 0:
                        #     yaw += 2 * math.pi
                        # # Check for wrap and compensate
                        # elif yaw > 2 * math.pi:
                        #     yaw -= 2 * math.pi

                        # length = math.sqrt(lat1 ** 2 + long1 ** 2)
                        lat2 = 0.0003 * math.sin(math.radians(yaw)) + lat1
                        long2 = 0.0003 * math.cos(math.radians(yaw)) + long1

                        self.kalman_plots.update_compass(
                            lat1, lat2, long1, long2)

                        # print(self.gps.latitude_deg, self.gps.longitude_deg, self.gps.altitude)
                        # if use_filter:
                        # print(self.filter.get_position(), self.filter.properties.estimated_velocity.T.tolist())
                        # print(math.degrees(roll), math.degrees(pitch), math.degrees(yaw))

                    if animate and self.live_plot.should_update():
                        if self.live_plot.plot() is False:
                            return False

    def update_ins(self, timestamp):
        if use_filter and self.filter is not None and self.imu_t0 != timestamp:
            self.filter.imu_updated(timestamp - self.imu_t0,
                                    self.imu.linaccel.x, self.imu.linaccel.y,
                                    self.imu.linaccel.z, self.imu.gyro.x,
                                    self.imu.gyro.y, self.imu.gyro.z)
            self.kalman_plots.update_kalman(self.filter.get_position())

            self.imu_t0 = timestamp

    def update_turret(self, timestamp):
        if not args.includelidar and self.turret.did_cloud_update():
            if self.turret.is_cloud_ready() and self.filter is not None and (
                    self.num_received(self.gps) - self.num_recv_from_fix >
                    250):
                v = self.filter.get_velocity()
                vx = v[0]
                vy = v[1]

                ang_v = self.filter.get_angular(timestamp - self.lidar_t0)[2]
                self.slam.update(timestamp, self.turret.distances,
                                 [vx, vy, ang_v])
                self.lidar_t0 = timestamp
                if animate:
                    self.slam_plots.update(self.slam.mapbytes,
                                           self.slam.get_pos())

                    # print(self.slam.get_pos(), (vx, vy, ang_v))

            if animate and not self.live_plot.plot():
                return False

    # ----- live methods -----
    def packet_received(self, timestamp, whoiam, packet):
        if self.did_receive(self.imu):
            if self.update_ins(timestamp) is False:
                return False
        elif self.did_receive(self.gps):
            if self.update_epoch(timestamp) is False:
                return False
            print(timestamp)
            print(self.gps)
            print(self.imu)
        elif self.did_receive(self.turret):
            if self.update_turret(timestamp) is False:
                return False
        else:
            print(packet)
            # elif self.did_receive(self.steering):# and self.steering.goal_reached:
            # print(self.steering.current_step)

    def loop(self):
        if not simulated:
            if self.joystick is not None:
                if self.steering.calibrated:
                    if self.joystick.axis_updated("left x"):
                        self.steering.set_speed(
                            self.joystick.get_axis("left x"))
                    elif self.joystick.button_updated(
                            "A") and self.joystick.get_button("A"):
                        self.steering.set_position(0)

                if self.joystick.button_updated(
                        "B") and self.joystick.get_button("B"):
                    self.brakes.pull()
                elif self.joystick.button_updated(
                        "X") and self.joystick.get_button("X"):
                    self.brakes.release()
                elif self.joystick.button_updated(
                        "Y") and self.joystick.get_button("Y"):
                    self.record(
                        "checkpoint", "%s\t%s\t%s" %
                        (self.checkpoint_num, self.gps.latitude_deg,
                         self.gps.longitude_deg))
                    if self.gps.latitude_deg is not None and self.gps.longitude_deg is not None:
                        print("checkpoint #%s: %3.6f, %3.6f" %
                              (self.checkpoint_num, self.gps.latitude_deg,
                               self.gps.longitude_deg))
                    self.checkpoint_num += 1
                    print("checkpoint #%s" % self.checkpoint_num)
                    # elif self.joystick.dpad_updated():
                    #     if self.joystick.dpad[1] == 1:
                    #         self.brakes.set_brake(self.brakes.goal_position + 20)
                    #     elif self.joystick.dpad[1] == -1:
                    #         self.brakes.set_brake(self.brakes.goal_position - 20)

    def start(self):
        if animate:
            self.live_plot.start_time(self.start_time)

    # ----- simulator methods -----

    def object_packet(self, timestamp):
        if self.did_receive(self.imu):
            if self.update_ins(timestamp) is False:
                return False
        elif self.did_receive(self.gps):
            if self.update_epoch(timestamp) is False:
                return False
        elif self.did_receive(self.turret):
            if self.update_turret(timestamp) is False:
                return False

    # def user_packet(self, timestamp, packet):
    # if self.did_receive("checkpoint"):
    #     print(timestamp, packet)

    # def command_packet(self, timestamp, packet):
    #     if self.did_receive(self.steering):
    #         print(timestamp, packet)

    def close(self):
        if plots_enabled:
            if not animate:
                self.static_plot.plot()
                self.static_plot.show()
            else:
                if simulated:
                    if not self.error_signalled:
                        print("Finished!")
                        self.live_plot.freeze_plot()

                self.live_plot.close()

        if args.computeslam:
            self.slam.make_image()
Exemplo n.º 2
0
class RoboQuasar(Robot):
    def __init__(self,
                 enable_plotting,
                 map_set_name,
                 initial_compass=None,
                 animate=True,
                 enable_cameras=True,
                 use_log_file_maps=True,
                 show_cameras=None,
                 day_mode=False,
                 only_cameras=False,
                 enable_pid=True,
                 enable_kalman=True):

        # ----- initialize robot objects -----
        self.gps = GPS()
        self.imu = IMU()

        self.steering = Steering()
        self.brakes = Brakes()
        self.underglow = Underglow()

        super(RoboQuasar, self).__init__(
            self.gps,
            self.imu,
            self.steering,
            self.brakes,
            self.underglow,
        )

        self.manual_mode = True

        # ----- init CV classes ------
        if show_cameras is None:
            show_cameras = enable_plotting
        self.only_cameras = only_cameras
        if self.only_cameras:
            self.enable_cameras = True
        self.camera = Camera("rightcam",
                             enabled=enable_cameras,
                             show=show_cameras)

        self.day_mode = day_mode
        self.pipeline = Pipeline(self.camera,
                                 self.day_mode,
                                 enabled=False,
                                 separate_read_thread=False)

        # ----- init filters and controllers -----
        # position state message (in or out of map)
        self.position_state = ""
        self.prev_pos_state = self.position_state

        # init controller
        self.map_set_name = map_set_name
        checkpoint_map_name, inner_map_name, outer_map_name, map_dir = map_sets[
            map_set_name]

        self.init_checkpoints = [
            0,  # 0, hill 1
            17,  # 1, hill 2
            121,  # 2, hill 3
            127,  # 3, hill 4
            143,  # 4, hill 5
        ]
        self.init_offset = 12
        self.map_manipulator = MapManipulator(
            checkpoint_map_name,
            map_dir,
            inner_map_name,
            outer_map_name,
            offset=2,
            init_indices=self.init_checkpoints)
        self.pid = None
        self.enable_pid = enable_pid

        self.gps_disabled = False
        self.prev_gps_disabled = False
        self.disabled_gps_checkpoints = [
            (0, 12),
            # (13, 17),
            (130, len(self.map_manipulator) - 1)
        ]
        self.angle_filter = AngleManipulator(initial_compass)

        self.use_log_file_maps = use_log_file_maps

        # ----- filtered outputs -----
        self.imu_heading_guess = 0.0
        self.steering_angle = 0.0
        self.map_heading = 0.0
        self.gps_corrected_lat = 0.0
        self.gps_corrected_long = 0.0
        self.left_limit = None
        self.map_lat = 0.0
        self.map_long = 0.0

        self.kalman = None
        self.gps_t0 = 0.0
        self.imu_t0 = 0.0

        # ----- dashboard (parameters to tweak) -----
        self.map_weight = 0.15
        self.kp = 0.9
        self.kd = 0.0
        self.ki = 0.0

        self.angle_threshold = math.radians(10)
        self.percent_threshold = 0.4

        self.enable_drift_correction = False
        self.enable_kalman = enable_kalman

        # ----- drift correction -----
        self.lat_drift = 0.0
        self.long_drift = 0.0
        self.drift_corrected = False
        self.lat_drift_data = []
        self.long_drift_data = []
        self.bearing_data = []

        # ----- status variables -----
        self.gps_within_map = True
        self.prev_within_map_state = True
        self.prev_gps_state = False
        self.uncertainty_condition = False
        self.prev_pipeline_value_state = False
        self.prev_index = -1

        # ----- link callbacks -----
        self.link_object(self.gps, self.receive_gps)
        self.link_object(self.imu, self.receive_imu)

        self.link_reoccuring(0.008, self.steering_event)
        self.link_reoccuring(0.01, self.brake_ping)

        # ----- init plots -----
        self.quasar_plotter = RoboQuasarPlotter(
            animate, enable_plotting, self.enable_kalman,
            self.map_manipulator.map, self.map_manipulator.inner_map,
            self.map_manipulator.outer_map, self.key_press, self.map_set_name,
            False)

    def start(self):
        # extract camera file name from current log file name
        file_name = self.get_path_info("file name no extension").replace(
            ";", "_")
        directory = self.get_path_info("input dir")

        # start cameras and pipelines
        self.open_cameras(file_name, directory, "avi")
        self.pipeline.start()

        # record important data
        self.record("map set", self.map_set_name)
        self.record_compass()
        if self.day_mode is not None:
            self.record("pipeline mode", str(int(self.day_mode)))

        self.debug_print("Using %s mode pipelines" %
                         ("day" if self.day_mode else "night"),
                         ignore_flag=True)

        self.brakes.unpause_pings()

        # self.left_limit = abs(self.steering.left_limit_angle / 3)
        self.pid = PID(
            self.kp, self.kd, self.ki
        )  # self.steering.left_limit_angle, self.steering.right_limit_angle)
        print(self.dt())

    # ----- camera initialization -----

    def open_cameras(self, log_name, directory, file_format):
        # create log name
        # cam_name = "%s%s.%s" % (log_name, self.camera.name, file_format)
        cam_name = "%s.%s" % (log_name, file_format)

        if self.logger is None:
            record = True
        else:
            record = self.logger.is_open()

        # launch a live camera if the robot is live, otherwise launch a video
        if self.is_live:
            status = self.camera.launch_camera(cam_name,
                                               directory,
                                               record,
                                               capture_number=1)
            if status is not None:
                return status
        else:
            self.camera.launch_video(cam_name, directory)

    # ----- sensor received -----

    def receive_gps(self, timestamp, packet, packet_type):
        is_valid = self.gps.is_position_valid()

        if is_valid != self.prev_gps_state:
            self.prev_gps_state = is_valid
            if is_valid:
                print("GPS has locked on:")
                print(self.gps)
            else:
                print("WARNING. GPS has lost connection!!!")

        if is_valid:
            if not self.map_manipulator.is_initialized():
                self.map_manipulator.initialize(self.gps.latitude_deg,
                                                self.gps.longitude_deg)

            if self.enable_kalman and self.kalman is not None:
                self.kalman.gps_updated(timestamp - self.gps_t0,
                                        self.gps.latitude_deg,
                                        self.gps.longitude_deg,
                                        self.gps.altitude)
                position = self.kalman.get_position()
                self.quasar_plotter.kalman_recomputed_plot.append(
                    position[0], position[1])
                self.gps_t0 = timestamp

            self.gps_corrected_lat = self.gps.latitude_deg
            self.gps_corrected_long = self.gps.longitude_deg

            if self.drift_corrected:
                if self.enable_drift_correction:
                    self.gps_corrected_lat += self.lat_drift
                    self.gps_corrected_long += self.long_drift

                outer_state = self.map_manipulator.point_inside_outer_map(
                    self.gps_corrected_lat, self.gps_corrected_long)
                inner_state = self.map_manipulator.point_outside_inner_map(
                    self.gps_corrected_lat, self.gps_corrected_long)
                self.gps_within_map = outer_state and inner_state

                if self.prev_within_map_state != self.gps_within_map and not self.gps_within_map:
                    print(
                        "WARNING. Drift correction has been applied but GPS is still out of the map."
                    )
                self.prev_within_map_state = self.gps_within_map

                self.quasar_plotter.update_map_colors(timestamp, outer_state,
                                                      inner_state)

                self.map_heading = self.map_manipulator.get_goal_angle(
                    self.gps_corrected_lat, self.gps_corrected_long)

                self.map_lat, self.map_long = self.map_manipulator.map[
                    self.map_manipulator.current_index]
                self.gps_corrected_lat = self.gps_corrected_lat * (
                    1 - self.map_weight) + self.map_lat * self.map_weight
                self.gps_corrected_long = self.gps_corrected_long * (
                    1 - self.map_weight) + self.map_long * self.map_weight

                if self.map_manipulator.current_index > self.init_checkpoints[
                        1]:
                    self.angle_filter.update_bearing(self.gps_corrected_lat,
                                                     self.gps_corrected_long)

                self.quasar_plotter.corrected_gps_plot.append(
                    self.gps_corrected_lat, self.gps_corrected_long)

        if self.gps.fix:
            status = self.quasar_plotter.update_gps_plot(
                timestamp, self.gps.latitude_deg, self.gps.longitude_deg)
            if status is not None:
                return status

    def receive_imu(self, timestamp, packet, packet_type):
        if self.only_cameras:
            self.check_pipeline()
            self.update_steering()
            return

        if (self.angle_filter.initialized and self.drift_corrected
                and self.gps.is_position_valid()
                and self.map_manipulator.is_initialized()):
            self.imu_heading_guess = self.angle_filter.offset_angle(
                self.imu.euler.z)

            # assign steering angle from IMU regardless of the pipeline,
            # in case the pipeline finds it's too close but sees the buggy is straight. In this case, the pipeline
            # would not assign a new angle

            self.prev_gps_disabled = self.gps_disabled
            self.gps_disabled = False
            goal_checkpoint = None
            current_checkpoint = None
            for checkpoint_set in self.disabled_gps_checkpoints:
                if checkpoint_set[
                        0] <= self.map_manipulator.current_index <= checkpoint_set[
                            1]:
                    self.gps_disabled = True
                    current_checkpoint = checkpoint_set[0]
                    goal_checkpoint = checkpoint_set[1]

                    if self.prev_gps_disabled != self.gps_disabled:
                        if self.gps_disabled:
                            self.debug_print(
                                "Ignoring GPS, driving straight until:",
                                checkpoint_set[1],
                                ignore_flag=True)
                        else:
                            self.debug_print(
                                "Reapplying GPS, current index:",
                                self.map_manipulator.current_index,
                                ignore_flag=True)
                    break

            if self.enable_kalman:
                if self.kalman is not None:
                    self.kalman.imu_updated(timestamp - self.imu_t0,
                                            self.imu.accel.x, self.imu.accel.y,
                                            self.imu.accel.z, self.imu.gyro.x,
                                            self.imu.gyro.y, self.imu.gyro.z)
                    self.imu_t0 = timestamp

                    position = self.kalman.get_position()
                    current_lat = position[0]
                    current_long = position[1]
                    # orientation = self.kalman.get_orientation()

                    angle_error = self.map_manipulator.update(
                        current_lat,
                        current_long,
                        self.imu_heading_guess  #orientation[2]
                    )
                    goal_lat = self.map_manipulator.goal_lat
                    goal_long = self.map_manipulator.goal_long
                else:
                    return

            else:
                if self.gps_disabled:
                    # self.check_pipeline()
                    current_lat, current_long = self.map_manipulator.map[
                        current_checkpoint]

                    angle_error = self.map_manipulator.update(
                        current_lat,
                        current_long,
                        self.imu_heading_guess,
                        goal_num=goal_checkpoint)
                    goal_lat, goal_long = self.map_manipulator.map[
                        goal_checkpoint]

                else:
                    angle_error = self.map_manipulator.update(
                        self.gps_corrected_lat, self.gps_corrected_long,
                        self.imu_heading_guess)

                    current_lat = self.gps_corrected_lat
                    current_long = self.gps_corrected_long

                    goal_lat = self.map_manipulator.goal_lat
                    goal_long = self.map_manipulator.goal_long

            if self.map_manipulator.current_index != self.prev_index:
                print("Current map location:",
                      self.map_manipulator.current_index)
                self.prev_index = self.map_manipulator.current_index

            if self.enable_pid:
                self.steering_angle = self.pid.update(angle_error, self.dt())
            else:
                self.steering_angle = angle_error

            self.quasar_plotter.update_indicators(
                (current_lat, current_long),
                (goal_lat, goal_long),
                self.imu_heading_guess,
                self.steering_angle,
            )

            self.update_steering()

    def received(self, timestamp, whoiam, packet, packet_type):
        if self.did_receive("initial compass"):
            self.angle_filter.init_compass(packet)
            self.debug_print("initial offset: %0.4f rad" %
                             self.angle_filter.compass_angle,
                             ignore_flag=True)

        elif self.did_receive("maps"):
            if self.use_log_file_maps:
                if "," in packet:
                    checkpoint_map_name, inner_map_name, outer_map_name, map_dir = packet.split(
                        ",")
                else:
                    checkpoint_map_name, inner_map_name, outer_map_name, map_dir = map_sets[
                        packet]
                    self.quasar_plotter.plot_image(packet)
                self.map_manipulator.init_maps(checkpoint_map_name, map_dir,
                                               inner_map_name, outer_map_name)

                self.quasar_plotter.update_maps(self.map_manipulator.map,
                                                self.map_manipulator.inner_map,
                                                self.map_manipulator.outer_map)

        elif self.did_receive("drift correction"):
            if int(packet) == 1:
                # self.calibrate_with_checkpoint()
                self.calibrate_imu()
            # elif int(packet) == 2:

        elif self.did_receive("pipeline mode"):
            day_mode = bool(int(packet))
            self.pipeline.day_mode = day_mode
            self.debug_print("Switching to %s mode" %
                             ("day" if day_mode else "night"),
                             ignore_flag=True)

    # ----- autonomous steering -----

    def update_steering(self):
        if (self.angle_filter.initialized
                and self.drift_corrected) or self.only_cameras:
            if self.left_limit is not None and self.steering_angle > self.left_limit:  # never turn left too much
                self.steering_angle = self.left_limit

            if not self.manual_mode:
                self.steering.set_position(self.steering_angle)

    def check_pipeline(self):
        if self.pipeline.safety_value > self.percent_threshold:
            if not self.prev_pipeline_value_state:
                print("Pipeline detected edge above threshold: %0.4f, %0.4f" %
                      (self.pipeline.safety_value, self.pipeline.line_angle))
                self.prev_pipeline_value_state = True

            if not (0 < self.pipeline.line_angle <
                    self.angle_threshold) or self.only_cameras:
                self.steering_angle = -self.pipeline.line_angle
                print("CV angle: %0.4f" % self.pipeline.line_angle)
            else:
                self.steering_angle = 0

            # steer more if the curb is closer
            self.steering_angle -= self.pipeline.safety_value * abs(
                self.steering.right_limit_angle)

        elif self.prev_pipeline_value_state:
            print(
                "IMU is taking over steering. Steering: %s, IMU: %s, Map: %s" %
                (self.steering_angle, self.imu_heading_guess,
                 self.map_heading))
            self.prev_pipeline_value_state = False

    def calibrate_with_checkpoint(self):
        if self.gps.is_position_valid():
            self.record("drift correction", 1)

            locked_lat, locked_long, locked_index = self.map_manipulator.lock_onto_map(
                self.gps.latitude_deg, self.gps.longitude_deg, True)
            self.lat_drift = locked_lat - self.gps.latitude_deg
            self.long_drift = locked_long - self.gps.longitude_deg

            # next_lat, next_long = self.map_manipulator.map[locked_index + self.init_offset]
            # bearing = self.angle_filter.bearing_to(locked_lat, locked_long, next_lat, next_long)
            # bearing = math.atan2(next_long - locked_long,
            #                      next_lat - locked_lat)
            # bearing %= 2 * math.pi
            # self.angle_filter.init_compass(bearing)
            # self.record_compass()

            self.debug_print("\n=====\n"
                             "\tCorrecting GPS"
                             "\tDrift: (%0.8f, %0.8f)\n"
                             "=====" % (self.lat_drift, self.long_drift),
                             ignore_flag=True)
            # self.debug_print("\tBearing: %0.6frad, %0.6fdeg\n"
            #                  "=====" % (bearing, math.degrees(bearing)), ignore_flag=True)
            if self.drift_corrected:
                self.debug_print("\tDrift already corrected", ignore_flag=True)
            self.drift_corrected = True

        else:
            self.debug_print("Invalid GPS. Ignoring", ignore_flag=True)

    def calibrate_imu(self):
        if self.gps.is_position_valid():
            self.record("drift correction", 2)

            locked_lat, locked_long, locked_index = self.map_manipulator.lock_onto_map(
                self.gps.latitude_deg, self.gps.longitude_deg, True)

            next_lat, next_long = self.map_manipulator.map[locked_index +
                                                           self.init_offset]
            bearing = math.atan2(next_long - locked_long,
                                 next_lat - locked_lat)
            bearing %= 2 * math.pi
            self.angle_filter.init_compass(bearing)
            self.record_compass()

            self.debug_print("\n=====\n" "\tCorrecting IMU", ignore_flag=True)
            self.debug_print("\tBearing: %0.6frad, %0.6fdeg\n"
                             "=====" % (bearing, math.degrees(bearing)),
                             ignore_flag=True)
            if self.drift_corrected:
                self.debug_print("\tIMU already corrected", ignore_flag=True)
            self.drift_corrected = True

            if self.enable_kalman and self.kalman is None:
                self.kalman = GrovesKalmanFilter(initial_roll=0.0,
                                                 initial_pitch=0.0,
                                                 initial_yaw=bearing,
                                                 initial_lat=locked_lat,
                                                 initial_long=locked_long,
                                                 initial_alt=self.gps.altitude,
                                                 **constants)

        else:
            self.debug_print("Invalid GPS. Ignoring", ignore_flag=True)

    def record_compass(self):
        if self.angle_filter.initialized:
            self.record("initial compass",
                        self.angle_filter.compass_angle_packet)
            self.debug_print("initial offset: %0.4f rad" %
                             self.angle_filter.compass_angle,
                             ignore_flag=True)

    # ----- manual steering -----

    def loop(self):
        if self.is_paused:
            status = self.quasar_plotter.check_paused(self.dt())
            if status is not None:
                return status

        self.update_joystick()

    def update_joystick(self):
        if self.joystick is not None:
            if self.steering.calibrated and self.manual_mode:
                if self.joystick.axis_updated(
                        "left x") or self.joystick.axis_updated("left y"):
                    mag = math.sqrt(
                        self.joystick.get_axis("left y")**2 +
                        self.joystick.get_axis("left x")**2)
                    if mag > 0.5:
                        angle = math.atan2(self.joystick.get_axis("left y"),
                                           self.joystick.get_axis("left x"))
                        angle -= math.pi / 2
                        if angle < -math.pi:
                            angle += 2 * math.pi
                        self.steering.set_position(angle / 4)
                elif self.joystick.button_updated(
                        "A") and self.joystick.get_button("A"):
                    self.steering.calibrate()
                elif self.joystick.dpad_updated():
                    if self.joystick.dpad[1] != 0:
                        self.steering.set_position(0)

            if self.joystick.button_updated("X") and self.joystick.get_button(
                    "X"):
                self.manual_mode = not self.manual_mode
                self.debug_print("Manual control enabled" if self.manual_mode
                                 else "Autonomous mode enabled",
                                 ignore_flag=True)

            elif self.joystick.button_updated("L"):
                if self.joystick.get_button("L"):
                    self.brakes.release()
                    self.underglow.signal_release()
                else:
                    self.brakes.pull()
                    self.underglow.signal_brake()

            elif self.joystick.button_updated(
                    "R") and self.joystick.get_button("R"):
                self.brakes.toggle()
                if self.brakes.engaged:
                    self.underglow.signal_brake()
                else:
                    self.underglow.signal_release()

            elif self.joystick.button_updated(
                    "Y") and self.joystick.get_button("Y"):
                self.calibrate_with_checkpoint()

            elif self.joystick.button_updated(
                    "B") and self.joystick.get_button("B"):
                self.calibrate_imu()

    @staticmethod
    def my_round(x, d=0):
        p = 10**d
        return float(math.floor((x * p) + math.copysign(0.5, x))) / p

    @staticmethod
    def sigmoid(x):  # modified sigmoid. -1...1
        return (-1 / (1 + math.exp(-x)) + 0.5) * 2

    # ----- extra events -----

    def brake_ping(self):
        self.brakes.ping()

    def steering_event(self):
        if self.steering.calibrated and self.manual_mode:
            joy_val = self.joystick.get_axis("right x")

            delta_step = int(-self.my_round(8 * joy_val))
            if abs(delta_step) > 0:
                self.steering.change_step(delta_step)

    def key_press(self, event):
        if event.key == " ":
            self.toggle_pause()
            if isinstance(self.quasar_plotter.plotter, LivePlotter):
                self.quasar_plotter.plotter.toggle_pause()

            self.pipeline.paused = not self.pipeline.paused
        elif event.key == "q":
            self.quasar_plotter.close("exit")
            self.close("exit")
        elif event.key == "[":
            if self.parser is not None:
                print(self.parser.index, end=" -> ")
                self.parser.index += 500
                print(self.parser.index, self.current_timestamp)
        elif event.key == "]":
            if self.parser is not None:
                print(self.parser.index, end=" -> ")
                self.parser.index += 5000
                print(self.parser.index, self.current_timestamp)
                self.pid.error_sum = 0

    def close(self, reason):
        if reason != "done":
            self.brakes.pull()
            self.debug_print("!!EMERGENCY BRAKE!!", ignore_flag=True)
        else:
            self.brakes.pause_pings()
        self.quasar_plotter.close(reason)
        self.pipeline.close()
        print("Ran for %0.4fs" % self.dt())
Exemplo n.º 3
0
class CameraGuidanceTest(Robot):
    def __init__(self, enable_cameras=True, show_cameras=False):

        # ----- initialize robot objects -----

        self.steering = Steering()
        self.brakes = Brakes()
        self.underglow = Underglow()

        super(CameraGuidanceTest, self).__init__(
            self.steering,
            self.brakes,
            self.underglow,
        )

        self.manual_mode = True

        # ----- init CV classes ------

        self.left_camera = Camera("leftcam",
                                  enabled=enable_cameras,
                                  show=show_cameras)
        self.right_camera = Camera("rightcam",
                                   enabled=False,
                                   show=show_cameras)
        self.left_pipeline = Pipeline(self.left_camera,
                                      separate_read_thread=False)
        self.right_pipeline = Pipeline(self.right_camera,
                                       separate_read_thread=False)

        # ----- init filters and controllers
        # position state message (in or out of map)
        self.position_state = ""
        self.prev_pos_state = self.position_state

        # init controller
        self.steering_angle = 0.0

        self.link_reoccuring(0.008, self.steering_event)

    def start(self):
        # extract camera file name from current log file name
        file_name = self.get_path_info("file name no extension").replace(
            ";", "_")
        directory = self.get_path_info("input dir")

        # start cameras and pipelines
        self.open_cameras(file_name, directory, "mp4")
        self.left_pipeline.start()
        self.right_pipeline.start()

    def open_cameras(self, log_name, directory, file_format):
        # create log name
        left_cam_name = "%s%s.%s" % (log_name, self.left_camera.name,
                                     file_format)
        right_cam_name = "%s%s.%s" % (log_name, self.right_camera.name,
                                      file_format)

        if self.logger is None:
            record = True
        else:
            record = self.logger.is_open()

        # launch a live camera if the robot is live, otherwise launch a video
        if self.is_live:
            status = self.left_camera.launch_camera(left_cam_name,
                                                    directory,
                                                    record,
                                                    capture_number=1)
            if status is not None:
                return status

            status = self.right_camera.launch_camera(right_cam_name,
                                                     directory,
                                                     record,
                                                     capture_number=2)
            if status is not None:
                return status
        else:
            self.left_camera.launch_video(left_cam_name,
                                          directory,
                                          start_frame=0)
            self.right_camera.launch_video(right_cam_name, directory)

    def received(self, timestamp, whoiam, packet, packet_type):
        self.left_pipeline.update_time(self.dt())

    def loop(self):
        status = self.update_pipeline()
        if status is not None:
            return status
        self.update_joystick()

    def steering_event(self):
        self.brakes.ping()
        if self.steering.calibrated and self.manual_mode:
            # if self.joystick.get_axis("ZR") >= 1.0:
            joy_val = self.joystick.get_axis("right x")
            if abs(joy_val) > 0.0:
                offset = math.copysign(0.3, joy_val)
                joy_val -= offset

            delta_step = self.my_round(16 * self.sigmoid(10.0 * joy_val))
            self.steering.change_step(delta_step)

    @staticmethod
    def my_round(x, d=0):
        p = 10**d
        return float(math.floor((x * p) + math.copysign(0.5, x))) / p

    @staticmethod
    def sigmoid(x):  # modified sigmoid. -1...1
        return (-1 / (1 + math.exp(-x)) + 0.5) * 2

    def update_joystick(self):
        if self.joystick is not None:
            if self.steering.calibrated and self.manual_mode:
                if self.joystick.axis_updated(
                        "left x") or self.joystick.axis_updated("left y"):
                    mag = math.sqrt(
                        self.joystick.get_axis("left y")**2 +
                        self.joystick.get_axis("left x")**2)
                    if mag > 0.5:
                        angle = math.atan2(self.joystick.get_axis("left y"),
                                           self.joystick.get_axis("left x"))
                        angle -= math.pi / 2
                        if angle < -math.pi:
                            angle += 2 * math.pi
                        self.steering.set_position(angle / 4)
                elif self.joystick.button_updated(
                        "A") and self.joystick.get_button("A"):
                    self.steering.calibrate()
                elif self.joystick.dpad_updated():
                    # if self.joystick.dpad[0] != 0:
                    #     self.steering.change_position(-self.joystick.dpad[0] * 10)
                    if self.joystick.dpad[1] != 0:
                        self.steering.set_position(0)

            if self.joystick.button_updated("X") and self.joystick.get_button(
                    "X"):
                self.manual_mode = not self.manual_mode
                print("Manual control enabled" if self.
                      manual_mode else "Autonomous mode enabled")

            elif self.joystick.button_updated("L"):
                if self.joystick.get_button("L"):
                    self.brakes.release()
                    self.underglow.signal_release()
                    # self.delay_function(1.5, self.dt(), self.underglow.rainbow_cycle)
                else:
                    self.brakes.pull()
                    self.underglow.signal_brake()
                    # self.delay_function(1.5, self.dt(), self.underglow.rainbow_cycle)

            elif self.joystick.button_updated(
                    "R") and self.joystick.get_button("R"):
                self.brakes.toggle()
                if self.brakes.engaged:
                    self.underglow.signal_brake()
                    # self.delay_function(1.5, self.dt(), self.underglow.rainbow_cycle)
                else:
                    self.underglow.signal_release()
                    # self.delay_function(1.5, self.dt(), self.underglow.rainbow_cycle)

    def update_pipeline(self):
        if not self.manual_mode:
            if self.left_pipeline.did_update():
                value = self.left_pipeline.safety_value
                if value > self.left_pipeline.safety_threshold:
                    new_angle = self.steering_angle + self.steering.left_limit_angle * value
                    print(new_angle)
                    self.steering.set_position(new_angle)
                    self.gps_imu_control_enabled = False
                else:
                    self.gps_imu_control_enabled = True

            if self.right_pipeline.did_update():
                value = self.right_pipeline.safety_value
                if value > self.right_pipeline.safety_threshold:
                    new_angle = self.steering_angle + self.steering.right_limit_angle * value
                    self.steering.set_position(new_angle)
                    self.gps_imu_control_enabled = False
                else:
                    self.gps_imu_control_enabled = True

            if self.left_pipeline.status is not None:
                return self.left_pipeline.status

            if self.left_pipeline.did_pause():
                self.toggle_pause()

            if self.right_pipeline.status is not None:
                return self.right_pipeline.status

    def close(self, reason):
        if reason != "done":
            self.brakes.pull()
            self.debug_print("!!EMERGENCY BRAKE!!", ignore_flag=True)

        self.left_pipeline.close()
        self.right_pipeline.close()

        print("Ran for %0.4fs" % self.dt())