k/joint/
joint.rs

1/*
2  Copyright 2017 Takashi Ogura
3
4  Licensed under the Apache License, Version 2.0 (the "License");
5  you may not use this file except in compliance with the License.
6  You may obtain a copy of the License at
7
8      http://www.apache.org/licenses/LICENSE-2.0
9
10  Unless required by applicable law or agreed to in writing, software
11  distributed under the License is distributed on an "AS IS" BASIS,
12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  See the License for the specific language governing permissions and
14  limitations under the License.
15*/
16use super::joint_type::*;
17use super::range::*;
18use super::velocity::*;
19use crate::errors::*;
20use na::{Isometry3, RealField, Translation3, UnitQuaternion};
21use nalgebra as na;
22use simba::scalar::SubsetOf;
23use std::cell::RefCell;
24use std::fmt::{self, Display};
25
26/// Joint with type
27#[derive(Debug, Clone)]
28pub struct Joint<T: RealField> {
29    /// Name of this joint
30    pub name: String,
31    /// Type of this joint
32    pub joint_type: JointType<T>,
33    /// position (angle) of this joint
34    position: T,
35    /// velocity of this joint
36    velocity: T,
37    /// Limits of this joint
38    pub limits: Option<Range<T>>,
39    /// local origin transform of joint
40    origin: Isometry3<T>,
41    /// cache of world transform
42    world_transform_cache: RefCell<Option<Isometry3<T>>>,
43    /// cache of world velocity
44    world_velocity_cache: RefCell<Option<Velocity<T>>>,
45}
46
47impl<T> Joint<T>
48where
49    T: RealField + SubsetOf<f64>,
50{
51    /// Create new Joint with name and type
52    ///
53    /// # Examples
54    ///
55    /// ```
56    /// use nalgebra as na;
57    ///
58    /// // create fixed joint
59    /// let fixed = k::Joint::<f32>::new("f0", k::JointType::Fixed);
60    /// assert!(fixed.joint_position().is_none());
61    ///
62    /// // create rotational joint with Y-axis
63    /// let rot = k::Joint::<f64>::new("r0", k::JointType::Rotational { axis: na::Vector3::y_axis() });
64    /// assert_eq!(rot.joint_position().unwrap(), 0.0);
65    /// ```
66    ///
67    pub fn new(name: &str, joint_type: JointType<T>) -> Joint<T> {
68        Joint {
69            name: name.to_string(),
70            joint_type,
71            position: T::zero(),
72            velocity: T::zero(),
73            limits: None,
74            origin: Isometry3::identity(),
75            world_transform_cache: RefCell::new(None),
76            world_velocity_cache: RefCell::new(None),
77        }
78    }
79    /// Set the position of the joint
80    ///
81    /// It returns Err if it is out of the limits, or this is fixed joint.
82    ///
83    /// # Examples
84    ///
85    /// ```
86    /// use nalgebra as na;
87    ///
88    /// // Create fixed joint
89    /// let mut fixed = k::Joint::<f32>::new("f0", k::JointType::Fixed);
90    /// // Set position to fixed joint always fails
91    /// assert!(fixed.set_joint_position(1.0).is_err());
92    ///
93    /// // Create rotational joint with Y-axis
94    /// let mut rot = k::Joint::<f64>::new("r0", k::JointType::Rotational { axis: na::Vector3::y_axis() });
95    /// // As default, it has not limit
96    ///
97    /// // Initial position is 0.0
98    /// assert_eq!(rot.joint_position().unwrap(), 0.0);
99    /// // If it has no limits, set_joint_position always succeeds.
100    /// rot.set_joint_position(0.2).unwrap();
101    /// assert_eq!(rot.joint_position().unwrap(), 0.2);
102    /// ```
103    ///
104    pub fn set_joint_position(&mut self, position: T) -> Result<(), Error> {
105        if !self.is_movable() {
106            return Err(Error::SetToFixedError {
107                joint_name: self.name.to_string(),
108            });
109        }
110        if let Some(ref range) = self.limits {
111            if !range.is_valid(position.clone()) {
112                return Err(Error::OutOfLimitError {
113                    joint_name: self.name.to_string(),
114                    position: na::try_convert(position).unwrap_or_default(),
115                    max_limit: na::try_convert(range.max.clone()).unwrap_or_default(),
116                    min_limit: na::try_convert(range.min.clone()).unwrap_or_default(),
117                });
118            }
119        }
120        self.position = position;
121        self.clear_caches();
122        Ok(())
123    }
124    /// Set the clamped position of the joint
125    ///
126    /// It refers to the joint limit and clamps the argument. This function does nothing if this is fixed joint.
127    ///
128    /// # Examples
129    ///
130    /// ```
131    /// use nalgebra as na;
132    ///
133    /// // Create rotational joint with Y-axis
134    /// let mut rot = k::Joint::<f64>::new("r0", k::JointType::Rotational { axis: na::Vector3::y_axis() });
135    ///
136    /// let limits = k::joint::Range::new(-1.0, 1.0);
137    /// rot.limits = Some(limits);
138    ///
139    /// // Initial position is 0.0
140    /// assert_eq!(rot.joint_position().unwrap(), 0.0);
141    /// rot.set_joint_position_clamped(2.0);
142    /// assert_eq!(rot.joint_position().unwrap(), 1.0);
143    /// rot.set_joint_position_clamped(-2.0);
144    /// assert_eq!(rot.joint_position().unwrap(), -1.0);
145    /// ```
146    ///
147    pub fn set_joint_position_clamped(&mut self, position: T) {
148        if !self.is_movable() {
149            return;
150        }
151        let position_clamped = if let Some(ref range) = self.limits {
152            range.clamp(position)
153        } else {
154            position
155        };
156        self.set_joint_position_unchecked(position_clamped);
157    }
158    pub fn set_joint_position_unchecked(&mut self, position: T) {
159        self.position = position;
160        self.clear_caches();
161    }
162    /// Returns the position (angle)
163    #[inline]
164    pub fn joint_position(&self) -> Option<T> {
165        match self.joint_type {
166            JointType::Fixed => None,
167            _ => Some(self.position.clone()),
168        }
169    }
170
171    #[inline]
172    pub fn origin(&self) -> &Isometry3<T> {
173        &self.origin
174    }
175
176    #[inline]
177    pub fn set_origin(&mut self, origin: Isometry3<T>) {
178        self.origin = origin;
179        self.clear_caches();
180    }
181
182    pub fn set_joint_velocity(&mut self, velocity: T) -> Result<(), Error> {
183        if let JointType::Fixed = self.joint_type {
184            return Err(Error::SetToFixedError {
185                joint_name: self.name.to_string(),
186            });
187        }
188        self.velocity = velocity;
189        self.world_velocity_cache.replace(None);
190        Ok(())
191    }
192
193    /// Returns the velocity
194    #[inline]
195    pub fn joint_velocity(&self) -> Option<T> {
196        match self.joint_type {
197            JointType::Fixed => None,
198            _ => Some(self.velocity.clone()),
199        }
200    }
201
202    /// Calculate and returns the transform of the end of this joint
203    ///
204    /// # Examples
205    ///
206    /// ```
207    /// use nalgebra as na;
208    ///
209    /// // Create linear joint with X-axis
210    /// let mut lin = k::Joint::<f64>::new("l0", k::JointType::Linear { axis: na::Vector3::x_axis() });
211    /// assert_eq!(lin.local_transform().translation.vector.x, 0.0);
212    /// lin.set_joint_position(-1.0).unwrap();
213    /// assert_eq!(lin.local_transform().translation.vector.x, -1.0);
214    /// ```
215    ///
216    pub fn local_transform(&self) -> Isometry3<T> {
217        let joint_transform = match &self.joint_type {
218            JointType::Fixed => Isometry3::identity(),
219            JointType::Rotational { axis } => Isometry3::from_parts(
220                Translation3::new(T::zero(), T::zero(), T::zero()),
221                UnitQuaternion::from_axis_angle(axis, self.position.clone()),
222            ),
223            JointType::Linear { axis } => Isometry3::from_parts(
224                Translation3::from(axis.clone().into_inner() * self.position.clone()),
225                UnitQuaternion::identity(),
226            ),
227        };
228        self.origin.clone() * joint_transform
229    }
230
231    #[inline]
232    pub(crate) fn set_world_transform(&self, world_transform: Isometry3<T>) {
233        self.world_transform_cache.replace(Some(world_transform));
234    }
235
236    #[inline]
237    pub(crate) fn set_world_velocity(&self, world_velocity: Velocity<T>) {
238        self.world_velocity_cache.replace(Some(world_velocity));
239    }
240    /// Get the result of forward kinematics
241    ///
242    /// The value is updated by `Chain::update_transforms`
243    #[inline]
244    pub fn world_transform(&self) -> Option<Isometry3<T>> {
245        self.world_transform_cache.borrow().clone()
246    }
247
248    #[inline]
249    pub fn world_velocity(&self) -> Option<Velocity<T>> {
250        self.world_velocity_cache.borrow().clone()
251    }
252
253    #[inline]
254    pub fn is_movable(&self) -> bool {
255        !matches!(self.joint_type, JointType::Fixed)
256    }
257
258    /// Clear caches defined in the world coordinate
259    #[inline]
260    pub fn clear_caches(&self) {
261        self.world_transform_cache.replace(None);
262        self.world_velocity_cache.replace(None);
263    }
264}
265
266impl<T: RealField> Display for Joint<T> {
267    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
268        write!(f, "{} {}", self.name, self.joint_type)
269    }
270}