260516

嗯...还是有问题,我们启动后还是会发生崩溃等一系列的问题。

所以我想了想,还是得祭出我们新版的工程式学习的 Skill:

交给 Codex 去完成我们 02 的新版教程吧。

在一顿操作后,我们算是弄好了 02 的 roadmap 文档以及其他供我们进行阅读的文档。

我们新开一个 02 的分支来放这部分代码好了,实际主分支的代码由我们自行实现。

按照 roadmap,我们先检查一下自己的依赖是否完备:

#!/usr/bin/env bash
set -euo pipefail

missing=0

echo "[check] ROS_DISTRO=${ROS_DISTRO:-unset}"
if [[ "${ROS_DISTRO:-}" != "jazzy" ]]; then
  echo "[error] 请先 source ROS2 Jazzy 环境,例如:source .vscode/project-terminal-init.sh"
  missing=1
fi

if ! command -v ros2 >/dev/null 2>&1; then
  echo "[error] ros2 命令不可用。"
  missing=1
fi

required_packages=(
  nav2_bringup
  nav2_controller
  nav2_planner
  nav2_bt_navigator
  nav2_msgs
  slam_toolbox
  ros_gz_bridge
)

for package_name in "${required_packages[@]}"; do
  if ros2 pkg prefix "${package_name}" >/dev/null 2>&1; then
    echo "[ok] ros2 package ${package_name}"
  else
    echo "[error] 缺少 ROS2 package: ${package_name}"
    missing=1
  fi
done

fastcdr_lib="/opt/ros/jazzy/lib/libfastcdr.so.2"
if [[ -f "${fastcdr_lib}" ]]; then
  fastcdr_symbols="$(nm -D "${fastcdr_lib}" 2>/dev/null | c++filt)"
  if [[ "${fastcdr_symbols}" == *"eprosima::fastcdr::Cdr::serialize(unsigned int)"* ]]; then
    echo "[ok] ${fastcdr_lib} exports Cdr::serialize(unsigned int)"
  else
    echo "[error] ${fastcdr_lib} 缺少 Cdr::serialize(unsigned int),Nav2 controller_server 会在运行时崩溃。"
    missing=1
  fi
else
  echo "[error] 未找到 ${fastcdr_lib}"
  missing=1
fi

if [[ "${missing}" -ne 0 ]]; then
  cat <<'EOF'

请更新 Nav2/Jazzy 运行依赖:

sudo apt-get update
sudo apt-get install -y \
  ros-jazzy-fastcdr \
  ros-jazzy-fastrtps \
  ros-jazzy-rmw-fastrtps-cpp \
  ros-jazzy-rmw-fastrtps-shared-cpp \
  ros-jazzy-rosidl-typesupport-fastrtps-c \
  ros-jazzy-rosidl-typesupport-fastrtps-cpp \
  ros-jazzy-rmw-cyclonedds-cpp

然后重新 source 环境并再次运行本检查。
EOF
  exit 1
fi

echo "[ok] Nav2 runtime dependencies look usable."

(.venv) jese--ki@KiBall:~/Projects/dev/KiBots/KiBotTwo$ bash src/kibot_one_sim/scripts/check_nav2_runtime_deps.sh
[check] ROS_DISTRO=jazzy
[ok] ros2 package nav2_bringup
[ok] ros2 package nav2_controller
[ok] ros2 package nav2_planner
[ok] ros2 package nav2_bt_navigator
[ok] ros2 package nav2_msgs
[ok] ros2 package slam_toolbox
[ok] ros2 package ros_gz_bridge
[ok] /opt/ros/jazzy/lib/libfastcdr.so.2 exports Cdr::serialize(unsigned int)
[ok] Nav2 runtime dependencies look usable.

运行环境正常。

然后我们验收一下结果,我们启动一下仿真、SLAM 和 Nav2:

ros2 launch kibot_one_sim nav2.launch.py use_rviz:=false

检查状态是否符合预期:

(.venv) jese--ki@KiBall:~/Projects/dev/KiBots/KiBotTwo$ ros2 topic info /clock # 预期结果:Publisher count: 1
Type: rosgraph_msgs/msg/Clock
Publisher count: 1
Subscription count: 18
(.venv) jese--ki@KiBall:~/Projects/dev/KiBots/KiBotTwo$ ros2 lifecycle get /controller_server # 预期结果 active [3]
ros2 lifecycle get /planner_server
ros2 lifecycle get /bt_navigator
ros2 lifecycle get /waypoint_follower
active [3]
active [3]
active [3]
active [3]
(.venv) jese--ki@KiBall:~/Projects/dev/KiBots/KiBotTwo$ ros2 action list | grep navigate_to_pose # 预期结果:/navigate_to_pose
/navigate_to_pose
(.venv) jese--ki@KiBall:~/Projects/dev/KiBots/KiBotTwo$ ros2 topic info /cmd_vel_smoothed -v | grep -A2 'Node name: kibot_one_bridge' # 预期结果:能看到 `kibot_one_bridge`
Node name: kibot_one_bridge
Node namespace: /
Topic type: geometry_msgs/msg/Twist

可以看到全部符合预期。

简单看一下我们的 map -> base_link 的 TF 坐标:

(.venv) jese--ki@KiBall:~/Projects/dev/KiBots/KiBotTwo$ ros2 run tf2_ros tf2_echo map base_link
[INFO] [1778928882.435440225] [tf2_echo]: Waiting for transform map ->  base_link: Invalid frame ID "map" passed to canTransform argument target_frame - frame does not exist
At time 8.140000000
- Translation: [-0.000, 0.000, 0.000]
- Rotation: in Quaternion (xyzw) [0.000, 0.000, -0.000, 1.000]
- Rotation: in RPY (radian) [0.000, 0.000, -0.000]
- Rotation: in RPY (degree) [0.000, 0.000, -0.000]
- Matrix:
  1.000  0.000  0.000 -0.000
 -0.000  1.000 -0.000  0.000
 -0.000  0.000  1.000  0.000
  0.000  0.000  0.000  1.000

可以看到几乎为 0,也是符合我们预期的。

接下来我们发一个短距离的目标看看:

(.venv) jese--ki@KiBall:~/Projects/dev/KiBots/KiBotTwo$ ros2 action send_goal /navigate_to_pose nav2_msgs/action/NavigateToPose \
  "{pose: {header: {frame_id: map}, pose: {position: {x: 0.5, y: 0.0, z: 0.0}, orientation: {w: 1.0}}}}"
Waiting for an action server to become available...
Sending goal:
     pose:
  header:
    stamp:
      sec: 0
      nanosec: 0
    frame_id: map
  pose:
    position:
      x: 0.5
      y: 0.0
      z: 0.0
    orientation:
      x: 0.0
      y: 0.0
      z: 0.0
      w: 1.0
behavior_tree: ''

Goal accepted with ID: bcb21d6e945c4e76b29c825dcd5dab88

Result:
    error_code: 0
error_msg: ''

Goal finished with status: SUCCEEDED

我们要求小车的目标位置为 x: 0.5,方向为正向,该 nav2 的 /navigate_to_pose 这个 action 成功执行了,我们看看现在的 map -> base_link:

(.venv) jese--ki@KiBall:~/Projects/dev/KiBots/KiBotTwo$ ros2 run tf2_ros tf2_echo map base_link
[INFO] [1778928985.674640708] [tf2_echo]: Waiting for transform map ->  base_link: Invalid frame ID "map" passed to canTransform argument target_frame - frame does not exist
[INFO] [1778928987.655299722] [tf2_echo]: Waiting for transform map ->  base_link: Could not find a connection between 'map' and 'base_link' because they are not part of the same tree.Tf has two or more unconnected trees.
At time 113.80000000
- Translation: [0.411, 0.009, 0.000]
- Rotation: in Quaternion (xyzw) [0.000, 0.000, -0.002, 1.000]
- Rotation: in RPY (radian) [0.000, 0.000, -0.004]
- Rotation: in RPY (degree) [0.000, 0.000, -0.216]
- Matrix:
  1.000  0.004  0.000  0.411
 -0.004  1.000 -0.000  0.009
 -0.000  0.000  1.000  0.000
  0.000  0.000  0.000  1.000

可以看到 x 接近 0.5 了,虽然有点误差,但是在可接受的范围内。

我们发个 x: -5 的看看:

(.venv) jese--ki@KiBall:~/Projects/dev/KiBots/KiBotTwo$ ros2 action send_goal /navigate_to_pose nav2_msgs/action/NavigateToPose   "{pose: {header: {frame_id: map}, pose: {position: {x: -5, y: 0.0, z: 0.0}, orientation: {w: 1.0}}}}"
Waiting for an action server to become available...
Sending goal:
     pose:
  header:
    stamp:
      sec: 0
      nanosec: 0
    frame_id: map
  pose:
    position:
      x: -5.0
      y: 0.0
      z: 0.0
    orientation:
      x: 0.0
      y: 0.0
      z: 0.0
      w: 1.0
behavior_tree: ''

Goal accepted with ID: 7e4c1f86ce874d2b8140db3f7ba55590

Result:
    error_code: 0
error_msg: ''

Goal finished with status: SUCCEEDED

然后再看看我们的 map -> base_link 的 TF:

At time 262.420000000
- Translation: [-5.071, 0.012, 0.000]
- Rotation: in Quaternion (xyzw) [0.000, 0.000, 0.138, 0.990]
- Rotation: in RPY (radian) [0.000, -0.000, 0.276]
- Rotation: in RPY (degree) [0.000, -0.000, 15.830]
- Matrix:
  0.962 -0.273  0.000 -5.071
  0.273  0.962  0.000  0.012
  0.000  0.000  1.000  0.000
  0.000  0.000  0.000  1.000

时间比较长,但是依旧是达到了我们的目标。

总的来说,我们目前接入的 nav2 是符合我们的需求的。

接下来就可以切换到 main 分支并合并文档来进行实际的实现了。