Open Dynamics Engine(ODE)物理引擎教程(B)–图表形式动态显示仿真结果(C++调用python第三方库)
(在这个教程中将在C++调用python函数的基础上,实现ODE物体状态的图标动态绘制功能)
本系列教程是在上一个教程基础上进行的,所以请保证你是通过上一个教程过来的:
Open Dynamics Engine(ODE)物理引擎教程(3)–ODE仿真框架介绍与重力仿真
在使用ODE
物理引擎做仿真后,我们可以存储下来数据使用matlab
或者是Excel
分析。在这里我们想要实现仿真过程中的动态数据显示,可以通过多种图像显示库如Gnuplot
、matplotlib-cpp
等,下面是他们的主页链接:
Gnuplot
简介【是一种用于多种操作系统的命令行式画图软件】
matplotlib-cpp
简介【Extremely simple yet powerful header-only C++ plotting library built on the popular matplotlib 】
这里我们想要使用python(也是对自己熟悉语言的锻炼)进行动态的图像分析,很重要的原因是可视化效果好,以及函数包丰富,未来还可以实现机器学习等等,也是提前做好接口。所以Open Dynamics Engine(ODE)物理引擎教程(A,B,C…)等系列就围绕python的相关内容编写,让我们共同提升。本文首先介绍如何在C++中调用python函数或者说是脚本。
C++调用python函数的配置方法很多,但是有很多坑,这里提供我使用的一种方法,如有雷同,那就是我用的那种。关注Open Dynamics Engine(ODE)物理引擎教程系列的老朋友应该知道,我的环境是:
更多Python与C/C++的API定义你可以查阅官方文档:
Python/C API Reference Manual
首先到python官网下载相应版本:
https://www.python.org/downloads/release/python-382/
我下载的是Python 3.8.2 x64
的可执行文件。
下载完成之后点击安装即可,没有什么要求,但是要记住自己的安装目录,我的是:
C:\Users\User_Name\AppData\Local\Programs\Python\Python38 //注意User_Name是你自己的用户名,我这里只不过用这个替换了
还记得我们配置生成VS项目而建立的头文件和库函数的存放文件吗,这时候就排上用场了,不清楚的朋友可以查看这篇博文:
Open Dynamics Engine(ODE)物理引擎教程(2)–项目环境配置与“Hello ODEWorld”
首先我们需要将python安装目录下的头文件和库函数文件放到项目的相应引用文件中:
是不是很熟悉的操作,由于python的头文件里面有很多文档,我们可以在ExternLab/include
下建立python
文件夹,然后将上图include
文件夹中的所有内容拷贝到新建的python
目录下。
同样的将上图的libs
文件夹下的内容拷贝到ExternLab/lib
文件夹下。
将上图中的python38.dll
文件拷贝到VS项目生成可执行文件的地方,即ODEEnvironment\x64\Debug
文件夹中,和ODEEnvironment.exe
放在一起。
由于上一个教程中已经进行过相关属性的配置,所以这里直接给出截图。
在VC++目录中的包含目录中增加:
$(SolutionDir)\..\ExternLab\include\python
python38.lib
首先在网上找一个python脚本,感谢原作者Zaric_Chou的分享:
python画图之“小海龟”turtle
我使用的是画钟表的案例:
# coding=utf-8
import turtle
from datetime import *
# 抬起画笔,向前运动一段距离放下
def Skip(step):
turtle.penup()
turtle.forward(step)
turtle.pendown()
def mkHand(name, length):
# 注册Turtle形状,建立表针Turtle
turtle.reset()
Skip(-length * 0.1)
# 开始记录多边形的顶点。当前的乌龟位置是多边形的第一个顶点。
turtle.begin_poly()
turtle.forward(length * 1.1)
# 停止记录多边形的顶点。当前的乌龟位置是多边形的最后一个顶点。将与第一个顶点相连。
turtle.end_poly()
# 返回最后记录的多边形。
handForm = turtle.get_poly()
turtle.register_shape(name, handForm)
def Init():
global secHand, minHand, hurHand, printer
# 重置Turtle指向北
turtle.mode("logo")
# 建立三个表针Turtle并初始化
mkHand("secHand", 135)
mkHand("minHand", 125)
mkHand("hurHand", 90)
secHand = turtle.Turtle()
secHand.shape("secHand")
minHand = turtle.Turtle()
minHand.shape("minHand")
hurHand = turtle.Turtle()
hurHand.shape("hurHand")
for hand in secHand, minHand, hurHand:
hand.shapesize(1, 1, 3)
hand.speed(0)
# 建立输出文字Turtle
printer = turtle.Turtle()
# 隐藏画笔的turtle形状
printer.hideturtle()
printer.penup()
def SetupClock(radius):
# 建立表的外框
turtle.reset()
turtle.pensize(7)
for i in range(60):
Skip(radius)
if i % 5 == 0:
turtle.forward(20)
Skip(-radius - 20)
Skip(radius + 20)
if i == 0:
turtle.write(int(12), align="center", font=("Courier", 14, "bold"))
elif i == 30:
Skip(25)
turtle.write(int(i/5), align="center", font=("Courier", 14, "bold"))
Skip(-25)
elif (i == 25 or i == 35):
Skip(20)
turtle.write(int(i/5), align="center", font=("Courier", 14, "bold"))
Skip(-20)
else:
turtle.write(int(i/5), align="center", font=("Courier", 14, "bold"))
Skip(-radius - 20)
else:
turtle.dot(5)
Skip(-radius)
turtle.right(6)
def Week(t):
week = ["星期一", "星期二", "星期三",
"星期四", "星期五", "星期六", "星期日"]
return week[t.weekday()]
def Date(t):
y = t.year
m = t.month
d = t.day
return "%s %d%d" % (y, m, d)
def Tick():
# 绘制表针的动态显示
t = datetime.today()
second = t.second + t.microsecond * 0.000001
minute = t.minute + second / 60.0
hour = t.hour + minute / 60.0
secHand.setheading(6 * second)
minHand.setheading(6 * minute)
hurHand.setheading(30 * hour)
turtle.tracer(False)
printer.forward(65)
printer.write(Week(t), align="center",
font=("Courier", 14, "bold"))
printer.back(130)
printer.write(Date(t), align="center",
font=("Courier", 14, "bold"))
printer.home()
turtle.tracer(True)
# 100ms后继续调用tick
turtle.ontimer(Tick, 100)
def main():
# 打开/关闭龟动画,并为更新图纸设置延迟。
turtle.tracer(False)
Init()
SetupClock(160)
turtle.tracer(True)
Tick()
turtle.mainloop()
if __name__ == "__main__":
main()
将其保存为addTest.py
的脚本,并存放在ODEEnvironment\x64\Debug
文件夹中,和ODEEnvironment.exe
放在一起。
接下来的代码发生在EnvMain.cpp
中。
首先引用头文件
#include
主函数中的函数格式:
int main(int argc, char **argv) {
cout << "Hello World!" << endl;
Py_Initialize(); //这是python的初始化,第一次使用时一定要调用
if (!Py_IsInitialized())
{
printf("初始化失败!");
return 0;
}
PyObject* pModule = NULL; //先生成相应的对象
PyObject* pFunc = NULL;
pModule = PyImport_ImportModule("addTest"); //参数为Python脚本的文件名
if (pModule)
{
pFunc = PyObject_GetAttrString(pModule, "main"); //获取函数
PyEval_CallObject(pFunc, NULL); //执行函数 这里的NULL是因为python脚本运行不需要参数
//在后面我们将讲到如果有参数怎么办
}
else
{
printf("导入Python模块失败...\n");
}
ODEMain(argc, argv);
Py_Finalize();
return 0;
}
然后编译并运行,出现如下效果就说明你的python配置成功了:
Open Dynamics Engine(ODE)物理引擎教程(B)–图表形式动态显示仿真结果(C++调用python第三方库)
(在这个教程中将在C++调用python函数的基础上,实现ODE物体状态的图标动态绘制功能)