# Ackermann Controller
> 2025-12-16
[TOC]
---
## 阿克曼機構 (Ackermann Steering Geometry)
**阿克曼轉向幾何(Ackermann Steering Geometry)** 是車輛工程與移動機器人設計中非常基礎且核心的概念。
簡單來說,它是一種機械連桿設計,目的是為了解決四輪車輛在轉彎時,內外側輪胎路徑半徑不同而導致的「磨胎」或「側滑」問題。
### 1. 核心問題:為什麼需要阿克曼幾何?
當一輛車子在轉彎時,四個輪子所走的軌跡其實是不一樣的:
* **內側輪(Inner Wheel)**:走的是半徑較小的圓(內圈)。
* **外側輪(Outer Wheel)**:走的是半徑較大的圓(外圈)。
如果車輛採用簡單的平行轉向(Parallel Steering),即左右輪轉動的角度完全一樣,那麼內側輪與外側輪會互相「打架」,導致其中一個輪子被強行拖曳(側滑),這會造成:
1. 輪胎過度磨損。
2. 轉向機構受力過大。
3. 轉向不順暢,車輛難以精確控制。
### 2. 阿克曼幾何的解決方案
阿克曼機構的設計原則非常直觀:**在轉彎時,讓內側輪的轉向角度比外側輪大。**
這樣可以確保所有四個輪子的旋轉軸心(垂直於輪胎的線)都能交匯於同一點,這個點稱為**瞬時旋轉中心(Instantaneous Center of Rotation, ICR)**。
#### 幾何特徵:
* **理想狀態**:左右前輪的轉向軸線(延長線)與後輪軸線(延長線)精確交會於同一點(ICR)。
* **梯形連桿**:在機械結構上,通常是將兩側轉向臂(Steering Arms)向內傾斜,使其延長線交匯於後軸的中心點。這形成了一個梯形結構,而不是矩形。
---
## 實作過程
:::info
📅 **Note**
其實是昨天做的,但我昨天趕著回去讀類神經網路 qq
Anyway...
:::
### 1. 製作車底盤
#### 啟動 Docker
複習一下,先用 `Docker`:
```bash
xhost +local:
docker run --name isaac-sim --entrypoint bash -it --gpus all -e "ACCEPT_EULA=Y" --rm --network=host --privileged \
-e "PRIVACY_CONSENT=Y" \
-e DISPLAY=$DISPLAY \
-v $HOME/.Xauthority:/isaac-sim/.Xauthority \
-v ~/docker/isaac-sim/cache/kit:/isaac-sim/kit/cache:rw \
-v ~/docker/isaac-sim/cache/ov:/isaac-sim/.cache/ov:rw \
-v ~/docker/isaac-sim/cache/pip:/isaac-sim/.cache/pip:rw \
-v ~/docker/isaac-sim/cache/glcache:/isaac-sim/.cache/nvidia/GLCache:rw \
-v ~/docker/isaac-sim/cache/computecache:/isaac-sim/.nv/ComputeCache:rw \
-v ~/docker/isaac-sim/cache/warp:/isaac-sim/.cache/warp:rw \
-v ~/docker/isaac-sim/logs:/isaac-sim/.nvidia-omniverse/logs:rw \
-v ~/docker/isaac-sim/config:/isaac-sim/.nvidia-omniverse/config:rw \
-v ~/docker/isaac-sim/data:/isaac-sim/.local/share/ov/data:rw \
-v ~/docker/isaac-sim/documents:/isaac-sim/Documents:rw \
-v ~/MyRobotProject:/isaac-sim/MyRobotProject:rw \
-v ~/IsaacSim-ros_workspaces/jazzy_ws/fastdds.xml:/tmp/fastdds.xml \
-e FASTRTPS_DEFAULT_PROFILES_FILE=/tmp/fastdds.xml \
-e ROS_DOMAIN_ID=30 \
nvcr.io/nvidia/isaac-sim:5.1.0
```
在容器內啟動 `isaac-sim`:
```bash
./runapp.sh
```
#### Isaac Sim 設定
接下來我們已經進入 `isaac-sim` 了:
1. 螢幕左上角打開 **`Create`** > **`Environments`** > **`Flat Grid`** 載入地面。
2. 再來一樣螢幕左上角打開 **`Create`** > **`ROS 2 Assets`** > **`Leatherback`** 打開車子模型。
> **選這台車的目的很簡單**:因為我們需要阿克曼結構。
> 它已經幫我們弄好了模型架構,ROS2 Action Graph,可以直接用!

然後將 `Stage` 中的 `Eletronics_Mounting` 眼睛關掉。
> (有點陽春的做法 xddddd)
### 2. ROS 2 橋接與控制
#### 🖥️ 開第一個新的 `Terminal` (轉譯節點)
一樣開啟新的 `Terminal 1`,輸入:
```bash
docker run -it --rm \
--net=host \
--env="DISPLAY" \
--env="ROS_DOMAIN_ID" \
-v ~/IsaacSim-ros_workspaces/jazzy_ws:/jazzy_ws \
--name ros_ws_docker \
my_isaac_ros /bin/bash
```
載入環境:
```bash
source /opt/ros/jazzy/setup.bash
source /jazzy_ws/install/local_setup.bash
export FASTRTPS_DEFAULT_PROFILES_FILE=/jazzy_ws/fastdds.xml
export ROS_DOMAIN_ID=30
```
如果你這時候打 `ros2 topic list`,應該會看到類似這樣:
```text
root@dodo-System-Product-Name:/# ros2 topic list
/ackermann_cmd
/depth
/odom
/parameter_events
/rgb
/rosout
/tf
```
接著執行範例鍵盤輸入轉譯:
```bash
ros2 launch cmdvel_to_ackermann cmdvel_to_ackermann.launch.py
```
成功的話應該會跳出:
```text
acceleration:=0.5 steering_velocity:=0.5
[INFO] [launch]: All log files can be found below /root/.ros/log/...
[INFO] [launch]: Default logging verbosity is set to INFO
[INFO] [cmdvel_to_ackermann.py-1]: process started with pid [74]
```
#### 🖥️ 開第二個新的 `Terminal` (鍵盤控制)
開啟第二個 `Terminal 2`,進入剛剛已經開啟的容器:
```bash
docker exec -it ros_ws_docker /bin/bash
```
一樣要載入環境:
```bash
source /opt/ros/jazzy/setup.bash
source /jazzy_ws/install/local_setup.bash
export FASTRTPS_DEFAULT_PROFILES_FILE=/jazzy_ws/fastdds.xml
export ROS_DOMAIN_ID=30
```
執行控制鍵盤的範例:
```bash
ros2 run teleop_twist_keyboard teleop_twist_keyboard
```
會跳出控制說明:
```text
This node takes keypresses from the keyboard and publishes them
as Twist/TwistStamped messages. It works best with a US keyboard layout.
---------------------------
Moving around:
u i o
j k l
m , .
For Holonomic mode (strafing), hold down the shift key:
---------------------------
U I O
J K L
M < >
t : up (+z)
b : down (-z)
anything else : stop
q/z : increase/decrease max speeds by 10%
w/x : increase/decrease only linear speed by 10%
e/c : increase/decrease only angular speed by 10%
CTRL-C to quit
currently: speed 0.50 turn 1.00
```
:::success
🎉 **然後就能操控了!**
:::
{%preview https://youtu.be/9-NoYgY_ljY %}
:::info
🔜 **預告**
接下來應該是更改 **ROS2 Python Script**
:::
> 更:
> 每次打這串太麻煩了
> 我用docker compose優化了一下
在 home 寫一個 `docker-compose.yaml`
```yaml
services:
# --- Isaac Sim 5.1.0 ---
isaac-sim:
image: nvcr.io/nvidia/isaac-sim:5.1.0
container_name: isaac-sim
entrypoint: bash
stdin_open: true
tty: true
network_mode: host
privileged: true
environment:
- ACCEPT_EULA=Y
- PRIVACY_CONSENT=Y
- DISPLAY=${DISPLAY}
- FASTRTPS_DEFAULT_PROFILES_FILE=/tmp/fastdds.xml
- ROS_DOMAIN_ID=30
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: all
capabilities: [gpu]
volumes:
- /tmp/.X11-unix:/tmp/.X11-unix
- ${HOME}/.Xauthority:/isaac-sim/.Xauthority
- ~/docker/isaac-sim/cache/kit:/isaac-sim/kit/cache:rw
- ~/docker/isaac-sim/cache/ov:/isaac-sim/.cache/ov:rw
- ~/docker/isaac-sim/cache/pip:/isaac-sim/.cache/pip:rw
- ~/docker/isaac-sim/cache/glcache:/isaac-sim/.cache/nvidia/GLCache:rw
- ~/docker/isaac-sim/cache/computecache:/isaac-sim/.nv/ComputeCache:rw
- ~/docker/isaac-sim/cache/warp:/isaac-sim/.cache/warp:rw
- ~/docker/isaac-sim/logs:/isaac-sim/.nvidia-omniverse/logs:rw
- ~/docker/isaac-sim/config:/isaac-sim/.nvidia-omniverse/config:rw
- ~/docker/isaac-sim/data:/isaac-sim/.local/share/ov/data:rw
- ~/docker/isaac-sim/documents:/isaac-sim/Documents:rw
- ~/MyRobotProject:/isaac-sim/MyRobotProject:rw
- ~/IsaacSim-ros_workspaces/jazzy_ws/fastdds.xml:/tmp/fastdds.xml
# --- ROS 2 Jazzy 控制環境 ---
ros2_jazzy:
image: my_isaac_ros
container_name: ros_ws_docker
network_mode: host
stdin_open: true
tty: true
environment:
- DISPLAY=${DISPLAY}
- ROS_DOMAIN_ID=30
- FASTRTPS_DEFAULT_PROFILES_FILE=/jazzy_ws/fastdds.xml
# 這裡是重點:讓 entrypoint 直接幫你 source
- BASH_ENV=/etc/bash.bashrc
volumes:
- /tmp/.X11-unix:/tmp/.X11-unix
- ~/IsaacSim-ros_workspaces/jazzy_ws:/jazzy_ws
# 這裡會在你進入 bash 之前先 source 必要的環境
command: >
bash -c "source /opt/ros/jazzy/setup.bash &&
source /jazzy_ws/install/local_setup.bash &&
bash"
```