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] <javascript:>> 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] <javascript:>. > > To post to this group, send email to [email protected] > <javascript:>. > > 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.
