论文的标题是DiffCloth: Differentiable Cloth Simulation with Dry Frictional Contact。
在ubuntu操作系统上输入命令:
git clone --recursive https://github.com/omegaiota/DiffCloth.git
即可下载代码到当前目录。
然后输入以下命令:
cd DiffCloth
mkdir build
cd build
cmake ..
make
即可编译代码到DiffCloth/build中。
然后就可以去玩了,怎么玩呢?
运行程序之前需要设置一个临时的环境变量,输入命令:
export OMP_NUM_THREADS=1
也可以将其设置为其他数字,输入命令
echo $OMP_NUM_THREADS
即可显示该环境变量的值,该环境变量应该设置的是线程数。
设置好环境变量之后,切换到build目录下,输入命令
./DiffCloth -demo tshirt -mode visualize -exp tshirt-exampleopt/iter0/
可以看到一个t恤衫的动画。
还有hat, sock等,但是没有训练出来。如果要训练hat,可以输入命令:
./DiffCloth -demo hat -mode optimize -seed 1
-seed后面可以是任意值,训练其他的东西也同理。
在main函数中,会先查找在环境变量中设置的线程数,并赋值给局部变量n_threads。然后创建几个线程,OPENMP_ENABLED在程序中默认为true。接着跟据输入过的命令执行visualization或optimization。
先说visualization。如果执行的是这条命令:
./DiffCloth -demo tshirt -mode visualize -exp tshirt-exampleopt/iter0/
则会在DiffCloth/output/tshirt-exampleopt/iter0/中找到每一帧对应的obj模型文件,并播放成动画。
然后,第二大块是optimize部分,这一部分很复杂。这一部分主要在runBackwardTask(demo, true, std::atoi(randSeedStr));函数中,这个函数的定义是:
void runBackwardTask(int demoIdx, bool isRandom, int srandSeed)
参数demoIdx指的是这几个宏:
Demos::DEMO_WIND_TSHIRT
Demos::DEMO_WEAR_SOCK
Demos::DEMO_WEAR_HAT
Demos::DEMO_SPHERE_ROTATE
Demos::DEMO_DRESS_TWIRL
这是五个选项,分别表示运行t恤衫被风吹、穿袜子、戴帽子、旋转球,卷曲裙子这几个demo中的哪一个。
isRandom:是否使用随机数
srandSeed:随机数种子
这个函数中,首先设置场景初始化的一些参数,这些参数在initSceneProfile对象中。代码里用hatScene作为场景的初始化参数。initSceneProfile的类型是Simulation::SceneConfiguration结构体,该结构体的定义如下:
struct SceneConfiguration {
FabricConfiguration fabric; // in SimulationConstant::fabricArrs
Orientation orientation;
Vec3d upVector;
AttachmentConfigs attachmentPoints;
std::vector<std::pair<double, std::vector<int>>> customAttachmentVertexIdx; // outside loop: different sets; each element: startFrame x vIdx
TrajectoryConfigs trajectory;
PrimitiveConfiguration primitiveConfig;
WindConfig windConfig;
Vec3d camPos;
Vec3d camFocusPos;
Vec3d sockLegOrientation;
CameraFocusPointType camFocusPointType;
AABB sceneBbox;
double timeStep;
int stepNum;
double forwardConvergenceThresh;
double backwardConvergenceThresh;
std::string name;
};
其中包括织物的材质信息、相机位置、朝向、风的属性、AABB包围盒的信息、时间步的间隔和步数等等信息。代码中对这些信息的设定如下:
Simulation::SceneConfiguration OptimizationTaskConfigurations::hatScene = {
.fabric = agenthat579,
.orientation = Orientation::FRONT,
.attachmentPoints = AttachmentConfigs::CUSTOM_ARRAY,
.customAttachmentVertexIdx = {{0.0, {394, 32}}}, //{{0.0, {501}}},
.trajectory = TrajectoryConfigs::CORNERS_2_WEARHAT,
.primitiveConfig = PrimitiveConfiguration::PLANE_BUST_WEARHAT,
.windConfig = WindConfig::NO_WIND,
.camPos = Vec3d(-22.14, 9.24, 7.59),
.camFocusPointType = CameraFocusPointType::CLOTH_CENTER,
.sceneBbox = AABB(Vec3d(-5, -1.5, -14), Vec3d(7, 10, 5)),
.timeStep = 1.0 / 100.0,
.stepNum = 400,
.forwardConvergenceThresh = 1e-8,
.backwardConvergenceThresh = 5e-4,
.name = "demo_wearhat"
};
其中agenthat579内容如下:
Simulation::FabricConfiguration OptimizationTaskConfigurations::agenthat579 = {
.clothDimX = 6,
.clothDimY = 6,
.k_stiff_stretching = 1200, // old param: 300 // TODO: WARNING: change back to 1200
.k_stiff_bending = 120, // old param: 50 // TODO: WARNING: change back to 120
.gridNumX = 40, //
.gridNumY = 80, //
.density = 0.224, // old param: 0.324
.keepOriginalScalePoint = false,
.isModel = true,
.custominitPos = false,
.fabricIdx = FabricEnumArray::AGENT_HAT579,
.color = Vec3d(1, 0, 0),
.name = "remeshed/agenthat2-579-rotated.obj",
};
大概是布料的劲度系数、颜色、对应的obj文件路径等一些信息。clothDim、gridNum这些不太理解是干嘛的。
接下来是这一行:
Simulation *clothSystem = Simulation::createSystem(
initSceneProfile,
Vec3d(0, 0, 0), true);
创建了一个Simulation类的对象,并用一个指针指向该对象。Simulation是一个巨大无比的类,里面的成员声明在Simulation.h中占了约一千多行,看着很头疼,干脆不看了。
那么这一行应该是跟据前面初始化的场景对象initSceneProfile创建了一个Simulation对象,其指针叫做clothSystem,并给该对象设置好中心点坐标为Vec3d(0,0,0),并且确定要使用反向传播算法,也就是最后一个参数为true.
然后就用这个设定好的clothSystem开始训练:
BackwardTaskSolver::solveDemo(clothSystem, [&](const std::string &v) {}, demoIdx, isRandom,
srandSeed);
这个函数的形参是这样的:
void BackwardTaskSolver::solveDemo(Simulation *system, const std::function<void(const std::string &)> &setTextBoxCB,
int demoNum, bool isRandom, int srandSeed)
这里面用到一个优化器叫LBFGS,虽然不太懂,但是肯定和SGD差不多。
OptimizeHelper是什么东东?也是一个大类但是没有Simulation大,其声明有100多行。里面似乎是记录梯度信息、误差等一些东西。
设定好helper和优化器后,接下来就是下面这一行:
optimizeLBFGS(system,helper, system->sceneConfig.stepNum, demoNum, isRandom, srandSeed, setTextBoxCB);
如何在vscode上调试这个东西
在vscode左边可以看到项目的目录,其中vscode生成了一个.vscode文件夹,里面有launch.json和tasks.json。tasks.json可以设置如何编译这个项目,launch.json可以设置用什么命令去运行这个项目。编写tasks.json如下:
{
"tasks": [
{
"type": "cppbuild",
"label": "C/C++: gcc build active file",
"command": "cd build && cmake .. && make",
"args": [
],
"options": {
"cwd": "${workspaceFolder}"
},
"problemMatcher": [
"$gcc"
],
"group": {
"kind": "build",
"isDefault": true
},
"detail": "Task generated by Debugger."
}
],
"version": "2.0.0"
}
其中command命令是创建build目录并且编译的命令。
然后编写launch.json文件如下:
{
"version": "0.2.0",
"configurations": [
{
"name": "C/C++: gcc build and debug active file",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/build/DiffCloth",
"args": ["-demo", "hat", "-mode", "optimize", "-seed", "1"],
"stopAtEntry": false,
"cwd": "${workspaceFolder}/build/",
"environment": [{"name": "OMP_NUM_THREADS", "value": "8"}],
"externalConsole": false,
"MIMode": "gdb",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
},
{
"description": "Set Disassembly Flavor to Intel",
"text": "-gdb-set disassembly-flavor intel",
"ignoreFailures": true
}
],
"preLaunchTask": "C/C++: gcc build active file",
"miDebuggerPath": "/usr/bin/gdb"
}
]
}
这个文件设定了OMP_NUM_THREADS环境变量,和运行程序的命令。
然后在CMakeLists.txt中把MAKE_BUILD_TYPE改成Debug:
最后,打断点,在上方菜单的Run中点击start Debugging即可调试。