\chapter{知识模型设计} \section{基本模型} 在建立数字孪生虚拟世界过程中,会有大大小小的规则、算法、逻辑等知识纳入其中,任何规则算法归纳来讲都是描述任意虚拟或实体对象相关作用关系,这些作用关系对于该系统内部世界来说就是一个个具体的知识。本文归纳了这些知识的共同特征,建立了一个一般性的知识模型,该模型是一个动态模型,其规则求解过程可根据特点纳入平台内运算或使用独立进程进行运算求解。 如图\ref{fig-f12}所示,基本模型需要先建立三个外部特征,触发域、输入域、输出域。触发域主要用于设定触发求解过程的条件。输入域是指定该模型需要哪些虚拟/实体对象的什么参数。输出域是指定该模型输入影响范围和参数。 对于不同的知识或者说规则,根据其作用域和功能特点,将其分为现实类知识、虚拟类知识、仿真类知识 \begin{figure}[h!] \centering \includegraphics[width=1\textwidth]{figure/f12.png} \caption{知识模型分类} \label{fig-f12} \end{figure} \begin{description} \item[现实类知识]该类知识是具体场景下最底层知识,其触发域和输入域仅能为实体对象数据,虚拟场景运行与否不影响该类知识触发和生效作用。 \item[仿真类知识]该类知识为虚拟环境中与实体对象对应的虚拟对象的数据的关联规则,其输出域可以设定于虚拟世界,也可作用于现实世界。 \item[虚拟类知识]该类知识其输出域仅能作用于虚拟世界。 \end{description} 如图\ref{fig-f9}所示,基本模型内部求解过程可以根据类型和功能特点选择提交公式或算法代码,或直接运行独立的求解程序。 本章后面以构建碰撞检测和刚体运动力学规则为例讲解代码和公式确定知识模型。 \begin{figure}[h!] \centering \includegraphics[width=0.8\textwidth]{figure/f9.png} \caption{知识模型上传过程} \label{fig-f9} \end{figure} \section{程序接入机制} 如图\ref{fig-f9}所示,用户仅需要申请访问秘钥,调用本平台开发的依赖库即刻纳入到通信网络中作为一个计算节点执行计算任务,与内部的计算节点无任何差别,唯一要考虑的是跨地区网络后消息实时性无法满足要求,不适合去做实时性分析人物。目前支持的依赖库python/go/c/rust。 \section{代码解析机制} 如图8流程所示,代码在用户上传完毕后会根据需要在平台上生成运行环境,并根据需要提供一定的运算资源和数据背景,提供更高阶的数据查询和接入方法。目前代码解析部分采取现成的语法,根据语言特性和场景需要,目前仅支持js和python,尽管采用了一定的沙盒机制去运行,但是仍然面临很大的安全问题,所以现在的代码机制仅供内部使用,后续根据需要开放出自定义代码或者代码检查。 \section{公式解析算法} 在构建规则模型其中很重要的一步是支持公式输入,对于一条规则,我们可以提供节点数据并转换成合适的格式,比如对于某一理想形状体,我给予100N的力10s,如何通过规则模型解析公式获得该物体10s内的运动状态,首先我们需要去解析公式,确定输入变量数量和格式,在确定输出变量数量和格式,根据公式以一定的频率去输出结果。 在公式撰写规则上直接采用先用的latex语法去语义化编写公式,采用AST算法去解析公式结构. 以该公式为例: \[ y = 1 * 2 + (10 - 2) * x \] 将右侧表达式转换为RPN表达式 \[ 1\ 2 * 10\ 2 - x * + \] 根据RPN表达式构建出的AST语法树为 \begin{center} \begin{tikzpicture} [thick,scale=1, every node/.style={scale=1}] \node {+} child {node {*} child {node {1}} child [missing] {} child {node {2}} } child [missing] {} child [missing] {} child [missing] {} child { node {*} child {node {-} child {node {10}} child {node {2}} } child [missing] {} child {node {x}} }; \end{tikzpicture} \end{center} 以AST树构建公式算法,输入目前支持浮点数及矩阵格式,支持常用函数表达,整体具体公式需要去做输入输出数据格式转换。 \section{代码规则案例-碰撞检测} 碰撞检测目前定义为虚拟类规则,暂时其输出域数据无法作用于现实世界,主要用于虚拟世界用户交互检测。 为了简化物体之间的碰撞检测运算,通常会对物体创建一个规则的几何外形将其包围。在本系统中,碰撞检测中将物体分为三种检测模型,点、AABB、球体。其中,AABB(axis-aligned bounding box)包围盒被称为轴对齐包围盒。 轴对齐包围盒是判断两个物体是否重叠的最快算法,物体被包裹在一个非旋转的(因此轴对齐的)盒中,并检查这些盒在三维坐标空间中的位置,以确定它们是否重叠。 由于性能原因,轴对齐是有一些约束的。两个非旋转的盒子之间是否重叠可以通过逻辑比较进行检查,而旋转的盒子则需要三角运算,这会导致性能下降。如果你有旋转的物体,可以通过修改边框的尺寸,这样盒子仍可以包裹物体,或者选择使用另一种边界几何类型,比如球体 (球体旋转,形状不会变)。 在该例中,根据不同需要适合使用代码片段去定义该规则。 \subsection{点与AABB} 如果检测到一个点是否在 AABB 内部就非常简单了 — 我们只需要检查这个点的坐标是否在 AABB 内; 分别考虑到每种坐标轴。如果假设 $P_x$, $P_y$ 和 $P_z$ 是点的坐标, $B_{minX} - B_{maxX}$, $B_{minY} - B_{maxY}$, 和$B_{minZ}–B_{maxZ}$ 是 AABB 的每一个坐标轴的范围,我们可以使用以下代码计算两者之间的碰撞是否发生: \begin{lstlisting}[ language={}, label={code-js-sample-1}, ] function isPointInsideAABB(point, box) { return (point.x >= box.minX && point.x <= box.maxX) && (point.y >= box.minY && point.y <= box.maxY) && (point.z >= box.minY && point.z <= box.maxZ); } \end{lstlisting} \subsection{AABB与AABB} 检查一个 AABB 是否和另一个 AABB 相交类似于检测两个点一样。我们只需要基于每一条坐标轴并利用盒子的边缘去检测。 \begin{lstlisting}[ language={}, label={code-js-sample}, ] function intersect(a, b) { return (a.minX <= b.maxX && a.maxX >= b.minX) && (a.minY <= b.maxY && a.maxY >= b.minY) && (a.minZ <= b.maxZ && a.maxZ >= b.minZ); } \end{lstlisting} \section{公式规则案例-刚体运动力学模型} 在虚拟世界中为关联现实设备运动状态,需要根据已有的传感器数据,如加速度、里程信息等估计实体对象运动姿态,或者由虚拟对象指导影响实体对象运动,两者相互作用皆需要实现基本的刚体运动。刚体运动的典型计算特征可用于验证公式规则模型,其评估效率和验证性能直观有效。 刚体的运动主要基于牛顿三大定律来模拟: \begin{itemize} \item 1.惯性 物体在不受力时,总是保持速度不变. \item 2.力,质量,加速度力在物体上产生加速度,满足$F = ma$. \item 3.力的作用是相互的 \end{itemize} 基于牛顿三大定律,在计算机中来模拟刚体的运动流程如此: 对于每个物体,使用循环的方式来模拟: % 流程图定义基本形状 \tikzstyle{startstop} = [rectangle, rounded corners, minimum width = 2cm, minimum height=1cm,text centered, draw = black] \tikzstyle{io} = [trapezium, trapezium left angle=70, trapezium right angle=110, minimum width=2cm, minimum height=1cm, text centered, draw=black] \tikzstyle{process} = [rectangle, minimum width=3cm, minimum height=1cm, text centered, draw=black] \tikzstyle{decision} = [diamond, aspect = 3, text centered, draw=black] % 箭头形式 \tikzstyle{arrow} = [->,>=stealth] \begin{center} \begin{tikzpicture}[node distance=0.5cm] %定义流程图具体形状 \node[startstop](start){Start}; \node[io, below of = start, yshift = -1cm](in1){分析受力}; \node[process, below of = in1, yshift = -1cm](pro1){更新速度和位置}; \node[process, below of = pro1, yshift = -1cm](pro2){碰撞检测}; \node[io, below of = pro2, yshift = -1cm](out1){解决约束}; \node[decision, below of = out1, yshift = -1cm](dec1){到达稳态}; \node[startstop, below of = dec1, yshift = -1cm](stop){显示结果}; \coordinate (point1) at (-3cm, -6cm); %连接具体形状 \draw [arrow] (start) -- (in1); \draw [arrow] (in1) -- (pro1); \draw [arrow] (pro1) -- (pro2); \draw [arrow] (pro1) -- (out1); \draw [arrow] (out1) -- (dec1); \draw (dec1) -- node [above] {N} (point1); \draw [arrow] (point1) |- (in1); \draw [arrow] (dec1) -- node [right] {Y} (stop); \end{tikzpicture} \end{center} 最后将所有运动分解为粒子运动和旋转运动,每秒以一定频率分别计算其空间位置和空间姿态,最终合成为完整运动分析过程。 \subsection{空间位置} 先不考虑物体的形状和旋转,把物体当成理想粒子来对待,根据牛顿定律来循环计算物体的速度和位置: \[ dt = t_{i+1} - t_{i} \] \[ v(t_{i+1}) = v(t_{i}) + (\frac{f(t_{i})}{m})dt \] \[ p(t_{i+1}) = v(t_{i}) + v(t_{i+1})dt \] 将其泰勒展开 \[ p(t_{i+1}) = p(t_{i}) + p^{'}(t_{i})dt + p^{''}(t_{i})\frac{dt^{2}}{2!} + p^{'''}(t_{i})\frac{dt^{3}}{3!} + ... \] 对于浏览器按像素点实时位置渲染精度而言,3阶泰勒展开已符合精度要求。 因此对于理想点的运动规则模拟可以提交公式 \begin{lstlisting}[ language={}, label={code}, ] p(t_{i+1}) = p(t_{i}) + p^{'}(t_{i})dt + p^{''}(t_{i})\frac{dt^{2}}{2!} + p^{'''}(t_{i})\frac{dt^{3}}{3!} \end{lstlisting} \subsection{空间姿态} 要计算物体的空间姿态,即物体绕某一定点在三自由度上的旋转量,可以用四元数q来表示刚体在极短时间内的旋转量, 惯性张量 在三维空间中任取一点$Q$ 及一个直角坐标系$Q_{xyz}$,可以得到物体的惯性张量: \begin{equation} I ={ \left[ \begin{array}{ccc} I_{xx} & I_{xy} & I_{xz}\\ I_{yz} & I_{yy} & I_{yz}\\ I_{zx} & I_{zy} & I_{zz} \end{array} \right ]} \end{equation} 对角元素$I_{xx}$,$I_{yy}$,$I_{yy}$是物体分别相对于$x$,$y$,$z$轴的转动惯量。 \[ I_{xx} = \int{(r_{y}^{2}+r_z^2)dm} \] \[ I_{yy} = \int{(r_{x}^{2}+r_z^2)dm} \] \[ I_{zz} = \int{(r_{x}^{2}+r_y^2)dm} \] 计算惯量积 \[ I_{xy} = I_{yx} = - \int{(r_x r_y)dm} \] \[ I_{xz} = I_{zx} = - \int{(r_x r_z)dm} \] \[ I_{yz} = I_{zy} = - \int{(r_z r_y)dm} \] 根据惯性张量计算力矩$\tau$,角将速度$d\omega$,角速度$\omega$。 \[ \tau = \frac{dL}{dt} = I \frac{d\omega}{dt} \] \[ d\omega = I^{-1} \tau dt \] \[ \omega(t_{i+1}) = \omega(t_i) + (I^{-1} \tau(t_i)) dt \] 用四元数$q$来表示刚体的旋转状态,刚体在$dt$时间内沿着$\omega$旋转了$|\omega dt|$的角度,得到 \begin{equation} q(t_{i+1}) = q(t_i) * [\cos{\frac{|\omega dt|}{2}},\sin{\frac{|\omega dt|}{2}}\frac{\omega}{|\omega|}] \end{equation} 对于获取刚体旋转状态的规则特征可以提交为 \begin{lstlisting}[ language={}, label={code}, ] q(t_{i+1}) = q(t_i) * [\cos{\frac{|\omega dt|}{2}},\sin{\frac{|\omega dt|}{2}}\frac{\omega}{|\omega|}] } \end{lstlisting}