20241020 从零开始:windows系统 visual studio 2019封装 dll 动态链接库 给 Coppeliasim作为 plugin 调用

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 复制到新工程文件夹
需要修改 dllexportdllimport

#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

CoppeliaSim 的 plugin手册

参考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

在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值