Skip to content
GitLab
Explore
Sign in
Register
Primary navigation
Search or go to…
Project
R
Robobin
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Wiki
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Deploy
Releases
Package registry
Model registry
Operate
Terraform modules
Monitor
Incidents
Analyze
Value stream analytics
Contributor analytics
Repository analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
GitLab community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
plw1g21
Robobin
Commits
b3a27a25
Commit
b3a27a25
authored
6 months ago
by
Paul-Winpenny
Browse files
Options
Downloads
Patches
Plain Diff
Added the calibration mode to the robobin.
parent
b3c7cae5
No related branches found
No related tags found
No related merge requests found
Changes
1
Show whitespace changes
Inline
Side-by-side
Showing
1 changed file
ros2/src/robobin/robobin/uwb_navigation_node.py
+96
-17
96 additions, 17 deletions
ros2/src/robobin/robobin/uwb_navigation_node.py
with
96 additions
and
17 deletions
ros2/src/robobin/robobin/uwb_navigation_node.py
+
96
−
17
View file @
b3a27a25
...
@@ -7,7 +7,8 @@ import rclpy
...
@@ -7,7 +7,8 @@ import rclpy
from
rclpy.node
import
Node
from
rclpy.node
import
Node
from
std_msgs.msg
import
String
from
std_msgs.msg
import
String
from
sensor_msgs.msg
import
Imu
from
sensor_msgs.msg
import
Imu
# from transforms3d.euler import quat2euler
from
tf.transformations
import
euler_from_quaternion
import
math
import
math
class
SerialController
:
class
SerialController
:
...
@@ -16,6 +17,12 @@ class SerialController:
...
@@ -16,6 +17,12 @@ class SerialController:
self
.
windowSize
=
windowSize
self
.
windowSize
=
windowSize
self
.
minSuccessfulValues
=
minSuccessfulValues
self
.
minSuccessfulValues
=
minSuccessfulValues
self
.
maxRetries
=
maxRetries
self
.
maxRetries
=
maxRetries
self
.
calibration_active
=
False
self
.
calibration_timer
=
None
self
.
calib_start_position
=
None
self
.
calib_start_yaw
=
None
self
.
tag1Window
=
[[]
for
_
in
range
(
4
)]
self
.
tag1Window
=
[[]
for
_
in
range
(
4
)]
self
.
tag2Window
=
[[]
for
_
in
range
(
4
)]
self
.
tag2Window
=
[[]
for
_
in
range
(
4
)]
...
@@ -111,10 +118,10 @@ class UWBNode(Node):
...
@@ -111,10 +118,10 @@ class UWBNode(Node):
self
.
cmd_pub
=
self
.
create_publisher
(
Twist
,
'
/cmd_vel
'
,
10
)
self
.
cmd_pub
=
self
.
create_publisher
(
Twist
,
'
/cmd_vel
'
,
10
)
self
.
subscription
=
self
.
create_subscription
(
String
,
'
/nav_command
'
,
self
.
handle_nav_command
,
10
)
self
.
subscription
=
self
.
create_subscription
(
String
,
'
/nav_command
'
,
self
.
handle_nav_command
,
10
)
#
self.imu_subscription = self.create_subscription(Imu, '/imu', self.handle_imu, 10)
self
.
imu_subscription
=
self
.
create_subscription
(
Imu
,
'
/imu
'
,
self
.
handle_imu
,
10
)
self
.
current_yaw
=
0.0
# Store the current IMU yaw
self
.
current_yaw
=
0.0
self
.
uwb_to_imu_offset
=
0.0
# Offset between UWB and IMU heading
self
.
uwb_to_imu_offset
=
0.0
self
.
mode
=
"
Manual
"
self
.
mode
=
"
Manual
"
self
.
serial_controller
=
SerialController
(
"
/dev/ttyACM0
"
)
self
.
serial_controller
=
SerialController
(
"
/dev/ttyACM0
"
)
self
.
anchors
=
{}
self
.
anchors
=
{}
...
@@ -207,13 +214,22 @@ class UWBNode(Node):
...
@@ -207,13 +214,22 @@ class UWBNode(Node):
self
.
get_logger
().
error
(
f
"
Failed to determine anchors:
{
e
}
"
)
self
.
get_logger
().
error
(
f
"
Failed to determine anchors:
{
e
}
"
)
def
handle_imu
(
self
,
msg
):
def
handle_imu
(
self
,
msg
):
# Extract yaw from quaternion using transforms3d
# Extract the quaternion from the IMU message
orientation_q
=
msg
.
orientation
qx
=
msg
.
orientation
.
x
orientation_list
=
[
orientation_q
.
w
,
orientation_q
.
x
,
orientation_q
.
y
,
orientation_q
.
z
]
qy
=
msg
.
orientation
.
y
# roll, pitch, yaw = quat2euler(orientation_list) # Converts quaternion to Euler angles
qz
=
msg
.
orientation
.
z
qw
=
msg
.
orientation
.
w
# Convert quaternion to Euler angles (roll, pitch, yaw)
# euler_from_quaternion expects [x, y, z, w]
(
roll
,
pitch
,
yaw
)
=
euler_from_quaternion
([
qx
,
qy
,
qz
,
qw
])
# Store yaw (in radians) for usage in your node
self
.
current_yaw
=
yaw
# Optional: Print or log if you want to see the values
self
.
get_logger
().
info
(
f
"
IMU yaw (rad):
{
yaw
:
.
3
f
}
"
)
self
.
current_yaw
=
0
# Update current yaw
# self.get_logger().info(f"IMU Yaw: {yaw:.2f} radians")
def
determine_anchor_coords
(
self
,
measured_distances
):
def
determine_anchor_coords
(
self
,
measured_distances
):
y_A
=
measured_distances
[
2
]
# Distance from A to D
y_A
=
measured_distances
[
2
]
# Distance from A to D
...
@@ -449,20 +465,20 @@ class UWBNode(Node):
...
@@ -449,20 +465,20 @@ class UWBNode(Node):
self
.
cmd_pub
.
publish
(
stop_cmd
)
# Publish zero velocity
self
.
cmd_pub
.
publish
(
stop_cmd
)
# Publish zero velocity
self
.
get_logger
().
info
(
"
Motors stopped.
"
)
self
.
get_logger
().
info
(
"
Motors stopped.
"
)
if
mode
in
[
"
Manual
"
,
"
Follow
"
,
"
Call
"
]:
if
mode
in
[
"
Manual
"
,
"
Follow
"
,
"
Call
"
,
"
Calibration
"
]:
self
.
mode
=
mode
self
.
mode
=
mode
self
.
get_logger
().
info
(
f
"
Mode set to:
{
mode
}
"
)
self
.
get_logger
().
info
(
f
"
Mode set to:
{
mode
}
"
)
# Stop publishing to the pathing node when switching modes
if
mode
==
"
Manual
"
:
if
mode
==
"
Manual
"
:
self
.
call_mode_active
=
False
# Disable call mode
self
.
call_mode_active
=
False
self
.
follow_offset_computed
=
False
self
.
follow_offset_computed
=
False
self
.
follow_target_reached
=
False
self
.
follow_target_reached
=
False
self
.
get_logger
().
info
(
"
Publishing to pathing node stopped.
"
)
self
.
get_logger
().
info
(
"
Publishing to pathing node stopped.
"
)
# Stop any movement commands
stop_cmd
=
Twist
()
stop_cmd
=
Twist
()
self
.
cmd_pub
.
publish
(
stop_cmd
)
# Publish zero velocity
self
.
cmd_pub
.
publish
(
stop_cmd
)
self
.
get_logger
().
info
(
"
Motors stopped.
"
)
self
.
get_logger
().
info
(
"
Motors stopped.
"
)
elif
mode
==
"
Follow
"
:
elif
mode
==
"
Follow
"
:
...
@@ -473,6 +489,9 @@ class UWBNode(Node):
...
@@ -473,6 +489,9 @@ class UWBNode(Node):
elif
mode
==
"
Call
"
:
elif
mode
==
"
Call
"
:
self
.
call_mode_active
=
False
self
.
call_mode_active
=
False
self
.
get_logger
().
info
(
"
Call mode initialized but not active yet.
"
)
self
.
get_logger
().
info
(
"
Call mode initialized but not active yet.
"
)
elif
mode
==
"
Calibration
"
:
self
.
get_logger
().
info
(
"
Calibration mode initialized.
"
)
self
.
start_calibration
()
else
:
else
:
self
.
get_logger
().
error
(
"
Invalid mode for UWB Navigation
"
)
self
.
get_logger
().
error
(
"
Invalid mode for UWB Navigation
"
)
...
@@ -487,7 +506,7 @@ class UWBNode(Node):
...
@@ -487,7 +506,7 @@ class UWBNode(Node):
end_location
=
parts
[
2
]
end_location
=
parts
[
2
]
self
.
get_logger
().
info
(
f
"
Call received: Start=
{
start_location
}
, End=
{
end_location
}
"
)
self
.
get_logger
().
info
(
f
"
Call received: Start=
{
start_location
}
, End=
{
end_location
}
"
)
# Load graph
# Load graph
workspace_root
=
os
.
getcwd
()
# Current working directory
workspace_root
=
os
.
getcwd
()
graph_file_path
=
os
.
path
.
abspath
(
os
.
path
.
join
(
workspace_root
,
"
src
"
,
"
robobin
"
,
"
robobin
"
,
"
graphs
"
,
"
graph.json
"
))
graph_file_path
=
os
.
path
.
abspath
(
os
.
path
.
join
(
workspace_root
,
"
src
"
,
"
robobin
"
,
"
robobin
"
,
"
graphs
"
,
"
graph.json
"
))
try
:
try
:
with
open
(
graph_file_path
,
'
r
'
)
as
f
:
with
open
(
graph_file_path
,
'
r
'
)
as
f
:
...
@@ -503,7 +522,7 @@ class UWBNode(Node):
...
@@ -503,7 +522,7 @@ class UWBNode(Node):
self
.
get_logger
().
error
(
f
"
Error:
{
str
(
e
)
}
"
)
self
.
get_logger
().
error
(
f
"
Error:
{
str
(
e
)
}
"
)
return
return
# Run A* to find path
self
.
path
=
self
.
a_star_search
(
self
.
graph_data
,
start_location
,
end_location
)
self
.
path
=
self
.
a_star_search
(
self
.
graph_data
,
start_location
,
end_location
)
if
self
.
path
:
if
self
.
path
:
self
.
get_logger
().
info
(
f
"
Path found:
{
self
.
path
}
"
)
self
.
get_logger
().
info
(
f
"
Path found:
{
self
.
path
}
"
)
...
@@ -583,6 +602,66 @@ class UWBNode(Node):
...
@@ -583,6 +602,66 @@ class UWBNode(Node):
total_path
.
insert
(
0
,
nodes
[
current
][
"
name
"
])
total_path
.
insert
(
0
,
nodes
[
current
][
"
name
"
])
return
total_path
return
total_path
def
start_calibration
(
self
):
"""
When doing calibration, the robobin will drive forward for 2 seconds at 0.25 m/s (WARNING).
"""
if
not
self
.
current_tag1_position
:
self
.
get_logger
().
error
(
"
No current UWB position available - cannot calibrate.
"
)
return
self
.
calibration_active
=
True
self
.
calib_start_position
=
self
.
current_tag1_position
self
.
calib_start_yaw
=
self
.
current_yaw
forward_cmd
=
Twist
()
forward_cmd
.
linear
.
x
=
0.25
self
.
cmd_pub
.
publish
(
forward_cmd
)
self
.
get_logger
().
info
(
"
Driving forward at 0.25 m/s for 2 seconds...
"
)
self
.
calibration_timer
=
self
.
create_timer
(
2.0
,
self
.
finish_calibration
)
def
finish_calibration
(
self
):
if
self
.
calibration_timer
:
self
.
destroy_timer
(
self
.
calibration_timer
)
self
.
calibration_timer
=
None
stop_cmd
=
Twist
()
self
.
cmd_pub
.
publish
(
stop_cmd
)
if
not
self
.
current_tag1_position
:
self
.
get_logger
().
error
(
"
No final UWB position available - calibration failed.
"
)
self
.
calibration_active
=
False
return
calib_end_position
=
self
.
current_tag1_position
calib_end_yaw
=
self
.
current_yaw
dx
=
calib_end_position
[
0
]
-
self
.
calib_start_position
[
0
]
dy
=
calib_end_position
[
1
]
-
self
.
calib_start_position
[
1
]
uwb_heading
=
math
.
atan2
(
dy
,
dx
)
offset
=
calib_end_yaw
-
uwb_heading
self
.
uwb_to_imu_offset
=
offset
self
.
get_logger
().
info
(
"
CALIBRATION COMPLETE!
"
)
self
.
get_logger
().
info
(
f
"
Start Pos (UWB):
{
self
.
calib_start_position
}
"
)
self
.
get_logger
().
info
(
f
"
End Pos (UWB):
{
calib_end_position
}
"
)
self
.
get_logger
().
info
(
f
"
UWB heading:
{
uwb_heading
:
.
3
f
}
rad
"
)
self
.
get_logger
().
info
(
f
"
IMU yaw:
{
calib_end_yaw
:
.
3
f
}
rad
"
)
self
.
get_logger
().
info
(
f
"
--> OFFSET:
{
offset
:
.
3
f
}
rad
"
)
self
.
calibration_active
=
False
self
.
mode
=
"
Manual
"
def
main
(
args
=
None
):
def
main
(
args
=
None
):
rclpy
.
init
(
args
=
args
)
rclpy
.
init
(
args
=
args
)
node
=
UWBNode
()
node
=
UWBNode
()
...
...
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment