예제 #1
0
    def test_Duration(self):
        Duration = rospy.Duration

        # test from_sec
        v = Duration(1000)
        self.assertEquals(v, Duration.from_sec(v.to_sec()))
        self.assertEquals(v, v.from_sec(v.to_sec()))
        v = Duration(0, 1000)
        self.assertEquals(v, Duration.from_sec(v.to_sec()))
        self.assertEquals(v, v.from_sec(v.to_sec()))

        # test neg
        v = -Duration(1, -1)
        self.assertEquals(-1, v.secs)
        self.assertEquals(1, v.nsecs)
        v = -Duration(-1, -1)
        self.assertEquals(1, v.secs)
        self.assertEquals(1, v.nsecs)
        v = -Duration(-1, 1)
        self.assertEquals(0, v.secs)
        self.assertEquals(999999999, v.nsecs)

        # test addition
        failed = False
        try:
            v = Duration(1, 0) + Time(1, 0)
            failed = True
        except:
            pass
        self.failIf(failed, "Duration + Time must fail")
        try:
            v = Duration(1, 0) + 1
            failed = True
        except:
            pass
        self.failIf(failed, "Duration + int must fail")

        v = Duration(1, 0) + Duration(1, 0)
        self.assertEquals(2, v.secs)
        self.assertEquals(0, v.nsecs)
        self.assertEquals(Duration(2, 0), v)
        v = Duration(-1, -1) + Duration(1, 1)
        self.assertEquals(0, v.secs)
        self.assertEquals(0, v.nsecs)
        self.assertEquals(Duration(), v)
        v = Duration(1) + Duration(0, 1000000000)
        self.assertEquals(2, v.secs)
        self.assertEquals(0, v.nsecs)
        self.assertEquals(Duration(2), v)
        v = Duration(100, 100) + Duration(300)
        self.assertEquals(Duration(400, 100), v)
        v = Duration(100, 100) + Duration(300, 300)
        self.assertEquals(Duration(400, 400), v)
        v = Duration(100, 100) + Duration(300, -101)
        self.assertEquals(Duration(399, 999999999), v)

        # test subtraction
        try:
            v = Duration(1, 0) - 1
            failed = True
        except:
            pass
        self.failIf(failed, "Duration - non duration must fail")
        try:
            v = Duration(1, 0) - Time(1, 0)
            failed = True
        except:
            pass
        self.failIf(failed, "Duration - Time must fail")

        v = Duration(1, 0) - Duration(1, 0)
        self.assertEquals(Duration(), v)
        v = Duration(-1, -1) - Duration(1, 1)
        self.assertEquals(Duration(-3, 999999998), v)
        v = Duration(1) - Duration(0, 1000000000)
        self.assertEquals(Duration(), v)
        v = Duration(2) - Duration(0, 1000000000)
        self.assertEquals(Duration(1), v)
        v = Duration(100, 100) - Duration(300)
        self.assertEquals(Duration(-200, 100), v)
        v = Duration(100, 100) - Duration(300, 101)
        self.assertEquals(Duration(-201, 999999999), v)

        # test abs
        self.assertEquals(abs(Duration()), Duration())
        self.assertEquals(abs(Duration(1)), Duration(1))
        self.assertEquals(abs(Duration(-1)), Duration(1))
        self.assertEquals(abs(Duration(0, -1)), Duration(0, 1))
        self.assertEquals(abs(Duration(-1, -1)), Duration(1, 1))
    def test_Duration(self):
        Duration = rospy.Duration

        # test from_sec
        v = Duration(1000)
        self.assertEquals(v, Duration.from_sec(v.to_sec()))
        self.assertEquals(v, v.from_sec(v.to_sec()))
        v = Duration(0,1000)
        self.assertEquals(v, Duration.from_sec(v.to_sec()))
        self.assertEquals(v, v.from_sec(v.to_sec()))
      
        # test neg
        v = -Duration(1, -1)
        self.assertEquals(-1, v.secs)
        self.assertEquals(1, v.nsecs)
        v = -Duration(-1, -1)
        self.assertEquals(1, v.secs)
        self.assertEquals(1, v.nsecs)
        v = -Duration(-1, 1)
        self.assertEquals(0, v.secs)
        self.assertEquals(999999999, v.nsecs)
      
        # test addition
        failed = False
        try:
            v = Duration(1,0) + Time(1, 0)
            failed = True
        except: pass
        self.failIf(failed, "Duration + Time must fail")
        try:
            v = Duration(1,0) + 1
            failed = True
        except: pass
        self.failIf(failed, "Duration + int must fail")
          
        v = Duration(1,0) + Duration(1, 0)
        self.assertEquals(2, v.secs)
        self.assertEquals(0, v.nsecs)
        self.assertEquals(Duration(2, 0), v)
        v = Duration(-1,-1) + Duration(1, 1)
        self.assertEquals(0, v.secs)
        self.assertEquals(0, v.nsecs)
        self.assertEquals(Duration(), v)
        v = Duration(1) + Duration(0, 1000000000)
        self.assertEquals(2, v.secs)
        self.assertEquals(0, v.nsecs)
        self.assertEquals(Duration(2), v)
        v = Duration(100, 100) + Duration(300)
        self.assertEquals(Duration(400, 100), v)
        v = Duration(100, 100) + Duration(300, 300)
        self.assertEquals(Duration(400, 400), v)
        v = Duration(100, 100) + Duration(300, -101)
        self.assertEquals(Duration(399, 999999999), v)
        
        # test subtraction
        try:
            v = Duration(1,0) - 1
            failed = True
        except: pass
        self.failIf(failed, "Duration - non duration must fail")
        try:
            v = Duration(1, 0) - Time(1,0)
            failed = True          
        except: pass
        self.failIf(failed, "Duration - Time must fail")
        
        v = Duration(1,0) - Duration(1, 0)
        self.assertEquals(Duration(), v)
        v = Duration(-1,-1) - Duration(1, 1)
        self.assertEquals(Duration(-3, 999999998), v)
        v = Duration(1) - Duration(0, 1000000000)
        self.assertEquals(Duration(), v)
        v = Duration(2) - Duration(0, 1000000000)
        self.assertEquals(Duration(1), v)
        v = Duration(100, 100) - Duration(300)
        self.assertEquals(Duration(-200, 100), v)
        v = Duration(100, 100) - Duration(300, 101)
        self.assertEquals(Duration(-201, 999999999), v)

        # test abs
        self.assertEquals(abs(Duration()), Duration())
        self.assertEquals(abs(Duration(1)), Duration(1))      
        self.assertEquals(abs(Duration(-1)), Duration(1))
        self.assertEquals(abs(Duration(0,-1)), Duration(0,1))
        self.assertEquals(abs(Duration(-1,-1)), Duration(1,1))
예제 #3
0
class BezierPath:
    def __init__(self, *args, **kwargs):
        self.bezier_curve = None
        self.duration = None

        if args:
            if len(args) == 1 and type(args[0]) is BezierPathMsg:
                self.bezier_curve = BezierCurve(args[0].order,
                                                args[0].control_points)
                self.duration = args[0].duration.data
            elif len(args) == 2:
                if type(args[0]) is BezierCurve:
                    self.bezier_curve = args[0]
                elif type(args[0]) is list:
                    self.bezier_curve = BezierCurve(args[0])
                else:
                    raise ValueError(f'Unknown argument {args[0]!r}')
                if type(args[1]) is Duration:
                    self.duration = args[1]
                elif type(args[1]) is float:
                    self.duration = Duration(args[1])
                else:
                    raise ValueError(f'Unknown argument {args[1]!r}')
            else:
                raise ValueError(f'Unknown arguments {args!r}')
        if kwargs:
            for k, v in kwargs.items():
                if k == 'msg':
                    if type(v) is not BezierPathMsg:
                        raise ValueError(
                            f'{k!r} must be {BezierPathMsg}, got {type(v)}')
                    self.bezier_curve = v.bezier_curve
                    self.duration = v.duration
                elif k == 'bezier_curve':
                    if type(v) is not BezierCurve:
                        raise ValueError(
                            f'{k!r} must be {BezierCurve}, got {type(v)}')
                    self.bezier_curve = v
                elif k == 'duration':
                    if type(v) is not Duration:
                        raise ValueError(
                            f'{k!r} must be {Duration}, got {type(v)}')
                    self.duration = v
                else:
                    raise ValueError(f'Unknown keyword argument {k!r}')

    def __repr__(self):
        return f'{self.__class__.__name__}({self.bezier_curve!r}, [{self.duration!r}])'

    def __str__(self):
        return f'{self.__class__.__name__}[{self.bezier_curve!s}, {self.duration!s}ns]'

    def to_param(self, secs):
        return (secs.to_sec() if type(secs) is Duration else
                float(secs)) / self.duration.to_sec()

    def from_param(self, vec):
        dt = 1.0 / self.duration.to_sec()
        return Vector3(vec.x * dt, vec.y * dt, vec.z * dt)

    def at(self, secs):
        t = self.to_param(secs)
        return self.bezier_curve.at(t)

    def vel_at(self, secs):
        t = self.to_param(secs)
        vec = self.bezier_curve.deriv(t)
        return self.from_param(vec)

    def speed_at(self, secs):
        vel = self.vel_at(secs)
        return math.sqrt(vel.x**2 + vel.y**2 + vel.z**2)

    def accel_at(self, secs):
        t = self.to_param(secs)
        dv = self.bezier_curve.hodograph().hodograph().at(t)
        dt = self.duration.to_sec()
        return Vector3(dv.x / (dt**2), dv.y / (dt**2), dv.z / (dt**2))

    def angle_at(self, secs):
        vel = self.vel_at(secs)
        if vel.x == 0 and vel.y == 0:
            vel = self.accel_at(secs)
        return math.atan2(vel.y, vel.x)

    def angular_vel_at(self, secs):
        vel = self.vel_at(secs)
        accel = self.accel_at(secs)
        return (vel.x * accel.x - vel.y * accel.y) / (vel.x**2 + vel.y**2)

    def to_msg(self):
        duration_msg = DurationMsg(self.duration)
        msg = BezierPathMsg(order=self.bezier_curve.order,
                            control_points=self.bezier_curve.control_points,
                            duration=duration_msg)
        return msg

    def split(self, secs):
        t = self.to_param(secs)
        curve1, curve2 = self.bezier_curve.de_casteljau(t)
        duration1 = Duration(secs)
        duration2 = Duration(self.duration.to_sec() - secs)
        path1 = BezierPath(bezier_curve=curve1, duration=duration1)
        path2 = BezierPath(bezier_curve=curve2, duration=duration2)
        return path1, path2
예제 #4
0
class GeographicTrajectory:
    """
    This class represents position of vehicle in time, acquired from GNSS.
    It supports two types of message:
    - nmea_msgs/Sentence
    - sensor_msgs/NavSatFix
    
    Attributes
    ----------
    timestamps : list of rospy.Time
        List of discrete timestamp at which pose is recorded
    coordinates : np.ndarray
        Position of vehicle at each timestamp
    duration : rospy.Duration
        Length of position recording
    frame_id : str
        Frame ID of coordinate sensor
    """

    supportedMsgTypes = ['sensor_msgs/NavSatFix', 'nmea_msgs/Sentence']

    def __init__(self,
                 randomBag=None,
                 eastingShift=0.0,
                 northingShift=0.0,
                 heightShift=0.0,
                 startTime=_startTime0,
                 stopTime=_stopTime_1):
        if (randomBag is None):
            self.timestamps = []
            self.coordinates = []
            self.duration = Duration(0)
            return

        if (randomBag.type() == 'sensor_msgs/NavSatFix'):
            self.timestamps, self.coordinates = GeographicTrajectory._parseFromNavSatFix(
                randomBag, eastingShift, northingShift, heightShift, startTime,
                stopTime)
        elif (randomBag.type() == 'nmea_msgs/Sentence'):
            self.timestamps, self.coordinates = GeographicTrajectory._parseFromNmea(
                randomBag, eastingShift, northingShift, heightShift, startTime,
                stopTime)
        else:
            raise ValueError("Input bag is of unknown type")

        self.duration = self.timestamps[-1] - self.timestamps[0]
        self.frame_id = randomBag[0].header.frame_id

    def __getitem__(self, t):
        if isinstance(t, numbers.Integral):
            return {
                'timestamp': self.timestamps[t],
                'pose': self.coordinates[t]
            }
        if hasattr(t, "to_sec"):
            if t < self.timestamps[0] or t > self.timestamps[-1]:
                raise ValueError("Requested timestamp is out of range")
            return {'timestamp': t, 'pose': self.positionAt(t)}
        if isinstance(t, numbers.Real):
            return {
                'timestamp': self.timestamps[0] + rospy.Duration.from_sec(t),
                'pose': self.positionAt(t)
            }

    def __len__(self):
        return len(self.timestamps)

    def positionAt(self, time):
        """
        Returns position at requested time using interpolation
        """
        if (isinstance(time, float)):
            if (time < 0 or time > self.duration.to_sec()):
                raise ValueError("Offset value is outside bag timestamp range")
            time = self.timestamps[0] + Duration.from_sec(time)
        elif (isinstance(time, Time)):
            if (time < self.timestamps[0] or time > self.timestamps[-1]):
                raise ValueError("Timestamp value is outside the bag range")

        _t1 = bisect_left(self.timestamps, time)
        if _t1 == len(self.timestamps) - 1:
            _t1 = len(self.timestamps) - 2
        t1 = self.timestamps[_t1]
        t2 = self.timestamps[_t1 + 1]
        r = ((time - t1).to_sec()) / (t2 - t1).to_sec()
        #         return self.coordinates[_t1] + (self.coordinates[_t1+1] - self.coordinates[_t1])*r
        return GeographicTrajectory.interpolate(self.coordinates[_t1],
                                                self.coordinates[_t1 + 1], r)

    def buildFromTimestamps(self, timestampList):
        """
        Creates new trajectory based on current one using interpolation of each timestamp
        """
        track = GeographicTrajectory()
        for t in tqdm(timestampList):
            if t <= self.timestamps[0]:
                track.coordinates.append(self.coordinates[0])
            elif t >= self.timestamps[-1]:
                track.coordinates.append(self.coordinates[-1])
            else:
                pos = self.positionAt(t)
                track.coordinates.append(pos)
            track.timestamps.append(t)
        track.duration = self.timestamps[-1] - self.timestamps[0]
        track.coordinates = np.array(track.coordinates)
        track.frame_id = self.frame_id
        return track

    @staticmethod
    def interpolate(pq1, pq2, ratio):
        position = pq1[0:3] + (pq2[0:3] - pq1[0:3]) * ratio
        orientation = tfx.quaternion_slerp(pq1[3:7], pq2[3:7], ratio)
        return np.append(position, orientation)

    @staticmethod
    def parseFromBag(randomBag):
        pass

    @staticmethod
    def _parseFromNmea(randomBag,
                       eastingShift=0.0,
                       northingShift=0.0,
                       heightShift=0.0,
                       startTime=_startTime0,
                       stopTime=_stopTime_1):
        try:
            import pynmea2
        except ImportError:
            raise RuntimeError(
                "NMEA support is not available; install pynmea2")

        parsedCoordinates = []
        timestamps = []
        msgSamples = GeographicTrajectory._createTimeRange(
            randomBag, startTime, stopTime)
        i = 0
        for s in tqdm(msgSamples):
            rawmsg = randomBag[s]
            i += 1
            try:
                m = pynmea2.parse(rawmsg.sentence)
                coord = utm.fromLatLong(float(m.latitude), float(m.longitude),
                                        float(m.altitude))
                gcoord = [
                    coord.easting, coord.northing, coord.altitude, 0, 0, 0, 0
                ]
                if len(parsedCoordinates) > 1:
                    quat = GeographicTrajectory.orientationFromPositionOnlyYaw(
                        parsedCoordinates[-1], parsedCoordinates[-2])
                    gcoord[3:7] = quat
                parsedCoordinates.append(gcoord)
                timestamps.append(rawmsg.header.stamp)
            except (AttributeError, TypeError, pynmea2.SentenceTypeError):
                continue
        parsedCoordinates = np.array(parsedCoordinates)
        return timestamps, parsedCoordinates

    @staticmethod
    def _parseFromNavSatFix(randomBag,
                            eastingShift=0.0,
                            northingShift=0.0,
                            heightShift=0.0,
                            startTime=_startTime0,
                            stopTime=_stopTime_1):
        assert (randomBag.type() == 'sensor_msgs/NavSatFix')
        timestamps = []
        i = 0
        msgSamples = GeographicTrajectory._createTimeRange(
            randomBag, startTime, stopTime)
        parsedCoordinates = np.zeros((len(msgSamples), 7), dtype=np.float)

        for s in tqdm(msgSamples):
            rawmsg = randomBag[s]
            coord = utm.fromLatLong(rawmsg.latitude, rawmsg.longitude,
                                    rawmsg.altitude)
            parsedCoordinates[i, 0:3] = [
                coord.easting + eastingShift, coord.northing + northingShift,
                coord.altitude + heightShift
            ]
            if i >= 1:
                quat = GeographicTrajectory.orientationFromPositionOnlyYaw(
                    parsedCoordinates[i], parsedCoordinates[i - 1])
                parsedCoordinates[i, 3:7] = quat
                if i == 1:
                    parsedCoordinates[0, 3:7] = quat
            timestamps.append(rawmsg.header.stamp)
            i += 1
        return timestamps, parsedCoordinates

    @staticmethod
    def _createTimeRange(randomBag, startTime, stopTime):
        if startTime == _startTime0 and stopTime == _stopTime_1:
            msgSamples = range(len(randomBag))
        else:
            if startTime == _startTime0:
                startTime = randomBag.timestamps[0]
            if stopTime == _stopTime_1:
                stopTime = randomBag.timestamps[-1]
            probeTime = startTime - Duration.from_sec(_timeFuzzyOffset)
            if probeTime >= randomBag.timestamps[0]:
                startTime = probeTime
            msgSamples = randomBag.desample(-1, True, startTime, stopTime)
        return msgSamples

    @staticmethod
    def orientationFromPositionOnlyYaw(curPosition, prevPosition):
        yaw = math.atan2(curPosition[1] - prevPosition[1],
                         curPosition[0] - prevPosition[0])
        return tfx.quaternion_from_euler(0, 0, yaw)