In [35]:
%matplotlib inline
In [19]:
from pylayers.mobility.ban.body import *
from pylayers.mobility.trajectory import Trajectory
from IPython.display import Image
from matplotlib.pyplot import *
The body mobility is imported from motion capture files. This is the chosen manner to achieve a high degree of realism for the modeling of the human motion. Two kind of files exist :
c3d
files are a set of point which are evolving in timebvh
files are a stuctured version of the motion capture.Both type of file will be exploited in the following.
To ease electromagnetic simulation a simplification of the motion capture data structure is necessary. Generally there is a large number of captutred points, not all of them being useful for our modeling.
The body model is a restriction of key body segments which are transformed into $K$ cylinders of radius $r_k$.
The chosen body model is made of 11 cylinders. 4 cylinders decribing the two arms, 4 cylinders decribing the two legs, 2 cylinders describing the trunk and 1 cylinder for the head.
The body cylinder model is handle by a dedicated Python class call Body
To create a void body, simply instantiate a Body object from the class
In [20]:
John = Body()
which is equivalent to :
In [21]:
John = Body(_filebody='John.ini',_filemocap='07_01.c3d')
The default body filename is John.ini and the default motion capture filename is '07_01.c3d'. The creation of a Body consists in reading a _filebody
and a _filemocap
An example of a body file is given below. It is a file in ini
format with 4 sections.
[nodes]
This section associates a node number to a c3d fils conventional node number
NodeId = C3DNODE
[cylinder]
This section associates a cylinder Id to a dictionnary wich contains cylinder tail head and radius information
CylId = {'t',NodeId1,'h',NodeId2,'r',float (m),'name',}
[device]
This section associates a device name to a dictionnary wich contains cylinder device related information
DevId = {'typ' : {static|mobile}
'cyl': CylId
'l' : length coordinate in ccs,
'h' : height coordinate in ccs,
'a' : angle coordinate in ccs,
'file' : antenna file ,
'T' : Rotation matrix }
In [22]:
#%load /home/uguen/Bureau/P1/ini/Francois.ini
In [23]:
John
Out[23]:
In [24]:
Francois = Body(_filebody='Francois.ini',_filemocap='07_01.c3d')
Francois
Out[24]:
A .c3d
motion capture file is loaded with the method loadC3D
with as arguments the motion capture file and the number of frames to load.
The motion is represented as a sequence of framef stored in the d
variable member.
It is possible to get the information from the C3D header by using the verbose option of the read_c3d
function
In [25]:
# Video Frame Rate
Vrate = 120
# Inter Frame
Tframe = 1./120
# select a number of frame
nframes = 300
# Time duration of the whole selected frame sequence
Tfseq = Tframe*nframes
#
# load a .c3dmotion capture file
# this update the g.pos
#
#bc.loadC3D(filename='07_01.c3d',nframes=nframes,centered=True)
The duration of the capture is
In [26]:
print "Duration of the motion capture sequence", Tfseq," seconds"
d
is a MDA of shape (3,npoint,nframe)
. It contains all the possible configurations of the body. In general it is supposed to be a cyclic motion as an integer number of walking steps. This allows to instantiate the body configuration anywhere else in space in a given trajectory.
A specific space-time configuration of the body is called a topos
.
In [27]:
np.shape(John.d)
Out[27]:
In [28]:
traj = Trajectory()
t = traj.generate()
In [29]:
traj.head()
Out[29]:
In [30]:
f,a = traj.plot()
In [31]:
traj.__repr__()
Out[31]:
In [32]:
John.settopos(traj,t=5)
In [36]:
plt.figure(figsize=(15,20))
for t in np.arange(traj.tmin+0.4,traj.tmax,0.5):
John.settopos(traj,t=t)
f,a=John.show(color='b',plane='yz',topos=True)
axis('off')
In [37]:
John
Out[37]:
In [38]:
Francois.settopos(traj,t=6)
Francois
Out[38]:
The figure below shows the projection in a vertival plane of the body nodes.
In [ ]:
John.centered
In order to translate the motion in any point in space-time, a distinction is made between the real motion or topos and the centered motion capture which acts as a virtual motion.
Let $\mathbf{p}^k$ denotes the center of gravity of the body in the (O,x,y) plane
In [ ]:
John.center()
In [ ]:
a = np.hstack((John.vg,John.vg[:,-1][:,newaxis]))
$\mathbf{v}_g$ is the velocity vector of the gravity center of the body.
In [ ]:
print np.shape(John.pg)
print np.shape(John.vg)
In [ ]:
print John.vg[:,145]
print John.vg[:,298]
At that point the body structure is centered.
The frame is centered in the xy plane by substracting from the configuration of points the projection of the body in the xy plane.
In [ ]:
np.shape(John.d)
In [ ]:
John.npoints
Each frame is centered above the origin. For example for a walk motion the effect of the centering is just like if the body was still walking but not moving forward exactly in the same manner as a walk on a conveyor belt.
In [ ]:
pgc = np.sum(John.d[:,:,0],axis=1)/16
pg0 = John.pg[:,0]
print "True center of gravity", pg0
print "Center of gravity of the centered frame",pgc
In [ ]:
np.shape(John.pg)
The current file contains 300 frames
In [ ]:
tframe = arange(John.nframes)
In [ ]:
np.shape(John.pg[0:-1,:])
In [ ]:
xg = John.pg[0,:]
yg = John.pg[1,:]
zg = John.pg[2,:]
figure(figsize=(8,8))
subplot(311)
plot(tframe,xg)
title('x component')
ylabel('m')
subplot(312)
xlabel('frame index')
title('y component')
ylabel('m')
plot(tframe,yg)
subplot(313)
xlabel('frame index')
title('Motion capture centroid trajectory')
ylabel('m')
plot(xg,yg,'.b')
d = John.pg[0:-1,1:]-John.pg[0:-1,0:-1]
smocap = cumsum(sqrt(sum(d*d,axis=0)))
Vmocap = smocap[-1]/Tfseq
title('Length = '+str(smocap[-1])+' V = '+str(Vmocap*3.6)+' km/h')
axis('scaled')
axis('off')
plt.tight_layout()
In [ ]:
plot(smocap)
title('evolution of curvilinear abscisse from motion capture centroid trajectory')
xlabel('frame index')
ylabel('distance (meters)')
A large scale trajectory is defined in the $(O,x,y)$ plane.
traj
is a data structure (Npt,2)
In [ ]:
v = Vmocap
print v*3.6,"Kmph"
In [ ]:
# time in seconds
time = np.arange(0,10,0.01)
x = v*time
y = np.zeros(len(time))
z = np.zeros(len(time))
traj = Trajectory()
traj.generate(time,np.vstack((x,y,y)).T)
traj.tmax
In [ ]:
fig ,ax = traj.plot()
traj.head()
The posvel()
function (position and velocity) takes as arguments the following parameters
traj
a plane trajectory object.and returns
$t_f = \frac{T_{fs}}{Nf}$ is the interframe time or frame sampling period, it is equal to the whole duration of the motion sequence $T_{fs}$ divided by the number of frames
settopos
is a method which takes as argument :
traj
a plane trajectory (Npt,2)In futher version of the project, this function will be modified to be able to avoid passing the whole trajectory.
In [ ]:
John.settopos(traj=traj,t=3)
There is now a new data structure in the Body objet. This data structure is called a topos
.
In [ ]:
print np.shape(John.topos)
In [ ]:
John.topos
In [ ]:
John.show3()
In [ ]:
John.settopos(traj=traj,t=1)
fig,ax=John.plot3d(topos=True,col='#87CEEB')
John.settopos(traj=traj,t=2)
John.plot3d(topos=True,fig=fig,ax=ax,col='#7EC0EE')
John.settopos(traj=traj,t=3)
John.plot3d(topos=True,fig=fig,ax=ax,col='#6A5ACD')
John.settopos(traj=traj,t=4)
John.plot3d(topos=True,fig=fig,ax=ax,col='#7A67EE')
John.settopos(traj=traj,t=5)
John.plot3d(topos=True,fig=fig,ax=ax,col='#473C8B')
Each cylinder of the Body
model bears one specific coordinate system.
One or several cylinder coordinate systems can be chosen to define the Body Local Coordinates System (BLCS) which is required for motion capture (BLCS) applications.
In general, the origin will be chosen on a position which is the most time invariant as on the chest or the back.
Those frames of references are all defined in the Global Coordinate System (GCS) of the scene.
The method setccs()
is used to associate a Cylinder Coordinate System (CCS) to each cylinder of the bodyCylinder model. Notice that those cylinders coordinates systems are not known by the localization application. The localization application will define the BLCS from the position of radiating devices placed on the body surface.
Each basis is constructed with the function from geomutil.onbfromaxe()
: orthonormal bases from axes. This function takes 2 sets of $n$ points $\mathbf{p}_{A,n}$ and $\mathbf{p}_{B,n}$ as input and provides an orthonormal basis as output.
3 unitary vectors are constructed :
$$\hat{\mathbf{w}}_n = \frac{\mathbf{p}_B-\mathbf{p}_A}{| \mathbf{p}_B-\mathbf{p}_A |} $$$$\hat{\mathbf{u}}_n = \frac{\hat{\mathbf{v}}_g - (\hat{\mathbf{v}}_g.{\hat{\mathbf{w}}_n}) \mathbf{\hat{w}}_n}{|\hat{\mathbf{v}_g} - (\hat{\mathbf{v}_g}.{\hat{\mathbf{w}}_n}) \mathbf{\hat{w}}_n|} $$$$\hat{\mathbf{v}}_n = \mathbf{\hat{w}}_n \times \mathbf{\hat{u}}_n $$Where $\hat{\mathbf{v}}_g$ is the unit velocity vector along actual trajectory.
The outpout of geomutil.onbframe
is an MDA $(3\times n \times 3)$ of $n$ unitary matrices aggregated along axis 1 $$\mathbf{T}_n=[\hat{\mathbf{u}}_n, \hat{\mathbf{v}}_n, \hat{\mathbf{w}}_n]$$
To create the CCS :
In [ ]:
John.setccs()
In [ ]:
import scipy.linalg as la
print "ccs dimensions : ",np.shape(John.ccs)
print John.ccs[0,:,:]
print "Check determinant : ", la.det(John.ccs[0,:,:])
Create a Wireframe body representation from the body graph model
Representation of frames associated with the cylinder
In [ ]:
John.show3()
On the figure below the wireframe model is shown associated with the 11 CCS (Cylinder coordinates systems)
In [ ]:
Image('CCS.png')
A DCS is refered by 4 numbers $(Id,l,h,\alpha)$
In [ ]:
Id = 4 # 4 Left Arm
l = 0.1 # Longitudinal coordinates
h = 0.03 # height
alpha = 45 # angle degrees
In [ ]:
John.dcyl
Rotate Matrix around z
In [ ]:
John.settopos(traj=traj,t=6,cs=True)
In [ ]:
John.dcyl
In [ ]:
John.show3(topos=True,dcs=True)
In [ ]:
John.show3(topos=True,pattern=True)
In [ ]:
Image('acs.png')
In [40]:
from IPython.core.display import HTML
def css_styling():
styles = open("../doc/notebookstyles/custom.css", "r").read()
return HTML(styles)
css_styling()