Announcement

Collapse
No announcement yet.

Bulk reads redux; confirming what is and isn't bulkable

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • Bulk reads redux; confirming what is and isn't bulkable

    Interative Opmode. 6.0 SDK. Same non-software mentor...

    So thanks to several of you the kids have been reading all sorts of info and writing CSV files to the SD card and beginning to do various analysis. But in watching their code run I need to resurrect a topic some of you helped me with last July. They have Bulk Reads working in MANUAL, and have successfully gotten their odometry deadwheels reading "simultaneously enough" to have their odometry math keep working as the robot drives around fairly quickly. In adding a bunch more hardware reads though, they kinda broke things and loop times jumped up. And things look to me like some of the reads they are doing are not "bulkable" ; and while they have all their hardware info .get's in one organized spot in loop(), right after a manual cache clear, they have mixed bulkable and non bulkable reads.

    Previously, it was kindly explained to me that the following were bulkable:
    • All digital inputs
    • All analog inputs
    • All encoder positions
    • All encoder velocities
    • All motor statuses (e.g. H-bridge over-temp)
    - Is that list exhaustive?
    - And thus are .get's via the Lynxmodule class for hub voltage and current non-bulkable?
    - How about motor *current* and such from the DCMotorEx class?

    And, while I am on the subject:
    - Does the bulk read process "care" about order? That is to say, can you freely mix hardware reads ultimately directed at things plugged into the Control Hub vs the Expansion Hub? Or is it more efficient to, say, order the series of .get's such that the Control Hub requests happen in a group followed by the Expansion Hub ones, that the bulk read process can more efficiently make use of the serial line between the hubs or something?
    - Even with bulk reads in use, what is the actual delay in hardware between, say, reading two encoders plugged into the same hub? And is that delay the same for the Control Hub as the Expension Hub? I want to make sure the heading-deviation noise/wobble they are detecting in their data is coming from their odometry pod suspension bouncing around vs possibly some time shift between actual snapshots of the encoder values.

    Thanks in advance

    Coach Z

  • #2
    Originally posted by zain View Post
    Is that list exhaustive?
    Yes.

    Originally posted by zain View Post
    Iterative OpMode
    Would suggest switching to a LinearOpMode with your own loop implemented inside runOpMode(). Linear has better stop behavior and has potentially slightly better performance since you're running on a dedicated thread instead of the main event loop.


    Originally posted by zain View Post
    And thus are .get's via the Lynxmodule class for hub voltage and current non-bulkable?
    Correct

    Originally posted by zain View Post
    How about motor *current* and such from the DCMotorEx class?
    Not bulk readable

    Originally posted by zain View Post
    Does the bulk read process "care" about order? That is to say, can you freely mix hardware reads ultimately directed at things plugged into the Control Hub vs the Expansion Hub? Or is it more efficient to, say, order the series of .get's such that the Control Hub requests happen in a group followed by the Expansion Hub ones, that the bulk read process can more efficiently make use of the serial line between the hubs or something?
    With the current SDK structure, there should be absolutely no difference since every command is blocking until an ACK/NAK/response is received from the module.

    Originally posted by zain View Post
    Even with bulk reads in use, what is the actual delay in hardware between, say, reading two encoders plugged into the same hub? And is that delay the same for the Control Hub as the Expension Hub?
    It will be the same between the ExH and CH, yes, since both use the same Lynx hardware controller board. In terms of the actual delay difference, I don't have firmware source code but I'd expect what's going on is that the firmware just does a memcpy and dumps all 4 encoder values into the datagram struct at the same time, so for all intents and purposes I would say you can assume they're read at exactly the same time.

    Comment


    • #3
      Thanks a ton. The kids and I sorta assumed more of the reads would be bulkable and did indeed mix in a bunch of non-bulkable ones.

      Would suggest switching to a LinearOpMode with your own loop implemented inside runOpMode(). Linear has better stop behavior and has potentially slightly better performance since you're running on a dedicated thread instead of the main event loop.
      Interesting. We started with iterative a long time ago with the team that graduated and never really reconsidered the choice. Is the higher performance in loop times or in the consistency of the loop times?

      And for stop behavior, do you mean vs the stop() method which in iterative opmode seems to me to not always run or complete running before the kid's code gets shut down by whatever code surrounds it and forces a shutdown when the stop button is pressed?

      Comment


      • #4
        Originally posted by zain View Post
        Interesting. We started with iterative a long time ago with the team that graduated and never really reconsidered the choice. Is the higher performance in loop times or in the consistency of the loop times?
        Theoretically a little bit of both. I have not done any quantifiable testing to verify this is the case, though.

        Originally posted by zain View Post
        And for stop behavior, do you mean vs the stop() method which in iterative opmode seems to me to not always run or complete running before the kid's code gets shut down by whatever code surrounds it and forces a shutdown when the stop button is pressed?
        The SDK cannot actually force a shutdown for either an iterative OpMode or a LinearOpMode**. In the end, the user code must cooperate. In a normal iterative OpMode, because the user code runs on the main event loop (which is where robocol commands are processed) the Robot Controller does not even process the stop command until the user code returns from loop(). Loop() is monitored by a watchdog which growls (and reboots the entire app) if user code does not return within 5 seconds, but this can still lead to less than ideal stop responsiveness. In a LinearOpMode, the stop command is processed completely independently over user code, and the SDK sets the interrupted flag on the LinearOpMode thread, which causes any hardware transactions in flight to be cancelled and turns any new transactions into "noops".

        **There is one last ditch effort the SDK tries before rebooting the entire app due to rogue user code, which is that just before rebooting it, there is a 100ms window during which hardware calls will actually throw a custom checked exception instead of just being noops, which bubble up to the SDK internals and allows the rogue code to be terminated. But that only works if the user code makes a hardware call.

        Comment


        • #5
          Will have to try a summer project with any interested kids to compare loop times between the two. This pic shows their current loop time histogram for their autonomous, which may or may not still have some vuforia elements running. And only has (I think) bulkable reads in it (though I still need to check their code). It seems pretty typical of what I've seen over the years with kids. Where the java/AndroidOS environment needs to go off and garbage collect (or whatever) at semi regular intervals.8381 loop times.png

          Comment


          • #6
            I am now much more curious about stop() though. Because I have clearly been confused as to how the stop() method was supposed to have worked (but seemingly wasn't) in interative opmode.

            I was under the impression that iterative opmode was intended to be "event driven." INIT is pressed, init() happens and then execution falls into init_loop(), where it stays until another event. Then START is pressed and start() happens and execution proceeds to loop(). And the code stays in loop until STOP is pressed, or happens because the 30 second timer expired, at which point the stop() method executes, once, and the kid's code is done.

            Right away it was apparent the stop() method did not actually behave this way. And I presumed that pressing STOP was processed without any interaction with user code, including the stop() method. Which seemed like an abrogation of the event driven modus presented, but we adapted. Are you saying that unless the kids code terminates loop() it's not possible to ever have stop() executed? And how would they even terminate loop() if iterative opmode is event driven only on button presses at the driver station?

            (Sorry, obviously ignorant MechE trying to also coach software questions here. But my even-limited understanding helps me simplify this stuff down to 13 y/o level.)

            Thanks

            Z

            Comment


            • #7
              Are you saying that unless the kids code terminates loop() it's not possible to ever have stop() executed?
              4634 has looked at this more recently than I have, but yes I believe that's right.

              And how would they even terminate loop() if iterative opmode is event driven only on button presses at the driver station?
              The whole point of the loop() method is that it should do its thing and then terminate as quickly as possible. If you program the loop() method that way (which is important for additional reasons besides this one), then it won't take long before stop() gets called.

              Comment


              • #8
                The whole point of the loop() method is that it should do its thing and then terminate as quickly as possible. If you program the loop() method that way (which is important for additional reasons besides this one), then it won't take long before stop() gets called.
                Yes of course. As you can see in the histogram above, their code typically executes in 15-20 mS or so. As their code reads sensors, thinks, and outputs commands over and over in loop().

                So my poorly phrased question was more along the lines of: When does loop() stop being called and stop() called instead? I would have thought that it would be when the STOP button was pressed. Or when the watchdog fails to see loop() end. Is that not the case? Sometimes it seems to me like it's not the case. Hence my query.

                Are we sure pressing the stop button invokes the stop() method in iterative opmode?

                Comment


                • #9
                  Originally posted by zain View Post

                  So my poorly phrased question was more along the lines of: When does loop() stop being called and stop() called instead? I would have thought that it would be when the STOP button was pressed. Or when the watchdog fails to see loop() end. Is that not the case? Sometimes it seems to me like it's not the case. Hence my query.
                  When the stop command is sent over from the Driver Station, it is held in queue until the main event loop processes it. That can only happen after loop() returns, since calling loop() is one of the things the event loop does. What *should* happen is that after loop() returns and the event loop processes the stop command, it will no longer call loop(), and instead call stop() one time before transitioning to the StopRobot OpMode.

                  Comment


                  • #10
                    That definitely sounds like my impression of how it's supposed to work. But some number of SDK revisions ago, we somehow became convinced it might not. Or not always. Perhaps erroneously.

                    I do get how stop() can't happen until loop() completes its last go after STOP has been pressed. So there might be a short delay. But assuming a normally operating loop() are there conditions where event loop might skip stop()?

                    How about at the end of autonomous? If the kid's code just keeps looping away in loop(), and the 30 second timer is what stops auto, does the stop() method execute or not?

                    Comment


                    • #11
                      I would like to respectfully disagree with 4634 Programmer on one thing. I strongly prefer OpMode over linear OpMode. I have a free book for learning Java for FTC - Available here: https://github.com/alan412/LearnJavaForFTC

                      Appendix B describes Linear OpMode and my opinion for why I think it is better to not use it.

                      On the subject of your "stop" not being called. If your loop doesn't return within the "stuck time" (you can change it but you probably shouldn't change it to be longer) then the FTC SDK will kill your OpMode and call the "STOP" OpMode. (so "stop()" in your OpMode will not be called.)

                      Hope this helps,

                      Alan

                      Comment


                      • #12
                        Alan,

                        I too prefer Opmode. Mostly because for a crusty old MechE like myself it seems like the obvious way to do it. And because it let's me introduce the kids to basic stuff like events, state machines, and measurement and feedback in a clear unambiguous way. And since the first team I had started with FIRST FLL then did Arduino projects with me before moving to FTC, doing things with init() then loop() seemed obvious.

                        But back to stop(). The reason I keep asking (in poorly formed questions it seems) about stop() is that I'm fairly sure the stop90 method in the kid code doesn't do anything.

                        Now, I totally get that bad things happen if loop() takes too long. Trust me, it does not in the kids' code. As a histogram I posted above illustrates, their code runs in nominally 20mS. Longer when the OS goes off to garbage collect something at regular intervals. But nothing close to 100 mS or longer.

                        I also get how it's *supposed* to work. That once somebody presses the STOP button (and perhaps when the 30 second timer ends autonomous) after loop terminates for what will be its last time, the stop() method is supposed to occur once.

                        However... it surely doesn't look like this is the case. The last thing their main loop does is write a line of data to a CSV file. Whenever we look at that file, it will often have a truncated last line. As though loop() was stopped by some other mechanism and did NOT get to terminate in an orderly fashion upon completion. Furthermore, as a test, I added a write step to their stop() method just now that writes a final line of the form "Stop method invoked at time: " etc etc etc to their CSV file. This also is never part of the file upon inspection. Even if the STOP button is pressed while teleop is running and the file is being updated every loop() as normal.

                        It has also seemed in the past to my students that commands to halt spinning digital servos and the like in stop() did not happen either. So generations learned to not use stop(). And I sorta forgot about it. Until this thread prompted me to ask.

                        Are we sure stop() happens under the conditions it is supposed to?

                        Comment


                        • #13
                          Originally posted by zain View Post
                          ... The last thing their main loop does is write a line of data to a CSV file. Whenever we look at that file, it will often have a truncated last line. As though loop() was stopped by some other mechanism and did NOT get to terminate in an orderly fashion upon completion. Furthermore, as a test, I added a write step to their stop() method just now that writes a final line of the form "Stop method invoked at time: " etc etc etc to their CSV file. This also is never part of the file upon inspection. Even if the STOP button is pressed while teleop is running and the file is being updated every loop() as normal ...
                          Coach Z, can you try stripping code out of your opmode little by little until there is nothing left but your CSV logger. If the behavior still persists, then I would say it is either a bug in the SDK or a bug in your CSV logger.
                          ULTIMATE GOAL robot reveal coming soon to https://www.facebook.com/groups/ninejabots

                          Comment


                          • #14
                            It would be helpful to at least have access to your code, so that we can try to reproduce the issue locally.

                            Comment


                            • #15
                              So I got a moment on the kids' robot and made a progressively more stripped down version of their code. No version ever appeared to run the few lines of code in the stop method. And every result was the same truncated last line of the CSV file. Which struck me as odd. Since if loop() was getting aborted somehow, why should it always have been during the write process for one new line of the CSV file? Certainly before I started taking things out, all kinds of other stuff was going on in loop() before that one try/catch that wrote a long string.

                              Thus, I tinkered with one line of code in the stop() method instead. To wit:

                              public void stop() {
                              try {
                              datawriter.write("Stop method invoked at runtime: " + String.format("%.3f", runtime.seconds()) + "\n");
                              datawriter.close(); // if this line is not present, the resulting CSV file is truncated on pressing STOP. If it is present, the CSV file is complete and includes text from line above
                              } catch (IOException e) {
                              e.printStackTrace();
                              }
                              }


                              I figured there was some sort of buffer not being handled at the end. So guessing, I tried the .close() and with the addition of the .close() things all seem to work as expected. And the string "Stop method invoked at..." is the last line of the file, even if the timer stops autonomous and the stop button is not pressed.

                              I don't know how the Writer and FileWriter classes work. I just had the kids copy from some examples ya'll linked to a prior question. But clearly the bug was on our end.

                              Comment

                              Working...
                              X