OSVR Reset Yaw

来源:互联网 发布:sap数据导入 编辑:程序博客网 时间:2024/05/21 10:31

作用:

该工具可用来对/me/head坐标系进行短期(在osvr server运行期间)校准,将头部的方向校正为正前方。它主要是为只有orientation数据的追踪器准备的;而带有完整pose信息的追踪器就不需要这个工具了,它们一般都会带有外部固定的已知位置的地标或者摄像头,而由于已知位置就能够建立一个不变的期望的坐标系。

如何使用:

在运行OSVR reset yaw之前,OSVR server应该先运行并且能正常接受到追踪数据。应用程序也在运行。

1.开启Reset Yaw应用程序。它会连接上OSVR Server并随后在提醒继续操作之前会获取追踪数据

2.应用程序会提示“将你的设备放置在零点方位并按下enter键”:这时头显应该朝向想要的成为正前方的方位,随后按下enter键

3.该应用程序会记录按下enter键这一刻的方位信息,然后再计算这个方位和一个默认方位间关于Y轴的相对旋转信息,随后给OSVR server发生一条信息以更新它的节点/me/head,该节点路径包含一个变形转换层可对头部坐标系进行转换操作,因此可使新的方位变为/me/head的有效正前方。

4.当所有的计算以及服务通信都完成时,应用程序会提醒再次按下enter键退出reset yaw程序,改变将会立即生效。

若是不满意,可再次运行该程序;新创建的校正数据将会刷掉旧的校正数据,所有正在运行的OSVR应用程序将会立即刷新以显示新校正数据的转换结果。

缺陷:

1.该校正数据只在当前服务进程操作时会被保存,其他任何地方都无法保存,因此当关闭OSVR server时,它们就会丢失。(大多数IMU设备在这一点上设计成在每个阶段都需要这这样的校正操作,这样才是合理的操作模式)

2.该操作只对/me/head生效,而对那些即使在同一坐标系中的追踪器或摄像头都不会起到效果。(这也是为什么说它最适合在仅仅有方位追踪器的系统中使用的原因)

3.正如名字所提示的,它只能校准偏航角yaw/heading:地平面假设正确。如果追踪被混合进它的转换中而不仅仅只是去校正它的yaw角,使用该工具也会出现错误。

代码分析:

{    namespace po = boost::program_options;    // clang-format off    po::options_description desc("Options");    desc.add_options()        ("help", "produce help message")        ("path", po::value<std::string>()->default_value("/me/head"), "path to reset-yaw on")        ("no-wait", "headless mode - immediately resets yaw without waiting")        ;    // clang-format on    po::variables_map vm;    po::store(po::command_line_parser(argc, argv).options(desc).run(), vm);    po::notify(vm);    const bool noWait = vm.count("no-wait");    {        /// Deal with command line errors or requests for help        bool usage = false;        if (vm.count("help")) {            cout << "Usage: osvr_reset_yaw [options]" << endl;            cout << desc << "\n";            return 1;        }    }    osvr::clientkit::ClientContext ctx("com.osvr.bundled.resetyaw");    std::string const path = vm["path"].as<std::string>();    // Get the interface associated with the destination route we    // are looking for.    osvr::clientkit::Interface iface = ctx.getInterface(path);    {        ClientMainloopThread client(ctx);        cout << "Running client mainloop briefly to start up..." << endl;        client.loopForDuration(boost::chrono::seconds(2));        cout << "Removing any previous yaw-reset transforms..." << endl;        // Get the alias element corresponding to the desired path, if possible.        auto elt = getAliasElement(ctx, path);        if (!elt) {            // No luck, sorry.            cerr << "Couldn't get the alias at " << path << endl;            return -1;        }        // Get a reference to the source associated with the portion        // of the tree that has this destination.  Then clean out        // any prior instance of our meddling by checking for an        // entry that has our flag key in it.  Then replace the        // original source tree with the cleaned tree.  Send this        // cleaned alias back to the server.        osvr::common::ParsedAlias origAlias{elt->getSource()};        if (!origAlias.isValid()) {            cerr << "Couldn't parse the alias!" << endl;            return -1;        }        cout << "Original transform: "             << origAlias.getAliasValue().toStyledString() << "\n" << endl;        osvr::common::GeneralizedTransform xforms{origAlias.getAliasValue()};        osvr::common::remove_if(xforms, [](Json::Value const ¤t) {            return current.isMember(FLAG_KEY) && current[FLAG_KEY].isBool() &&                   current[FLAG_KEY].asBool();        });        cout << "Cleaned transform: "             << xforms.get(origAlias.getLeaf()).toStyledString() << "\n"             << endl;        elt->setSource(            osvr::common::jsonToCompactString(xforms.get(origAlias.getLeaf())));        ctx.get()->sendRoute(createJSONAlias(path, *elt));        cout << "Sent cleaned transform, starting again and waiting a few "                "seconds for startup..."             << endl;        client.start();        boost::this_thread::sleep(SETTLE_TIME);        if (!noWait) {            cout << "\n\nPlease place your device for " << path                << " in its 'zero' orientation and press enter." << endl;            std::cin.ignore();        }        OSVR_OrientationState state;        OSVR_TimeValue timestamp;        OSVR_ReturnCode ret;        {            /// briefly interrupt the client mainloop so we can get stuff done            /// with the client state.            ClientMainloopThread::lock_type lock(client.getMutex());            ret = osvrGetOrientationState(iface.get(), ×tamp, &state);            if (ret != OSVR_RETURN_SUCCESS) {                cerr << "Sorry, no orientation state available for this path - "                        "are you sure you have a device plugged in and your "                        "path correct?"                     << endl;                if (!noWait) {                    std::cin.ignore();                }                return -1;            }            auto q = osvr::util::eigen_interop::map(state);            auto yaw = osvr::util::extractYaw(q);            cout << "Correction: " << -yaw << " radians about Y" << endl;            Json::Value newLayer(Json::objectValue);            newLayer["postrotate"]["radians"] = -yaw;            newLayer["postrotate"]["axis"] = "y";            newLayer[FLAG_KEY] = true;            xforms.wrap(newLayer);            cout << "New source: "                 << xforms.get(origAlias.getLeaf()).toStyledString() << endl;            elt->setSource(osvr::common::jsonToCompactString(                xforms.get(origAlias.getLeaf())));            ctx.get()->sendRoute(createJSONAlias(path, *elt));            boost::this_thread::sleep(SETTLE_TIME / 2);        }        boost::this_thread::sleep(SETTLE_TIME);        if (!noWait) {            cout << "Press enter to exit.";            std::cin.ignore();        }    }    return 0;}


整个过程为:通过path: /me/head 获取接口,通过接口再获取四元数,通过四元数再获取欧拉角的yaw角。创建一个json对象用来存储欧拉角yaw值,再进行几何变形转换。再将该Json 写入path对应的文件。
输出:


原创粉丝点击