windows下py调用vs2019生成dll库

之前使用py调用了matlab的 .m 文件(上一篇博客),能够实现使用matlab进行计算的效果,但实际使用下来,计算的速度还是稍微慢了一些。同样的一个函数执行100次,使用py调用matlab的方法,大概需要6s左右;使用py调用dll库的方法,只需要0.2s。。

生成dll库,我使用的方法是,先将matlab代码转成c代码,再利用vs将c代码生成dll库。

软件环境:win10(64位),py3.6,vs2019, matlab2020b

一.matlab部分
1.首先先写好需要转化的matlab代码,写成函数形式。
注意,函数名要和文件名一致,并且函数要在开头定义。

function [out_x, out_y, out_z] = matlabToC(in_Bx, in_By, in_Bz)

	% 需要实现功能的代码...

    out_x = x_gbn(1);
    out_y = x_gbn(2);
    out_z = x_gbn(3);
end

我定义的是一个3输入3输出的函数。

2.使用matlab自带的工具箱进行代码生成。
在这里插入图片描述
选择编写好的 .m文件
在这里插入图片描述
确定函数的参数数据类型,输入一个调用函数的示例即可。
matlab自动识别后,还可以进行手动调整。我使用的函数三个参数都是double类型的。
在这里插入图片描述
这里可以让matlab验证一下你的函数输入输出,生成一些数据测试,可以跳过。
在这里插入图片描述
选择c或c++,我选择的是c++,然后生成即可。
在这里插入图片描述
在 .m文件所在的文件夹,会生成一个 codegen 文件夹,生成的代码就在里面
在这里插入图片描述
在matlab生成的文件中,有一个文件夹是 examples ,里面有两个文件,分别是 main.h 和 main.cpp ,里面是自动生成的调用模板,可以参考。
至此,matlab部分完成。

二.vs2019部分。
使用vs2019利用matlab生成的c代码,生成dll库。
此部分可以参考博客:
https://blog.csdn.net/maosijunzi/article/details/79354806

首先新建一个项目
在这里插入图片描述
生成的界面为
在这里插入图片描述
接下来需要将matlab生成的所有 .cpp 和 .h 文件加入到工程中。
将第一部分生成的所有的 .cpp 和 .h 文件复制到vs创建的工程下,添加项即可。
得到的结果为
在这里插入图片描述
接下来修改 pch.h 和 pch.cpp 。这两个文件是新建工程时自动生成。
主要是添加外部调用的接口。

在参考的main函数中,自动生成的调用参考为:

 static void main_matlabToC()
{
  double in_Bx_tmp;
  double out_x;
  double out_y;
  double out_z;

  // Initialize function 'matlabToC' input arguments.
  in_Bx_tmp = argInit_real_T();

  // Call the entry-point 'matlabToC'.
  matlabToC(in_Bx_tmp, in_Bx_tmp, in_Bx_tmp, &out_x, &out_y, &out_z);
}

所以,我需要调用的函数是matlabToC,并且输入的参数分别是三个double类型,和三个double类型的引用。

因此在 pch.cpp 中,调用的接口可以写成

#include "matlabToC.h"

void matlab_Gauss(double in_Bx_tmp, double in_By_tmp, double in_Bz_tmp, double &out_x, double &out_y, double &out_z)
{
	matlabToC(in_Bx_tmp, in_By_tmp, in_Bz_tmp, &out_x, &out_y, &out_z);
}

而在 pch.h 中,需要声明为外部调用,在函数声明前加上
extern “C” _declspec(dllexport)

extern "C" _declspec(dllexport) void matlab_Gauss(double in_Bx_tmp, double in_By_tmp, double in_Bz_tmp, double& out_x, double& out_y, double& out_z);

这两个文件的内容如图
在这里插入图片描述
此时点击重新生成dll,即可生成dll库。
在这里插入图片描述
但有可能会出现报错。
在这里插入图片描述
缺少 tmwtypes.h 文件。这个文件是matlab自带的库文件,所以在生成过程时没有添加到c文件中。
可以将该文件的路径添加到编译器中,或者把文件复制过来。
此文件的路径为 XX\R2020b\extern\include
XX表示的是你的matlab的安装路径。

再次重新生成dll库时,可能还会提示另一个错误
在这里插入图片描述
大概是需要向每一个源文件中添加一句 #include “pch.h” 。。
注意,#include "pch.h"一定要放在第一句。

之后再生成dll库,就能够正常生成通过了。

三.py调用dll库
用py加载dll库,使用自定义的函数。加载dll库,我使用的是py的 ctypes 库。因为py和c的数据类型不相同,所以需要借用 ctypes 库对两者的数据类型进行转化。
参考资料:
https://docs.python.org/zh-cn/3/library/ctypes.html#type-conversions

在这里插入图片描述
在参考资料中,也有关于引用传递形参的介绍
在这里插入图片描述
需要注意的是,py是不需要定义数据类型就能够直接使用一个变量,而c中需要定义一个变量类型,分配内存,才能够使用变量。所以在py中,需要先利用 ctype 库定义变量,才能够在c函数中使用。而对于引用,则可以使用 byref 函数。

py的参考代码:

import ctypes
# 加载dll库
pDll = ctypes.cdll.LoadLibrary
matlabGuass = pDll("E:\\myDesktop\\protect\\vs2019\\testDll2\\x64\\Release\\testDll2.dll")

# 定义变量
in_Bx = ctypes.c_double(0.02)
in_By = ctypes.c_double(0.05)
in_Bz = ctypes.c_double(0.01)
out_x = ctypes.c_double()
out_y = ctypes.c_double()
out_z = ctypes.c_double()

# 使用dll库中的函数
matlabGuass.matlab_Gauss(in_Bx, in_By, in_Bz, ctypes.byref(out_x), ctypes.byref(out_y), ctypes.byref(out_z))

# 使用得到的结果
print(out_x.value, out_y.value, out_z.value)

-------------------------------------假装是分割线------------------------------------------
在之后,我需要输入两个6*3的矩阵作为输入,返回5个值。
使用matlab生成的c++函数中,两个矩阵的输入转化为了一维数组,所以在python中,需要利用ctype库把列表转化为数组。对于一个double数组,使用ctype库可以定义为

import ctypes
a = (ctypes.c_double * 18)(0.5, 2.1, 3.0)

如果将列表转化为数组的话,可以写成

world_sensor_position = np.array([[ 0.025, 0.05, -0.049], [ 0.025, 0.0, -0.049], [ 0.025, -0.05, -0.049],
                                   [-0.025, 0.05, -0.049], [-0.025, 0.0, -0.049], [-0.025, -0.05, -0.049]])

c_world_sensor_position = (ctypes.c_double * 18)(*(world_sensor_position.flatten()[i] for i in range(18)))

参考博客为:https://www.codenong.com/p12242009/
其中使用到了 * 表达式,具体使用方式参考:https://blog.csdn.net/DawnRanger/article/details/78028171

最终的参考代码为

import ctypes

pDll = ctypes.cdll.LoadLibrary
libadd = pDll("E:\\myDesktop\\protect\\vs2019\\Dll3\\x64\\Release\\Dll3.dll")

world_sensor_position = np.array([[ 0.025, 0.05, -0.049], [ 0.025, 0.0, -0.049], [ 0.025, -0.05, -0.049],
                                   [-0.025, 0.05, -0.049], [-0.025, 0.0, -0.049], [-0.025, -0.05, -0.049]])

magnet_sensor_position = np.array([[-0.23584, -0.23859, 0.60491], [-0.14756, 0.53043, 0.72895], [0.30435, 0.23026, -0.08304],
                                   [0.41739, -0.24664, 0.69865], [1.0436, 1.38045, -0.18543], [0.38696, -0.25923, -0.91707]])

# 注意: flatten()按行展平时, 与matlab里面定义的矩阵不相同, 展平方式需要用flatten("F")来按列展平, 原因我也不知道为啥....
c_world_sensor_position = (ctypes.c_double * 18)(*(world_sensor_position.flatten("F")[i] for i in range(18)))
c_magnet_sensor_position = (ctypes.c_double * 18)(*(magnet_sensor_position.flatten("F")[i] for i in range(18)))

out_alpha = ctypes.c_double()
out_theta = ctypes.c_double()
out_x = ctypes.c_double()
out_y = ctypes.c_double()
out_z = ctypes.c_double()

libadd.matlab_PSO_position(c_world_sensor_position, c_magnet_sensor_position,
                           ctypes.byref(out_alpha), ctypes.byref(out_theta),
                           ctypes.byref(out_x), ctypes.byref(out_y), ctypes.byref(out_z))

print([out_alpha.value, out_theta.value, out_x.value, out_y.value, out_z.value])
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值