Hello Everyone, longtime reader first time user.
I didn't know where else to turn and since this is such a knowledgeable forum, I figured I could at least try here.
I'm working on a robot arm. Its not my first one, but on this one I would like much more precise control of these Servos(Link), as well as hopefully eliminate those servo 'jumps' at power on. It seems that any length of time between uses are enough to throw my other robot arms off to where they wont grab something larger like a lighter off my desk without adjustment.
I plan to use the AS5600 magnetic encoder for position feedback. I have removed the internal feedback potentiometer from the servo, and ran the 3 wires out. I am currently using a DAC MCP4725, but without great success. In respect to where the internal pot was soldered, I now have the DACs' output ground to that ground, and the OUT of the DAC to where the wiper terminal of the Servos old pot was, but I'm not sure what to do with that +3.3V that went to the servos old pot, and so currently it is disconnected/floating.
I also have the standard 3-wires of the servo on a separate 5V power supply (with joining grounds between MCU and Servo Power) - and the Servo Control wire to the MCU pin.
Counterclockwise, the servo works great. Very smooth (there is some occasional jitter in there, see below) and normal wattage used. Goes through all speeds min to max quite well.
But clockwise is so jittery from about 1/4 of the max speed up through about 3/4 speed- and you can hear the power surges in the motor as well as see it on the voltage meter. The range is also a lot less from stop to max speed, like half (give or take) of counterclockwise range.
I have tried a lot of different things without much improvement. Tried a PID controller for a few months without success. I'm hoping to run across someone that has tried something similar or has an idea or suggestion.
I will also post my code below, just in case. Its nothing special, it just allows me to use the Serial Terminal to send either the DAC or the Servo PWM values to control the output speed of the servo.
#include "Wire.h"
#include "MCP4725.h"
MCP4725 MCP(0x60);
const int NumOfChars = 150;
char buf[NumOfChars];
char ThisMsg[NumOfChars];
int servoPin = 3;
byte servoChannel = 1;
int servoFreq = 100;
int servoResolution = 12;
int servoStopSpd = 613;
int servoVal = servoStopSpd;
int DACStopSpd = 2048;
int DACVal = DACStopSpd;
int Mode = 1; // enables between DAC output (by typing "c" in Serial Mon.) or Servo Speed Control ("s")
bool SerialMessage;
int oldServoRead;
int oldDACRead;
unsigned long lastDACSend;
unsigned long lastServoSend;
bool Findmode = 0; //enables to slowly ramp up or down to find speeds at all ranges
void setup()
{
Serial.begin(115200);
unsigned long last = millis() - 500;
//Wait until an input is typed into serial monitor - solves my ESP32 C3 reboot and serial monitor problem
while (!SerialMessage)
{
WriteSerial();
if (millis() - last >= 500)
{
Serial.println("Type Anything to proceed");
last = millis();
}
}
SerialMessage = 0;
Serial.println();
Serial.println("Beginning Setup");
Serial.println(F("Sketch Location: "__FILE__ ));
Serial.println(F("Install Date/Time: "__DATE__ " " __TIME__)); // Print file location and time
Wire.begin(1, 0);
//Wire.setClock(400000);
MCP.begin();
WriteDAC();
ledcAttachChannel(servoPin, servoFreq, servoResolution, servoChannel);
WriteServo();
Serial.println();
Serial.println("Setup Complete");
Serial.println();
}
void loop()
{
WriteSerial();
DoStuffWMessage();
WriteServo();
WriteDAC();
}
//Changes the DAC Values
void WriteDAC()
{
int DACRead = MCP.getValue();
//if (millis() - lastDACSend >= 2) // trying delays here
{
lastDACSend = millis();
if (DACRead != DACVal)
{
if (Findmode) //type "f" in Serial Monitor to change Findmode State
{
if (DACRead > DACVal)
MCP.setValue(DACRead - 1);
else if (DACRead < DACVal)
MCP.setValue(DACRead + 1);
}
else
MCP.setValue(DACVal);
}
}
if (DACRead != oldDACRead)
{
oldDACRead = DACRead;
sprintf(buf, "DACRead: %d", DACRead);
Serial.println(buf);
}
}
// Change the Servo PWM Values
void WriteServo()
{
int ServoRead = ledcRead(servoPin);
//if (millis() - lastServoSend >= 2) // trying delays here
{
lastServoSend = millis();
if (ServoRead != servoVal) //type "f" in Serial Monitor to change Findmode State
{
if (Findmode)
{
if (ServoRead > servoVal)
ledcWrite(servoPin, ServoRead - 1);
else if (ServoRead < servoVal)
ledcWrite(servoPin, ServoRead + 1);
}
else
ledcWrite(servoPin, servoVal);
}
}
if (ServoRead != oldServoRead)
{
oldServoRead = ServoRead;
sprintf(buf, "ServoRead: %d servoVal: %d", ServoRead, servoVal);
Serial.println(buf);
}
}
// Stores Serial Monitor messages for use in DoStuffWMessage
void WriteSerial() {
while (Serial.available() > 0) {
static unsigned int message_pos = 0;
char inByte = Serial.read();
if (inByte != '\n' && (message_pos < NumOfChars - 1)) {
ThisMsg[message_pos] = inByte;
message_pos++;
}
else {
ThisMsg[message_pos] = '\0';
message_pos = 0;
SerialMessage = 1;
}
}
}
// Type messages in Serial monitor and take action as needed
void DoStuffWMessage() {
char DoStuffBuffer[NumOfChars] = {};
if (SerialMessage) {
size_t msglen = strlen(ThisMsg);
char str2[] = "0123456789";
int DigitPos = strcspn(ThisMsg, str2); // Look for any numbers in the message and show what position
if (DigitPos >= msglen) // If there are no numbers in the message
DigitPos = -1; // Set to -1 for (No Digits)
char msgtxt[150]; // Create a place to store the text-only
strcpy(msgtxt, ThisMsg);
// Save as Text Only by replacing the first number with a null terminator, this will stop the read at that position:
msgtxt[DigitPos] = '\0';
int msgdigits = 0;
if (DigitPos != -1) // If there are digits in the message
msgdigits = atoi(ThisMsg + DigitPos); // Save the Numbers Only to an Integer
if (DigitPos == 0) { //Only a number was entered
if (Mode == 0) //DAC Mode
DACVal = msgdigits;
if (Mode == 1) //Servo Mode
servoVal = msgdigits;
}
if (strcmp(ThisMsg, "") == 0 || strcmp(ThisMsg, "stop") == 0) // Pressing Enter is a quick way to stop the servo
{
DACVal = DACStopSpd;
servoVal = servoStopSpd;
sprintf(DoStuffBuffer, "Stopped - Servo %d DAC: %d", servoStopSpd, DACStopSpd);
}
if (strcmp(ThisMsg, "+") == 0) {
if (Mode == 0)
DACVal++;
if (Mode == 1)
servoVal++;
}
if (strcmp(ThisMsg, "-") == 0) {
if (Mode == 0)
DACVal--;
if (Mode == 1)
servoVal--;
}
if (strcmp(ThisMsg, "f") == 0) { //enable findmode to ramp up or down to each position
Findmode = !Findmode;
sprintf(DoStuffBuffer, "Findmode: %d", Findmode);
}
if (strcmp(msgtxt, "servstop") == 0) {
servoStopSpd = msgdigits;
sprintf(DoStuffBuffer, "servoStopSpd: %d", servoStopSpd);
}
if (strcmp(msgtxt, "dacstop") == 0) {
DACStopSpd = msgdigits;
sprintf(DoStuffBuffer, "DACStopSpd: %d", DACStopSpd);
}
if (strcmp(ThisMsg, "s") == 0) { // enables between DAC output control or Servo Speed Control
Mode = 1;
sprintf(DoStuffBuffer, "Servo Mode");
}
if (strcmp(ThisMsg, "d") == 0) { // enables between DAC output control or Servo Speed Control
Mode = 0;
sprintf(DoStuffBuffer, "DAC Mode");
}
if (strcmp(msgtxt, "frq") == 0) {
servoFreq = msgdigits;
ledcChangeFrequency(servoPin, servoFreq, servoResolution);
sprintf(buf, "Servo Frequency: %d", servoFreq);
}
if (strcmp(msgtxt, "res") == 0) {
servoResolution = msgdigits;
ledcChangeFrequency(servoPin, servoFreq, servoResolution);
sprintf(buf, "Servo Resolution: %d", servoResolution);
}
if (strcmp(DoStuffBuffer, "") != 0)
Serial.println(DoStuffBuffer);
SerialMessage = 0;
}
}