Hi,
I write on this forum to ask for help, I have a robot with a wheel on each side at the back controlled by two motors (one motor per wheel) and a pen as a central wheel at the front.
My goal is to draw a circle but not with the classical method (one wheel turning in one direction and the other in the other). I need to draw a circle from 2 centimeters radius to 20 by making a circle with the pen not with the robot.
It's hard to explain so here is the different steps to draw the circle :
I can’t understand how to make a move like this, is it a mathematical expression, a logic that I don’t have, ...
I am using an ESP32 and motor with integrated encoder. Here is a simple code that make the robot move :
// ==== Libraries ====
#include <WiFi.h>
// ==== Wi-Fi Network ====
const char* ssid = "leboss";
const char* password = "1234567890";
WiFiServer server(80);
// ==== Encoder Pins ====
#define ENC_L_CH_A 32
#define ENC_L_CH_B 33
#define ENC_R_CH_A 27
#define ENC_R_CH_B 14
// ==== Motor Pins ====
#define IN1_L 17
#define IN2_L 16
#define IN1_R 18
#define IN2_R 19
#define EN_L 4
#define EN_R 23
// ==== Constants ====
const float TICK_TO_CM = (28.274 / 4200.0) * 4.0;
const float WHEELBASE_CM = 9.0;
const int BASE_SPEED = 85;
const int BRAKE_PWM = 70;
const int BRAKE_DURATION = 50;
const int ROTATION_BRAKE_DURATION = 100;
// ==== Variables ====
volatile long tickL = 0;
volatile long tickR = 0;
bool motionComplete = true;
bool isRotating = false;
bool rotateLeft = true;
float targetDistance = 0;
long targetRotationTicks = 0;
// ==== PID Structure ====
struct PID {
float Kp, Ki, Kd;
float errorSum;
float previousError;
PID(float p, float i, float d) : Kp(p), Ki(i), Kd(d), errorSum(0), previousError(0) {}
float compute(float error) {
errorSum += error;
float deltaError = error - previousError;
previousError = error;
return Kp * error + Ki * errorSum + Kd * deltaError;
}
};
PID pidTrajectory(1.5, 0.0, 0.5);
PID pidRotation(2.0, 0.0, 0.8);
// ==== Interrupts ====
void IRAM_ATTR handleEncoderL() { tickL++; }
void IRAM_ATTR handleEncoderR() { tickR++; }
// ==== Motor Functions ====
void setMotors(int pwmL, int pwmR) {
digitalWrite(IN1_R, HIGH); digitalWrite(IN2_R, LOW);
digitalWrite(IN1_L, HIGH); digitalWrite(IN2_L, LOW);
analogWrite(EN_R, constrain(pwmR, 0, 255));
analogWrite(EN_L, constrain(pwmL, 0, 255));
}
void setRotation(bool left, int pwmL, int pwmR) {
if (left) {
digitalWrite(IN1_R, HIGH); digitalWrite(IN2_R, LOW);
digitalWrite(IN1_L, LOW); digitalWrite(IN2_L, HIGH);
} else {
digitalWrite(IN1_R, LOW); digitalWrite(IN2_R, HIGH);
digitalWrite(IN1_L, HIGH); digitalWrite(IN2_L, LOW);
}
analogWrite(EN_R, constrain(pwmR, 0, 255));
analogWrite(EN_L, constrain(pwmL, 0, 255));
}
void activeBrake() {
digitalWrite(IN1_R, LOW); digitalWrite(IN2_R, HIGH);
digitalWrite(IN1_L, LOW); digitalWrite(IN2_L, HIGH);
analogWrite(EN_R, BRAKE_PWM);
analogWrite(EN_L, BRAKE_PWM);
delay(BRAKE_DURATION);
}
void activeBrakeRotation() {
digitalWrite(IN1_R, LOW); digitalWrite(IN2_R, LOW);
digitalWrite(IN1_L, LOW); digitalWrite(IN2_L, LOW);
analogWrite(EN_R, 0);
analogWrite(EN_L, 0);
delay(ROTATION_BRAKE_DURATION);
}
void stopMotors() {
digitalWrite(IN1_R, LOW); digitalWrite(IN2_R, LOW);
digitalWrite(IN1_L, LOW); digitalWrite(IN2_L, LOW);
analogWrite(EN_R, 0); analogWrite(EN_L, 0);
}
void resetTicks() {
tickL = 0;
tickR = 0;
}
void moveForward(float distance_cm) {
resetTicks();
targetDistance = distance_cm;
motionComplete = false;
isRotating = false;
while (!motionComplete) {
float averageDist = ((float)tickL + (float)tickR) / 2.0 * TICK_TO_CM;
float tickError = (float)tickL - (float)tickR;
float correction = pidTrajectory.compute(tickError);
int pwmL = BASE_SPEED - correction;
int pwmR = BASE_SPEED + correction;
setMotors(pwmL, pwmR);
if (averageDist >= targetDistance) {
activeBrake();
stopMotors();
motionComplete = true;
}
}
}
void rotate(bool left) {
resetTicks();
targetRotationTicks = (WHEELBASE_CM * PI / 4.0) / TICK_TO_CM;
motionComplete = false;
isRotating = true;
while (!motionComplete) {
long error = tickR + tickL;
float correction = pidRotation.compute(error);
int pwmL = BASE_SPEED - correction;
int pwmR = BASE_SPEED - correction;
setRotation(left, pwmL, pwmR);
long avgTicks = (abs(tickL) + abs(tickR)) / 2;
if (avgTicks >= targetRotationTicks) {
activeBrakeRotation();
stopMotors();
motionComplete = true;
isRotating = false;
}
}
}
void turnLeft90() {
rotate(true);
}
void turnRight90() {
rotate(false);
}
void runStairPath() {
moveForward(20);
turnLeft90();
moveForward(10);
turnRight90();
moveForward(40);
}
// ==== Setup ====
void setup() {
Serial.begin(115200);
pinMode(IN1_R, OUTPUT); pinMode(IN2_R, OUTPUT);
pinMode(IN1_L, OUTPUT); pinMode(IN2_L, OUTPUT);
pinMode(EN_R, OUTPUT); pinMode(EN_L, OUTPUT);
pinMode(ENC_L_CH_A, INPUT); pinMode(ENC_L_CH_B, INPUT);
pinMode(ENC_R_CH_A, INPUT); pinMode(ENC_R_CH_B, INPUT);
attachInterrupt(digitalPinToInterrupt(ENC_L_CH_A), handleEncoderL, RISING);
attachInterrupt(digitalPinToInterrupt(ENC_R_CH_A), handleEncoderR, RISING);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) delay(500);
server.begin();
Serial.println(WiFi.localIP());
}
// ==== Loop ====
void loop() {
WiFiClient client = server.available();
if (client) {
String request = client.readStringUntil('\r');
client.flush();
if (request.indexOf("/FORWARD") != -1) {
int index = request.indexOf("distance=");
if (index != -1) {
String param = request.substring(index + 9);
int cm = param.toInt();
if (cm > 0 && cm < 1000) {
moveForward(cm);
}
}
}
if (request.indexOf("/LEFT") != -1) {
turnLeft90();
}
if (request.indexOf("/RIGHT") != -1) {
turnRight90();
}
if (request.indexOf("/STAIR") != -1) {
runStairPath();
}
client.println("HTTP/1.1 200 OK");
client.println("Content-type:text/html\n");
client.println();
client.println("<!DOCTYPE html><html><head><meta charset='UTF-8'><title>PID Robot</title></head><body>");
client.println("<h2>PID Wi-Fi Robot</h2>");
client.println("<form action='/FORWARD' method='GET'>");
client.println("<label>Distance (cm): </label><input type='number' name='distance' min='1' max='1000'>");
client.println("<input type='submit' value='Forward'>");
client.println("</form>");
client.println("<form action='/LEFT' method='GET'>");
client.println("<input type='submit' value='Turn 90° Left'>");
client.println("</form>");
client.println("<form action='/RIGHT' method='GET'>");
client.println("<input type='submit' value='Turn 90° Right'>");
client.println("</form>");
client.println("<form action='/STAIR' method='GET'>");
client.println("<input type='submit' value='Stair Path'>");
client.println("</form>");
client.println("</body></html>");
client.stop();
}
}
I would really appreciate your help to get this circle drawn ![]()














