本文主要内容为在VS中以C++调用Windows提供的API,来实现Vrep与VS交互过程中的多线程效果。详细实现思路及步骤在第三部分中。
实际工程中往往都是多任务的,同时进行外设驱动,定位,PID控制器等,使用UCOS或者FreeRTOS的实时操作系统进行协调处理。而在学习Vrep这一仿真软件到目前为止,都是使用的线性结构程序代码,无法满足后续的实际需求。就开始学习有什么方法能够实现这一点。
感觉使用Vrep的Lua语言脚本并不是特别方便,之前用过的也只是non-threaded。讲究父子结构树关系,包括设置优先级时也要注意这些限制。搜索了相关资料作些了解吧。对于Lua的“多线程”用协程来描述更妥些。
《Vrep线程之间的切换》
《Vrep脚本的执行顺序》
《vrep中thread scirpt以及simRemoteApi.start()的一点点微小的认识》
至于里面提及的API,官方文档API表单中都能查询到,只需要注意一些输入输出参数的信息并不是很详细全面。
自C++11标准之后,已经提供了"thread.h"专门的线程文件,相关使用介绍,可以看下面两篇博客的介绍。
《C++使用thread类多线程编程》
《C++ 多线程编程之在类中使用多线程(thread)的方法》
这边还有一个C++11标准之前,通过“process.h”,自写仿java的Thread类实现多线程的方法。
《C++ 实现多线程编程(通过process.h)》
而本次使用的则是Windows系统提供的相关API。
《C++ 实现多线程编程(通过Windows提供的API)》
这里具体步骤就不细说了,熟悉Vrep操作之后算是很简单的操作了。详细可以参考这一篇,我初学时看的《Vrep小车建模——前进和转向》。
稍有不同的就是添加了两侧驱动轮的转速,并通过框图表显示出来。如何添加可以参考同一作者的这一篇《Vrep小车建模——Lua内嵌脚本》。
#include
#include
using namespace std;
extern "C" {
#include "extApi.h"
}
int clientID;
int leftvalue;
int rightvalue;
int countLeft = 0;
int countRight = 0;
simxInt LeftMotor;
simxInt RightMotor;
DWORD WINAPI myfun1(LPVOID lpparm);
DWORD WINAPI myfun2(LPVOID lpparm);
int main()
{
clientID = simxStart("127.0.0.1", 19997, true, true, 5000, 5);
if (clientID != -1)
{
cout << "Success!" << endl;
}
else
{
cerr << "error" << endl;
}
leftvalue = 3;
rightvalue= 3;
simxStartSimulation(clientID, simx_opmode_oneshot_wait);
simxGetObjectHandle(clientID, "LeftMotor", &LeftMotor, simx_opmode_blocking);
simxGetObjectHandle(clientID, "RightMotor", &RightMotor, simx_opmode_blocking);
HANDLE h1, h2;
h1 = CreateThread(NULL, 0, myfun1, NULL, 0, NULL);
cout << "线程1开始运行\n" << endl;
h2 = CreateThread(NULL, 0, myfun2, NULL, 0, NULL);
cout << "线程2开始运行" << endl;
CloseHandle(h1);
CloseHandle(h2);
while (1)
{
if (getchar() == 'q')
{
simxStopSimulation(clientID, simx_opmode_oneshot);
simxFinish(clientID);
return 0;
}
else
{
Sleep(100);
}
}
}
DWORD WINAPI myfun1(LPVOID lpparm)
{
while (clientID != -1)
{
simxSetJointTargetVelocity(clientID, LeftMotor, leftvalue, simx_opmode_oneshot);
Sleep(10);
countLeft++;
if(countLeft == 300)
{
rightvalue = -rightvalue;
countLeft = 0;
}
}
return 0;
}
DWORD WINAPI myfun2(LPVOID lpparm)
{
while (clientID != -1)
{
simxSetJointTargetVelocity(clientID, RightMotor, rightvalue, simx_opmode_oneshot);
Sleep(10);
countRight++;
if (countRight == 300)
{
leftvalue = -leftvalue;
countRight = 0;
}
}
return 0;
}
可以看到小车如同代码程序一样。
DWORD WINAPI myfun1(LPVOID lpparm)
{
while (clientID != -1)
{
simxSetJointTargetVelocity(clientID, LeftMotor, leftvalue, simx_opmode_oneshot);
Sleep(10);
countLeft++;
if(countLeft == 300)
{
rightvalue = -rightvalue;
countLeft = 0;
}
}
return 0;
}
每间隔3000ms,电机转速取反,这边为了测试,能否线程之间对同一数据修改,如同上面代码缩写,在控制左轮的线程中,给右轮转速取反。同样,在控制右轮的线程中,给左轮转速取反。
上面大图中的轮速数据表图不太清楚,下面给一个清晰的。
可以看到两条红线的变化,基本实现了3000ms同时换向一次。
以下面的框架结构,即可实现多任务的效果。
...
int main()
{
...
HANDLE h1, h2;
h1 = CreateThread(NULL, 0, myfun1, NULL, 0, NULL);
...
CloseHandle(h1);
...
while (1)
{
}
return 0;
}
...
DWORD WINAPI myfun1(LPVOID lpparm)
{
while (1)
{
...
Sleep(10);
}
}
...
需要注意的就是目前的设立方法,并没有注重线程的同步和优先级的考虑。意味着,多线程的资源是随机分配的。若想在这两点上也加以限制,可以参考下面两篇博文:
《C++ 线程同步的四种方式》
《VS C++ 线程篇之三设置优先级》