Skip to content

Releases

v3.1.0 — 难度阶梯、被控对象旋钮与生成的配置表

Choose a tag to compare

@ravenkernel ravenkernel released this 06 Nov 00:25

3.1.0

可测量的基准与可查的配置:新增被控对象旋钮与中间难度场景,配置字段表改为从 dataclass 生成。

Breaking

无。benchmark() / benchmark_cell() 的新参数都是关键字默认值的可选参数。

Added

  • benchmark(..., dynamics=, sensor_noise_std=, sensor_latency_steps=) 与同签名的 benchmark_cell(...):分别打开执行器模型(none/limits/wheel)、给前后各挂一支 60 线 Lidar 并注入噪声、以及给传感器加零阶保持延迟。非法取值的 ValueError 在开跑前抛出(DYNAMICS_KINDS)。结果对象新增 plant 属性并写进 to_dict() 与表格的 被控对象 行。
  • CLI 三个旗标:--dynamics {none,limits,wheel}、--sensor-noise-std、--sensor-latency-steps,原样透传给 benchmark(...)。
  • 场景 corridor_medium:与 corridor 同样的两堵长墙,缝隙 0.8 m —— 补上 0.6 m 窄走廊与开阔场地之间的中间档(场景总数 11 → 12)。实测 pp 22.2% → 66.7%、dwa 33.3% → 88.9%。
  • tools/gen_config_docs.py:从 EnvConfig 的 dataclass 字段生成 README 两份的 48 字段分组表(六组、含默认值),--check 在 CI 的 docs job 里跑;新增 tests/test_config_reference.py 锁住覆盖、描述齐全与两份 README 不过期。
  • README 两份新增三小节:难度阶梯(五个场景 × 两策略,n=9)、被控对象保真度(none vs wheel)、传感器退化(含「pp/dwa 不读扫描」这一如实说明)。

Changed

  • README 的 obstacles_sparse 基线表改用与难度阶梯相同的协议(3 seed × 3 回合、max_steps=250),数字随之更新(pp 94.6 步、dwa 76.2 步);Wilson 半宽从 ±20.4%(n=15)变为 ±29.9%(n=9)。

Fixed

  • README 两份把 success_ci 写成「Wilson 区间的半宽」,而实现取的是 max(p - low, high - p)(保守半宽)。文档已按实现改写,数值口径不变。

v3.0.3 — 逐回合地图工厂对手写场景也重掷障碍

Choose a tag to compare

@ravenkernel ravenkernel released this 05 Nov 04:49

3.0.3

逐回合地图工厂:手写场景也真的每回合换图,基准表因此能给出置信区间。

Breaking

无。

Added

  • chassis_sim.scenarios.roll_obstacles(obstacles, roll):把「同族不同款」的障碍重掷(±0.10 m / ±0.10 rad 位姿抖动、15% 概率丢弃、保底 6 个)做成公开函数;env 默认场景与逐回合工厂共用这一份实现,参数是模块级常量 OBSTACLE_JITTER_XY / OBSTACLE_JITTER_THETA / OBSTACLE_DROP_PROB / OBSTACLE_MIN_KEEP。

Changed

  • scenario_reset_options_factory(...) 对手写(core)场景不再返回 YAML 原图:每回合按种子重掷障碍(参考路径仍固定),与 env 默认场景路径同源。dense_static 因此从「9 个回合 1 张图」变成 9 张图,benchmark 的 maps_independent 转 True、success_ci 给出真区间(实测 pp 3 种子 × 3 回合:distinct 9/9、success 1.0、ci 0.299、mean_steps 102.0)。单次 scenario_to_reset_options(...) 行为不变,仍原样返回 YAML 里的地图。

Fixed

  • 3.0.0/3.0.1/3.0.2 里 dense_static 这类手写障碍场景走 benchmark 时是「一张图重放 n 次」:distinct_maps 恒为 1、maps_independent 恒 False、success_ci 恒 None,而同一场景走 env.reset(seed=...) 与 cs.evaluate 是会重掷的 —— 两条路径口径不一致。现在两条路径共用 roll_obstacles,并有测试锁住「同 roll 同地图」。

v3.0.2 — Colab quickstart notebook 与元信息对齐

Choose a tag to compare

@ravenkernel ravenkernel released this 05 Nov 04:34

元信息与上手路径:公共 API、观测、传感器与渲染行为一律未动。

Breaking

无。

Added

  • examples/quickstart.ipynb:可在 Colab 一键打开的上手 notebook(三种底盘各跑 30 步、观测九段、seed 重置与重掷、渲染一帧、pp/dwa 打分)。README 两份顶部加 Open in Colab 徽章,Examples 表加一行。
  • README 两份新增 How it compares(和别的方案比)小节:与 PyBullet·MuJoCo·Gazebo、CARLA·DonkeyCar、ir-sim 一类 2D 仿真、通用 Gymnasium 基准套件的分工对照。

Changed

  • PyPI 元信息与 GitHub 对齐:description 换成轻量 Gymnasium 环境的一句话(纯 NumPy、CPU、免装仿真器),keywords 7 → 14 个,project_urls 新增 Documentation(指向 quickstart notebook)。

Fixed

无。

v3.0.1 — 参数化 LiDAR 整帧向量化

Choose a tag to compare

@ravenkernel ravenkernel released this 05 Nov 01:22

纯性能补丁:公共 API 只增不改,观测/传感器读数与 3.0.0 逐位相同。

Breaking

无。

Added

  • SensorSuite 新增可选钩子 raycast_many(origin_xy, angles, max_range)(供自定义传感器套件整帧批量取距;不传时行为与 3.0.0 一致)。

Changed

无。

Fixed

  • 参数化 LiDAR 整帧向量化:SensorSuite 新增可选钩子 raycast_many(一帧只调一次,优先于标量 raycast),SimpleBridge 同时注入 _raycast_body_rays。实测单步耗时 N=60 5.07 → 0.685 ms、N=180 14.40 → 0.754 ms、N=360 28.19 → 0.870 ms;读数与逐条标量钩子 np.array_equal 逐位相同,逐条旧用法继续可用。

v3.0.0 — 闭式积分、渲染契约、有限观测空间、基准协议

Choose a tag to compare

@ravenkernel ravenkernel released this 04 Nov 20:13

3.0.0 是 API 稳定线:从这一版起本包承诺 semver,因此 classifier 保持
Development Status :: 5 - Production/Stable,不降级为 Beta。

Breaking

  • Gymnasium id 由 -v0 改成 -v1:ChassisSim-{Ackermann,DiffDrive,Mecanum}-v0 不再注册,
    gym.make("ChassisSim-Ackermann-v0") 抛 gym.error.DeprecatedEnv,消息里直接点名要用的 -v1 id。
    三个 -v1 id 的 entry_point、动作空间与 max_episode_steps 与 2.x 相同。
  • 默认场景改为 dense_static:EnvConfig.default_scenario 默认 "dense_static",reset() 不带
    显式 seed 时每回合重掷地图(default_scenario=None 才回到 2.x 的 straight 直线兜底)。
    2.x 里 reset() 拿到的是固定直线场景,所以这既是默认值变更,也会改变无 seed 训练的分布。
  • observation_space 从无界 Box(±inf) 变成全有限:各段按物理上界声明——path 段的 ρ 上界是
    world_diag + lookahead + 0.1 = 34.24213790893555、角度段 π、归一化段 1 + 1 ulp = 1.0000001192092896;
    非零有限界各放宽 1 ulp。真正越界的观测值才被 clip,并在 info["obs_clipped"] 给出计数(计数为 0 时该键不出现)。
    依赖 low/high 为 inf 的包装器要改用新值。
  • 闭式积分替换 Euler 步进:同一动作序列下位姿会变——圆周 max-radial 误差 5.04e-2 → 3.33e-16 m,
    60 步开环序列的 xy 最大差 0.0403 m,θ 通道逐位不变;|ω·dt| ≤ 1.49e-8(sqrt(float64 eps))时仍走
    与 2.x 逐位相同的 Euler 分支。用 2.x 生成的 golden 必须重录:本仓 tests/data/ackermann_golden.json
    已重录,旧轨迹以 poses_euler_legacy 保留并与 HEAD 的 poses np.array_equal=True。

Added

  • 观测段可声明物理上界:新增 BoundContext(dt / v_max / v_min / vy_max / omega_max /
    lookahead / world_diag / lidar)与 ResolvedObsSpec.bounds(),内置段各自给出界,
    Custom 段也能带自己的界;observation_space 由这些界拼成,不再是无界 Box。
  • 渲染契约:render_mode 支持 None / "human" / "rgb_array",render() 返回 uint8 (600, 600, 3),
    metadata 带 render_modes 与 render_fps = 10;新增 chassis_sim.render.render_to_array(bridge, dpi=...)
    供无窗口取帧。缺 matplotlib 时抛带 pip install "chassis-sim[viz]" 的 ImportError。
  • 轮级运动学层(纯只读):Chassis.wheel_kinematics(cmd) -> WheelKinematics | None,给出 Ackermann 的
    内外前轮角、DiffDrive 的左右轮速、Mecanum 的四轮速(逆 Jacobian);Ackermann 另有 front_wheel_angles(delta)。
    不支持时返回 None,不返回空 dict;积分器与动作语义一律未动。
  • integration_substeps 与 ccd:前者把一步切成 N 段分别积分与碰撞采样,后者在每段内按 0.02 m /
    0.05 rad 采样内部位姿(上限 64 个)以拦住薄墙穿透。默认 1 / False,与 2.x 逐位相同;
    ccd 只接受 bool,1 / 0 / "false" 一律 ValueError。
  • 基准协议:chassis_sim.eval.benchmark(...)(策略 × held-out 场景 × ≥3 seed、同 (场景, seed) 的地图在
    策略间共享做配对、Wilson 区间、独立样本闸门——地图指纹有重复时 success_ci 返回 None 而不是给假数字),
    四基线 zero / random / pp / dwa,以及 python -m chassis_sim benchmark 子命令。evaluate() 签名未动。
  • EnvConfig.default_scenario:默认 "dense_static",设为 None 回到 2.x 的直线兜底。
  • CI 的 ubuntu 矩阵覆盖 NumPy 1.x 与 2.x 两档(matrix.numpy:numpy<2 /
    numpy>=2),每档都跑完整套件,并用一步断言实测到的 NumPy 主版本与矩阵档位一致。
    3.13 × numpy<2 显式排除:PyPI 上 cp313 的 NumPy 从 2.1.0 才开始发布
    (pip download --python-version 3.13 "numpy<2" 报
    No matching distribution found),pip 只能退回源码构建。3.13 仍在
    numpy>=2 档真跑:numba 的 cp313 轮子从 0.61.0 起提供,numba>=0.60 在
    3.13 上会解析到 0.61+。
  • 新增 CI 的 docs job 与 tools/check_readme_blocks.py:把两份 README 里的
    python 围栏块逐个放进独立子进程执行,任一失败即红;块首行写 # no-exec
    才允许跳过,跳过计入 skipped 而不是 passed,一个 python 块都没有也算失败。
  • dev extra 自带全部闸门工具:mypy==2.4.1、pytest-cov==7.1.0、
    types-PyYAML==6.0.12.20261103。pip install -e ".[dev]" 之后即可直接跑
    MYPYPATH=src mypy 与 pytest --cov,不必再单独装工具。

Changed

  • planning extra 的 numba 下界 >=0.56 → >=0.60:Numba 0.60.0 是第一个提供
    NumPy 2.0 二进制支持的版本,更早的组合与 numpy>=2 在 import 阶段就失败。
    运行依赖 numpy>=1.24 不变 —— 1.x 与 2.x 都仍然支持。
  • README 与包定位改口径:从 “Gymnasium benchmark” 改为「底盘控制 + 观测/奖励工作台」,并写明三条边界
    (底盘级运动学而非轮级刚体动力学、碰撞是几何判据无接触力、无 GPU 并行);CITATION.cff 的标题与摘要同步。
  • 闭式积分下的 integration_substeps 语义:精度不再随分段数改善(已到浮点噪声),它的作用只剩提高碰撞采样密度;
    文档与 docstring 都按这个口径写,不宣称「更高精度积分」。

Fixed

  • 注入的桥不再被塞进 env 的内置场景:2.x 起 ChassisSimEnv(bridge=...) 且 reset() 不带 options 时,
    env 会把 default_scenario 的 obstacles / path_waypoints / initial_heading 直接递给调用方的桥,
    而桥照单全收、不报错(主仓的 PhysicalCommandBridge(NumpySimBridge) 正是这种注入形态)。现在只有
    「options is None + 桥由 env 自造 + 无 map_generator + 配了 default_scenario」四条全成立才接管;
    注入桥保留调用方自己的世界。
  • render() 与渲染元数据补齐:2.x 的 metadata 只有 render_modes=["human"]、没有 render_fps,
    env.render() 直接 NotImplementedError,gym.make(..., render_mode="rgb_array") 会 TypeError。
    3.0 起三者都按 Gymnasium 契约工作,gymnasium.utils.env_checker.check_env(..., skip_render_check=False)
    在三种底盘上都不再报 Box maximum value is infinity。

v2.0.0 — 删除无效的 device 形参,CI 扩到 macOS/Windows

Choose a tag to compare

@ravenkernel ravenkernel released this 04 Nov 14:24

Breaking

  • ChassisSimEnv(device=...) and cs.make(..., device=...) are gone. The parameter
    had been inert since 1.0 — the encoder lives in the Actor and ContextBuilder only
    computes numpy — and was documented as inert. A no-op keyword is still public API, so
    removing it is a major bump rather than a patch: ChassisSimEnv(device="cpu") now
    raises TypeError: __init__() got an unexpected keyword argument 'device', and
    cs.make(device="cpu") raises ValueError: 未知的 make() 参数 ['device'] followed by
    the 57 accepted keys. Grep training scripts for device= before upgrading; nothing
    else about the constructor changed.
  • The matching entry is gone from both READMEs' Known limitations sections, which
    are renumbered (nine items, no gap).

Changed

  • CI now runs the same suite on macOS and Windows (new test-os job: Python 3.12,
    [dev,viz,planning], shell: bash, PYTHONUTF8=1), so the declared
    Operating System :: OS Independent classifier has something behind it instead of an
    ubuntu-only history. ubuntu still covers 3.10–3.13. The matrix values are written as
    arrays: a scalar python-version keeps the YAML valid but makes GitHub reject the whole
    workflow (the run then fails with zero jobs and no logs), so tests/test_ci_workflow.py
    pins that shape as well as the job's shell: bash steps.
  • The planning parity golden compares cusp_rejections only in strict mode. That
    count is discrete: both of its criteria (min_cusp_fold_arc, min_cusp_leg_separation)
    compare a float against a fixed threshold, so a single ulp decides whether one candidate
    is rejected. On the first macOS/Windows run the generated obstacles, path and headings
    matched inside 1e-9 while the counter read 1 vs 0 and 1 vs 2 — the same path after a
    different number of retries, not a porting error. CHASSIS_STRICT_GOLDEN=1 still
    asserts it, and the invariant that matters — a generated path contains no violating
    cusp — is asserted independently by test_cusp_filter_effective_on_generated_paths.

Fixed

  • Worker crash logs no longer vanish on Windows. reset() and step() wrote the
    traceback to a hard-coded /tmp/ackermann_worker_crash.log; Windows has no /tmp, so
    open() raised FileNotFoundError, the surrounding except swallowed it, and the
    diagnostic disappeared. The path now comes from tempfile.gettempdir() and the write is
    explicitly UTF-8 — a Chinese traceback cannot be encoded through the default ANSI code
    page either.

No other functional changes: observation layout, rewards, sensors, controllers, planning
and rendering are untouched in this release.

v1.1.1 — docs/ 退出仓库,README 只讲当前版本

Choose a tag to compare

@ravenkernel ravenkernel released this 04 Nov 13:46

Changed

  • The gallery images moved from docs/media/ to assets/, and docs/ is no longer
    tracked.
    The wiki-style documents under docs/ were internal working notes; they are
    not part of the library and are no longer published. No code, no public API and no
    observation/sensor behaviour changed in this release.
  • Both READMEs now describe only the current release. The migration notes, the
    "0.1.x line vs 1.x line" comparisons and the "since 1.1.0" phrasing are gone; every
    statement is present-tense so the text does not rot with each release. Version numbers
    live in pyproject.toml and in this changelog, not in the README prose.

v1.1.0 — seed 真正控制域随机化;reset() 自动消费场景默认值

Choose a tag to compare

@ravenkernel ravenkernel released this 04 Nov 12:23

Added

  • EnvConfig.seed — a run seed that actually reproduces the run. With domain_rand=True
    the LiDAR noise and the odometry drift used to come from the global np.random, so two
    runs with the same seed diverged. SimpleBridge(seed=...) now owns a
    np.random.Generator and every domain-randomisation draw goes through it;
    cs.make(seed=...) sets the run seed and — when sensor_seed is not given — also the
    sensor streams, through one rule implemented once
    (chassis_sim.config.resolve_sensor_seed):
    sensor_seed if sensor_seed is not None else (seed if seed is not None else 0).
    A bridge you construct yourself keeps ownership of its own randomness.
    Bit-for-bit compatibility: with seed=None the bridge still draws from the global
    np.random (verified identical to 1.0.2 on a clean worktree), and make(seed=3) alone
    still yields the same sensor streams and scenario maps as 1.0.2.

Changed

  • env.reset() now consumes env.default_reset_options. cs.make(scenario=...) already
    stored the scenario keys on the env, but reset() ignored them unless you handed them
    over yourself — a forgotten options= silently fell back to the empty field. From this
    release reset(options=None) falls back to the stored defaults, while an explicit
    options= (including {}) always wins.

No other functional changes.

v1.0.2 — 元数据说真话、社区文件、文档不再写死版本

Choose a tag to compare

@ravenkernel ravenkernel released this 04 Nov 11:34

Changed

  • The metadata now describes what the package is. Classifiers say
    Development Status :: 5 - Production/Stable instead of 4 - Beta, and gained
    Programming Language :: Python :: 3.13 and Typing :: Typed. The CI test matrix
    grew a 3.13 entry in the same commit, so the new claim is backed by an actual run
    rather than assertion.
  • The docs no longer hard-code the release they describe. Sentences that described the
    current product now say 1.x, and the install example uses ==X.Y.Z instead of a pinned
    literal: pyproject.toml is the single source of truth, so no version number rots inside
    prose on every release.

Added

  • CITATION.cff — gives GitHub's "Cite this repository" button for academic users.
  • CONTRIBUTING.md — the five gates with their exact commands, checked line-by-line against
    .github/workflows/ci.yml, plus the repository conventions (Chinese comments and error
    messages, no lowering of the coverage floor, both READMEs kept in sync).
  • SECURITY.md — supported range, private reporting through GitHub Security Advisories,
    response targets, and what is explicitly out of scope.
  • .github/ISSUE_TEMPLATE/{bug_report,feature_request}.yml — issue forms that ask for the
    version, Python, OS and a minimal reproduction.
  • .pre-commit-config.yaml — ruff 0.17.3 plus pre-commit-hooks.

No functional changes: the package code is identical to 1.0.1.

v1.0.1 — 项目链接跟随 GitHub 改名(ravenkernel)

Choose a tag to compare

@ravenkernel ravenkernel released this 04 Nov 11:03

Fixed

  • Project links follow the GitHub rename (zhao6663 → ravenkernel). The badges, the
    eleven gallery image URLs, the release/issue links and [project.urls] now point at
    github.com/ravenkernel/chassis-sim. The old links still resolve through GitHub's
    redirect, but a redirect is not a contract.
  • The metadata published as 1.0.0 could not be corrected in place. Project URLs and the
    long description are immutable once uploaded, so 1.0.1 re-publishes the same content with
    the new owner (the images inside the 1.0.0 PyPI description pointed at the old account).

No functional changes: the package code is identical to 1.0.0.