rotating_habitat is a small Python simulation of projectile motion inside a rotating cylindrical space habitat, viewed from the habitat's rotating frame.
The core idea is simple: in an inertial frame, the ball travels in a straight line at constant velocity after release. In the habitat frame, the observer is rotating underneath that motion, so the trajectory appears curved. This script visualizes that apparent path until the object reaches the cylinder wall.
- Models a 2D cross-section of a cylindrical habitat with normalized radius
1 - Simulates a point-like object released from some position with some velocity
- Converts inertial motion into the apparent trajectory seen by a rotating observer
- Animates the result with Matplotlib
This is closer to a compact visualization / toy physics model than a full simulation package. It is useful for intuition, teaching, and experimenting with how throws behave inside O'Neill-style rotating habitats.
Inside a rotating habitat, "down" is toward the outer hull because the floor is accelerating inward while the observer moves in a circle. Once you let go of an object, it keeps the tangential velocity it had at the release point.
From the rotating observer's perspective, two effects dominate:
- The object seems to drift sideways because it is no longer being forced to follow the habitat's circular motion.
- The path appears curved because the observer keeps rotating while the ball continues along an inertial straight-line path.
In a rotating-frame treatment, that apparent deflection is usually described with fictitious forces:
- Centrifugal effect: pushes apparent motion outward toward the hull
- Coriolis effect: bends the path sideways relative to the direction of travel
This script does not explicitly integrate those fictitious-force equations. Instead, it uses an inertial-frame trajectory and repeatedly rotates coordinates into the observer's frame. For this problem, that is a clean and direct way to get the same visible result.
- The habitat is represented as a perfect circle in 2D.
- The radius is normalized to
1, so all positions are fractions of the cylinder radius. - The object is point-like.
- There is no drag, lift, spin, collision rebound, or gravity source other than the apparent effect of rotation.
- The object moves with constant velocity in the inertial frame after release.
- The simulation stops when the object leaves the cylinder boundary.
rotating_habitat.py: simulation classes and a sample animation runrequirements.txt: Python dependencies
Use a modern Python 3 environment.
python -m venv .venv
.venv\Scripts\activate
pip install -r requirements.txtpython rotating_habitat.pyThe default demo:
- sets the habitat spin rate to
2 RPM - releases the ball from
20%of the radius above the floor along the observer's vertical centerline - throws it at
80%of the local rotational speed - uses a throw angle of
135degrees measured from the spinward direction
The script exposes two main classes: Ball and Cylinder.
Represents an object moving with constant inertial velocity.
initial_position:numpy.ndarrayshaped like[[x], [y]]initial_speed:numpy.ndarrayshaped like[[vx], [vy]]time_step: simulation time step in seconds
Represents the observer's rotating reference frame.
angular_speed: habitat rotation rate in RPMtime_step: simulation time step in seconds
Use this for a non-rotating cylinder only.
ball_position:[x, y]ball_speed:[vx, vy]
Coordinates are given in units of habitat radius, so the wall is at sqrt(x^2 + y^2) = 1.
Use this for a rotating cylinder.
relative_position: vertical release location as a fraction of radius, measured upward from the bottomball_speed:[speed_ratio, angle_degrees]
Interpretation:
speed_ratiois the throw speed relative to the local habitat tangential speed at that heightangle_degreesis measured from the spinward direction
The method adds the release-point inertial tangential velocity automatically.
from rotating_habitat import Cylinder
cyl = Cylinder(0, 0.01)
data = cyl.throw_still([0.3, 0.1], [0.0, 0.0])from rotating_habitat import Cylinder
cyl = Cylinder(2, 0.01)
data = cyl.throw_ball(0.2, [0.8, 135])Both methods return a dictionary with:
data["x"]: x positions in the observer's rotating framedata["y"]: y positions in the observer's rotating frame
These can be plotted or animated however you like.
For a cylinder of radius R spinning at angular speed omega:
- Tangential speed is
v = omega * r - Apparent floor acceleration is
a = omega^2 * r
If the habitat is tuned for Earth-like apparent gravity at the hull:
omega^2 * R ~= 9.81 m/s^2
That immediately creates tradeoffs:
- Larger habitats can spin more slowly for the same apparent gravity
- Smaller habitats need higher RPM, which makes Coriolis effects much more noticeable
That is why ballistics inside small rotating habitats often look counterintuitive. Throws that feel "straight" to the person releasing them can curve strongly in the habitat frame.
- Single-file script rather than a packaged library
- No tests yet
- No parameterized CLI
- 2D cross-section only
- Impact handling stops at boundary crossing rather than solving the exact wall intersection time
This project is licensed under the MIT License. See LICENSE.