23 优化
上一章我们建立了一个棒球飞行模型,其中包括重力和一个简单的阻力,但忽略了自旋、马格努斯力和阻力系数对速度的依赖。
在本章中,我们将对该模型进行优化。
23.1 曼尼-拉米雷斯问题
曼尼-拉米雷斯是波士顿红袜队(美国棒球队)的前成员,他因其轻松的态度和讲笑话的品味而闻名。我们本章的目标是解决下面这个受曼尼启发的问题:
在芬威公园击出全垒打所需的最小力是多少?
芬威公园是一座位于马萨诸塞州波士顿的棒球场。其中最著名的特色之一就是“绿色怪物”,它是左外野的一堵墙,离本垒板非常近,只有310英尺,为了弥补这个短距离,这面墙异常的高,有37英尺。(详见http://modsimpy.com/wally)
我们要找到球离开本垒同时还能越过“绿色怪物”的最低速度,我们将按照以下步骤进行:
1.给定速度,我们要找出最佳发射角度,也就是球离开本垒的角度,以使球到达墙面时高度最大。
2.然后我们将找到在最佳发射角度下越过墙的最小速度。
我们将使用与上一章相同的模型,和这个Params对象:
t_end = 20 * s
dt = t_end / 100
params = Params(x = 0 * m,
y = 1 * m,
g = 9.8 * m/s**2,
mass = 145e-3 * m,
rho = 1.2 * kg/m**3,
C_d = 0.3,
angle = 45 * degree;
velocity = 40 * m/s,
t_end=t_end,
dt=dt)
make_system函数:
def make_system(params):
angle, velocity = params.angle, params.velocity
# convert angle to degrees
theta = np.deg2rad(angle)
# compute x and y components of velocity
vx, vy = pol2cart(theta, velocity)
# make the initial state
R = Vector(params.x, params.y)
V = Vector(vx, vy)
init = State(R=R, V=V)
# compute area from diameter
diameter = params.diameter
area = np.pi * (diameter/2)**2
return System(params, init=init, area=area)
slope函数:
def slope_func(state, t, system):
R, V = state
mass, g = system.mass, system.g
a_drag = drag_force(V, system) / mass
a_grav = Vector(0, -g)
A = a_grav + a_drag
return V, A
还有event函数:
def event_func(state, t, system):
R, V = state
return R.y
23.2 寻找范围
假设我们想找到最大射程的发射角度,最大射程就是球在落地前在空中的飞行距离。我们将使用ModSim库中的一个函数maximize,它可以接受一个函数并找到其最大值。
我们传递给maximize的函数应该包含发射角度和一个params对象,并返回射程:
def range_func(angle, params):
params = Params(params, angle=angle)
system = make_system(params)
results, details = run_ode_solver(system, slope_func,
events=event_func)
x_dist = get_last_value(results.R).x
print(angle, x_dist)
return x_dist
range_func用给定的angle值创建一个新的Params对象,然后创建System对象,调用run_ode_solver,从结果中返回x的最终值。
我们可以像这样直接调用range_func:
range_func(45, params)
然后我们可以像这样扫描一系列的角度:
angles = linspace(20, 80, 21)
sweep = SweepSeries()
for angle in angles:
x_dist = range_func(angle, params)
print(angle, x_dist)
sweep[angle] = x_dist
图23.1展示了结果,看来最佳角度是在40°到45°之间。
图23.1:给定速度,发射角度和距离本垒板的距离构成的函数
我们可以更精确、更有效地使用maximize找到最佳角度,比如这样:
res = maximize(range_func, [0, 90], params)
第一个参数是我们要最大化的函数,第二个参数是我们要找的数值范围,本例中数值范围为0°到90°,第三个参数可以是任何对象,当maximize调用range_func时它会被作为参数传递过来。
maximize的返回值是一个表示结果的一维数组,包括x,即达到函数最高点的角度,和fun,即range_func在x处的值,也就是棒球以最佳角度发射时和本垒板的距离。
对于这些参数,最佳角度约为42°,对应射程为103m。
maximize使用了黄金分割搜索,你可以在这里阅读到http://modsimpy.com/minimize
23.3解决问题
在本章的notebook代码chap22.ipynb中,你将有机会解决曼尼-拉米雷斯问题,不过你需要做以下几件事:
- 在上一节中,“最佳”发射角度是指最大射程的角度,但这不是我们想要的。相反,我们想要的是当球到达墙边(距离本垒板310英尺)时高度最大化的角度,所以你需要写一个高度函数来计算它,然后使用maximize来找到修改后的最优值。
- 一旦找到任意速度的最佳角度,接着我们需要找出让球越过墙壁的最小速度,你可以写一个函数,把速度作为参数,计算出这个速度的最佳角度,然后使用这个最佳角度返回球在墙上的高度。
- 最后,你可以使用root_bisect来找到速度,使墙的高度刚好是37英尺。
notebook上提供了一些额外的提示,这上面应该有你需要的一切,祝你好运!
如果你喜欢这个练习,你可能会对这篇文章感兴趣:How to hit home runs: Optimum baseball bat swing parameters for maximum range trajectories,作者是Sawicki,Hubbard和Stronge,网址: http://modsimpy.com/runs.
本书的中文翻译由南开大学医学院智能医学工程专业2018级、2019级的师生完成,方便后续学生学习《Python仿真建模》课程。翻译人员(排名不分前后):薛淏源、金钰、张雯、张莹睿、赵子雨、李翀、慕振墺、许靖云、李文硕、尹瀛寰、沈纪辰、迪力木拉、樊旭波、商嘉文、赵旭、连煦、杨永新、樊一诺、刘志鑫、彭子豪、马碧婷、吴晓玲、常智星、陈俊帆、高胜寒、韩志恒、刘天翔、张艺潇、刘畅。
整理校订由刘畅完成,如果您发现有翻译不当或者错误,请邮件联系changliu@nankai.edu.cn。
本书中文版不用于商业用途,供大家自由使用。
未经允许,请勿转载。