Im trying to control a Powerwheels with RC Car motors.
I'm using an arduino to interpret a Proportional throttle and brake pedal (hall effect?) and then send out servo commands which is working well with the exception of the Scaling/Expo of the pedals once you depress the pedal fully it maxes out at 636 and Fully released is 36.
So halfway pressed pedal is about 500
Is there a way I can make the mapping a curve rather than linear?
// RCCarTest.ino
// ----------LIBRARIES--------------
#include <Servo.h>
// --------CONSTANTS (won't change)---------------
const int ThrottlePin = A0; // the pin number for the Throttle
const int BrakePin = A2; // the pin number for the Throttle
const int servoPin = 5; // the pin number for the servo signal
int servoInterval = 100; // initial millisecs between servo moves
//------------ VARIABLES (will change)---------------------
int ThrottleValue = 0;
int BrakeValue = 0;
Servo myservo; // create servo object to control servo1
int servoPosition = 90; // the current angle of the servo - starting at 90.
unsigned long currentMillis = 0; // stores the value of millis() in each iteration of loop()
unsigned long previousServoMillis = 0; // the time when servo1 was last moved
//========================================
void setup() {
Serial.begin(9600);
Serial.println("Starting RCCarTest.ino"); // so we know what sketch is running
pinMode(ThrottlePin, INPUT);
pinMode(BrakePin, INPUT);
myservo.write(servoPosition); // sets the initial position - Throttle
myservo.attach(servoPin);
}
//========================================
void loop() {
currentMillis = millis(); // capture the latest value of millis()
servoThrottle();
}
//========================================
void servoThrottle() {
// nothing happens unless the interval has expired
// the value of currentMillis was set in loop()
if (currentMillis - previousServoMillis >= servoInterval) { // its time for another move
previousServoMillis += servoInterval;
// Open Throttle
if (ThrottleValue != map(analogRead(ThrottlePin), 36, 636, 90, 180)) {
ThrottleValue = map(analogRead(ThrottlePin), 36, 636, 90, 180); // reads the value of the potentiometer and scales it from 90-180 Degrees
Serial.print("Throttle Value has Changed - New Value: "); // so we know that Throttle Value has Changed.
Serial.println(ThrottleValue);
} // Close Throttle
// Open Brake
if (BrakeValue != map(analogRead(BrakePin), 36, 636, 0, 90)) {
BrakeValue = map(analogRead(BrakePin), 36, 636, 0, 90); // reads the value of the potentiometer and scales it from 0-90 Degrees
Serial.print("Brake Value has Changed - New Value: "); // so we know that Brake Value has Changed.
Serial.println(BrakeValue);
} //Close Brake
// Open Servo
if (servoPosition != ThrottleValue-BrakeValue) {
servoPosition = ThrottleValue-BrakeValue; // scale it to use it with the servo (value between 0 and 180)
Serial.print("Servo Position has Changed - New Value: "); // so we know that Servo Value has Changed.
Serial.println(servoPosition);
// make the servo move to the next position
myservo.write(servoPosition);
}// Close Servo
}// Close MoveTime
}// Close servoThrottle()
//========================================END
the default startup mode for pins is INPUT. An analog capable pin in that mode can be used in that mode, that is to say you don't even need to use pinMode(), and doing will not keep them from working as analog.
You could do it with a look up table. If you have an idea of what servo position you want for a range of read analog values, then you could define that set of pairs of values (analog value, servo position). Then scan the table to find a position in the table where your analog value is < current table entry and > next table entry. Then do linear interpolation to get the servo value (some value between current table entry and next table entry). So you’d have a ‘curve’ made up of N straight lines, where N = number of table entries-1. I was writing this before @jremington posted, the library they suggested might be easier / better.
I would prefer it to still be an infinitely variable curve,
Rather than a bunch of straight lines strung together.
It will of course be trial and error to get it feeling right, but currently as soon as you touch it. You have to just feather your touch to get low speed, and before you know it. Its pretty much full speed.
Im guessing its Currently a Logarithmic output, but would prefer linear or some form of Exponential.
What mcu chip are you using? I’ve found that ESP32 (Dev Kit C V4 WROOM-32D) ADC has issues with accuracy, but there’s plenty of info about how to improve that available elsewhere in this forum.
I used part of a sine wave to achieve a better LED dimming and brightening sequence.
From memory, I converted the sin values in Excel to an array covering values of 0 to 255 and mapped those to a PWM output. It worked very smoothly.
I know that vintage sewing machine enthusiasts use similar pedals designed for electric scooter to control motor speed. They have a 0 to 5 volt output. These can be mapped to suit the user.
I'm currently working on a soft start triac system to preserve the working life of a 300-watt slide projector lamp. It looks like an early halogen lamp and gets very, very hot. Expensive to buy replacements, if you can find them.
A word of warning on halogen lamps. I had an open faced (no glass cover) floodlight that decided to explode. Very lucky not to be badly injured.
If you can derive the formula from physics, you will get the optimal formula. Optimal as the system may depends on more parameters than you include.
E.g. distance sensors often use 340 m/s as speed of sound while the SOS depends on humidity, temperature, pressure, gas composition, …
This always raises the question: how correct is the formula in practice?
There is a curve fitting library you might want to look at
If you have many many data points you can use tools like a spreadsheet or online (see below) to find a fitting curve.
Often there are other requirements like performance (large formulas can take serious time) that must be met too. Furthermore ultimately it is the accuracy (significant digits, max error allowed) that is important. If one parameter has 3 significant digits, you often don’t need to calculate everything in more than 3 digits.
(as author of multimap some background)
I wrote multimap to speed up slow math (think it was the NTC formula).
Although it is multiple linear interpolation it has two features that make it work well in practice (for many apps).
data points do not need to be equidistant
number of interpolation points is user defined
These allow you to use many points where needed (curvy or high resolution parts), and a few where possible (typical end of scale).
The multimap library has a few related classes including the multiMapBS class. BS == binary search, which optimizes the search for the right interval for large interpolation sets.
36 => 0
500 => 45
636 => 90
Can you provide more data points?
A possible optimization for your code
you use two analogRead()’s where one is sufficient (my assumption)
// Open Throttle
if (ThrottleValue != map(analogRead(ThrottlePin), 36, 636, 90, 180)) {
ThrottleValue = map(analogRead(ThrottlePin), 36, 636, 90, 180); // reads the value of the potentiometer and scales it from 90-180 Degrees
Serial.print("Throttle Value has Changed - New Value: "); // so we know that Throttle Value has Changed.
Serial.println(ThrottleValue);
} // Close Throttle
This can be made more efficient / faster
// Open Throttle
// read the value of the potentiometer and scales it from 90-180 Degrees
uint16_t raw = map(analogRead(ThrottlePin), 36, 636, 90, 180);
if (ThrottleValue != raw) {
ThrottleValue = raw;
Serial.print("Throttle Value has Changed - New Value: ");
Serial.println(ThrottleValue);
} // Close Throttle