{"id":39566738,"url":"https://github.com/dmrokan/focont","last_synced_at":"2026-01-18T07:14:50.052Z","repository":{"id":57431672,"uuid":"338775620","full_name":"dmrokan/focont","owner":"dmrokan","description":"Static output feedback (SOF) and fixed order (FO) controller design package for Python","archived":false,"fork":false,"pushed_at":"2024-09-28T10:07:10.000Z","size":158,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-10-27T04:33:37.622Z","etag":null,"topics":["control","controller","fixed-order","optimal","sof"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dmrokan.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-02-14T10:06:25.000Z","updated_at":"2024-09-29T11:36:28.000Z","dependencies_parsed_at":"2024-08-19T19:43:16.204Z","dependency_job_id":null,"html_url":"https://github.com/dmrokan/focont","commit_stats":{"total_commits":8,"total_committers":1,"mean_commits":8.0,"dds":0.0,"last_synced_commit":"f9d1c30c555525e4fdadb0e3503e00f1f8547f2d"},"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/dmrokan/focont","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dmrokan%2Ffocont","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dmrokan%2Ffocont/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dmrokan%2Ffocont/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dmrokan%2Ffocont/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dmrokan","download_url":"https://codeload.github.com/dmrokan/focont/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dmrokan%2Ffocont/sbom","scorecard":{"id":348316,"data":{"date":"2025-08-11","repo":{"name":"github.com/dmrokan/focont","commit":"0543ef42f7cf9cac39bc575381b7e799515cedf6"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":4.2,"checks":[{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Code-Review","score":0,"reason":"Found 0/21 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: jobLevel 'contents' permission set to 'write': .github/workflows/build.yml:76","Warn: no topLevel permission defined: .github/workflows/build.yml:1"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/build.yml:17: update your workflow using https://app.stepsecurity.io/secureworkflow/dmrokan/focont/build.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/build.yml:22: update your workflow using https://app.stepsecurity.io/secureworkflow/dmrokan/focont/build.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/build.yml:41: update your workflow using https://app.stepsecurity.io/secureworkflow/dmrokan/focont/build.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/build.yml:59: update your workflow using https://app.stepsecurity.io/secureworkflow/dmrokan/focont/build.yml/main?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/build.yml:64: update your workflow using https://app.stepsecurity.io/secureworkflow/dmrokan/focont/build.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/build.yml:81: update your workflow using https://app.stepsecurity.io/secureworkflow/dmrokan/focont/build.yml/main?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/build.yml:86: update your workflow using https://app.stepsecurity.io/secureworkflow/dmrokan/focont/build.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/build.yml:127: update your workflow using https://app.stepsecurity.io/secureworkflow/dmrokan/focont/build.yml/main?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/build.yml:132: update your workflow using https://app.stepsecurity.io/secureworkflow/dmrokan/focont/build.yml/main?enable=pin","Warn: pipCommand not pinned by hash: .github/workflows/build.yml:29","Warn: pipCommand not pinned by hash: .github/workflows/build.yml:34","Info:   0 out of   6 GitHub-owned GitHubAction dependencies pinned","Info:   0 out of   3 third-party GitHubAction dependencies pinned","Info:   0 out of   2 pipCommand dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"SAST","score":0,"reason":"no SAST tool detected","details":["Warn: no pull requests merged into dev branch"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE.md:0","Info: FSF or OSI recognized license: MIT License: LICENSE.md:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Packaging","score":10,"reason":"packaging workflow detected","details":["Info: Project packages its releases by way of GitHub Actions.: .github/workflows/build.yml:46"],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Signed-Releases","score":8,"reason":"1 out of the last 1 releases have a total of 1 signed artifacts.","details":["Info: signed release artifact: focont-1.0.1-py3-none-any.whl.sigstore: https://github.com/dmrokan/focont/releases/tag/v1.0.1","Warn: release artifact v1.0.1 does not have provenance: https://api.github.com/repos/dmrokan/focont/releases/170437998"],"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'main'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}}]},"last_synced_at":"2025-08-18T07:42:35.401Z","repository_id":57431672,"created_at":"2025-08-18T07:42:35.401Z","updated_at":"2025-08-18T07:42:35.401Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28532793,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-18T00:39:45.795Z","status":"online","status_checked_at":"2026-01-18T02:00:07.578Z","response_time":98,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["control","controller","fixed-order","optimal","sof"],"created_at":"2026-01-18T07:14:49.996Z","updated_at":"2026-01-18T07:14:50.045Z","avatar_url":"https://github.com/dmrokan.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Focont\n\n**Static output feedback and fixed order controller design package for Python**\n\n## Static output feedback (SOF)\n\nSOF is the simplest feedback controller structure. More precisely, it redirects the system output to the system input after multiplying by a constant gain matrix. You can find brief information about SOF in [this page](https://otomatik.art/content/static-feedback-calculator).\n\nThe algorithm implemented in this package can calculate a stabilizing SOF gain which also minimizes the $\\mathcal{H}_2$ norm of the closed loop system. The resulting controller is comparable to the result obtained by linear quadratic regulators (LQR) with respect to the impulse response energy of the closed loop system.\n\nHowever, this algorithm works when some sufficient conditions are satisfied. If the problem parameters (listed below) is not appropriate, the algorithm fails and prints an error message. Please see the [article](https://journals.sagepub.com/doi/abs/10.1177/0142331220943071), and the [PhD thesis](http://hdl.handle.net/11693/54900), for detailed information and analysis.\n\nThe algorithm is mainly developed for discrete time systems, but it may also compute similar SOF gains for continuous time systems when this algorithm is applied to the zero-order hold (ZOH) discretized versions with a sufficiently large sampling frequency.\n\nFurthermore, the algorithm can be used to calculate fixed-order controllers. Please, check [tests](./tests/test_01.py) for examples and [docs](./docs/focont.md) for detailed information.\n\n## Installation\n\n```\npython3 -m venv venv\nsource venv/bin/activate\npip install -e '.[dev]'\npytest\n```\n\n### Also,\n\nIt can be installed via pip.\n```\npip install focont\n```\n\n## Example\n\nConsider this highly simplified real life example below which is a naively discretized version\nof the ideal Newton motion equations. It is a model of a ball's motion on an inclined plane.\n\n![Simple model](https://raw.githubusercontent.com/dmrokan/focont/main/docs/readme_example.png)\n\nIn the model, the top of plane is assumed to be the origin and force $F$ pushes the ball to\nmove it to the origin. The force $F_g$ is the effect of gravity and there is a mild friction\nwhich is damping the velocity of the ball. The system parameters are chosen in a way to make\nnumbers simpler to write.\n\n$$\np_{t+1} = p_t + 0.01 p_t + 0.1 v_t\n$$\n\nwhere $t \\in \\\\{ 0, 1, \\dots \\\\}$ is the discrete time instants, $p_t$ is the vertical position\n$v_t$ is the velocity and is accumulated in $p_t$. The term $+0.01 p_t$ is a result of\nconstant $F_g$ force which make the system unstable. Meaning that, the ball move downwards\nby accelerating when $F = 0$.\n\n$$\nv_{t+1} = v_t - 0.01 v_t + u_t \\\\\nu_t = F_t - F_g\n$$\n\nwhere $v_t$ is the velocity. The second term is damping and the last term is the force that\npushes the ball upwards which also must exceed $F_g$ to achieve this. Finally, it is assumed\nthat the sum of vertical position and velocity can be measured.\n\n$$\ny_t = p_t + v_t\n$$\n\nWith this information, the system matrices can be written as\n\n$$\nx_{t+1} = Ax_t + Bu_t, ~~~ y_t = Cx_t\n$$\n\nwhere\n\n$$\nx_t = \\begin{bmatrix}\n    p_{t} \\\\\n    v_{t}\n\\end{bmatrix} ~~ A = \\begin{bmatrix}\n    1.01 \u0026 0.1 \\\\\n    0 \u0026 0.99\n\\end{bmatrix} ~~ B = \\begin{bmatrix}\n    0 \\\\\n    1\n\\end{bmatrix} ~~ C = \\left[ 1 ~~ 1 \\right]\n$$\n\n**Problem:**\n\nCalculate a static output feedback (SOF) gain $K$ that minimizes\n\n$$\nJ = \\sum_{t=0}^{\\infty} p_t^2+u_t^2\n$$\n\nwhere force is a function of measurment\n\n$$\nu_t = Ky_t\n$$\n\nIn other terms, how can I use the measurment $y_t$ to move the ball to the top\nas quickly as possible while spending as low as energy possible. The script below\nsolves this problem.\n\n```python\nfrom focont import foc, system\n\ndef main():\n    A = [\n        [ 1.01, 0.10 ],\n        [ 0.00, 0.99 ],\n    ]\n    B = [\n        [ 0 ],\n        [ 1 ],\n    ]\n    C = [ [ 1, 1 ] ]\n    Q = [\n        [ 1, 0 ], # Weight of p_t\n        [ 0, 0 ],\n    ]\n    R = [ [ 1 ] ] # Weight of u_t\n    data = { \"A\": A, \"B\": B, \"C\": C, \"Q\": Q, \"R\": R }\n\n    pdata = system.load(data)\n    foc.solve(pdata)\n    foc.print_results(pdata)\n\nif __name__ == \"__main__\":\n    main()\n```\n\nPrints out:\n\n```bash\n Progress:      10%, dP=0.4662574448312318\n Iterations converged, a solution is found\n- Stabilizing SOF gain:\n[[-0.6147]]\n- Eigenvalues of the closed loop system:\n[0.8907 0.4946]\n |e|:\n[0.8907 0.4946]\n```\n\nMeaning that, $K$ must be chosen as $K=-0.6147$. In other words, the ball must be\npushed as strong as $K$ times the measurement $y_t$ at each time instant $t$.\n\nFor this problem, the closed loop system\nis stable when $-2 \u003c K \u003c -0.01$ according to the result of Octave's `rlocus` method. When\nthe cost $J$ is calculated for $K$ in this interval starting from inital $p_0=-1$ and $v_0=0$\nthe plot below is obtained.\n\n![Cost vs K](https://raw.githubusercontent.com/dmrokan/focont/main/docs/cost_vs_K.png)\n\nIn this plot, the minimum cost is $7.35$ when $K=-0.7306$ which is close to the cost $7.38$ at $K=-0.6147$.\n\nLet us modify the cost function and assign a higher weight to the consumed energy. Meaning that,\nwe do not care much about how quickly the ball is transported but we want to spend less energy.\n\n$$\nJ = \\sum_{t=0}^{\\infty} p_t^2+10 u_t^2\n$$\n\nIn this case, the SOF gain $K=-0.2717$ is obtained. The plot below shows the differences between\n$p_t, v_t$ and $u_t$ for both SOF gains $K$.\n\n![Results](https://raw.githubusercontent.com/dmrokan/focont/main/docs/result_plots.png)\n\n\u003e Results for different cost weigths. Position (red), velocity (yellow), force $u_t$ (blue), $J = \\sum_{t=0}^{\\infty} p_t^2+ u_t^2$ (solid), $J = \\sum_{t=0}^{\\infty} p_t^2+10 u_t^2$ (dashed)\n\n\nThe dashed lines are obtained when the consumed energy is largely penalized in the cost function.\nAs it can be seen, it gets closer to the origin slower (red), but consumed energy (blue) is smaller.\n\n**Comments:** First, I should emphasize that this is a very simplified, naive model of the\nactual physical system. How strange, in the problem's story the guy pushing the ball upwards\ncan measure the sum of position and velocity. Assume that, you are driving a car and the front\ndashboard only shows the sum of how many kilometers you have traveled and the current velocity,\ninstead of showing them separately. How would you avoid getting speeding tickets?\n\nIn many real life control problems, similar to this hypothetical example you can not be\naware of the system's all internal states but can only measure a combination (a function) of them.\n\nIn the hypothetical example above, if both state variables, $p_t$ and $v_t$ could be measured, \nthe problem would turn into a classical linear quadratic regulator (LQR, state feedback) problem \nwhich has a well-know solution.\n\nHowever, being able to measure a combination of the state variables makes the optimization problem \nmore complicated (possibly non-convex) which is the reason of undershooting blue lines in the\nplot above. Negative values in blue lines mean, the guy stops pushing and leaves the work to\nthe gravity time by time, because of the lack of full information of the system's state.\n\nMeasuring a combination of the states turns the problem into a static output feedback (SOF) problem.\nFocont implements an approximate dynamic programming based approach to the SOF problem and comes\nup with the solution above.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdmrokan%2Ffocont","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdmrokan%2Ffocont","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdmrokan%2Ffocont/lists"}