示例#1
0
def acceleration(timestamp, speed):
    """Calculate acceleration from speed stream.

    This node calculates the acceleration values from speed values. It is useful in cases where no
    acceleration data from a sensor is available.

    Args:
        timestamp: Message stream with timestamps of distances.
        speed: Message stream with speed values.

    Yields:
        Message stream with acceleration values.

    """
    msg_ts, msg_speed = yield marv.pull_all(timestamp, speed)
    if msg_ts is None or msg_speed is None:
        return

    msg_ts, msg_speed = yield marv.pull_all(timestamp, speed)
    if msg_ts is None or msg_speed is None:
        return

    yield marv.set_header(title=timestamp.title)
    yield marv.push({'value': 0})
    yield marv.push({'value': 0})
    pts = msg_ts
    psp = msg_speed.value

    while (msg_ts :=
           (yield marv.pull(timestamp))) and (msg_speed :=
                                              (yield marv.pull(speed))):
示例#2
0
def distance_xyz(pos):
    """Calculate distance from Cartesian positions.

    Args:
        pos: Message stream with Cartesian position messages.

    Yields:
        Message stream with distances.

    """
    msg = yield marv.pull(pos)
    if msg is None:
        return

    yield marv.set_header(title=pos.title)
    yield marv.push({'value': 0})
    prev = msg

    while msg := (yield marv.pull(pos)):
        diffe = msg['x'] - prev['x']
        diffn = msg['y'] - prev['y']
        diffa = msg['z'] - prev['z']
        dist = (diffe**2 + diffn**2 + diffa**2)**0.5
        yield marv.push({'value': dist})
        prev = msg
示例#3
0
def orientations(imus, navsatorients):
    while True:
        tmp = yield marv.pull(imus)
        if tmp is None:
            break
        yield marv.push(tmp)
    while True:
        tmp = yield marv.pull(navsatorients)
        if tmp is None:
            break
        yield marv.push(tmp)
示例#4
0
def collect(chatter):
    pytype = get_message_type(chatter)
    rosmsg = pytype()
    msg = yield marv.pull(chatter)
    assert msg is not None
    rosmsg.deserialize(msg.data)
    yield marv.push(rosmsg.data)
    while True:
        msg = yield marv.pull(chatter)
        if msg is None:
            return
        rosmsg.deserialize(msg.data)
        yield marv.push(rosmsg.data)
示例#5
0
def images(cam):
    """Extract images from input stream to jpg files.

    Args:
        cam: Input stream of raw rosbag messages.

    Returns:
        File instances for images of input stream.

    """
    # Set output stream title and pull first message
    yield marv.set_header(title=cam.topic)

    # Fetch and process first 20 image messages
    name_template = '%s-{}.jpg' % cam.topic.replace('/', ':')[1:]
    while True:
        idx, msg = yield marv.pull(cam, enumerate=True)
        if msg is None or idx >= 20:
            break

        # Deserialize raw ros message
        pytype = get_message_type(cam)
        rosmsg = pytype()
        rosmsg.deserialize(msg.data)

        # Write image to jpeg and push it to output stream
        img = imgmsg_to_cv2(rosmsg, 'rgb8')
        name = name_template.format(idx)
        imgfile = yield marv.make_file(name)
        cv2.imwrite(imgfile.path, img)
        yield marv.push(imgfile)
示例#6
0
def navsatfix(stream):
    yield marv.set_header(title=stream.topic)
    log = yield marv.get_logger()
    deserialize = make_deserialize(stream)
    get_timestamp = make_get_timestamp(log, stream)
    erroneous = 0
    while True:
        msg = yield marv.pull(stream)
        if msg is None:
            break
        rosmsg = deserialize(msg.data)

        if not hasattr(rosmsg, 'status') or \
           np.isnan(rosmsg.longitude) or \
           np.isnan(rosmsg.latitude) or \
           np.isnan(rosmsg.altitude):
            erroneous += 1
            continue

        yield marv.push({
            'status': rosmsg.status.status,
            'lon': rosmsg.longitude,
            'lat': rosmsg.latitude,
            'timestamp': get_timestamp(rosmsg, msg),
        })

    if erroneous:
        log.warning('skipped %d erroneous messages', erroneous)
def nooutput(stream):
    yield marv.set_header()
    while True:
        msg = yield marv.pull(stream)
        if msg is None:
            return
        yield marv.push(msg)
示例#8
0
def speed(timestamp, distance):
    """Calculate speed from distance stream.

    This node calculates the speed values from distance values. It is useful in cases where no speed
    data from a sensor is available.

    Args:
        timestamp: Message stream with timestamps of distances.
        distance: Message stream with distance values.

    Yields:
        Message stream with speed values.

    """
    msg_ts, msg_dist = yield marv.pull_all(timestamp, distance)
    if msg_ts is None or msg_dist is None:
        return

    yield marv.set_header(title=timestamp.title)
    yield marv.push({'value': 0})
    pts = msg_ts

    while (msg_ts :=
           (yield marv.pull(timestamp))) and (msg_dist :=
                                              (yield marv.pull(distance))):
示例#9
0
def gallery_section(images, title):
    """Create detail section with gallery.

    Args:
        title (str): Title to be displayed for detail section.
        images: stream of marv image files

    Returns:
        One detail section.

    """
    # pull all images
    imgs = []
    while True:
        img = yield marv.pull(images)
        if img is None:
            break
        imgs.append({'src': img.relpath})
    if not imgs:
        return

    # create gallery widget and section containing it
    widget = {'title': images.title, 'gallery': {'images': imgs}}
    section = {'title': title, 'widgets': [widget]}
    yield marv.push(section)
示例#10
0
def filesize_plot(filesizes):
    # Pull all filesizes
    sizes = []
    while True:
        size = yield marv.pull(filesizes)
        if size is None:
            break
        sizes.append(size)

    # plot with plotly
    fig = go.Figure(data=go.Scatter(y=sizes))

    # save plotly figure to file
    plotfile = yield marv.make_file('filesizes_plotly.json')
    Path(plotfile.path).write_text(fig.to_json())

    # create plotly widget referencing file
    widget_plotly = {
        'title': 'Filesizes (plotly)',
        'plotly': f'marv-partial:{plotfile.relpath}',
    }

    # plot with matplotlib
    fig = plt.figure()
    axis = fig.add_subplot(1, 1, 1)
    axis.plot(sizes, 'bo')

    # save mpld3 figure to file
    plotfile = yield marv.make_file('filesizes_mpld3.json')
    Path(plotfile.path).write_text(json.dumps(mpld3.fig_to_dict(fig)))

    # create mpld3 plot widget referencing file
    widget_mpld3 = {
        'title': 'Filesizes (mpld3)',
        'mpld3': f'marv-partial:{plotfile.relpath}',
    }

    # save plot as image
    plotfile = yield marv.make_file('filesizes.jpg')
    fig.savefig(plotfile.path)

    # create image widget referencing file
    widget_image = {
        'title': 'Filesizes (image)',
        'image': {
            'src': plotfile.relpath
        },
    }

    # Let the user choose which widget to show with a dropdown
    yield marv.push({
        'title': 'Filesize plots',
        'dropdown': {
            'widgets': [
                widget_plotly,
                widget_mpld3,
                widget_image,
            ],
        },
    })
示例#11
0
def filesize_plot_fixed(filesizes):
    # set_header() helps marv to schedule nodes
    yield marv.set_header()

    # Pull all filesizes
    sizes = []
    while True:
        size = yield marv.pull(filesizes)
        if size is None:
            break
        sizes.append(size)

    # plot
    fig = plt.figure()
    axis = fig.add_subplot(1, 1, 1)
    axis.plot(sizes, 'bo')
    # axis.set_xlabel('foo')
    # axis.set_ylabel('bat')

    # save figure to file
    plotfile = yield marv.make_file('filesizes.json')
    with open(plotfile.path, 'w') as f:
        json.dump(mpld3.fig_to_dict(fig), f)

    # create plot widget referencing file
    widget = {
        'title': 'Filesizes',
        'mpld3': f'marv-partial:{plotfile.relpath}',
    }
    yield marv.push(widget)
示例#12
0
def image(cam):
    """Extract first image of input stream to jpg file.

    Args:
        cam: Input stream of raw rosbag messages.

    Returns:
        File instance for first image of input stream.

    """
    # Set output stream title and pull first message
    yield marv.set_header(title=cam.topic)
    msg = yield marv.pull(cam)
    if msg is None:
        return

    # Deserialize raw ros message
    pytype = get_message_type(cam)
    rosmsg = pytype()
    rosmsg.deserialize(msg.data)

    # Write image to jpeg and push it to output stream
    name = f"{cam.topic.replace('/', ':')[1:]}.jpg"
    imgfile = yield marv.make_file(name)
    img = imgmsg_to_cv2(rosmsg, 'rgb8')
    cv2.imwrite(imgfile.path, img, (cv2.IMWRITE_JPEG_QUALITY, 60))
    yield marv.push(imgfile)
示例#13
0
def metadata(dataset):
    dataset = yield marv.pull(dataset)
    dataset_yaml = next((x for x in dataset.files if x.path.endswith('dataset.yaml')), None)
    if dataset_yaml is None:
        return

    dct = yaml.safe_load(Path(dataset_yaml.path).read_text())
    yield marv.push(dct)
示例#14
0
def rosmsg_imgstream(stream):
    """Convert stream of raw ros messages into stream of deserialized messages."""
    deserialize = make_deserialize(stream)
    while True:
        msg = yield marv.pull(stream)
        if msg is None:
            break

        rosmsg = deserialize(msg.data)
        yield marv.push(rosmsg)
示例#15
0
def connections_section(bagmeta, dataset, title):
    """Section displaying information about ROS connections."""
    dataset, bagmeta = yield marv.pull_all(dataset, bagmeta)
    if not bagmeta.topics:
        raise marv.Abort()
    columns = [
        {
            'title': 'Topic'
        },
        {
            'title': 'Type'
        },
        {
            'title': 'MD5'
        },
        {
            'title': 'Latching'
        },
        {
            'title': 'Message count',
            'align': 'right'
        },
    ]
    rows = [{
        'id':
        idx,
        'cells': [
            {
                'text': con.topic
            },
            {
                'text': con.datatype
            },
            {
                'text': con.md5sum
            },
            {
                'bool': con.latching
            },
            {
                'uint64': con.msg_count
            },
        ]
    } for idx, con in enumerate(bagmeta.connections)]
    widgets = [{'table': {'columns': columns, 'rows': rows}}]
    # TODO: Add text widget explaining what can be seen here: ROS bag
    # files store connections. There can be multiple connections for
    # one topic with the same or different message types and message
    # types with the same name might have different md5s. For
    # simplicity connections with the same topic, message type and md5
    # are treated as one, within one bag file as well as across bags
    # of one set. If one of such connections is latching, the
    # aggregated connection will be latching.
    yield marv.push({'title': title, 'widgets': widgets})
示例#16
0
def objects(dataset):
    dataset = yield marv.pull(dataset)
    tracklet_file = next((x for x in dataset.files if x.path.endswith('tracklet_labels.xml')), None)
    if tracklet_file is None:
        return

    fixme

    root = ET.parse(tracklet_file.path).getroot()
    cars = root.findall(".//*[objectType='Car']")
    yield marv.push({'cars': len(cars)})
示例#17
0
def summary_keyval(dataset):  # pylint: disable=redefined-outer-name
    dataset = yield marv.pull(dataset)
    if len(dataset.files) < 2:
        return
    yield marv.push({'keyval': {
        'items': [
            {'title': 'size', 'formatter': 'filesize', 'list': False,
             'cell': {'uint64': sum(x.size for x in dataset.files)}},
            {'title': 'files', 'list': False,
             'cell': {'uint64': len(dataset.files)}},
        ],
    }})
示例#18
0
def bbox(stream):
    """Create videofile from input sream."""
    func, catmap = make_detect_function(
        '/opt/marv/centernet_hg104_512x512_coco17_tpu-8',
        '/opt/marv/mscoco_label_map.pbtxt')
    while True:
        img = yield marv.pull(stream)
        if img is None:
            break

        detections = detect(img, func)
        vizimg = visualize(img, detections, catmap)
        yield marv.push(vizimg)
示例#19
0
def galleries(stream):
    """Galleries for all images streams.

    Used by marv_robotics.detail.images_section.
    """
    yield marv.set_header(title=stream.title)
    images = []
    while True:
        img = yield marv.pull(stream)
        if img is None:
            break
        images.append({'src': img.relpath})
    yield marv.push({'title': stream.title, 'gallery': {'images': images}})
示例#20
0
def images_section(galleries, title):
    """Section with galleries of images for each images stream."""
    tmp = []
    while True:
        msg = yield marv.pull(galleries)
        if msg is None:
            break
        tmp.append(msg)
    galleries = tmp
    galleries = sorted(galleries, key=lambda x: x.title)
    widgets = yield marv.pull_all(*galleries)
    if widgets:
        yield marv.push({'title': title, 'widgets': widgets})
示例#21
0
def summary_keyval(dataset, bagmeta):
    """Keyval widget summarizing bag metadata.

    Useful for detail_summary_widgets.
    """
    dataset, bagmeta = yield marv.pull_all(dataset, bagmeta)
    yield marv.push({
        'keyval': {
            'items': [
                {
                    'title': 'size',
                    'formatter': 'filesize',
                    'list': False,
                    'cell': {
                        'uint64': sum(x.size for x in dataset.files)
                    }
                },
                {
                    'title': 'files',
                    'list': False,
                    'cell': {
                        'uint64': len(dataset.files)
                    }
                },
                {
                    'title': 'start time',
                    'formatter': 'datetime',
                    'list': False,
                    'cell': {
                        'timestamp': bagmeta.start_time
                    }
                },
                {
                    'title': 'end time',
                    'formatter': 'datetime',
                    'list': False,
                    'cell': {
                        'timestamp': bagmeta.end_time
                    }
                },
                {
                    'title': 'duration',
                    'formatter': 'timedelta',
                    'list': False,
                    'cell': {
                        'timedelta': bagmeta.duration
                    }
                },
            ],
        }
    })
示例#22
0
def fulltext_per_topic(stream):
    yield marv.set_header(title=stream.topic)
    words = set()
    deserialize = make_deserialize(stream)
    while True:
        msg = yield marv.pull(stream)
        if msg is None:
            break
        rosmsg = deserialize(msg.data)
        words.update(WSNULL.split(rosmsg.data))

    if not words:
        raise marv.Abort()
    yield marv.push({'words': list(words)})
示例#23
0
def distance_gps(pos):
    """Calculate distance from GPS positions.

    Args:
        pos: Message stream with GPS position messages.

    Yields:
        Message stream with distances.

    """
    msg = yield marv.pull(pos)
    if msg is None:
        return

    yield marv.set_header(title=pos.title)
    yield marv.push({'value': 0})
    lat_p = radians(msg['lat'])
    lon_p = radians(msg['lon'])
    alt_p = msg['alt']

    while msg := (yield marv.pull(pos)):
        lat = radians(msg['lat'])
        lon = radians(msg['lon'])
        alt = msg['alt']
        lat_d = lat_p - lat
        lon_d = lon_p - lon
        alt_d = alt_p - alt
        dis = sin(lat_d * 0.5)**2 + cos(lat) * cos(lat_p) * sin(lon_d * 0.5)**2
        dis = 2 * 6371008.8 * asin(sqrt(dis))
        if not math.isnan(alt_d):
            dis = sqrt(
                dis**2 +
                alt_d**2)  # euclidean approx taking altitude into account
        yield marv.push({'value': dis})
        lat_p = lat
        lon_p = lon
        alt_p = alt
示例#24
0
def fulltext(streams):
    """Extract all text from bag file and store for fulltext search."""
    tmp = []
    while True:
        stream = yield marv.pull(streams)
        if stream is None:
            break
        tmp.append(stream)
    streams = tmp
    if not streams:
        raise marv.Abort()

    msgs = yield marv.pull_all(*streams)
    words = {x for msg in msgs for x in msg.words}
    yield marv.push({'words': sorted(words)})
示例#25
0
def filesizes(images):
    """Stat filesize of files.

    Args:
        images: stream of marv image files

    Returns:
        Stream of filesizes

    """
    # Pull each image and push its filesize
    while True:
        img = yield marv.pull(images)
        if img is None:
            break
        yield marv.push(img.size)
示例#26
0
def video_section(src, title):
    """Section displaying a video player."""
    videofile = yield marv.pull(src)
    if videofile is None:
        return

    yield marv.push({
        'title':
        title,
        'widgets': [{
            'title': 'Camera',
            'video': {
                'src': videofile.relpath
            },
        }],
    })
示例#27
0
def imgsrc(stream):
    """Convert ROS sensor_msgs/Image stream into cv2 image stream."""
    while True:
        rosmsg = yield marv.pull(stream)
        if rosmsg is None:
            break

        try:
            img = imgmsg_to_cv2(rosmsg, 'bgr8')
        except (ImageFormatError, ImageConversionError) as err:
            log = yield marv.get_logger()
            log.error('could not convert image from topic %s: %s ',
                      stream.topic, err)
            raise marv.Abort()

        yield marv.push(img)
示例#28
0
def positions(stream):
    yield marv.set_header(title=stream.topic)
    log = yield marv.get_logger()
    deserialize = make_deserialize(stream)
    get_timestamp = make_get_timestamp(log, stream)

    erroneous = 0
    e_offset = None
    n_offset = None
    u_offset = None
    positions = []
    while True:
        msg = yield marv.pull(stream)
        if msg is None:
            break
        rosmsg = deserialize(msg.data)

        if not hasattr(rosmsg, 'status') or \
           np.isnan(rosmsg.longitude) or \
           np.isnan(rosmsg.latitude) or \
           np.isnan(rosmsg.altitude) or \
           np.isnan(rosmsg.position_covariance[0]):
            erroneous += 1
            continue

        e, n, _, _ = utm.from_latlon(rosmsg.latitude, rosmsg.longitude)
        if e_offset is None:
            e_offset = e
            n_offset = n
            u_offset = rosmsg.altitude
        e = e - e_offset
        n = n - n_offset
        u = rosmsg.altitude - u_offset

        positions.append([get_timestamp(rosmsg, msg) / 1e9,
                          rosmsg.latitude,
                          rosmsg.longitude,
                          rosmsg.altitude,
                          e, n, u,
                          rosmsg.status.status,
                          np.sqrt(rosmsg.position_covariance[0])])
    if erroneous:
        log = yield marv.get_logger()
        log.warning('skipped %d erroneous messages', erroneous)
    if positions:
        yield marv.push({'values': positions})
示例#29
0
def trajectory(navsatfixes):
    # Only one topic for now
    navsatfix = yield marv.pull(navsatfixes)  # pylint: disable=redefined-outer-name
    if not navsatfix:
        raise marv.Abort()
    yield marv.set_header(title=navsatfix.title)
    features = []
    quality = None
    coords = []
    timestamps = []
    while True:
        msg = yield marv.pull(navsatfix)
        if msg is None:
            break

        timestamps.append(msg['timestamp'])

        # Whether to output an augmented fix is determined by both the fix
        # type and the last time differential corrections were received.  A
        # fix is valid when status >= STATUS_FIX.
        # STATUS_NO_FIX =  -1 -> unable to fix position       -> color id 0 = red
        # STATUS_FIX =      0 -> unaugmented fix              -> color id 1 = orange
        # STATUS_SBAS_FIX = 1 -> satellite-based augmentation -> color id 2 = blue
        # STATUS_GBAS_FIX = 2 -> ground-based augmentation    -> color id 3 = green
        #                     -> unknown status id            -> color id 4 = black
        if -1 <= msg['status'] <= 2:
            new_quality = msg['status'] + 1
        else:
            new_quality = 4

        # start new feature if quality changed
        if quality != new_quality:
            if coords:
                features.append(_create_feature(coords, quality, timestamps))
            quality = new_quality
            coords = []
            timestamps = []

        coords.append((msg['lon'], msg['lat']))

    if coords:
        features.append(_create_feature(coords, quality, timestamps))

    if features:
        out = {'feature_collection': {'features': features}}
        yield marv.push(out)
示例#30
0
def video_sink(stream):
    """Create videofile from input sream."""
    framerate = 10
    encoder = None
    while True:
        img = yield marv.pull(stream)
        if img is None:
            break

        if not encoder:
            video = yield marv.make_file('camera.webm')
            height, width = img.shape[0:2]
            ffargs = [
                'ffmpeg',
                '-f',
                'rawvideo',
                '-pixel_format',
                'bgr24',
                '-video_size',
                f'{width}x{height}',
                '-framerate',
                f'{framerate}',
                '-i',
                '-',
                '-c:v',
                'libvpx-vp9',
                '-pix_fmt',
                'yuv420p',
                '-loglevel',
                'error',
                '-threads',
                '8',
                '-speed',
                '4',
                '-y',
                video.path,
            ]
            encoder = popen(ffargs, stdin=PIPE)

        encoder.stdin.write(img)

    if encoder:
        encoder.stdin.close()
        encoder.wait()
        yield marv.push(video)