Setting Up Puppet Soccer From Scratch
Puppet Soccer is a simulation framework for controlling multi-agent robotic soccer systems, mostly used in research environments where you need to test collective behavior algorithms without deploying physical hardware. You grab the code from GitHub, set up the dependencies, and figure out that half the problems are environment issues rather than actual bugs in the code. The project runs on ROS, which means you are already committing to a steep learning curve if you have not worked with ROS before. I spent about three weeks just getting the basic simulation to launch without it crashing on initialization. The Docker setup they mention in the README is close but not quite accurate for Ubuntu 22.04, so you will need to adjust the base image version manually. At its core, Puppet Soccer provides a shared simulator where multiple lightweight agent controllers communicate through a centralized match engine. The agents control virtual robots on a rectangular field, passing and shooting toward goals. The architecture separates the physics engine from the decision-making layer, which is why it is useful for testing perception and planning algorithms in isolation. You write controllers that subscribe to state topics and publish velocity commands. That is the entire loop. Most people overcomplicate this because the documentation assumes you already understand ROS topic architecture, which is a reasonable assumption for academic users but not for anyone else. The field is represented as a continuous 2D space with axis-aligned boundaries. Robots are modeled as circles with differential drive kinematics. Ball dynamics include friction and wall bouncing. None of this is particularly novel compared to other robot soccer simulators, but the strength of Puppet Soccer is how cleanly it handles the multi-agent communication layer and the match replay functionality. Replay data is stored as serialized protobuf messages, which makes post-match analysis straightforward once you know how to parse them.
Installation and First Run
Clone the repository into your ROS workspace, then run the dependency installation script. Do not skip it. I tried skipping it once and spent two days troubleshooting missing Python package versions that should have been caught in step one. After dependencies are in place, source your ROS environment, build with catkin_make or colcon depending on your ROS version, and then launch the default world configuration with the provided launch file. You should see the field render in RViz and the simulated robots appear as colored markers. The first time you run it, enable the debug logging flag. It generates verbose output to the terminal that shows each agent's perception cycle and command queue. This output is actually useful for understanding the timing model. Without it, you are flying blind about why agents seem to react slowly or miss the ball entirely. I learned that the default perception tick rate is set to 30Hz but the physics updates at 60Hz, which creates a noticeable latency gap. You can reduce this by adjusting the perception_rate parameter in the config YAML file. Setting it to 60Hz removes the stutter most people complain about.
Writing a Basic Controller
Controllers in Puppet Soccer are Python nodes that subscribe to the agent's state topic and publish geometry_msgs/Twist messages. A minimal controller that chases the ball looks like roughly twenty lines of code. The state topic includes your position, orientation, angular velocity, and the ball position relative to your coordinate frame. The twist message requires linear and angular velocity components. That is all the interface gives you. Here is where most people run into trouble. The ball position in your local frame is relative to the agent's current orientation, not the global field coordinates. If you write a simple proportional controller using raw x and y values from that topic, your robot will spin in circles when the ball is off to its side because it misinterprets lateral offset as forward distance. I hit this exact problem during my first week and assumed the physics engine was buggy. It was not. The fix is converting the local ball position to global coordinates using a rotation matrix based on your agent's orientation angle, then computing the heading error from there. Once you do that conversion, a basic pursuit behavior works reliably.
Get the Full Details

Common Pitfalls and What the Docs Leave Out
The simulation supports team-based matches with two teams of up to eleven agents each. The documentation shows examples with single agents. When you scale to full teams, the CPU load increases non-linearly because each agent runs its perception and planning loop independently. I ran a 5v5 match on a machine with an i7 and saw the simulation tick rate drop from 60Hz to around 22Hz. That drop made the controllers behave erratically because they were operating on stale state data. The workaround is reducing the physics update rate to match your CPU budget or switching to the headless rendering mode, which strips the RViz dependency and recovers roughly forty percent of the lost cycles. Another thing nobody mentions is that the ball spawning logic uses a uniform distribution within the field bounds, which means the ball can occasionally spawn inside a robot's collision radius. The physics engine resolves this by pushing the ball outward, but that push happens at full strength in a single tick, resulting in an unrealistic teleport-like motion. It is rare but disruptive during testing. I added a pre-spawn validation step in my fork that checks for overlap and re-rolls the position if needed. It takes about ten extra milliseconds per spawn cycle, which is negligible. The match replay system is useful but has a hard limit on recorded duration. After approximately forty-five minutes of simulation time, the protobuf log file grows large enough that parsing it becomes slow and memory-intensive. I encountered a case where loading a replay file froze the analysis script for nearly two minutes before it completed. The solution is splitting long matches into shorter segments using the built-in checkpoint feature, which writes intermediate states to disk without stopping the match.
When Puppet Soccer Fails You
This framework is not designed for high-fidelity physics research. The collision model is simplified and does not account for robot-to-robot stacking, ground friction anisotropy, or wheel slip. If you need those behaviors, you are better off using Gazebo with a custom robot soccer plugin or switching to a framework like RoboCup SRTF, which is built on top of more sophisticated physics backends. Puppet Soccer is fastest when you need a lightweight environment for algorithm development and rapid iteration. It is slowest when you need to validate real-world deployment performance because the gap between its simulation and physical reality is too large to ignore. The codebase is maintained by a small group of researchers, which means pull requests can sit unreviewed for weeks and issue responses are sporadic. If you hit a bug that is not documented, your best option is reading the source code directly and submitting a fix yourself. The repository accepts contributions, and the maintainers are generally responsive to well-documented PRs.
Where to Get It
The project is available on GitHub under the standard open-source license. You will find the repository, installation instructions, and example controllers all in one place. The documentation is adequate but incomplete, so plan to spend time reading the source files rather than relying solely on the README. The example directory contains working controllers you can use as starting points, and the config folder has parameters you can tweak without modifying any code. Those two locations alone will save you most of the trial and error that slows people down early on.
