# Using JuMP to model a slider-crank mechanism

**URL:** https://discourse.julialang.org/t/using-jump-to-model-a-slider-crank-mechanism/122560
**Category:** Optimization (Mathematical)
**Tags:** jump, optimization
**Created:** [November 12, 2024, 8:53pm UTC](https://discourse.julialang.org/t/using-jump-to-model-a-slider-crank-mechanism/122560 "2024-11-12T20:53:42Z")
**Posts on this page:** 4
**Page:** 1

<div class="post-metadata">

### Author: ![mrufsvold](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mrufsvold/32/31600_2.png) [@mrufsvold](https://discourse.julialang.org/u/mrufsvold)
#### Post date: [November 12, 2024, 8:53pm UTC](https://discourse.julialang.org/t/using-jump-to-model-a-slider-crank-mechanism/122560/1 "2024-11-12T20:53:42Z")

</div>

Hi! I was actually able to get this working wtih help from @araujoms on slack, but documenting here for future Googlers.

I am trying to model a slider-crank mechanism like this:

 ![Hand drawn slider crank mechanism diagram](https://global.discourse-cdn.com/julialang/original/3X/5/2/52fb9b7f01cb2ee9b49e093c5fb3c75719fc5dae.jpeg)

I want to minimize the height of the system for a certain amount of travel of the slider box.

Here is my original (incorrect!!) code:

```julia
using JuMP, Ipopt

model = Model(Ipopt.Optimizer)

# length of crank shaft off the motor shaft
@variable(model, r_crank >= 0, start = 3)

# length of arm from end of crank to connection point on slider block
@variable(model, r_arm >= 0, start = 8)

# Clearance from lowest point of crank shaft to the line of action for the slider block
@variable(model, 8 >= clearance >= 7, start = 7.5)

# total offset between line of action and motor shaft
offset = r_crank + clearance

# angle formed by the line perpendicular to the line of action which intersects the motor shaft and the arm when it is fully retracted 
θ = acos(offset/r_arm)

x = cos(θ)*r_crank
z = sin(θ)*r_crank
y = x - offset
a = √(r_arm^2 - y^2)

# minimum x-dimension distance from motor shaft to the connection point on the slider block
d_min = sin(θ)*r_arm

# max x-dimension distance from motor shaft to the connection point on the slider block
d_max = z + a

#total distance traveled by slider box
d_diff = d_max - d_min

# we want the slider box to move 15 to 20 cm
@constraint(model, c1, 15 <= d_diff <= 20)

# we want the minimum angle to be at least 45 and at most 90 degrees
@constraint(model, c2, 45*π/180 <= θ <= 90*π/180)

# we'd like the system to be as short as possible
@objective(model, Min, offset)

# We'd like the arm to be as short as possible
@objective(model, Min, r_arm+r_crank)

optimize!(model)

```

This was throwing the error:

```julia
This is Ipopt version 3.14.16, running with linear solver MUMPS 5.7.3.

Number of nonzeros in equality constraint Jacobian...: 0
Number of nonzeros in inequality constraint Jacobian.: 8
Number of nonzeros in Lagrangian Hessian.............: 12

The inequality constraints contain an invalid number

Number of Iterations....: 0

Number of objective function evaluations = 0
Number of objective gradient evaluations = 0
Number of equality constraint evaluations = 0
Number of inequality constraint evaluations = 1
Number of equality constraint Jacobian evaluations = 0
Number of inequality constraint Jacobian evaluations = 0
Number of Lagrangian Hessian evaluations = 0
Total seconds in IPOPT = 0.000

EXIT: Invalid number in NLP function or derivative detected.

```

This code had the following problems:

1. `y` could be greater than `r_arm` so `a` could be imaginary
2. ~~You need to use `@NLconstraint` for constraints that have nonlinear functions.~~ I was corrected below
3. I didn’t have a constraint to relate the length of the arm to the length of the crank, so I was getting ridiculous results even after the errors were fixed.
4. I want to be able to pick how much clearance I have with my offset and how far the slider box travels, so that shouldn’t be a variable but a constant input.

In the end, I got this as my final code:

```julia
using JuMP, Ipopt
const clearance = 7
const target_distance_traveled = 20
begin
	model = Model(Ipopt.Optimizer)
	register(model, :√, 1, √; autodiff = true)
	# length of crank shaft off the motor shaft
	@variable(model, r_crank >= 0, start = 3)
	# length of arm from end of crank to connection point on slider block
	@variable(model, r_arm >= 0, start = 5*clearance)
	# angle between the vertical line connecting the motor shaft to the line of action and the arm when in the retracted position
	@variable(model, 50*π/180<= θ <= 85*π/180, start = 65*π/180)
	# total offset between line of action and motor shaft
	offset = r_crank + clearance
	# y-dimesion distance from motor shaft 
	x = cos(θ)*r_crank
	z = sin(θ)*r_crank
	y = offset - x
	a = √(r_arm^2 - y^2)
	# minimum x-dimension distance from motor shaft to the connection point on the slider block
	d_min = sin(θ)*r_arm
	# max x-dimension distance from motor shaft to the connection point on the slider block
	d_max = z + a
	#total distance traveled by slider box
	d_diff = d_max - d_min
	@NLconstraint(model, d_diff == target_distance_traveled)
	# need to assert that the arm is longer than the height above the line of action when extended or we get NaN results.
	@NLconstraint(model, r_arm >= y)
	# Need to assert the relationship between the arm and crank length
	@NLconstraint(model, r_arm == 2r_crank + √(y^2+(d_min-z)^2))
	# we'd like the system to be as short as possible
	@objective(model, Min, offset)
	optimize!(model)
end

```

After these modifications, I’m getting reasonable results and no errors 😃

I’m just a hobbyist here with no training in mechanical engineering, optimization, or anything else, so I just want to say what a pleasure the JuMP.jl docs are and how helpful this community is. Thank you @jd-foster, @araujoms , and @odow .

---

<div class="post-metadata">

### Author: ![odow](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/odow/32/28685_2.png) [@odow](https://discourse.julialang.org/u/odow)
#### Post date: [November 12, 2024, 9:47pm UTC](https://discourse.julialang.org/t/using-jump-to-model-a-slider-crank-mechanism/122560/2 "2024-11-12T21:47:34Z")

</div>

Hi @mrufsvold, thanks for posting this over here (I’ve moved this to the optimization category).

I find it easier to discuss questions here on Discourse than on slack, and the discussion can be searched for and linked to by other people in the future.

I would write your model as:

```julia
using JuMP, Ipopt
begin
	clearance = 7
	target_distance_traveled = 20
	model = Model(Ipopt.Optimizer)
	@variables(model, begin
		r_crank >= 0, (start = 3)
		r_arm >= 0, (start = 5*clearance)
		50 <= θ <= 85, (start = 65)
	end)
	@expressions(model, begin
		offset, r_crank + clearance
		x, cosd(θ) * r_crank
		z, sind(θ) * r_crank
		y, offset - x
		d_min, sind(θ) * r_arm
		d_max, z + √(r_arm^2 - y^2)
	end)
	@constraints(model, begin
		d_max - d_min == target_distance_traveled
		r_arm >= y
		r_arm == 2r_crank + √(y^2 + (d_min - z)^2)
	end)
	@objective(model, Min, offset)
	optimize!(model)
end

```

You don’t need to use the `@NL` macros or `register`, and you can use `sind` and `cosd` to compute in degrees rather than radians.

> so I just want to say what a pleasure the [JuMP.jl](https://juliahub.com/ui/Packages/General/JuMP) docs are and how helpful this community is.

Thanks!

---

<div class="post-metadata">

### Author: ![mrufsvold](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mrufsvold/32/31600_2.png) [@mrufsvold](https://discourse.julialang.org/u/mrufsvold)
#### Post date: [November 12, 2024, 9:50pm UTC](https://discourse.julialang.org/t/using-jump-to-model-a-slider-crank-mechanism/122560/3 "2024-11-12T21:50:23Z")

</div>

What is the benefit of using `@expresions` instead of doing them as raw assignments like I did?

---

<div class="post-metadata">

### Author: ![odow](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/odow/32/28685_2.png) [@odow](https://discourse.julialang.org/u/odow)
#### Post date: [November 12, 2024, 9:58pm UTC](https://discourse.julialang.org/t/using-jump-to-model-a-slider-crank-mechanism/122560/4 "2024-11-12T21:58:22Z")

</div>

For simple cases like this: very little.

For more complicated cases, building inside a macro can be more performant: [Performance tips · JuMP](https://jump.dev/JuMP.jl/stable/tutorials/getting_started/performance_tips/#Use-macros-to-build-expressions). This mainly applies if you are building linear expressions with many terms.

I tend to use `@expression` when I post on this forum just encourage people to get in the habit of using it.
