【青瞳视觉C++接入文档】
1. 简介
青瞳视觉提供了一套用于连接和操作其动捕系统(CMAvatar 和 CMTracker)的C++ SDK,允许用户获取实时的刚体、骨骼及手指数据。本接入文档旨在指导开发者如何使用该SDK进行开发,包括环境设置、API调用方法、示例代码以及故障排除。
源码地址:https://github.com/ChingMuVisionTech/ChingMuCppSDKs
2. 开始前准备
在开始之前,请确保您的开发环境满足以下条件:
- 操作系统:Windows 10 或更高版本。
- 编译器:Visual Studio 2019 或以上版本。
- 依赖库:确保已安装
CMVrpn.dll
及其相关依赖项,可以从官方GitHub仓库下载最新版本。
2.1 安装SDK
从GitHub仓库下载最新的C++ SDK,并解压到一个合适的位置。将 ChingmuDLL
文件夹路径添加到系统的环境变量 PATH
中,以便能够加载动态链接库 CMVrpn.dll
。
2.2 打开软件服务器
启动CMTracker Server软件服务器
然后运行CMTracker软件,窗口查看网络地址:192.168.0.9
接下来需要运行代码的电脑也同时接入到这个服务器的路由器,进行VRPN通讯
此时手动添加4个动捕小球组合成一个刚体Body_0
打开下面接口配置,参考下图配置
3. 初始化与配置
3.1 加载动态库
在项目中包含 CMVrpn.h
头文件,并确保 CMVrpn.dll
在运行时可以被找到。通常情况下,您只需要确保 ChingmuDLL
文件夹位于可执行文件所在的目录或系统路径中。
#include "CMVrpn.h"
3.2 启动VRPN线程
为了开始接收数据,必须启动VRPN线程:
CMVrpnStartExtern();
3.3 启用日志记录
启用日志记录可以帮助调试和监控程序运行状态:
CMVrpnEnableLog(true);
4. 数据获取
根据需求,您可以选择获取不同类型的数据,例如刚体位置和旋转、人物姿态信息等。
4.1 获取刚体数据
要获取特定ID的刚体预测位姿信息,可以使用 CMTrackerExtern
函数:
void GetRigidBodyData(const char* address, int bodyID, int frameCount) {
double bodyPos[3], bodyRot[4];
for (int i = 0; i < 3; ++i) {
bodyPos[i] = CMTrackerExtern(address, bodyID, i, frameCount);
}
for (int i = 0; i < 4; ++i) {
bodyRot[i] = CMTrackerExtern(address, bodyID, i + 3, frameCount);
}
bool isDetected = CMTrackerExternIsDetected(address, bodyID, frameCount);
if (isDetected) {
printf("Body pos: %lf %lf %lf\n", bodyPos[0], bodyPos[1], bodyPos[2]);
printf("Body quaternion: %lf %lf %lf %lf\n", bodyRot[0], bodyRot[1], bodyRot[2], bodyRot[3]);
} else {
printf("Rigid body %d not detected\n", bodyID);
}
}
4.2 注册回调函数
为了处理层级结构更新和重置事件,可以注册相应的回调函数:
void CallbackUpdateHierarchy(void*, VrpnHierarchy b) {
// 处理层级结构更新
printf("name:%s id:%d parent id:%d\n", b.name, b.sensor, b.parent);
}
void CallbackResetHierarchy(void*, timeval t) {
// 处理层级结构重置
printf("reset hierarchy\n");
}
// 注册回调函数
std::unique_ptr<VrpnHierarchy> userData(new VrpnHierarchy);
bool ret = CMPluginRegisterUpdateHierarchy(address, userData.get(), CallbackUpdateHierarchy);
ret = CMPluginRegisterResetHierarchy(address, userData.get(), CallbackResetHierarchy);
5. 完整代码
为了持续获取数据,通常会在一个循环中调用上述函数,并控制采集频率。下面是一个完整的例子:
#include "CMVrpn.h"
#include <stdio.h>
#include <windows.h>
#include <memory>
#include <signal.h>
volatile sig_atomic_t stop;
void signalHandler(int signum) {
stop = 1;
}
void CallbackUpdateHierarchy(void*, VrpnHierarchy b) {
// hierarchy info
printf("name:%s id:%d parent id:%d\n", b.name, b.sensor, b.parent);
}
void CallbackResetHierarchy(void*, timeval t) {
// reset hierarchy
printf("reset hierarchy\n");
}
int main() {
signal(SIGINT, signalHandler); // 捕获 Ctrl+C 信号
char *address = "MCServer@192.168.0.9";
// start vrpn thread
CMVrpnStartExtern();
// enable write trace_log.txt
CMVrpnEnableLog(true);
std::unique_ptr<VrpnHierarchy> userData(new VrpnHierarchy);
// register human model hierarchy
bool ret = CMPluginRegisterUpdateHierarchy(address, userData.get(), CallbackUpdateHierarchy);
// register human model reset hierarchy
ret = CMPluginRegisterResetHierarchy(address, userData.get(), CallbackResetHierarchy);
int frameCount = 0;
while (!stop) {
auto timecode = std::make_unique<VrpnTimeCode>();
bool isHumanDetected, isBodyDetected;
int segmentIsDetected[MAX_SEGMENT_NUM];
// Person ID displayed on the server
int bodyID = 0;
double bodyPos[3], bodyRot[4];
// get body component 0:x, 1:y, 2:z, 3:rx, 4:ry, 5:rz, 6:rw with time predict
bodyPos[0] = CMTrackerExtern(address, bodyID, 0, frameCount);
bodyPos[1] = CMTrackerExtern(address, bodyID, 1, frameCount);
bodyPos[2] = CMTrackerExtern(address, bodyID, 2, frameCount);
bodyRot[0] = CMTrackerExtern(address, bodyID, 3, frameCount);
bodyRot[1] = CMTrackerExtern(address, bodyID, 4, frameCount);
bodyRot[2] = CMTrackerExtern(address, bodyID, 5, frameCount);
bodyRot[3] = CMTrackerExtern(address, bodyID, 6, frameCount);
// check body detected
isBodyDetected = CMTrackerExternIsDetected(address, bodyID, frameCount);
if (isBodyDetected) {
printf("Body pos:%lf %lf %lf\n", bodyPos[0], bodyPos[1], bodyPos[2]);
printf("Body quaternion:%lf %lf %lf %lf\n", bodyRot[0], bodyRot[1], bodyRot[2], bodyRot[3]);
}
// 其他相关代码略...
frameCount++;
Sleep(8);
}
// quit vrpn thread
CMVrpnQuitExtern();
return 0;
}
- 需要修改两处代码,首先是查看服务器地址,你采用
CMAvatar
和CMTracker
分别一一对应,我是CMTracker
采用MCServer@192.168.0.9
。
//set CMTracker server address
char *address = "MCServer@192.168.0.9";
//set CMAvatar server address
char *address = "MCAvatar@192.168.0.9";
根据按照动捕服务器可分为两种类型。
-
动捕服务器为 CMAvatar 时,vrpn 的地址为 MCAvatar@动捕服务器地址,默认的端口采用3883。其中,刚体数据的范围为 0~300,和动捕服务器中的刚体 id 一一对应。骨骼数据从 300开始,间隔为 150,即第一个人的第一个关节为 300,第二个人的第一个关节为 450,依次类推。重定向骨骼数据和非重定向的骨骼数据的定义一致,根据重定向发送的层级信息来确定骨骼 id与模型关节的对应关系。
-
动捕服务器为 CMTracker 时,tracker server 和 tracker client 均有 vrpn server 存在。MCServer@动捕服务器地址为 tracker server,而 MCServer@动捕服务器地址:3884 为 tracker client。其中,刚体数据的范围为 0-100,和动捕服务器中的刚体 id 一一对应。刚体上的 Marker 点的 id 范围为1300-6300,每个刚体预留 50 位,即第二个刚体的第一个 marker 的 id 从 1350 开始。骨骼的数据为 100 开始,3883 端口下,间隔为 23,3884 端口下,间隔为 150。骨骼上的 marker 点序号从6300 开始,每个人预留 100 位。未标记 marker 点的 id 序号是从 11300 开始。3883 端口仅支持发送非重定向数据。3884 端口可以通过界面勾选来发送非重定向数据或者非重定向数据。
-
bodyID = 0
对应刚体编号Body_0
// Person ID displayed on the server
int bodyID = 0;
显示输出效果
Body pos:-1562.754311 -618.426219 39.156954
Body quaternion:-0.003409 0.016292 0.999808 0.010322
Body pos:-1562.223550 -618.217084 39.144874
Body quaternion:-0.003398 0.016289 0.999803 0.010853
Body pos:-1561.867860 -618.076987 39.136774
Body quaternion:-0.003388 0.016290 0.999799 0.011208
Body pos:-1562.230338 -618.219820 39.144930
Body quaternion:-0.003393 0.016293 0.999803 0.010846
Body pos:-1561.944678 -618.107239 39.138659
Body quaternion:-0.003387 0.016295 0.999800 0.011132
Body pos:-1561.680335 -618.003060 39.132796
Body quaternion:-0.003382 0.016295 0.999797 0.011396
Body pos:-1562.002715 -618.130056 39.140132
Body quaternion:-0.003387 0.016297 0.999800 0.011074
Body pos:-1561.635982 -617.985476 39.131813
Body quaternion:-0.003377 0.016296 0.999796 0.011440
Body pos:-1561.322893 -617.862171 39.124797
Body quaternion:-0.003360 0.016295 0.999792 0.011754
Body pos:-1561.344569 -617.870736 39.125332
Body quaternion:-0.003348 0.016295 0.999793 0.011733
Body pos:-1561.727363 -618.021565 39.134002
Body quaternion:-0.003349 0.016297 0.999797 0.011351
Body pos:-1562.268602 -618.234813 39.146238
Body quaternion:-0.003356 0.016300 0.999803 0.010811
Body pos:-1561.734440 -618.024409 39.134380
Body quaternion:-0.003334 0.016296 0.999797 0.011344
6. 常见问题解答
-
Q: 我遇到了
std::out_of_range
异常,如何解决?- A: 这个异常通常是由于尝试访问超出容器边界的位置引起的。请检查您的代码中对容器的访问是否正确,并确保在访问前进行了适当的边界检查。
-
Q: 如何确保我使用的
CMVrpn.dll
是最新版本?- A: 请定期检查 GitHub 仓库中的更新,并确保本地使用的
CMVrpn.dll
与 C++ SDK 的版本匹配。如果有新的版本发布,建议及时更新。
- A: 请定期检查 GitHub 仓库中的更新,并确保本地使用的
-
Q: 在多线程环境中使用SDK时需要注意什么?
- A: 当在多线程环境中使用SDK时,确保每个线程独立地管理其资源,避免多个线程同时操作同一个对象或资源。此外,注意线程安全性和同步问题,以防止竞态条件。
7. 结论
通过本接入文档,您应该能够顺利地将青瞳视觉的C++ SDK集成到您的项目中,并开始获取和处理动捕数据。如果您遇到任何问题或有进一步的需求,请参考官方文档或联系技术支持团队获取帮助。
附录A: API参考
为了方便开发者查阅,以下是部分常用API的简要说明:
CMVrpnStartExtern()
: 启动VRPN线程,开始接收数据。CMVrpnEnableLog(bool enable)
: 启用或禁用日志记录。CMTrackerExtern(const char* host, int bodyID, int component, int frameCount) -> double
: 获取刚体的某个分量值(如位置或旋转)。CMTrackerExternIsDetected(const char* host, int bodyID, int frameCount) -> bool
: 检查刚体是否被检测到。CMPluginRegisterUpdateHierarchy(const char* address, void* userData, void (*callback)(void*, VrpnHierarchy)) -> bool
: 注册层级结构更新回调。CMPluginRegisterResetHierarchy(const char* address, void* userData, void (*callback)(void*, timeval)) -> bool
: 注册层级结构重置回调。CMVrpnQuitExtern()
: 退出VRPN线程,停止接收数据。
请注意,这些API的具体参数和返回值可能根据实际版本有所不同,建议结合官方文档进行开发。
从而实现对外部世界进行感知,充分认识这个有机与无机的环境,科学地合理地进行创作和发挥效益,然后为人类社会发展贡献一点微薄之力。🤣🤣🤣
- 我会持续更新对应专栏博客,非常期待你的三连!!!🎉🎉🎉
- 如果鹏鹏有哪里说的不妥,还请大佬多多评论指教!!!👍👍👍
- 下面有我的🐧🐧🐧群推广,欢迎志同道合的朋友们加入,期待与你的思维碰撞😘😘😘