Skip to content

Commit 345cd52

Browse files
authored
372 integration thermal (#450)
* initial commit; moving from laptop to PC Signed-off-by: Peter Geurts <peter.geurts@proton.me> * refactoring to raw TcpStream as camera API is very picky about header order Signed-off-by: Peter Geurts <peter.geurts@proton.me> * updated structure and added Dockerfile Signed-off-by: Peter Geurts <peter.geurts@proton.me> * implemented extract_response Signed-off-by: Peter Geurts <peter.geurts@proton.me> * implemented login data extraction Signed-off-by: Peter Geurts <peter.geurts@proton.me> * CHECKPOINT: ditching rust for python Signed-off-by: Peter Geurts <peter.geurts@proton.me> * switched to python Signed-off-by: Peter Geurts <peter.geurts@proton.me> * added IP address search based on MAC Signed-off-by: Peter Geurts <peter.geurts@proton.me> * first version of hardware working Signed-off-by: Peter Geurts <peter.geurts@proton.me> * gazebo world for thermal camera Signed-off-by: Peter Geurts <peter.geurts@proton.me> * added color palettes Signed-off-by: Peter Geurts <peter.geurts@proton.me> * docs Signed-off-by: Peter Geurts <peter.geurts@proton.me> * linting Signed-off-by: Peter Geurts <peter.geurts@proton.me> * added MacAddressFinder to look get IP address based on MAC address Signed-off-by: Peter Geurts <peter.geurts@proton.me> * fixed image and color palette Signed-off-by: Peter Geurts <peter.geurts@proton.me> * package restructuring without setup.py Signed-off-by: Peter Geurts <peter.geurts@proton.me> * package restructuring part 2, now the same as alliander_meta Signed-off-by: Peter Geurts <peter.geurts@proton.me> * publish image if simulation, compressed if hardware Signed-off-by: Peter Geurts <peter.geurts@proton.me> --------- Signed-off-by: Peter Geurts <peter.geurts@proton.me> Signed-off-by: Peter Geurts <25298249+geurto@users.noreply.github.com>
1 parent 5509d05 commit 345cd52

25 files changed

Lines changed: 922 additions & 2 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ ignore/
1717

1818
build/
1919
clangd/
20+
**/target/
2021

2122
.cache
2223
.venv

alliander_core/src/alliander_description/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ find_package(ament_cmake_python REQUIRED)
1111

1212
# Shared folders:
1313
install(
14-
DIRECTORY ewellix franka husarion nmea_gps ouster realsense velodyne xsens zed
14+
DIRECTORY ewellix franka husarion nmea_gps ouster realsense seekthermal velodyne xsens zed
1515
DESTINATION share/${PROJECT_NAME}
1616
)
1717

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
<?xml version="1.0"?>
2+
<!--
3+
SPDX-FileCopyrightText: Alliander N. V.
4+
SPDX-License-Identifier: Apache-2.0
5+
-->
6+
<robot xmlns:xacro="http://www.ros.org/wiki/xacro" name="seekthermal_module">
7+
<xacro:macro name="seekthermal_g300"
8+
params="
9+
namespace
10+
parent
11+
use_sim:=true
12+
">
13+
14+
<!-- Gazebo requires a joint to a link called "world" for statically mounted robots -->
15+
<xacro:if value="${parent == 'world'}">
16+
<link name="${parent}" />
17+
<joint name="${parent}_joint" type="fixed">
18+
<parent link="${parent}" />
19+
<child link="base_link" />
20+
</joint>
21+
</xacro:if>
22+
23+
<link name="base_link"/>
24+
<link name="seekthermal_link">
25+
26+
<visual>
27+
<origin xyz="0 0 0" rpy="0 0 0"/>
28+
<geometry>
29+
<box size="0.105 0.050 0.050"/>
30+
</geometry>
31+
<material name="dark_grey">
32+
<color rgba="0.2 0.2 0.2 1.0"/>
33+
</material>
34+
</visual>
35+
36+
<collision>
37+
<origin xyz="0 0 0" rpy="0 0 0"/>
38+
<geometry>
39+
<box size="0.105 0.050 0.050"/>
40+
</geometry>
41+
</collision>
42+
43+
<inertial>
44+
<mass value="0.340"/>
45+
<origin xyz="0 0 0" rpy="0 0 0"/>
46+
<inertia ixx="0.000142" ixy="0.0" ixz="0.0"
47+
iyy="0.000384" iyz="0.0"
48+
izz="0.000384"/>
49+
</inertial>
50+
51+
</link>
52+
53+
<joint name="seekthermal_joint" type="fixed">
54+
<parent link="base_link"/>
55+
<child link="seekthermal_link"/>
56+
<origin xyz="0 0 0" rpy="0 0 0"/>
57+
</joint>
58+
59+
<link name="seekthermal_link_optical"/>
60+
<joint name="seekthermal_optical_joint" type="fixed">
61+
<parent link="seekthermal_link"/>
62+
<child link="seekthermal_link_optical"/>
63+
<origin xyz="0 0 0" rpy="0 0 0"/>
64+
</joint>
65+
66+
<xacro:if value="${use_sim}">
67+
<gazebo reference="seekthermal_link_optical">
68+
<sensor type="thermal" name="seekthermal">
69+
<always_on>true</always_on>
70+
<update_rate>9.0</update_rate>
71+
72+
<topic>${namespace}/thermal/image</topic>
73+
<visualize>true</visualize>
74+
75+
<gz_frame_id>${namespace}/seekthermal_link_optical</gz_frame_id>
76+
<camera>
77+
<horizontal_fov>${58.0/180.0*pi}</horizontal_fov>
78+
<vertical_fov>${42.0/180.0*pi}</vertical_fov>
79+
<image>
80+
<width>320</width>
81+
<height>240</height>
82+
<format>L16</format>
83+
</image>
84+
<clip>
85+
<near>0.1</near>
86+
<far>100.0</far>
87+
</clip>
88+
</camera>
89+
90+
<plugin
91+
filename="ignition-gazebo-thermal-sensor-system"
92+
name="gz::sim::systems::ThermalSensor">
93+
<!-- -20 °C to 550 °C -->
94+
<min_temp>253.15</min_temp>
95+
<max_temp>823.15</max_temp>
96+
<!-- 570 degrees / 16 bits ~ 0.009 degrees resolution -->
97+
<resolution>0.01</resolution>
98+
</plugin>
99+
</sensor>
100+
</gazebo>
101+
</xacro:if>
102+
</xacro:macro>
103+
<xacro:arg name="namespace" default="seekthermal" />
104+
<xacro:arg name="parent" default="" />
105+
<xacro:seekthermal_g300 namespace="$(arg namespace)" parent="$(arg parent)" />
106+
</robot>

alliander_core/src/alliander_utilities/alliander_utilities/config_objects.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,17 @@ class Camera(Platform):
305305
platform_type: str = "Camera"
306306

307307

308+
@dataclass
309+
class ThermalCamera(Platform):
310+
"""Configuration for a ThermalCamera platform.
311+
312+
Attributes:
313+
platform_type (str): Type identifier for the platform.
314+
"""
315+
316+
platform_type: str = "ThermalCamera"
317+
318+
308319
@dataclass
309320
class Lidar(Platform):
310321
"""Configuration for a Lidar platform.
@@ -372,7 +383,7 @@ class PlatformList(Config):
372383

373384
platforms: List[
374385
Annotated[
375-
Union[Platform, Arm, Vehicle, Camera, GPS, IMU, Lidar, Lift],
386+
Union[Platform, Arm, Vehicle, Camera, GPS, IMU, Lidar, Lift, ThermalCamera],
376387
Discriminator(field="platform_type", include_supertypes=True),
377388
]
378389
] = field(default_factory=list)

alliander_gazebo/src/alliander_gazebo/launch/gazebo.launch.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,10 @@ def get_bridge_topics(platforms: list[T]) -> list[str]:
8383
bridge_topics.append(
8484
f"/{platform.namespace}/gps/fix@sensor_msgs/msg/NavSatFix@gz.msgs.NavSat"
8585
)
86+
if platform.platform_type == "ThermalCamera":
87+
bridge_topics.append(
88+
f"/{platform.namespace}/thermal/image@sensor_msgs/msg/Image@gz.msgs.Image"
89+
)
8690
if platform.platform_type == "IMU":
8791
bridge_topics.append(
8892
f"/{platform.namespace}/imu/data@sensor_msgs/msg/Imu@gz.msgs.IMU"
Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
<?xml version="1.0" ?>
2+
<!--
3+
SPDX-FileCopyrightText: Alliander N. V.
4+
5+
SPDX-License-Identifier: Apache-2.0
6+
-->
7+
<!-- from https://github.com/gazebosim/gz-sim/blob/e647570f25f962d63af75cf669ff72731d57bd5e/examples/worlds/thermal_camera.sdf -->
8+
9+
<!--
10+
Demo world consisting of a thermal camera sensor and several models with
11+
different temperatures specified via the thermal system plugin.
12+
-->
13+
14+
<sdf version="1.6">
15+
<world name="thermal_camera">
16+
<physics name="1ms" type="ignored">
17+
<max_step_size>0.001</max_step_size>
18+
<real_time_factor>1.0</real_time_factor>
19+
</physics>
20+
<plugin
21+
filename="ignition-gazebo-physics-system"
22+
name="ignition::gazebo::systems::Physics">
23+
</plugin>
24+
<plugin
25+
filename="ignition-gazebo-sensors-system"
26+
name="ignition::gazebo::systems::Sensors">
27+
<render_engine>ogre2</render_engine>
28+
</plugin>
29+
<plugin
30+
filename="ignition-gazebo-scene-broadcaster-system"
31+
name="ignition::gazebo::systems::SceneBroadcaster">
32+
</plugin>
33+
<plugin
34+
filename="ignition-gazebo-user-commands-system"
35+
name="ignition::gazebo::systems::UserCommands">
36+
</plugin>
37+
38+
<light type="directional" name="sun">
39+
<cast_shadows>true</cast_shadows>
40+
<pose>0 0 10 0 0 0</pose>
41+
<diffuse>0.8 0.8 0.8 1</diffuse>
42+
<specular>0.2 0.2 0.2 1</specular>
43+
<attenuation>
44+
<range>1000</range>
45+
<constant>0.9</constant>
46+
<linear>0.01</linear>
47+
<quadratic>0.001</quadratic>
48+
</attenuation>
49+
<direction>-0.5 0.1 -0.9</direction>
50+
</light>
51+
52+
<atmosphere type="adiabatic">
53+
<temperature>300</temperature>
54+
</atmosphere>
55+
56+
<model name="ground_plane">
57+
<static>true</static>
58+
<link name="link">
59+
<collision name="collision">
60+
<geometry>
61+
<!--plane>
62+
<normal>0 0 1</normal>
63+
<size>100 100</size>
64+
</plane-->
65+
<box>
66+
<size>20 20 0.1</size>
67+
</box>
68+
</geometry>
69+
</collision>
70+
<visual name="visual">
71+
<geometry>
72+
<!--plane>
73+
<normal>0 0 1</normal>
74+
<size>100 100</size>
75+
</plane-->
76+
<box>
77+
<size>20 20 0.1</size>
78+
</box>
79+
</geometry>
80+
<material>
81+
<ambient>0.8 0.8 0.8 1</ambient>
82+
<diffuse>0.8 0.8 0.8 1</diffuse>
83+
<specular>0.8 0.8 0.8 1</specular>
84+
</material>
85+
<plugin
86+
filename="ignition-gazebo-thermal-system"
87+
name="ignition::gazebo::systems::Thermal">
88+
<temperature>288.0</temperature>
89+
</plugin>
90+
</visual>
91+
</link>
92+
</model>
93+
94+
<model name="hotbox">
95+
<pose>-1 1 0.5 0 0 0</pose>
96+
<link name="hotbox_link">
97+
<inertial>
98+
<inertia>
99+
<ixx>1</ixx>
100+
<ixy>0</ixy>
101+
<ixz>0</ixz>
102+
<iyy>1</iyy>
103+
<iyz>0</iyz>
104+
<izz>1</izz>
105+
</inertia>
106+
<mass>1.0</mass>
107+
</inertial>
108+
<collision name="hotbox_collision">
109+
<geometry>
110+
<box>
111+
<size>1 1 1</size>
112+
</box>
113+
</geometry>
114+
</collision>
115+
116+
<visual name="hotbox_visual">
117+
<geometry>
118+
<box>
119+
<size>1 1 1</size>
120+
</box>
121+
</geometry>
122+
<material>
123+
<ambient>1 0 0 1</ambient>
124+
<diffuse>1 0 0 1</diffuse>
125+
<specular>1 0 0 1</specular>
126+
</material>
127+
<plugin
128+
filename="ignition-gazebo-thermal-system"
129+
name="ignition::gazebo::systems::Thermal">
130+
<temperature>373.0</temperature>
131+
</plugin>
132+
</visual>
133+
</link>
134+
</model>
135+
136+
<model name="coldbox">
137+
<pose>1 1.5 0.5 0 0 0</pose>
138+
<link name="coldbox_link">
139+
<inertial>
140+
<inertia>
141+
<ixx>1</ixx>
142+
<ixy>0</ixy>
143+
<ixz>0</ixz>
144+
<iyy>1</iyy>
145+
<iyz>0</iyz>
146+
<izz>1</izz>
147+
</inertia>
148+
<mass>1.0</mass>
149+
</inertial>
150+
<collision name="coldbox_collision">
151+
<geometry>
152+
<box>
153+
<size>1 1 1</size>
154+
</box>
155+
</geometry>
156+
</collision>
157+
158+
<visual name="coldbox_visual">
159+
<geometry>
160+
<box>
161+
<size>1 1 1</size>
162+
</box>
163+
</geometry>
164+
<material>
165+
<ambient>1 0 0 1</ambient>
166+
<diffuse>1 0 0 1</diffuse>
167+
<specular>1 0 0 1</specular>
168+
</material>
169+
<plugin
170+
filename="ignition-gazebo-thermal-system"
171+
name="ignition::gazebo::systems::Thermal">
172+
<temperature>298.0</temperature>
173+
</plugin>
174+
</visual>
175+
</link>
176+
</model>
177+
</world>
178+
</sdf>
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# SPDX-FileCopyrightText: Alliander N. V.
2+
#
3+
# SPDX-License-Identifier: Apache-2.0
4+
ARG BASE_IMAGE=ubuntu:latest
5+
FROM $BASE_IMAGE
6+
7+
ARG COLCON_BUILD_SEQUENTIAL
8+
ENV ROS_DISTRO=jazzy
9+
10+
# Install dependencies:
11+
RUN apt update && apt install -y --no-install-recommends \
12+
arp-scan \
13+
iproute2 \
14+
&& rm -rf /var/lib/apt/lists/* \
15+
&& apt autoremove -y \
16+
&& apt clean
17+
18+
# Install repo package:
19+
WORKDIR /$WORKDIR/ros
20+
COPY alliander_core/src/ /$WORKDIR/ros/src
21+
COPY alliander_seekthermal/src/ /$WORKDIR/ros/src
22+
RUN /$WORKDIR/colcon_build.sh
23+
24+
# Install python dependencies:
25+
WORKDIR $WORKDIR
26+
COPY pyproject.toml /$WORKDIR/pyproject.toml
27+
RUN uv sync --group alliander-seekthermal \
28+
&& echo "export PYTHONPATH=\"$(dirname $(dirname $(uv python find)))/lib/python3.12/site-packages:\$PYTHONPATH\"" >> /root/.bashrc \
29+
&& echo "export PATH=\"$(dirname $(dirname $(uv python find)))/bin:\$PATH\"" >> /root/.bashrc
30+
31+
# Finalize
32+
WORKDIR /$WORKDIR
33+
ENTRYPOINT ["/entrypoint.sh"]
34+
CMD ["sleep", "infinity"]
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# SPDX-FileCopyrightText: Alliander N. V.
2+
#
3+
# SPDX-License-Identifier: Apache-2.0
4+
services:
5+
alliander_seekthermal:
6+
image: allianderrobotics/seekthermal
7+
container_name: alliander_seekthermal
8+
runtime: nvidia
9+
network_mode: host
10+
privileged: true
11+
mem_limit: 6gb
12+
tty: true
13+
env_file:
14+
- .env
15+
volumes:
16+
- "/tmp/.X11-unix:/tmp/.X11-unix"
17+
- "/dev:/dev"
18+
command: ["/bin/bash", "-c", "ros2 launch alliander_seekthermal seekthermal.launch.py"]

0 commit comments

Comments
 (0)