OSVR Reset Yaw

作用:

该工具可用来对/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对应的文件。
输出:


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值