Repository navigation
Releases
Releases · ravenkernel/chassis-sim
v3.1.0 — 难度阶梯、被控对象旋钮与生成的配置表
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)。实测pp22.2% → 66.7%、dwa33.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)、被控对象保真度(
nonevswheel)、传感器退化(含「pp/dwa不读扫描」这一如实说明)。
Changed
- README 的
obstacles_sparse基线表改用与难度阶梯相同的协议(3 seed × 3 回合、max_steps=250),数字随之更新(pp94.6 步、dwa76.2 步);Wilson 半宽从 ±20.4%(n=15)变为 ±29.9%(n=9)。
Fixed
- README 两份把
success_ci写成「Wilson 区间的半宽」,而实现取的是max(p - low, high - p)(保守半宽)。文档已按实现改写,数值口径不变。
v3.0.3 — 逐回合地图工厂对手写场景也重掷障碍
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 与元信息对齐
元信息与上手路径:公共 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、免装仿真器),keywords7 → 14 个,project_urls新增Documentation(指向 quickstart notebook)。
Fixed
无。
v3.0.1 — 参数化 LiDAR 整帧向量化
纯性能补丁:公共 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 — 闭式积分、渲染契约、有限观测空间、基准协议
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,消息里直接点名要用的-v1id。
三个-v1id 的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-16m,
60 步开环序列的 xy 最大差0.0403m,θ 通道逐位不变;|ω·dt| ≤ 1.49e-8(sqrt(float64 eps))时仍走
与 2.x 逐位相同的 Euler 分支。用 2.x 生成的 golden 必须重录:本仓tests/data/ackermann_golden.json
已重录,旧轨迹以poses_euler_legacy保留并与 HEAD 的 posesnp.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 的
docsjob 与tools/check_readme_blocks.py:把两份 README 里的
python围栏块逐个放进独立子进程执行,任一失败即红;块首行写# no-exec
才允许跳过,跳过计入 skipped 而不是 passed,一个 python 块都没有也算失败。 devextra 自带全部闸门工具: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
Breaking
ChassisSimEnv(device=...)andcs.make(..., device=...)are gone. The parameter
had been inert since 1.0 — the encoder lives in the Actor andContextBuilderonly
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
raisesTypeError: __init__() got an unexpected keyword argument 'device', and
cs.make(device="cpu")raisesValueError: 未知的 make() 参数 ['device']followed by
the 57 accepted keys. Grep training scripts fordevice=before upgrading; nothing
else about the constructor changed.- The matching entry is gone from both READMEs'
Known limitationssections, which
are renumbered (nine items, no gap).
Changed
- CI now runs the same suite on macOS and Windows (new
test-osjob: Python 3.12,
[dev,viz,planning],shell: bash,PYTHONUTF8=1), so the declared
Operating System :: OS Independentclassifier 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 scalarpython-versionkeeps the YAML valid but makes GitHub reject the whole
workflow (the run then fails with zero jobs and no logs), sotests/test_ci_workflow.py
pins that shape as well as the job'sshell: bashsteps. - The planning parity golden compares
cusp_rejectionsonly 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 inside1e-9while 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=1still
asserts it, and the invariant that matters — a generated path contains no violating
cusp — is asserted independently bytest_cusp_filter_effective_on_generated_paths.
Fixed
- Worker crash logs no longer vanish on Windows.
reset()andstep()wrote the
traceback to a hard-coded/tmp/ackermann_worker_crash.log; Windows has no/tmp, so
open()raisedFileNotFoundError, the surroundingexceptswallowed it, and the
diagnostic disappeared. The path now comes fromtempfile.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 只讲当前版本
Changed
- The gallery images moved from
docs/media/toassets/, anddocs/is no longer
tracked. The wiki-style documents underdocs/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 inpyproject.tomland in this changelog, not in the README prose.
v1.1.0 — seed 真正控制域随机化;reset() 自动消费场景默认值
Added
EnvConfig.seed— a run seed that actually reproduces the run. Withdomain_rand=True
the LiDAR noise and the odometry drift used to come from the globalnp.random, so two
runs with the same seed diverged.SimpleBridge(seed=...)now owns a
np.random.Generatorand every domain-randomisation draw goes through it;
cs.make(seed=...)sets the run seed and — whensensor_seedis 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: withseed=Nonethe bridge still draws from the global
np.random(verified identical to 1.0.2 on a clean worktree), andmake(seed=3)alone
still yields the same sensor streams and scenario maps as 1.0.2.
Changed
env.reset()now consumesenv.default_reset_options.cs.make(scenario=...)already
stored the scenario keys on the env, butreset()ignored them unless you handed them
over yourself — a forgottenoptions=silently fell back to the empty field. From this
releasereset(options=None)falls back to the stored defaults, while an explicit
options=(including{}) always wins.
No other functional changes.
v1.0.2 — 元数据说真话、社区文件、文档不再写死版本
Changed
- The metadata now describes what the package is. Classifiers say
Development Status :: 5 - Production/Stableinstead of4 - Beta, and gained
Programming Language :: Python :: 3.13andTyping :: 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 say1.x, and the install example uses==X.Y.Zinstead of a pinned
literal:pyproject.tomlis 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)
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.