Example: Use ViconReceiver to Access Vicon Data¶
The API for accessing the Vicon data is implemented in C++ but bindings are generated, so it can also be used with Python.
Below are examples in both Python and C++. Note that they only use the low level receiver class which gives poses as provided by Vicon and does not transform them to a specific reference frame.
Python¶
The script vicon_print_data_py.py
shows how to set up a
ViconReceiver
instance to get the object information provided by Vicon and print it to stdout.
#!/usr/bin/env python3
# SPDX-License-Identifier: BSD-3-Clause
"""Get data from Vicon server and forward with ZMQ."""
import argparse
import contextlib
import sys
from vicon_transformer import ViconReceiverConfig, ViconReceiver, to_json
def main() -> int:
# parse arguments
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument(
"vicon_host",
nargs="?",
help="Host name, in the format of server:port",
default="localhost:801",
)
parser.add_argument(
"--lightweight",
action="store_true",
help="Enable lightweight segment data.",
)
parser.add_argument(
"--subjects",
type=str,
nargs="+",
metavar="SUBJECT",
help="""Filter for listed subjects. Other subjects are still listed in the
frame data but without actual data.
""",
)
parser.add_argument(
"--once", action="store_true", help="Exit after printing one frame."
)
parser.add_argument(
"--json", action="store_true", help="Print output in JSON format."
)
args = parser.parse_args()
config = ViconReceiverConfig()
config.enable_lightweight = args.lightweight
config.filtered_subjects = args.subjects or []
print("Config:\n{}\n".format(to_json(config)))
with ViconReceiver(args.vicon_host, config) as receiver:
receiver.print_info()
print("=======================================")
while True:
frame = receiver.read()
receiver.print_latency_info()
if args.json:
print(to_json(frame))
else:
print(frame)
if args.once:
break
return 0
if __name__ == "__main__":
with contextlib.suppress(KeyboardInterrupt):
sys.exit(main())
C++¶
Below is the code of the vicon_print_data
executable implemented in C++. It is
mostly equivalent to the Python implementation above but also supports using a
PlaybackReceiver
to play back a previously recorded
file instead of connecting to a live Vicon system.
// SPDX-License-Identifier: MIT
//////////////////////////////////////////////////////////////////////////////////
// MIT License
//
// Copyright (c) 2017 Vicon Motion Systems Ltd
// Copyright (c) 2022 Max Planck Gesellschaft
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//////////////////////////////////////////////////////////////////////////////////
#include <vicon-datastream-sdk/DataStreamClient.h>
#include <filesystem>
#include <iostream>
#include <string>
#include <vector>
#include <fmt/format.h>
#include <fmt/ostream.h>
#include <spdlog/sinks/stdout_color_sinks.h>
#include <spdlog/spdlog.h>
#include <cereal/archives/json.hpp>
#include <cli_utils/program_options.hpp>
#include <vicon_transformer/errors.hpp>
#include <vicon_transformer/vicon_receiver.hpp>
namespace
{
// Class to get console arguments
class Args : public cli_utils::ProgramOptions
{
public:
std::string host_or_file = "localhost:801";
bool lightweight = false;
int num_frames = 0;
bool json_output = false;
std::vector<std::string> filtered_subjects;
std::string help() const override
{
return R"(Print Vicon data to terminal.
Usage: vicon_print_data <vicon-host-name-or-file> [options]
)";
}
void add_options(boost::program_options::options_description &options,
boost::program_options::positional_options_description
&positional) override
{
namespace po = boost::program_options;
// clang-format off
options.add_options()
("vicon-host-name-or-file",
po::value<std::string>(&host_or_file)->required(),
"Host name (or IP) of the Vicon PC or the path to a recorded file.")
("subjects",
po::value<std::vector<std::string>>(&filtered_subjects)->multitoken(),
"Only receive data for the listed subjects.")
("lightweight",
"Enable lightweight frames (needs less bandwidth at the cost of lower precision).")
("num,n",
po::value<int>(&num_frames),
"Only print the specified number of frames.")
("json",
"Produce JSON-formatted output.")
;
// clang-format on
positional.add("vicon-host-name-or-file", 1);
}
// for boolean flags without values, some post-processing is needed
void postprocess(const boost::program_options::variables_map &args) override
{
lightweight = args.count("lightweight") > 0;
json_output = args.count("json") > 0;
}
};
} // namespace
int main(int argc, char *argv[])
{
auto logger = spdlog::get("root");
if (!logger)
{
logger = spdlog::stderr_color_mt("root");
// auto log_level = spdlog::level::from_str(config.logger_level);
logger->set_level(spdlog::level::debug);
}
// Program options
Args args;
if (!args.parse_args(argc, argv))
{
return 1;
}
std::unique_ptr<vicon_transformer::Receiver> receiver;
// if the argument is an existing file, load it for playback, otherwise
// assume its a hostname and try to connect
bool use_vicon_receiver = !std::filesystem::exists(args.host_or_file);
if (use_vicon_receiver)
{
// argument is a hostname/IP
vicon_transformer::ViconReceiverConfig config;
config.enable_lightweight = args.lightweight;
config.filtered_subjects = args.filtered_subjects;
receiver = std::make_unique<vicon_transformer::ViconReceiver>(
args.host_or_file, config, logger);
auto ptr =
static_cast<vicon_transformer::ViconReceiver *>(receiver.get());
ptr->connect();
ptr->print_info();
}
else
{
// argument is a recorded file
receiver = std::make_unique<vicon_transformer::PlaybackReceiver>(
args.host_or_file, logger);
if (args.lightweight)
{
logger->warn(
"Argument --lightweight is ignored when playing back recorded "
"file.");
}
if (!args.filtered_subjects.empty())
{
logger->warn(
"Argument --subjects is ignored when playing back recorded "
"file.");
}
}
fmt::print("\n==============================\n\n");
// Loop until a key is pressed
for (int i = 0; args.num_frames == 0 || i < args.num_frames; i++)
{
// read the frame
vicon_transformer::ViconFrame frame;
try
{
frame = receiver->read();
}
catch (std::out_of_range &)
{
logger->info("Reached end of recording.");
break;
}
// print latency info (only when using ViconReceiver)
if (use_vicon_receiver)
{
auto ptr =
static_cast<vicon_transformer::ViconReceiver *>(receiver.get());
ptr->print_latency_info();
}
// print the frame data
if (args.json_output)
{
cereal::JSONOutputArchive json_out(std::cout);
frame.serialize(json_out);
}
else
{
fmt::print("{}\n\n", frame);
}
}
if (use_vicon_receiver)
{
auto ptr =
static_cast<vicon_transformer::ViconReceiver *>(receiver.get());
ptr->disconnect();
}
}