Calculating velocity and acceleration from (timer reading, position) data using CAPM

I’m collecting some calculations I find useful for evaluating student data and for more advanced students to process their own data. CAPM says that we think acceleration is basically constant, gives us a mathematical model for how position and velocity depend on time, and allows us to make predictions. If we believe that CAPM holds, then we only need three points to calculate velocity and acceleration, one data point at the spacetime event in question and one on either side of the event in time.

Assumptions

  1. Acceleration is constant. Thus x(t)=\frac12 At^2+Bt+C and v(t)=At+B.
  2. The data point in question is (t_0,x_0), and the points on either side are (t_0-dt_-, x_0-dx_-) and (t_0+dt_+, x_0+dx_+).

General case

  • v(t_0)=\frac{\frac{dx_+}{dt_+}\cdot dt_- + \frac{dx_-}{dt_-}\cdot dt_+}{dt_-+dt_+}
  • a(t_0)=\frac{\frac{dx_+}{dt_+}-\frac{dx_-}{dt_-}}{\frac12(dt_-+dt_+)}

Breadcrumb method case (dt_-=dt_+=dt)

  • v(t_0)=\frac{dx_-+dx_+}{2dt}
  • a(t_0)=\frac{dx_+-dx_-}{dt^2}

Split time method case (dx_-=dx_+=dx)

  • v(t_0)=\frac{dx(dt_-^2+dt_+^2)}{dt_-dt_+(dt_-+dt_+)}
  • a(t_0)=\frac{2dx(dt_--dt_+)}{dt_- dt_+(dt_-+dt_+)} (Note: This needs some reinterpretation.)

Summary for use

With N data points, one can easily calculate N-2 velocities and accelerations at the same timer readings. Of course, one can calculate N-1 velocities at intermediate timer readings, but this can make it difficult for students to make spreadsheets for velocity. Using the method above, one can calculate both instantaneous velocity and instantaneous acceleration at the mesh of timer readings by focusing on three data points at a time. This makes it easier to test the validity of the Constant Acceleration Particle Model (CAPM).

I like to throw the formula into a spreadsheet that I use when looking at student data.  When they whiteboard in class, I type in their data to see how consistent it is, helping me to assess where students are going wrong and whether their graphs make any sense.

Advertisements

Stacked kinematic graphs with Asymptote

Update 2: Current versions of the code are now available at my GitHub repository.

Update: The code below is not as encapsulated as I would like, but it’s still pretty usable. The different versions reflect different amounts of refactoring, but I hope that they give you enough to work with. If you have a question about how to modify one of my examples to product your own favorite version of a stacked graph, please leave a comment, and I’ll try to create one within a reasonable time.

I’ve been using Asymptote to create graphs like the stacked kinematic graphs below.  I’ll post more complicated graphs in the future, but here’s a simple one with axes only.  I like Asymptote’s ability to use consistent units in the graphs so that the t-axis is synchronized.  Although it takes more work to create a version in Asymptote than in a graphics package, I can immediately differentiate it to different problems just by changing a line here and there.

Old version with x/v/at graphs: kinematic_stack.pdf kinematic_stack.png

New version (from refactored code) with s/v/at graphs: kinematic_stack_pos_vel_acc.pdf kinematic_stack_pos_vel_acc.png (N.B. IB Physics uses the convention that position is given by the variable s, so I try to use it with students, although we also use x).

// Asymptote code for kinematic_stack_pos_vel_acc.asy
import graph;

pen axis_p = linewidth(1.4)+black;
pen grid_p = linewidth(1.0)+gray(0.2);

real hticks = 5;
real vMin_ticks = -5;
real vMax_ticks = 5;

void kingraph(picture pic, Label vL="", real vMin=vMin_ticks, real vMax=vMax_ticks, Label hL="$t$", real hMin=0, real hMax=hticks) {
  scale(pic, Linear, Linear);
  xlimits(pic, hMin, hMax);
  ylimits(pic, vMin, vMax);
  xaxis(pic, hL, YZero, axis_p, Arrow(6));
  yaxis(pic, vL, XZero, axis_p, Arrow(6));
}

picture pos_pic;
kingraph(pos_pic, "$s$");

picture vel_pic;
kingraph(vel_pic, "$v$");

picture acc_pic;
kingraph(acc_pic, "$a$");

//xequals(pos_pic,3,Dotted);
//xequals(vel_pic,3,Dotted);
//xequals(acc_pic,3,Dotted);

// boring code for stacking the graphs.  The only interesting part is the htick/vtick settings, which can be used to change the size of the horizontal and vertical units of the graphs.
void stack(picture pics[]) {
  real margin=2mm;
  real htick = .8cm;
  real vtick = .4cm;
  frame[] frames = new frame[pics.length];
  for(int i=0; i<pics.length; ++i) {     unitsize(pics[i], htick, vtick);     frames[i] = pics[i].fit();     if (i>0) {
      frames[i] = shift(0,min(frames[i-1]).y-max(frames[i]).y-margin)*frames[i];
    }
    add(frames[i]);
  }
}

stack(new picture[] {pos_pic, vel_pic, acc_pic});

With numbered ticks: kinematic_stack_pos_vel_acc_nticks.pdf kinematic_stack_pos_vel_acc_nticks.png

// Asymptote code for kinematic_stack_pos_vel_acc_nticks.asy
import graph;

pen axis_p = linewidth(1.4)+black;
pen tick_p = linewidth(1.0)+gray(0.2)+fontsize(8);
pen ticklabel_p = tick_p;
int hticks = 5;
int vMin_ticks = -5;
int vMax_ticks = 5;
real[] hTicks_a = sequence(1, hticks);
real[] vTicks_a = sequence(vMin_ticks, vMax_ticks);
ticks hTicks = Ticks(format=Label("$%.4g$", align=E, p=ticklabel_p), Ticks=hTicks_a, Size=1mm, pTick=tick_p);
ticks vTicks = Ticks(format=Label("$%.4g$", align=W, p=ticklabel_p), Ticks=vTicks_a, Size=1mm, pTick=tick_p);
real axis_extra = 0.7; // extend the axis just a bit past the last tick mark

axis VZero(bool extend=true) {
  return new void(picture pic, axisT axis) {
    axis.type = 0; // Value
    axis.value = pic.scale.x.T(pic.scale.x.scale.logarithmic ? 1 : 0); // I'm good with Linear 0
    axis.position = 1; // relative position of axis label
    axis.side = left;
    axis.align = 5*E;
    axis.extend = extend;
    };
}
axis VZero = VZero();

axis HZero(bool extend=true) {
  return new void(picture pic, axisT axis) {
    axis.type = 0; // Value
    axis.value = pic.scale.y.T(pic.scale.y.scale.logarithmic ? 1 : 0); // I'm good with Linear 0
    axis.position = 0.5; // relative position of axis label
    axis.side = right;
    axis.align = W;
    axis.extend = extend;
    };
}
axis HZero = HZero();

void kingraph(picture pic, Label vL="", real vMin=vMin_ticks-axis_extra, real vMax=vMax_ticks+axis_extra, Label hL="$t$ [sec]", real hMin=0, real hMax=hticks+axis_extra) {
  scale(pic, Linear, Linear);
  xlimits(pic, hMin, hMax);
  ylimits(pic, vMin, vMax);
  xaxis(pic=pic, L=hL, axis=VZero(false), p=axis_p, ticks=hTicks, arrow=Arrow(6), above=false);
  yaxis(pic=pic, L=vL, axis=HZero(false), p=axis_p, ticks=vTicks, arrow=Arrow(6), above=false);
}

picture pos_pic;
kingraph(pos_pic, "$s$ [m]");

picture vel_pic;
kingraph(vel_pic, "$v$ [m/sec]");

picture acc_pic;
kingraph(acc_pic, "$a$ [m/sec/sec]");

//xequals(pos_pic,3,Dotted);
//xequals(vel_pic,3,Dotted);
//xequals(acc_pic,3,Dotted);

// boring code for stacking the graphs.  The only interesting part is the htick/vtick settings, which can be used to change the size of the horizontal and vertical units of the graphs.
void stack(picture pics[]) {
  real margin=2mm;
  real htick = .8cm;
  real vtick = .4cm;
  frame[] frames = new frame[pics.length];
  for(int i=0; i<pics.length; ++i) {
    unitsize(pics[i], htick, vtick);
    frames[i] = pics[i].fit();
    if (i>0) {
      frames[i] = shift(0,min(frames[i-1]).y-max(frames[i]).y-margin)*frames[i];
    }
    add(frames[i]);
  }
}

stack(new picture[] {pos_pic, vel_pic, acc_pic});

With unnumbered ticks: kinematic_stack_pos_vel_acc_ticks.pdfkinematic_stack_pos_vel_acc_ticks.png

// Asymptote code for kinematic_stack_pos_vel_acc_ticks.asy
import graph;

pen axis_p = linewidth(1.4)+black;
pen tick_p = linewidth(1.0)+gray(0.2)+fontsize(.01);
pen ticklabel_p = tick_p;
int hticks = 10;
int vMin_ticks = -5;
int vMax_ticks = 5;
real[] hTicks_a = sequence(1, hticks);
real[] vTicks_a = sequence(vMin_ticks, vMax_ticks);
ticks hTicks = Ticks(format=Label(" ", align=E, p=ticklabel_p), Ticks=hTicks_a, Size=1mm, pTick=tick_p);
ticks vTicks = Ticks(format=Label(" ", align=W, p=ticklabel_p), Ticks=vTicks_a, Size=1mm, pTick=tick_p);
real axis_extra = 0.7; // extend the axis just a bit past the last tick mark

axis VZero(bool extend=true) {
  return new void(picture pic, axisT axis) {
    axis.type = 0; // Value
    axis.value = pic.scale.x.T(pic.scale.x.scale.logarithmic ? 1 : 0); // I'm good with Linear 0
    axis.position = 1; // relative position of axis label
    axis.side = left;
    axis.align = .01*S;
    axis.extend = extend;
    };
}
axis VZero = VZero();

axis HZero(bool extend=true) {
  return new void(picture pic, axisT axis) {
    axis.type = 0; // Value
    axis.value = pic.scale.y.T(pic.scale.y.scale.logarithmic ? 1 : 0); // I'm good with Linear 0
    axis.position = 1; // relative position of axis label
    axis.side = right;
    axis.align = .01*W;
    axis.extend = extend;
    };
}
axis HZero = HZero();

void kingraph(picture pic, Label vL="", real vMin=vMin_ticks-axis_extra, real vMax=vMax_ticks+axis_extra, Label hL="$t$", real hMin=0, real hMax=hticks+axis_extra) {
  scale(pic, Linear, Linear);
  xlimits(pic, hMin, hMax);
  ylimits(pic, vMin, vMax);
  xaxis(pic=pic, L=hL, axis=VZero(false), p=axis_p, ticks=hTicks, arrow=Arrow(6), above=false);
  yaxis(pic=pic, L=vL, axis=HZero(false), p=axis_p, ticks=vTicks, arrow=Arrow(6), above=false);
}

picture pos_pic;
kingraph(pos_pic, "$s$");

picture vel_pic;
kingraph(vel_pic, "$v$");

picture acc_pic;
kingraph(acc_pic, "$a$");

//xequals(pos_pic,3,Dotted);
//xequals(vel_pic,3,Dotted);
//xequals(acc_pic,3,Dotted);

// boring code for stacking the graphs.  The only interesting part is the htick/vtick settings, which can be used to change the size of the horizontal and vertical units of the graphs.
void stack(picture pics[]) {
  real margin=2mm;
  real htick = .4cm;
  real vtick = .4cm;
  frame[] frames = new frame[pics.length];
  for(int i=0; i<pics.length; ++i) {
    unitsize(pics[i], htick, vtick);
    frames[i] = pics[i].fit();
    if (i>0) {
      frames[i] = shift(0,min(frames[i-1]).y-max(frames[i]).y-margin)*frames[i];
    }
    add(frames[i]);
  }
}

stack(new picture[] {pos_pic, vel_pic, acc_pic});

With grid: kinematic_stack_pos_vel_acc_grid.pdf kinematic_stack_pos_vel_acc_grid.png

// Asymptote code for kinematic_stack_pos_vel_acc_grid.asy
import graph;

pen axis_p = linewidth(1.4)+black;
pen grid_p = linewidth(0.8)+gray(0.2);
pen ticklabel_p = fontsize(.01);
int hticks = 10;
int vMin_ticks = -5;
int vMax_ticks = 5;
real[] hTicks_a = sequence(1, hticks);
real[] vTicks_a = sequence(vMin_ticks, vMax_ticks);
real axis_extra = 0.7; // extend the axis just a bit past the last tick mark

axis VZero(bool extend=true) {
  return new void(picture pic, axisT axis) {
    axis.type = 0; // Value
    axis.value = pic.scale.x.T(pic.scale.x.scale.logarithmic ? 1 : 0); // I'm good with Linear 0
    axis.position = 1; // relative position of axis label
    axis.side = left;
    axis.align = 1.5*E;
    axis.extend = extend;
    };
}
axis VZero = VZero();

axis HZero(bool extend=true) {
  return new void(picture pic, axisT axis) {
    axis.type = 0; // Value
    axis.value = pic.scale.y.T(pic.scale.y.scale.logarithmic ? 1 : 0); // I'm good with Linear 0
    axis.position = 1; // relative position of axis label
    axis.side = right;
    axis.align = W;
    axis.extend = extend;
    };
}
axis HZero = HZero();

void kingraph(picture pic, Label vL="", real vMin=vMin_ticks, real vMax=vMax_ticks, Label hL="$t$", real hMin=0, real hMax=hticks) {
  scale(pic, Linear, Linear);
  xlimits(pic, hMin, hMax);
  ylimits(pic, vMin, vMax);
  ticks hTicks = LeftTicks(format=Label(" ", align=E, p=ticklabel_p), Ticks=hTicks_a, extend=true, pTick=grid_p); // The space clears the labels on the ticks.
  ticks vTicks = RightTicks(format=Label(" ", align=W, p=ticklabel_p), Ticks=vTicks_a, extend=true, pTick=grid_p);
  xaxis(pic=pic, L="", axis=BottomTop, p=grid_p, ticks=hTicks);
  yaxis(pic=pic, L="", axis=LeftRight, p=grid_p, ticks=vTicks);
  xaxis(pic=pic, L=hL, axis=VZero(false), p=axis_p, ticks=NoTicks, arrow=Arrow(6), above=true);
  yaxis(pic=pic, L=vL, axis=HZero(false), p=axis_p, ticks=NoTicks, arrow=Arrow(6), above=true);
}

picture pos_pic;
kingraph(pos_pic, "$s$");

picture vel_pic;
kingraph(vel_pic, "$v$");

picture acc_pic;
kingraph(acc_pic, "$a$");

//xequals(pos_pic,3,Dotted);
//xequals(vel_pic,3,Dotted);
//xequals(acc_pic,3,Dotted);

// boring code for stacking the graphs.  The only interesting part is the htick/vtick settings, which can be used to change the size of the horizontal and vertical units of the graphs.
void stack(picture pics[]) {
  real margin=0mm;
  real htick = .4cm;
  real vtick = .4cm;
  frame[] frames = new frame[pics.length];
  for(int i=0; i<pics.length; ++i) {
    unitsize(pics[i], htick, vtick);
    frames[i] = pics[i].fit();
    if (i>0) {
      frames[i] = shift(0,min(frames[i-1]).y-max(frames[i]).y-margin)*frames[i];
    }
    add(frames[i]);
  }
}

stack(new picture[] {pos_pic, vel_pic, acc_pic});

Blank position and velocity graphs only: kinematic_stack_pos_vel.pdf kinematic_stack_pos_vel.png

// Asymptote code for kinematic_stack_pos_vel.asy
import graph;

pen axis_p = linewidth(1.4)+black;
pen grid_p = linewidth(1.0)+gray(0.2);

real hticks = 5;
real vMin_ticks = -5;
real vMax_ticks = 5;

void kingraph(picture pic, Label vL="", real vMin=vMin_ticks, real vMax=vMax_ticks, Label hL="$t$", real hMin=0, real hMax=hticks) {
  scale(pic, Linear, Linear);
  xlimits(pic, hMin, hMax);
  ylimits(pic, vMin, vMax);
  xaxis(pic, hL, YZero, axis_p, Arrow(6));
  yaxis(pic, vL, XZero, axis_p, Arrow(6));
}

picture pos_pic;
kingraph(pos_pic, "$s$");

picture vel_pic;
kingraph(vel_pic, "$v$");

// boring code for stacking the graphs.  You can change the stack statement at the bottom to choose which graphs to include in what order.  The only interesting part of the stack function is the htick/vtick settings, which can be used to change the size of the horizontal and vertical units of the graphs.
void stack(picture pics[]) {
  real margin=2mm;
  real htick = .8cm;
  real vtick = .4cm;
  frame[] frames = new frame[pics.length];
  for(int i=0; i<pics.length; ++i) {
    unitsize(pics[i], htick, vtick);
    frames[i] = pics[i].fit();
    if (i>0) {
      frames[i] = shift(0,min(frames[i-1]).y-max(frames[i]).y-margin)*frames[i];
    }
    add(frames[i]);
  }
}

stack(new picture[] {pos_pic, vel_pic});