The initial pulse width is also set in ticks, so multiply the value by 2 in your case. Channels in your case are the 4 different ESC. The rest of what you wrote is correct. Just had a thought: Why are you even bothering with the sequencer as opposed to using 4 PwmOutput? The latter has a much simpler API and is likely suitable for your needs. On Dec 26, 2014 2:46 AM, <[email protected]> wrote:
> Hi Ytai, > > Allow me to put in words how I understand the whole sequencer now, after > reading your emails and the documentation a few more times: > > Here is how I believe the sequencer should be set in my case: > final ChannelConfigPwmSpeed dcMotorNEConfig = new > Sequencer.ChannelConfigPwmSpeed( > Sequencer.Clock.CLK_2M, 20000, 1060, new DigitalOutput.Spec(2)); > > I chose the 2MHz clock based on this documentation: > * A steps channel allows determining the clock rate on a per-cue basis. > This often allows > * avoiding having to split cues, thus resulting in a less total cues and > more efficient > * execution. The rule for choosing the correct clock is to always use the > highest rate that > * will cause the resulting period to be <= 2^16. In other words, choose > the highest available > * clock which is less than or equal to (2^16 / Tp) or (2^16 * Fp), where > Tp is the desired > * period in seconds, or Fp is the desired frequency in Hz. For example, > if we want to generate > * pulses at 51Hz, 65536 * Fp = 3.34MHz, so we should use the 2MHz clock, > and the period value > * will be round(2MHz / 51Hz) = 39216. This result in an actual rate of > 2MHz / 39216 ~= > * 50.9996[Hz] > > So, as I went for 100Hz, the closest clock rate to 6.55MHz is 2MHz. The > period is calculated 2MHz/100Hz so the value of 20000 comes up. > > The 1060 value for the init Pulse Width is the one I take from the ESC > specs and is set in microseconds. > Is this right? > > What I don't grasp yet very well, even after I read: > "The IOIO motor control API introduces the notion of channels. Each > channel generates a single waveform of a given type, typically (but not > always) driving a single output pin. On a given application, you would > normally have multiple channels of different types, which are then > controlled in synchronization to achieve a complex motion." it's still not > very clear for me what a channel is. > What confuses me the most is that we have to define both a clock frequency > and a period. From my understanding of frequency, once you define it, let's > say 2MHz, the period can be calculated automatically 1/2MHz = > 0.5microseconds. > What is the period that is defined per channel for? > I assume it is for converting the channel clock frequency to the sequencer > one. Is this right? > > Now, about sending the pulse width to the ESC. > From our previous conversation I understood that the formula should be as > such: > // motorsPowers comes as values between 0 and 255 > // needs to be transformed to values from 0 to 1 by multiplying to 0.39f > Math.round((1060.f + (800 * motorsPowers.nw * 0.39f)) * 2); > > The pulse width is sent in this case in ticks and not microseconds (while > the initial pulse width when defining the channel is set in microseconds > and not ticks), right? > 1 tick = 1/2MHz = 0.5 microseconds so we need the multiplier by 2 so ESC > gets the value converted to microseconds. > > The sequencer push should be, based on my understanding so far: > sequencer_.push(cue_, 625); > That will set a freq of 100Hz. > 100Hz equals 10ms periods which means 625 ticks of 16 microseconds. > > Is my understanding correct? > If yes, my next step will be to write a simple program with a slider that > will control the ESC. Once I make that to work, I will return to my > original code and make it work as well. > > > On Thursday, December 25, 2014 8:06:22 PM UTC+2, Ytai wrote: >> >> Inline >> >> On Dec 25, 2014 1:27 AM, <[email protected]> wrote: >> > >> > Hi Ytai, >> > >> > Well, will try to go your approach. Most of the program you see there >> is from open source projects we tried to patch together, but by now we >> should be able to do a simple program that does as you suggest. >> > A few questions based on your comments, if I may: >> > >> > - you say that our cues are 10 sequencer ticks. We've set it like this >> as we thought that the smaller it is, the better. I understand now it is >> not the best approach. >> > But just to make sure: what is a tick? Here is what I understand now: >> > 10 ticks = 16 microseconds >> > the cue should be set to be the period of the input frequency of the >> ESC. If our ESC has a 1KHz input PWM (according to specs), which means a >> period of 1ms or 62.5 times of 16 microseconds. >> > Does this mean the cue should be set to 625 ticks? >> > Is this right or am I still far off? >> >> As documented on the wiki and the sequencer javadocs, a sequencer tick is >> 16us. You cannot do fractional ticks. Also, not sure where you got 1KHz >> from. I'm almost certain that your ESC, when operating in PWM input mode >> will work with 50-100 Hz, as it is supposed to interface with standard >> receivers etc. If you want to have faster control you'd have to use its I2C >> or UART input modes, both possible with the IOIO. But the ultimate decision >> on what should be your control rate has to do with how fast you actually >> need to update it in order to match your system dynamics. Unless it is a >> very small vehicle (which surely cannot carry a phone), 1KHz is an >> overkill. I'd start with 100Hz and move to higher rates with I2C control if >> proven insufficient. >> >> > >> > If that is right, than it means the phone will send a new signal to the >> ESC at each 625 ticks and it should set pulse widths of between 1060 >> microseconds and 1860 microseconds. >> > Here is where I get stuck. 1060 microseconds is bigger than 1 >> millisecond. How can I send pulse widths that are larger than the period? >> >> Your understanding is correct. As above, I assert that the expected >> period is 10ms-20ms. >> >> > Also, what do the 2MHz clock and period refer to? >> > If the number of ticks in the sequencer push already sets the >> frequency, what are these values for? >> >> As per the wiki and Javadocs, different channel types may have a >> different clock frequency than the sequencer's. In your case you configured >> it to 2MHz in your channel configuration. Higher rate means greater time >> resolution, but smaller maximum pulse width, since there's a limit on the >> maximum number of ticks for the pulse width of 65535. So in general, set >> the channel clock so that it is as high as possible for your maximum pulse >> width of about 2ms. >> >> > >> > I am not even sure that this is the place where to ask these questions >> as they are probably more related to our very basic knowledge on frequency >> and devices than IOIO. Please let me know if you think we should check out >> other forums. >> > >> >> All your questions so far have been quite IOIO specific so this is an >> appropriate place to ask them. >> >> > Believe it or not, we got to the magic number of 6111 by doing some >> math, but later realized that our math was totally wrong however I was >> baffled that it worked. >> > >> > We did go through the motor control page wiki, more than a few times. >> Probably not having an engineering background doesn't help at all but we >> went through a lot of material to try to get to the bottom of it and we >> still don't understand a few things, or better said, the documentation >> makes total sense to us, but we still find it difficult to convert it to >> code. >> >> Let me know if there's anything I should change on the wiki to make it >> easier to understand. Most of your questions are answered there, but maybe >> not clearly enough. >> >> > >> > On Thursday, December 25, 2014 12:52:46 AM UTC+2, Ytai wrote: >> >> >> >> First and most important, I think the way you're approaching debugging >> is inefficient. You're using a long program where a lot of things can go >> wrong to bring up some very low level functionality. A more effective >> approach would be to write a very simple program to exercise one ESC and >> make sure you got that one right. Only then move on to for motors with dumb >> logic and only then to your actual business logic. >> >> Concretely, a few things I've noticed: >> >> - Your cues are super short: 10 sequencer ticks or 16 microseconds as >> per your push method call. This makes no sense: why would you run your loop >> at 60kHz? Your ESC probably can't handle more than 100Hz or so anyway, so >> you're sending updates about 600 times faster than required and in practice >> probably saturating the Android CPU or the IOIO MCU or the USB link. >> >> - This magic 6111 number makes no sense. Unless you have a good reason >> to have chosen it, I propose that you pick your constants according to >> specs as opposed to trial and error. The latter is a sure recipe for >> unreliable results and frustration. >> >> - The code that calculates the PWM bandwidth seems to have some >> inconsistency between what the comment says and what the code does. A >> simple test program as suggested would convince you that the problem is not >> in the low level ESC support code. >> >> >> >> BTW, have you seen the wiki page about motor control. It should make >> the operation of the sequencer clear if it isn't already. >> >> >> >> Let me know if you need any more help. >> >> >> >> On Dec 24, 2014 1:31 AM, <[email protected]> wrote: >> >>> >> >>> Thank you Ytai, >> >>> >> >>> The BaseIOIOLooper starts at line 164: https://github.com/ >> cllaudiu/vespi/blob/master/vespidrone/src/ioio/rd/ >> vespidrone/QuadcopterActivity.java >> >>> The loop is at line 230. >> >>> >> >>> The pulsewidth is set starting at line 243 like this: >> >>> dcMotorNECue_.pulseWidth = mainController.getNEMotorsPowers(); >> >>> >> >>> The values returned by mainController.getNEMotorsPowers(); are set >> starting with line 260 in this file: https://github.com/ >> cllaudiu/vespi/blob/master/vespidrone/src/ioio/rd/ >> vespidrone/MainController.java >> >>> >> >>> I've also pasted the code here in case it is more convenient: >> >>> >> >>> class Looper extends BaseIOIOLooper { >> >>> /** The on-board LED. */ >> >>> >> >>> private Sequencer.ChannelCuePwmSpeed dcMotorNECue_ = new >> ChannelCuePwmSpeed(); >> >>> private Sequencer.ChannelCuePwmSpeed dcMotorNWCue_ = new >> ChannelCuePwmSpeed(); >> >>> private Sequencer.ChannelCuePwmSpeed dcMotorSECue_ = new >> ChannelCuePwmSpeed(); >> >>> private Sequencer.ChannelCuePwmSpeed dcMotorSWCue_ = new >> ChannelCuePwmSpeed(); >> >>> >> >>> private Sequencer.ChannelCue[] cue_ = new Sequencer.ChannelCue[] { >> dcMotorNECue_,dcMotorNWCue_,dcMotorSECue_,dcMotorSWCue_ }; >> >>> private Sequencer sequencer_; >> >>> >> >>> /** >> >>> * Called every time a connection with IOIO has been established. >> >>> * Typically used to open pins. >> >>> * >> >>> * @throws ConnectionLostException >> >>> * When IOIO connection is lost. >> >>> * @throws InterruptedException >> >>> * >> >>> * @see ioio.lib.util.IOIOLooper#setup() >> >>> */ >> >>> @Override >> >>> protected void setup() throws ConnectionLostException, >> InterruptedException { >> >>> showVersions(ioio_, "IOIO connected!"); >> >>> enableUi(true); >> >>> //led_ = ioio_.openDigitalOutput(0, true); >> >>> final ChannelConfigPwmSpeed dcMotorNEConfig = new Sequencer. >> ChannelConfigPwmSpeed( >> >>> Sequencer.Clock.CLK_2M, 6111, 0, new DigitalOutput.Spec(2)); >> >>> final ChannelConfigPwmSpeed dcMotorNWConfig = new Sequencer. >> ChannelConfigPwmSpeed( >> >>> Sequencer.Clock.CLK_2M, 611, 0, new DigitalOutput.Spec(3)); >> >>> final ChannelConfigPwmSpeed dcMotorSEConfig = new Sequencer. >> ChannelConfigPwmSpeed( >> >>> Sequencer.Clock.CLK_2M, 6111, 0, new DigitalOutput.Spec(4)); >> >>> final ChannelConfigPwmSpeed dcMotorSWConfig = new Sequencer. >> ChannelConfigPwmSpeed( >> >>> Sequencer.Clock.CLK_2M, 6111, 0, new DigitalOutput.Spec(5)); >> >>> >> >>> final ChannelConfig[] config = new ChannelConfig[] { dcMotorNEConfig, >> dcMotorNWConfig,dcMotorSEConfig,dcMotorSWConfig }; >> >>> sequencer_= ioio_.openSequencer(config); >> >>> sequencer_.waitEventType(Sequencer.Event.Type.STOPPED); >> >>> while (sequencer_.available() > 0) { >> >>> push(); >> >>> } >> >>> sequencer_.start(); >> >>> } >> >>> >> >>> /** >> >>> * Called repetitively while the IOIO is connected. >> >>> * >> >>> * @throws ConnectionLostException >> >>> * When IOIO connection is lost. >> >>> * @throws InterruptedException >> >>> * When the IOIO thread has been interrupted. >> >>> * >> >>> * @see ioio.lib.util.IOIOLooper#loop() >> >>> */ >> >>> @Override >> >>> public void loop() throws ConnectionLostException, >> InterruptedException { >> >>> //led_.write(!button_.isChecked()); >> >>> push(); >> >>> //servo_.setDutyCycle(0.05f + _varValue * 0.05f); >> >>> //led_.setDutyCycle(0.05f + _varValue * 0.05f); >> >>> //led_.setPulseWidth(1500); >> >>> } >> >>> private void push() throws ConnectionLostException, >> InterruptedException { >> >>> dcMotorNECue_.pulseWidth = mainController.getNEMotorsPowers(); >> >>> dcMotorNWCue_.pulseWidth = mainController.getNWMotorsPowers(); >> >>> dcMotorSECue_.pulseWidth = mainController.getSEMotorsPowers(); >> >>> dcMotorSWCue_.pulseWidth = mainController.getSWMotorsPowers(); >> >>> sequencer_.push(cue_, 10); >> >>> } >> >>> >> >>> >> >>> >> >>> >> >>> The motor pusleWidth is read from this code: >> >>> // motorsPowers comes as values between 0 and 255 >> >>> // needs to be transformed to values from 0 to 1 >> >>> // Math.round((1060.f + (800 * motorsPowers.nw * 0.39f)) * 2); >> >>> >> >>> uno_varValue = Math.round(1060 + (motorsPowers.nw * 8)); >> >>> due_varValue = Math.round(1060 + (motorsPowers.ne * 8)); >> >>> tre_varValue = Math.round(1060 + (motorsPowers.sw * 8)); >> >>> qua_varValue = Math.round(1060 + (motorsPowers.se * 8)); >> >>> >> >>> The above code works but as the numbers we've set make no real sense >> to us we are not sure why it works and if it works at the optimal >> parameters. >> >>> Here is a video with it working: https://www.youtube. >> com/watch?v=qXMKKL9BIT8 >> >>> >> >>> Thank you very much for looking into this. We plan to actually write >> an article after we get to the bottom of it as it will hopefully help >> others as well. >> >>> Claudiu >> >>> >> >>> >> >>> >> >>> On Wednesday, December 24, 2014 8:34:40 AM UTC+2, Ytai wrote: >> >>>> >> >>>> The multiplication by 2 is only valid for the 2MHz case, where each >> tick is half a microsecond or each 2 ticks are one microsecond. >> >>>> The ESC is likely not arming as result of sending the wrong pulse >> width. Your example doesn't show how you set the pulse width. Also, you >> seem to be setting the default pulse width to 0 instead of 1060us, which >> means that every time you stall the sequencer you'd get 0, which the ESC is >> likely to interpret as a fault. >> >>>> If you send me the entire code in question (please reduce it to the >> minimum that demonstrates your problem) I can probably help you find your >> actual problems and save a few email round trips. >> >>>> >> >>>> On Dec 22, 2014 3:20 AM, <[email protected]> wrote: >> >>>>> >> >>>>> One more thing. >> >>>>> >> >>>>> If I switch to: >> >>>>> new Sequencer.ChannelConfigPwmSpeed(Sequencer.Clock.CLK_250K, 750, >> 0, new DigitalOutput.Spec(2)); >> >>>>> which would translate to the same input PWM freq as with the 2MHz >> example, the ESC does not arm. >> >>>>> >> >>>>> I feel I am missing something basic but I am not sure what. >> >>>>> >> >>>>> Claudiu >> >>>>> >> >>>>> On Monday, December 22, 2014 12:54:19 PM UTC+2, >> [email protected] wrote: >> >>>>>> >> >>>>>> Thank you Ytai for jumping in. >> >>>>>> >> >>>>>> Just tried the value of 40000. The ESC does not arm. >> >>>>>> Also tried the value of 2000 (the ESC specs state the Input PWM is >> 1KHz). Same behavior. >> >>>>>> >> >>>>>> When switching back to 6100 (it works with 6200 as well and any >> value in between) things work again. >> >>>>>> Does this mean that the ESC input PWM freq is at around 350Hz? >> >>>>>> Do you have any idea how I could probe this? >> >>>>>> >> >>>>>> When you say we feed the pulse width in microseconds instead of >> ticks, what do you mean? >> >>>>>> The difference I see from your way of feeding the pulseWidth is >> that you multiply by 2. Is this how it should be converted from >> microseconds to ticks? >> >>>>>> If we multiply by 2 like in your example, the ESC doesn't arm. >> >>>>>> >> >>>>>> Thank you, >> >>>>>> Claudiu >> >>>>>> >> >>>>>> On Sunday, December 21, 2014 7:45:15 PM UTC+2, Ytai wrote: >> >>>>>>> >> >>>>>>> Is it possible that you're confusing the motor (output) PWM rate >> with the control (input) PWM? >> >>>>>>> Hobby ESC are almost universally 50Hz (20ms) input. >> >>>>>>> You also used the wrong type of PWM channel and fed the pulse >> width in microseconds instead of ticks. >> >>>>>>> >> >>>>>>> So I imagine you should need something like: >> >>>>>>> >> >>>>>>> // 40000 ticks @ 2MHz == 20000us == 20ms >> >>>>>>> // Also, the PWM rate corresponds to SPEED not POSITION >> >>>>>>> new Sequencer.ChannelConfigPwmSpeed(Sequencer.Clock.CLK_2M, >> 40000, 0, new DigitalOutput.Spec(2)); >> >>>>>>> >> >>>>>>> ... >> >>>>>>> >> >>>>>>> // Then: >> >>>>>>> ChannelCuePwmSpeed cue; >> >>>>>>> >> >>>>>>> ... >> >>>>>>> float power = <value between 0 (stopped) and 1 (full speed)>; >> >>>>>>> cue.pulseWidth = Math.round((1060.f + (800 * power)) * 2); >> >>>>>>> >> >>>>>>> On Sun, Dec 21, 2014 at 4:29 AM, <[email protected]> >> wrote: >> >>>>>>>> >> >>>>>>>> Hello, >> >>>>>>>> >> >>>>>>>> I am having troubles understanding how the Sequencer works for >> controlling a couple of ESCs. >> >>>>>>>> The ESCs are Afro ESC from HobbyKing. Their manual can be here: >> http://www.hobbyking.com/hobbyking/store/uploads/745353836X1335174X28.pdf >> >>>>>>>> According to their specs: For PWM input, motor stop / arm below >> 1060µs, full power at 1860µs. >> >>>>>>>> That would be the pulse width for controllig the speed of the >> motor, right? >> >>>>>>>> >> >>>>>>>> The code we have in our Android app to initiate the sequencer: >> >>>>>>>> final ChannelConfigPwmPosition motorESC = new Sequencer. >> ChannelConfigPwmPosition( >> >>>>>>>> Sequencer.Clock.CLK_2M, 6120, 0, new DigitalOutput.Spec(2)); >> >>>>>>>> >> >>>>>>>> the cue value is set to 10: sequencer_.push(cue_, 10); >> >>>>>>>> >> >>>>>>>> and the value that we send for the pulse width are generated as >> follows: >> >>>>>>>> uno_varValue = (int) (1060 + (motorsPowers.nw * 8)); >> >>>>>>>> >> >>>>>>>> The motor is running with these values but I just can't >> understand why. >> >>>>>>>> Some of the questions we have: >> >>>>>>>> - why only values around 6100 work for the period. The ESC >> should have an input freq of 1 Khz (according to this: >> http://www.hobbyking.com/hobbyking/store/__52338__Afro_ >> ESC_30Amp_Multi_rotor_Motor_Speed_Controller_SimonK_ >> Firmware_US_Warehouse_.html) though the manual says this: The default >> PWM frequency is 18kHz with 800 distinct steps, but may be adjusted to any >> frequency. >> >>>>>>>> - why only 2Mhz clock values worked. I was not able to make it >> work with other values >> >>>>>>>> - if the documentation says that 1860µs is the value for full >> power, why I don't get full power when I send 1860(I can send up to 2500 >> and there is a clear change in the motor speed) >> >>>>>>>> >> >>>>>>>> The whole code can be found here: https://github.com/ >> cllaudiu/vespi/tree/master/vespidrone/src/ioio/rd/vespidrone >> >>>>>>>> >> >>>>>>>> We are trying to build a drone based on Android and we have some >> stability issues at this stage. It flies but we find it hard to control it. >> Before getting into PID tuning and other elements we want to make sure that >> the Android - IOIO part works perfectly and that we understand what happens >> there. Thank you for your time. >> >>>>>>>> >> >>>>>>>> -- >> >>>>>>>> You received this message because you are subscribed to the >> Google Groups "ioio-users" group. >> >>>>>>>> To unsubscribe from this group and stop receiving emails from >> it, send an email to [email protected]. >> >>>>>>>> To post to this group, send email to [email protected]. >> >>>>>>>> Visit this group at http://groups.google.com/group/ioio-users. >> >>>>>>>> For more options, visit https://groups.google.com/d/optout. >> >>>>> >> >>>>> -- >> >>>>> You received this message because you are subscribed to the Google >> Groups "ioio-users" group. >> >>>>> To unsubscribe from this group and stop receiving emails from it, >> send an email to [email protected]. >> >>>>> To post to this group, send email to [email protected]. >> >>>>> Visit this group at http://groups.google.com/group/ioio-users. >> >>>>> For more options, visit https://groups.google.com/d/optout. >> >>> >> >>> -- >> >>> You received this message because you are subscribed to the Google >> Groups "ioio-users" group. >> >>> To unsubscribe from this group and stop receiving emails from it, >> send an email to [email protected]. >> >>> To post to this group, send email to [email protected]. >> >>> Visit this group at http://groups.google.com/group/ioio-users. >> >>> For more options, visit https://groups.google.com/d/optout. >> > >> > -- >> > You received this message because you are subscribed to the Google >> Groups "ioio-users" group. >> > To unsubscribe from this group and stop receiving emails from it, send >> an email to [email protected]. >> > To post to this group, send email to [email protected]. >> > Visit this group at http://groups.google.com/group/ioio-users. >> > For more options, visit https://groups.google.com/d/optout. >> > -- > You received this message because you are subscribed to the Google Groups > "ioio-users" group. > To unsubscribe from this group and stop receiving emails from it, send an > email to [email protected]. > To post to this group, send email to [email protected]. > Visit this group at http://groups.google.com/group/ioio-users. > For more options, visit https://groups.google.com/d/optout. > -- You received this message because you are subscribed to the Google Groups "ioio-users" group. To unsubscribe from this group and stop receiving emails from it, send an email to [email protected]. To post to this group, send email to [email protected]. Visit this group at http://groups.google.com/group/ioio-users. For more options, visit https://groups.google.com/d/optout.
