I see, so RevoluteConstraint wasn’t quite what I imagined - I guess what I imagined is closer to RevoluteJoint, where it keeps them a certain distance apart while allowing them both to rotate about the “connection” to the joint, while looks like RevoluteConstraint the second object’s center constant in the first object’s local space while allowing the second object to rotate?
Actually RevoluteConstraint
and RevoluteJoint
just implement two different mathematical model to simulate the same thing commonly used in robotics: revolute joints. They look like this (taken randomly from google image):
They are not, strictly speaking, constraints on the distance between the two bodies. It’s better seen as a joint that only allow one relative rotation between two bodies. The classical physics double pendulum is often modeled conceptually with revolute joints. And there are two ways to model a revolute joint mathematically:
- Using reduced coordinates, i.e., the position of both bodies are analytically determined by two angles (theta1 and theta2) and the position of the ground. It is called “reduced coordinates” because you solve a system of differential equations for only two variables (the two thetas). This is the model implemented by
RevoluteJoint
in nphysics. Is supsect that the simulation from myphysicslab uses this approach too.
- Using full coordinates, i.e. the position of both bodies are determined by a system of equations that constraint the degrees of freedoms of the two bodies. It is called “full coordinates” because you solve a system of differential equation with 6 variables in 2D (translation1, rotation1, translation2, rotation2) and 5 constraints that ensure the bodies don’t do any forbidden relative motion. This is the model implemented by
RevoluteConstraint
in nphysics.
Both models have their advantages and inconvenient. The reduced-coordinates approach is much more accurate because there is no way for the two bodies to achieve invalid position wrt. the joint. However, it can be much more computationally expensive for large joint system. The full-coordinates approach can result in constraints violation if the constraint solver does not converge (and this is where your “jiggly” problem comes from). However when there are lots of joints, the full-coordinates approach can be much more computationally efficient.
Additionally, looks like in the simulation the "string"s are very jiggly as opposed to for example this simulation where their length is absolutely constant. Is there a way to fix that? This is my current code.
There are a few issues here. First you are comparing two simulations with completely different inputs, so you can’t expect the same behavior numerically speaking! In your code, you are using huge objects. A good way to visualize things is to think that 1 unit in the physics world is 1 meter. In the simulation from myphysicslab, the balls are about 20cm wide. In your simulation, your are using balls of radius 20metters and your strings are 100m long. Because these values are so big, the constraints solver in nphysics will have a harder time to converge, hence the “jiggly” problem. You should use the same initial condition as in myphysicslab if you want to compare things.
Though maybe I understand why you did this: if you use smaller values, the 2D visualization becomes too small to understand. Here is some code that sets up a 2D camera in kiss3d (Sidescroll
) with a proper zoom to visualize a smaller scene. I also modified the FIRST_RADIUS and SECOND_RADIUS values to 1.0:
const FIRST_RADIUS: f32 = 1.0;
const SECOND_RADIUS: f32 = 1.0;
fn main() {
let mut window = Window::new("Double Pendulum");
window.set_light(Light::StickToCamera);
let mut camera = FirstPerson::new(Point3::new(0.0, -5.0, -0.0), Point3::new(0.0, -5.0, 0.0));
let mut planar_camera = Sidescroll::new();
planar_camera.set_zoom(100.0);
camera.set_up_axis(Vector3::new(0.0, 1.0, 0.0));
let pendulum1 = window.add_circle(0.1);
let pendulum2 = window.add_circle(0.1);
let mut model = Model {
world: create_world(2.0, 3.0),
pendulum1,
pendulum2,
};
while window.render_with_cameras(&mut camera, &mut planar_camera) {
for _ in 0..SIMULATION_SPEED_MULTIPLIER {
model.world.step();
}
model.draw(&mut window);
}
}
If you still think this is not accurate enough for your uses then you have two solutions:
- You can either increase the number of constraint resolution iteration:
mechanical_world.integration_parameters.max_velocity_iterations = 50
.
- Or you can use a multibody with
RevoluteJoint
(based on reduced-coordinates) instead of of using rigid bodies with RevoluteConstraint
(based on full-coordinates). The RevoluteJoint
approach will be much more accurate for the reasons mentioned earlier.