:~/ros2_compose/ros2_data/ros2_ws4/src/twowheel_bot
├── CMakeLists.txt
├── package.xml
├── setup.cfg
├── setup.py
├── <rviz>Artificial Intelligence and Machine Learning , ROS Applications Course Notes created by handel liao
:~/ros2_compose/ros2_data/ros2_ws4/src/twowheel_bot
├── CMakeLists.txt
├── package.xml
├── setup.cfg
├── setup.py
├── <rviz>下面以 ROS 1 (catkin,例:Noetic) 為準,給你最精簡、可直接照做的流程(Python 與 C++ 都含):
# 開新終端,不要 source ROS 2
source /opt/ros/noetic/setup.bash
mkdir -p ~/catkin_ws/src
cd ~/catkin_ws
catkin_make # 會自動初始化
# 之後每次開新殼,記得:
source ~/catkin_ws/devel/setup.bash
cd ~/catkin_ws/src
catkin_create_pkg my_pkg roscpp rospy std_msgs
# 安裝相依(如需)
rosdep install -i --from-paths . -y
~/catkin_ws/src/my_pkg/scripts/talker.py
#!/usr/bin/env python3
import rospy
from std_msgs.msg import String
if __name__ == "__main__":
rospy.init_node("talker_py")
pub = rospy.Publisher("chatter", String, queue_size=10)
rate = rospy.Rate(5)
while not rospy.is_shutdown():
pub.publish(String(data="hello ros1"))
rate.sleep()
chmod +x ~/catkin_ws/src/my_pkg/scripts/talker.py
~/catkin_ws/src/my_pkg/src/talker.cpp
#include <ros/ros.h>
#include <std_msgs/String.h>
int main(int argc, char** argv){
ros::init(argc, argv, "talker_cpp");
ros::NodeHandle nh;
ros::Publisher pub = nh.advertise<std_msgs::String>("chatter", 10);
ros::Rate rate(5);
while (ros::ok()){
std_msgs::String msg; msg.data = "hello ros1";
pub.publish(msg);
rate.sleep();
}
}
在 ~/catkin_ws/src/my_pkg/CMakeLists.txt
加:
add_executable(talker src/talker.cpp) target_link_libraries(talker ${catkin_LIBRARIES}) add_dependencies(talker ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS}) install(TARGETS talker RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION})
cd ~/catkin_ws
catkin_make
source devel/setup.bash
開 1:啟動 master
roscore
開 2:跑節點
source ~/catkin_ws/devel/setup.bash
rosrun my_pkg talker.py # Python
# 或
rosrun my_pkg talker # C++
開 3:檢查 Topic
rostopic list
rostopic echo /chatter
~/catkin_ws/src/my_pkg/launch/demo.launch
<launch>
<node pkg="my_pkg" type="talker.py" name="talker_py" output="screen"/>
<!-- <node pkg="my_pkg" type="talker" name="talker_cpp" output="screen"/> -->
</launch>
roslaunch my_pkg demo.launch
不要同時 source ROS 1 與 ROS 2(避免路徑衝突)。
之後新增相依就改 package.xml
與 CMakeLists.txt
,再 catkin_make
。
喜歡 catkin_tools
的話:sudo apt install python3-catkin-tools
→ catkin build
(取代 catkin_make
)。
下面給你「一步到位」的順序+可直接複製的範例(含 Python/C++ 兩種做法)。
範例使用工作區名:~/ros2_ws_demo
,套件名:my_pkg
.
# 建立目錄結構
mkdir -p ~/ros2_ws_demo/src
cd ~/ros2_ws_demo
# 只載入系統 ROS 2
source /opt/ros/humble/setup.bash
cd ~/ros2_ws_demo/src
ros2 pkg create my_pkg --build-type ament_python --dependencies rclpy std_msgs
最小節點(my_pkg/my_pkg/talker.py
):
#!/usr/bin/env python3
import rclpy
from rclpy.node import Node
from std_msgs.msg import String
class Talker(Node):
def __init__(self):
super().__init__('talker')
self.pub = self.create_publisher(String, 'chatter', 10)
self.create_timer(0.5, self.tick)
def tick(self):
msg = String(); msg.data = 'hello ros2'
self.pub.publish(msg)
def main():
rclpy.init(); rclpy.spin(Talker()); rclpy.shutdown()
if __name__ == '__main__': main()
在 setup.py
的 entry_points
增加(已自帶,補一行即可):
'console_scripts': [
'talker = my_pkg.talker:main',
],
cd ~/ros2_ws_demo/src
ros2 pkg create my_pkg_cpp --build-type ament_cmake --dependencies rclcpp std_msgs
最小節點(my_pkg_cpp/src/talker.cpp
):
#include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/string.hpp"
using std::placeholders::_1;
int main(int argc, char** argv){
rclcpp::init(argc, argv);
auto node = rclcpp::Node::make_shared("talker_cpp");
auto pub = node->create_publisher<std_msgs::msg::String>("chatter", 10);
rclcpp::WallTimer<std::chrono::milliseconds> *timer;
timer = new rclcpp::WallTimer<std::chrono::milliseconds>(
std::chrono::milliseconds(500),
[pub](){
auto msg = std_msgs::msg::String();
msg.data = "hello ros2";
pub->publish(msg);
});
node->create_wall_timer(std::chrono::milliseconds(500), [pub](){
auto msg = std_msgs::msg::String(); msg.data = "hello ros2"; pub->publish(msg);
});
rclcpp::spin(node); rclcpp::shutdown(); return 0;
}
在 my_pkg_cpp/CMakeLists.txt
增加可執行檔與安裝:
add_executable(talker src/talker.cpp) ament_target_dependencies(talker rclcpp std_msgs) install(TARGETS talker DESTINATION lib/${PROJECT_NAME})
~/ros2_ws_demo/src/my_pkg/launch/demo.launch.py
from launch import LaunchDescription
from launch_ros.actions import Node
def generate_launch_description():
return LaunchDescription([
Node(package='my_pkg', executable='talker', name='talker_py'),
# Node(package='my_pkg_cpp', executable='talker', name='talker_cpp'), # 若用 C++
])
cd ~/ros2_ws_demo
colcon build --symlink-install
source install/setup.bash
# 直接跑節點
ros2 run my_pkg talker # Python
# ros2 run my_pkg_cpp talker # C++
# 或用 launch
ros2 launch my_pkg demo.launch.py
# 檢查
ros2 node list
ros2 topic echo /chatter
每次更動程式後:colcon build
→ source install/setup.bash
。
若有多個 workspace,確保最後 source
的是你要用的那個。
新增相依套件:rosdep install -i --from-paths src -y
。
在 URDF/Xacro 中,LiDAR 本體會是一個 link(例如 lidar_link
),通常是個小圓柱體(僅供視覺化)。
接著用一個 joint(通常是 fixed)把 lidar_link
固定到 base_link
或車體上。
在 LiDAR link 裡,用 <gazebo>
標籤附加 Gazebo 插件,這樣模擬才會真的產生雷射掃描資料。
這是 gazebo_ros 套件提供的 Ray Sensor Plugin。它會把 Gazebo 模擬的 Ray sensor(光線投射)封裝成 ROS 2 的 Topic。
主要功能:
在 Gazebo 中模擬雷射掃描器(LiDAR)。
透過 ROS 2 topic 輸出 sensor_msgs/msg/LaserScan
或 sensor_msgs/msg/PointCloud2
。
可設定角度範圍、解析度、更新頻率、噪聲模型。
<!-- LiDAR 本體 link -->
<link name="lidar_link">
<visual>
<geometry>
<cylinder length="0.05" radius="0.03"/>
</geometry>
<material>
<color rgba="0 1 0 1"/>
</material>
</visual>
</link>
<!-- 固定在 base_link 上 -->
<joint name="lidar_joint" type="fixed">
<parent link="base_link"/>
<child link="lidar_link"/>
<origin xyz="0.0 0.0 0.1" rpy="0 0 0"/>
</joint>
<!-- Gazebo 插件 -->
<gazebo reference="lidar_link">
<sensor type="ray" name="lidar_sensor">
<pose>0 0 0 0 0 0</pose>
<visualize>true</visualize>
<update_rate>10</update_rate>
<ray>
<scan>
<horizontal>
<samples>360</samples>
<resolution>1</resolution>
<min_angle>-3.14159</min_angle>
<max_angle>3.14159</max_angle>
</horizontal>
</scan>
<range>
<min>0.12</min>
<max>12.0</max>
<resolution>0.01</resolution>
</range>
<noise>
<type>gaussian</type>
<mean>0.0</mean>
<stddev>0.01</stddev>
</noise>
</ray>
<!-- 插件: ROS 2 雷射轉換 -->
<plugin name="gazebo_ros_ray_sensor" filename="libgazebo_ros_ray_sensor.so">
<ros>
<namespace>/</namespace>
<remapping>scan:=/scan</remapping>
</ros>
<output_type>sensor_msgs/LaserScan</output_type>
<frame_name>lidar_link</frame_name>
</plugin>
</sensor>
</gazebo>
<sensor type="ray">
:告訴 Gazebo 這是一個 Ray Sensor(雷射)。
<ray><scan>…</scan></ray>
:定義雷射的掃描角度與分辨率(360 點、360°)。
<plugin filename="libgazebo_ros_ray_sensor.so">
:把 Gazebo 的 Ray Sensor 綁定到 ROS 2。
<output_type>
:決定輸出的資料型別(LaserScan / PointCloud2)。
<remapping>
:改 ROS topic 名稱(這裡輸出到 /scan
)。
<frame_name>
:指定發送 TF 的 frame id(通常是 lidar_link
)。
常見主題名稱:/scan
(2D LaserScan)或 /points
/ /points_raw
(PointCloud2)。實際名稱取決於你在 URDF/Gazebo 插件裡的 <remapping>
設定。
如何確認:
ros2 topic list | grep -E 'scan|point'
ros2 topic info /scan
# 或
ros2 interface show sensor_msgs/msg/LaserScan
RViz2 顯示 LaserScan:
開 rviz2
→ Fixed Frame 設 odom
(或 base_link
)。
Add → By display type → LaserScan。
Topic 選剛剛找到的 …/scan
;Frame 會用 frame_id
(常是 lidar_link
)。
若看不到:在 LaserScan display 裡把 Size (m) 調到 0.02~0.05、確認時間同步(/clock)與 TF 完整。
RViz2 顯示 PointCloud2(若輸出點雲):
Add → PointCloud2。
Topic 選 /points
(或你的點雲 topic)。
Style 選 Points,Size 0.02~0.05。
小檢查:你現在 plugin 輸出的是 sensor_msgs/LaserScan
還是 sensor_msgs/PointCloud2
?(告訴我型別,我幫你把 RViz 的設定精準化。)
下面是 Gazebo Classic 的 gzserver
/ gzclient
常用指令精簡表(不含新一代 gz sim
):
啟動空世界(純伺服器,無 GUI)
gzserver /usr/share/gazebo-11/worlds/empty.world
詳細輸出(除錯)
gzserver --verbose <world.sdf|world>
開始時暫停
gzserver --pause <world>
設定隨機種子(重現性)
gzserver --seed <int> <world>
只跑 N 個 step 後退出(批次/測試)
gzserver --iters <N> <world>
載入「伺服器插件」(e.g., ROS 工廠/控制)
gzserver -s libgazebo_ros_factory.so <world>
gzserver -s libgazebo_ros_init.so <world>
顯示說明 / 版本
gzserver --help
gzserver --version
連線到已啟動的 gzserver
gzclient
詳細輸出
gzclient --verbose
載入「GUI 插件」(面板/工具)
gzclient -g libgazebo_gui_overlay.so
說明 / 版本
gzclient --help
gzclient --version
伺服器與客戶端分開:
export ALSOFT_DRIVERS=null # 避免 ALSA 音效錯誤(可選)
gzserver /usr/share/gazebo-11/worlds/empty.world --verbose &
gzclient
gzserver --verbose empty.world \ -s libgazebo_ros_init.so \ -s libgazebo_ros_factory.so \ -s libgazebo_ros_force_system.so
直接用 wrapper 一起開(server+client):
gazebo --verbose /usr/share/gazebo-11/worlds/empty.world
與 ROS 2 整合(Classic):
# 允許從 /robot_description 動態生成模型
gzserver -s libgazebo_ros_factory.so /usr/share/gazebo-11/worlds/empty.world &
ros2 run gazebo_ros spawn_entity.py -entity my_bot -topic /robot_description
GAZEBO_RESOURCE_PATH
:worlds、materials 等資源搜尋路徑
GAZEBO_MODEL_PATH
:模型搜尋路徑(.sdf/.config 的目錄)
ALSOFT_DRIVERS=null
:關閉 OpenAL 音效(無音效卡時很有用)
[SLAM建圖] → [儲存地圖] → [AMCL定位] → [Nav2導航] → [移動控制]
bashsudo apt update sudo apt install ros-humble-navigation2 ros-humble-nav2-bringup \ ros-humble-slam-toolbox ros-humble-map-server \ ros-humble-amcl ros-humble-rviz2
ros-humble-map-server
找不到您收到的錯誤訊息 E: Unable to locate package ros-humble-map-server
表示您的系統找不到名為 ros-humble-map-server
的套件。
這是一個非常常見的錯誤,原因有以下幾點:
套件名稱不正確:在 ROS2 Humble 中,map_server
套件已經被整合到 navigation2
框架中。它不再是一個獨立的套件,而是作為 navigation2
的一部分。
軟體庫未更新:您的 apt
套件列表可能已經過時,導致無法找到新套件。
ROS2 軟體庫未添加:您可能沒有正確添加 ROS2 的軟體庫到您的 sources.list
。
要解決這個問題,您應該安裝整個 navigation2
套件,因為 map_server
已經是其中的一部分。
在安裝任何新套件之前,請務必更新您的本地套件清單。
sudo apt-get update
navigation2
navigation2
包含了 ROS2 的所有導航功能,其中也包含了 map_server
。
sudo apt-get install ros-humble-navigation2
這個指令會自動安裝 map_server
以及所有其他與導航相關的套件,例如:
amcl
:自適應蒙特卡洛定位。
slam_toolbox
:用於即時 SLAM。
nav2_bringup
:包含許多啟動檔案。
map_server
已安裝安裝完成後,您可以使用 ros2
指令來確認 map_server
是否已經可用。
ros2 pkg list | grep map_server
如果命令返回 map_server
,則表示套件已成功安裝。
總結:您不需要單獨安裝 map-server
。在 ROS2 Humble 中,它已經被整合到 navigation2
中。通過安裝 ros-humble-navigation2
,您將獲得所需的所有工具,不僅僅是 map-server
,還包括一個完整的導航堆疊。
為了在 RVIZ2 中監看 /twowheel_amr/scan
的雷射雷達數據並將其轉換成點素(點雲),
你需要啟動 RVIZ2 並正確設定顯示器(Display)。
首先,請確保你的 Gazebo 模擬器正在運行,並且機器人模型已成功載入。這會發布 /twowheel_amr/scan
主題數據。
接下來,在一個新的終端機視窗中,啟動 RVIZ2。
# 確保你是在你的工作空間中,並且環境變數已更新
cd ~/data/ros2_ws4
source install/setup.bash
# 啟動 RVIZ2
ros2 launch twowheel_bot twowheel_rviz.launch.py
這個指令會啟動 RVIZ2 並載入我們之前建立的 twowheel.rviz
配置檔。
如果 twowheel.rviz
檔案已經正確配置,LaserScan
顯示器應該會自動載入。如果你沒有看到,可以手動添加:
在 RVIZ2 左側的 Displays
面板中,點擊左下角的 Add
按鈕。
在彈出的視窗中,選擇 by topic
頁籤,找到 /twowheel_amr/scan
並選擇 sensor_msgs/msg/LaserScan
。
點擊 OK
。
LaserScan
轉換為點雲LaserScan
數據是二維的,RVIZ2 會以點或標記的形式將其顯示。如果你想將它轉換為三維的點雲(Point Cloud),你需要一個額外的 ROS2 節點。
laserscan_to_pointcloud
節點:這個節點會訂閱 sensor_msgs/msg/LaserScan
主題,並將其轉換為 sensor_msgs/msg/PointCloud2
主題。
安裝套件:
sudo apt-get install ros-humble-laser-filters
啟動節點:在一個新的終端機中,執行以下指令:
ros2 run laser_filters scan_to_cloud_node --ros-args --remap scan:=/twowheel_amr/scan
這個節點會自動發布一個新的主題,通常是 /scan_cloud
。
現在你已經有了點雲主題,可以在 RVIZ2 中將它顯示出來:
在 RVIZ2 左側的 Displays
面板中,點擊 Add
按鈕。
在彈出的視窗中,選擇 PointCloud2
。
在 Topic
欄位中,選擇 /scan_cloud
。
點擊 OK
。
你現在應該可以看到一個由雷射掃描數據生成的 3D 點雲。這個點雲會隨著你的機器人在 Gazebo 中移動而實時更新。