"""Compare instantaneous friction on a box with two surface velocities. Writes an opaque white PNG to CWD. Tested with Python 3.14.4, NumPy 2.3.5 and Matplotlib 3.10.7 (distribution build 3.10.7+dfsg1). Stipulated dry-contact parameters; arrows show velocities and contact force, not a trajectory. The ideal belt motor maintains U and has no other losses. """ from pathlib import Path import matplotlib matplotlib.use("Agg") import matplotlib.pyplot as plt from matplotlib.patches import Rectangle import numpy as np def arrow(ax, start, end, label, color, label_y): ax.annotate("", xy=end, xytext=start, arrowprops={"arrowstyle": "->", "lw": 2.5, "color": color}) ax.text((start[0] + end[0]) / 2, label_y, label, color=color, ha="center", fontsize=11) def main(): box_velocity = 1.0 surface_velocities = np.array([0.0, 2.0]) mu_kinetic, normal_force = 0.3, 10.0 slips = box_velocity - surface_velocities forces = -mu_kinetic * normal_force * np.sign(slips) box_powers = forces * box_velocity heating = -forces * slips blue, orange, green = "#1768ac", "#bd5217", "#25784b" plt.rcParams.update({"font.size": 11, "figure.facecolor": "white", "axes.facecolor": "white"}) fig, axes = plt.subplots(1, 2, figsize=(10.5, 5.7), dpi=120) fig.subplots_adjust(left=.035, right=.98, bottom=.29, top=.80, wspace=.16) for i, ax in enumerate(axes): ax.set(xlim=(0, 5), ylim=(0, 3.4)) ax.axis("off") ax.add_patch(Rectangle((.2, 1.0), 4.6, .22, facecolor="#e5e7eb", edgecolor="#626872")) ax.add_patch(Rectangle((1.9, 1.22), 1.2, .9, facecolor="#e7f2fa", edgecolor=blue, lw=1.5)) ax.text(2.5, 1.66, "Box", ha="center", color=blue) arrow(ax, (1.9, 2.65), (3.0, 2.65), "v = +1 m/s", blue, 2.83) force_end = 1.0 if forces[i] < 0 else 4.0 arrow(ax, (2.5, 1.32), (force_end, 1.32), f"Friction on box: {forces[i]:+g} N", orange, .55) slip_end = 3.55 if slips[i] > 0 else 1.45 arrow(ax, (2.5, 2.30), (slip_end, 2.30), f"Relative slip v − U = {slips[i]:+g} m/s", green, 3.15) if surface_velocities[i] == 0: ax.text(2.5, .13, "Stationary ground: U = 0", ha="center", color=".3") else: arrow(ax, (1.0, .28), (3.2, .28), "Belt: U = +2 m/s", blue, .02) ax.set_title("Stationary surface" if i == 0 else "Faster moving belt", fontsize=13, pad=12) fig.suptitle("Kinetic friction opposes relative slip", fontsize=16, y=.97) fig.text(.5, .90, r"Instantaneous states: $m=1$ kg, $N=10$ N, $\mu_k=0.3$; right is positive", ha="center", fontsize=11) fig.text(.26, .23, f"Box power = {box_powers[0]:+g} W; heating = {heating[0]:g} W", ha="center", fontsize=11) fig.text(.75, .23, f"Box power = {box_powers[1]:+g} W; heating = {heating[1]:g} W", ha="center", fontsize=11) fig.text(.5, .14, "Belt case: ideal motor input 6 W = box kinetic-energy gain 3 W + heating 3 W", ha="center", fontsize=11) fig.text(.5, .075, "The box moves right in both cases. The friction force reverses when the surface overtakes it.", ha="center", fontsize=10) fig.text(.5, .025, "Motor power balances the −6 W contact power on the belt; other losses are neglected.", ha="center", fontsize=10) fig.savefig(Path(__file__).stem + ".png", facecolor="white", transparent=False) plt.close(fig) if __name__ == "__main__": main()