Physics simulation – a “squishy” ball bouncing

A short while ago a computer science student asked me how one could simulate the physics of a “squishy” ball. The idea was to imitate the ball in the following video: http://vimeo.com/27907918

After playing around with it for a bit, I finally came up with something that at least looked realistic. Very briefly, for people unfamiliar with the physics, here is how I did it. I divided the simulation into x- and y- velocity components to simplify the math. The dynamics of a squishy ball include the following:

  1. Gravity causes an acceleration in the downward direction.
  2. Friction. Every time the ball moves parallel to a surface, frictional forces act to reduce the speed of the ball.
  3. Loss of momentum due to inelastic collisions. Every time the ball hits a surface, it bounces back with slightly lower speed.
  4. Deformation or “squishing.” As the ball hits a surface, it flattens against that surface.

Here is the code. It is implemented on Processing 2.1.


/*********************************************************
SquishyBallV04
Created June 18, 2014
Created on Processing 2.1
**********************************************************

Demonstrates the dynamics of a flexible ball bouncing
off the borders of the screen and slowing down with
friction and loss of momentum.

**********************************************************/

float framesPerSecond = 60.0;                   // [per second]
float dt = 1.0/60.0;                            // time between two frames [seconds]
float currTime;                                 // [seconds]
int WIDTH = 640, HEIGHT = 360;                  // graphics window width and height [pixels]
float currXPos, currYPos;                       // current position of the center of the ball [pixels]
float initXVel = 10.0/dt, initYVel = 20.0/dt;   // initial velocities in x and y directions [pixels/frame]
float currXVel = initXVel, currYVel = initYVel; // current velocities [pixels/frame]
float minXVelMu = 10.0, minYVelMu = 50.0;       /* velocities below which friction effects become so large
                                                   that the ball comes to a stop [pixels/frame]              */
float minXVelSquish = 10.0, minYVelSquish = 30.0;/* minimum velocities to cause squishing [pixels/frame]     */
float maxDia = 100, minDia = 50;
float ballWidth = maxDia, ballHeight = maxDia;  /* ball defined as an ellipse [pixels]                 */
float gravity = 980*3;                          /* Acceleration due to gravity [pixels/frame/frame]    */
float muX = 150.0, muY = 150.0;                 /* Coefficent of friction along x and y directions
                                                   Affects loss of from velocity rolling friction
                                                   along a wall [dimensionless]                        */
float cRX = 0.8, cRY = 0.8;                     /* Coefficient of restitution along x and y directions
                                                   Effects loss of momentum after a collision.
                                                   [dimensionless]                                      */

/*
This is run once
*/
void setup()
{
  //frameRate(framesPerSecond);
  size(WIDTH, HEIGHT); // drawing window
  // initial position at center of window
  currXPos = WIDTH/2;
  currYPos = HEIGHT/2;
  currTime = (float)millis()/1000.0; // [seconds]
}

/*
This is run repeatedly
*/
void draw()
{
  /*
  Redraw and recompute only once every time interval dt. This ensures
  the animation takes place at a rate of 1/dt regardless of which plaform
  is used.
  */
  if ( ((float(millis())/1000.0) - currTime) > dt /* seconds */)
  {
    currTime = float(millis())/1000.0;  // [seconds]
    background(0); // draw background
    ellipse(currXPos, currYPos, ballWidth, ballHeight); // draw ball
    moveBall();
  }
 
   
}

/*
Apply the equations of motion and move the ball
*/
void moveBall ()
{  
  updatePositions();
  applyEquationsOfMotion();  
 
}

/*
Updates positions based on velocities
*/
void updatePositions ()
{
  currXPos += currXVel * dt;
  currYPos += currYVel * dt;
 
}

/*
Updates velocities based on the equations of motion.
*/
void applyEquationsOfMotion ()
{
  applyCollisionEffects();
  applyGravityEffects();  
  applyFrictionEffects();  
 
}

/*
 Equations of motion for collision with wall. It has three effects.
 The travel direction is reversed, some momentum is lost due to
 the collision being inelastic, and deformation (squihing) occurs.
 Magnitude of loss of momentum depends on the coefficient of restitution
 with that wall. To simulate real world deformation, deformation occurs
 only above a certain velocity.
*/
void applyCollisionEffects ()
{
  // if ball touches right/left wall
 
  if ((currXPos + minDia/2) > WIDTH)  {
    
     currXVel *= -cRX;  
     currXPos = WIDTH - minDia/2;  
     
  }  else if ((currXPos - minDia/2) < 0)  {
    
     currXVel *= -cRX;    
     currXPos = minDia/2;
     
  }  else if ((currXPos + maxDia/2) > WIDTH){
    
    // partial squishing, right wall
    ballWidth = maxDia - (currXPos + ballWidth/2 - WIDTH);
    ballHeight = maxDia + (currXPos + ballWidth/2 - WIDTH);
    
  }   else if ((currXPos - maxDia/2) < 0)  {
    
    // partial squishing, left wall
    ballWidth = maxDia + (currXPos - ballWidth/2);
    ballHeight = maxDia - (currXPos - ballWidth/2);
  }
 
 
  // if ball touches top/bottom wall
  if ( (currYVel > minYVelSquish) && (currYVel < (-1.0*minYVelSquish)) ){
    
    // high speed collision effects, with squishing
    if ((currYPos + minDia/2) > HEIGHT)  { // maximum squishing
      
       currYVel *= -cRY;
       currYPos = HEIGHT - minDia/2;  
       
       
    } else if  ((currYPos - minDia/2) < 0) {
      
       currYVel *= -cRY;    
       currYPos = minDia/2;    
       
    } else if ((currYPos + maxDia/2) > HEIGHT) {
      
      // partial squishing, bottom wall    
      ballHeight = maxDia - (currYPos + ballHeight/2 - HEIGHT);
      ballWidth = maxDia + (currYPos + ballHeight/2 - HEIGHT);
      
      
    }  else if  ((currYPos - maxDia/2) < 0) {
      
      // partial squishing, top wall
      ballHeight = maxDia + (currYPos - ballHeight/2);
      ballWidth = maxDia - (currYPos - ballHeight/2);
      
    }
  } else {
    
    // low speed collision effects, no squishing
    if ((currYPos + maxDia/2) > HEIGHT) {
      
      // no squishing, bottom wall    
      currYVel *= -cRY;
      currYPos = HEIGHT - maxDia/2;
      
      
    }  else if  ((currYPos - maxDia/2) < 0) {
      
      // no squishing, top wall
      currYVel *= -cRY;    
      currYPos = maxDia/2;  
    }
    
  }
 
}

/*
  Equations of motion for effects of gravity. Gravity is positive
  in the positive y direction.
*/
void applyGravityEffects ()
{
  currYVel += gravity * dt;
}

/*
  Equations of motion for the friction effects with walls. Assuming only friction
  along horizontal walls is significant. Assuming friction with vertical walls is
  negligible for our animation, since the ball spends only a negligible amount of
  time sliding down on the vertical walls. At very low speeds the ball comes to a
  stop, simulating the real world effect.
*/
void applyFrictionEffects () {
  // if touching any of the horizontal walls
  if (((currYPos + maxDia/2) >= HEIGHT) || ((currYPos - maxDia/2) <= 0))  {
    if(currXVel > minXVelMu) {
      currXVel -= muX*dt;
    } else if (currXVel < (-1.0*minXVelMu) ) {
      currXVel += muX*dt;
    } else {
      currXVel = 0.0; // stop if too slow
    }
  }
 
}

</pre>
<pre>

BeagleBone Black – Digital output using C/C++

Did some more digging after my last post and came up with C++ example for digital output on the BBB. I used it to drive an LED. This example controls GPIO1_28 (pin 12 on the P9 header). All the pins are listed on the user manual. Note that this pin is treated as pin 60 in the code. This is how it’s set up in Linux. Here’s how to convert. GPIOn_m is n x 32 + m. So GPIO1_28 is 32 x 1 + 28 = 60.

There are four steps to the process of using a GPIO pin. First, you export the pin, i.e. tell the OS that you want to use it for something. Second, you set the direction, e.g. “out” in this case. Third, you do what you want with the pin. In this example we toggle it between high and low. Fourth, when you are done with the pin, you must unexport it. Doing any of the above four steps with pins is like writing to a file. That’s why you need to check if the file is accessible right before every file write. And right after the file write, you need to close the file handle. It seems that this needs to be done after every file write. If you blink an LED 50 times, you close the handle after each high/low event.

Lastly, a note on the hardware side of things. The BBB is a 3.3V board and is NOT 5V tolerant. The recommended source/sink current is only about 4-6 mA. So if you’re driving an LED, use a transistor, say, a PN2222A. Use a 10K resistor on the base, and 100 ohm on the collector. For this setup, connect pin 7 on the P9 header (SYS_5V) to the LED. This is 5V from the USB. Connect the ground to pin 45 (GND) on the P9 header. Be sure to check the data sheet for your transistor. Different variations of the same transistors have different pinouts. I nearly connected mine all wrong while looking at the connections in one video I was following. Turned out I had a different 2222 transistor with different pinouts.

Hope this helps other new BBB users like me!

/*******************************************************
* Example code for digital output on a GPIO pin
* on the new beaglebone black running the
* Linux Angstrom distribution.
********************************************************
* 
* Instructions:
* -Compile and run from the root directory
* -Windows users can use WinSCP to transfer
* code files to the beaglebone. This will
* allow you to edit code in your favorite
* editor on Windows
* -It is recommended that you do not try to
* source more than 4 - 6 mA. Use a transistor
* e.g. the PN2222A to source higher currents,
* such as for driving an LED
*
* Code adapted from:
* - Derek Molloy, "Beaglebone: C/C++ Programming
* Introduction for ARM Embedded Linux Development
* using Eclipse CDT" video tutorial,
* (link: <a href="http://www.youtube.com/watch?v=SaIpz00lE84" title="http://www.youtube.com/watch?v=SaIpz00lE84">http://www.youtube.com/watch?v=SaIpz00lE84</a>)
* -  Mark A. Yoder, EBC Exercise 10 Flashing an LED
* (link: http://www.elinux.org/EBC_Exercise_10_Flashing_an_LED)
*
*******************************************************/

#include <stdio.h>
#include <unistd.h>
#include <string.h>

using namespace std;

int main(int argc, char** argv) {

    int GPIOPin=60, /* GPIO1_28 or pin 12 on the P9 header */ times=10;

    printf("\nStarting GPIO output program\n");
    FILE *myOutputHandle = NULL;
    char setValue[4], GPIOString[4], GPIOValue[64], GPIODirection[64];
    sprintf(GPIOString, "%d", GPIOPin);
    sprintf(GPIOValue, "/sys/class/gpio/gpio%d/value", GPIOPin);
    sprintf(GPIODirection, "/sys/class/gpio/gpio%d/direction", GPIOPin);

    // Export the pin
    if ((myOutputHandle = fopen("/sys/class/gpio/export", "ab")) == NULL){
        printf("Unable to export GPIO pin\n");
        return 1;
    }
    strcpy(setValue, GPIOString);
    fwrite(&setValue, sizeof(char), 2, myOutputHandle);
    fclose(myOutputHandle);

    // Set direction of the pin to an output
    if ((myOutputHandle = fopen(GPIODirection, "rb+")) == NULL){
        printf("Unable to open direction handle\n");
        return 1;
    }
    strcpy(setValue,"out");
    fwrite(&setValue, sizeof(char), 3, myOutputHandle);
    fclose(myOutputHandle);

    for(int i=0; i<times; i++){
        // Set output to high
        if ((myOutputHandle = fopen(GPIOValue, "rb+")) == NULL){
            printf("Unable to open value handle\n");
            return 1;
        }
        strcpy(setValue, "1"); // Set value high
        fwrite(&setValue, sizeof(char), 1, myOutputHandle);
        fclose(myOutputHandle);
        sleep(1); // wait for 1 sec

        // Set output to low
        if ((myOutputHandle = fopen(GPIOValue, "rb+")) == NULL){
            printf("Unable to open value handle\n");
            return 1;
        }
        strcpy(setValue, "0"); // Set value low
        fwrite(&setValue, sizeof(char), 1, myOutputHandle);
        fclose(myOutputHandle);
        sleep(1); // wait for 1 sec

    }

    // Unexport the pin
    if ((myOutputHandle = fopen("/sys/class/gpio/unexport", "ab")) == NULL) {
        printf("Unable to unexport GPIO pin\n");
        return 1;
    }
    strcpy(setValue, GPIOString);
    fwrite(&setValue, sizeof(char), 2, myOutputHandle);
    fclose(myOutputHandle);
    printf("\nCompleted GPIO output program\n");

    return 0;
}

BeagleBone Black – Controlling user LEDs using C/C++

The $45 BeagleBone Black is here! With all its GPIO ports, it can be a great tool for robotics projects. It ships with javascript example code (in the Cloud9 IDE). There are many examples on the internet showing pin manipulation with javascript and python. There are lots of examples on pin manipulation from the terminal. Unfortunately for a new BBB owner, I could only find suggestions on how to do this on C/C++. I could not find a complete, short, and plain C/C++ example which I could just copy and use as a starting point…something like the traditional “blink” example. Some people have posted their own C++ libraries for pin manipulation, but I find that confusing without understanding the basic concepts.

After a lot of digging, ssh-ing, and g++-ing, I finally got a “blink” example up and running on C++. Here it is. Hopefully, for all the new BBB users out there, this can serve as a starting point in understanding how to use the GPIO ports through C/C++.


/*******************************************************
 * Example code for user LEDs on the new beaglebone
 * black running the Linux Angstrom distribution
 ********************************************************
 * Instructions:
 * -Compile and run from the root directory
 * -For the older (white) beaglebone, change
 *  "beaglebone:green:usr0" to "beaglebone::usr0"
 *
 * Code adapted from:
 * - Derek Molloy, "Beaglebone: C/C++ Programming
 * Introduction for ARM Embedded Linux Development
 * using Eclipse CDT" video tutorial,
 * (link: www.youtube.com/watch?v=vFv_-ykLppo)
 * -  Mark A. Yoder, EBC Exercise 10 Flashing an LED
 * (link: "elinux.org/EBC_Exercise_10_Flashing_an_LED)
 *******************************************************/

#include <stdio.h>
#include <unistd.h>

using namespace std;

int main(int argc, char** argv) {

  FILE *LEDHandle = NULL;
  char *LEDBrightness = "/sys/class/leds/beaglebone:green:usr0/brightness";
  printf("\nStarting simple LED blink program\n");

  while(1){

    if((LEDHandle = fopen(LEDBrightness, "r+")) != NULL){
      fwrite("1", sizeof(char), 1, LEDHandle);
      fclose(LEDHandle);
    }

    sleep(1);

    if((LEDHandle = fopen(LEDBrightness, "r+")) != NULL){
      fwrite("0", sizeof(char), 1, LEDHandle);
      fclose(LEDHandle);
    }

    sleep(1);
  }
  return 0;

}