0. 参考:
visual studio2019+opencv440 Dll动态链接库封装demo
1. 新建库工程
打开 visual studio 2019 新建 c++ ,dll 动态链接库工程
2. 封装库
头文件 pch.h
#pragma once
#include <iostream>
using namespace std;
class _declspec(dllexport) PATH;
class PATH
{
public:
PATH(); //构造函数
int add(int a, int b);
};
源文件 pch.cpp
#include "pch.h"
#include "dlltest.h" //把刚刚的头文件包含进去
PATH::PATH()
{
}
int PATH::add(int a, int b)
{
return a + b;
}
生成解决方案后 x64/release文件夹生成了 .dll 和 .lib 文件
3. 新工程调用库进行测试
将头文件 pch.h 复制到新工程文件夹
需要修改 dllexport
为 dllimport
#pragma once
#include <iostream>
using namespace std;
class _declspec(dllimport) PATH;
class PATH
{
public:
PATH(); //构造函数
int add(int a, int b);
};
新工程的源文件 Plugin_Test.cpp
// Plugin_Test.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include "pch.h"
#include <iostream>
#pragma comment(lib,"simKINOVA-1.lib")
int main()
{
std::cout << "Hello World!\n";
PATH pdll;
int out = pdll.add(4, 9);
std::cout << out << endl;
}
// 运行程序: Ctrl + F5 或调试 >“开始执行(不调试)”菜单
// 调试程序: F5 或调试 >“开始调试”菜单
// 入门使用技巧:
// 1. 使用解决方案资源管理器窗口添加/管理文件
// 2. 使用团队资源管理器窗口连接到源代码管理
// 3. 使用输出窗口查看生成输出和其他消息
// 4. 使用错误列表窗口查看错误
// 5. 转到“项目”>“添加新项”以创建新的代码文件,或转到“项目”>“添加现有项”以将现有代码文件添加到项目
// 6. 将来,若要再次打开此项目,请转到“文件”>“打开”>“项目”并选择 .sln 文件
生成解决方案后运行即可
4. 采用coppeliasim的plugin封装库格式封装API
参考Git库里面的 simSkeleton 项目,是一个简单版的plugin封装
核心代码修改 simKinova.cpp 中的callback function 注册以及定义函数
注册getSum函数后,在 coppeliasim 脚本中即可使用 simKINOVA.getSum
// Register the new function:
simRegisterScriptCallbackFunction("getSum", nullptr, LUA_GETSUM_CALLBACK);
以及
void LUA_GETSUM_CALLBACK(SScriptCallBack* p)
{ // the callback function of the new Lua command ("simSkeleton.getData")
int stack = p->stackID;
CStackArray inArguments;
inArguments.buildFromStack(stack);
if ((inArguments.getSize() >= 2) && inArguments.isNumber(0) && inArguments.isNumber(1))
{
float a = inArguments.getFloat(0);
float b = inArguments.getFloat(1);
CStackArray outArguments;
outArguments.pushFloat(a + b);
outArguments.buildOntoStack(stack);
}
else
simSetLastError(nullptr, "Not enough arguments or wrong arguments.");
}
项目总览
simKINOVA.h 全部内容:
#pragma once
#include <simLib/simTypes.h>
#include <simLib/simExp.h>
SIM_DLLEXPORT int simInit(SSimInit*);
SIM_DLLEXPORT void simCleanup();
SIM_DLLEXPORT void simMsg(SSimMsg*);
SIM_DLLEXPORT void simInit_ui();
SIM_DLLEXPORT void simMsg_ui(SSimMsg_ui*);
SIM_DLLEXPORT void simCleanup_ui();
simKINOVA.cpp 全部内容:
#include "simkinova.h"
#include <simStack/stackArray.h>
#include <simStack/stackMap.h>
#include <simLib/simLib.h>
#include <iostream>
#define PLUGIN_VERSION 6 // 2 since version 3.2.1, 3 since V3.3.1, 4 since V3.4.0, 5 since V3.4.1, 6 since V4.6
static LIBRARY simLib; // the CoppelisSim library that we will dynamically load and bind
char pluginName[100];
// --------------------------------------------------------------------------------------
// simSkeleton.getData: an example of custom Lua command
// --------------------------------------------------------------------------------------
void LUA_GETSUM_CALLBACK(SScriptCallBack* p)
{ // the callback function of the new Lua command ("simSkeleton.getData")
int stack = p->stackID;
CStackArray inArguments;
inArguments.buildFromStack(stack);
if ((inArguments.getSize() >= 2) && inArguments.isNumber(0) && inArguments.isNumber(1))
{ // we expect at least 2 arguments: a string and a map
//CStackMap* map = inArguments.getMap(1);
//std::string tmp("we received a string (");
//tmp += inArguments.getString(0).c_str();
//tmp += ") and following message in the map: ";
//tmp += map->getString("message").c_str();
//simAddLog("PluginSkeleton", sim_verbosity_msgs, tmp.c_str());
float a = inArguments.getFloat(0);
float b = inArguments.getFloat(1);
CStackArray outArguments;
outArguments.pushFloat(a + b);
outArguments.buildOntoStack(stack);
}
else
simSetLastError(nullptr, "Not enough arguments or wrong arguments.");
// Now return a string and a map:
/* CStackArray outArguments;
outArguments.pushString("Hello World");
CStackMap* map = new CStackMap();
map->setBool("operational", true);
CStackArray* pos = new CStackArray();
double _pos[3] = { 0.0,1.0,2.0 };
pos->setDoubleArray(_pos, 3);
map->setArray("position", pos);
outArguments.pushMap(map);
outArguments.buildOntoStack(stack);*/
}
// --------------------------------------------------------------------------------------
// This is the plugin start routine (called just once, just after the plugin was loaded):
SIM_DLLEXPORT int simInit(SSimInit* info)
{
// Dynamically load and bind CoppelisSim functions:
simLib = loadSimLibrary(info->coppeliaSimLibPath);
if (simLib == NULL)
{
simAddLog(info->pluginName, sim_verbosity_errors, "could not find or correctly load the CoppeliaSim library. Cannot start the plugin.");
return(0); // Means error, CoppelisSim will unload this plugin
}
if (getSimProcAddresses(simLib) == 0)
{
simAddLog(info->pluginName, sim_verbosity_errors, "could not find all required functions in the CoppeliaSim library. Cannot start the plugin.");
unloadSimLibrary(simLib);
return(0); // Means error, CoppelisSim will unload this plugin
}
// Check the version of CoppelisSim:
int simVer, simRev;
simGetInt32Param(sim_intparam_program_version, &simVer);
simGetInt32Param(sim_intparam_program_revision, &simRev);
sprintf(pluginName, "simKINOVA-1");
if ((simVer < 40000) || ((simVer == 40000) && (simRev < 1)))
{
simAddLog(pluginName, sim_verbosity_errors, "sorry, your CoppelisSim copy is somewhat old, CoppelisSim 4.0.0 rev1 or higher is required. Cannot start the plugin.");
unloadSimLibrary(simLib);
return(0); // Means error, CoppelisSim will unload this plugin
}
// Register the new function:
simRegisterScriptCallbackFunction("getSum", nullptr, LUA_GETSUM_CALLBACK);
return(PLUGIN_VERSION); // initialization went fine, we return the version number of this plugin (can be queried with simGetModuleName)
}
// This is the plugin end routine (called just once, when CoppelisSim is ending, i.e. releasing this plugin):
SIM_DLLEXPORT void simCleanup()
{
// Here you could handle various clean-up tasks
unloadSimLibrary(simLib); // release the library
}
// This is the plugin messaging routine (i.e. CoppelisSim calls this function very often, with various messages):
SIM_DLLEXPORT void simMsg(SSimMsg* info)
{ // This is called quite often. Just watch out for messages/events you want to handle
// Here we can intercept many messages from CoppelisSim. Only the most important messages are listed here.
// For a complete list of messages that you can intercept/react with, search for "sim_message_eventcallback"-type constants
// in the CoppelisSim user manual.
if (info->msgId == sim_message_eventcallback_instancepass)
{ // This message is sent each time the scene was rendered (well, shortly after) (very often)
printf("start");
}
if (info->msgId == sim_message_eventcallback_simulationabouttostart)
{ // Simulation is about to start
printf("start");
}
if (info->msgId == sim_message_eventcallback_simulationended)
{ // Simulation just ended
printf("start");
}
if (info->msgId == sim_message_eventcallback_instanceswitch)
{ // We switched to a different scene. Such a switch can only happen while simulation is not running
printf("start");
}
// You can add many more messages to handle here
}
SIM_DLLEXPORT void simInit_ui()
{
}
SIM_DLLEXPORT void simCleanup_ui()
{
}
SIM_DLLEXPORT void simMsg_ui(SSimMsg_ui*)
{
}
项目配置中,需要在C/C+±>预处理器->定义 中添加两项
WIN_SIM
以及 _CRT_SECURE_NO_WARNINGS
生成解决方案后,在x64/release文件夹中有simKINOVA.dll 和 simKINOVA.lib 都拷贝到 coppeliasim 主目录中。
5. coppeliasim软件启动时的 lua预处理文件
在coppeliasim/lua 文件夹中新建两个lua文件,用文本编辑器修改内容
simKINOVA.lua
local simKINOVA = loadPlugin 'simKINOVA';
return simKINOVA
simKINOVA-ce.lua
local codeEditorInfos = [[
float res = simKINOVA.getSum(float a, float b)
]]
registerCodeEditorInfos("simKINOVA", codeEditorInfos)
在-ce.lua文件中,需要写明在coppeliasim脚本中调用api函数的输入输出格式,与simKINOVA.cpp中的 CALLBACKFUNCTION 内容对应
6. 打开coppeliasim软件并调用plugin
新场景中添加non-threaded lua脚本,调用simKINOVA plugin 中的getSum 函数完成计算
function sysCall_init()
sim = require('sim')
simKINOVA= require('simKINOVA')
a = 0
b = 100
-- do some initialization here
end
function sysCall_actuation()
-- put your actuation code here
a = a + 1
b = b - 1/3
c = simKINOVA.getSum(a, b)
print(c)
end
function sysCall_sensing()
-- put your sensing code here
end
function sysCall_cleanup()
-- do some clean-up here
end
-- See the user manual or the available code snippets for additional callback functions and details
7. 封装kinova状态读取函数到callback中
初始化函数
void LUA_INITKINOVA_CALLBACK(SScriptCallBack* p)
{ // the callback function of the new Lua command ("simSkeleton.getData")
int stack = p->stackID;
CStackArray outArguments;
CStackArray outs;
//We load the API.
commandLayer_handle = LoadLibrary(L"CommandLayerWindows.dll");
//We load the functions from the library
MyInitAPI = (int(*)()) GetProcAddress(commandLayer_handle, "InitAPI");
MyCloseAPI = (int(*)()) GetProcAddress(commandLayer_handle, "CloseAPI");
MyMoveHome = (int(*)()) GetProcAddress(commandLayer_handle, "MoveHome");
MyInitFingers = (int(*)()) GetProcAddress(commandLayer_handle, "InitFingers");
MyGetDevices = (int(*)(KinovaDevice devices[MAX_KINOVA_DEVICE], int& result)) GetProcAddress(commandLayer_handle, "GetDevices");
MySetActiveDevice = (int(*)(KinovaDevice devices)) GetProcAddress(commandLayer_handle, "SetActiveDevice");
MySendBasicTrajectory = (int(*)(TrajectoryPoint)) GetProcAddress(commandLayer_handle, "SendBasicTrajectory");
MyGetCartesianCommand = (int(*)(CartesianPosition&)) GetProcAddress(commandLayer_handle, "GetCartesianCommand");
MyGetAngularCommand = (int(*)(AngularPosition&)) GetProcAddress(commandLayer_handle, "GetAngularCommand");
int result = (*MyInitAPI)();
//cout << "Initialization's result :" << result << endl;
outs.pushFloat(1);
if (result == 1)
{
KinovaDevice list[MAX_KINOVA_DEVICE];
int devicesCount = MyGetDevices(list, result);
outs.pushFloat(devicesCount);
//cout << "Found a robot on the USB bus (" << list[0].SerialNumber << ")" << endl;
//Setting the current device as the active device.
int res = MySetActiveDevice(list[0]);
outs.pushFloat(res);
//cout << "Send the robot to HOME position" << endl;
//res = MyMoveHome();
//outs.pushFloat(res);
//cout << "Initializing the fingers" << endl;
res = MyInitFingers();
outs.pushFloat(res);
}
else
{
outs.pushFloat(-1);
}
outArguments.pushArray(&outs);
outArguments.buildOntoStack(stack);
}
读取关节角函数
void LUA_GETKINOVAPOS_CALLBACK(SScriptCallBack* p)
{ // the callback function of the new Lua command ("simSkeleton.getData")
int stack = p->stackID;
CStackArray outArguments;
AngularPosition currentjoints;
int result = MyGetAngularCommand(currentjoints);
//cout << "Initialization's result :" << result << endl;
CStackArray rjp;
if (result == 1)
{
rjp.pushFloat(currentjoints.Actuators.Actuator1);
rjp.pushFloat(currentjoints.Actuators.Actuator2);
rjp.pushFloat(currentjoints.Actuators.Actuator3);
rjp.pushFloat(currentjoints.Actuators.Actuator4);
rjp.pushFloat(currentjoints.Actuators.Actuator5);
rjp.pushFloat(currentjoints.Actuators.Actuator6);
outArguments.pushArray(&rjp);
}
else
{
rjp.pushFloat(-1);
outArguments.pushArray(&rjp);
}
outArguments.buildOntoStack(stack);
}
设置关节速度函数
void LUA_SETKINOVAVEL_CALLBACK(SScriptCallBack* p)
{ // the callback function of the new Lua command ("simSkeleton.getData")
int stack = p->stackID;
CStackArray inArguments;
inArguments.buildFromStack(stack);
CStackArray outArguments;
float jps[6];
CStackArray *jp;
jp = inArguments.getArray(0);
for (int i = 0; i < 6; ++i)
{
jps[i] = jp->getFloat(i);
}
AngularPosition currentjoints;
MyGetAngularCommand(currentjoints);
TrajectoryPoint pointToSend;
pointToSend.InitStruct();
pointToSend.Position.Type = ANGULAR_VELOCITY;// ANGULAR_POSITION;
pointToSend.Position.HandMode = VELOCITY_MODE;// ANGULAR_POSITION;
double vel = 30;
if (jps[0] > currentjoints.Actuators.Actuator1 + 1) pointToSend.Position.Actuators.Actuator1 = vel;// +(jps[0] - pointToSend.Position.Actuators.Actuator1);
else if (jps[0] < currentjoints.Actuators.Actuator1 - 1) pointToSend.Position.Actuators.Actuator1 = -vel;// + (jps[0] - pointToSend.Position.Actuators.Actuator1);
else pointToSend.Position.Actuators.Actuator1 = 0;
if (jps[1] > currentjoints.Actuators.Actuator2 + 1) pointToSend.Position.Actuators.Actuator2 = vel;// + (jps[1] - pointToSend.Position.Actuators.Actuator2);
else if (jps[1] < currentjoints.Actuators.Actuator2 - 1) pointToSend.Position.Actuators.Actuator2 = -vel;// + (jps[1] - pointToSend.Position.Actuators.Actuator2);
else pointToSend.Position.Actuators.Actuator2 = 0;
if (jps[2] > currentjoints.Actuators.Actuator3 + 1) pointToSend.Position.Actuators.Actuator3 = vel;// + (jps[2] - pointToSend.Position.Actuators.Actuator3);
else if (jps[2] < currentjoints.Actuators.Actuator3 - 1) pointToSend.Position.Actuators.Actuator3 = -vel;// + (jps[2] - pointToSend.Position.Actuators.Actuator3);
else pointToSend.Position.Actuators.Actuator3 = 0;
if (jps[3] > currentjoints.Actuators.Actuator4 + 1) pointToSend.Position.Actuators.Actuator4 = vel;// + (jps[3] - pointToSend.Position.Actuators.Actuator4);
else if (jps[3] < currentjoints.Actuators.Actuator4 - 1) pointToSend.Position.Actuators.Actuator4 = -vel;// + (jps[3] - pointToSend.Position.Actuators.Actuator4);
else pointToSend.Position.Actuators.Actuator4 = 0;
if (jps[4] > currentjoints.Actuators.Actuator5 + 1) pointToSend.Position.Actuators.Actuator5 = vel;// + (jps[4] - pointToSend.Position.Actuators.Actuator5);
else if (jps[4] < currentjoints.Actuators.Actuator5 - 1) pointToSend.Position.Actuators.Actuator5 = -vel;// + (jps[4] - pointToSend.Position.Actuators.Actuator5);
else pointToSend.Position.Actuators.Actuator5 = 0;
if (jps[5] > currentjoints.Actuators.Actuator6 + 1) pointToSend.Position.Actuators.Actuator6 = vel;// + (jps[5] - pointToSend.Position.Actuators.Actuator6);
else if (jps[5] < currentjoints.Actuators.Actuator6 - 1) pointToSend.Position.Actuators.Actuator6 = -vel;// + (jps[5] - pointToSend.Position.Actuators.Actuator6);
else pointToSend.Position.Actuators.Actuator6 = 0;
pointToSend.Position.Fingers.Finger1 = 0;
pointToSend.Position.Fingers.Finger2 = 0;
pointToSend.Position.Fingers.Finger3 = 0;
int result = MySendBasicTrajectory(pointToSend);
if (result == 1)
{
outArguments.pushFloat(1);
}
else
{
outArguments.pushFloat(0);
}
outArguments.buildOntoStack(stack);
}
注册上述函数在siminit中
// Register the new function:
simRegisterScriptCallbackFunction("getSum", nullptr, LUA_GETSUM_CALLBACK);
simRegisterScriptCallbackFunction("initKinova", nullptr, LUA_INITKINOVA_CALLBACK);
simRegisterScriptCallbackFunction("getKinovaPos", nullptr, LUA_GETKINOVAPOS_CALLBACK);
simRegisterScriptCallbackFunction("setKinovaVel", nullptr, LUA_SETKINOVAVEL_CALLBACK);
生成dll库
8. coppeliasim调用plugin驱动kinova运动
新建non-thread 脚本,修改actuation(),求解逆运动学后读取仿真中的关节角度,发送到实际机器人端,采用速度模式进行运动
function sysCall_actuation()
count = count + 1
if count%2 == 0 then
simIK.handleGroup(ikEnv,ikGroup_damped,{syncWorlds=true,allowError=true})
end
cp = simKINOVA.getKinovaPos()
tp[1] = sim.getJointPosition( j1)*180/math.pi
tp[2] = sim.getJointPosition( j2)*180/math.pi
tp[3] = sim.getJointPosition(j3)*180/math.pi
tp[4] = sim.getJointPosition( j4)*180/math.pi
tp[5] = sim.getJointPosition( j5)*180/math.pi
tp[6] = sim.getJointPosition( j6)*180/math.pi
if count % 100 == 0 then
print("************")
print(cp)
print(tp)
end
simKINOVA.setKinovaVel(tp)
end