VREP中直接设置物体姿态的函数有3个:

  1. simSetObjectOrientation:通过欧拉角设置姿态
  2. simSetObjectQuaternion:通过四元数设置姿态
  3. simSetObjectMatrix:通过旋转矩阵设置姿态(同时也可以设置位置)
  通过设置物体矩阵可以同时改变其位置与姿态,参数matrix是一个包含12个元素的列表: 12 simFloat values (the last row of the 4x4 matrix (0,0,0,1) is not needed).
  The x-axis of the orientation component is (matrix[0],matrix[4],matrix[8])
  The y-axis of the orientation component is (matrix[1],matrix[5],matrix[9])
  The z-axis of the orientation component is (matrix[2],matrix[6],matrix[10])
  The translation component is (matrix[3],matrix[7],matrix[11])

  下面将一个坐标系绕X轴旋转90°,则可以分别使用欧拉角、四元数或变换矩阵实现:

handle=simGetObjectHandle('Marker')

local eulerAngles = {math.pi/, , }   -- X-Y-Z Euler angles
local quaternion = {0.707, , , 0.707} -- {x,y,z,w}
local matrix = {,,,, ,,-,, ,,,0.5}
simSetObjectOrientation(handle, -, eulerAngles)
--simSetObjectQuaternion(handle, -1, quaternion)
--simSetObjectMatrix(handle, -1, matrix)

  • 球关节

  Spherical joints have three DoF and are used to describe rotational movements (with 3 DoF) between objects. Their configuration is defined by three values that represent the amount of rotation around their first reference frame's x-, y- and z-axis. The three values that define a spherical joint's configuration are specified as Euler angles. In some situations, a spherical joint can be thought of as 3 concurrent and orthogonal to each other joints, that are parented in a hierarchy-chain. Spherical joints are always passive joints, and cannot act as motors.

[Two equivalent mechanisms (in this configuration): spherical joint (left) and 3 revolute joints (right)]

  在场景中创建一个球关节和一个连杆(处于竖直状态),将连杆拖到球关节下面作为其子节点,球关节设置为Passive模式。下面的代码控制了球关节的姿态,使用simSetSphericalJointMatrix函数设置关节旋转矩阵,使得连杆绕X轴旋转90°

handle=simGetObjectHandle('Spherical_joint')
local matrix = {,,,, ,,-,, ,,,} -- the translational components will be ignored
-- Sets the intrinsic orientation matrix of a spherical joint object. This function cannot be used with non-spherical joints
simSetSphericalJointMatrix(handle, matrix)


  • C++客户端程序与VREP通信

  进行C++客户端程序与VREP服务端通信,需要对工程进行如下配置:

  1. 在项目中包含下列文件(可以在V-REP安装路径的programming/remoteApi文件夹下找到这些文件)

  • extApi.h
  • extApi.c
  • extApiPlatform.h (contains platform specific code)
  • extApiPlatform.c (contains platform specific code)

  2. 在项目属性-->C/C++-->预处理器-->预处理器定义中定义:NON_MATLAB_PARSING 和 MAX_EXT_API_CONNECTIONS=255

  3. 在项目属性-->C/C++-->常规-->附加包含目录中包含:

  C:\Program Files\V-REP3\V-REP_PRO_EDU\programming\include

  C:\Program Files\V-REP3\V-REP_PRO_EDU\programming\remoteApi


  下面创建一个简单的场景使用Kinect来控制NAO机器人头部的运动。具体步骤是获得Neck关节的姿态四元数后将其转换为欧拉角,经过适当转换后通过simxSetJointPosition函数直接设置转动关节的角度(关节要设置为Passive模式)。如果不通过关节来控制头部的运动也可以直接采用上面提到的SetObjectOrientation、SetObjectQuaternion或SetObjectMatrix方式来设置头部姿态。需要注意的是Kinect的Head关节为末端节点,不包含姿态信息,因此这里采用了Neck关节的姿态来控制头部,但效果不是很好。如果想直接获取头部姿态,可以参考Kinect for Windows SDK 2.0中的HD Face Basics例子,其中FaceFrameResult Class可以获取代表面部姿态的四元数:

hr = pFaceFrameResult -> get_FaceRotationQuaternion(&faceRotation);

  下面是一些与之相关的代码。最容易出错的部分是在于Kinect坐标系和VREP坐标系的姿态不一样,因此获得的角度要经过适当转换:Kinect中头部左右摇摆是绕Y轴,而VREP中是绕Z轴;Kinect中头向上仰为绕X轴正方向,而VREP中头向上仰是绕Y轴负方向...

#define PI 3.1415926

int   Sign(double x) { if (x < ) return -; else return ; }

float R2D(float angle){ return angle * 180.0 / PI; }

enum  RotSeq{ zyx, xyz }; // 欧拉角旋转次序

CameraSpacePoint CBodyBasics::QuaternionToEuler(Vector4 q, RotSeq rotSeq)
{
CameraSpacePoint euler = { };
const double Epsilon = 0.0009765625f;
const double Threshold = 0.5f - Epsilon; switch (rotSeq)
{
case zyx: // Z-Y-X Euler angles(RPY angles)
{
double TEST = q.w*q.y - q.x*q.z;
if (TEST < -Threshold || TEST > Threshold) // 奇异姿态,俯仰角为±90°
{
int sign = Sign(TEST);
euler.Z = - * sign * (double)atan2(q.x, q.w); // yaw
euler.Y = sign * (PI / 2.0); // pitch
euler.X = ; // roll }
else
{
euler.X = atan2( * (q.y*q.z + q.w*q.x), q.w*q.w - q.x*q.x - q.y*q.y + q.z*q.z); // roll
euler.Y = asin(- * (q.x*q.z - q.w*q.y)); // pitch
euler.Z = atan2( * (q.x*q.y + q.w*q.z), q.w*q.w + q.x*q.x - q.y*q.y - q.z*q.z); // yaw
}
}
break; case xyz: // X-Y-Z Euler angles
{
double TEST = q.x*q.z + q.w*q.y;
if (TEST < -Threshold || TEST > Threshold) // 奇异姿态,俯仰角为±90°
{
int sign = Sign(TEST);
euler.X = * sign * (double)atan2(q.x, q.w);
euler.Y = sign * (PI / 2.0);
euler.Z = ;
}
else
{
euler.X = atan2(- * (q.y*q.z - q.w*q.x), q.w*q.w - q.x*q.x - q.y*q.y + q.z*q.z);
euler.Y = asin( * (q.x*q.z + q.w*q.y));
euler.Z = atan2(- * (q.x*q.y - q.w*q.z), q.w*q.w + q.x*q.x - q.y*q.y - q.z*q.z);
}
} }
return euler;
} /// Handle new body data
void CBodyBasics::ProcessBody(IBody* pBody)
{
HRESULT hr;
BOOLEAN bTracked = false;
hr = pBody->get_IsTracked(&bTracked); // Retrieves a boolean value that indicates if the body is tracked if (SUCCEEDED(hr) && bTracked) // 判断是否追踪到骨骼
{
Joint joints[JointType_Count];
JointOrientation jointOrientations[JointType_Count]; HandState leftHandState = HandState_Unknown;
HandState rightHandState = HandState_Unknown; DepthSpacePoint *depthSpacePosition = new DepthSpacePoint[_countof(joints)]; // 存储深度坐标系中的关节点位置 pBody->get_HandLeftState(&leftHandState); // 获取左右手状态
pBody->get_HandRightState(&rightHandState); hr = pBody->GetJointOrientations(_countof(joints), jointOrientations); // 获取joint orientation
if (SUCCEEDED(hr))
{
CameraSpacePoint euler = QuaternionToEuler(jointOrientations[JointType_Neck].Orientation, xyz); // 四元数转换为X-Y-Z欧拉角 simxSetJointPosition(clientID, Handle_Yaw, euler.Y, simx_opmode_oneshot); // 控制头部左右摆动
simxSetJointPosition(clientID, Handle_Pitch, PI-euler.X, simx_opmode_oneshot); // 控制头部俯仰 extApi_sleepMs();
} hr = pBody->GetJoints(_countof(joints), joints); // 获得25个关节点
if (SUCCEEDED(hr))
{
// Filtered Joint
filter.Update(joints);
const DirectX::XMVECTOR* vec = filter.GetFilteredJoints(); // Retrive Filtered Joints for (int type = ; type < JointType_Count; type++)
{
if (joints[type].TrackingState != TrackingState::TrackingState_NotTracked)
{
float x = 0.0f, y = 0.0f, z = 0.0f;
// Retrieve the x/y/z component of an XMVECTOR Data and storing that component's value in an instance of float referred to by a pointer
DirectX::XMVectorGetXPtr(&x, vec[type]);
DirectX::XMVectorGetYPtr(&y, vec[type]);
DirectX::XMVectorGetZPtr(&z, vec[type]);
CameraSpacePoint cameraSpacePoint = { x, y, z };
m_pCoordinateMapper->MapCameraPointToDepthSpace(cameraSpacePoint, &depthSpacePosition[type]);
}
}
DrawBody(joints, depthSpacePosition);
DrawHandState(depthSpacePosition[JointType_HandLeft], leftHandState);
DrawHandState(depthSpacePosition[JointType_HandRight], rightHandState);
}
delete[] depthSpacePosition;
}
cv::imshow("skeletonImg", skeletonImg);
cv::waitKey(); // 延时5ms /// Constructor
CBodyBasics::CBodyBasics() :
m_pKinectSensor(NULL),
m_pCoordinateMapper(NULL),
m_pBodyFrameReader(NULL)
{
clientID = simxStart((simxChar*)"127.0.0.1", , true, true, , ); // 连接VREP服务端
if (clientID != -)
{
std::cout << "Connected to remote API server" << std::endl; // Send some data to V-REP in a non-blocking fashion:
simxAddStatusbarMessage(clientID, "Connected to V-REP!", simx_opmode_oneshot); Handle_Yaw = ;
Handle_Pitch = ;
simxGetObjectHandle(clientID, "HeadYaw", &Handle_Yaw, simx_opmode_oneshot_wait); // 获取VREP中Yaw关节的句柄
simxGetObjectHandle(clientID, "HeadPitch", &Handle_Pitch, simx_opmode_oneshot_wait); // 获取VREP中Pitch关节句柄
} } /// Destructor
CBodyBasics::~CBodyBasics()
{
SafeRelease(m_pBodyFrameReader);
SafeRelease(m_pCoordinateMapper); if (m_pKinectSensor)
{
m_pKinectSensor->Close();
}
SafeRelease(m_pKinectSensor); // Close the connection to V-REP:
simxFinish(clientID);
}

  另外还有一个问题就是原始数据的抖动比较大,头部旋转的时候不够平滑,有很多种滤波方式可以解决这一问题。最简单的移动平均滤波参考代码如下:

#include <iostream>
#include <stddef.h>
#include <assert.h> using std::cout;
using std::endl; // Simple_moving_average
class SMA
{
public:
SMA(unsigned int period) :period(period), window(new double[period]), head(NULL), tail(NULL), total()
{
assert(period >= );
}
~SMA()
{
delete[] window;
} // Adds a value to the average, pushing one out if nescessary
void add(double val)
{
// Special case: Initialization
if (head == NULL)
{
head = window;
*head = val;
tail = head;
inc(tail);
total = val;
return;
} // Were we already full?
if (head == tail)
{
// Fix total-cache
total -= *head;
// Make room
inc(head);
} // Write the value in the next spot.
*tail = val;
inc(tail); // Update our total-cache
total += val;
} // Returns the average of the last P elements added to this SMA.
// If no elements have been added yet, returns 0.0
double avg() const
{
ptrdiff_t size = this->size();
if (size == )
return ; // No entries => 0 average return total / (double)size; // Cast to double for floating point arithmetic
} private:
unsigned int period;
double * window; // Holds the values to calculate the average of. // Logically, head is before tail
double * head; // Points at the oldest element we've stored.
double * tail; // Points at the newest element we've stored. double total; // Cache the total so we don't sum everything each time. // Bumps the given pointer up by one.
// Wraps to the start of the array if needed.
void inc(double * & p)
{
if (++p >= window + period)
{
p = window;
}
} // Returns how many numbers we have stored.
ptrdiff_t size() const
{
if (head == NULL)
return ;
if (head == tail)
return period;
return (period + tail - head) % period;
}
}; int main(int argc, char * * argv)
{
SMA foo();
SMA bar(); int data[] = { , , , , , , , , , };
for (int * itr = data; itr < data + ; itr++)
{
foo.add(*itr);
cout << "Added " << *itr << " avg: " << foo.avg() << endl;
} cout << endl; for (int * itr = data; itr < data + ; itr++)
{
bar.add(*itr);
cout << "Added " << *itr << " avg: " << bar.avg() << endl;
} return ;
}

  下面是NAO随着我的头部先进行俯仰,然后左右摇摆的动态图:

  另外也可以使用获取到的三维坐标点计算出关节夹角,以此来控制虚拟模型。下面计算出肘关节和肩关节角度,来控制VREP场景中的一个2自由度手臂:

参考:

Kinect2.0骨骼层次与Joint Orientation

quaternions.online

Averages/Simple moving average

使用Kinect2.0控制VREP中的虚拟模型的更多相关文章

  1. 使用Kinect2.0获取点云以在GLUT中显示

    这篇文章用来记录Kinect2.0如何生成点云. 以下示例源自Kinect提供的example修改完成,其名称会在小标题下方注解. 首先,要获取点云需要获取图像的深度数据和颜色数据.最后再将深度数据与 ...

  2. IIS6.0、IIS7中的站点、应用程序和虚拟目录详细介绍

    这里说的不是如何解决路径重写或者如何配置的问题,而是阐述一下站点(site),应用程序(application)和虚拟目录 (virtual directory)概念与作用,已及这三个东西在IIS6与 ...

  3. 全向轮运动学与V-rep中全向移动机器人仿真

    Wheeled mobile robots may be classified in two major categories, omnidirectional and nonholonomic. O ...

  4. ubuntu /etc/network/interfaces 中配置虚拟链路

    ubuntu /etc/network/interfaces 中配置虚拟链路 平常做一些关于网络的测试时,像一些需要在二层上运行的功能,一个网卡不足够的情况下,可使用 ip link 工具加一些虚拟的 ...

  5. C#调PowerShell在SCVMM中创建虚拟机时,实时显示创建进度

    关于c#调用PowerShell来控制SCVMM,网上有很多例子,也比较简单,但创建虚拟机的过程,是一个很漫长的时间,所以一般来说,创建的时候都希望可以实时的显示当前虚拟机的创建进度.当时这个问题困扰 ...

  6. 控制GridView中字段的长度,规范数据

    前台:   <asp:GridView ID="GridView1" runat="server" OnRowDataBound="GridVi ...

  7. 在Apache中开启虚拟主机

    最近在自学LAMP,在Apache中尝试着开启虚拟主机的时候,遇到了挺多麻烦的,这里也顺便总结一下,在Apache中开启虚拟主机的时候,主要有下面几个步骤: 1.新建一个文件夹作为虚拟主机,用来存储网 ...

  8. virtenv 0.8.6 发布,虚拟桌面配置工具 - 开源中国社区

    virtenv 0.8.6 发布,虚拟桌面配置工具 - 开源中国社区 virtenv 0.8.6 发布,virtenv 是一个用 QT4 开发的应用,用来配置和启动基于 LXC 的虚拟桌面环境.该容器 ...

  9. 【译】.NET Core 3.0 Preview 3中关于ASP.NET Core的更新内容

      .NET Core 3.0 Preview 3已经推出,它包含了一系列关于ASP.NET Core的新的更新. 下面是该预览版的更新列表: Razor组件改进: 单项目模板 新的Razer扩展 E ...

随机推荐

  1. 理解 Linux 的处理器负载均值

    原文链接: http://blog.scoutapp.com/articles/2009/07/31/understanding-load-averages 你可能对于 Linux 的负载均值(loa ...

  2. 奇怪吸引子---Aizawa

    奇怪吸引子是混沌学的重要组成理论,用于演化过程的终极状态,具有如下特征:终极性.稳定性.吸引性.吸引子是一个数学概念,描写运动的收敛类型.它是指这样的一个集合,当时间趋于无穷大时,在任何一个有界集上出 ...

  3. 7.1 服务暴露前的准备-ServiceBean的装配

    dubbo的服务暴露以第一章 第一个dubbo项目中的dubbo-demo-provider来讲述. 列出dubbo-demo-provider的xml配置: <?xml version=&qu ...

  4. c#利用SWIG调用c++dll学习总结【转】

    开发环境: 操作系统:windows 7 IDE:Microsoft Visual Studio Professional 2015 SWIG: 3.0.12 swig的介绍 详细介绍可看官网,一下贴 ...

  5. Validate Binary Search Tree leetcode java

    题目: Given a binary tree, determine if it is a valid binary search tree (BST). Assume a BST is define ...

  6. 大数据开发实战:Hive表DDL和DML

    1.Hive 表 DDL 1.1.创建表 Hive中创建表的完整语法如下: CREATE [EXTERNAL] TABLE [IF NOT EXISTS]  table_name [ (col_nam ...

  7. Laravel学习笔记之Session源码解析(上)

    说明:本文主要通过学习Laravel的session源码学习Laravel是如何设计session的,将自己的学习心得分享出来,希望对别人有所帮助.Laravel在web middleware中定义了 ...

  8. MySQL8.0手动安装心得。

    https://dev.mysql.com/downloads/mysql/ mysql从5.7一下子跳跃到了8.0,其中的改变还是很大,有点这里就不说了,小伙伴们自己去百度了解一下,这里重点说一下, ...

  9. Android通用框架设计与完整电商APP开发系列文章

    作者|傅猿猿 责编|Javen205 有福利 有福利 有福利 鸣谢 感谢@傅猿猿 邀请写此系列文章 Android通用框架设计与完整电商APP开发 课程介绍 [导学视频] [课程详细介绍] 以下是部分 ...

  10. Android 模仿QQ风格的 UI

    本文内容 环境 演示模仿QQ风格的界面 本文主要演示的是 UI,如何模仿 QQ 风格的界面.虽然这个 UI 跟现在的QQ空间有点差别,但是也能学到很多东西. 下载 Demo 环境 Windows 7 ...