Skip to content

Commit d3b7b4f

Browse files
author
Cornelius Krupp
committed
Add joint control messages
1 parent 0796a92 commit d3b7b4f

2 files changed

Lines changed: 115 additions & 10 deletions

File tree

src/bitbots_lowlevel/bitbots_x02_joint_control/src/example_server.rs

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,15 @@ impl ArmService for DummyArmService {
2323
// --- 2. Implement the methods ---
2424

2525
async fn get_arm_config(&self, _: Request<Empty>) -> Result<Response<DroidConfigs>, Status> {
26-
Ok(Response::new(DroidConfigs::default()))
26+
Ok(Response::new(DroidConfigs {
27+
joint_name: ["UL1", "UL2", "UL3", "UL4", "UL5", "UR1", "UR2", "UR3", "UR4", "UR5"].into_iter().map(String::from).collect(),
28+
pzero: [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0].to_vec(),
29+
pmin: [-120.0, 0.0, -130.0, 0.0, -200.0, -120.0, 0.0, -130.0, 0.0, -200.0].to_vec(),
30+
pmax: [160.0, 160.0, 130.0, 140.0, 90.0, 160.0, 200.0, 130.0, 140.0, 90.0].to_vec(),
31+
imax: [15.0, 15.0, 15.0, 15.0, 15.0, 15.0, 15.0, 15.0, 15.0, 15.0].to_vec(),
32+
kp: [48.8, 48.8, 40.8, 48.8, 9.76, 48.8, 48.8, 40.8, 48.8, 9.76].to_vec(),
33+
kd: [1.39, 1.39, 1.19, 1.39, 0.195, 1.39, 1.39, 1.19, 1.39, 0.195].to_vec(),
34+
}))
2735
}
2836

2937
async fn get_arm_state(&self, _: Request<Empty>) -> Result<Response<DroidArmResponse>, Status> {
@@ -42,8 +50,9 @@ impl ArmService for DummyArmService {
4250

4351
async fn set_arm_command(
4452
&self,
45-
_: Request<DroidCommandRequest>,
53+
req: Request<DroidCommandRequest>,
4654
) -> Result<Response<Empty>, Status> {
55+
println!("{:?}",req);
4756
Ok(Response::new(Empty::default()))
4857
}
4958

@@ -94,7 +103,15 @@ impl LegService for DummyLegService {
94103
type ExchangeLegControlStreamStream = ReceiverStream<Result<DroidStateResponse, Status>>;
95104

96105
async fn get_leg_config(&self, _: Request<Empty>) -> Result<Response<DroidConfigs>, Status> {
97-
Ok(Response::new(DroidConfigs::default()))
106+
Ok(Response::new(DroidConfigs {
107+
joint_name: ["muT", "msL", "mhL", "mkL", "maL", "mbT", "msR", "mhR", "mkR", "maR"].into_iter().map(String::from).collect(),
108+
pzero: [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0].to_vec(),
109+
pmin: [-30.0, -10.0, -30.0, -140.0, -360.0, -30.0, -10.0, -30.0, -140.0, -360.0].to_vec(),
110+
pmax: [30.0, 15.0, 90.0, 5.0, 360.0, 30.0, 15.0, 90.0, 5.0, 360.0].to_vec(),
111+
imax: [15.0, 30.0, 60.0, 60.0, 60.0, 15.0, 30.0, 60.0, 60.0, 60.0].to_vec(),
112+
kp: [0.003, 0.02, 0.0269, 0.02, 0.0309, 0.003, 0.02, 0.0269, 0.02, 0.0309].to_vec(),
113+
kd: [0.0001, 0.0004, 0.00043, 0.0004, 0.0005, 0.0001, 0.0004, 0.00043, 0.0004, 0.0005].to_vec(),
114+
}))
98115
}
99116

100117
async fn get_leg_state(
@@ -116,8 +133,9 @@ impl LegService for DummyLegService {
116133

117134
async fn set_leg_command(
118135
&self,
119-
_: Request<DroidCommandRequest>,
136+
req: Request<DroidCommandRequest>,
120137
) -> Result<Response<Empty>, Status> {
138+
println!("{:?}",req);
121139
Ok(Response::new(Empty::default()))
122140
}
123141

src/bitbots_lowlevel/bitbots_x02_joint_control/src/main.rs

Lines changed: 93 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use std::env;
44
use std::sync::{Arc, Mutex};
55
use std::time::Duration;
66
use tokio::time::interval;
7+
use tokio_stream::StreamExt;
78

89
pub mod droidgrpc {
910
tonic::include_proto!("droidgrpc");
@@ -20,6 +21,11 @@ struct RobotState {
2021
// Key: ROS joint name, Value: position
2122
joint_data: HashMap<String, f64>,
2223
}
24+
#[derive(Default)]
25+
struct GoalState {
26+
// Key: ROS joint name, Value: position target
27+
joint_targets: HashMap<String, f32>,
28+
}
2329

2430
#[tokio::main]
2531
async fn main() -> Result<(), Box<dyn std::error::Error>> {
@@ -32,9 +38,12 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
3238
"/joint_states",
3339
r2r::QosProfile::default(),
3440
)?;
41+
let mut cmd_sub = node.subscribe::<r2r::bitbots_msgs::msg::JointCommand>(
42+
"/x02_joint_commands",
43+
r2r::QosProfile::default()
44+
)?;
3545
let state_pub = Arc::new(Mutex::new(state_pub));
36-
let node = Arc::new(Mutex::new(node));
37-
let spin_node = node.clone();
46+
let spin_node = Arc::new(Mutex::new(node));
3847

3948
// --- 1. Mapping & Virtual Joints ---
4049
let mut name_map = HashMap::new();
@@ -90,13 +99,38 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
9099
.into_inner()
91100
.joint_name;
92101

93-
// --- 4. Writer Tasks: Update shared state when data arrives ---
102+
let mut initial_goals = GoalState::default();
103+
104+
// Seed Leg goals
105+
if let Ok(resp) = leg_client.get_leg_state(Empty {}).await {
106+
let current_pos = resp.into_inner().position;
107+
for (i, hw_id) in leg_joint_names.iter().enumerate() {
108+
if let Some(ros_name) = name_map.get(hw_id) {
109+
initial_goals.joint_targets.insert(ros_name.clone(), current_pos.get(i).cloned().unwrap_or(0.0));
110+
}
111+
}
112+
}
113+
114+
// Seed Arm goals
115+
if let Ok(resp) = arm_client.get_arm_state(Empty {}).await {
116+
let current_pos = resp.into_inner().position;
117+
for (i, hw_id) in arm_joint_names.iter().enumerate() {
118+
if let Some(ros_name) = name_map.get(hw_id) {
119+
initial_goals.joint_targets.insert(ros_name.clone(), current_pos.get(i).cloned().unwrap_or(0.0));
120+
}
121+
}
122+
}
123+
124+
let shared_goals = Arc::new(Mutex::new(initial_goals));
125+
126+
// --- GPRC Tasks: Update shared state when data arrives, send out commands ---
94127

95128
// Leg Poller
96129
let mut l_client = leg_client.clone();
97130
let l_state = shared_state.clone();
98131
let l_names = leg_joint_names.clone();
99132
let l_map = name_map.clone();
133+
let l_goals = shared_goals.clone();
100134
tokio::spawn(async move {
101135
let mut ticker = interval(Duration::from_millis(20)); // Poll at 50Hz
102136
loop {
@@ -113,6 +147,23 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
113147
}
114148
}
115149
}
150+
let cmd_request = {
151+
let goals = l_goals.lock().unwrap();
152+
let positions: Vec<f32> = l_names.iter()
153+
.map(|hw_id| {
154+
let ros_name = l_map.get(hw_id).expect("Map error");
155+
*goals.joint_targets.get(ros_name).expect("Leg Joint has no value assigned")
156+
})
157+
.collect();
158+
159+
droidgrpc::DroidCommandRequest {
160+
position: positions,
161+
..Default::default()
162+
}
163+
};
164+
if let Err(e) = l_client.set_leg_command(cmd_request).await{
165+
eprintln!("gRPC Error: Failed to set Arm command: {}", e);
166+
}
116167
}
117168
});
118169

@@ -121,9 +172,28 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
121172
let a_state = shared_state.clone();
122173
let a_names = arm_joint_names.clone();
123174
let a_map = name_map.clone();
175+
let a_goals = shared_goals.clone();
124176
tokio::spawn(async move {
125177
let mut ticker = interval(Duration::from_millis(20));
126178
loop {
179+
let cmd_request = {
180+
let goals = a_goals.lock().unwrap();
181+
let positions: Vec<f32> = a_names.iter()
182+
.map(|hw_id| {
183+
let ros_name = a_map.get(hw_id).expect("Arm mapping error");
184+
*goals.joint_targets.get(ros_name).expect("Arm Joint has no value assigned")
185+
})
186+
.collect();
187+
188+
droidgrpc::DroidCommandRequest {
189+
position: positions,
190+
..Default::default()
191+
}
192+
};
193+
194+
if let Err(e) = a_client.set_arm_command(cmd_request).await {
195+
eprintln!("gRPC Error: Failed to set Arm command: {}", e);
196+
}
127197
ticker.tick().await;
128198
if let Ok(resp) = a_client.get_arm_state(Empty {}).await {
129199
let positions = resp.into_inner().position;
@@ -140,7 +210,8 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
140210
}
141211
});
142212

143-
// --- 5. Reader Task: Publish at Fixed ROS Rate ---
213+
// --- Reader Task: Publish at Fixed ROS Rate ---
214+
let read_node = spin_node.clone();
144215
let pub_state = shared_state.clone();
145216
tokio::spawn(async move {
146217
let mut ticker = interval(Duration::from_millis(10)); // Publish at 100Hz
@@ -150,7 +221,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
150221
let mut ros_msg = r2r::sensor_msgs::msg::JointState::default();
151222
// Get the duration from the clock
152223
let now_duration = {
153-
let n_lock = node.lock().expect("node lock poisoned");
224+
let n_lock = read_node.lock().expect("node lock poisoned");
154225
let clock_res = n_lock.get_ros_clock();
155226
let mut c_lock = clock_res.lock().expect("clock lock poisoned");
156227
c_lock.get_now().expect("Clock failed")
@@ -194,8 +265,24 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
194265
}
195266
}
196267
});
268+
// ---ROS receive goals---
269+
let sub_goals = shared_goals.clone();
270+
271+
tokio::spawn(async move {
272+
while let Some(msg) = cmd_sub.next().await {
273+
if msg.joint_names.len() != msg.positions.len() { continue; }
274+
275+
let mut goals = sub_goals.lock().unwrap();
276+
for (i, ros_name) in msg.joint_names.iter().enumerate() {
277+
// Only update if it's a joint we actually track
278+
if goals.joint_targets.contains_key(ros_name) {
279+
goals.joint_targets.insert(ros_name.clone(), msg.positions[i] as f32);
280+
}
281+
}
282+
}
283+
});
197284

198-
// --- 6. ROS Spin ---
285+
// ---ROS Spin ---
199286
loop {
200287
spin_node
201288
.lock()

0 commit comments

Comments
 (0)