Thank You Ytai! It works. Such a relief to not be using magic numbers anymore :)
I got both the sequencer and the pwmoutput method working. We did a basic program as suggested and after that worked used the setup in our code and that worked as well. I'll probably need to use a frequency around 500Hz as the quad is less responsive than with the magic numbers, but at least now I know what I am doing and have a good understanding of it. I will write an article about it, hopefully making it easy for others as well. Will let you know when it's done. Thank you. Claudiu On Friday, December 26, 2014 6:30:15 PM UTC+2, Ytai wrote: > > 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] <javascript:>> 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] <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.
