The controller is the brain. Picking one depends on scale, language and how much you want to build yourself.
Controller comparisons usually turn into feature tables, which is the least useful way to choose one. In practice the decision comes down to three questions: what language your team will actually maintain, how much availability you genuinely need, and whether you are building a product or running a network.
Carrier-grade, distributed, built for high availability. Java.
Modular, model-driven, huge plugin ecosystem. Java.
Lightweight, Python, ideal for learning and prototypes.
Compact, production-focused, config as YAML.
A lab or a single campus fabric does not need a distributed controller cluster, and running one adds operational weight for no benefit. A carrier network does, and choosing a lightweight controller there means building the clustering yourself — which is how projects end up maintaining infrastructure instead of networks.
You will be debugging this at three in the morning. The controller written in the language your team knows is the right controller, and that consideration outranks most of the feature comparisons published about them.
Learning or prototyping? Start with Ryu. Running a real fabric? Look at ONOS or Faucet. Need deep integration with existing systems and a model-driven approach? OpenDaylight, with the understanding that its flexibility costs setup time. See our tools and reading list.