Looking to simulate CANCoders with Swerve Drive and the Magicbot framework

I’ve seen a few recent messages on using CTRE devices within simulation. I’ve tried creating a physics.py and modify some examples I’ve seen to simulate our swerve drivetrain, but even in debugging, I don’t see the CANCoder devices and can’t seem to get them to be recognized by the Sim.
Even without a physics.py, I can run the sim and I see all 8 TalonFX motors represented. When I run wpilib.simulation.SimDeviceSim.enumerateDevices() in debug, I see all the same devices that the Simulator shows (which makes sense). How do I get it to recognize, or how do I add the CANCoder used for steer positioning? If I get that, I should be able to work out feeding the values to the sim objects and updating positioning, etc.
I suppose the only other issue might be how to work with the integrated sensor on the drive Falcon, but at least that object is showing in the sim.
Finally, since MagicBot does a lot of things “automagically” would it be better to use the commandv2 framework and define sim objects more explicitly with the built-in CTRE sim methods?
Thanks for any clues that can be provided.

Edit: Here’s the basic, untested, SwerveDrive code I’d like to test in simulation: https://github.com/FROG3160/2022-rapid-react/tree/PhysicsSimulation/roborio

If it’s not showing up in “Other Devices” in the sim GUI, you need to petition @Jacob_C to add support inside the CTRE library itself. A cursory look at the CANCoder headers indicates they don’t have any of the other sim things that the other devices have, so it probably isn’t supported at this time.

I’m not sure that I understand your question?

I think what you’re asking is “how do I get a reference to a CTRE object from physics.py so I can use the CTRE sim collection methods”.

The answer at this time is … well, you can’t.

I’ve tried to hide away all simulation stuff and separate it completely from the robot, but it does seem that with all the new sim things requiring the original instances of the objects that were created, I have probably lost this battle.

As of pyfrc 2022.0.1, your PhysicsEngine object can accept a second argument to its constructor, and it will be an instance of your robot class. PhysicsEngine is created right after robotInit is called, so you should have access to all of your motors and such. If you run python robot.py create-physics there’s an example that includes this.

Thanks for your response, @virtuald. It sparked a memory for me, because I thought I had remembered several weeks ago that I had confirmed CANCoders WERE supposed to be available in simulation. So I just went back to find why I thought that and I found Simulation — Phoenix documentation.

Then I noticed on that page this note: “To view simulated devices in the WPILib simulation GUI, use the WPI_* class extensions.” I went back and immediately changed my CANCoder objects to WPI_CANCoder objects and they now show up in the sim!

I love it when it turns out to be a simple oversight. I had just about given up on trying to get simulation working this year. And with the knowledge about passing my robot to PhysicsEngine, I think I can get this thing licked!

@virtuald , on a sidenote, where were you able to view the headers for the CANCoder? Is that something we all can look at? I’m guessing the WPI_ classes add the functionality to populate the simulation with the objects created from getSimCollection() methods on each ctre object?

I just always like to understand a little deeper what I’m working with. :slight_smile:

The easiest way to find them is in the wheel for robotpy-ctre. They are also installed to your site-packages/ctre/… something folder.

They’re downloaded as part of the build process, so I actually went and viewed them by looking at my local build directory. I don’t recommend that.

So as suggested I ran python robot.py create-physics and started working with the created physics.py file. My first problem was that typing.TYPE_CHECKING evaluated to false so it wouldn’t import my robot class. I’m not even sure why it’s checking, but I commented that out. I got an error on line 145 of pyfrc/physics/core.py saying AttributeError: 'FROGbot' object has no attribute 'robot' (FROGbot is the name of the robot instead of MyRobot). I then tried passing the class instead of the example string, and also an instance of the class but they both hit the exception on line 154 of core.py.

I then abandoned trying to get it to take my robot class, and tried working with CTRE’s built in getSimCollection method. To do that I had to create an instance of FROGbot as an attribute to PhysicsEngine, then call it’s create_objects() method just to get the TalonFX and CANCoder instances with the correct CAN IDs. I WAS able to update the motor’s percentOutput as shown in the Sim GUI, but this seems like a really convoluted way to go about getting the sim updated.

Also, I found that I have no access to my swerve module classes and my swerve chassis. I assume it’s because those only get created during the injection that occurs when originally creating the magicrobot object.
I’m wondering if anyone has better ideas, or knows how to fix my original issue with passing my robot class to the PhysicsEngine init().

The code can be found here: https://github.com/FROG3160/2022-rapid-react/tree/PhysicsSimulation/roborio

That sounds like a bug in the terrible hack that I used to get the robot instance. What version of Python do you have?

v3.9.9

1 Like

Hadn’t tested the original hack on magicbot. pyfrc 2022.0.2 fixes the issue.

That fix did remove the exception I was getting. So now I’m able to set an attribute for PhysicsEngine during init:

    def __init__(self, physics_controller: PhysicsInterface, robot: "FROGbot"):
        """
        :param physics_controller: `pyfrc.physics.core.Physics` object
                                   to communicate simulation effects to
        :param robot: your robot objet
        """

        self.physics_controller = physics_controller
        self.robot = robot

It is now at least updating the drive motor percentOutput settings. I’ll have to work with it to see what else I can accomplish. Hopefully I can work with the ChassisSpeeds and drive() methods I’ve already defined in the robot to update the sim.

I would avoid doing that (and that’s one of the reasons why I’ve resisted putting the robot in the physics.py for as long as I have…). The sim should be only computing things using motor values and ‘actual’ physical positions as computed by the sim, not feedback being collected in your code. They’re potentially running at different rates, among other reasons.

There is a swerve drive simulation in pyfrc that you might find useful for this task.

Thanks, I did see that before, but I took more time tonight and worked out how to use it. I now have a fully functioning swerve drive with field oriented movement in simulation. The only piece I’m missing now is getting this year’s field in the simulation and getting our robot’s dimensions and starting field position to update. My config.json in the sim directory doesn’t seem to have any effect, so I’m open to suggestions. :slight_smile: Thanks for your help, as always, @virtuald.

For any that are interested in looking at the code, our pre-release Alpha 2 version is here: GitHub - FROG3160/2022-rapid-react at f7817149088ead635b0e9bf3d56835ffa6a7931b

pyfrc puts the field in simulation by default, but you just can’t see it. In the sim menu, go to “NetworkTables” then “SmartDashboard” and then ensure “Field” is selected. You can move the robot using the PhysicsInterface.move_robot function:

https://robotpy.readthedocs.io/projects/pyfrc/en/stable/physics.html#pyfrc.physics.core.PhysicsInterface.move_robot

It just uses wpilib.Field2d, which is available as the ‘field’ attribute of the PhysicsInterface object. There’s other things you can do with the field object, refer to the wpilib docs.

Hmm, what if we want to add the field to SmartDashboard on the real robot? Do I have to have an isReal() check?

Nope. You can add multiple fields.

This topic was automatically closed 365 days after the last reply. New replies are no longer allowed.