Skip to content
julesbrettle edited this page Aug 6, 2021 · 22 revisions

Table of Contents

Individual robot crashing and running the simulation with multiple flippies

Section created by J. Brettle (Summer 2021), modified by __

Getting more than 4 or 5 flippies going at a time will likely be an ongoing struggle for this project. With only 2 or 3 flippies, the simulation already at 1/3 to 1/4 speed on Jules' computer (Note: further testing indicates it works a bit faster on Olin laptops), getting progressively worse with more robots. It might be worthwhile to look into getting the program onto a more powerful system via either a physical system or cloud computing.

In addition to the rapidly slowing speeds, getting more than 4 robots to all run without crashing or not starting to begin with currently requires some manual adjustments to position and drop height (and still obtains mixed results). Worlds with multiple robots seem more likely to start successfully when they don't all start flipping at the same time, so we started dropping them onto the terrain from slightly varying heights so that Webots would not have to handle so many collisions during the same timestep. While this occasionally works, it's not a reliable or complete solution. If in the future, it is more convenient for flippies to start touching/fixed to the ground and other methods are determined for keeping the flippies from stopping, this can be relatively easily accomplished by reducing the y coordinate in offset=np.array([0, 0.08, -0.45]) in the make_flippys.py script, and potentially rotating the flippies to start level rather than angled in r1 = R.from_euler('z', -30, degrees=True).

Reducing processing power and attempted touch sensor only rulesets

Section created by J. Brettle (Summer 2021), modified by __

Much of work during Summer 2021 was spent testing the hypothesis that the flippies could operate without direct confirmation of whether they were touching the floor or another flippy, but instead with just the information of which of the four touch sensors were being used. The answer: from the Webots flippy_controller.cpp code perspective, probably. Preliminary simulations with only a few robots have had some success, the problems being mostly geometric, fixable behavioral bugs, or Webots becoming overloaded and crashing. However, to properly model flippies grabbing each other and be able to complete a movement while attached, the names of both bodies (not just one flippy) need to be sent to the physics plugin (see "The usage of exclusively 'flippy to floor' fixed joints as opposed to including 'flippy to flippy' fixed joints" section for more details).

In the 4-touch-sensor flippy model, the flippy uses the separate touch sensors at the top and bottom of it's spheres to determine if it's being walked on by another flippy. This also automatically determines right of way. If a flippy is touched on the "top," it assumes it is being walked on, and goes to the closest stable position and waits until it's been the pre-defined BRIDGE_DELAY period since it was last walked on to move again. When a flippy is touched on the bottom of its moving sphere while it's flippy, it determines that it's time to switch movingMode and flip around the other sphere (not knowing or caring if it is walking on another flippy).

The primary time it might be useful for a flippy to definitively know if it is touching another flippy, is the occasional situation where a flippy's spheres are rotated such that both touch sensors are touching the floor at the same time. In this situation, the flippy believes it should stay in a bridge state forever, and is either left behind or blocks the way for forward progress of flippies behind it.

Theory behind using 4 touch sensors

Section created by J. Brettle (Summer 2021), modified by __

In the image above, pink represents touch sensors that, when triggered, indicate to the flippy that it is being walked on. Yellow represents touch sensors that will indicate to the robot when it is time to switch the sphere it's pivoting around.

We can assume that there are very few circumstances when two "pink sensors" or two "yellow sensors" will touch each other, thus, there is always a defined right-of-way in any flippy-flippy interaction. Because all flippies rotate at the same rate (the defined velocity), two flippies in the _01 mode cannot touch each other while walking on the same plane and in the same direction.

There are a few interesting edge situations to note:

  1. Two "yellow sensors" touch when two flippies walk into each other from opposite directions This will hopefully be fixed when grabbing/joints are made to be flippy-flippy instead of just fixing the spheres spacially (see "The usage of exclusively "flippy-to-floor" fixed joints as opposed to including "flippy-to-flippy" fixed joints" section). When this is properly implemented, the flippies should just slowly slide to the floor as the blue sphere motor (m2) turns.

  2. A flippy's "pink sensor" is triggered by a platform at an angle that is too acute. This is more of a geometric problem that needs to be solved by properly positioning the flippy so that it could complete the step. If the flippy can't flip the full 90 degrees required for the "pink sensor" to become a "yellow sensor" then it wouldn't be able to start the next flip anyway.

  3. A flippy's "pink sensor" is triggered by another flippy's "pink sensor" because they are on different angled surfaces. This is another more geometric problem. The way to disassemble this bridge would be for the flippies to grab their respective floors with their green spheres instead, and for one to start walking first. How the flippies in this situation would determine that they should do that is not something currently solved, but perhaps something to explore as a future rule/behavior for flippies that have been in bridge state (movingMode 4) for extended periods of time.

(Note: another strange - and still unsolved - case is shown in video 24 where the "yellow sensor" is triggered by what the flippy is intentionally walking on, and the "pink sensor" is triggered by a weird collision with the ground. Click here to see still image of the moment in the video documentation spreadsheet.)

3 digit movingMode case numbers and control diagram

Section created by J. Brettle (Summer 2021), modified by __

https://docs.google.com/drawings/d/1jg2MxTJxfkJQajN77TrSdM6I2me4SA7FkS61Uu0N7IM/edit?usp=sharing

How joints are created

Section created by J. Zerez (Spring 2021), modified by __

A significant part of this project was working to figure out how to effectively create joints between flippys and their environment. This page will describe how the joints are crated and the methods that do not work.

What worked

Each flippy is given a unique name in the form of "F###", where ### is a zero-padded integer that represents the index of the flippy. For instance, The first flippy in the swarm is called F000, and the second is called F001 and so on. Each spherical node of each flippy is given a name derived from its parent flippy name. The first flippy will have nodes named F000_S1 and F000_S2. As the flippy starts to rotate, if the moving sphere (ie: the one actually translating through space) touches another body, the controller script sends a message to the physics plug-in with the name of the moving sphere. The physics plug-in freezes the sphere in place by adding a fixed joint between it and the floor of the world. An offset is added so that it gets fixed in place rather than snapping to the origin of the world. The other sphere gets unlocked, allowing for free rotation.

What didn't work

  • Using Webot's connector objects: The main problem with this strategy is that these connectors do not support many simultaneous connections. In other-words, only one flippy can connect to the floor at a time, unless the floor had a connector object for each flippy in the swarm, which would be inefficient and slow.

The usage of exclusively "flippy-to-floor" fixed joints as opposed to including "flippy-to-flippy" fixed joints

Section created by J. Brettle (Summer 2021), modified by __

The way flippies currently "grab" each other or the floor follows this process:

  1. The flippy registers that the touch sensor waiting to contact the ground (or a flippy) below it has been triggered
  2. The flippy controller sends two packets to the physics plugin. The first is the name of the current/old stationary sphere, and the second is the name of the current/old moving sphere.
  3. When the flippy controller has completed everything for that timestep, the physics plugin uses the first name it's given to find the body associated with the name and removes all fixed joints involving that body. It then finds the body associated with the second name and adds a fixed joint between that and the body with DEF "FLOOR".

These fixed joints retain the current translation and rotation of the the sphere relative to the floor, regardless of what the touch sensor actually contacted. If whatever the flippy touched (e.g. another flippy) doesn't stop moving, the first flippy could have its sphere fixed in mid air. What gives the illusion of flippies sticking to each other is that flippies stop when they sense they've been walked on.

Possible fix: Webots collision detection

In the flippy_physics.cpp file, there is a function called webots_physics_collide() that allows you to override the Webots normal physics simulator (Jon Zerez worked on this some, Jules Brettle did not). Having this work to properly add and remove the joints may be ideal because the function runs anytime two bodies collide, and receives the dGeomID of both bodies in the collision (as opposed to all info coming from one flippy, that only knows it's own IDs and not those of whatever it's touching). A version could be made where double-grabs (a situation where both robots grab each other, so one of them letting go wouldn't actually allow them to move) could be nullified by remove all fixed joints when releasing, even those initiated by other flippies (with real-world flippies, this might mean a different vibration signal is sent essentially saying "let me go"). Another version could be made to model double-grabs as a problem that needs to be solved another way.

Possible fix: Flippy-to-flippy communication

If the Webots collision detection avenue doesn't work, an alternative could be that flippies transmit their body name when their back/"top" touch sensors are triggered, and listen for a body name when their flipping/"bottom" touch sensor is triggered. If the receiver gets a name, it tells the physics plugin to create fixed joint between the two bodies. If it doesn't, it creates a fixed joint between it and the floor.

Flippy steering

Section created by J. Brettle (Summer 2021), modified by __

Devices

Each flippy sphere has a Hinge2Joint, controlled by two motors. The motors that cause the flipping action are named M1 and M2 (initialized in the controller as m1 and m2) and the motors that allow steering in the 2D plane are named RM1 and RM2 (initialized as rm1 and rm2). Webots motors have a motorTag->setPosition(POSITION) PID function that we use for better accuracy than checking encoder values at each timestep.

The flippy also has an Inertial Unit sensor and a GPS (both base Webots simulated sensors). The inertial unit gives roll, pitch, and yaw as a 3 element array when you call inertialUnitTag->getRollPitchYaw(). The GPS gives the x, y, and z coordinates of the robot's center (where the spheres meet) as a 3 element array when you call gpsTag->getValues().

At the beginning of each flip, the controller gets the robot's current yaw and x-z position relative to the goal point, and calculates the turnAngle (to be fed into the setPosition() function for the steering motors) that will rotate the flippy to face the goal point in 2D.

Calculations

deltaZ and deltaX are the z and x distances between the flippy's current location and the goal location. In this example, the goal point is at (0,0,0).

The first step is to calculate theta, effectively the goal yaw. It would seem ideal to use the atan() function, but that C++ function only returns angles between -pi/2 and +pi/2. To get the full -pi to +pi range, we use acos() which returns values from 0 to pi. To get the 0 to -pi part, we use the sign of deltaZ, mathematically normzEq. This gives us

thetaEq

or

theta = -acos(deltaX/sqrt(pow(deltaX,2.0)+pow(deltaZ,2.0)))*deltaZ/abs(deltaZ);

There's a negative sign because yaw is measured with positive clockwise (positive numbers to the right of the vertical axis), whereas in trig functions, angle is measured with positive counterclockwise (upwards from the horizontal axis). The 90 degree rotation of the 0 axis is handled by the adjacent side in the adjacent over hypotenuse calculation of cosine being the vertical axis rather than the horizontal one.

Flip delay / distance variable speed control

Section created by J. Brettle (Summer 2021), modified by __

To create the traffic that causes the flippies to walk over each other, they are set to wait longer between flips (essentially slowing down) when near given points in the Webots world. Between each flip, lambda/auto calcFlipDelay() is called to calculate the number of steps to wait between flips for its current position. calcFlipDelay() calculates the distance between the flippy robot and the slowCoords point and puts that distance, along with constants at the top of the controller code, into the below equation.

flipDelayEq

y is the flipDelay calculated (the number of steps waited before starting the next flip)

amax and amin are the FLIP_DELAY_MAX and FLIP_DELAY_MIN set at the start of the flippy_controller.cpp code.

x is the radial dist to the slowCoords point (given at the start of the flippy_controller.cpp code), calculated from the robot's GPS sensor output.

b is the range multiplier that defines how far the effects of the slowCoords point are felt.

In C++, this looks like flipDelay = (FLIP_DELAY_MAX-FLIP_DELAY_MIN)*exp(-dist/slowCoords[0][3])+FLIP_DELAY_MIN.

Play with it yourself: https://www.desmos.com/calculator/slvtjcosha

Using flippy_sim_autogenerated.wbt and Script Inputs

Section created by J. Zerez (Spring 2021), modified by J. Brettle (Summer 2021)

If one wishes to run a simulation with multiple flippys one must first initialize a world with the desired number of flippys. To do this quickly, use the make_flippys.py to automate the process. (Note: PROTOs cannot be used in this project because each flippy needs to have a unique ID and unique DEF to interface with the physics engine. As such, each flippy needs to be individually created.)

  1. Make sure that script_inputs/flippy_template.proto has the correct flippy to be copied.
    • Make sure to replace all DEF and name instances of F000 with F### so that the python script can properly replace them with the flippy index
    • Also remove the values after translation and rotation in the main robot definition
  2. Open make_flippys.py
  3. Scroll to if __name__=="__main__":
  4. Modify dims = np.array([ #wide, #tall, #long]) to the right flippy swarm dimensions.
    • spacing = np.array((0.15, 0.03, 0.2)) defines the spacing between each flippy (m)
    • offset = np.array([-0.15, 0.03, 0]) defines where the first flippy will be located (m)
  5. Open Powershell and cd Documents\GitHub\Webots_Playground
  6. Run python file using python make_flippys.py
    • use python3 -m pip install scipy and python3 -m pip install numpy if "ModuleNotFoundError"
  7. Open / Reload flippy_sim_autogenerated.wbt in Webots

Below is an example of how to configure the script.

if __name__ == "__main__":
  # Create a swarm of flippys that is 4 wide, 1 tall, and 2 long
  dims = np.array([4, 1, 2])
  # The average spacing between the flippys will be 0.15m, 0.03m and 0.2m
  spacing = np.array((0.15, 0.03, 0.2))
  # The first flippy will be located at:
  offset = np.array([-0.15, 0.03, 0])

  # Create the location and rotation of each flippy in the swarm, including noise
  locs, rots = create_layout(dims, spacing, offset=offset)

  # Writes the flippys to the output .wbt file
  #(./worlds/flippy_sim_autogenerated.wbt)
  make_flippys(locs, rots, write_to_wbt=True, start_index=0)

If the argument write_to_wbt is set to True in the call to make_flippys(), a file called flippy_sim_autogenerated.wbt will be created in the ./worlds directory of the project. Simply open up that file in Webots to visualize the swarm.

If write_to_wbt is set to False, a file called flippys.txt will be created in the ./script_outputs directory of the project. The text in the file must be copied into the desired world file.

More information about using this script and creating a world can be found here.