Skip to content

Angle hiccups when rotation wraps +-PI, and other rotational musings. #331

@ninnghazad

Description

@ninnghazad

Hey @louis-langholtz ,
i have a problem with spurious hiccups in rotation when the angle goes from/to +PI / -PI.
Wrote a GravityJoint, which pulls bodies together. Now i want to rotate the second body to be upright wrt the direction of gravity towards the first body.
In a test a small and a big disk are connected using this joint, and the small disk slides around the surface of the big disk. (see code below)
This seemingly works, however on some rotations, when the small disk passes the point where it's angle flips from +PI to -PI the body has wrong angle values for a frame or two.
The location is not affected, only the rotation. This does not happen every time, but very often.

Console output, see how when the angle wraps, even if the joint sets 3.1258, after Step() the body has a rotation of -1.5201 instead of 3.1258 for a single step, and then continues with the correct values set by the joint in the next step:

Main loop: angle: -3.1135 location: 0.0631x-10.2480
Inside GravityJoint: angle: -3.1354  impulse: -0.0221x3.5819 slop: 0.0349 err: 0.0219  body angle: -3.1354
Main loop: angle: -3.1354 location: -0.1615x-10.2494
Inside GravityJoint: angle: 3.1258  impulse: 0.0564x3.5798 slop: 0.0349 err: 6.2613  body angle: 3.1258
Inside GravityJoint: angle: 3.1258  impulse: 0.0564x3.5798 slop: 0.0349 err: 0.0000  body angle: 3.1258
Main loop: angle: -1.5201 location: -0.3861x-10.2385
Inside GravityJoint: angle: 3.1039  impulse: 0.1351x3.5812 slop: 0.0349 err: 4.6240  body angle: 3.1039
Inside GravityJoint: angle: 3.1039  impulse: 0.1351x3.5812 slop: 0.0349 err: 0.0000  body angle: 3.1039
Main loop: angle: 3.1039 location: -0.6108x-10.2300

A test using said joint:

//g++ -std=c++17 -O3 -march=native -Isubmodules/PlayRho ideas_and_tests/cpp_playrho_gravity.cpp -o /tmp/test libs/libPlayRho.a

#include <PlayRho/PlayRho.hpp>
#include <iostream>
#include <iomanip>

using namespace playrho;
using namespace playrho::d2;

int main()
{
	auto world = World{};
	std::cout << std::fixed << std::setprecision(4);

	const auto planet = world.CreateBody(BodyConf{}
		.UseLocation(Length2{0, 0})
		.UseType(BodyType::Static));

	planet->CreateFixture(Shape{DiskShapeConf{}
		.UseRadius(1_m*10)
		.UseFriction(10.0)
		.UseRestitution(0)
		.UseDensity(0.0)
	});

	auto location = Length2{1_m*10+.25,0};
	auto body = world.CreateBody(BodyConf{}
		.UseLocation(location)
		.UseType(BodyType::Dynamic));
	body->CreateFixture(Shape{DiskShapeConf{}
		.UseLocation(Length2{0,0})
		.UseRadius(1_m*0.25)
		.UseFriction(0.0)
		.UseRestitution(0)
		.UseDensity(1.0)
	});
	body->SetFixedRotation(true);

	GravityJointConf def{};
	def.bodyA = planet;
	def.bodyB = body;
	def.factor = 2000;
	def.radius = 50;
	auto joint = world.CreateJoint(def);

	auto dt = playrho::Second / 50.0;
	StepConf stepConfWarm;
	stepConfWarm.SetTime(dt);
	stepConfWarm.doWarmStart = true;


	ApplyLinearImpulse(*body,Length2{0,-2},body->GetWorldCenter());

	bool flip{false};
	std::size_t i{0},abrt{0};
	while(++i < 100000) {
		world.Step(stepConfWarm);
		std::cout << "Main loop: angle: " << body->GetAngle() << " location: " << body->GetLocation()[0] << "x" << body->GetLocation()[1] << std::endl;

		// Abort 4 frames after error detected
		if(!abrt) {
			if(flip && std::abs(body->GetAngle()) < 2.7) ++abrt;
			else if(std::abs(body->GetAngle()) > 3) flip = true;
			else flip = false;
		} else {
			if(++abrt > 4) return 1;
		}
	}
	return 0;
}

Setting rotation in joint solver:

bool GravityJoint::SolvePositionConstraints(BodyConstraintsMap& bodies, const ConstraintSolverConf& conf) const
{
	auto& bodyConstraintB = At(bodies, GetBodyB());
	auto posB = bodyConstraintB->GetPosition();

	// get direction of gravitational force from impulse
	const auto targetU = UnitVec::Get(m_impulse[0],m_impulse[1]).first;

	const auto targetAngle = GetAngle(targetU.Rotate(UnitVec::GetTop()));
	const auto err = abs(posB.angular - targetAngle);

	posB.angular = targetAngle;
	bodyConstraintB->SetPosition(posB);

	std::cout << "Inside GravityJoint: angle: " << targetAngle << " "
	<< " impulse: " << m_impulse[0] << "x" << m_impulse[1]
	<< " slop: " << conf.angularSlop << " err: " << err << " "
	<< " body angle: " << bodyConstraintB->GetPosition().angular
	<< std::endl;
	return err < conf.angularSlop;
}

Do you have any idea what i am doing wrong here?
This has me clueless.

The full source for the GravityJoint can be found here https://github.com/ninnghazad/PlayRho/tree/gravity_joint .

Metadata

Metadata

Labels

BugFor issues or changes that describe or fix bugs.LibraryFor issues that effect the library and aren't specific to any particular application.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions