Okapilib turning incorrect angle

When using PROS okapilib’s built in 3-wheel odometry chassis controller, calling a turnAngle function produces a turn approximately half the rotation specified. I don’t think that the controller is even really using the tracking wheels for turning properly anyway so any help with the code would be appreciated. The drive is geared 60:36 on 3.25" omnis. For example, the following code is set to turn -135 degrees during the program but actually turns about 90 degrees. Some parts such as the 2d motion profiling controller are commented out. Sorry about the rushed post.

#include "main.h"
#include "okapi/api.hpp"
using namespace okapi;

std::shared_ptr<OdomChassisController> chassis = ChassisControllerBuilder()

.withMotors(1, -4, 3, -2)
.withDimensions(AbstractMotor::gearset::green, {{4_in, 212_mm}, 665})
.withSensors
(
	ADIEncoder{'A', 'B'},
	ADIEncoder{'E', 'F', true},
	ADIEncoder{'C', 'D'}
)
.withOdometry({{2.75_in, 212_mm, 214_mm, 2.75_in}, quadEncoderTPR})
.buildOdometry();

void initialize() {}

void disabled() {}

void competition_initialize() {}

void autonomous()
{

	chassis->setState({0_in, 0_in, 0_deg});

	pros::Motor br (7, pros::E_MOTOR_GEARSET_06, true);
	pros::Motor tr (9, pros::E_MOTOR_GEARSET_06, false);

	pros::Motor il (11, pros::E_MOTOR_GEARSET_06, false);
	pros::Motor ir (12, pros::E_MOTOR_GEARSET_06, true);

	pros::Optical bo (19);
	pros::Optical to (18);



/*
	auto profile1 = AsyncMotionProfileControllerBuilder()

	.withLimits({1.00, 1.00, 5.00})
	.withOutput(chassis)
  .buildMotionProfileController();

	profile1->generatePath({
  {0_ft, 0_ft, 0_deg},
  {3_ft, 0_ft, 0_deg}},
  "A");*/

	int timeout = 0;

	tr.move(127);
	il.move(127);
	ir.move(127);
	chassis->setMaxVelocity(100);
	chassis->moveDistance(23_in);
	tr.move(0);
	br.move(127);
	pros::delay(200);
	br.move(0);
	chassis->turnAngle(-135_deg);
	il.move(0);
	ir.move(0);
	chassis->moveDistance(24_in);
	//chassis->waitUntilSettled();
	il.move(127);
	ir.move(127);
	tr.move(127);
	br.move(127);
	for (int i = 0; i < 2; i++)
	{
	timeout = pros::millis() + 1500;
	while ((to.get_hue() < 80 || to.get_proximity() < 180) && (pros::millis() < timeout))
	{
		pros::delay(10);
	}
	tr.move(-127);
	timeout = pros::millis() + 1500;
	while ((to.get_hue() > 120 && to.get_proximity() > 180) && (pros::millis() < timeout))
	{
		pros::delay(10);
	}
	}

	il.move(0);
	ir.move(0);
	tr.move(0);
	br.move(0);

}

void opcontrol()
{
	pros::Motor lf (1, pros::E_MOTOR_GEARSET_18, false);
	pros::Motor lb (2, pros::E_MOTOR_GEARSET_18, true);
	pros::Motor rb (3, pros::E_MOTOR_GEARSET_18, false);
	pros::Motor rf (4, pros::E_MOTOR_GEARSET_18, true);

	pros::Motor br (7, pros::E_MOTOR_GEARSET_06, true);
	pros::Motor tr (9, pros::E_MOTOR_GEARSET_06, false);

	pros::Motor il (11, pros::E_MOTOR_GEARSET_06, false);
	pros::Motor ir (12, pros::E_MOTOR_GEARSET_06, true);

	pros::Optical bo (19);
	pros::Optical to (18);

	lf.set_brake_mode(pros::E_MOTOR_BRAKE_COAST);
	lb.set_brake_mode(pros::E_MOTOR_BRAKE_COAST);
	rf.set_brake_mode(pros::E_MOTOR_BRAKE_COAST);
	rb.set_brake_mode(pros::E_MOTOR_BRAKE_COAST);

	pros::Controller master (pros::E_CONTROLLER_MASTER);

	int stick_thresh = 5;

	tr.move(127);
	pros::delay(250);
	tr.move(0);

	while (true)
	{

		if (abs(master.get_analog(pros::E_CONTROLLER_ANALOG_LEFT_Y)) > stick_thresh)
		{
			lf = (master.get_analog(pros::E_CONTROLLER_ANALOG_LEFT_Y));
			lb = (master.get_analog(pros::E_CONTROLLER_ANALOG_LEFT_Y));
		}
		else
		{
			lf = 0;
			lb = 0;
		}

		if (abs(master.get_analog(pros::E_CONTROLLER_ANALOG_RIGHT_Y)) > stick_thresh)
		{
			rf = (master.get_analog(pros::E_CONTROLLER_ANALOG_RIGHT_Y));
			rb = (master.get_analog(pros::E_CONTROLLER_ANALOG_RIGHT_Y));
		}
		else
		{
			rf = 0;
			rb = 0;
		}

		if (master.get_digital(pros::E_CONTROLLER_DIGITAL_R1) == 1)
		{
			il = 127;
			ir = 127;
		}
		else if (master.get_digital(pros::E_CONTROLLER_DIGITAL_B) == 1)
		{
			il = -127;
			ir = -127;
		}
		else
		{
			il = 0;
			ir = 0;
		}


		if (master.get_digital(pros::E_CONTROLLER_DIGITAL_L1) == 1)
		{
			tr = 127;
			br = 127;
		}
		else if (master.get_digital(pros::E_CONTROLLER_DIGITAL_L2) == 1)
		{
			tr = -127;
		}
		else if (master.get_digital(pros::E_CONTROLLER_DIGITAL_R2) == 1)
		{
			if (bo.get_proximity() > 150 || to.get_proximity() > 150)
			{
				tr = 30;
				br = 50;
			}
			else
			{
				//tr = -50;
			}
		}
		else
		{
			tr = 0;
		}

		if (master.get_digital(pros::E_CONTROLLER_DIGITAL_R2) == 1 || master.get_digital(pros::E_CONTROLLER_DIGITAL_L2) == 1)
		{
			br = 127;
		}
		else
		{
			br = 0;
		}

		if (to.get_hue() > 120 && to.get_proximity() > 180)
		{
			tr = -127;
			br = 127;
			pros::delay(50);
		}


		pros::lcd::set_text(0, std::to_string(to.get_hue()));
		pros::lcd::set_text(1, std::to_string(to.get_proximity()));

		pros::delay(10);
	}
}

This declaration type is for an X-Drive/Mecanum. If your robot is Tank/Skid please try:

.withMotors({1,-4},{3,-2})

Not sure that this will fix the problem. What version of OkapiLib are you using?

1 Like

I’m having a similar problem. I haven’t spent much time working on my robot since last competition. I’m only using 2 tracking wheels.

This line is also suspicious. What does the number 665 represent?

Based on:

https://okapilib.github.io/OkapiLib/md_docs_tutorials_walkthrough_chassisControllerBuilder.html

I would expect that to be something akin to, especially if you are using 3.25" wheels and not 4" wheels:

.withDimensions(AbstractMotor::gearset::green,  {{3.25_in, 212_mm}, imev5GreenTPR * (36.0 / 60.0)})
2 Likes

That number might be the result of imev5GreenTPR * (36.0 / 60.0) and he just put the number in by hand.

This is what’s known as a “magic number”. The code doesn’t document what the number means. Given that the value of imev5GreenTPD is 900, it is not 900 * 36 / 60. This is why commenting code, or spelling things out is important.

If the thought process to hard-coding this number is to “make the code run faster”, modern compilers tend to optimize calculations between constants for the programmer.

3 Likes

Yes, this value is the number of ticks per wheel revolution. Using 4_in for 3.25" omnis probably didn’t help, good point! However, this wouldn’t account for the degree of error that I am seeing. The tracking wheels should also account for this, right? I will try this on Monday.

I’m pretty sure that Odometry wheels are only used to track position. All movements are set in terms of motor rotations and chassis dimensions. So even when you use turnToPoint and driveToPoint the chassis uses the current odometry position (as computed by the tracking wheels in your case) and calculates how much the motors should move based on the chassis wheels and dimensions.

TL;DR - yes, it is probable that the wheel size substantially accounts for your turning error. If it doesn’t, take a look at your ticks per wheel revolution. I’m not sure what combination of 36, 60, 360 (degrees in a circle), or 900 (the value of imev6GreenTPR) yields 665.

1 Like

I have updated the line to this:
.withDimensions(AbstractMotor::gearset::green, {{3.25_in, 373_mm}, imev5GreenTPR * (60 / 36)})
However the turns are now around 70 degrees to every 90!

The distance between the two drive sides is 373mm and we are using 3.25" omnis. The gearing is 60 to 36 with 200rpm green cartridges.

It’s possible there is a bug with OkapiLib as this may be similar to:

What happens if you change it to .withDimensions(AbstractMotor::gearset::green, {{3.25_in, 373_mm}, imev5GreenTPR * (36.0 / 60.0)})

You may also want to explicitly use (60.0 / 36.0) in your as well to ensure that it doesn’t treat it as integer division instead of floating point.

1 Like

I am not in a position to test this right now, however, is there any way of getting around this bug, eg. with the gearset ratio pair structure within okapilib? Thanks for the response.
image

@1408F I believe you must put the simplified ratio for

ChassisControllerBuilder()
.withMotors(
{1, -4}, // Left motors are 1 & 4
{3, -2} // Right motors are 3 & 2
.withDimensions(AbstractMotor::gearset::green, {{4_in, 11.5_in}, imev5GreenTPR * (5.0 / 3.0)})
.withSensors
(
ADIEncoder{‘A’, ‘B’},
ADIEncoder{‘E’, ‘F’, true},
ADIEncoder{‘C’, ‘D’}
.withOdometry({{2.75_in, 212_mm, 214_mm, 2.75_in}, quadEncoderTPR})
.buildOdometry();

note I may be wrong as i have never used okapi
also have you tried making it a pid loop using the built in okapi controller

.withGains(
{0.001, 0, 0.0001}, // Distance controller gains
{0.001, 0, 0.0001}, // Turn controller gains
{0.001, 0, 0.0001} // Angle controller gains (helps drive straight)
)

Since (60/36) is just an expression, it should yield the same result (0.6) as far as I’m aware (apart from the integer division possible issue). As for a custom PID controller, I’m not sure this is necessary as the built in gains are fairly well optimized for a standard chassis. Nevertheless, you may be right and thanks for the response!

hey you used 373 mm which is 14.685 which is impossible with the vex system
Wheel track means the shortest distance between the center of the tire treads on the same axle. On vehicles having dissimilar axle widths, the axle with the widest distance is used for all calculations.
try to remeasure

image

that track is not possible

Can you explain why, because I can see for myself that the track is correct (I’m using a ruler)?

The vex system is made to use .25 .5 and .75 nothing else the only way you did that is if you have weird washers try 14.5 in or 15

Sorry but this doesn’t really make any sense.

Slightly vexed rant below, if you dare (not meant to be offensive).

rant

Have you ever used spacers or collars? Have you ever screwed two c-channels together and realised that the spacing is out because the metal is strangely not infinitely thin? Did you realise that omnis are actually 1.125" thick? 1.125 / 2 (eg. the centre) = 0.5625!

The vex system as you say, uses components centred around these measurements, however, chassis measurements such as wheel to wheel distance vary greatly since wheels do not have to be in a set position on an axel. Thank you for your input regardless, this issue is solved now, I will post the solution soon.

2 Likes