0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

v_max を下げたら速くなった: km/h と m/s が混ざったパラメータの話

0
Last updated at Posted at 2026-09-12

Team Hayes(Ajayaditya Lokchandra, Nithisha Venkatesh)です。自動運転AIチャレンジ2026 のスターターキットの MPC(multi_purpose_mpc_ros)で、速度上限を実行中に下げると逆に速くなる不具合を見つけたので、原因と直し方を短くまとめます。

結論: ros2 param set /mpc_controller v_max 10.0(10 km/h のつもり)を実行すると、MPC が使う速度上限は 20 km/h から 30 km/h に上がります。km/h の値が m/s の変数にそのまま入るためです。

v_max を 10 km/h に下げたときの速度上限

同じ変数に、2 つの単位が入る

MPCConfig.v_max は m/s で持つ設計です。起動時はそのとおり変換されています(mpc_controller.py:384 付近)。

kmh_to_m_per_sec(self.BUG_VEL if self.USE_BUG_ACC else cfg_mpc.v_max),

ところが、パラメータを実行中に変えたときのコールバック(:266)は、km/h の値をそのまま入れています。

if param.name == "v_max" and param.type_ == Parameter.Type.DOUBLE:
    mpc_cfg.v_max = param.value                      # km/h のまま
    self._mpc.update_v_max(kmh_to_m_per_sec(param.value))

直後の update_v_max() は m/s に変換しているので、変更した瞬間は正しく見えます。問題は次の制御周期です。

次の周期で上書きされる

参照速度を区間ごとに決める処理(:811-815)が、毎周期この値で上限をかけ直します。

ref_vel_mps = self._ref_vel_configulator.get_ref_vel(self._mpc.model.wp_id)
ref_vel_kmph = min(
    kmh_to_m_per_sec(ref_vel_mps),
    self._mpc_cfg.v_max)
self._mpc.update_v_max(ref_vel_kmph)

v_max に 10 を設定すると、ここでは「10 m/s(36 km/h)」として比べられます。ref_vel.yaml のほとんどの区間は 30 km/h(8.33 m/s)なので min(8.33, 10) は 8.33 m/s、つまり 30 km/h が上限になります。起動時の上限 20 km/h より速くなります。制御を止めたときのブレーキ処理(:826)も同じ v_max を m/s として使っています。

実車で v_max を安全のための速度制限として使うと、下げたつもりで上がる、という一番困る向きに壊れます。

変数名も逆だった

上のコードをもう一度見ると、ref_vel_mps には ref_vel.yaml の km/h の値が入り、ref_vel_kmph には m/s に変換した値が入っています。名前が中身と逆です。名前を信じて読むと、min() で km/h と m/s を比べていることに気づけません。

直し方

起動時と同じく、コールバックでも m/s に変換してから保存します。ログは km/h のままです。

-                    mpc_cfg.v_max = param.value
-                    self._mpc.update_v_max(kmh_to_m_per_sec(param.value))
-                    v_ref: List[float] = [kmh_to_m_per_sec(param.value)] * len(self._reference_path.waypoints)
+                    mpc_cfg.v_max = kmh_to_m_per_sec(param.value)
+                    self._mpc.update_v_max(mpc_cfg.v_max)
+                    v_ref: List[float] = [mpc_cfg.v_max] * len(self._reference_path.waypoints)

rclpy をスタブにして、実際のパラメータコールバックを呼ぶテストを付けました。修正前は 2 件失敗し(assert 10.0 == 2.777...assert 8.333... == 2.777...)、修正後は test/ 全体の 17 件が通ります。

学んだこと

  • 単位は変数名に入れる。ただし名前と中身が一致しているかを、変換関数を通るたびに確認する(今回は名前が逆だった)。
  • 同じ値を設定する経路が 2 つ(起動時と実行中)あるときは、両方をテストで通す。起動時だけ正しい、はよくあります。
  • 「下げたら上がる」系の不具合は、ログ(v_max was updated to '10.0' [km/h])だけ見ると正しく見えます。値を使う側で確かめる必要があります。

作り方について

調査とパッチ作成には AI コーディングエージェントを使いました。数値は、修正前に失敗し修正後に通るテストで確認しています。


English

Team Hayes (Ajayaditya Lokchandra, Nithisha Venkatesh). A short note on a bug in the starter-kit MPC (multi_purpose_mpc_ros) of the JSAE AI Challenge 2026: lowering the speed limit at runtime made the kart faster.

In one line: after ros2 param set /mpc_controller v_max 10.0 (meant as 10 km/h), the speed cap the MPC uses goes up from 20 km/h to 30 km/h, because a km/h value is stored in a variable that holds m/s everywhere else (figure above).

One variable, two units

MPCConfig.v_max is meant to be m/s, and at start-up it is converted that way (around mpc_controller.py:384). The runtime parameter callback (:266) stores the raw km/h value instead. The update_v_max() call right after it does convert, so the change looks correct at first.

Overwritten on the next cycle

Every control cycle, the per-section reference-velocity code (:811-815) re-applies the cap with min(kmh_to_m_per_sec(ref_vel), self._mpc_cfg.v_max). With v_max set to 10, that compares against 10 m/s (36 km/h). Most sections in ref_vel.yaml are 30 km/h (8.33 m/s), so the cap becomes 8.33 m/s: 30 km/h, faster than the 20 km/h start-up cap. The brake path used when control is disabled (:826) also reads v_max as m/s.

On a real kart where v_max is used as a safety limit, this fails in the worst direction: you lower it, and the car speeds up.

The names were backwards too

In that same code, ref_vel_mps holds the km/h value from ref_vel.yaml, and ref_vel_kmph holds the converted m/s value. If you trust the names, you will not see that min() is comparing km/h with m/s.

The fix

Convert to m/s in the callback, as at start-up (diff above); the log message stays in km/h. A test with rclpy stubbed calls the real parameter callback: 2 failures before the fix (assert 10.0 == 2.777..., assert 8.333... == 2.777...), and all 17 tests in test/ pass after it.

What we took from it

  • Put the unit in the name, and check the name still matches the value every time a conversion happens (here the names were swapped).
  • When a value can be set on two paths (start-up and runtime), test both. "Correct at start-up only" is common.
  • A "lower it and it goes up" bug looks fine in the log (v_max was updated to '10.0' [km/h]). Check the value where it is used.

How this was made

We used AI coding agents for the investigation and the patch. The numbers were checked with a test that fails before the fix and passes after it.


Team Hayes の自動運転AIチャレンジ2026 シリーズ / series

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?