Using linear programming for solving mechanical engineering problems: The ladder against a wall problem (part 2)

In my previous blog post I demonstrated how to use linear programming for solving the ladder against a wall problem. More specifically, the R code in that blog found the maximum position x a person could reach before the ladder starts sliding.

In this blog post I will go one step further. In particular, I will develop a solution strategy for finding the minimum angle θ such that a ladder doesn’t slip down. For simplicity, I will assume that no person is on the ladder, and that the ladder slips down due to its own weight (Q). Furthermore, I assume that both the wall and floor are rough surfaces. As a consequence, friction occurs between the top of the ladder and the vertical wall, and between the base of the ladder and the horizontal floor. This minimum angle problem is depicted in the following figure (with on the right-hand side a free body diagram).

linear programming ladder against wall problem angle

What is the intuition behind my solution strategy for the minimum angle problem? Let’s do the following Gedanken experiment. Imagine that the ladder’s center of mass is not fixed at the middle of the ladder (at L/2, or similarly at m=cos(θ)*L/2), but instead is free to move. In other words, the center of mass is allowed to climb up and down the ladder. This “free to move” center of mass could possibly, for a specified angle θ, reach position x (see figure above). This distance x is the maximum position the center of mass can reach before the ladder starts sliding. Put differently, the ladder starts sliding if the center of mass would find itself at a position larger than this x. In my previous blog post, I demonstrated how to use linear programming to locate this position x.

But in reality the center of mass is, of course, fixed at a position. Moreover, the ladder starts sliding as soon as the distance m of the fixed center of mass is larger than the distance x of the “free to move” center of mass.
That is, the ladder will slide if the fixed center of mass is on the left of position x of the “free to move” center of mass. On the other hand, the ladder is stable if the fixed center of mass is on the right of position x of the “free to move” center of mass.
As a consequence, the ladder would be on the verge of sliding once the difference between position x of the “free to move” center of mass and position m of the fixed center of mass is zero. Note that this is exactly the position where the angle θ is a its minimum.

The following R code implements the above proposed solution strategy. This R code finds the minimum angle θ in two steps. First, the code uses linear programming to locate the position x of the “free to move” center of mass at a specified angle θ.
Secondly, the code computes the difference between positions x and m, and uses an optimization algorithm (i.e., Brent’s algorithm) to find the position where this difference between x and m is at its minimum (i.e., zero).

Finally, an analytical solution (in comparison to the proposed numerical solution strategy above) can be found in Nelson’s 2009 book Engineering mechanics: statics and dynamics.

Suggestions and/or questions? Please contact Stefan Gelissen (email: info at datall-analyse.nl).
See this page for an overview of all of Stefan’s R code blog posts.

R code

library(lpSolve)

Q <- 5*9.81 #mass of ladder is 5kg
muFloor <- .3 #static friction coefficient between floor and ladder
muWall <- .15 #static friction coefficient between wall and ladder
lengthLadder <- 10 #length of ladder

#function for computing minimum angle
angleLadder <- function(theta, lengthLadder, muFloor, muWall, Qladder) {
  alpha <- 90 - theta
  #convert degrees to radians (by muliplying with pi/180), and compute tangent of angle
  tanAlpha <- tan(alpha*pi/180)
  h <- cos(alpha*pi/180)*lengthLadder
  
  #midpoint of base (i.e., position of the fixed center of mass)
  m <- .5*cos(theta*pi/180)*lengthLadder
  
  #set up linear programming algorithm
  #- objective function
  #x = ((h*tanAlpha)/Q)*Hb+(h/Q)*Nb+0*Ha+0*Na
  fObj <- c((h*tanAlpha)/Q,h/Q,0,0)
  #- constraints
  #0*Hb+1*Nb-1*Ha+0*Na=0
  #1*Hb+0*Nb+0*Ha+1*Na=Q
  #1*Hb-muWall*Nb+0*Ha+0*Na<=0
  #0*Hb+0*Nb+1*Ha-muFloor*Na<=0
  fConstr <- matrix(c(0,1,-1,0,1,0,0,1,1,-muWall,0,0,0,0,1,-muFloor), nrow=4, byrow=TRUE)
  fDir <- c("==", "==", "<=", "<=")
  fRhs <- c(0,Q,0,0)
  
  #compute position x (applying revised simplex method)
  x <- lp("max", fObj, fConstr, fDir, fRhs)$objval
  
  #compute difference between position x and midpoint m
  abs(x-m)
}

#obtain start value for angle theta
#1) generate a sequence of angles
thetas <- seq(0, 90, length.out=500)
#2) compute difference between positions x and m
ll <-sapply(thetas, angleLadder,
            lengthLadder=lengthLadder, muFloor=muFloor,
            muWall=muWall, Qladder=Q)
#plot difference
plot(thetas,ll, type='l', xlab=expression(theta), ylab="absolute difference")
#include horizontal line at zero difference
abline(h=0, lty=2, col="blue")

#optimization (find minimum angle theta)
angleMin <- optim(par=40, angleLadder, method="Brent",
                  upper=90,lower=0,
                  lengthLadder=lengthLadder, muFloor=muFloor,
                  muWall=muWall, Qladder=Q)
#minimum angle theta
angleMin$par

#Nelson's analytic solution yields 57.83 degrees, which is similar to the
#numerical solution. The reason for the small difference in angle between
#the analytical and numerical solution is due to Nelson's rounding of numbers
#at intermediate steps of the analytical solution.
#With no such rounding of numbers the analytical solution yields
#an angle of 57.85999 degrees, which is identical to the numerical solution.




##different static friction coefficients: increase the value of the friction coeffients
muFloor <- .8
muWall <- .65

#obtain start value for theta
thetas <- seq(0, 90, length.out=500)
ll <-sapply(thetas, angleLadder,
            lengthLadder=lengthLadder, muFloor=muFloor,
            muWall=muWall, Qladder=Q)

plot(thetas,ll, type='l', xlab=expression(theta), ylab="absolute difference")
abline(h=0, lty=2, col="blue")

#optimization
angleMin <- optim(par=40, angleLadder, method="Brent",
                  upper=90,lower=0,
                  lengthLadder=lengthLadder, muFloor=muFloor,
                  muWall=muWall, Qladder=Q)
#minimum angle theta
angleMin$par
#note: computed minimum angle is identical to the analytic solution (=16.70 degrees)




##different static friction coefficients: low static friction coefficients
muFloor <- .1
muWall <- .05

#obtain start value for theta
thetas <- seq(0, 90, length.out=500)
ll <-sapply(thetas, angleLadder,
            lengthLadder=lengthLadder, muFloor=muFloor,
            muWall=muWall, Qladder=Q)

plot(thetas,ll, type='l', xlab=expression(theta), ylab="absolute difference")
abline(h=0, lty=2, col="blue")

#optimization
angleMin <- optim(par=40, angleLadder, method="Brent",
                  upper=90,lower=0,
                  lengthLadder=lengthLadder, muFloor=muFloor,
                  muWall=muWall, Qladder=Q)
#minimum angle theta
angleMin$par
#note: computed minimum angle is identical to the analytic solution (=78.63 degrees)



##different static friction coefficients: very (sticky) rough surfaces
muFloor <- 10
muWall <- 10

#obtain start value for theta
thetas <- seq(-90, 90, length.out=500)
ll <-sapply(thetas, angleLadder,
            lengthLadder=lengthLadder, muFloor=muFloor,
            muWall=muWall, Qladder=Q)

plot(thetas,ll, type='l', xlab=expression(theta), ylab="absolute difference")
abline(h=0, lty=2, col="blue")

#optimization
angleMin <- optim(par=-50, angleLadder, method="Brent",
                  upper=90,lower=-90,
                  lengthLadder=lengthLadder, muFloor=muFloor,
                  muWall=muWall, Qladder=Q)
#minimum angle theta
angleMin$par
#notice the negative angle for theta, which is physically impossible
#therefore, we may set the angle to zero degrees
#note that this computed negative angle is again identical
#to the analytic solution (=-78.58 degrees)