Skip to content

feat: add omni_angular kinematics and body-frame velocity for omni robots#270

Open
hanruihua wants to merge 12 commits intomainfrom
feat/omni_angular
Open

feat: add omni_angular kinematics and body-frame velocity for omni robots#270
hanruihua wants to merge 12 commits intomainfrom
feat/omni_angular

Conversation

@hanruihua
Copy link
Copy Markdown
Owner

@hanruihua hanruihua commented Apr 6, 2026

Resolves #214

Summary

  • New omni_angular kinematics model: adds a 3-DOF omnidirectional robot type with independent yaw rate control. Velocity is [forward, lateral, yaw_rate] in body frame, allowing the robot to translate and rotate simultaneously.
  • Refactor omni kinematics to body-frame velocity: the existing omni model now takes [forward, lateral] in body frame (relative to the robot's heading) instead of world-frame [vx, vy]. The heading (theta) is preserved in state but no longer directly controllable — use omni_angular if yaw control is needed.
  • Redesigned keyboard control (3-axis): W/S for forward/backward, A/D for turn (diff/acker) or strafe (omni/omni_angular), Q/E for rotation (omni_angular). Shift+Z/C adjusts max linear velocity, Z/C adjusts max angular velocity.
  • omni_angular dash behavior: navigates to a goal using body-frame forward/lateral movement while turning to face the target, then rotates in place to match the goal orientation.
  • New usage examples: robot_omni_angular_world.yaml and keyboard_control_pynput_omni_angular.yaml for the new kinematics type.
  • Updated docs: configuration reference, keyboard control guide, and Chinese translations updated for body-frame kinematics and new key mappings.

Test plan

  • test_kinematics.py updated with body-frame omni and new omni_angular tests
  • test_keyboard.py updated for 3-axis key_vel and new key mappings
  • test_all_objects.py updated to include omni_angular robot type
  • All existing tests pass (pytest tests/)

williamleong and others added 9 commits April 6, 2026 21:21
* chore(deps-dev): bump ruff from 0.15.6 to 0.15.7 (#259)

Bumps [ruff](https://github.com/astral-sh/ruff) from 0.15.6 to 0.15.7.
- [Release notes](https://github.com/astral-sh/ruff/releases)
- [Changelog](https://github.com/astral-sh/ruff/blob/main/CHANGELOG.md)
- [Commits](astral-sh/ruff@0.15.6...0.15.7)

---
updated-dependencies:
- dependency-name: ruff
  dependency-version: 0.15.7
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* chore(deps-dev): bump ty from 0.0.23 to 0.0.24 (#260)

Bumps [ty](https://github.com/astral-sh/ty) from 0.0.23 to 0.0.24.
- [Release notes](https://github.com/astral-sh/ty/releases)
- [Changelog](https://github.com/astral-sh/ty/blob/main/CHANGELOG.md)
- [Commits](astral-sh/ty@0.0.23...0.0.24)

---
updated-dependencies:
- dependency-name: ty
  dependency-version: 0.0.24
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* chore(deps-dev): bump pytest-cov from 7.0.0 to 7.1.0 (#261)

Bumps [pytest-cov](https://github.com/pytest-dev/pytest-cov) from 7.0.0 to 7.1.0.
- [Changelog](https://github.com/pytest-dev/pytest-cov/blob/master/CHANGELOG.rst)
- [Commits](pytest-dev/pytest-cov@v7.0.0...v7.1.0)

---
updated-dependencies:
- dependency-name: pytest-cov
  dependency-version: 7.1.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* chore(deps): bump importlib-metadata from 8.7.1 to 9.0.0 (#262)

Bumps [importlib-metadata](https://github.com/python/importlib_metadata) from 8.7.1 to 9.0.0.
- [Release notes](https://github.com/python/importlib_metadata/releases)
- [Changelog](https://github.com/python/importlib_metadata/blob/main/NEWS.rst)
- [Commits](python/importlib_metadata@v8.7.1...v9.0.0)

---
updated-dependencies:
- dependency-name: importlib-metadata
  dependency-version: 9.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* feat(usage): add CBF-QP and collision-cone examples in 21cbf_world (#258)

* feat(usage): add CBF-QP and collision-cone examples in 21cbf_world

* refactor: refine to adapt to project style

---------

Co-authored-by: hanruihua <hanrh@connect.hku.hk>

* feat: add omni_angular kinematics model with yaw_rate integration

Add a new kinematics model that extends omni with a third velocity
channel (yaw_rate) so that orientation is integrated natively inside
the simulation step, removing the need for external post-step hacks.

* docs: add omni_angular to kinematics documentation

Update README support table, YAML configuration reference, and
kinematics models card to include the new omni_angular model.

* fix(omni_angular): Refactor as suggested

* feat(docs): update configuration for omni_angular kinematics and velocity parameters

* fix(kinematics): set default alpha values for Omni and OmniAngular kinematics

* fix(tests): update test description for kinematics registry to include all built-in types

* doc: add chinese translation

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Xinyi WANG <49263319+Lawliet9666@users.noreply.github.com>
Co-authored-by: hanruihua <hanrh@connect.hku.hk>
Change omni_kinematics and omni_angular_kinematics to accept body-frame
velocity [forward, lateral] and [forward, lateral, yaw_rate] respectively.
The body-to-world rotation is now handled inside the kinematics functions.
Simplify OmniDash and OmniAngularDash behaviors to output body-frame
velocity directly. Update velocity_to_xy and compute_heading accordingly.
Expand keyboard from 2-axis to 3-axis control [linear, lateral, angular].
W/S controls forward/backward, A/D controls lateral strafe, Q/E controls
rotation. Add Shift+Z/C for max linear velocity adjustment. Simplify
per-kinematics mapping in _assign_keyboard_action.
Update omni and omni_angular kinematics tests to validate body-frame
velocity semantics. Update keyboard tests for Q/E rotation and
Shift+Z/C linear max velocity adjustment.
Restore original A/D behavior: sets key_ang for diff/acker rotation
and omni lateral movement. Q/E now sets a separate key_rot axis used
only by omni_angular for yaw rate control.
…trols

Update velocity descriptions from world-frame [vx, vy] to body-frame
[forward, lateral] for omni and omni_angular. Add omni_angular to
kinematics comparison tables. Update keyboard control key mapping docs
for A/D, Q/E, and Shift+Z/C. Update Chinese translations accordingly.
@codecov
Copy link
Copy Markdown

codecov bot commented Apr 6, 2026

Codecov Report

❌ Patch coverage is 99.73958% with 1 line in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
irsim/lib/handler/kinematics_handler.py 97.56% 1 Missing ⚠️
Flag Coverage Δ
service ?

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
irsim/env/env_base.py 95.72% <100.00%> (-0.34%) ⬇️
irsim/gui/keyboard_control.py 94.86% <100.00%> (+0.33%) ⬆️
irsim/lib/algorithm/__init__.py 100.00% <100.00%> (ø)
irsim/lib/algorithm/kinematics.py 97.59% <100.00%> (+1.36%) ⬆️
irsim/lib/behavior/behavior_methods.py 98.73% <100.00%> (-0.49%) ⬇️
tests/test_all_objects.py 99.20% <100.00%> (-0.02%) ⬇️
tests/test_behaviors.py 99.35% <100.00%> (+0.19%) ⬆️
tests/test_env.py 99.60% <100.00%> (+0.01%) ⬆️
tests/test_keyboard.py 95.40% <100.00%> (+0.17%) ⬆️
tests/test_kinematics.py 100.00% <100.00%> (ø)
... and 1 more

... and 2 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds an omni_angular kinematics model (body-frame [forward, lateral, yaw_rate]) and refactors existing omni kinematics to use body-frame [forward, lateral], alongside a 3-axis keyboard control scheme and updated docs/tests.

Changes:

  • Implement omni_angular_kinematics + OmniAngularKinematics, and refactor omni to body-frame velocity.
  • Redesign keyboard control to a 3-axis command and adapt env action assignment for different kinematics.
  • Update behaviors (dash), tests, usage examples, and documentation (including zh_CN translations).

Reviewed changes

Copilot reviewed 23 out of 23 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
irsim/lib/algorithm/kinematics.py Refactors omni_kinematics to body-frame + adds omni_angular_kinematics.
irsim/lib/algorithm/__init__.py Re-exports omni_angular_kinematics.
irsim/lib/handler/kinematics_handler.py Adds OmniAngularKinematics handler + updates OmniKinematics metadata/conversions.
irsim/lib/behavior/behavior_methods.py Adds omni_angular dash behavior and updates OmniDash to body-frame.
irsim/gui/keyboard_control.py Updates key mappings and expands keyboard command vector to 3 axes.
irsim/env/env_base.py Converts 3-axis keyboard command into per-kinematics action vectors.
tests/test_kinematics.py Updates omni tests for body-frame + adds omni_angular tests.
tests/test_keyboard.py Updates keyboard tests for new mappings and 3-axis behavior.
tests/test_env.py Adds coverage for keyboard-action assignment for omni vs omni_angular.
tests/test_behaviors.py Adds/updates tests for body-frame OmniDash and OmniAngularDash behavior.
tests/test_all_objects.py Updates integration-style keyboard assertions for new mappings.
usage/09keyboard_control/keyboard_control.py Points example runner at the new omni_angular keyboard config.
usage/09keyboard_control/keyboard_control_pynput_omni_angular.yaml Adds a new keyboard-control example for omni_angular.
usage/02robot_world/robot_world.py Points example runner at the new omni_angular world config.
usage/02robot_world/robot_omni_angular_world.yaml Adds a new world example for omni_angular.
docs/source/yaml_config/configuration.md Documents omni_angular and body-frame velocity semantics.
docs/source/usage/configure_robots_obstacles.md Updates kinematics table/definitions for omni vs omni_angular.
docs/source/usage/configure_keyboard_Mouse_control.md Updates keyboard mapping documentation (including shift+z/c).
docs/source/get_started/quick_start.md Updates quick-start kinematics list to include omni_angular.
docs/locale/zh_CN/LC_MESSAGES/yaml_config/configuration.po Updates zh_CN strings for new kinematics/velocity semantics (partial).
docs/locale/zh_CN/LC_MESSAGES/usage/configure_robots_obstacles.po Updates zh_CN usage translation for omni_angular and body-frame omni.
docs/locale/zh_CN/LC_MESSAGES/usage/configure_keyboard_Mouse_control.po Updates zh_CN keyboard mapping translation.
README.md Updates top-level docs to mention omni_angular.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 250 to 252
self.key_ang = self.key_ang_max
if key.char == "d":
self.key_ang = -self.key_ang_max
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a/d are documented as strafe for omni/omni_angular, but the code sets key_ang using key_ang_max (the max angular velocity). This couples lateral strafe speed to the angular limit and makes shift+z/c (linear max) not affect strafing as intended. Consider using key_lv_max for the strafe axis when controlling omni/omni_angular (or otherwise separating lateral max from angular max based on the selected robot kinematics).

Suggested change
self.key_ang = self.key_ang_max
if key.char == "d":
self.key_ang = -self.key_ang_max
self.key_ang = self.key_lv_max
if key.char == "d":
self.key_ang = -self.key_lv_max

Copilot uses AI. Check for mistakes.
Comment on lines 391 to 393
self.key_ang = self.key_ang_max
if base == "d":
self.key_ang = -self.key_ang_max
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same issue as the pynput backend: in the mpl backend a/d set key_ang using key_ang_max, but for omni/omni_angular this axis is lateral strafe (linear). This makes the lateral speed limit track the angular limit instead of key_lv_max and conflicts with the documented key mappings.

Suggested change
self.key_ang = self.key_ang_max
if base == "d":
self.key_ang = -self.key_ang_max
self.key_ang = self.key_lv_max
if base == "d":
self.key_ang = -self.key_lv_max

Copilot uses AI. Check for mistakes.
Comment on lines 127 to 145
@@ -135,26 +135,97 @@ def omni_kinematics(
"""
Calculate the next position for an omnidirectional robot.

Uses body-frame velocity: the two components are forward and lateral
speeds relative to the robot heading (theta). Since omni robots have
no yaw control, theta remains unchanged.

Args:
state: A 2x1 vector [x, y] representing the current position.
velocity: A 2x1 vector [vx, vy] representing the current velocities.
state: A 3x1 vector [x, y, theta] representing the current state.
velocity: A 2x1 vector [forward, lateral] in body frame.
step_time: The time step for the simulation.
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

omni_kinematics now enforces state.shape[0] >= 3 via @validate_shape(state=3, ...), which will raise at runtime for any existing callers passing a 2D [x, y] state (previously accepted). If backward compatibility is desired, consider accepting 2D state by treating missing theta as 0 (or explicitly migrating callers and updating any docs that still claim [x, y] is allowed).

Copilot uses AI. Check for mistakes.
Comment on lines +15 to +18
state: [5, 5, 1, 0]
goal: [40, 40, 0]
vel_max: [3.0, 3.0, 3.0] # [v_x, v_y, angular]
vel_min: [-3.0, -3.0, -3.0] # [v_x, v_y, angular]
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This example config uses a 4-element state: [5, 5, 1, 0], but omni_angular kinematics/state are 3D ([x, y, theta]). The extra element will be truncated/padded implicitly, which is confusing for users. Also the vel_max/vel_min comments still refer to [v_x, v_y, angular] rather than body-frame [forward, lateral, yaw_rate].

Suggested change
state: [5, 5, 1, 0]
goal: [40, 40, 0]
vel_max: [3.0, 3.0, 3.0] # [v_x, v_y, angular]
vel_min: [-3.0, -3.0, -3.0] # [v_x, v_y, angular]
state: [5, 5, 1]
goal: [40, 40, 0]
vel_max: [3.0, 3.0, 3.0] # [forward, lateral, yaw_rate]
vel_min: [-3.0, -3.0, -3.0] # [forward, lateral, yaw_rate]

Copilot uses AI. Check for mistakes.
next_state = omni_angular_kinematics(state, velocity, 1.0)
assert np.allclose(next_state, np.array([[0], [1], [np.pi / 2]]), atol=1e-10)

# Test lateral movement at theta=0 (lateral = -y in world... no, +y)
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The inline comment here reads like an edit note ("... no, +y") and is likely unintentional. Please clean it up to avoid confusion about the sign convention for lateral motion.

Suggested change
# Test lateral movement at theta=0 (lateral = -y in world... no, +y)
# Test lateral movement at theta=0 (lateral = +y in world)

Copilot uses AI. Check for mistakes.
Comment on lines 2701 to 2709
#: ../../source/yaml_config/configuration.md:1195
msgid ""
"**Kinematics and Velocities**: Ensure that the `velocity` and `vel_max` "
"parameters match the kinematics model. For example, a differential drive "
"robot (`'diff'`) should have velocities in `[v, omega]`, while an "
"omnidirectional robot (`'omni'`) uses `[vx, vy]`."

msgstr ""
"**运动学与速度**:确保 `velocity` 与 `vel_max` 的格式与运动学模型匹配,例如差速 (`'diff'`) 使用 `[v, "
"omega]`,全向 (`'omni'`) 使用 `[vx, vy]`。"
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This translated string still describes omni velocity as world-frame [vx, vy], but the PR changes omni to body-frame [forward, lateral] and adds omni_angular. The zh_CN translation should be updated to match the new semantics (and ideally regenerated from the updated source gettext catalogs so msgid/msgstr stay in sync).

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add yaw for omni kinematics

3 participants