Skip to content

Commit 083a303

Browse files
BackFront + Joy + change IHM + some change + some fix (#26)
Co-authored-by: Modelec <modelec.isen@gmail.com>
1 parent a481324 commit 083a303

43 files changed

Lines changed: 797 additions & 408 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

install.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,9 @@ git submodule init
2727
git submodule update
2828

2929
echo "source /opt/ros/jazzy/setup.bash
30-
source ~/modelec-marcel-ROS/install/setup.bash
30+
source ~/Modelec-ROS/install/setup.bash
3131
export RMW_IMPLEMENTATION=rmw_fastrtps_cpp
32-
export FASTRTPS_DEFAULT_PROFILES_FILE=~/modelec-marcel-ROS/fastdds_setup.xml
32+
export FASTRTPS_DEFAULT_PROFILES_FILE=~/Modelec-ROS/fastdds_setup.xml
3333
export ROS_DOMAIN_ID=128" >> ~/.bashrc
3434

3535
source ~/.bashrc

no_lidar.ros2_launch_marcel.desktop

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
Type=Application
33
Name=No Lidar
44
Comment=Lance le système ROS 2 avec GUI
5-
Exec=/home/modelec/modelec-marcel-ROS/start_ros2.sh with_joy:=false with_lidar:=false
5+
Exec=/home/modelec/modelec-marcel-ROS/start_ros2.sh with_joy:=false with_rplidar:=false
66
Icon=utilities-terminal
77
Terminal=true
88
Categories=Utility;

simulated_pcb/action.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,7 @@ def stop(self):
210210
sim.stop()
211211
print("Stopped.")
212212

213-
# socat -d -d pty,raw,echo=0,link=/tmp/ACTION_SIM pty,raw,echo=0,link=/tmp/ACTION_USB
214-
# python3 simulated_pcb/action.py --port /tmp/ACTION_SIM
215-
# socat -d -d pty,raw,echo=0,link=/tmp/ODO_SIM pty,raw,echo=0,link=/tmp/ODO_USB
216-
# python3 simulated_pcb/action.py --port /tmp/ODO_SIM
213+
# socat -d -d pty,raw,echo=0,link=/tmp/SIM_ACTION pty,raw,echo=0,link=/tmp/USB_ACTION
214+
# python3 simulated_pcb/action.py --port /tmp/SIM_ACTION
215+
# socat -d -d pty,raw,echo=0,link=/tmp/SIM_ODO pty,raw,echo=0,link=/tmp/USB_ODO
216+
# python3 simulated_pcb/action.py --port /tmp/SIM_ODO

simulated_pcb/odo.py

Lines changed: 85 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,20 @@ def __init__(self, port='/dev/pts/6', baud=115200):
1414
self.x = 1225.0
1515
self.y = 300.0
1616
self.theta = math.pi / 2
17+
1718
self.vx = 0.0
1819
self.vy = 0.0
1920
self.vtheta = 0.0
21+
2022
self.last_update = time.time()
2123
self.last_send = time.time()
2224

2325
self.pid = [0, 0, 0]
2426
self.tof = {0: 1000, 1: 1000, 2: 1000}
25-
self.waypoints = {} # waypoints by id
26-
self.waypoint_order = [] # ordered list of ids
27+
28+
# --- waypoint system ---
29+
self.waypoints = {} # id -> waypoint
30+
self.waypoint_queue = [] # FIFO execution
2731
self.current_wp_id = None
2832

2933
self.thread = threading.Thread(target=self.run)
@@ -37,52 +41,53 @@ def update_position(self):
3741
dt = now - self.last_update
3842
self.last_update = now
3943

40-
if self.start:
41-
if self.waypoint_order:
42-
wp = self.waypoints[self.waypoint_order[0]]
43-
dx = wp['x'] - self.x
44-
dy = wp['y'] - self.y
45-
distance = math.hypot(dx, dy)
46-
angle = math.atan2(dy, dx)
47-
angle_diff = self.normalize_angle(wp['theta'] - self.theta)
48-
49-
if wp['type'] == 1:
50-
if distance < 5.0 and abs(angle_diff) < 0.15:
51-
self.vx = 0.0
52-
self.vy = 0.0
53-
self.vtheta = 0.0
54-
self.ser.write(f'SET;WAYPOINT;{wp["id"]}\n'.encode('utf-8'))
55-
self.current_wp_id = wp['id']
56-
self.waypoint_order.pop(0)
57-
del self.waypoints[wp['id']]
58-
else:
59-
speed = min(100.0, distance / 2)
60-
self.vx = speed * math.cos(angle)
61-
self.vy = speed * math.sin(angle)
62-
self.vtheta = max(-9.0, min(9.0, angle_diff * 2))
63-
else:
64-
speed = min(150.0, distance / 2)
65-
self.vx = speed * math.cos(angle)
66-
self.vy = speed * math.sin(angle)
67-
self.vtheta = 0.0
68-
if distance < 100:
69-
self.ser.write(f'SET;WAYPOINT;{wp["id"]}\n'.encode('utf-8'))
70-
self.current_wp_id = wp['id']
71-
self.waypoint_order.pop(0)
72-
del self.waypoints[wp['id']]
73-
else:
74-
self.vx = 0.0
75-
self.vy = 0.0
44+
if self.start and self.waypoint_queue:
45+
wp_id = self.waypoint_queue[0]
46+
wp = self.waypoints[wp_id]
47+
48+
dx = wp['x'] - self.x
49+
dy = wp['y'] - self.y
50+
distance = math.hypot(dx, dy)
51+
52+
target_angle = math.atan2(dy, dx)
53+
theta_error = self.normalize_angle(wp['theta'] - self.theta)
54+
55+
# --- motion ---
56+
if wp['type'] == 1: # precise waypoint
57+
speed = min(120.0, distance * 1)
58+
self.vtheta = max(-6.0, min(6.0, theta_error * 2))
59+
else: # transit waypoint
60+
speed = min(180.0, distance * 2)
7661
self.vtheta = 0.0
7762

78-
self.x += self.vx * dt
79-
self.y += self.vy * dt
80-
self.theta += self.vtheta * dt
81-
self.theta = self.normalize_angle(self.theta)
63+
self.vx = speed * math.cos(target_angle)
64+
self.vy = speed * math.sin(target_angle)
65+
66+
reached_pos = distance < 5.0
67+
reached_theta = abs(theta_error) < 0.15 or wp['type'] == 0
68+
69+
if reached_pos and reached_theta:
70+
self.vx = self.vy = self.vtheta = 0.0
71+
self.current_wp_id = wp_id
72+
73+
self.ser.write(f"SET;WAYPOINT;REACH;{wp_id}\n".encode())
8274

83-
if now - self.last_send > 0.1 and self.start:
84-
print(f'[TX] SET;POS;{int(self.x)};{int(self.y)};{self.theta:.5f}')
85-
self.ser.write(f'SET;POS;{int(self.x)};{int(self.y)};{self.theta:.5f}\n'.encode())
75+
self.waypoint_queue.pop(0)
76+
del self.waypoints[wp_id]
77+
78+
else:
79+
self.vx = self.vy = self.vtheta = 0.0
80+
81+
# --- integrate ---
82+
self.x += self.vx * dt
83+
self.y += self.vy * dt
84+
self.theta = self.normalize_angle(self.theta + self.vtheta * dt)
85+
86+
# --- periodic position ---
87+
if self.start and now - self.last_send > 0.1:
88+
self.ser.write(
89+
f"SET;POS;{int(self.x)};{int(self.y)};{self.theta:.5f}\n".encode()
90+
)
8691
self.last_send = now
8792

8893
def run(self):
@@ -98,73 +103,62 @@ def run(self):
98103

99104
def handle_message(self, msg):
100105
if msg == "GET;POS":
101-
print(f'[TX] SET;POS;{int(self.x)};{int(self.y)};{self.theta:.3f}')
102-
self.ser.write(f'SET;POS;{int(self.x)};{int(self.y)};{self.theta:.3f}\n'.encode())
106+
self.ser.write(
107+
f"SET;POS;{int(self.x)};{int(self.y)};{self.theta:.3f}\n".encode()
108+
)
103109

104110
elif msg == "GET;SPEED":
105-
print(f'[TX] SET;SPEED;{int(self.vx)};{int(self.vy)};{int(self.vtheta)}')
106-
self.ser.write(f'SET;SPEED;{int(self.vx)};{int(self.vy)};{int(self.vtheta)}\n'.encode())
111+
self.ser.write(
112+
f"SET;SPEED;{int(self.vx)};{int(self.vy)};{int(self.vtheta)}\n".encode()
113+
)
107114

108115
elif msg.startswith("GET;DIST;"):
109116
n = int(msg.split(';')[2])
110117
dist = self.tof.get(n, 0)
111-
print(f'[TX] SET;DIST;{n};{dist}')
112-
self.ser.write(f'SET;DIST;{n};{dist}\n'.encode())
118+
self.ser.write(f"SET;DIST;{n};{dist}\n".encode())
113119

114120
elif msg == "GET;PID":
115-
print(f'[TX] SET;PID;{self.pid[0]};{self.pid[1]};{self.pid[2]}')
116-
self.ser.write(f'SET;PID;{self.pid[0]};{self.pid[1]};{self.pid[2]}\n'.encode())
121+
self.ser.write(f"SET;PID;{self.pid[0]};{self.pid[1]};{self.pid[2]}\n".encode())
117122

118123
elif msg.startswith("SET;PID"):
119-
print(f'[TX] OK;PID;1')
120-
self.ser.write(f'OK;PID;1\n'.encode())
124+
self.ser.write(b"OK;PID;1\n")
121125

122126
elif msg.startswith("SET;START;"):
123127
self.start = msg.split(';')[2] == '1'
124-
print(f'[TX] OK;START;1')
125-
self.ser.write(f'OK;START;1\n'.encode())
128+
self.ser.write(b"OK;START;1\n")
126129

130+
# --- MULTI WAYPOINT (clears previous) ---
127131
elif msg.startswith("SET;WAYPOINT;"):
128-
parts = msg.split(';')
129-
if len(parts) >= 7:
132+
parts = msg.split(';')[2:]
133+
134+
if len(parts) % 5 != 0:
135+
self.ser.write(b"ERR;WAYPOINT;0\n")
136+
return
137+
138+
# clear existing waypoints
139+
self.waypoints.clear()
140+
self.waypoint_queue.clear()
141+
142+
for i in range(0, len(parts), 5):
130143
wp = {
131-
'id': int(parts[2]),
132-
'type': int(parts[3]),
133-
'x': float(parts[4]),
134-
'y': float(parts[5]),
135-
'theta': float(parts[6])
144+
'id': int(parts[i]),
145+
'type': int(parts[i + 1]),
146+
'x': float(parts[i + 2]),
147+
'y': float(parts[i + 3]),
148+
'theta': float(parts[i + 4]),
136149
}
150+
137151
self.waypoints[wp['id']] = wp
138-
if wp['id'] not in self.waypoint_order:
139-
self.waypoint_order.append(wp['id'])
140-
self.ser.write(f'OK;WAYPOINT;{wp["id"]}\n'.encode())
152+
self.waypoint_queue.append(wp['id'])
141153

142-
elif msg.startswith("SET;WAYPOINTS;"):
143-
parts = msg.split(';')
144-
if len(parts) / 7 >= 1:
145-
self.waypoints.clear()
146-
self.waypoint_order.clear()
147-
for i in range(2, len(parts), 5):
148-
wp = {
149-
'id': int(parts[i]),
150-
'type': int(parts[i + 1]),
151-
'x': float(parts[i + 2]),
152-
'y': float(parts[i + 3]),
153-
'theta': float(parts[i + 4])
154-
}
155-
self.waypoints[wp['id']] = wp
156-
if wp['id'] not in self.waypoint_order:
157-
self.waypoint_order.append(wp['id'])
158-
self.ser.write(f'OK;WAYPOINT;{wp["id"]}\n'.encode())
159-
self.ser.write(b'OK;WAYPOINTS;1\n')
154+
self.ser.write(f"OK;WAYPOINT;{wp['id']}\n".encode())
160155

161156
elif msg.startswith("SET;POS"):
162157
parts = msg.split(';')
163158
self.x = float(parts[2])
164159
self.y = float(parts[3])
165160
self.theta = float(parts[4])
166-
print(f'[TX] OK;POS;1')
167-
self.ser.write(b'OK;POS;1\n')
161+
self.ser.write(b"OK;POS;1\n")
168162

169163
def stop(self):
170164
self.running = False
@@ -174,13 +168,15 @@ def stop(self):
174168

175169
if __name__ == '__main__':
176170
parser = argparse.ArgumentParser(description="Simulated PCB")
177-
parser.add_argument('--port', type=str, default='/dev/pts/6', help='Serial port to use')
171+
parser.add_argument('--port', type=str, default='/dev/pts/6')
178172
args = parser.parse_args()
179173

180174
sim = None
181175
try:
182176
sim = SimulatedPCB(port=args.port)
177+
while True:
178+
time.sleep(1)
183179
except KeyboardInterrupt:
184180
if sim:
185181
sim.stop()
186-
print("Simulation stopped")
182+
print("Simulation stopped")

src/modelec_com/include/modelec_com/pcb_odo_interface.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ namespace Modelec
5656
rclcpp::Subscription<modelec_interfaces::msg::OdometryWaypoints>::SharedPtr odo_add_waypoints_subscriber_;
5757
rclcpp::Subscription<modelec_interfaces::msg::OdometryPos>::SharedPtr odo_set_pos_subscriber_;
5858
rclcpp::Subscription<modelec_interfaces::msg::OdometryPid>::SharedPtr odo_set_pid_subscriber_;
59+
rclcpp::Subscription<std_msgs::msg::Empty>::SharedPtr odo_ask_active_waypoint_subscriber_;
5960
rclcpp::Subscription<sensor_msgs::msg::Joy>::SharedPtr joy_subscriber_;
6061

6162
void AddWaypointCallback(const modelec_interfaces::msg::OdometryWaypoint::SharedPtr msg);

src/modelec_com/src/pcb_action_interface.cpp

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -263,11 +263,14 @@ namespace Modelec
263263
};*/
264264

265265
servo_value_ = {
266-
{0, 0},
267-
{1, 3},
268-
{2, 1.45},
269-
{3, 1.6},
270-
{4, 0},
266+
{0, 2.95},
267+
{1, 0.93},
268+
{2, 0},
269+
{3, 3},
270+
{4, 0.8},
271+
{5, 0.8},
272+
{6, 0.8},
273+
{7, 0.8},
271274
};
272275

273276
std::string data = "MOV;SERVO;" + std::to_string(servo_value_.size()) + ";";

src/modelec_com/src/pcb_odo_interface.cpp

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,13 @@ namespace Modelec
6969
SetPIDCallback(msg);
7070
});
7171

72+
odo_ask_active_waypoint_subscriber_ = this->create_subscription<std_msgs::msg::Empty>(
73+
"odometry/ask_active_waypoint", 10,
74+
[this](const std_msgs::msg::Empty::SharedPtr)
75+
{
76+
GetData("WAYPOINT", {"ACTIVE"});
77+
});
78+
7279
joy_subscriber_ = this->create_subscription<sensor_msgs::msg::Joy>(
7380
"joy", 30,
7481
[this](const sensor_msgs::msg::Joy::SharedPtr msg)
@@ -170,12 +177,38 @@ namespace Modelec
170177
}
171178
else if (tokens[1] == "WAYPOINT")
172179
{
173-
int id = std::stoi(tokens[2]);
180+
if (tokens[2] == "REACH")
181+
{
182+
// RCLCPP_INFO(this->get_logger(), "Waypoint reached: ID %s", tokens[3].c_str());
174183

175-
auto message = modelec_interfaces::msg::OdometryWaypoint();
176-
message.id = id;
184+
int id = std::stoi(tokens[3]);
177185

178-
odo_waypoint_reach_publisher_->publish(message);
186+
auto message = modelec_interfaces::msg::OdometryWaypoint();
187+
message.id = id;
188+
189+
odo_waypoint_reach_publisher_->publish(message);
190+
}
191+
else if (tokens.size() >= 7)
192+
{
193+
int id = std::stoi(tokens[3]);
194+
bool is_end = tokens[4] == "1";
195+
long x = std::stol(tokens[5]);
196+
long y = std::stol(tokens[6]);
197+
double theta = std::stod(tokens[7]);
198+
bool reach = tokens.size() > 7 ? tokens[8] == "1" : false;
199+
200+
if (reach)
201+
{
202+
auto message = modelec_interfaces::msg::OdometryWaypoint();
203+
message.id = id;
204+
message.is_end = is_end;
205+
message.x = x;
206+
message.y = y;
207+
message.theta = theta;
208+
209+
odo_waypoint_reach_publisher_->publish(message);
210+
}
211+
}
179212
}
180213
else if (tokens[1] == "PID")
181214
{
@@ -201,6 +234,7 @@ namespace Modelec
201234
}
202235
else if (tokens[1] == "WAYPOINT")
203236
{
237+
// RCLCPP_INFO(this->get_logger(), "Waypoint added successfully.");
204238
}
205239
else if (tokens[1] == "PID")
206240
{

0 commit comments

Comments
 (0)