urdf로 모델을 그리려면 각 좌표값를 모두 알아야 한다. 수치를 넣어야 되는데 시간 많이 걸린다. 다행히도 gazebo가 제공하는 model 에디터로 사각형, 바퀴 등 을 쉽게 그릴 수 있다. 원하는 그림이 없다면 무료 프로그램인 blender로 스케치한 다음 stl, dae 파일로 export 후 gazebo에서 import하면 만족도가 높아진다.
그런데 ros가 urdf를 정식으로 지원하고, sdf는 지원하지 않는 듯 하다. sdf만으로는 joint_state_publisher, robot_state_publisher를 제대로 이용할 수 없다. sdf와 urdf는 사용 분야가 다르고, 한 형식이 확장하는 개념은 아니다. ros에서 모델을 불러 제어하기 위해서는 어떻게든 sdf를 urdf로 바꿔야 한다고 결론을 냈다. 다행히 관련 스크립트를 찾을 수 있었다.
https://ik-test.readthedocs.io/en/latest/pysdf/
실행하면 sdf 1.7은 지원하지 않지만, parse.py의 supported_sdf_versions에 1.7을 추가하면 큰 무리없이 사용할 수 있다.(오픈소스의 위대함!!)
22 mesh_path_env_name='MESH_WORKSPACE_PATH' 23 if mesh_path_env_name in os.environ: 24 catkin_ws_path = os.environ[mesh_path_env_name] 25 else: 26 catkin_ws_path = os.path.expanduser('~') + '/catkin_ws/src/' 27 supported_sdf_versions = [1.4, 1.5, 1.6, 1.7]
mesh 파일 부분을 넣는 부분이 잘 안되는데, stl 위치를 export로 넣어주면 된다. 찾은 stl, dae 모두 OR로 넣어주는데, 해당 부분을 찾아 정확하게 수정해야 한다.
now0930@amd2004:~/ros_test/workspace/src/battery_agv$ tree . ├── CMakeLists.txt ├── include │ └── battery_agv ├── launch │ ├── robot_description.launch │ └── staion.launch ├── meshes │ ├── axis_gear.stl │ ├── stepperMotor.stl │ └── stepperMotor3.stl ├── models │ └── AGV │ ├── axis_gear.stl │ ├── model.config │ ├── model.sdf │ ├── stepperMotor.stl │ └── stepperMotor3.stl ├── package.xml ├── src │ └── red_green_light.py ├── urdf │ ├── agv.urdf │ └── agv.xacro └── worlds └── testbed2 9 directories, 16 files
<?xml version="1.0"?> <robot xmlns:xacro="http://www.ros.org/wiki/xacro" name="AGV"> <joint name="AGV__link_bottomplate_JOINT_0" type="fixed"> <parent link="AGV__link_bottomplate"/> <child link="AGV__link_topplate"/> <origin xyz="0 -0.00667 1.86611" rpy="1.5708 0 0.01"/> <axis xyz="0 0 0"/> <limit lower="0" upper="0" effort="0" velocity="0"/> </joint> <joint name="AGV__link_bottomplate_JOINT_2" type="fixed"> <parent link="AGV__link_bottomplate"/> <child link="AGV__link_leftplate"/> <origin xyz="-8.80000e-04 -4.18110e-01 1.42999e+00" rpy="0.e+00 0.e+00 -2.e-05"/> <axis xyz="0 0 0"/> <limit lower="0" upper="0" effort="0" velocity="0"/> </joint> <joint name="AGV__link_bottomplate_JOINT_3" type="fixed"> <parent link="AGV__link_bottomplate"/> <child link="AGV__link_rightplate"/> <origin xyz="8.80000e-04 4.12490e-01 1.42998e+00" rpy="-3.14159 0 0"/> <axis xyz="0 0 0"/> <limit lower="0" upper="0" effort="0" velocity="0"/> </joint> <joint name="AGV__link_bottomplate_JOINT_4" type="revolute"> <parent link="AGV__link_bottomplate"/> <child link="AGV__link_leftwheel"/> <origin xyz="0.73669 -0.27702 1.05254" rpy="1.5708 0 0"/> <axis xyz="0 0 1"/> <limit lower="-1.79769e+308" upper="1.79769e+308" effort="-1.0" velocity="-1.0"/> </joint> <joint name="AGV__link_bottomplate_JOINT_5" type="revolute"> <parent link="AGV__link_bottomplate"/> <child link="AGV__link_rightwheel"/> <origin xyz="0.73669 0.27702 1.05434" rpy="1.5708 0 0"/> <axis xyz="0 0 1"/> <limit lower="-1.79769e+308" upper="1.79769e+308" effort="-1.0" velocity="-1.0"/> </joint> <joint name="AGV__link_bottomplate_JOINT_6" type="fixed"> <parent link="AGV__link_bottomplate"/> <child link="AGV__link_rightwheel0"/> <origin xyz="-0.73669 0.27702 1.05254" rpy="1.5708 0 0"/> <axis xyz="0 0 0"/> <limit lower="0" upper="0" effort="0" velocity="0"/> </joint> <joint name="AGV__link_bottomplate_JOINT_7" type="fixed"> <parent link="AGV__link_bottomplate"/> <child link="AGV__link_leftwheel0"/> <origin xyz="-0.73669 -0.27702 1.05254" rpy="1.5708 0 0"/> <axis xyz="0 0 0"/> <limit lower="0" upper="0" effort="0" velocity="0"/> </joint> <joint name="AGV__link_loadingplate_JOINT_2" type="fixed"> <parent link="AGV__link_loadingplate"/> <child link="AGV__link_rightgear"/> <origin xyz="0.87839 -0.40904 -0.19812" rpy="-1.57395e+00 -3.18700e-02 1.20000e-04"/> <axis xyz="0 0 0"/> <limit lower="0" upper="0" effort="0" velocity="0"/> </joint> <joint name="AGV__link_loadingplate_JOINT_3" type="fixed"> <parent link="AGV__link_loadingplate"/> <child link="AGV__link_leftgear"/> <origin xyz="0.86434 -0.40766 0.24228" rpy="-1.57395e+00 -3.18700e-02 1.20000e-04"/> <axis xyz="0 0 0"/> <limit lower="0" upper="0" effort="0" velocity="0"/> </joint> <joint name="AGV__link_loadingplate_JOINT_4" type="fixed"> <parent link="AGV__link_loadingplate"/> <child link="AGV__link_rightgear2"/> <origin xyz="-0.86391 -0.40925 -0.25368" rpy="-1.57395e+00 -3.18700e-02 1.20000e-04"/> <axis xyz="0 0 0"/> <limit lower="0" upper="0" effort="0" velocity="0"/> </joint> <joint name="AGV__link_loadingplate_JOINT_5" type="fixed"> <parent link="AGV__link_loadingplate"/> <child link="AGV__link_leftgear2"/> <origin xyz="-0.87795 -0.40787 0.18672" rpy="-1.57395e+00 -3.18700e-02 1.20000e-04"/> <axis xyz="0 0 0"/> <limit lower="0" upper="0" effort="0" velocity="0"/> </joint> <joint name="AGV__link_topplate_JOINT_0" type="fixed"> <parent link="AGV__link_topplate"/> <child link="AGV__link_motor"/> <origin xyz="-0.01114 0.04217 -0.43284" rpy="-1.00e-05 2.08e-03 0.00e+00"/> <axis xyz="0 0 0"/> <limit lower="0" upper="0" effort="0" velocity="0"/> </joint> <joint name="AGV__link_topplate_JOINT_6" type="prismatic"> <parent link="AGV__link_topplate"/> <child link="AGV__link_loadingplate"/> <origin xyz="0.0000e+00 4.0032e-01 3.1000e-04" rpy="3.150e-03 2.187e-02 -2.000e-05"/> <axis xyz="0 0 1"/> <limit lower="0.0" upper="0.3" effort="-1.0" velocity="-1.0"/> </joint> <link name="AGV__link_leftplate"> <inertial> <mass value="432.31"/> <origin xyz="0 0 0" rpy="0 0 0"/> <inertia ixx="26.0634" ixy="0" ixz="0" iyy="184.903" iyz="0" izz="158.909"/> </inertial> <collision name="AGV__collision"> <origin xyz="0 0 0" rpy="0 0 0"/> <geometry> <box size="2.1 0.01 0.85"/> </geometry> </collision> <visual name="AGV__visual"> <origin xyz="0 0 0" rpy="0 0 0"/> <geometry> <box size="2.1 0.01 0.85"/> </geometry> </visual> </link> <link name="AGV__link_bottomplate"> <inertial> <mass value="432.31"/> <origin xyz="0 0 0" rpy="0 0 0"/> <inertia ixx="26.0634" ixy="0" ixz="0" iyy="184.903" iyz="0" izz="158.909"/> </inertial> <collision name="AGV__collision"> <origin xyz="0 0 1" rpy="1.5708 0 0"/> <geometry> <box size="2.1 0.01 0.85"/> </geometry> </collision> <visual name="AGV__visual"> <origin xyz="0 0 1" rpy="1.5708 0 0"/> <geometry> <box size="2.1 0.01 0.85"/> </geometry> </visual> </link> <link name="AGV__link_leftgear"> <inertial> <mass value="20"/> <origin xyz="0 0 0" rpy="0 0 0"/> <inertia ixx="0" ixy="0" ixz="0" iyy="0" iyz="0" izz="0"/> </inertial> <collision name="AGV__collision"> <origin xyz="0 0 0" rpy="0 0 0"/> <geometry> <mesh filename="package://battery_agv/meshes/axis_gear.stl" scale="1 1 1"/> </geometry> </collision> <visual name="AGV__visual"> <origin xyz="0 0 0" rpy="0 0 0"/> <geometry> <mesh filename="package://battery_agv/meshes/axis_gear.stl" scale="1 1 1"/> </geometry> </visual> </link> <link name="AGV__link_leftgear2"> <inertial> <mass value="20"/> <origin xyz="0 0 0" rpy="0 0 0"/> <inertia ixx="0" ixy="0" ixz="0" iyy="0" iyz="0" izz="0"/> </inertial> <collision name="AGV__collision"> <origin xyz="0 0 0" rpy="0 0 0"/> <geometry> <mesh filename="package://battery_agv/meshes/axis_gear.stl" scale="1 1 1"/> </geometry> </collision> <visual name="AGV__visual"> <origin xyz="0 0 0" rpy="0 0 0"/> <geometry> <mesh filename="package://battery_agv/meshes/axis_gear.stl" scale="1 1 1"/> </geometry> </visual> </link> <link name="AGV__link_leftwheel"> <inertial> <mass value="10.3358"/> <origin xyz="0 0 0" rpy="0 0 0"/> <inertia ixx="0.107579" ixy="0" ixz="0" iyy="0.107579" iyz="0" izz="0.206717"/> </inertial> <collision name="AGV__collision"> <origin xyz="0 0 0" rpy="0 0 0"/> <geometry> <cylinder radius="0.2" length="0.07"/> </geometry> </collision> <visual name="AGV__visual"> <origin xyz="0 0 0" rpy="0 0 0"/> <geometry> <cylinder radius="0.2" length="0.07"/> </geometry> </visual> </link> <link name="AGV__link_leftwheel0"> <inertial> <mass value="10.3358"/> <origin xyz="0 0 0" rpy="0 0 0"/> <inertia ixx="0.107579" ixy="0" ixz="0" iyy="0.107579" iyz="0" izz="0.206717"/> </inertial> <collision name="AGV__collision"> <origin xyz="0 0 0" rpy="0 0 0"/> <geometry> <cylinder radius="0.2" length="0.07"/> </geometry> </collision> <visual name="AGV__visual"> <origin xyz="0 0 0" rpy="0 0 0"/> <geometry> <cylinder radius="0.2" length="0.07"/> </geometry> </visual> </link> <link name="AGV__link_loadingplate"> <inertial> <mass value="432.31"/> <origin xyz="0 0 0" rpy="0 0 0"/> <inertia ixx="26.0634" ixy="0" ixz="0" iyy="184.903" iyz="0" izz="158.909"/> </inertial> <collision name="AGV__collision"> <origin xyz="0 0 0" rpy="0 0 0"/> <geometry> <box size="2.1 0.01 0.85"/> </geometry> </collision> <visual name="AGV__visual"> <origin xyz="0 0 0" rpy="0 0 0"/> <geometry> <box size="2.1 0.01 0.85"/> </geometry> </visual> </link> <link name="AGV__link_motor"> <inertial> <mass value="1"/> <origin xyz="0 0 0" rpy="0 0 0"/> <inertia ixx="0.166667" ixy="0" ixz="0" iyy="0.166667" iyz="0" izz="0.166667"/> </inertial> <collision name="AGV__collision"> <origin xyz="0 0 0" rpy="0 0 0"/> <geometry> <mesh filename="package://battery_agv/meshes/stepperMotor3.stl" scale="1 1 1"/> </geometry> </collision> <visual name="AGV__visual"> <origin xyz="0 0 0" rpy="0 0 0"/> <geometry> <mesh filename="package://battery_agv/meshes/stepperMotor3.stl" scale="1 1 1"/> </geometry> </visual> </link> <link name="AGV__link_rightgear"> <inertial> <mass value="20"/> <origin xyz="0 0 0" rpy="0 0 0"/> <inertia ixx="0" ixy="0" ixz="0" iyy="0" iyz="0" izz="0"/> </inertial> <collision name="AGV__collision"> <origin xyz="0 0 0" rpy="0 0 0"/> <geometry> <mesh filename="package://battery_agv/meshes/axis_gear.stl" scale="1 1 1"/> </geometry> </collision> <visual name="AGV__visual"> <origin xyz="0 0 0" rpy="0 0 0"/> <geometry> <mesh filename="package://battery_agv/meshes/axis_gear.stl" scale="1 1 1"/> </geometry> </visual> </link> <link name="AGV__link_rightgear2"> <inertial> <mass value="20"/> <origin xyz="0 0 0" rpy="0 0 0"/> <inertia ixx="0" ixy="0" ixz="0" iyy="0" iyz="0" izz="0"/> </inertial> <collision name="AGV__collision"> <origin xyz="0 0 0" rpy="0 0 0"/> <geometry> <mesh filename="package://battery_agv/meshes/axis_gear.stl" scale="1 1 1"/> </geometry> </collision> <visual name="AGV__visual"> <origin xyz="0 0 0" rpy="0 0 0"/> <geometry> <mesh filename="package://battery_agv/meshes/axis_gear.stl" scale="1 1 1"/> </geometry> </visual> </link> <link name="AGV__link_rightplate"> <inertial> <mass value="432.31"/> <origin xyz="0 0 0" rpy="0 0 0"/> <inertia ixx="26.0634" ixy="0" ixz="0" iyy="184.903" iyz="0" izz="158.909"/> </inertial> <collision name="AGV__collision"> <origin xyz="0 0 0" rpy="0 0 0"/> <geometry> <box size="2.1 0.01 0.85"/> </geometry> </collision> <visual name="AGV__visual"> <origin xyz="0 0 0" rpy="0 0 0"/> <geometry> <box size="2.1 0.01 0.85"/> </geometry> </visual> </link> <link name="AGV__link_rightwheel"> <inertial> <mass value="10.3358"/> <origin xyz="0 0 0" rpy="0 0 0"/> <inertia ixx="0.107579" ixy="0" ixz="0" iyy="0.107579" iyz="0" izz="0.206717"/> </inertial> <collision name="AGV__collision"> <origin xyz="0 0 0" rpy="0 0 0"/> <geometry> <cylinder radius="0.2" length="0.07"/> </geometry> </collision> <visual name="AGV__visual"> <origin xyz="0 0 0" rpy="0 0 0"/> <geometry> <cylinder radius="0.2" length="0.07"/> </geometry> </visual> </link> <link name="AGV__link_rightwheel0"> <inertial> <mass value="10.3358"/> <origin xyz="0 0 0" rpy="0 0 0"/> <inertia ixx="0.107579" ixy="0" ixz="0" iyy="0.107579" iyz="0" izz="0.206717"/> </inertial> <collision name="AGV__collision"> <origin xyz="0 0 0" rpy="0 0 0"/> <geometry> <cylinder radius="0.2" length="0.07"/> </geometry> </collision> <visual name="AGV__visual"> <origin xyz="0 0 0" rpy="0 0 0"/> <geometry> <cylinder radius="0.2" length="0.07"/> </geometry> </visual> </link> <link name="AGV__link_topplate"> <inertial> <mass value="432.31"/> <origin xyz="0 0 0" rpy="0 0 0"/> <inertia ixx="26.0634" ixy="0" ixz="0" iyy="184.903" iyz="0" izz="158.909"/> </inertial> <collision name="AGV__collision"> <origin xyz="0 0 0" rpy="0 -0.01 0"/> <geometry> <box size="2.1 0.01 0.85"/> </geometry> </collision> <visual name="AGV__visual"> <origin xyz="0 0 0" rpy="0 -0.01 0"/> <geometry> <box size="2.1 0.01 0.85"/> </geometry> </visual> </link> </robot>
mesh는 package로 넣어야 하는 듯 한다. urdf로 R2D2를 만든 사람은 공간 감각이 뛰어나다.
잘 되다고 생각했으나, 문제가 있었다. 로봇이 제대로 움직이는지 확인하기 위해 joint_state_publisher를 실행해야 하는데, 프로세스가 죽는다. 왜 그런지 확인하기 어려웠는데, python 코드를 수정하여 어디에서 문제되는지 확인했다.
833 [rospy.simtime][INFO] 2022-10-09 13:09:14,685: initializing /clock core topic 834 [rospy.simtime][INFO] 2022-10-09 13:09:14,688: connected to core topic /clock 835 [rosout][INFO] 2022-10-09 13:09:14,760: Centering 836 [rospy.core][INFO] 2022-10-09 13:09:14,798: signal_shutdown [atexit] 837 [rospy.impl.masterslave][INFO] 2022-10-09 13:09:14,801: atexit
여기를 보면 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
해당부 코드를 고쳐보고, 로그를 출력시켜 보면
75 def center_event(self, event): 176 self.center() 177 178 def center(self): 179 rospy.loginfo("Centering") 180 for name, joint_info in self.joint_map.items(): 181 ##debug 182 rospy.loginfo("debug") 183 joint = joint_info['joint'] 184 rospy.loginfo(joint) 185 joint_info['slider'].setValue(self.valueToSlider(joi nt['zero'], joint))
결과를 다시 보면 argument 1이 min으로 들어온 -1.79..e+308임을 알았다.
866 [rosout][INFO] 2022-10-09 13:13:17,733: Centering 867 [rosout][INFO] 2022-10-09 13:13:17,735: debug 868 [rosout][INFO] 2022-10-09 13:13:17,736: {'min': -1.79769e+308, 'max': 1.79769e+308, 'zero': 0, 'position': 0} 869 [rospy.core][INFO] 2022-10-09 13:13:17,780: signal_shutdown [atexit]
limit를 적은 값으로 수정하면 된다.
24 <joint name="AGV__link_bottomplate_JOINT_4" type="revolute"> 25 <parent link="AGV__link_bottomplate"/> 26 <child link="AGV__link_leftwheel"/> 27 <origin xyz="0.73669 -0.27702 1.05254" rpy="1.5708 0 0"/> 28 <axis xyz="0 0 1"/> 29 <limit lower="-0.2" upper="0.5" effort="-1.0" velocity="-1.0"/> 30 </joint>
그런데 해당 부분은 바퀴라서 type을 continuous로 수정해도 된다. sdf가 continuous type을 선택할 수 없었다. 이거 찾는다고 거의 4시간을 썼다. TF 표시는 gazebo가 실행되면 제대로 표시된다.