Sure. Here is my whole file (disregard any errors in the right turn, backwards movement, or the new left turn I’m currently making based on a PID):
#include <Pololu3piPlus32U4.h>
#include <Pololu3piPlus32U4IMU.h>
#include <Wire.h>
#include <PID_v1.h>
#define PI 3.141592
#define ENCODER_COUNTS_PER_REV 358.3 // encoder value for 1rev
using namespace Pololu3piPlus32U4;
OLED display;
ButtonB buttonB;
BumpSensors bumpSensors;
Motors motors;
Encoders encoders;
IMU imu;
#include "TurnSensor.h"
int16_t maxSpeed = 80;
int32_t turnSpeed = 50;
int32_t turn90Speed = 50;
// constants for PID
//double Kp = 1, Ki = -0.0002, Kd = 0.005;
double Kp = 1, Ki = -0.0002, Kd = 0;
boolean hasRun = false;
void moveForwardDistance(float distance_mm){
// TODO: perhaps add trap motion profile
int32_t acceleration = 10;
float mm_per_rev = PI * 32;
int32_t targetCounts = (distance_mm / mm_per_rev) * ENCODER_COUNTS_PER_REV;
int32_t turnAngleIntegral = 0;
//offset
//distance_mm -= 60;
encoders.getCountsAndResetLeft();
encoders.getCountsAndResetRight();
while (true){
// add acceleration and deceleration
int32_t leftCount = encoders.getCountsLeft();
int32_t rightCount = encoders.getCountsRight();
int32_t averageCount = (leftCount + rightCount) / 2;
if (averageCount >= targetCounts){
break;
}
turnSensorUpdate();
// add to integral term
// a RIGHT turn of 1 degree reads as -1 degrees **
turnAngleIntegral += -((int32_t)turnAngle/(float)turnAngle1);
int32_t turnSpeed = (int32_t)(Kp * (-(int32_t)turnAngle / ((float)turnAngle1)) + Kd*(-0.07 * turnRate) - Ki * (turnAngleIntegral));
//int32_t turnSpeed = 0;
Serial.print(-(int32_t)turnAngle / (float)(turnAngle1));
Serial.print(" ");
Serial.print(-0.07 * turnRate);
Serial.print(" ");
Serial.print(-maxSpeed);
Serial.print(" ");
Serial.println(turnAngleIntegral);
// see that these all must have the same signs
turnSpeed = constrain(turnSpeed, -maxSpeed, maxSpeed);
motors.setSpeeds((int16_t)maxSpeed-turnSpeed, (int16_t)maxSpeed+turnSpeed);
display.clear();
// display.println(turnSpeed);
display.println(-(int32_t)turnAngle/turnAngle1);
display.println((int16_t)turnSpeed);
}
//motors.setSpeeds(speed, speed);
motors.setSpeeds(0, 0);
}
void moveBackwardDistance(float distance_mm){
float mm_per_rev = PI * 32;
int32_t targetCounts = -1* (distance_mm / mm_per_rev) * ENCODER_COUNTS_PER_REV;
int32_t turnAngleIntegral = 0;
float acceleration = 0;
float currSpeed = 40;
int32_t counter = 0;
//distance_mm -= 60;
encoders.getCountsAndResetLeft();
encoders.getCountsAndResetRight();
while (true){
int32_t leftCount = encoders.getCountsLeft();
int32_t rightCount = encoders.getCountsRight();
int32_t averageCount = (leftCount + rightCount) / 2;
if (averageCount <= targetCounts){
break;
}
turnSensorUpdate();
turnAngleIntegral += -((int32_t)turnAngle/turnAngle1);
int32_t turnSpeed = Kp * (-(int32_t)turnAngle / (turnAngle1)) + Kd*(-0.07 * turnRate) - Ki * (turnAngleIntegral);
//turnSpeed = -1 * constrain(turnSpeed, -100, 100);
turnSpeed = -1 * constrain(turnSpeed, -maxSpeed, maxSpeed);
motors.setSpeeds(-1*((int32_t)maxSpeed-turnSpeed), -1* ((int32_t)maxSpeed+turnSpeed));
display.clear();
//display.println(-(int32_t)turnAngle/turnAngle1);
display.println(averageCount);
display.println(targetCounts);
}
motors.setSpeeds(0, 0);
}
void turn_right_90()
{
// TODO: perhaps add trap motion profile
//TODO: refine turn angles
display.clear();
encoders.getCountsAndResetLeft();
encoders.getCountsAndResetRight();
//turnSensorReset();
motors.setSpeeds(0,0);
float speed = 0;
float turnAngleDegrees = 0;
while (encoders.getCountsLeft() < 237 && encoders.getCountsRight() > -237){
motors.setSpeeds(turn90Speed, -turn90Speed);
turnSensorUpdate();
display.clear();
display.print(turnAngle/turnAngle1);
}
motors.setSpeeds(0,0);
turnSensorUpdate();
display.clear();
display.print(turnAngle/turnAngle1);
//turnAngle = turnAngle1*(turnAngle/turnAngle1 - 270);
display.clear();
display.print(-(int32_t)turnAngle/turnAngle1);
turnSensorReset();
// while (true){
// display.clear();
// display.print(-(int32_t)turnAngle/turnAngle1);
// turnSensorUpdate();
// }
}
void turn_left_90()
{
display.clear();
encoders.getCountsAndResetLeft();
encoders.getCountsAndResetRight();
//turnSensorReset();
motors.setSpeeds(0,0);
display.print(-(int32_t)turnAngle/(float)turnAngle1);
delay(500);
while (encoders.getCountsRight() < 235 && encoders.getCountsLeft() > -235){
motors.setSpeeds(-turn90Speed, turn90Speed);
turnSensorUpdate();
}
motors.setSpeeds(0,0);
turnSensorUpdate();
display.clear();
//turnAngle = 1 * (turnAngle90 -(int32_t)turnAngle);
display.print(-(int32_t)turnAngle/float(turnAngle1));
turnSensorReset();
// while (true){
// display.clear();
// //display.gotoXY(0,1);
// display.print(-(int32_t)turnAngle/float(turnAngle1));
// turnSensorUpdate();
// delay(5);
// }
}
void new_turn_left_90()
{
display.clear();
encoders.getCountsAndResetLeft();
encoders.getCountsAndResetRight();
//turnSensorReset();
motors.setSpeeds(0,0);
float speed = 0;
float relative_heading = 0;
float turnAngleDegrees = 0;
float time = millis();
double Setpoint, Input, Output;
//double Kp_turn = 5, Ki_turn = 2, Kd_turn = 1;
//double Kp_turn = 3, Ki_turn = 1.5, Kd_turn = 0;
double Kp_turn = 5, Ki_turn = 0.02, Kd_turn = 0;
PID myPID(&Input, &Output, &Setpoint, Kp_turn, Ki_turn, Kd_turn, DIRECT);
Setpoint = turnAngle90 / turnAngle1;
myPID.SetMode(AUTOMATIC);
myPID.SetOutputLimits(-maxSpeed, maxSpeed);
while (millis() - time < 3000){
//PID control for turn
Input = turnAngle / turnAngle1;
myPID.Compute();
// prevent outputs from -20 to 20
if (Output > -20 && Output < 20) {
if (Output < 0)
Output -= 20;
else
Output += 20;
}
motors.setSpeeds(-Output, Output);
turnSensorUpdate();
display.clear();
display.print(turnAngle / turnAngle1);
display.gotoXY(0, 1);
display.print(Output);
}
// while (abs(turnAngle90 - turnAngle) > 2 * turnAngle1){
// speed = 100*relative_heading/180;
// relative_heading = (turnAngle90 - turnAngle) /turnAngle1;
// Serial.println(relative_heading);
// if (speed < 0)
// speed -= 25;
// else
// speed += 25;
// // if heading is negative, turn right
// // if heading is positive, turn left
// // if (350>relative_heading > 180)
// // motors.setSpeeds(speed, -speed);
// // else{
// // motors.setSpeeds(-speed, speed);
// // }
// // FIX ABOVE
// motors.setSpeeds(-speed, speed);
// turnSensorUpdate();
// display.clear();
// display.print(turnAngle/turnAngle1);
// }
//delay(500);
turnSensorUpdate();
display.clear();
display.print(turnAngle/turnAngle1);
motors.setSpeeds(0,0);
turnSensorUpdate();
turnAngle = 1*(turnAngle90 -(int32_t)turnAngle);
//turnSensorReset();
display.clear();
display.print(-(int32_t)turnAngle/(float)turnAngle1);
//turnAngle = (turnAngle1*(90-(int32_t)turnAngle/turnAngle1));
//turnAngle = (int32_t)(turnAngle90 -(int32_t)turnAngle)/float(turnAngle1)
// while (true){
// display.clear();
// //turnAngle = turnAngle90 -(int32_t)turnAngle;
// display.print(-(int32_t)turnAngle/(float)turnAngle1);
// display.gotoXY(0,1);
// display.print((turnAngle90 -(int32_t)turnAngle)/(float)turnAngle1);
// turnSensorUpdate();
// }
//int32_t turnAngleNew = 90*turnAngle1 -(int32_t)turnAngle;
//display.print(-(int32_t)turnAngle/turnAngle1);
//turnSensorReset();
while (true){
display.clear();
display.print(-(int32_t)turnAngle/float(turnAngle1));
display.gotoXY(0,1);
display.print((int32_t)(turnAngle90 -(int32_t)turnAngle)/float(turnAngle1));
//display.print((int32_t)(turnAngle1*(90-(int32_t)turnAngle/turnAngle1))/turnAngle1);
turnSensorUpdate();
delay(5);
}
}
void setup(){
display.clear();
display.print(F("Press B"));
while(!buttonB.getSingleDebouncedPress());
//delay(1000);
turnSensorSetup();
turnSensorReset();
display.clear();
display.print(F("Running..."));
}
void loop(){
// TODO: see if the starting lag will be an issue
int32_t curr_time = millis();
//delay(500);
if (!hasRun){
moveForwardDistance(500);
delay(500);
turn_left_90();
delay(500);
moveForwardDistance(500);
delay(500);
turn_left_90();
delay(500);
moveForwardDistance(500);
delay(500);
turn_left_90();
delay(500);
moveForwardDistance(500);
delay(500);
turn_left_90();
delay(500);
hasRun = true;
}
}
I also use the turnSensor.h file provided by pololu with a longer calibration time:
#include <stdint.h>
// Turnsensor.h provides functions for configuring the
// 3pi+ 32U4's gyro, calibrating it, and using it to
// measure how much the robot has turned about its Z axis.
//
// This file should be included *once* in your sketch,
// somewhere after you define objects named buttonA,
// display, and imu.
const int32_t calval = 5000;
#include <Wire.h>
// This constant represents a turn of 45 degrees.
const int32_t turnAngle45 = 0x20000000;
// This constant represents a turn of 90 degrees.
const int32_t turnAngle90 = turnAngle45 * 2;
// This constant represents a turn of approximately 1 degree.
const int32_t turnAngle1 = (turnAngle45 + 22) / 45;
/* turnAngle is a 32-bit unsigned integer representing the amount
the robot has turned since the last time turnSensorReset was
called. This is computed solely using the Z axis of the gyro, so
it could be inaccurate if the robot is rotated about the X or Y
axes.
Our convention is that a value of 0x20000000 represents a 45
degree counter-clockwise rotation. This means that a uint32_t
can represent any angle between 0 degrees and 360 degrees. If
you cast it to a signed 32-bit integer by writing
(int32_t)turnAngle, that integer can represent any angle between
-180 degrees and 180 degrees. */
uint32_t turnAngle = 0;
// turnRate is the current angular rate of the gyro, in units of
// 0.07 degrees per second.
int16_t turnRate;
// This is the average reading obtained from the gyro's Z axis
// during calibration.
int16_t gyroOffset;
// This variable helps us keep track of how much time has passed
// between readings of the gyro.
uint16_t gyroLastUpdate = 0;
// This should be called to set the starting point for measuring
// a turn. After calling this, turnAngle will be 0.
void turnSensorReset()
{
gyroLastUpdate = micros();
turnAngle = 0;
}
// Read the gyro and update the angle. This should be called as
// frequently as possible while using the gyro to do turns.
void turnSensorUpdate()
{
// Read the measurements from the gyro.
imu.readGyro();
turnRate = imu.g.z - gyroOffset;
// Figure out how much time has passed since the last update (dt)
uint16_t m = micros();
uint16_t dt = m - gyroLastUpdate;
gyroLastUpdate = m;
// Multiply dt by turnRate in order to get an estimation of how
// much the robot has turned since the last update.
// (angular change = angular velocity * time)
int32_t d = (int32_t)turnRate * dt;
// The units of d are gyro digits times microseconds. We need
// to convert those to the units of turnAngle, where 2^29 units
// represents 45 degrees. The conversion from gyro digits to
// degrees per second (dps) is determined by the sensitivity of
// the gyro: 0.07 degrees per second per digit.
//
// (0.07 dps/digit) * (1/1000000 s/us) * (2^29/45 unit/degree)
// = 14680064/17578125 unit/(digit*us)
turnAngle += (int64_t)d * 14680064 / 17578125;
}
/* This should be called in setup() to enable and calibrate the
gyro. It uses the display, yellow LED, and button A. While the
display shows "Gyro cal", you should be careful to hold the robot
still.
The digital zero-rate level of the gyro can be as high as
25 degrees per second, and this calibration helps us correct for
that. */
void turnSensorSetup()
{
Wire.begin();
imu.init();
imu.enableDefault();
imu.configureForTurnSensing();
display.clear();
display.print(F("Gyro cal"));
// Turn on the yellow LED in case the display is not available.
ledYellow(1);
// Delay to give the user time to remove their finger.
delay(500);
// Calibrate the gyro.
int32_t total = 0;
for (uint16_t i = 0; i < calval; i++)
{
// Wait for new data to be available, then read it.
while(!imu.gyroDataReady()) {}
imu.readGyro();
// Add the Z axis reading to the total.
total += imu.g.z;
}
ledYellow(0);
gyroOffset = total / calval;
// // Display the angle (in degrees from -180 to 180) until the
// // user presses A.
// display.clear();
// turnSensorReset();
// while (!buttonA.getSingleDebouncedRelease())
// {
// turnSensorUpdate();
// display.gotoXY(0, 0);
// display.print((((int32_t)turnAngle >> 16) * 360) >> 16);
// display.print(F(" "));
// }
// display.clear();
}