-
Servo smoothing via logistic function
12/16/2023 at 16:30 • 0 commentsSmoothed servo movements are necessary in robotics to make them appear more organic. For this purpose, I use the logistic function (sigmoid curve) with the equation:
where x0 is the x value of the function's midpoint, L is the supremum of the values of the function and k is the logistic growth rate or steepness of the curve.
Fig. 1 Standard logistic function where L = 1, k = 1 and x0 = 0
Of course, the logistics function is not yet usable in this way. Let x0 = 0 and f(x) = 0 if x = 0. These conditions lead to
But we can also simply use
then L is again our supremum.
Fig. 2 Modified logistic function where L = 50, and k = 0.2
We now have a nice normal function instead of an often-used recursive function that we can use for servo smoothing (accelerate and decelerate).
-
Lip sync​
12/05/2023 at 15:47 • 0 commentsThe following code lets the EMIC-2 speak what you enter into the serial monitor:
#include <SoftwareSerial.h> #define rxPin 10 // Serial input (connects to Emic 2's SOUT pin) #define txPin 11 // Serial output (connects to Emic 2's SIN pin) // Set up a new SoftwareSerial port SoftwareSerial emicSerial = SoftwareSerial(rxPin, txPin); void setup() { // Define pin modes pinMode(rxPin, INPUT); pinMode(txPin, OUTPUT); // Set the data rate for the SoftwareSerial port emicSerial.begin(9600); Serial.begin(9600); /* When the Emic 2 powers on, it takes about 3 seconds for it to successfully initialize. It then sends a ":" character to indicate it's ready to accept commands. If the Emic 2 is already initialized, a CR will also cause it to send a ":" */ emicSerial.println("P1"); // Select parser, P0: DECtalk, P1: Epson emicSerial.println("N4"); // Nx, select voice: x = 0 to 8 emicSerial.println("V5"); // Vx, set the audio output volume in dB from x = -48 (softest) to x = 18 (loudest). emicSerial.println("W200"); // Wx, set the speaking rate in words per minute from x = 75 (slowest) to x = 600 (fastest). emicSerial.print('\n'); // Send a CR in case the system is already up while (emicSerial.read() != ':'); // When the Emic 2 has initialized and is ready, it will send a single ':' character, so wait here until we receive it delay(10); // Short delay emicSerial.flush(); // Flush the receive buffer } void loop() { while (Serial.available() == 0); // Wait for data available String userInput = Serial.readString(); // Read until timeout userInput.trim(); // Remove any whitespace at the end of the String Serial.println(userInput); emicSerial.print('S'); emicSerial.print(userInput); emicSerial.print('\n'); /* Wait here until the Emic 2 responds with a ":" indicating it's ready to accept the next command */ while (emicSerial.read() != ':'); }
The problem with lip-synchronization is that there is no way on the software side to determine exactly when the EMIC-2 has finished the speech. The line
while (emicSerial.read() != ':');
is completely useless for that. It merely shows when the EMIC-2 is ready for new commands, which will come true long before the end of the speech. Others have already had this problem and I will solve it in the same way. I will use the audio output of the EMIC-2 to control the four servos that are responsible for the movement of the mouth. This requires a so-called envelop follower, which reproduces the volume envelope of an applied waveform.
Fig. 1 A signal and its envelope marked with red
Fig 2. shows such an envelope detector circuit.
Fig. 2
Apart from the operating voltage, it is only important that the opamp has a rail-to-rail output swing. The LMV358 can also be used, for example.
-
Videos​
12/02/2023 at 11:38 • 0 comments -
Extracting piperine from black pepper
11/27/2023 at 19:42 • 0 commentsPiperine is a piperidine alkaloid from the group of acid amide alkaloids. It is the amide of piperic acid and piperidine and forms a colorless to yellowish solid with a monoclinic crystal structure. It is the source of the pungent pepper flavor. In order to develop a reliable colorimetric test for piperine, I need the substance to be relatively pure. Out of interest, I have decided to isolate the piperine from the black pepper myself using various methods. The yield is not particularly high but sufficient for my experiments. Please check out the video for more information.
-
Modelling the Pt1000 temperature sensor & building a thermostat
11/23/2023 at 18:03 • 0 commentsThe characteristic curve of the Pt1000 is as follows:
Fig. 1
It can be assumed to be a good approximation of a straight line. However, we can do little with this, as the microcontroller measures the voltage at the output of the opamp. The microcontroller maps the input voltages between 0 and 5 V to integer values between 0 and 1023. We do not need to convert the measured voltage back into volts, it is sufficient to make a note of the analogRead() values. During the measurements, the Pt1000 is simply replaced by a potentiometer whose resistance is set according to the characteristic curve of the Pt1000 so that it corresponds to a certain temperature, for example, 1000 Ω at 0 °C and 1385 Ω at 100 °C.
Fig. 2
If we assume in the best case that the analogRead() value is also linearly dependent on the temperature, two measuring points are sufficient and we can determine the corresponding linear equation using the two-point form:
A PCB was designed according to Fig. 2, with an additional logic level n-channel power MOSFET (RFP30N06LE) for switching the heating cartridge.
Fig. 3
Fig. 4
After a few measurements, it is clear that our assumption of linearity is correct.
Fig. 5
Using linear regression, the following function is obtained:
The maximum value of the analogRead()-value for this opamp configuration is 764, which corresponds to 3.73 V (10-bit ADC resolution, 5 V-microcontroller).
Unfortunately, the Pt1000 I bought does not correspond to the characteristic curve in Fig. 1. I then noted the analogRead()-values at 100 °C and 12°C. To do this, the sensor was immersed in water at the corresponding temperatures (I used a laboratory thermometer to measure the water temperature), and the function was recalculated:
In the meantime, I have developed the glucose receptor a little further. It was mounted on an aluminum plate and fitted with two heat sinks.
Fig. 6
I am using a PID algorithm to control the temperature. The code is mainly adapted from here.
// Pins int PWM_pin = 9; // Variables float temperature_read = 0.0; float set_temperature = 100; float PID_error = 0; float previous_error = 0; float elapsedTime, Time, timePrev; int PID_value = 0; // PID constants int kp = 15.0; int ki = 0.3; int kd = 1.8; int PID_p = 0; int PID_i = 0; int PID_d = 0; void setup() { Serial.begin(9600); pinMode(PWM_pin,OUTPUT); // Set timer 2 divisor to 32 for PWM frequency of 980.39 Hz // on pin D9 and D10 (Arduino Mega) TCCR2B = TCCR2B & B11111000 | B00000011; Time = millis(); } void loop() { // First we read the real value of temperature int AD_C = 0; AD_C = analogRead(0); temperature_read = 0.3548 * AD_C - 45.839; // Next we calculate the error between the setpoint and the real value PID_error = set_temperature - temperature_read; // Calculate the P value PID_p = kp * PID_error; // Calculate the I value in a range on +-3 if(-3 < PID_error <3) { PID_i = PID_i + (ki * PID_error); } // For derivative we need real time to calculate speed change rate timePrev = Time; // the previous time is stored before the actual time read Time = millis(); // actual time read elapsedTime = (Time - timePrev) / 1000; // Now we can calculate the D calue PID_d = kd*((PID_error - previous_error)/elapsedTime); // Final total PID value is the sum of P + I + D PID_value = PID_p + PID_i + PID_d; // We define PWM range between 0 and 255 if(PID_value < 0) { PID_value = 0; } if(PID_value > 255) { PID_value = 255; } // Now we can write the PWM signal to the mosfet on digital pin D9 analogWrite(PWM_pin, PID_value); Serial.println(PID_value); previous_error = PID_error; // Remember to store the previous error for next loop. delay(300); Serial.print("Set temperature: "); Serial.println(set_temperature); Serial.print("Real temperature: "); Serial.println (temperature_read); Serial.println(""); }
The constants must be determined experimentally.