gazebo 모델 에디터에서 원하는 모양을 그리기 너무 어렵다. 좌표를 수정하기 어렵고, 버그로 인해 객체가 가끔 보이지 않는다. 이번 기회에 오픈소스 프로그램 blender를 배워 3d로 직접 그리기로 했다.
쉬운 게 없다고, blender는 다른 세상이다. 기본인 mesh에서 solidify로 두께를 만든다 던가, cursor를 selection으로 이동하는 등 몇 가지 필수로 알아야 한다. 기본이 없는 상태에서 시작하면 시간을 너무 오래 쓴다. 2D 그림보다 3D 그림이 삼십배 시간이 많이 든다. 3D로 그릴 때 dimension 넣는 방법도 몰라 한참을 헤맸다.
디멘전까지 정확하게 넣었고, 잘 이동한 다음, 줄과 열을 맞춰 4~5개 객체를 통째로 stl로 출력했다. 자 이제 이 단순화된 그림을 gazebo 모델 에디터로 부르기만 하면 된다. 기존에 10개 넘었던 링크가 2개로 단순화 된다!
웬걸..망했다. gazebo는 대략적으로 형상과 재질을 설정하고, 무게와 관성 모멘트를 대충 넣어 주는데, stl을 불러오면 그런 거 하나도 없다. 눈에 어떻게 보이는지만 다르다. 결론은 복잡해도 gazebo에서 대략적으로 그려줘야 한다. 하루종일 그려준게 아까우니 urdf로 만든 파일에 geomery에서 mesh를 찾아 넣어 주기로 했다.
아. gazebo에서 그릴 때 좌표계를 확인하고 넣어 줬어야 했는데, 쓰기 어렵다 보니 일일히 신경쓰지 않았다. 웬걸. 스킨이 방향이 하나도 안 맞는gazebo 모델 에디터에서 원하는 모양을 그리기 너무 어렵다. 좌표를 수정하기 어렵고, 버그로 인해 객체가 가끔 보이지 않는다. 이번 기회에 오픈소스 프로그램 blender를 배워 3d로 직접 그리기로 했다.다. 이거 하는데 하루 종일 걸렸다. 알면 너무나 쉬운데, 모르면 너무 오래 걸린다.
urdf로 모델을 그리려면 각 좌표값를 모두 알아야 한다. 수치를 넣어야 되는데 시간 많이 걸린다. 다행히도 gazebo가 제공하는 model 에디터로 사각형, 바퀴 등 을 쉽게 그릴 수 있다. 원하는 그림이 없다면 무료 프로그램인 blender로 스케치한 다음 stl, dae 파일로 export 후 gazebo에서 import하면 만족도가 높아진다.
그런데 ros가 urdf를 정식으로 지원하고, sdf는 지원하지 않는 듯 하다. sdf만으로는 joint_state_publisher, robot_state_publisher를 제대로 이용할 수 없다. sdf와 urdf는 사용 분야가 다르고, 한 형식이 확장하는 개념은 아니다. ros에서 모델을 불러 제어하기 위해서는 어떻게든 sdf를 urdf로 바꿔야 한다고 결론을 냈다. 다행히 관련 스크립트를 찾을 수 있었다.
여기를 보면 centering 하다가 죽었음을 알았다. 왜 그런지 알 수 없으니, stdout에 표시된 코드를 보면 joint_state_publiser_gui의 182번 줄에서 죽었음을 확인했다.
QStandardPaths: wrong ownership on runtime directory /run/user/1000, 1000 instead of 0
QStandardPaths: wrong ownership on runtime directory /run/user/1000, 1000 instead of 0
Traceback (most recent call last):
File "/opt/ros/noetic/lib/joint_state_publisher_gui/joint_state_publisher_gui", line 52, in <module>
jsp_gui = joint_state_publisher_gui.JointStatePublisherGui("Node: " + rospy.get_name(),
File "/opt/ros/noetic/lib/python3/dist-packages/joint_state_publisher_gui/__init__.py", line 125, in __init__
self.center()
File "/opt/ros/noetic/lib/python3/dist-packages/joint_state_publisher_gui/__init__.py", line 182, in center
joint_info['slider'].setValue(self.valueToSlider(joint['zero'], joint))
TypeError: setValue(self, int): argument 1 has unexpected type 'float'
[joint_state_publisher_gui-1] process has died [pid 11275, exit code 1, cmd /opt/ros/noetic/lib/joint_state_publisher_gui/joint_state_publisher_gui __name:=joint_state_publisher_gui __log:=/root/.ros/log/b95997f0-47a6-11ed-a96e-2cf05d88e0ec/joint_state_publisher_gui-1.log].
log file: /root/.ros/log/b95997f0-47a6-11ed-a96e-2cf05d88e0ec/joint_state_publisher_gui-1*.log
여기 있는 gmapping, slam 튜토리얼을 했다. 튜토리얼은 상당히 쉽게 구성되어 있으나, 내가 실재로 해보면 잘 안된다. 왜 그런지 확인하기 위해 삽질을 했다. 해당 사이트에서 다운로드 받을 수 있는 rosbag 파일에 어떤 토픽이 저장되어 있는지 궁금했다. rqt가 제공하는 rosbag 플러그인으로 어떤 데이터가 있는지 확인했다.
rosbag play로 실행하면 뭐가 잘못 되었는지 다음 메세지를 보았다. debug 레벨을 켜라는 소리인 듯 한데…
[ WARN] [1664664315.511464153]: MessageFilter [target=odom ]: Dropped 100.00% of messages so far. Please turn the [ros.gmapping.message_filter] rosconsole logger to DEBUG for more information.
rosconsole로 message filter 현재 상태를 확인하고 변경할 수 있다.
root:workspace$rosconsole get /slam_gmapping ros.gmapping.message_filter
info
root:workspace$rosconsole set slam_gmapping ros.gmapping.message_filter debug
rosbag에 -a 옵션을 주고 모든 데이터를 저장하면 지도를 만들 수 있다. 아래와 같이 tf와 scan 데이터만 저장한 다음 rosbag play를 실행하면 tf tree가 끊어져 있다. 정확하게는 basefoot_print와 base_link가 끊겨져 있고, scanner_link가 없다.
ros@workspace$rosbag record /tf /laser/scan
[ INFO] [1664767902.638118674]: Subscribing to /laser/scan
[ INFO] [1664767902.642276643]: Subscribing to /tf
[ INFO] [1664767902.645339564]: Recording to '2022-10-03-03-31-42.bag'.
ros@workspace$ros@bag$rosbag play --clock 2022-10-03-03-31-42.bag
하나씩 해 보다가 /tf_static까지 record 해 보았다. 아, 힘들었다^^!
ros@bag$rosbag record -o mydata /tf /tf_static /laser/scan
[ INFO] [1664769638.007469286]: Subscribing to /laser/scan
[ INFO] [1664769638.011680347]: Subscribing to /tf
[ INFO] [1664769638.014193685]: Subscribing to /tf_static
[ INFO] [1664769638.298793651, 1626.350000000]: Recording to 'mydata_2022-10-03-04-00-38.bag'.
record한 결과를 보면 지도가 잘 생성됨을 확인했다. tf_tree를 보면
위 빠진 tf_tree와 다르게 base_footprint와 base_link가, base_link와 scanner_link가 연결되었다. 왜 이렇게 결정되었는지 확인해 보면, urdf에 fixed라고 joint를 정의해서 그렇다. 아래 urdf를 정의한 부분을 확인해 보면 fixed로 joint를 정의했기 때문에 tf_static을 필요로 한다.
42
43 <joint name = "base_footprint_fixed" type = "fixed">
44 <origin xyz = "0 0 0.03" rpy = "0 0 0"/>
45 <parent link = "base_footprint"/>
46 <child link = "base_link"/>
47 </joint>
48
146 <joint name = "left_wheel_joint" type = "continuous">
147 <origin xyz = "0.1 0.15 0" rpy = "0 0 0"/>
148 <parent link = "base_link"/>
149 <child link = "left_wheel"/>
150 <axis xyz = "0 1 0"/>
151
152 </joint>
153
154 <joint name = "left_wheel_joint_back" type = "continuous">
155 <origin xyz = "-0.1 0.15 0" rpy = "0 0 0"/>
156 <parent link = "base_link"/>
157 <child link = "left_wheel_back"/>
158 <axis xyz = "0 1 0"/>
159
160 </joint>
161
162
205 <joint name = "head_scanner" type = "fixed">
206 <!-- rpy를 수정하면 카메라 각도가 바뀜-->
207 <origin xyz = "0.15 0 0" rpy = "0 0 0"/>
208 <parent link = "base_link"/>
209 <child link = "scanner_link"/>
210 </joint>
211
기록한 rosbag 파이을 보면, scan, tf, tf_static 3개가 들어가 있다.
launch가 robot description을 실행하는데, command로 robot.xacro로 세부 설정을 한다. command가 아닌 다른 방식으로는 모델이 움직이지 않는다. robot description 내부에는 robot_description, joint_state_publisher, robot_state_publisher 3개를 설정한다.
now0930@amd2004:~/ros_test/workspace/src/wander_bot$ cat launch/robot_description.launch
<?xml version="1.0"?>
<launch>
<!-- send urdf to param server -->
<!-- xacro -->
<param name="robot_description" command="$(find xacro)/xacro --inorder '$(find wander_bot)/urdf/robot.xacro'" />
<!-- urdf or raw xml file -->
<!-- <param name="robot_description" command="cat $(find museumGuide)/urdf_model/peoplebot.xml" /> -->
<!-- Send fake joint values-->
<node name="joint_state_publisher" pkg="joint_state_publisher" type="joint_state_publisher">
<param name="use_gui" value="False"/>
</node>
<!-- Send robot states to tf -->
<node name="robot_state_publisher" pkg="robot_state_publisher" type="robot_state_publisher" respawn="false" output="screen"/>
</launch>
물리 엔진을 가지고 있는 gazebo로 로봇을 만들지 않고도 쉽게 테스트 해 볼 수 있다. 기본 설정 파일이 있는데, 사용자가 취향에 맞춰 자유롭게 환경 설정을 할 수도 있다. 빈 공간에 각종 모듈을 불러 내 공간에 넣고, gazebo tutorial을 따르면 로봇도 spawn 가능하다.
Programming Robots With Ros 책 144p에 보면 간단한 python 파일을 만들고 gazebo에 로딩하는 내용이 있다.
사용자가 만든 urdf 파일을 gazebo 가상 환경에 spawn 할 수 있다. 다만 inetia를 모두 입력해야(틀린 값이라도 입력해야) gazebo가 인식한다. spawn_model가 gazebo_ros 패키지에 포함되어 있다. inetia 값을 입력하지 않으면 로딩이 안된다. 중간에 나오는 spawn service failed는 무시해도 괜찮은 듯 하다. 어쨋든 굴러 가니까.
[INFO] [1662392890.824294, 55.975000]: Spawn status: SpawnModel: Entity pushed to spawn queue, but spawn service timed out waiting for entity to appear in simulation under the name agv_number1
[ERROR] [1662392890.827400, 55.975000]: Spawn service failed. Exiting.