From cab78410a434b2e1d2db2744ff3edcf72b541381 Mon Sep 17 00:00:00 2001 From: ParkerTenBroeck <51721964+ParkerTenBroeck@users.noreply.github.com> Date: Wed, 16 Apr 2025 00:12:49 -0400 Subject: [PATCH] changed things --- doc/doc.pdf | Bin 0 -> 30734 bytes robot/{distance.h => distance.cpp} | 4 +- robot/encoder.cpp | 82 +++++++++++++++++++ robot/encoder.h | 50 ------------ robot/{gyro.h => gyro.cpp} | 22 +++--- robot/headers.h | 114 +++++++++++++++++++++++++++ robot/{pid.h => pid.cpp} | 15 ++-- robot/robot.ino | 57 ++++++++------ robot/{webserver.h => webserver.cpp} | 57 ++++++-------- 9 files changed, 269 insertions(+), 132 deletions(-) create mode 100644 doc/doc.pdf rename robot/{distance.h => distance.cpp} (79%) create mode 100644 robot/encoder.cpp delete mode 100644 robot/encoder.h rename robot/{gyro.h => gyro.cpp} (91%) create mode 100644 robot/headers.h rename robot/{pid.h => pid.cpp} (89%) rename robot/{webserver.h => webserver.cpp} (82%) diff --git a/doc/doc.pdf b/doc/doc.pdf new file mode 100644 index 0000000000000000000000000000000000000000..8dfc8a55776ace2f7946c4f66d5472be6502fd46 GIT binary patch literal 30734 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.0; + + posInput = -posInput; + } +// Serial.println(desiredYaw); + + odom.x += (float)(cos(ang)*displacement); + odom.y += (float)(sin(ang)*displacement); +} diff --git a/robot/encoder.h b/robot/encoder.h deleted file mode 100644 index 139fccc..0000000 --- a/robot/encoder.h +++ /dev/null @@ -1,50 +0,0 @@ - - -struct Encoder{ - int rotations; - int currentAngle; - int estAngle; - bool threashhold; - - float position(){ - return rotations + (estAngle / 360.0); - } -}; - -Encoder encoder; - - -void initEncoder(){ - pinMode(A0, INPUT); - encoder.currentAngle = 0; - encoder.rotations = 0; - encoder.estAngle=0; - encoder.threashhold=false; -} - -void updateEncoder(){ - int rawIn = analogRead(A0); -// static long lastRan = 0; -// if(50>millis()-lastRan) return; -// lastRan = millis(); - - - int rotation = map(rawIn, 4, 348, 0, 360); - int diff = rotation-encoder.currentAngle; - if(!encoder.threashhold && (abs(diff)>=50)){ - encoder.threashhold=true; - if(diff<0){ - encoder.rotations++; - encoder.estAngle = 0; - } - if(diff>0){ - encoder.rotations--; - encoder.estAngle = 360; - } - } - if(abs(diff)<30){ - encoder.threashhold=false; - encoder.estAngle = rotation; - } - encoder.currentAngle = rotation; -} diff --git a/robot/gyro.h b/robot/gyro.cpp similarity index 91% rename from robot/gyro.h rename to robot/gyro.cpp index 3f35628..f7999ee 100644 --- a/robot/gyro.h +++ b/robot/gyro.cpp @@ -1,12 +1,8 @@ +#include "headers.h" #include "MPU6050_6Axis_MotionApps612.h" //#include "MPU6050.h" // not necessary if using MotionApps include file - -#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE -#include "Wire.h" -#endif - -MPU6050 mpu; +MPU6050 mpu(MPU6050_ADDRESS_AD0_LOW, &Wire1); // MPU control/status vars bool dmpReady = false; // set true if DMP init was successful @@ -54,6 +50,8 @@ void initGyro(){ Serial.println(F("Initializing DMP...")); devStatus = mpu.dmpInitialize(); +// mpu.setRate(0); + // supply your own gyro offsets here, scaled for min sensitivity mpu.setXAccelOffset(-6009); @@ -102,17 +100,15 @@ void initGyro(){ bool updateGyro(){ if (!dmpReady) return false; if (mpu.dmpGetCurrentFIFOPacket(fifoBuffer)) { -// Xf = 1/4*(aaRealLast.x + aaReal.x)t^2 + Vot + Xo -// aaRealLast = aaReal; mpu.dmpGetQuaternion(&q, fifoBuffer); - mpu.dmpGetAccel(&aa, fifoBuffer); - mpu.dmpGetGyro(&gy, fifoBuffer); +// mpu.dmpGetAccel(&aa, fifoBuffer); +// mpu.dmpGetGyro(&gy, fifoBuffer); mpu.dmpGetGravity(&gravity, &q); mpu.dmpGetYawPitchRoll(ypr, &q, &gravity); - mpu.dmpGetEuler(euler, &q); - mpu.dmpGetLinearAccel(&aaReal, &aa, &gravity); - mpu.dmpGetLinearAccelInWorld(&aaWorld, &aaReal, &q); +// mpu.dmpGetEuler(euler, &q); +// mpu.dmpGetLinearAccel(&aaReal, &aa, &gravity); +// mpu.dmpGetLinearAccelInWorld(&aaWorld, &aaReal, &q); return true; } diff --git a/robot/headers.h b/robot/headers.h new file mode 100644 index 0000000..3dd8ba2 --- /dev/null +++ b/robot/headers.h @@ -0,0 +1,114 @@ + +#define LOOP_INTERVAL_MS 20 + +struct FVec2{ + float x,y; +}; +struct FVec3{ + float x,y,z; +}; +struct FVec4{ + float x,y,z; +}; + +extern FVec2 desiredPos; + +struct Everything{ + float motorTargetAngle; + float position; + FVec3 anglePID; + FVec3 posPID; + FVec3 ypr; + FVec3 euler; + FVec3 gravity; + FVec4 q; + FVec3 aa; + FVec3 gy; + FVec3 aaReal; + FVec3 aaWorld; +}; + +extern float angleOffset; +extern float desiredYaw; +extern float currentYaw; + +struct DebugState{ + int motorTargetAngle; +}; + +extern DebugState dbgState; + +//-------- wire +#include +#define Wire1 Wire +#define Wire2 Wire + +void wire1(); +void wire2(); + +//-------- gyro +#include "MPU6050_6Axis_MotionApps612.h" + +extern Quaternion q; // [w, x, y, z] quaternion container +extern VectorInt16 aa; // [x, y, z] accel sensor measurements +extern VectorInt16 gy; // [x, y, z] gyro sensor measurements +extern VectorInt16 aaReal; // [x, y, z] gravity-free accel sensor measurements +extern VectorInt16 aaRealLast; // [x, y, z] gravity-free accel sensor measurements +extern VectorInt16 aaWorld; // [x, y, z] world-frame accel sensor measurements +extern VectorFloat gravity; // [x, y, z] gravity vector +extern float euler[3]; // [psi, theta, phi] Euler angle container +extern float ypr[3]; // [yaw, pitch, roll] yaw/pitch/roll container and gravity vector + +void initGyro(); +bool updateGyro(); + +//-------- distance +extern float distanceReading; + +void initDistance(); +void updateDistance(); + +//-------- encoder + +struct EncoderOdom{ + float x; + float y; + float angle; + + float left; + float right; +}; + + +extern EncoderOdom odom; + +void zeroOdom(); +void initEncoder(); +void updateEncoder(); + +//--------- pid +#include +#define PID_ARR_COUNT 3 +extern PID* pids[]; + +extern double angleInput, angleOutput, angleSetpoint; +extern PID anglePID; + +extern double posInput, posOutput, posSetpoint; +extern PID posPID; + +extern double turnInput, turnOutput, turnSetpoint; +extern PID turnPID; + +struct Speeds{ + float left; + float right; +}; + +Speeds updatePID(); +void initPID(); + +//--------- webserver + +void initWifi(bool host); +void initServer(); diff --git a/robot/pid.h b/robot/pid.cpp similarity index 89% rename from robot/pid.h rename to robot/pid.cpp index c7bddb1..7f9791e 100644 --- a/robot/pid.h +++ b/robot/pid.cpp @@ -1,3 +1,4 @@ +#include "headers.h" #include //double angKp=3.5, angKi=80, angKd=0.042; @@ -17,9 +18,9 @@ double posInput, posOutput, posSetpoint; PID posPID(&posInput, &posOutput, &posSetpoint, posKp, posKi, posKd, P_ON_E, DIRECT); double turnInput, turnOutput, turnSetpoint; -PID turnPID(&turnInput, &turnOutput, &turnSetpoint, turnKp, turnKi, turnKd, P_ON_E, REVERSE); +PID turnPID(&turnInput, &turnOutput, &turnSetpoint, turnKp, turnKi, turnKd, P_ON_E, DIRECT); -PID* pids[] = {&anglePID, &posPID, &turnPID}; +PID* pids[PID_ARR_COUNT] = {&anglePID, &posPID, &turnPID}; void initPID(){ angleSetpoint = 0; @@ -39,11 +40,7 @@ void initPID(){ turnPID.SetSampleTime(5); } -struct Speeds{ - float left; - float right; -}; - + Speeds updatePID(){ posPID.Compute(); angleSetpoint = posOutput; @@ -51,8 +48,8 @@ Speeds updatePID(){ float maxTurn = max(0.0f, 15.0f-abs((float)angleOutput)); turnPID.SetOutputLimits(-maxTurn, maxTurn); - turnSetpoint = desiredYaw; - turnInput = currentYaw; + turnSetpoint = 0; + turnInput = fmod(currentYaw-desiredYaw, 180); turnPID.Compute(); Speeds speeds; diff --git a/robot/robot.ino b/robot/robot.ino index 51850a6..f4feb7d 100644 --- a/robot/robot.ino +++ b/robot/robot.ino @@ -1,5 +1,16 @@ -#include "I2Cdev.h" + +#include "headers.h" + +void wire1(){ + Wire1.begin(SDA, SCL); + Wire1.setClock(400000); +} + +void wire2(){ + Wire1.begin(D7, D5); + Wire2.setClock(400000); +} #include @@ -10,19 +21,10 @@ float angleOffset = -2.4241745; float desiredYaw = 0.0; float currentYaw = 0.0; -struct DebugState{ - int motorTargetAngle; -}; +FVec2 desiredPos; DebugState dbgState; -#include "distance.h" -#include "encoder.h" -#include "gyro.h" -#include "pid.h" -#include "webserver.h" - - void initSerial(){ Serial.begin(115200); while (!Serial); @@ -36,11 +38,10 @@ void initMotors(){ } void initI2C(){ - digitalWrite(D2, LOW); - digitalWrite(D1, LOW); - delay(100); - Wire.begin(); - Wire.setClock(400000); + delay(100); + + wire2(); + wire1(); } void setup() { @@ -50,23 +51,24 @@ void setup() { initMotors(); initPID(); initI2C(); - initEncoder(); initDistance(); - initGyro(); + initGyro(); + initEncoder(); } void loop() { + long start = millis(); + if (updateGyro()) { //gyro data - currentYaw=ypr[0]*180/M_PI; double angle = ypr[1] * 180 / M_PI; if(angle>180) angle -= 180; angleInput = angle + angleOffset; } - {// encoder data - updateEncoder(); - posInput = encoder.position(); - } + + updateEncoder(); + currentYaw=odom.angle*180/M_PI; + updateDistance(); @@ -76,6 +78,13 @@ void loop() { speeds.right = min(90.0f-10, max(-90.0f+10, speeds.right)); left.write(90+(int)speeds.left); right.write(90+(int)speeds.right); + + long end = millis(); - delay(5); + if(end-start>LOOP_INTERVAL_MS){ + Serial.print("Overran "); + Serial.println(end-start); + }else{ + delay(LOOP_INTERVAL_MS-(end-start)); + } } diff --git a/robot/webserver.h b/robot/webserver.cpp similarity index 82% rename from robot/webserver.h rename to robot/webserver.cpp index edc0fe7..31fe4c6 100644 --- a/robot/webserver.h +++ b/robot/webserver.cpp @@ -1,3 +1,5 @@ +#include "headers.h" + #include #include #include @@ -31,48 +33,31 @@ void initWifi(bool host){ Serial.println(WiFi.localIP()); } -struct FVec3{ - float x,y,z; -}; -struct FVec4{ - float x,y,z; -}; - -struct Everything{ - float motorTargetAngle; - float position; - FVec3 anglePID; - FVec3 posPID; - FVec3 ypr; - FVec3 euler; - FVec3 gravity; - FVec4 q; - FVec3 aa; - FVec3 gy; - FVec3 aaReal; - FVec3 aaWorld; -}; void initServer(){ server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){ request->send_P(200, "text/html", index_html); }); server.on("/get_stuff_bin", HTTP_GET, [](AsyncWebServerRequest *request){ + static float arr[4]; + arr[0] = odom.angle; + arr[1] = distanceReading; + arr[2] = odom.x; + arr[3] = odom.y; + request->send(200, "application/octet-stream", (uint8_t*)(const char*)arr, sizeof(arr)); + }); + server.on("/fuckyou", HTTP_GET, [](AsyncWebServerRequest *request){ float arr[] = { - dbgState.motorTargetAngle, - encoder.position(), - angleSetpoint, angleInput, angleOutput, - posSetpoint, posInput, posOutput, - ypr[0]*180/M_PI, ypr[1]*180/M_PI, ypr[2]*180/M_PI, - euler[0]*180/M_PI, euler[1]*180/M_PI, euler[2]*180/M_PI, - gravity.x, gravity.y, gravity.z, - q.w, q.x, q.y, q.z, - aa.x, aa.y, aa.z, - gy.x, gy.y, gy.z, - aaReal.x, aaReal.y, aaReal.z, - aaWorld.x, aaWorld.y, aaWorld.z + odom.angle, + distanceReading, + odom.x, + odom.y }; - request->send(200, "application/text", (const uint8_t*)arr, sizeof(arr)*4); + request->send(200, "application/text", "hello"); + }); + server.on("/zero", HTTP_GET, [](AsyncWebServerRequest *request){ + zeroOdom(); + request->send(200); }); server.on("/get_stuff", HTTP_GET, [](AsyncWebServerRequest *request){ char buff[1024]; @@ -84,6 +69,7 @@ void initServer(){ "anglePID": {"setpoint": %lf, "input": %lf, "output": %lf}, "posPID": {"setpoint": %lf, "input": %lf, "output": %lf}, "turnPID": {"setpoint": %lf, "input": %lf, "output": %lf}, + "odom": {"left": %f, "right": %f, "x": %f, "y": %f, "angle": %f}, "ypr": {"yaw": %f, "pitch": %f, "roll": %f}, "euler": {"psi": %f, "theta": %f, "phi": %f}, "gravity": {"x": %f, "y": %f, "z": %f}, @@ -95,10 +81,11 @@ void initServer(){ })", (float)dbgState.motorTargetAngle, (float)distanceReading, - encoder.position(), + 0.0, //encoder.position(), angleSetpoint, angleInput, angleOutput, posSetpoint, posInput, posOutput, turnSetpoint, turnInput, turnOutput, + odom.left, odom.right, odom.x, odom.y, odom.angle, ypr[0]*180/M_PI, ypr[1]*180/M_PI, ypr[2]*180/M_PI, euler[0]*180/M_PI, euler[1]*180/M_PI, euler[2]*180/M_PI, gravity.x, gravity.y, gravity.z,