help with PWM soft motor pull timing

Hi!

For the project I am making I need a DC motor to start and stop smoothly when switching directions. At the end, I plan on using 4 MCUs to each swing its own bell (there will be 4 bells), but they will swing in sequence 1234 1234 1234 and the timing must be accurate enough so that they will not get out of order in under 5 minutes (at least) - they will all have the same program, the same swing time, but the motor speeds could differ because they will not all be the same weight and motors will have to do harder/lighter jobs depending on the bell and also swinging angles will be different. I am having a lot of trouble writing soft pull function that will do the job with as small error as possible. So far I have this:

void pwmSmoothPull(int dutyCycleStart, int dutyCycleEnd, int motorPinPWM, int speedChangeTime)
{
	/* Enables the motor to change duty cycle slowly to avoid sudden strong pulls and stops. */
	int deltaDutyCycleSigned = dutyCycleEnd - dutyCycleStart;
	int deltaDutyCycle = abs(deltaDutyCycleSigned); // How many steps to make to reach desired duty cycle.
	double speedChangeTimeStepDec = ((double) speedChangeTime / deltaDutyCycle) * 1000; // How much time to wait per step (in microseconds).
	int speedChangeTimeStepInt = (int) speedChangeTimeStepDec; // How much time per step (in microseconds) as integer (for delay function).
	double speedChangeTimeStepRemainder = speedChangeTimeStepDec - speedChangeTimeStepInt; // The decimal part of time step (lost in integer rounding).
	int speedChangeTimeRemainder = (int) (speedChangeTimeStepRemainder * deltaDutyCycle); // The time not waited due to integer rounding.
	int dutyCycleCurrent;

	// CASE: Speedup.
	if (deltaDutyCycleSigned >= 0)
	{
		dutyCycleCurrent = dutyCycleStart + 1;
		
		while (dutyCycleCurrent <= dutyCycleEnd)
		{
			analogWrite(motorPinPWM, dutyCycleCurrent);
			dutyCycleCurrent++;
			delayMicroseconds(speedChangeTimeStepInt);
		}
	// CASE: Slowdown.
	} else
	{
		dutyCycleCurrent = dutyCycleStart - 1;
		
		while (dutyCycleCurrent >= dutyCycleEnd)
		{
			analogWrite(motorPinPWM, dutyCycleCurrent);
			dutyCycleCurrent--;
			delayMicroseconds(speedChangeTimeStepInt);
		}
	}
	
	delayMicroseconds(speedChangeTimeRemainder); // To make this function last as close as possible to the time given in speedChangeTime, we need to wait the time that was lost in integer rounding (even though we are not changing speed anymore).
}

This function will be called each time the motor will have to reverse, so that can means at a rate of roughly 400 ms (+ the time of the function * 2 = around 600 ms of total swing time). The problem is, when I measure the time taken by it, it varies, from 2.6 msec off to 4 msec off or even 20 msec off, all of those values are the time waited too much, not too little (of course the error depends on the distance between two pwm duty cycles it was given - at this time I use from 0 to 155 and vice versa, but with different h-bridge it might be smaller, ie from 0 to 70 or so) and I cannot even plot a function from it - when I used 0-100 pwm I got 520ms, when I used 0-120 I got 504ms, when I used 0-140 I got 504.1ms, when I used 0-160 I got 520ms again - there doesn't seem to be any logic behind. That is far to much for my application as if bell swings for example 100 times per minute that means A LOT of error. Now I might have done this the wrong way, or perhaps there is a solution with timers. I would really appreciate anyone looking at this and helping me if possible or suggest a new approach as I am new in this field. Thank you very much!

It may just be me, but I can't make any sense of what you have written, though I do appreciate that you have taken some trouble.

I think the problem is that you are presenting the problem without a context - and you only have shown a piece of your code. You should show the whole code.

Why are you planning to use 4 Arduinos to control 4 bells - surely one Arduino would be sufficient and would avoid the problem of the bells getting out of sync?

Can you describe what the motors actually do?

...R

If you need to keep synchronised with external events you should not just use delay() or delayMicroseconds() to control the execution speed. You need to manage the timing based on the value of millis() or micros().

You have a fundamental choice to make at the outset - whether your code is going to be blocking or non-blocking. Blocking code is easier to write for simple jobs but is only suitable for doing one thing at a time. If your sketch needs to control more than one thing concurrently then a non-blocking design would be more appropriate.

Do you need to measure the actual position/movement of each motor to keep them exactly in sync, or would it be sufficient to just make each motor change direction at the same instant so that they tend to stay roughly in sync?

Robin2:
It may just be me, but I can't make any sense of what you have written, though I do appreciate that you have taken some trouble.

I think the problem is that you are presenting the problem without a context - and you only have shown a piece of your code. You should show the whole code.

Why are you planning to use 4 Arduinos to control 4 bells - surely one Arduino would be sufficient and would avoid the problem of the bells getting out of sync?

Can you describe what the motors actually do?

...R

Thank you for answering!

Yeah, I might have written a little confusing code :S
Here's what it does:

  • first it calculates the number of pwm steps between motor's current speed and motors desired speed
  • then, from that, it calculates how much time it has to wait each step in between (in a loop it increments pwm duty cycle by one each pass and then waits) by dividing the time given for the action of speeding up/slowing down with number of steps
  • since delay functions only accepts integers, it converts the waiting time for each step into integer, but saves the decimal part so that later, it will be able to make up for the microseconds lost to rounding - it is important that the function lasts for exactly as much as specified in arguments, even if process of speeding up/slowing down is completed a few tens of microseconds sooner
  • then the while loop comes, where, in each step, the speed of motor is incremented by one and then delay waits for as long as calculated above (step) - at the end, those delays should sum into almost exactly the time specified in arguments. The loop is divided into two parts, either for slowing down or for accelerating.
  • at the end it waits for as long as the lost decimals in rounding show in order to come as close to the value specified in argument

I hope this explains it a little better.

You are right. I should explain it better. I love bells and decided to make my miniature ones. I have two programs. One is for testing, the other one uses a RTC to ring a bell at the right time (because Im still waiting for my other bells to ship in). In all cases I have only used one bell and one hammer (solenoid), but that is about to change. I am making 4 mosfet h-bridges to extend the number of bells (currently I use Arduino Motor Shield R3). I suspected that 5 hammers (I will simulate Big Ben) and 4 bells that I plan to use are going to need at least two if not three pins each, one Arduino Uno won't be enough. So I started thinking about Shift Register. But there was also the problem that if I plan to run 4 bells at the same time, the smooth pull functions will have to overlap and it would be very hard to make it work. So I started thinking of buying 4 micro PIC MCUs (because they are 2.5€ and only have a few pins, more than enough to serve as simple PWM swinging machines), which would receive a signal from Arduino to start swinging the bells. Their "mistakes" in timing didn't seem so bad because I would only run bells for shorter periods of time, where those mistakes shouldn't be felt (yet). And now I was promised an old ringing machine from real church that has relays which turn bells on and off and also has clock built it. But its problem is that it only supports one sided pulls without PWM, and I don't have enough control over the motors that way. So I decided to stick with the plan of either Arduino or PICs being the "intermediary" between the h-bridges and the ringing machine. Now I am struggling to find the best and cheapest way out of this mess.

The only thing motors have to do is rotate in one direction for set amount of time and then reverse, again spin for set amount of time, then the whole process begins again. But direction changes have to be smooth so that it simulates "hand ringing", not crude machine ringing. The actual time the motors will have to pull will be measured to match bell's natural swing time to avoid mechanical damage. With only the ringing machine (crude one way pulls), my friend made "bells" out of pots: http://www.youtube.com/watch?v=pUypjCkbcMw#t=99 This might give you a better idea of what I want to achieve.

testing code: int motorPullTime = 670; // stores the length of swingint motorDutyCycle = 140 - Pastebin.com

PeterH:
If you need to keep synchronised with external events you should not just use delay() or delayMicroseconds() to control the execution speed. You need to manage the timing based on the value of millis() or micros().

You have a fundamental choice to make at the outset - whether your code is going to be blocking or non-blocking. Blocking code is easier to write for simple jobs but is only suitable for doing one thing at a time. If your sketch needs to control more than one thing concurrently then a non-blocking design would be more appropriate.

Do you need to measure the actual position/movement of each motor to keep them exactly in sync, or would it be sufficient to just make each motor change direction at the same instant so that they tend to stay roughly in sync?

Yeah, probably not, but I do not know of other ways :frowning: The soft motor pulls will overlap and I really have no idea of how to make it happen without multithreading :S

The blocking code is probably only good for one bell. But since I don't know how to write non-blocking code on Arduino (and other MCUs), I chose 4 separate small cheap MCUs for the job of swinging.

No, no measurements will be done by MCU, the actual swing time of motor will be measured by swinging the bell by hand and measuring its swing time, then the value will be input into the MCU. All 4 bells have the same swing time (they will be counter-weighted if necessary), but can swing in different orders: 1234 or 4321 or 1324 or 4231. Its a common practice in our country, but those advanced systems for big bells use AC and the code is not accessible, so I must "invent" my own DC smaller version. Example: http://www.youtube.com/watch?v=VLxCQK_XmT8#t=103

Well that all seems perfectly feasible. If you use one Arduino per bell your basic sketch will be simpler but you will need a scheme to get them all started in synch. Do you have a scheme for that? (For example, power them all from a common wall socket so you can switch them on together.)

I think the problem is both Physical and programmatic.

On the physics side i believe a bell can be modeled as a pendulum. A pendulum has a period time wich is desribed by this formula:
T = 2pisqrt(L/g)*(1+f(a))
Where:
T = period time
L = length of pendulum
g = gravitational acceleration
f(a) a funcion that increases period time with amplitude.

If the pendulum is motor driven we have 3 situations

  • The energy supplied by the motor equals the energy lost by friction ect. We have equilibrium, Period time and amplitude remain constant
  • The energy supplied by the motor is less than the enery lost. Amplitude will decrease until losses equal the added energy. Period time will decrese toward he natural period time: T=2pisqrt(L/g)
  • The energy supplied by the motor is grater than the losses. Amplitude and period time will increase.

This can be used sync each bell. If it is ahead of schedule increase energy to the motor, if the bell is behind decrease energy to the motor.

On the programmatic side i would go for a single arduino, making synchronisation so much easier. There definitely should be some sensor for bell position or speed. Otherwise i imagine the following will happen.

suppose the bell is approaching it outermost sving position ahead of schedule. In this case the motor will counteract the bell movement and decrease the pendulums energy. This will decrease the amplitude and thereby the period time. This is the opposite of what you want. If you have some sensor you can determine if the bell is ehead of or behind schedule and correct the motor power based on that.

Here is a example how that can be done. It uses my statemachine library fund here: http://playground.arduino.cc/Code/SMlib

The code is somewhat runnable, connect led to digital pins 6, 9, 10, 10 and watch dem fade and light uo in sync. Pins 2, 3, 4, 5 will indicate motor direction.

There is no code for the regulators because this demands a physical setup. You can create one statemachine for each bell doing the regulation as indicated.

#include <SM.h>

SM Master(Clock);
SM Bell1(Correct1);

const double Pi = 355/113;//Good aprox
const int NBells = 4;
//all these are pwm outputs
const int Bell_spd[NBells] = {6, 9, 10, 11};

//these are digital outputs
const int Bell_dir[NBells] = {2, 3, 4, 5};

//time constants
const int Period_T = 1200;
const int HPeriod_T = Period_T/2;
const int Bell_offs[NBells] = {0, 150, 300, 450};

//arrays for regulating amplitude and period
double Bell_sin[NBells];
int Bell_amp[NBells] = {127, 127, 127, 127};


void setup(){
  for(int i = 0; i<NBells; i++) pinMode(Bell_spd[i], OUTPUT);
  for(int i = 0; i<NBells; i++) pinMode(Bell_dir[i], OUTPUT);
}//setup()

void loop(){
  EXEC(Master);
  EXEC(Bell1);
}//loop()

State Clock(){
   if(Master.Timeout(Period_T)) Master.Set(Clock);//master clock
   for(int i = 0; i<NBells; i++){
     int BellTime = (Master.Statetime()+Bell_offs[i])%Period_T;
     Bell_sin[i] = sin(2*Pi*BellTime/Period_T);
     analogWrite(Bell_spd[i], abs(Bell_amp[i]*Bell_sin[i]));//Output motor speed
     digitalWrite(Bell_dir[i], (Bell_sin[i]>0));//output motor direction
   }//for(Bell)
}//Clock()

State Correct1(){
  //code to correct amplitude
}//Correct1()

Wouldn't it be much simpler to leave the bells stationary and hit them with hammers using servos, or even a modified relay with a "striker" attached to the actuator. A servo (or relay) only requires a single Arduino pin. And you don't have to worry at all about the natural swing frequency of the bells.

By the way Attinys are very cheap if you want smaller/cheaper Arduino compatible MCUs.

...R